diff --git a/lib/ast.h b/lib/ast.h index 685ddd4ad..8de390ee0 100644 --- a/lib/ast.h +++ b/lib/ast.h @@ -832,6 +832,7 @@ inline bool is_app(ast const * n) { return n->get_kind() == AST_APP; } inline bool is_var(ast const * n) { return n->get_kind() == AST_VAR; } inline bool is_quantifier(ast const * n) { return n->get_kind() == AST_QUANTIFIER; } inline bool is_forall(ast const * n) { return is_quantifier(n) && static_cast(n)->is_forall(); } +inline bool is_exists(ast const * n) { return is_quantifier(n) && static_cast(n)->is_exists(); } // ----------------------------------- // diff --git a/lib/dl_mk_coalesce.cpp b/lib/dl_mk_coalesce.cpp index c2ec0adc5..a19b2853c 100644 --- a/lib/dl_mk_coalesce.cpp +++ b/lib/dl_mk_coalesce.cpp @@ -20,8 +20,8 @@ Notes: Implements proof rule of the form: - a(x) & q(x) -> p(x) b(y) & q(y) -> p(y) - --------------------------------------------- + a(x) & q(x) -> p(x), b(y) & q(y) -> p(y) + ---------------------------------------------- (a(z) \/ b(z)) & q(z) -> p(z) diff --git a/lib/lib.vcxproj b/lib/lib.vcxproj index 01a33e4f7..70007e23b 100644 --- a/lib/lib.vcxproj +++ b/lib/lib.vcxproj @@ -753,6 +753,7 @@ + @@ -1160,6 +1161,7 @@ + diff --git a/lib/pdr_context.cpp b/lib/pdr_context.cpp index af5dbe54d..0bd594768 100644 --- a/lib/pdr_context.cpp +++ b/lib/pdr_context.cpp @@ -41,6 +41,7 @@ Notes: #include "model_smt2_pp.h" #include "dl_mk_rule_inliner.h" #include "ast_smt2_pp.h" +#include "qe_lite.h" namespace pdr { @@ -124,7 +125,7 @@ namespace pdr { datalog::rule const& pred_transformer::find_rule(model_core const& model) const { obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - TRACE("pdr", + TRACE("pdr_verbose", for (; it != end; ++it) { expr* pred = it->m_key; tout << mk_pp(pred, m) << ":\n"; @@ -394,7 +395,7 @@ namespace pdr { lbool is_sat = m_solver.check_conjunction_as_assumptions(n.state()); if (is_sat == l_true && core) { core->reset(); - model2cube(*model, *core); + model2cube(*model,*core); n.set_model(model); } return is_sat; @@ -522,6 +523,7 @@ namespace pdr { expr_ref_vector conj(m); app_ref_vector& var_reprs = *(alloc(app_ref_vector, m)); qinst* qi = 0; + ptr_vector aux_vars; unsigned ut_size = rule.get_uninterpreted_tail_size(); unsigned t_size = rule.get_tail_size(); @@ -534,7 +536,7 @@ namespace pdr { init_atom(pts, rule.get_tail(i), var_reprs, conj, i); } for (unsigned i = ut_size; i < t_size; ++i) { - ground_free_vars(rule.get_tail(i), var_reprs); + ground_free_vars(rule.get_tail(i), var_reprs, aux_vars); } SASSERT(check_filled(var_reprs)); expr_ref_vector tail(m); @@ -585,6 +587,7 @@ namespace pdr { m_rule2qinst.insert(&rule, qi); } m_rule2inst.insert(&rule,&var_reprs); + m_rule2vars.insert(&rule, aux_vars); } bool pred_transformer::check_filled(app_ref_vector const& v) const { @@ -595,7 +598,7 @@ namespace pdr { } // create constants for free variables in tail. - void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars) { + void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars) { ptr_vector sorts; get_free_vars(e, sorts); while (vars.size() < sorts.size()) { @@ -604,6 +607,7 @@ namespace pdr { for (unsigned i = 0; i < sorts.size(); ++i) { if (sorts[i] && !vars[i].get()) { vars[i] = m.mk_fresh_const("aux", sorts[i]); + aux_vars.push_back(vars[i].get()); } } } @@ -1099,6 +1103,7 @@ namespace pdr { m_inductive_lvl(0), m_cancel(false) { + m_use_model_generalizer = m_params.get_bool("use-model-generalizer", false); } context::~context() { @@ -1296,19 +1301,9 @@ namespace pdr { void context::init_model_generalizers(datalog::rule_set& rules) { reset_model_generalizers(); - classifier_proc classify(m, rules); - if (classify.is_bool_arith()) { - m_model_generalizers.push_back(alloc(bool_model_evaluation_generalizer, *this, m)); - } - else { + if (m_use_model_generalizer) { m_model_generalizers.push_back(alloc(model_evaluation_generalizer, *this, m)); } - if (m_params.get_bool(":use-farkas-model", false)) { - m_model_generalizers.push_back(alloc(model_farkas_generalizer, *this)); - } - if (m_params.get_bool(":use-precondition-generalizer", false)) { - m_model_generalizers.push_back(alloc(model_precond_generalizer, *this)); - } } void context::reset_core_generalizers() { @@ -1556,12 +1551,11 @@ namespace pdr { close_node(n); } else { - TRACE("pdr", tout << "node: " << &n << "\n"; - expr_ref cb(m.mk_and(cube.size(),cube.c_ptr()), m); - tout << mk_pp(cb.get(), m) << "\n";); + TRACE("pdr", tout << "node: " << &n << "\n";); for (unsigned i = 0; i < m_model_generalizers.size(); ++i) { (*m_model_generalizers[i])(n, cube); - } + } + create_children(n, m_pm.mk_and(cube)); } break; @@ -1634,7 +1628,11 @@ namespace pdr { } // create children states from model cube. - void context::create_children(model_node& n, expr* model) { + void context::create_children(model_node& n, expr* model) { + if (!m_use_model_generalizer) { + create_children2(n); + return; + } expr_ref_vector literals(m), sub_lits(m); expr_ref o_cube(m), n_cube(m); datalog::flatten_and(model, literals); @@ -1652,6 +1650,7 @@ namespace pdr { tout << preds[i]->get_name() << "\n"; } ); + for (unsigned i = 0; i < preds.size(); ++i) { pred_transformer& pt = *m_rels.find(preds[i]); SASSERT(pt.head() == preds[i]); @@ -1689,34 +1688,149 @@ namespace pdr { Goal is to find phi0(x0), phi1(x1) such that: - phi(x) & phi0(x0) & phi1(x1) => psi(x0, x1, x) + phi(x) & phi0(x0) & phi1(x1) => psi(x0, x1, x) + + or at least (ignoring psi alltogether): + + phi(x) & phi0(x0) & phi1(x1) => T(x0, x1, x) Strategy: + - Extract literals from T & phi using ternary simulation with M. + - resulting formula is Phi. + - perform cheap existential quantifier elimination on - exists x . T(x0,x1,x) & phi(x) + Phi <- exists x . Phi(x0,x1,x) (e.g., destructive equality resolution) + + - Sub-strategy 1: rename remaining x to fresh variables. + - Sub-strategy 2: replace remaining x to M(x). + + - For each literal L in result: + + - if L is x0 pure, add L to L0 + - if L is x1 pure, add L to L1 + - if L mixes x0, x1, add x1 = M(x1) to L1, add L(x1 |-> M(x1)) to L0 + + - Create sub-goals for L0 and L1. - - pull equalities that use - - */ - void context::create_children2(model_node& n, expr* psi) { + void context::create_children2(model_node& n) { SASSERT(n.level() > 0); - - model_core const& M = n.model(); - datalog::rule const& r = n.pt().find_rule(M); - expr* T = n.pt().get_transition(r); + + pred_transformer& pt = n.pt(); + model_ref M = n.model_ptr(); + datalog::rule const& r = pt.find_rule(*M); + expr* T = pt.get_transition(r); expr* phi = n.state(); - expr_ref_vector Ts(m); - datalog::flatten_and(T, Ts); + IF_VERBOSE(2, verbose_stream() << "Model:\n"; + model_smt2_pp(verbose_stream(), m, *M, 0); + verbose_stream() << "\n"; + verbose_stream() << "Transition:\n" << mk_pp(T, m) << "\n"; + verbose_stream() << "Phi:\n" << mk_pp(phi, m) << "\n";); + + model_evaluator mev(m); + expr_ref_vector mdl(m), forms(m); + forms.push_back(T); + forms.push_back(phi); + datalog::flatten_and(forms); + ptr_vector forms1(forms.size(), forms.c_ptr()); + expr_ref_vector Phi = mev.minimize_literals(forms1, M); ptr_vector preds; - n.pt().find_predecessors(r, preds); - n.pt().remove_predecessors(Ts); + pt.find_predecessors(r, preds); + pt.remove_predecessors(Phi); + + app_ref_vector vars(m); + unsigned sig_size = pt.head()->get_arity(); + for (unsigned i = 0; i < sig_size; ++i) { + vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); + } + ptr_vector aux_vars; + pt.get_aux_vars(r, aux_vars); + vars.append(aux_vars.size(), aux_vars.c_ptr()); + + qe_lite qe(m); + expr_ref phi1 = m_pm.mk_and(Phi); + qe(vars, phi1); + + IF_VERBOSE(2, + verbose_stream() << "Vars:\n"; + for (unsigned i = 0; i < vars.size(); ++i) { + verbose_stream() << mk_pp(vars[i].get(), m) << "\n"; + } + verbose_stream() << "Literals\n"; + verbose_stream() << mk_pp(m_pm.mk_and(Phi), m) << "\n"; + verbose_stream() << "Reduced\n" << mk_pp(phi1, m) << "\n";); + + if (!vars.empty()) { + // also fresh names for auxiliary variables in body? + expr_substitution sub(m); + expr_ref tmp(m); + proof_ref pr(m); + pr = m.mk_asserted(m.mk_true()); + + for (unsigned i = 0; i < vars.size(); ++i) { + M->eval(vars[i]->get_decl(), tmp); + sub.insert(vars[i].get(), tmp, pr); + } + scoped_ptr rep = mk_default_expr_replacer(m); + rep->set_substitution(&sub); + (*rep)(phi1); + IF_VERBOSE(2, verbose_stream() << "Projected:\n" << mk_pp(phi1, m) << "\n";); + } + Phi.reset(); + datalog::flatten_and(phi1, Phi); + unsigned_vector indices; + vector child_states; + child_states.resize(preds.size(), expr_ref_vector(m)); + for (unsigned i = 0; i < Phi.size(); ++i) { + m_pm.collect_indices(Phi[i].get(), indices); + if (indices.size() == 0) { + IF_VERBOSE(2, verbose_stream() << "Skipping " << mk_pp(Phi[i].get(), m) << "\n";); + } + else if (indices.size() == 1) { + child_states[indices.back()].push_back(Phi[i].get()); + } + else { + expr_substitution sub(m); + expr_ref tmp(m); + proof_ref pr(m); + pr = m.mk_asserted(m.mk_true()); + vector > vars; + m_pm.collect_variables(Phi[i].get(), vars); + SASSERT(vars.size() == indices.back()+1); + for (unsigned j = 1; j < indices.size(); ++j) { + ptr_vector const& vs = vars[indices[j]]; + for (unsigned k = 0; k < vs.size(); ++k) { + M->eval(vs[k]->get_decl(), tmp); + sub.insert(vs[k], tmp, pr); + child_states[indices[j]].push_back(m.mk_eq(vs[k], tmp)); + } + } + tmp = Phi[i].get(); + scoped_ptr rep = mk_default_expr_replacer(m); + rep->set_substitution(&sub); + (*rep)(tmp); + child_states[indices[0]].push_back(tmp); + } + } + + expr_ref n_cube(m); + for (unsigned i = 0; i < preds.size(); ++i) { + pred_transformer& pt = *m_rels.find(preds[i]); + SASSERT(pt.head() == preds[i]); + expr_ref o_cube = m_pm.mk_and(child_states[i]); + m_pm.formula_o2n(o_cube, n_cube, i); + model_node* child = alloc(model_node, &n, n_cube, pt, n.level()-1); + ++m_stats.m_num_nodes; + m_search.add_leaf(*child); + IF_VERBOSE(2, verbose_stream() << "Predecessor: " << mk_pp(o_cube, m) << "\n";); + } + check_pre_closed(n); + - // TBD ... TRACE("pdr", m_search.display(tout);); } diff --git a/lib/pdr_context.h b/lib/pdr_context.h index b2cd17f84..15124e2cb 100644 --- a/lib/pdr_context.h +++ b/lib/pdr_context.h @@ -60,6 +60,7 @@ namespace pdr { }; typedef obj_map rule2expr; + typedef obj_map > rule2apps; manager& pm; // pdr-manager ast_manager& m; // manager @@ -70,13 +71,14 @@ namespace pdr { ptr_vector m_rules; // rules used to derive transformer prop_solver m_solver; // solver context vector m_levels; // level formulas - expr_ref_vector m_invariants; // properties that are invariant. - obj_map m_prop2level; // map property to level where it occurs. + expr_ref_vector m_invariants; // properties that are invariant. + obj_map m_prop2level; // map property to level where it occurs. obj_map m_tag2rule; // map tag predicate to rule. - rule2expr m_rule2tag; // map rule to predicate tag. + rule2expr m_rule2tag; // map rule to predicate tag. qinst_map m_rule2qinst; // map tag to quantifier instantiation. rule2inst m_rule2inst; // map rules to instantiations of indices rule2expr m_rule2transition; // map rules to transition + rule2apps m_rule2vars; // map rule to auxiliary variables expr_ref m_transition; // transition relation. expr_ref m_initial_state; // initial state. reachable_cache m_reachable; @@ -94,7 +96,7 @@ namespace pdr { void init_rule(decl2rel const& pts, datalog::rule const& rule, expr_ref& init, ptr_vector& rules, expr_ref_vector& transition); void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx); - void ground_free_vars(expr* e, app_ref_vector& vars); + void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars); void model2cube(const model_core& md, func_decl * d, expr_ref_vector& res) const; void model2cube(app* c, expr* val, expr_ref_vector& res) const; @@ -137,6 +139,7 @@ namespace pdr { void find_predecessors(model_core const& model, ptr_vector& preds) const; datalog::rule const& find_rule(model_core const& model) const; expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } + void get_aux_vars(datalog::rule const& r, ptr_vector& vs) { m_rule2vars.find(&r, vs); } bool propagate_to_next_level(unsigned level); void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level. @@ -194,7 +197,8 @@ namespace pdr { ptr_vector const& children() { return m_children; } pred_transformer& pt() const { return m_pt; } model_node* parent() const { return m_parent; } - model_core const& model() const { return *m_model; } + model* model_ptr() const { return m_model.get(); } + model const& model() const { return *m_model; } unsigned index() const; bool is_closed() const { return m_closed; } @@ -301,7 +305,6 @@ namespace pdr { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; - front_end_params& m_fparams; params_ref const& m_params; @@ -314,6 +317,7 @@ namespace pdr { pred_transformer* m_query; model_search m_search; lbool m_last_result; + bool m_use_model_generalizer; unsigned m_inductive_lvl; ptr_vector m_model_generalizers; ptr_vector m_core_generalizers; @@ -332,8 +336,8 @@ namespace pdr { void check_pre_closed(model_node& n); void expand_node(model_node& n); lbool expand_state(model_node& n, expr_ref_vector& cube); - void create_children(model_node& n, expr* cube); - void create_children2(model_node& n, expr* cube); + void create_children(model_node& n, expr* model); + void create_children2(model_node& n); expr_ref mk_sat_answer() const; expr_ref mk_unsat_answer() const; diff --git a/lib/pdr_dl_interface.cpp b/lib/pdr_dl_interface.cpp index d7952fec3..cb7d88fd6 100644 --- a/lib/pdr_dl_interface.cpp +++ b/lib/pdr_dl_interface.cpp @@ -202,8 +202,7 @@ void dl_interface::collect_params(param_descrs& p) { p.insert(":inline-proofs", CPK_BOOL, "PDR: (default true) run PDR with proof mode turned on and extract Farkas coefficients directly (instead of creating a separate proof object when extracting coefficients)"); p.insert(":flexible-trace", CPK_BOOL, "PDR: (default false) allow PDR generate long counter-examples by extending candidate trace within search area"); p.insert(":unfold-rules", CPK_UINT, "PDR: (default 0) unfold rules statically using iterative squarring"); - PRIVATE_PARAMS(p.insert(":use-farkas-model", CPK_BOOL, "PDR: (default false) enable using Farkas generalization through model propagation");); - PRIVATE_PARAMS(p.insert(":use-precondition-generalizer", CPK_BOOL, "PDR: (default false) enable generalizations from weakest pre-conditions");); + p.insert(":use-model-generalizer", CPK_BOOL, "PDR: (default false) use model for backwards propagation (instead of symbolic simulation)"); PRIVATE_PARAMS(p.insert(":use-multicore-generalizer", CPK_BOOL, "PDR: (default false) extract multiple cores for blocking states");); PRIVATE_PARAMS(p.insert(":use-inductive-generalizer", CPK_BOOL, "PDR: (default true) generalize lemmas using induction strengthening");); PRIVATE_PARAMS(p.insert(":use-interpolants", CPK_BOOL, "PDR: (default false) use iZ3 interpolation for lemma generation");); diff --git a/lib/pdr_generalizers.cpp b/lib/pdr_generalizers.cpp index d3db59d39..0ba4f6260 100644 --- a/lib/pdr_generalizers.cpp +++ b/lib/pdr_generalizers.cpp @@ -28,65 +28,18 @@ Revision History: namespace pdr { - static void solve_for_next_vars(expr_ref& F, model_node& n, expr_substitution& sub) { - ast_manager& m = F.get_manager(); - manager& pm = n.pt().get_pdr_manager(); - const model_core & mdl = n.model(); - unsigned sz = mdl.get_num_constants(); - expr_ref_vector refs(m); - - for (unsigned i = 0; i < sz; i++) { - func_decl * d = mdl.get_constant(i); - expr_ref interp(m); - ptr_vector cs; - if (m.is_bool(d->get_range())) { - get_value_from_model(mdl, d, interp); - app* c = m.mk_const(d); - refs.push_back(c); - refs.push_back(interp); - sub.insert(c, interp); - } - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(F); - th_rewriter rw(m); - rw(F); - ptr_vector todo; - todo.push_back(F); - expr* e1, *e2; - while (!todo.empty()) { - expr* e = todo.back(); - todo.pop_back(); - if (m.is_and(e)) { - todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - } - else if ((m.is_eq(e, e1, e2) && pm.is_n(e1) && pm.is_o_formula(e2)) || - (m.is_eq(e, e2, e1) && pm.is_n(e1) && pm.is_o_formula(e2))) { - sub.insert(e1, e2); - TRACE("pdr", tout << mk_pp(e1, m) << " |-> " << mk_pp(e2, m) << "\n";); - } - } - } // // eliminate conjuncts from cube as long as state is satisfied. // void model_evaluation_generalizer::operator()(model_node& n, expr_ref_vector& cube) { - ptr_vector forms; + expr_ref_vector forms(cube.get_manager()); forms.push_back(n.state()); forms.push_back(n.pt().transition()); - m_model_evaluator.minimize_model(forms, cube); - } - - // - // eliminate conjuncts from cube as long as state is satisfied. - // - void bool_model_evaluation_generalizer::operator()(model_node& n, expr_ref_vector& cube) { - ptr_vector forms; - forms.push_back(n.state()); - forms.push_back(n.pt().transition()); - m_model_evaluator.minimize_model(forms, cube); + datalog::flatten_and(forms); + ptr_vector forms1(forms.size(), forms.c_ptr()); + model_ref mdl = n.model_ptr(); + m_model_evaluator.minimize_model(forms1, mdl, cube); } // @@ -121,10 +74,6 @@ namespace pdr { TRACE("pdr", tout << "old size: " << old_core_size << " new size: " << core.size() << "\n";); } - // - // extract multiple cores from unreachable state. - // - void core_multi_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { UNREACHABLE(); @@ -207,31 +156,6 @@ namespace pdr { m_farkas_learner.collect_statistics(st); } - void model_precond_generalizer::operator()(model_node& n, expr_ref_vector& cube) { - ast_manager& m = n.pt().get_manager(); - manager& pm = n.pt().get_pdr_manager(); - expr_ref A(m), state(m); - expr_ref_vector states(m); - A = n.pt().get_formulas(n.level(), true); - - // extract substitution for next-state variables. - expr_substitution sub(m); - solve_for_next_vars(A, n, sub); - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - A = m.mk_and(A, n.state()); - (*rep)(A); - - datalog::flatten_and(A, states); - - for (unsigned i = 0; i < states.size(); ++i) { - expr* s = states[i].get(); - if (pm.is_o_formula(s) && pm.is_homogenous_formula(s)) { - cube.push_back(s); - } - } - TRACE("pdr", for (unsigned i = 0; i < cube.size(); ++i) tout << mk_pp(cube[i].get(), m) << "\n";); - } /** < F, phi, i + 1 > @@ -567,78 +491,4 @@ namespace pdr { uses_level = true; } } - - - // - // cube => n.state() & formula - // so n.state() & cube & ~formula is unsat - // so weaken cube while result is still unsat. - // - void model_farkas_generalizer::operator()(model_node& n, expr_ref_vector& cube) { - ast_manager& m = n.pt().get_manager(); - manager& pm = n.pt().get_pdr_manager(); - front_end_params& p = m_ctx.get_fparams(); - farkas_learner learner(p, m); - expr_ref A0(m), A(m), B(m), state(m); - expr_ref_vector states(m); - - A0 = n.pt().get_formulas(n.level(), true); - - // extract substitution for next-state variables. - expr_substitution sub(m); - solve_for_next_vars(A0, n, sub); - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(A0); - A0 = m.mk_not(A0); - - state = n.state(); - (*rep)(state); - - datalog::flatten_and(state, states); - - ptr_vector preds; - n.pt().find_predecessors(n.model(), preds); - - TRACE("pdr", for (unsigned i = 0; i < cube.size(); ++i) tout << mk_pp(cube[i].get(), m) << "\n";); - - for (unsigned i = 0; i < preds.size(); ++i) { - pred_transformer& pt = m_ctx.get_pred_transformer(preds[i]); - SASSERT(pt.head() == preds[i]); - expr_ref_vector lemmas(m), o_cube(m), other(m), o_state(m), other_state(m); - pm.partition_o_atoms(cube, o_cube, other, i); - pm.partition_o_atoms(states, o_state, other_state, i); - TRACE("pdr", - tout << "cube:\n"; - for (unsigned i = 0; i < cube.size(); ++i) tout << mk_pp(cube[i].get(), m) << "\n"; - tout << "o_cube:\n"; - for (unsigned i = 0; i < o_cube.size(); ++i) tout << mk_pp(o_cube[i].get(), m) << "\n"; - tout << "other:\n"; - for (unsigned i = 0; i < other.size(); ++i) tout << mk_pp(other[i].get(), m) << "\n"; - tout << "o_state:\n"; - for (unsigned i = 0; i < o_state.size(); ++i) tout << mk_pp(o_state[i].get(), m) << "\n"; - tout << "other_state:\n"; - for (unsigned i = 0; i < other_state.size(); ++i) tout << mk_pp(other_state[i].get(), m) << "\n"; - ); - A = m.mk_and(A0, pm.mk_and(other), pm.mk_and(other_state)); - B = m.mk_and(pm.mk_and(o_cube), pm.mk_and(o_state)); - - TRACE("pdr", - tout << "A: " << mk_pp(A, m) << "\n"; - tout << "B: " << mk_pp(B, m) << "\n";); - - if (learner.get_lemma_guesses(A, B, lemmas)) { - cube.append(lemmas); - cube.append(o_state); - TRACE("pdr", - tout << "New lemmas:\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - tout << mk_pp(lemmas[i].get(), m) << "\n"; - } - ); - } - } - TRACE("pdr", for (unsigned i = 0; i < cube.size(); ++i) tout << mk_pp(cube[i].get(), m) << "\n";); - } - }; diff --git a/lib/pdr_generalizers.h b/lib/pdr_generalizers.h index 22d9784c7..2994beb96 100644 --- a/lib/pdr_generalizers.h +++ b/lib/pdr_generalizers.h @@ -25,11 +25,11 @@ Revision History: namespace pdr { - class bool_model_evaluation_generalizer : public model_generalizer { - ternary_model_evaluator m_model_evaluator; + class model_evaluation_generalizer : public model_generalizer { + model_evaluator m_model_evaluator; public: - bool_model_evaluation_generalizer(context& ctx, ast_manager& m) : model_generalizer(ctx), m_model_evaluator(m) {} - virtual ~bool_model_evaluation_generalizer() {} + model_evaluation_generalizer(context& ctx, ast_manager& m) : model_generalizer(ctx), m_model_evaluator(m) {} + virtual ~model_evaluation_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector& cube); }; @@ -50,28 +50,6 @@ namespace pdr { virtual void collect_statistics(statistics& st) const; }; - class model_precond_generalizer : public model_generalizer { - public: - model_precond_generalizer(context& ctx): model_generalizer(ctx) {} - virtual ~model_precond_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& cube); - }; - - class model_farkas_generalizer : public model_generalizer { - public: - model_farkas_generalizer(context& ctx) : model_generalizer(ctx) {} - virtual ~model_farkas_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& cube); - }; - - class model_evaluation_generalizer : public model_generalizer { - th_rewriter_model_evaluator m_model_evaluator; - public: - model_evaluation_generalizer(context& ctx, ast_manager& m) : model_generalizer(ctx), m_model_evaluator(m) {} - virtual ~model_evaluation_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& cube); - }; - class core_multi_generalizer : public core_generalizer { core_bool_inductive_generalizer m_gen; public: diff --git a/lib/pdr_manager.h b/lib/pdr_manager.h index e7bfb38c8..87eaa03bf 100644 --- a/lib/pdr_manager.h +++ b/lib/pdr_manager.h @@ -223,6 +223,20 @@ namespace pdr { bool is_homogenous_formula(expr * e) const { return m_mux.is_homogenous_formula(e); } + + /** + Collect indices used in expression. + */ + void collect_indices(expr* e, unsigned_vector& indices) const { + m_mux.collect_indices(e, indices); + } + + /** + Collect used variables of each index. + */ + void collect_variables(expr* e, vector >& vars) const { + m_mux.collect_variables(e, vars); + } /** Return true iff both s1 and s2 are either "n" or "o" of the same index. @@ -275,8 +289,6 @@ namespace pdr { bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value); bool try_get_state_decl_from_atom(expr * atom, func_decl *& state); - void get_state_cube_from_model(const model_core & mdl, expr_ref_vector & cube) const - { return m_mux.get_muxed_cube_from_model(mdl, cube); } std::string pp_model(const model_core & mdl) const { return m_mux.pp_model(mdl); } diff --git a/lib/pdr_sym_mux.cpp b/lib/pdr_sym_mux.cpp index f17177a7f..08b590e16 100644 --- a/lib/pdr_sym_mux.cpp +++ b/lib/pdr_sym_mux.cpp @@ -228,6 +228,76 @@ bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const return true; } +class sym_mux::index_collector { + sym_mux const& m_parent; + svector m_indices; +public: + index_collector(sym_mux const& s): + m_parent(s) {} + + void operator()(expr * e) { + if (is_app(e)) { + func_decl * sym = to_app(e)->get_decl(); + unsigned idx; + if (m_parent.try_get_index(sym, idx)) { + SASSERT(idx > 0); + --idx; + if (m_indices.size() <= idx) { + m_indices.resize(idx+1, false); + } + m_indices[idx] = true; + } + } + } + + void extract(unsigned_vector& indices) { + for (unsigned i = 0; i < m_indices.size(); ++i) { + if (m_indices[i]) { + indices.push_back(i); + } + } + } +}; + + + +void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const { + indices.reset(); + index_collector collector(*this); + for_each_expr(collector, m_visited, e); + m_visited.reset(); + collector.extract(indices); +} + +class sym_mux::variable_collector { + sym_mux const& m_parent; + vector >& m_vars; +public: + variable_collector(sym_mux const& s, vector >& vars): + m_parent(s), m_vars(vars) {} + + void operator()(expr * e) { + if (is_app(e)) { + func_decl * sym = to_app(e)->get_decl(); + unsigned idx; + if (m_parent.try_get_index(sym, idx)) { + SASSERT(idx > 0); + --idx; + if (m_vars.size() <= idx) { + m_vars.resize(idx+1, ptr_vector()); + } + m_vars[idx].push_back(to_app(e)); + } + } + } +}; + +void sym_mux::collect_variables(expr* e, vector >& vars) const { + vars.reset(); + variable_collector collector(*this, vars); + for_each_expr(collector, m_visited, e); + m_visited.reset(); +} class sym_mux::hmg_checker { const sym_mux & m_parent; @@ -445,38 +515,6 @@ void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const { } } -void sym_mux::get_muxed_cube_from_model(const model_core & mdl, expr_ref_vector & res) const -{ - res.reset(); - unsigned sz = mdl.get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = mdl.get_constant(i); - - if(!is_muxed(d) || m_non_model_syms.contains(get_primary(d))) { continue; } - - SASSERT(d->get_arity()==0); - expr_ref interp(m); - get_value_from_model(mdl, d, interp); - - app_ref constant(m.mk_const(d), m); - app_ref lit(m); - if(m.is_bool(d->get_range())) { - if(m.is_true(interp)) { - lit = constant; - } - else { - SASSERT(m.is_false(interp)); - lit = m.mk_not(constant); - } - } - else { - lit = m.mk_eq(constant, interp); - } - res.push_back(lit); - } - //LOGV(5, " got cube "< >& vars) const; /** Convert symbol sym which has to be of src_idx variant into variant tgt_idx. diff --git a/lib/pdr_util.cpp b/lib/pdr_util.cpp index 04ad64851..e9b15c790 100644 --- a/lib/pdr_util.cpp +++ b/lib/pdr_util.cpp @@ -86,127 +86,12 @@ std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& m) { ///////////////////////// -// model_evaluator_base +// model_evaluator // -void model_evaluator_base::minimize_model(ptr_vector const & formulas, expr_ref_vector & model) -{ - ast_manager & m = model.get_manager(); - bool has_unknown, has_false; - DEBUG_CODE( - check_model(formulas, model, has_unknown, has_false); - if (has_false) { - std::cout<<"formulas: "<& m_assignment; -public: - expr_rewriter_cfg(const obj_map& assignment) - : m_assignment(assignment) - { - } - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - return m_assignment.find(s,t); - } -}; - -void th_rewriter_model_evaluator::setup_assignment(expr_ref_vector const& model, obj_map& assignment) { - - for (unsigned i = 0; i < model.size(); ++i) { - expr * mlit = model[i]; //model literal - if (is_uninterp(mlit)) { - assignment.insert(mlit, m.mk_true()); - } - expr * arg1; - expr * arg2; - if (m.is_not(mlit, arg1)) { - assignment.insert(arg1, m.mk_false()); - } - else if (m.is_eq(mlit, arg1, arg2)) { - if (!is_uninterp(arg1)) { - std::swap(arg1, arg2); - } - if (is_uninterp(arg1)) { - assignment.insert(arg1, arg2); - } - else { - assignment.insert(mlit, m.mk_true()); - } - } - } -} - -void th_rewriter_model_evaluator::check_model(ptr_vector const & formulas, expr_ref_vector & model, - bool & has_unknown, bool & has_false) -{ - obj_map assignment; - - setup_assignment(model, assignment); - - expr_rewriter_cfg r_cfg(assignment); - rewriter_tpl rwr(m, false, r_cfg); - - has_false = false; - has_unknown = false; - - for (unsigned i = 0; i < formulas.size(); ++i) { - expr * form = formulas[i]; - expr_ref asgn_form(m); - rwr(form, asgn_form); - expr_ref simpl_form(m); - m_rewriter(asgn_form, simpl_form); - if (m.is_false(simpl_form)) { - has_false = true; - } - else if (!m.is_true(simpl_form)) { - IF_VERBOSE(7, verbose_stream() << "formula evaluated as unknown:\noriginal: " - << mk_pp(form, m) << "\n" - << "simplified: " << mk_pp(simpl_form,m) << "\n";); - has_unknown = true; - } - } - m_rewriter.reset(); -} - -bool ternary_model_evaluator::get_assignment(expr* e, expr*& var, expr*& val) { +bool model_evaluator::get_assignment(expr* e, expr*& var, expr*& val) { if (m.is_eq(e, var, val)) { if (!is_uninterp(var)) { std::swap(var, val); @@ -232,111 +117,211 @@ bool ternary_model_evaluator::get_assignment(expr* e, expr*& var, expr*& val) { } } - -void ternary_model_evaluator::add_model(expr* e) { - expr* var, *val; +void model_evaluator::assign_value(expr* e, expr* val) { rational r; - // SASSERT(is_unknown(e)); - if (get_assignment(e, var, val)) { - if (is_unknown(var)) { - if (m.is_true(val)) { - set_true(var); - } - else if (m.is_false(val)) { - set_false(var); - } - else if (m_arith.is_numeral(val, r)) { - set_value(var, r); - } - } + if (m.is_true(val)) { + set_true(e); + } + else if (m.is_false(val)) { + set_false(e); + } + else if (m_arith.is_numeral(val, r)) { + set_number(e, r); + } + else if (m.is_value(val)) { + set_value(e, val); } else { - TRACE("pdr_verbose", tout << "no value set of " << mk_pp(e, m) << "\n";); + IF_VERBOSE(2, verbose_stream() << "Not evaluated " << mk_pp(e, m) << "\n";); + TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << "\n";); + set_x(e); } } -void ternary_model_evaluator::del_model(expr* e) { - expr *var, *val; - if (get_assignment(e, var, val)) { - set_unknown(var); - if (!m.is_bool(var)) { - m_values.remove(var); - } - } - else { - TRACE("pdr_verbose", tout << "no value set of " << mk_pp(e, m) << "\n";); - } -} - -void ternary_model_evaluator::setup_model(expr_ref_vector const& model) { +void model_evaluator::setup_model(model_ref& model) { + m_numbers.reset(); m_values.reset(); - for (unsigned i = 0; i < model.size(); ++i) { - expr* e = model[i]; - if (is_unknown(e)) { - add_model(e); - } + m_model = model; + rational r; + unsigned sz = model->get_num_constants(); + for (unsigned i = 0; i < sz; i++) { + func_decl * d = model->get_constant(i); + expr* val = model->get_const_interp(d); + expr* e = m.mk_const(d); + m_refs.push_back(e); + assign_value(e, val); } + m_level1 = m1.get_level(); m_level2 = m2.get_level(); } -void ternary_model_evaluator::minimize_model(ptr_vector const & formulas, expr_ref_vector & model) -{ - setup_model(model); - - TRACE("pdr_verbose", - for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n"; - tout << "formulas\n"; - for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; - ); - - prune_by_cone_of_influence(formulas, model); - - // prune_by_probing(formulas, model); - +void model_evaluator::reset() { m1.reset(); m2.reset(); m_values.reset(); + m_visited.reset(); + m_numbers.reset(); + m_refs.reset(); + m_model = 0; } +void model_evaluator::minimize_model(ptr_vector const & formulas, model_ref& mdl, expr_ref_vector & model) { + setup_model(mdl); -void ternary_model_evaluator::prune_by_probing(ptr_vector const& formulas, expr_ref_vector& model) { - unsigned sz1 = model.size(); - for (unsigned i = 0; i < model.size(); ) { - expr_ref removed(model[i].get(), m); - if (i + 1 < model.size()) { - model[i] = model.back(); + TRACE("pdr_verbose", + tout << "formulas:\n"; + for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; + ); + + prune_by_cone_of_influence(formulas, model); + TRACE("pdr_verbose", + tout << "pruned model:\n"; + for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n";); + + reset(); + + DEBUG_CODE( + setup_model(mdl); + VERIFY(check_model(formulas)); + reset();); +} + +expr_ref_vector model_evaluator::minimize_literals(ptr_vector const& formulas, model_ref& mdl) { + + TRACE("pdr", + tout << "formulas:\n"; + for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; + ); + + setup_model(mdl); + expr_ref_vector result(m); + ptr_vector tocollect; + + collect(formulas, tocollect); + for (unsigned i = 0; i < tocollect.size(); ++i) { + expr* e = tocollect[i]; + SASSERT(m.is_bool(e)); + SASSERT(is_true(e) || is_false(e)); + if (is_true(e)) { + result.push_back(e); } - model.pop_back(); - del_model(removed); - m1.set_level(m_level1); - m2.set_level(m_level2); - - bool formulas_hold = check_model(formulas); - - m1.set_level(m_level1); - m2.set_level(m_level2); - - if (!formulas_hold) { - // if we introduced unknown, we restore the removed element - add_model(removed); - m_level1 = m1.get_level(); - m_level2 = m2.get_level(); - if (i < model.size()) { - model.push_back(model[i].get()); - model[i] = removed; - } - else { - model.push_back(removed); - } - i++; + else { + result.push_back(m.mk_not(e)); } } - TRACE("pdr", tout << sz1 << " ==> " << model.size() << "\n";); + reset(); + return result; } -void ternary_model_evaluator::prune_by_cone_of_influence(ptr_vector const & formulas, expr_ref_vector& model) { - ptr_vector todo, tocollect; +void model_evaluator::process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect) { + SASSERT(m.is_bool(e)); + SASSERT(is_true(e) || is_false(e)); + unsigned v = is_true(e); + unsigned sz = e->get_num_args(); + expr* const* args = e->get_args(); + if (e->get_family_id() == m.get_basic_family_id()) { + switch(e->get_decl_kind()) { + case OP_TRUE: + break; + case OP_FALSE: + break; + case OP_EQ: + case OP_IFF: + if (args[0] == args[1]) { + SASSERT(v); + // no-op + } + else if (!m.is_bool(args[0])) { + tocollect.push_back(e); + } + else { + todo.append(sz, args); + } + break; + case OP_DISTINCT: + tocollect.push_back(e); + break; + case OP_ITE: + if (args[1] == args[2]) { + tocollect.push_back(args[1]); + } + else if (is_true(args[1]) && is_true(args[2])) { + todo.append(2, args+1); + } + else if (is_false(args[1]) && is_false(args[2])) { + todo.append(2, args+1); + } + else if (is_true(args[0])) { + todo.append(2, args); + } + else { + SASSERT(is_false(args[0])); + todo.push_back(args[0]); + todo.push_back(args[2]); + } + break; + case OP_AND: + if (v) { + todo.append(sz, args); + } + else { + unsigned i = 0; + for (; !is_false(args[i]) && i < sz; ++i); + if (i == sz) { + fatal_error(1); + } + VERIFY(i < sz); + todo.push_back(args[i]); + } + break; + case OP_OR: + if (v) { + unsigned i = 0; + for (; !is_true(args[i]) && i < sz; ++i); + if (i == sz) { + fatal_error(1); + } + VERIFY(i < sz); + todo.push_back(args[i]); + } + else { + todo.append(sz, args); + } + break; + case OP_XOR: + case OP_NOT: + todo.append(sz, args); + break; + case OP_IMPLIES: + if (v) { + if (is_true(args[1])) { + todo.push_back(args[1]); + } + else if (is_false(args[0])) { + todo.push_back(args[0]); + } + else { + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + } + } + else { + todo.append(sz, args); + } + break; + default: + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + } + } + else { + tocollect.push_back(e); + } +} + +void model_evaluator::collect(ptr_vector const& formulas, ptr_vector& tocollect) { + ptr_vector todo; todo.append(formulas); m_visited.reset(); m1.set_level(m_level1); @@ -347,116 +332,21 @@ void ternary_model_evaluator::prune_by_cone_of_influence(ptr_vector const while (!todo.empty()) { app* e = to_app(todo.back()); todo.pop_back(); - if (m_visited.is_marked(e)) { - continue; + if (!m_visited.is_marked(e)) { + process_formula(e, todo, tocollect); + m_visited.mark(e, true); } - unsigned v = is_true(e); - SASSERT(m.is_bool(e)); - SASSERT(is_true(e) || is_false(e)); - unsigned sz = e->get_num_args(); - expr* const* args = e->get_args(); - if (e->get_family_id() == m.get_basic_family_id()) { - switch(e->get_decl_kind()) { - case OP_TRUE: - break; - case OP_FALSE: - break; - case OP_EQ: - case OP_IFF: - if (e->get_arg(0) == e->get_arg(1)) { - // no-op - } - else if (!m.is_bool(e->get_arg(0))) { - tocollect.push_back(e); - } - else { - todo.append(sz, args); - } - break; - case OP_DISTINCT: - tocollect.push_back(e); - break; - case OP_ITE: - if (args[1] == args[2]) { - // - } - else if (is_true(args[1]) && is_true(args[2])) { - todo.append(2, args+1); - } - else if (is_false(args[2]) && is_false(args[2])) { - todo.append(2, args+1); - } - else if (is_true(args[0])) { - todo.push_back(args[0]); - todo.push_back(args[1]); - } - else { - SASSERT(is_false(args[0])); - todo.push_back(args[0]); - todo.push_back(args[2]); - } - break; - case OP_AND: - if (v) { - todo.append(sz, args); - } - else { - unsigned i = 0; - for (; !is_false(args[i]) && i < sz; ++i); - if (i == sz) { - fatal_error(1); - } - VERIFY(i < sz); - todo.push_back(args[i]); - } - break; - case OP_OR: - if (v) { - unsigned i = 0; - for (; !is_true(args[i]) && i < sz; ++i); - if (i == sz) { - fatal_error(1); - } - VERIFY(i < sz); - todo.push_back(args[i]); - } - else { - todo.append(sz, args); - } - break; - case OP_XOR: - case OP_NOT: - todo.append(sz, args); - break; - case OP_IMPLIES: - if (v) { - if (is_true(args[1])) { - todo.push_back(args[1]); - } - else if (is_false(args[0])) { - todo.push_back(args[0]); - } - else { - UNREACHABLE(); - } - } - else { - todo.append(sz, args); - } - break; - default: - UNREACHABLE(); - } - } - else { - tocollect.push_back(e); - } - m_visited.mark(e, true); } - m1.set_level(m_level1); - m2.set_level(m_level2); m_visited.reset(); - for (unsigned i = 0; i < tocollect.size(); ++i) { +} + +void model_evaluator::prune_by_cone_of_influence(ptr_vector const & formulas, expr_ref_vector& model) { + ptr_vector tocollect; + collect(formulas, tocollect); + m1.reset(); + m2.reset(); + for (unsigned i = 0; i < tocollect.size(); ++i) { + TRACE("pdr_verbose", tout << "collect: " << mk_pp(tocollect[i], m) << "\n";); for_each_expr(*this, m_visited, tocollect[i]); } unsigned sz1 = model.size(); @@ -464,7 +354,6 @@ void ternary_model_evaluator::prune_by_cone_of_influence(ptr_vector const expr* e = model[i].get(), *var, *val; if (get_assignment(e, var, val)) { if (!m_visited.is_marked(var)) { - del_model(e); model[i] = model.back(); model.pop_back(); --i; @@ -475,17 +364,341 @@ void ternary_model_evaluator::prune_by_cone_of_influence(ptr_vector const TRACE("pdr", tout << sz1 << " ==> " << model.size() << "\n";); } +void model_evaluator::eval_arith(app* e) { + rational r, r2; -bool ternary_model_evaluator::check_model(ptr_vector const& formulas) { +#define ARG1 e->get_arg(0) +#define ARG2 e->get_arg(1) + + unsigned arity = e->get_num_args(); + for (unsigned i = 0; i < arity; ++i) { + expr* arg = e->get_arg(i); + if (is_x(arg)) { + set_x(e); + return; + } + SASSERT(!is_unknown(arg)); + } + switch(e->get_decl_kind()) { + case OP_NUM: + VERIFY(m_arith.is_numeral(e, r)); + set_number(e, r); + break; + case OP_IRRATIONAL_ALGEBRAIC_NUM: + set_x(e); + break; + case OP_LE: + set_bool(e, get_number(ARG1) <= get_number(ARG2)); + break; + case OP_GE: + set_bool(e, get_number(ARG1) >= get_number(ARG2)); + break; + case OP_LT: + set_bool(e, get_number(ARG1) < get_number(ARG2)); + break; + case OP_GT: + set_bool(e, get_number(ARG1) > get_number(ARG2)); + break; + case OP_ADD: + r = rational::zero(); + for (unsigned i = 0; i < arity; ++i) { + r += get_number(e->get_arg(i)); + } + set_number(e, r); + break; + case OP_SUB: + r = get_number(e->get_arg(0)); + for (unsigned i = 1; i < arity; ++i) { + r -= get_number(e->get_arg(i)); + } + set_number(e, r); + break; + case OP_UMINUS: + SASSERT(arity == 1); + set_number(e, get_number(e->get_arg(0))); + break; + case OP_MUL: + r = rational::one(); + for (unsigned i = 0; i < arity; ++i) { + r *= get_number(e->get_arg(i)); + } + set_number(e, r); + break; + case OP_DIV: + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + set_number(e, get_number(ARG1) / r); + } + break; + case OP_IDIV: + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + set_number(e, div(get_number(ARG1), r)); + } + break; + case OP_REM: + // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + r2 = mod(get_number(ARG1), r); + if (r.is_neg()) r2.neg(); + set_number(e, r2); + } + break; + case OP_MOD: + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + set_number(e, mod(get_number(ARG1), r)); + } + break; + case OP_TO_REAL: + SASSERT(arity == 1); + set_number(e, get_number(ARG1)); + break; + case OP_TO_INT: + SASSERT(arity == 1); + set_number(e, floor(get_number(ARG1))); + break; + case OP_IS_INT: + SASSERT(arity == 1); + set_bool(e, get_number(ARG1).is_int()); + break; + case OP_POWER: + set_x(e); + break; + default: + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + break; + } +} + +void model_evaluator::inherit_value(expr* e, expr* v) { + SASSERT(!is_unknown(v)); + SASSERT(m.get_sort(e) == m.get_sort(v)); + if (m.is_bool(e)) { + SASSERT(m.is_bool(v)); + if (is_true(v)) set_true(e); + else if (is_false(v)) set_false(e); + else set_x(e); + } + else if (m_arith.is_int_real(e)) { + set_number(e, get_number(v)); + } + else if (m.is_value(v)) { + set_value(e, v); + } + else { + set_x(e); + } +} + +void model_evaluator::eval_iff(app* e, expr* arg1, expr* arg2) { + if (arg1 == arg2) { + set_true(e); + } + else if (is_x(arg1) || is_x(arg2)) { + set_x(e); + } + else { + bool val = is_true(arg1) == is_true(arg2); + SASSERT(val == (is_false(arg1) == is_false(arg2))); + if (val) { + set_true(e); + } + else { + set_false(e); + } + } +} + +void model_evaluator::eval_basic(app* e) { + expr* arg1, *arg2; + expr *argCond, *argThen, *argElse, *arg; + bool has_x = false; + unsigned arity = e->get_num_args(); + switch(e->get_decl_kind()) { + case OP_AND: + for (unsigned j = 0; j < arity; ++j) { + expr * arg = e->get_arg(j); + if (is_false(arg)) { + set_false(e); + return; + } + else if (is_x(arg)) { + has_x = true; + } + else { + SASSERT(is_true(arg)); + } + } + if (has_x) { + set_x(e); + } + else { + set_true(e); + } + break; + case OP_OR: + for (unsigned j = 0; j < arity; ++j) { + expr * arg = e->get_arg(j); + if (is_true(arg)) { + set_true(e); + return; + } + else if (is_x(arg)) { + has_x = true; + } + else { + SASSERT(is_false(arg)); + } + } + if (has_x) { + set_x(e); + } + else { + set_false(e); + } + break; + case OP_NOT: + VERIFY(m.is_not(e, arg)); + if (is_true(arg)) { + set_false(e); + } + else if (is_false(arg)) { + set_true(e); + } + else { + SASSERT(is_x(arg)); + set_x(e); + } + break; + case OP_IMPLIES: + VERIFY(m.is_implies(e, arg1, arg2)); + if (is_false(arg1) || is_true(arg2)) { + set_true(e); + } + else if (arg1 == arg2) { + set_true(e); + } + else if (is_true(arg1) && is_false(arg2)) { + set_false(e); + } + else { + SASSERT(is_x(arg1) || is_x(arg2)); + set_x(e); + } + break; + case OP_IFF: + VERIFY(m.is_iff(e, arg1, arg2)); + eval_iff(e, arg1, arg2); + break; + case OP_ITE: + VERIFY(m.is_ite(e, argCond, argThen, argElse)); + if (is_true(argCond)) { + inherit_value(e, argThen); + } + else if (is_false(argCond)) { + inherit_value(e, argElse); + } + else if (argThen == argElse) { + inherit_value(e, argThen); + } + else if (m.is_bool(e)) { + SASSERT(is_x(argCond)); + if (is_x(argThen) || is_x(argElse)) { + set_x(e); + } + else if (is_true(argThen) == is_true(argElse)) { + inherit_value(e, argThen); + } + else { + set_x(e); + } + } + else { + set_x(e); + } + break; + case OP_TRUE: + set_true(e); + break; + case OP_FALSE: + set_false(e); + break; + case OP_EQ: + VERIFY(m.is_eq(e, arg1, arg2)); + if (m.is_bool(arg1)) { + eval_iff(e, arg1, arg2); + } + else if (arg1 == arg2) { + set_true(e); + } + else if (is_x(arg1) || is_x(arg2)) { + set_x(e); + } + else if (m_arith.is_int_real(arg1)) { + set_bool(e, get_number(arg1) == get_number(arg2)); + } + else { + expr* e1 = get_value(arg1); + expr* e2 = get_value(arg2); + if (m.is_value(e1) && m.is_value(e2)) { + set_bool(e, e1 == e2); + } + else { + set_x(e); + } + } + break; + case OP_DISTINCT: { + vector values; + for (unsigned i = 0; i < arity; ++i) { + expr* arg = e->get_arg(i); + if (is_x(arg)) { + set_x(e); + return; + } + values.push_back(get_number(arg)); + } + std::sort(values.begin(), values.end()); + for (unsigned i = 0; i + 1 < values.size(); ++i) { + if (values[i] == values[i+1]) { + set_false(e); + return; + } + } + set_true(e); + break; + } + default: + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + } +} + +bool model_evaluator::check_model(ptr_vector const& formulas) { ptr_vector todo; assign_vector_with_casting(todo, formulas); - expr *argCond, *argThen, *argElse, *arg; - rational r, r2; - - while(!todo.empty()) { + while (!todo.empty()) { expr * curr_e = todo.back(); - unsigned pre_curr_depth = todo.size()-1; if (!is_app(curr_e)) { todo.pop_back(); @@ -493,381 +706,38 @@ bool ternary_model_evaluator::check_model(ptr_vector const& formulas) { } app * curr = to_app(curr_e); -#define ARG1 curr->get_arg(0) -#define ARG2 curr->get_arg(1) - if (!is_unknown(curr)) { todo.pop_back(); continue; } unsigned arity = curr->get_num_args(); - - if (curr->get_family_id() == m_arith.get_family_id()) { - bool all_set = true, some_x = false; - for (unsigned i = 0; !some_x && i < arity; ++i) { - expr* arg = curr->get_arg(i); - if (is_unknown(arg)) { - todo.push_back(arg); - all_set = false; - } - else if (is_x(arg)) { - some_x = true; - } - } - if (some_x) { - set_x(curr); - } - else if (!all_set) { - continue; - } - else { - switch(curr->get_decl_kind()) { - case OP_NUM: - VERIFY(m_arith.is_numeral(curr,r)); - set_value(curr, r); - break; - case OP_IRRATIONAL_ALGEBRAIC_NUM: - set_x(curr); - break; - case OP_LE: - set_bool(curr, get_value(ARG1) <= get_value(ARG2)); - break; - case OP_GE: - set_bool(curr, get_value(ARG1) >= get_value(ARG2)); - break; - case OP_LT: - set_bool(curr, get_value(ARG1) < get_value(ARG2)); - break; - case OP_GT: - set_bool(curr, get_value(ARG1) > get_value(ARG2)); - break; - case OP_ADD: - r = rational::zero(); - for (unsigned i = 0; i < arity; ++i) { - r += get_value(curr->get_arg(i)); - } - set_value(curr, r); - break; - case OP_SUB: - r = get_value(curr->get_arg(0)); - for (unsigned i = 1; i < arity; ++i) { - r -= get_value(curr->get_arg(i)); - } - set_value(curr, r); - break; - case OP_UMINUS: - SASSERT(arity == 1); - set_value(curr, get_value(curr->get_arg(0))); - break; - case OP_MUL: - r = rational::one(); - for (unsigned i = 0; i < arity; ++i) { - r *= get_value(curr->get_arg(i)); - } - set_value(curr, r); - break; - case OP_DIV: - SASSERT(arity == 2); - r = get_value(ARG2); - if (r.is_zero()) { - set_x(curr); - } - else { - set_value(curr, get_value(ARG1) / r); - } - break; - case OP_IDIV: - SASSERT(arity == 2); - r = get_value(ARG2); - if (r.is_zero()) { - set_x(curr); - } - else { - set_value(curr, div(get_value(ARG1), r)); - } - break; - case OP_REM: - // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) - SASSERT(arity == 2); - r = get_value(ARG2); - if (r.is_zero()) { - set_x(curr); - } - else { - r2 = mod(get_value(ARG1), r); - if (r.is_neg()) r2.neg(); - set_value(curr, r2); - } - break; - case OP_MOD: - SASSERT(arity == 2); - r = get_value(ARG2); - if (r.is_zero()) { - set_x(curr); - } - else { - set_value(curr, mod(get_value(ARG1), r)); - } - break; - case OP_TO_REAL: - SASSERT(arity == 1); - set_value(curr, get_value(ARG1)); - break; - case OP_TO_INT: - SASSERT(arity == 1); - set_value(curr, floor(get_value(ARG1))); - break; - case OP_IS_INT: - SASSERT(arity == 1); - set_bool(curr, get_value(ARG1).is_int()); - break; - case OP_POWER: - set_x(curr); - break; - default: - UNREACHABLE(); - break; - } + for (unsigned i = 0; i < arity; ++i) { + if (is_unknown(curr->get_arg(i))) { + todo.push_back(curr->get_arg(i)); } } - else if (curr->get_family_id() == m_bv.get_family_id()) { - throw default_exception("PDR engine currently does not support bit-vectors"); + if (todo.back() != curr) { + continue; + } + todo.pop_back(); + if (curr->get_family_id() == m_arith.get_family_id()) { + eval_arith(curr); } else if (curr->get_family_id() == m.get_basic_family_id()) { - expr* arg1, *arg2; - if (m.is_and(curr)) { - // we're adding unknowns on the top of the todo stack, if there is none added, - // all arguments were known - bool has_x = false, has_false = false; - unsigned sz = todo.size(); - for (unsigned j = 0; !has_false && j < arity; ++j) { - expr * arg = curr->get_arg(j); - if (is_false(arg)) { - has_false = true; - } - else if (is_x(arg)) { - has_x = true; - } - else if (is_unknown(arg)) { - todo.push_back(arg); - } - } - if (has_false) { - todo.resize(sz); - set_false(curr); - } - else { - if (todo.back() != curr) { - continue; - } - else if (has_x) { - set_x(curr); - } - else { - set_true(curr); - } - } - } - else if (m.is_or(curr)) { - bool has_x = false, has_true = false; - unsigned sz = todo.size(); - for (unsigned j = 0; !has_true && j < arity; ++j) { - expr * arg = curr->get_arg(j); - if (is_true(arg)) { - has_true = true; - } - else if (is_x(arg)) { - has_x = true; - } - else if (is_unknown(arg)) { - todo.push_back(arg); - } - } - if (has_true) { - todo.resize(sz); - set_true(curr); - } - else { - if (todo.back() != curr) { - continue; - } - else if (has_x) { - set_x(curr); - } - else { - set_false(curr); - } - } - } - else if (m.is_not(curr, arg)) { - if (is_unknown(arg)) { - todo.push_back(arg); - continue; - } - if (is_true(arg)) { - set_false(curr); - } - else if (is_false(arg)) { - set_true(curr); - } - else { - SASSERT(is_x(arg)); - set_x(curr); - } - } - else if (m.is_implies(curr, arg1, arg2)) { - if (is_false(arg1) || is_true(arg2)) { - set_true(curr); - } - else if (is_unknown(arg1) || is_unknown(arg2)) { - if (is_unknown(arg1)) { todo.push_back(arg1); } - if (is_unknown(arg2)) { todo.push_back(arg2); } - continue; - } - else if (is_true(arg1) && is_false(arg2)) { - set_false(curr); - } - else { - SASSERT(is_x(arg1) || is_x(arg2)); - set_x(curr); - } - } - else if (m.is_iff(curr, arg1, arg2) || - (m.is_eq(curr, arg1, arg2) && m.is_bool(arg1))) { - if (is_x(arg1) || is_x(arg2)) { - set_x(curr); - } - else if (is_unknown(arg1) || is_unknown(arg2)) { - if (is_unknown(arg1)) { todo.push_back(arg1); } - if (is_unknown(arg2)) { todo.push_back(arg2); } - continue; - } - else { - bool val = is_true(arg1)==is_true(arg2); - SASSERT(val == (is_false(arg1)==is_false(arg2))); - if (val) { - set_true(curr); - } - else { - set_false(curr); - } - } - } - else if (m.is_ite(curr, argCond, argThen, argElse) && m.is_bool(argThen)) { - if (is_true(argCond)) { - if (is_true(argThen)) { set_true(curr); } - else if (is_false(argThen)) { set_false(curr); } - else if (is_x(argThen)) { set_x(curr); } - else { - todo.push_back(argThen); - continue; - } - } - else if (is_false(argCond)) { - if (is_true(argElse)) { set_true(curr); } - else if (is_false(argElse)) { set_false(curr); } - else if (is_x(argElse)) { set_x(curr); } - else { - todo.push_back(argElse); - continue; - } - } - else if (is_true(argThen) && is_true(argElse)) { - set_true(curr); - } - else if (is_false(argThen) && is_false(argElse)) { - set_false(curr); - } - else if (is_x(argCond) && (is_x(argThen) || is_x(argElse)) ) { - set_x(curr); - } else if (is_unknown(argCond) || is_unknown(argThen) || is_unknown(argElse)) { - if (is_unknown(argCond)) { todo.push_back(argCond); } - if (is_unknown(argThen)) { todo.push_back(argThen); } - if (is_unknown(argElse)) { todo.push_back(argElse); } - continue; - } - else { - SASSERT(is_x(argCond)); - SASSERT((is_true(argThen) && is_false(argElse)) || - (is_false(argThen) && is_true(argElse))); - set_x(curr); - } - } - else if (m.is_true(curr)) { - set_true(curr); - } - else if (m.is_false(curr)) { - set_false(curr); - } - else if (m.is_eq(curr, arg1, arg2) && arg1 == arg2) { - set_true(curr); - } - else if (m.is_eq(curr, arg1, arg2)) { - if (is_unknown(arg1)) { - todo.push_back(arg1); - } - if (is_unknown(arg2)) { - todo.push_back(arg2); - } - if (curr != todo.back()) { - continue; - } - if (is_x(arg1) || is_x(arg2)) { - set_x(curr); - } - else { - set_bool(curr, get_value(arg1) == get_value(arg2)); - } - } - else if (m.is_ite(curr, argCond, argThen, argElse) && m_arith.is_int_real(argThen)) { - if (is_true(argCond) || (argThen == argElse)) { - if (is_unknown(argThen)) { - todo.push_back(argThen); - continue; - } - if (is_x(argThen)) { - set_x(curr); - } - else { - set_value(curr, get_value(argThen)); - } - } - else if (is_false(argCond)) { - if (is_unknown(argElse)) { - todo.push_back(argElse); - continue; - } - if (is_x(argElse)) { - set_x(curr); - } - else { - set_value(curr, get_value(argElse)); - } - } - else if (is_unknown(argCond)) { - todo.push_back(argCond); - continue; - } - else { - set_x(curr); - } - } - else { - UNREACHABLE(); - } + eval_basic(curr); } else { - TRACE("pdr_verbse", tout << "Not evaluated " << mk_pp(curr, m) << "\n";); - set_x(curr); + expr_ref vl(m); + m_model->eval(curr, vl); + assign_value(curr, vl); } IF_VERBOSE(35,verbose_stream() << "assigned "< const& formulas) { tout << "formula is " << (is_true(form) ? "true" : is_false(form) ? "false" : "unknown") << "\n" <get_arity()==0); - expr_ref interp(m); - pdr::get_value_from_model(mdl, d, interp); - - app_ref constant(m.mk_const(d), m); - app_ref lit(m); - if (m.is_bool(d->get_range())) { - if (m.is_true(interp)) { - lit = constant; - } - else { - SASSERT(m.is_false(interp)); - lit = m.mk_not(constant); - } - } - else { - lit = m.mk_eq(constant, interp); - } - res.push_back(lit); - } -} } diff --git a/lib/pdr_util.h b/lib/pdr_util.h index f3cddd130..e1eec3410 100644 --- a/lib/pdr_util.h +++ b/lib/pdr_util.h @@ -170,34 +170,15 @@ void vect_set_union(ref_vector & tgt, ref_vector & src, Comp } -class model_evaluator_base { -protected: - virtual void check_model(ptr_vector const & formulas, - expr_ref_vector & model, bool & has_unknown, bool & has_false) = 0; -public: - virtual void minimize_model(ptr_vector const & formulas, expr_ref_vector & model); -}; -class th_rewriter_model_evaluator : public model_evaluator_base { - class expr_rewriter_cfg; - ast_manager& m; - th_rewriter m_rewriter; - - void setup_assignment(expr_ref_vector const& model, obj_map& assignment); - -protected: - virtual void check_model(ptr_vector const & formulas, - expr_ref_vector & model, bool & has_unknown, - bool & has_false); -public: - th_rewriter_model_evaluator(ast_manager& m) : m(m), m_rewriter(m) {} -}; - -class ternary_model_evaluator : public model_evaluator_base { +class model_evaluator { ast_manager& m; arith_util m_arith; bv_util m_bv; - obj_map m_values; + obj_map m_numbers; + expr_ref_vector m_refs; + obj_map m_values; + model_ref m_model; //00 -- non-visited //01 -- X @@ -209,13 +190,18 @@ class ternary_model_evaluator : public model_evaluator_base { unsigned m_level2; expr_mark m_visited; - void setup_model(expr_ref_vector const& model); - void add_model(expr* e); - void del_model(expr* e); - + + void reset(); + void setup_model(model_ref& model); + void assign_value(expr* e, expr* v); bool get_assignment(expr* e, expr*& var, expr*& val); + void collect(ptr_vector const& formulas, ptr_vector& tocollect); + void process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect); void prune_by_cone_of_influence(ptr_vector const & formulas, expr_ref_vector& model); - void prune_by_probing(ptr_vector const & formulas, expr_ref_vector& model); + void eval_arith(app* e); + void eval_basic(app* e); + void eval_iff(app* e, expr* arg1, expr* arg2); + void inherit_value(expr* e, expr* v); //00 -- non-visited //01 -- X @@ -231,20 +217,27 @@ class ternary_model_evaluator : public model_evaluator_base { inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); } inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } } - inline rational const& get_value(expr* x) const { return m_values.find(x); } - inline void set_value(expr* x, rational const& v) { set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_values.insert(x,v); } - + inline rational const& get_number(expr* x) const { return m_numbers.find(x); } + inline void set_number(expr* x, rational const& v) { set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v); } + inline expr* get_value(expr* x) { return m_values.find(x); } + inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); } + protected: + bool check_model(ptr_vector const & formulas); - virtual void check_model(ptr_vector const & formulas, expr_ref_vector & model, - bool & has_unknown, bool & has_false) { - UNREACHABLE(); - } public: - ternary_model_evaluator(ast_manager& m) : m(m), m_arith(m), m_bv(m) {} - virtual void minimize_model(ptr_vector const & formulas, expr_ref_vector & model); + model_evaluator(ast_manager& m) : m(m), m_arith(m), m_bv(m), m_refs(m) {} + + virtual void minimize_model(ptr_vector const & formulas, model_ref& mdl, expr_ref_vector& model); + + /** + \brief extract literals from formulas that satisfy formulas. + + \pre model satisfies formulas + */ + expr_ref_vector minimize_literals(ptr_vector const & formulas, model_ref& mdl); // for_each_expr visitor. void operator()(expr* e) {} @@ -252,11 +245,6 @@ public: void get_value_from_model(const model_core & mdl, func_decl * f, expr_ref& res); -/** -If the solver argument is non-zero, we will exclude its auxiliary symbols from the generated cube. -*/ -void get_cube_from_model(const model_core & mdl, expr_ref_vector & res, pdr::prop_solver& solver); - } #endif