z3-z3-4.13.0.src.smt.smt_quick_checker.cpp Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_quick_checker.cpp
Abstract:
Incomplete model checker.
Author:
Leonardo de Moura (leonardo) 2008-06-20.
Revision History:
--*/
#include "smt/smt_context.h"
#include "smt/smt_quick_checker.h"
#include "ast/ast_pp.h"
#include
namespace smt {
quick_checker::collector::collector(context & c):
m_context(c),
m_manager(c.get_manager()),
m_conservative(true) {
}
void quick_checker::collector::init(quantifier * q) {
m_num_vars = q->get_num_decls();
m_already_found.reserve(m_num_vars+1, false);
m_candidates.reserve(m_num_vars+1);
m_tmp_candidates.reserve(m_num_vars+1);
for (unsigned i = 0; i < m_num_vars; i++) {
m_already_found[i] = false;
m_candidates[i].reset();
}
m_cache.reset();
}
/**
\brief Returns true if there is a term (f ... n' ...) in the logical
context such that n' is the i-th argument of f, and n and n' are in the
same equivalence class.
If f == 0 then true is returned (f == 0 means there is no parent to check)
*/
bool quick_checker::collector::check_arg(enode * n, func_decl * f, unsigned i) {
if (!f || !m_conservative)
return true;
for (enode * curr : m_context.enodes_of(f)) {
if (m_context.is_relevant(curr) && curr->is_cgr() && i < curr->get_num_args() && curr->get_arg(i)->get_root() == n->get_root())
return true;
}
return false;
}
void quick_checker::collector::collect_core(app * n, func_decl * p, unsigned i) {
func_decl * f = n->get_decl();
unsigned num_args = n->get_num_args();
for (unsigned j = 0; j < num_args; j++) {
expr * arg = n->get_arg(j);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (idx >= m_num_vars)
return;
if (m_already_found[idx] && m_conservative) {
enode_set & s = m_candidates[idx];
enode_set & ns = m_tmp_candidates[idx];
if (s.empty())
continue;
ns.reset();
for (enode * curr : m_context.enodes_of(f)) {
if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) {
enode * arg = curr->get_arg(j)->get_root();
// intersection
if (s.contains(arg))
ns.insert(arg);
}
}
SASSERT(m_conservative);
s.swap(ns);
}
else {
m_already_found[idx] = true;
enode_set & s = m_candidates[idx];
for (enode * curr : m_context.enodes_of(f)) {
if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) {
enode * arg = curr->get_arg(j)->get_root();
s.insert(arg);
}
}
}
}
else {
if (n->get_family_id() != m_manager.get_basic_family_id())
collect(arg, n->get_decl(), j);
else
collect(arg, nullptr, 0);
}
}
}
void quick_checker::collector::collect(expr * n, func_decl * f, unsigned idx) {
if (is_quantifier(n))
return;
if (is_var(n))
return;
if (is_ground(n))
return;
entry e(n, f, idx);
if (m_cache.contains(e))
return;
m_cache.insert(e);
collect_core(to_app(n), f, idx);
}
void quick_checker::collector::save_result(vector & candidates) {
candidates.reserve(m_num_vars+1);
for (unsigned i = 0; i < m_num_vars; i++) {
enode_vector & v = candidates[i];
v.reset();
enode_set & s = m_candidates[i];
for (enode * curr : s) {
v.push_back(curr);
}
}
TRACE("collector",
tout << "candidates:\n";
for (unsigned i = 0; i < m_num_vars; i++) {
tout << "var " << i << ":";
enode_vector & v = candidates[i];
for (enode * n : v)
tout << " #" << n->get_owner_id();
tout << "\n";
});
}
void quick_checker::collector::operator()(quantifier * q, bool conservative, vector & candidates) {
flet l(m_conservative, conservative);
init(q);
TRACE("collector", tout << "model checking: #" << q->get_id() << "\n" << mk_pp(q, m_manager) << "\n";);
collect(q->get_expr(), nullptr, 0);
save_result(candidates);
}
quick_checker::quick_checker(context & c):
m_context(c),
m_manager(c.get_manager()),
m_collector(c),
m_new_exprs(m_manager) {
}
/**
\brief Instantiate instances unsatisfied by the current model. Return true if new instances were generated.
*/
bool quick_checker::instantiate_unsat(quantifier * q) {
TRACE("quick_checker", tout << "instantiate instances unsatisfied by current model\n" << mk_pp(q, m_manager) << "\n";);
m_candidate_vectors.reset();
m_collector(q, true, m_candidate_vectors);
m_num_bindings = q->get_num_decls();
return process_candidates(q, true);
}
/**
\brief Instantiate instances not satisfied by the current model. Return true if new instances were generated.
*/
bool quick_checker::instantiate_not_sat(quantifier * q) {
TRACE("quick_checker", tout << "instantiate instances not satisfied by current model\n" << mk_pp(q, m_manager) << "\n";);
m_candidate_vectors.reset();
m_collector(q, false, m_candidate_vectors);
m_num_bindings = q->get_num_decls();
return process_candidates(q, false);
}
bool quick_checker::instantiate_not_sat(quantifier * q, unsigned num_candidates, expr * const * candidates) {
// initialize m_candidates using the given set of candidates.
m_candidate_vectors.reset();
m_num_bindings = q->get_num_decls();
m_candidate_vectors.reserve(m_num_bindings+1);
for (unsigned i = 0; i < m_num_bindings; i++) {
m_candidate_vectors[i].reset();
sort * s = q->get_decl_sort(i);
for (unsigned j = 0; j < num_candidates; j++) {
if (candidates[j]->get_sort() == s) {
expr * n = candidates[j];
m_context.internalize(n, false);
enode * e = m_context.get_enode(n);
m_candidate_vectors[i].push_back(e);
}
}
}
return process_candidates(q, false);
}
bool quick_checker::process_candidates(quantifier * q, bool unsat) {
vector> empty_used_enodes;
buffer szs;
buffer it;
for (unsigned i = 0; i < m_num_bindings; i++) {
unsigned sz = m_candidate_vectors[i].size();
if (sz == 0)
return false;
szs.push_back(sz);
it.push_back(0);
}
TRACE("quick_checker_sizes", tout << mk_pp(q, m_manager) << "\n"; for (unsigned i = 0; i < szs.size(); i++) tout << szs[i] << " "; tout << "\n";);
TRACE("quick_checker_candidates",
tout << "candidates:\n";
for (unsigned i = 0; i < m_num_bindings; i++) {
enode_vector & v = m_candidate_vectors[i];
for (enode * n : v)
tout << "#" << n->get_owner_id() << " ";
tout << "\n";
});
bool result = false;
m_bindings.reserve(m_num_bindings+1, 0);
do {
for (unsigned i = 0; i < m_num_bindings; i++)
m_bindings[m_num_bindings - i - 1] = m_candidate_vectors[i][it[i]];
if (!m_context.contains_instance(q, m_num_bindings, m_bindings.data())) {
bool is_candidate = false;
TRACE("quick_checker", tout << "processing bindings:";
for (unsigned i = 0; i < m_num_bindings; i++) tout << " #" << m_bindings[i]->get_owner_id();
tout << "\n";);
if (unsat)
is_candidate = check_quantifier(q, false);
else
is_candidate = !check_quantifier(q, true);
if (is_candidate) {
TRACE("quick_checker", tout << "found new candidate\n";);
TRACE("quick_checker_sizes", tout << "found new candidate\n";
for (unsigned i = 0; i < m_num_bindings; i++) tout << "#" << m_bindings[i]->get_owner_id() << " "; tout << "\n";);
unsigned max_generation = get_max_generation(m_num_bindings, m_bindings.data());
if (m_context.add_instance(q, nullptr /* no pattern was used */, m_num_bindings, m_bindings.data(), nullptr,
max_generation,
0, // min_top_generation is only available for instances created by the MAM
0, // max_top_generation is only available for instances created by the MAM
empty_used_enodes))
result = true;
}
}
}
while (product_iterator_next(szs.size(), szs.data(), it.data()));
return result;
}
bool quick_checker::check_quantifier(quantifier * n, bool is_true) {
bool r = check(n->get_expr(), is_true);
m_new_exprs.reset();
m_check_cache.reset();
m_canonize_cache.reset();
return r;
}
bool quick_checker::all_args(app * a, bool is_true) {
unsigned num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (!check(a->get_arg(i), is_true))
return false;
return true;
}
bool quick_checker::any_arg(app * a, bool is_true) {
unsigned num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (check(a->get_arg(i), is_true))
return true;
return false;
}
bool quick_checker::check_core(expr * n, bool is_true) {
SASSERT(m_manager.is_bool(n));
if (m_context.b_internalized(n) && m_context.is_relevant(n)) {
lbool val = m_context.get_assignment(n);
if (val != l_undef && is_true == (val == l_true))
return true;
else
return false;
}
if (!is_app(n))
return false;
app * a = to_app(n);
if (a->get_family_id() == m_manager.get_basic_family_id()) {
switch (a->get_decl_kind()) {
case OP_TRUE:
return is_true;
case OP_FALSE:
return !is_true;
case OP_NOT:
return check(a->get_arg(0), !is_true);
case OP_OR:
return is_true ? any_arg(a, true) : all_args(a, false);
case OP_AND:
return is_true ? all_args(a, true) : any_arg(a, false);
case OP_ITE:
if (check(a->get_arg(0), true))
return check(a->get_arg(1), is_true);
else if (check(a->get_arg(0), false))
return check(a->get_arg(2), is_true);
else
return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true);
case OP_EQ:
if (m_manager.is_iff(a)) {
if (is_true)
return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false));
else
return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true));
}
if (is_true) {
return canonize(a->get_arg(0)) == canonize(a->get_arg(1));
}
else {
expr * lhs = canonize(a->get_arg(0));
expr * rhs = canonize(a->get_arg(1));
if (m_context.e_internalized(lhs) && m_context.is_relevant(lhs) &&
m_context.e_internalized(rhs) && m_context.is_relevant(rhs) &&
m_context.get_enode(lhs)->get_root() != m_context.get_enode(rhs)->get_root())
return true;
return m_manager.are_distinct(lhs, rhs);
}
default:
break;
}
}
expr * new_a = canonize(a);
TRACE("quick_checker_canonizer", tout << "before:\n" << mk_pp(a, m_manager) << "\nafter:\n" << mk_pp(new_a, m_manager) << "\n";);
if (m_context.lit_internalized(new_a) && m_context.is_relevant(new_a)) {
lbool val = m_context.get_assignment(new_a);
if (val != l_undef)
return is_true == (val == l_true);
}
if (is_true && m_manager.is_true(new_a))
return true;
if (!is_true && m_manager.is_false(new_a))
return true;
return false;
}
bool quick_checker::check(expr * n, bool is_true) {
expr_bool_pair p(n, is_true);
bool r;
if (m_check_cache.find(p, r))
return r;
r = check_core(n, is_true);
m_check_cache.insert(p, r);
return r;
}
expr * quick_checker::canonize(expr * n) {
if (is_var(n)) {
unsigned idx = to_var(n)->get_idx();
if (idx >= m_num_bindings)
return n;
// VAR 0 is stored in the last position of m_bindings
return m_bindings[m_num_bindings - idx - 1]->get_root()->get_expr();
}
if (m_context.e_internalized(n))
return m_context.get_enode(n)->get_root()->get_expr();
if (!is_app(n) || to_app(n)->get_num_args() == 0)
return n;
expr * r;
if (m_canonize_cache.find(n, r))
return r;
bool has_arg_enodes = true;
ptr_buffer new_args;
ptr_buffer new_arg_enodes;
unsigned num_args = to_app(n)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = canonize(to_app(n)->get_arg(i));
new_args.push_back(arg);
if (m_context.e_internalized(arg))
new_arg_enodes.push_back(m_context.get_enode(arg));
else
has_arg_enodes = false;
}
if (has_arg_enodes) {
enode * e = m_context.get_enode_eq_to(to_app(n)->get_decl(), num_args, new_arg_enodes.data());
if (e) {
m_canonize_cache.insert(n, e->get_root()->get_expr());
return e->get_root()->get_expr();
}
}
// substitute by values in the model
for (unsigned i = 0; i < num_args; i++) {
expr * arg = new_args[i];
if (m_context.e_internalized(arg)) {
expr_ref new_value(m_manager);
if (m_context.get_value(m_context.get_enode(arg), new_value)) {
new_args[i] = new_value;
m_new_exprs.push_back(new_value);
}
}
}
expr_ref new_expr(m_manager);
new_expr = m_context.get_rewriter().mk_app(to_app(n)->get_decl(), num_args, new_args.data());
m_new_exprs.push_back(new_expr);
m_canonize_cache.insert(n, new_expr);
return new_expr;
}
};