From 0da0fa2b2761f9cec1a9c1ce9837c14eaa4252e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 29 Oct 2022 13:42:57 -0700 Subject: [PATCH 01/57] #6429 --- src/math/simplex/model_based_opt.cpp | 2 +- src/qe/mbp/mbp_arith.cpp | 38 ++++++++++++++++------------ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index ac7e89d5b..9e7ac75ce 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -1299,7 +1299,7 @@ namespace opt { def result; unsigned_vector div_rows(_div_rows), mod_rows(_mod_rows); SASSERT(!div_rows.empty() || !mod_rows.empty()); - TRACE("opt", display(tout << "solve_div " << x << "\n")); + TRACE("opt", display(tout << "solve_div v" << x << "\n")); rational K(1); for (unsigned ri : div_rows) diff --git a/src/qe/mbp/mbp_arith.cpp b/src/qe/mbp/mbp_arith.cpp index e97eb4ef7..5d9d3c19c 100644 --- a/src/qe/mbp/mbp_arith.cpp +++ b/src/qe/mbp/mbp_arith.cpp @@ -421,15 +421,23 @@ namespace mbp { mbo.display(tout);); vector defs = mbo.project(real_vars.size(), real_vars.data(), compute_def); + vector rows; + u_map def_vars; mbo.get_live_rows(rows); - rows2fmls(rows, index2expr, fmls); + for (row const& r : rows) { + if (r.m_type == opt::t_mod) + def_vars.insert(r.m_id, r); + else if (r.m_type == opt::t_div) + def_vars.insert(r.m_id, r); + } + rows2fmls(def_vars, rows, index2expr, fmls); TRACE("qe", mbo.display(tout << "mbo result\n"); for (auto const& d : defs) tout << "def: " << d << "\n"; tout << fmls << "\n";); if (compute_def) - optdefs2mbpdef(defs, index2expr, real_vars, result); + optdefs2mbpdef(def_vars, defs, index2expr, real_vars, result); if (m_apply_projection && !apply_projection(eval, result, fmls)) return false; @@ -442,7 +450,7 @@ namespace mbp { return true; } - void optdefs2mbpdef(vector const& defs, ptr_vector const& index2expr, unsigned_vector const& real_vars, vector& result) { + void optdefs2mbpdef(u_map const& def_vars, vector const& defs, ptr_vector const& index2expr, unsigned_vector const& real_vars, vector& result) { SASSERT(defs.size() == real_vars.size()); for (unsigned i = 0; i < defs.size(); ++i) { auto const& d = defs[i]; @@ -450,8 +458,12 @@ namespace mbp { bool is_int = a.is_int(x); expr_ref_vector ts(m); expr_ref t(m); - for (var const& v : d.m_vars) - ts.push_back(var2expr(index2expr, v)); + for (var const& v : d.m_vars) { + t = id2expr(def_vars, index2expr, v.m_id); + if (v.m_coeff != 1) + t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); + ts.push_back(t); + } if (!d.m_coeff.is_zero()) ts.push_back(a.mk_numeral(d.m_coeff, is_int)); if (ts.empty()) @@ -492,7 +504,8 @@ namespace mbp { t = a.mk_int(mod(r.m_coeff, r.m_mod)); return t; } - ts.push_back(a.mk_int(r.m_coeff)); + if (r.m_coeff != 0) + ts.push_back(a.mk_int(r.m_coeff)); t = mk_add(ts); t = a.mk_mod(t, a.mk_int(r.m_mod)); return t; @@ -501,7 +514,8 @@ namespace mbp { t = a.mk_int(div(r.m_coeff, r.m_mod)); return t; } - ts.push_back(a.mk_int(r.m_coeff)); + if (r.m_coeff != 0) + ts.push_back(a.mk_int(r.m_coeff)); t = mk_add(ts); t = a.mk_idiv(t, a.mk_int(r.m_mod)); return t; @@ -513,15 +527,7 @@ namespace mbp { } } - void rows2fmls(vector const& rows, ptr_vector const& index2expr, expr_ref_vector& fmls) { - - u_map def_vars; - for (row const& r : rows) { - if (r.m_type == opt::t_mod) - def_vars.insert(r.m_id, r); - else if (r.m_type == opt::t_div) - def_vars.insert(r.m_id, r); - } + void rows2fmls(u_map& def_vars, vector const& rows, ptr_vector const& index2expr, expr_ref_vector& fmls) { for (row const& r : rows) { expr_ref t(m), s(m), val(m); From 9fc4015c46360058ad4b4fcccc7fee076ca5c74c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 30 Oct 2022 03:57:39 -0700 Subject: [PATCH 02/57] remove ternary clause optimization Removing ternary clause optimization from sat_solver simplifies special case handling of ternary clauses throughout the sat solver and dependent solvers (pb_solver). Benchmarking on QF_BV suggests the ternary clause optimization does not have any effect. While removing ternary clause optimization two bugs in unit propagation were also uncovered: it missed propagations when the only a single undef literal remained in the non-watched literals and it did not update blocked literals in cases where it could in the watch list. These performance bugs were for general clauses, ternary clause propagation did not miss propagations (and don't use blocked literals), but fixing these issues for general clauses appear to have made ternary clause optimization irrelevant based on what was measured. --- src/sat/sat_cleaner.cpp | 3 - src/sat/sat_drat.cpp | 4 - src/sat/sat_gc.cpp | 24 ---- src/sat/sat_integrity_checker.cpp | 26 ---- src/sat/sat_justification.h | 21 +--- src/sat/sat_proof_trim.cpp | 17 --- src/sat/sat_simplifier.cpp | 3 - src/sat/sat_solver.cpp | 197 ++---------------------------- src/sat/sat_solver.h | 6 - src/sat/sat_types.h | 2 - src/sat/sat_watched.cpp | 33 ----- src/sat/sat_watched.h | 38 +----- src/sat/smt/pb_solver.cpp | 28 ----- 13 files changed, 16 insertions(+), 386 deletions(-) diff --git a/src/sat/sat_cleaner.cpp b/src/sat/sat_cleaner.cpp index bbdedab86..8da933fb2 100644 --- a/src/sat/sat_cleaner.cpp +++ b/src/sat/sat_cleaner.cpp @@ -64,9 +64,6 @@ namespace sat { } TRACE("cleanup_bug", tout << "keeping: " << ~to_literal(l_idx) << " " << it2->get_literal() << "\n";); break; -#if ENABLE_TERNARY - case watched::TERNARY: -#endif case watched::CLAUSE: // skip break; diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp index 306ff2493..46a608151 100644 --- a/src/sat/sat_drat.cpp +++ b/src/sat/sat_drat.cpp @@ -445,10 +445,6 @@ namespace sat { return false; case justification::BINARY: return contains(c, j.get_literal()); -#if ENABLE_TERNARY - case justification::TERNARY: - return contains(c, j.get_literal1(), j.get_literal2()); -#endif case justification::CLAUSE: return contains(s.get_clause(j)); default: diff --git a/src/sat/sat_gc.cpp b/src/sat/sat_gc.cpp index ab9bc8857..a655956db 100644 --- a/src/sat/sat_gc.cpp +++ b/src/sat/sat_gc.cpp @@ -178,33 +178,9 @@ namespace sat { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << (sz - new_sz) << ")\n";); } -#if ENABLE_TERNARY - bool solver::can_delete3(literal l1, literal l2, literal l3) const { - if (value(l1) == l_true && - value(l2) == l_false && - value(l3) == l_false) { - justification const& j = m_justification[l1.var()]; - if (j.is_ternary_clause()) { - watched w1(l2, l3); - watched w2(j.get_literal1(), j.get_literal2()); - return w1 != w2; - } - } - return true; - } -#endif - bool solver::can_delete(clause const & c) const { if (c.on_reinit_stack()) return false; -#if ENABLE_TERNARY - if (c.size() == 3) { - return - can_delete3(c[0],c[1],c[2]) && - can_delete3(c[1],c[0],c[2]) && - can_delete3(c[2],c[0],c[1]); - } -#endif literal l0 = c[0]; if (value(l0) != l_true) return true; diff --git a/src/sat/sat_integrity_checker.cpp b/src/sat/sat_integrity_checker.cpp index a6086d8a7..223e58677 100644 --- a/src/sat/sat_integrity_checker.cpp +++ b/src/sat/sat_integrity_checker.cpp @@ -26,13 +26,6 @@ namespace sat { integrity_checker::integrity_checker(solver const & _s): s(_s) { } - -#if ENABLE_TERNARY - // for ternary clauses - static bool contains_watched(watch_list const & wlist, literal l1, literal l2) { - return wlist.contains(watched(l1, l2)); - } -#endif // for nary clauses static bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) { @@ -65,18 +58,6 @@ namespace sat { if (c.frozen()) return true; -#if ENABLE_TERNARY - if (c.size() == 3) { - CTRACE("sat_ter_watch_bug", !contains_watched(s.get_wlist(~c[0]), c[1], c[2]), tout << c << "\n"; - tout << "watch_list:\n"; - s.display_watch_list(tout, s.get_wlist(~c[0])); - tout << "\n";); - VERIFY(contains_watched(s.get_wlist(~c[0]), c[1], c[2])); - VERIFY(contains_watched(s.get_wlist(~c[1]), c[0], c[2])); - VERIFY(contains_watched(s.get_wlist(~c[2]), c[0], c[1])); - return true; - } -#endif { if (s.value(c[0]) == l_false || s.value(c[1]) == l_false) { bool on_prop_stack = false; @@ -174,13 +155,6 @@ namespace sat { tout << "\n";); VERIFY(find_binary_watch(s.get_wlist(~(w.get_literal())), l)); break; -#if ENABLE_TERNARY - case watched::TERNARY: - VERIFY(!s.was_eliminated(w.get_literal1().var())); - VERIFY(!s.was_eliminated(w.get_literal2().var())); - VERIFY(w.get_literal1().index() < w.get_literal2().index()); - break; -#endif case watched::CLAUSE: VERIFY(!s.get_clause(w.get_clause_offset()).was_removed()); break; diff --git a/src/sat/sat_justification.h b/src/sat/sat_justification.h index 1fc97a38c..f83173aa7 100644 --- a/src/sat/sat_justification.h +++ b/src/sat/sat_justification.h @@ -22,11 +22,7 @@ namespace sat { class justification { public: - enum kind { NONE = 0, BINARY = 1, -#if ENABLE_TERNARY - TERNARY = 2, -#endif - CLAUSE = 3, EXT_JUSTIFICATION = 4}; + enum kind { NONE = 0, BINARY = 1, CLAUSE = 2, EXT_JUSTIFICATION = 3}; private: unsigned m_level; size_t m_val1; @@ -36,9 +32,7 @@ namespace sat { public: justification(unsigned lvl):m_level(lvl), m_val1(0), m_val2(NONE) {} explicit justification(unsigned lvl, literal l):m_level(lvl), m_val1(l.to_uint()), m_val2(BINARY) {} -#if ENABLE_TERNARY - justification(unsigned lvl, literal l1, literal l2):m_level(lvl), m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {} -#endif + explicit justification(unsigned lvl, clause_offset cls_off):m_level(lvl), m_val1(cls_off), m_val2(CLAUSE) {} static justification mk_ext_justification(unsigned lvl, ext_justification_idx idx) { return justification(lvl, idx, EXT_JUSTIFICATION); } @@ -51,12 +45,6 @@ namespace sat { bool is_binary_clause() const { return m_val2 == BINARY; } literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(val1()); } -#if ENABLE_TERNARY - bool is_ternary_clause() const { return get_kind() == TERNARY; } - literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(val1()); } - literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 3); } -#endif - bool is_clause() const { return m_val2 == CLAUSE; } clause_offset get_clause_offset() const { return m_val1; } @@ -73,11 +61,6 @@ namespace sat { case justification::BINARY: out << "binary " << j.get_literal(); break; -#if ENABLE_TERNARY - case justification::TERNARY: - out << "ternary " << j.get_literal1() << " " << j.get_literal2(); - break; -#endif case justification::CLAUSE: out << "clause"; break; diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index 4c4b9ecaf..df55aecd7 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -125,10 +125,6 @@ namespace sat { in_coi |= m_in_coi.contains(lit.index()); else if (js.is_binary_clause()) in_coi = m_in_coi.contains(js.get_literal().index()); -#if ENABLE_TERNARY - else if (js.is_ternary_clause()) - in_coi = m_in_coi.contains(js.get_literal1().index()) || m_in_coi.contains(js.get_literal2().index()); -#endif else UNREACHABLE(); // approach does not work for external justifications @@ -226,12 +222,6 @@ namespace sat { case justification::BINARY: add_dependency(j.get_literal()); break; -#if ENABLE_TERNARY - case justification::TERNARY: - add_dependency(j.get_literal1()); - add_dependency(j.get_literal2()); - break; -#endif case justification::CLAUSE: for (auto lit : s.get_clause(j)) if (s.value(lit) == l_false) @@ -262,13 +252,6 @@ namespace sat { m_clause.push_back(l); m_clause.push_back(j.get_literal()); break; -#if ENABLE_TERNARY - case justification::TERNARY: - m_clause.push_back(l); - m_clause.push_back(j.get_literal1()); - m_clause.push_back(j.get_literal2()); - break; -#endif case justification::CLAUSE: s.get_clause(j).mark_used(); IF_VERBOSE(3, verbose_stream() << "add core " << s.get_clause(j) << "\n"); diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index b67ef6a2a..9ba536091 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -271,9 +271,6 @@ namespace sat { watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { switch (it2->get_kind()) { -#if ENABLE_TERNARY - case watched::TERNARY: -#endif case watched::CLAUSE: // consume break; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 56dcdf0df..eea3a2475 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -448,10 +448,6 @@ namespace sat { if (redundant && m_par) m_par->share_clause(*this, lits[0], lits[1]); return nullptr; -#if ENABLE_TERNARY - case 3: - return mk_ter_clause(lits, st); -#endif default: return mk_nary_clause(num_lits, lits, st); } @@ -545,58 +541,6 @@ namespace sat { m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } -#if ENABLE_TERNARY - clause * solver::mk_ter_clause(literal * lits, sat::status st) { - VERIFY(ENABLE_TERNARY); - m_stats.m_mk_ter_clause++; - clause * r = alloc_clause(3, lits, st.is_redundant()); - bool reinit = attach_ter_clause(*r, st); - if (reinit || has_variables_to_reinit(*r)) push_reinit_stack(*r); - if (st.is_redundant()) - m_learned.push_back(r); - else - m_clauses.push_back(r); - for (literal l : *r) { - m_touched[l.var()] = m_touch_index; - } - return r; - } - - bool solver::attach_ter_clause(clause & c, sat::status st) { - VERIFY(ENABLE_TERNARY); - bool reinit = false; - if (m_config.m_drat) m_drat.add(c, st); - TRACE("sat_verbose", tout << c << "\n";); - SASSERT(!c.was_removed()); - m_watches[(~c[0]).index()].push_back(watched(c[1], c[2])); - m_watches[(~c[1]).index()].push_back(watched(c[0], c[2])); - m_watches[(~c[2]).index()].push_back(watched(c[0], c[1])); - if (!at_base_lvl()) - reinit = propagate_ter_clause(c); - return reinit; - } - - bool solver::propagate_ter_clause(clause& c) { - bool reinit = false; - if (value(c[1]) == l_false && value(c[2]) == l_false) { - m_stats.m_ter_propagate++; - assign(c[0], justification(std::max(lvl(c[1]), lvl(c[2])), c[1], c[2])); - reinit = !c.is_learned(); - } - else if (value(c[0]) == l_false && value(c[2]) == l_false) { - m_stats.m_ter_propagate++; - assign(c[1], justification(std::max(lvl(c[0]), lvl(c[2])), c[0], c[2])); - reinit = !c.is_learned(); - } - else if (value(c[0]) == l_false && value(c[1]) == l_false) { - m_stats.m_ter_propagate++; - assign(c[2], justification(std::max(lvl(c[0]), lvl(c[1])), c[0], c[1])); - reinit = !c.is_learned(); - } - return reinit; - } -#endif - clause * solver::mk_nary_clause(unsigned num_lits, literal * lits, sat::status st) { m_stats.m_mk_clause++; clause * r = alloc_clause(num_lits, lits, st.is_redundant()); @@ -663,13 +607,7 @@ namespace sat { void solver::attach_clause(clause & c, bool & reinit) { SASSERT(c.size() > 2); - reinit = false; -#if ENABLE_TERNARY - if (ENABLE_TERNARY && c.size() == 3) - reinit = attach_ter_clause(c, c.is_learned() ? sat::status::redundant() : sat::status::asserted()); - else -#endif - reinit = attach_nary_clause(c, c.is_learned() && !c.on_reinit_stack()); + reinit = attach_nary_clause(c, c.is_learned() && !c.on_reinit_stack()); } void solver::set_learned(clause& c, bool redundant) { @@ -917,12 +855,6 @@ namespace sat { } void solver::detach_clause(clause& c) { -#if ENABLE_TERNARY - if (c.size() == 3) { - detach_ter_clause(c); - return; - } -#endif detach_nary_clause(c); } @@ -932,14 +864,6 @@ namespace sat { erase_clause_watch(get_wlist(~c[1]), cls_off); } -#if ENABLE_TERNARY - void solver::detach_ter_clause(clause & c) { - erase_ternary_watch(get_wlist(~c[0]), c[1], c[2]); - erase_ternary_watch(get_wlist(~c[1]), c[0], c[2]); - erase_ternary_watch(get_wlist(~c[2]), c[0], c[1]); - } -#endif - // ----------------------- // // Basic @@ -1123,31 +1047,6 @@ namespace sat { *it2 = *it; it2++; break; -#if ENABLE_TERNARY - case watched::TERNARY: { - lbool val1, val2; - l1 = it->get_literal1(); - l2 = it->get_literal2(); - val1 = value(l1); - val2 = value(l2); - if (val1 == l_false && val2 == l_undef) { - m_stats.m_ter_propagate++; - assign_core(l2, justification(std::max(curr_level, lvl(l1)), l1, not_l)); - } - else if (val1 == l_undef && val2 == l_false) { - m_stats.m_ter_propagate++; - assign_core(l1, justification(std::max(curr_level, lvl(l2)), l2, not_l)); - } - else if (val1 == l_false && val2 == l_false) { - CONFLICT_CLEANUP(); - set_conflict(justification(std::max(curr_level, lvl(l1)), l1, not_l), ~l2); - return false; - } - *it2 = *it; - it2++; - break; - } -#endif case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n"; @@ -2534,12 +2433,6 @@ namespace sat { case justification::BINARY: process_antecedent(~(js.get_literal()), num_marks); break; -#if ENABLE_TERNARY - case justification::TERNARY: - process_antecedent(~(js.get_literal1()), num_marks); - process_antecedent(~(js.get_literal2()), num_marks); - break; -#endif case justification::CLAUSE: { clause & c = get_clause(js); unsigned i = 0; @@ -2718,13 +2611,6 @@ namespace sat { SASSERT(consequent != null_literal); process_antecedent_for_unsat_core(~(js.get_literal())); break; -#if ENABLE_TERNARY - case justification::TERNARY: - SASSERT(consequent != null_literal); - process_antecedent_for_unsat_core(~(js.get_literal1())); - process_antecedent_for_unsat_core(~(js.get_literal2())); - break; -#endif case justification::CLAUSE: { clause & c = get_clause(js); unsigned i = 0; @@ -2861,12 +2747,6 @@ namespace sat { case justification::BINARY: level = update_max_level(js.get_literal(), level, unique_max); return level; -#if ENABLE_TERNARY - case justification::TERNARY: - level = update_max_level(js.get_literal1(), level, unique_max); - level = update_max_level(js.get_literal2(), level, unique_max); - return level; -#endif case justification::CLAUSE: for (literal l : get_clause(js)) level = update_max_level(l, level, unique_max); @@ -3218,15 +3098,6 @@ namespace sat { return false; } break; -#if ENABLE_TERNARY - case justification::TERNARY: - if (!process_antecedent_for_minimization(~(js.get_literal1())) || - !process_antecedent_for_minimization(~(js.get_literal2()))) { - reset_unmark(old_size); - return false; - } - break; -#endif case justification::CLAUSE: { clause & c = get_clause(js); unsigned i = 0; @@ -3382,12 +3253,6 @@ namespace sat { case justification::BINARY: update_lrb_reasoned(js.get_literal()); break; -#if ENABLE_TERNARY - case justification::TERNARY: - update_lrb_reasoned(js.get_literal1()); - update_lrb_reasoned(js.get_literal2()); - break; -#endif case justification::CLAUSE: { clause & c = get_clause(js); for (literal l : c) { @@ -3457,20 +3322,6 @@ namespace sat { unmark_lit(~l2); } } -#if ENABLE_TERNARY - else if (w.is_ternary_clause()) { - literal l2 = w.get_literal1(); - literal l3 = w.get_literal2(); - if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) { - // eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3 - unmark_lit(~l3); - } - else if (is_marked_lit(~l2) && is_marked_lit(l3) && l0 != ~l2) { - // eliminate ~l2 from lemma because we have the clause l \/ l2 \/ l3 - unmark_lit(~l2); - } - } -#endif else { // May miss some binary/ternary clauses, but that is ok. // I sort the watch lists at every simplification round. @@ -3735,17 +3586,6 @@ namespace sat { } else { clause & c = *(cw.get_clause()); -#if ENABLE_TERNARY - if (ENABLE_TERNARY && c.size() == 3) { - if (propagate_ter_clause(c) && !at_base_lvl()) - m_clauses_to_reinit[j++] = cw; - else if (has_variables_to_reinit(c) && !at_base_lvl()) - m_clauses_to_reinit[j++] = cw; - else - c.set_reinit_stack(false); - continue; - } -#endif detach_clause(c); attach_clause(c, reinit); if (reinit && !at_base_lvl()) @@ -4012,12 +3852,6 @@ namespace sat { case justification::BINARY: out << "binary " << js.get_literal() << "@" << lvl(js.get_literal()); break; -#if ENABLE_TERNARY - case justification::TERNARY: - out << "ternary " << js.get_literal1() << "@" << lvl(js.get_literal1()) << " "; - out << js.get_literal2() << "@" << lvl(js.get_literal2()); - break; -#endif case justification::CLAUSE: { out << "("; bool first = true; @@ -4677,24 +4511,14 @@ namespace sat { if (!check_domain(lit, ~js.get_literal())) return false; s |= m_antecedents.find(js.get_literal().var()); break; -#if ENABLE_TERNARY - case justification::TERNARY: - if (!check_domain(lit, ~js.get_literal1()) || - !check_domain(lit, ~js.get_literal2())) return false; - s |= m_antecedents.find(js.get_literal1().var()); - s |= m_antecedents.find(js.get_literal2().var()); - break; -#endif case justification::CLAUSE: { clause & c = get_clause(js); for (literal l : c) { if (l != lit) { - if (check_domain(lit, ~l) && all_found) { - s |= m_antecedents.find(l.var()); - } - else { - all_found = false; - } + if (check_domain(lit, ~l) && all_found) + s |= m_antecedents.find(l.var()); + else + all_found = false; } } break; @@ -4729,12 +4553,11 @@ namespace sat { bool solver::extract_fixed_consequences1(literal lit, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq) { index_set s; - if (m_antecedents.contains(lit.var())) { + if (m_antecedents.contains(lit.var())) return true; - } - if (assumptions.contains(lit)) { - s.insert(lit.index()); - } + + if (assumptions.contains(lit)) + s.insert(lit.index()); else { if (!extract_assumptions(lit, s)) { SASSERT(!m_todo_antecedents.empty()); @@ -4806,7 +4629,7 @@ namespace sat { clause_vector const & cs = *(vs[i]); for (clause* cp : cs) { clause & c = *cp; - if (ENABLE_TERNARY && c.size() == 3) + if (c.size() == 3) num_ter++; else num_cls++; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 88c404c7c..2e53e4620 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -305,11 +305,6 @@ namespace sat { void mk_bin_clause(literal l1, literal l2, sat::status st); void mk_bin_clause(literal l1, literal l2, bool learned) { mk_bin_clause(l1, l2, learned ? sat::status::redundant() : sat::status::asserted()); } bool propagate_bin_clause(literal l1, literal l2); -#if ENABLE_TERNARY - clause * mk_ter_clause(literal * lits, status st); - bool attach_ter_clause(clause & c, status st); - bool propagate_ter_clause(clause& c); -#endif clause * mk_nary_clause(unsigned num_lits, literal * lits, status st); bool has_variables_to_reinit(clause const& c) const; bool has_variables_to_reinit(literal l1, literal l2) const; @@ -345,7 +340,6 @@ namespace sat { void detach_bin_clause(literal l1, literal l2, bool learned); void detach_clause(clause & c); void detach_nary_clause(clause & c); - void detach_ter_clause(clause & c); void push_reinit_stack(clause & c); void push_reinit_stack(literal l1, literal l2); diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 7c4e4fb73..4e119a2ae 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -34,8 +34,6 @@ class params_ref; class reslimit; class statistics; -#define ENABLE_TERNARY false - namespace sat { #define SAT_VB_LVL 10 diff --git a/src/sat/sat_watched.cpp b/src/sat/sat_watched.cpp index dedbf45f0..5573212f5 100644 --- a/src/sat/sat_watched.cpp +++ b/src/sat/sat_watched.cpp @@ -71,34 +71,6 @@ namespace sat { VERIFY(found); } -#if ENABLE_TERNARY - void erase_ternary_watch(watch_list& wlist, literal l1, literal l2) { - watched w(l1, l2); - watch_list::iterator it = wlist.begin(), end = wlist.end(); - watch_list::iterator it2 = it; - bool found = false; - for (; it != end; ++it) { - if (!found && w == *it) { - found = true; - } - else { - *it2 = *it; - ++it2; - } - } - wlist.set_end(it2); -#if 0 - VERIFY(found); - for (watched const& w2 : wlist) { - if (w2 == w) { - std::cout << l1 << " " << l2 << "\n"; - } - //VERIFY(w2 != w); - } -#endif - } -#endif - void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist) { watch_list::iterator end = wlist.end(); for (; it != end; ++it, ++it2) @@ -120,11 +92,6 @@ namespace sat { if (w.is_learned()) out << "*"; break; -#if ENABLE_TERNARY - case watched::TERNARY: - out << "(" << w.get_literal1() << " " << w.get_literal2() << ")"; - break; -#endif case watched::CLAUSE: out << "(" << w.get_blocked_literal() << " " << *(ca.get_clause(w.get_clause_offset())) << ")"; break; diff --git a/src/sat/sat_watched.h b/src/sat/sat_watched.h index 7e88c8c51..6d91434db 100644 --- a/src/sat/sat_watched.h +++ b/src/sat/sat_watched.h @@ -40,11 +40,7 @@ namespace sat { class watched { public: enum kind { - BINARY = 0, -#if ENABLE_TERNARY - TERNARY, -#endif - CLAUSE, EXT_CONSTRAINT + BINARY = 0, CLAUSE, EXT_CONSTRAINT }; private: size_t m_val1; @@ -59,18 +55,6 @@ namespace sat { SASSERT(learned || is_binary_non_learned_clause()); } -#if ENABLE_TERNARY - watched(literal l1, literal l2) { - SASSERT(l1 != l2); - if (l1.index() > l2.index()) - std::swap(l1, l2); - m_val1 = l1.to_uint(); - m_val2 = static_cast(TERNARY) + (l2.to_uint() << 2); - SASSERT(is_ternary_clause()); - SASSERT(get_literal1() == l1); - SASSERT(get_literal2() == l2); - } -#endif unsigned val2() const { return m_val2; } @@ -101,11 +85,6 @@ namespace sat { void set_learned(bool l) { if (l) m_val2 |= 4u; else m_val2 &= ~4u; SASSERT(is_learned() == l); } -#if ENABLE_TERNARY - bool is_ternary_clause() const { return get_kind() == TERNARY; } - literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(static_cast(m_val1)); } - literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 2); } -#endif bool is_clause() const { return get_kind() == CLAUSE; } clause_offset get_clause_offset() const { SASSERT(is_clause()); return static_cast(m_val1); } @@ -124,21 +103,14 @@ namespace sat { bool operator!=(watched const & w) const { return !operator==(w); } }; - static_assert(0 <= watched::BINARY && watched::BINARY <= 3, ""); -#if ENABLE_TERNARY - static_assert(0 <= watched::TERNARY && watched::TERNARY <= 3, ""); -#endif - static_assert(0 <= watched::CLAUSE && watched::CLAUSE <= 3, ""); - static_assert(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 3, ""); + static_assert(0 <= watched::BINARY && watched::BINARY <= 2, ""); + static_assert(0 <= watched::CLAUSE && watched::CLAUSE <= 2, ""); + static_assert(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 2, ""); struct watched_lt { bool operator()(watched const & w1, watched const & w2) const { if (w2.is_binary_clause()) return false; if (w1.is_binary_clause()) return true; -#if ENABLE_TERNARY - if (w2.is_ternary_clause()) return false; - if (w1.is_ternary_clause()) return true; -#endif return false; } }; @@ -148,8 +120,6 @@ namespace sat { watched* find_binary_watch(watch_list & wlist, literal l); watched const* find_binary_watch(watch_list const & wlist, literal l); bool erase_clause_watch(watch_list & wlist, clause_offset c); - void erase_ternary_watch(watch_list & wlist, literal l1, literal l2); - void set_ternary_learned(watch_list& wlist, literal l1, literal l2, bool learned); class clause_allocator; std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist, extension* ext); diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index c3e43bfbe..424b20d4e 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -690,15 +690,6 @@ namespace pb { inc_coeff(consequent, offset); process_antecedent(js.get_literal(), offset); break; -#if ENABLE_TERNARY - case sat::justification::TERNARY: - inc_bound(offset); - SASSERT (consequent != sat::null_literal); - inc_coeff(consequent, offset); - process_antecedent(js.get_literal1(), offset); - process_antecedent(js.get_literal2(), offset); - break; -#endif case sat::justification::CLAUSE: { inc_bound(offset); sat::clause & c = s().get_clause(js); @@ -1019,16 +1010,6 @@ namespace pb { inc_coeff(consequent, 1); process_antecedent(js.get_literal()); break; -#if ENABLE_TERNARY - case sat::justification::TERNARY: - SASSERT(consequent != sat::null_literal); - round_to_one(consequent.var()); - inc_bound(1); - inc_coeff(consequent, 1); - process_antecedent(js.get_literal1()); - process_antecedent(js.get_literal2()); - break; -#endif case sat::justification::CLAUSE: { sat::clause & c = s().get_clause(js); unsigned i = 0; @@ -3476,15 +3457,6 @@ namespace pb { ineq.push(lit, offset); ineq.push(js.get_literal(), offset); break; -#if ENABLE_TERNARY - case sat::justification::TERNARY: - SASSERT(lit != sat::null_literal); - ineq.reset(offset); - ineq.push(lit, offset); - ineq.push(js.get_literal1(), offset); - ineq.push(js.get_literal2(), offset); - break; -#endif case sat::justification::CLAUSE: { ineq.reset(offset); sat::clause & c = s().get_clause(js); From 1646a41b2f2ffd319138036e4debd3282b6ff10e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 08:44:55 -0700 Subject: [PATCH 03/57] minor fixes - ensure mk_extract performs simplification to distribute over extract and removing extract if the range is the entire bit-vector - ensure bool_rewriter simplifeis disjunctions when applicable. --- src/ast/euf/euf_egraph.cpp | 4 ++++ src/ast/euf/euf_etable.cpp | 2 -- src/ast/justified_expr.h | 11 +++++------ src/ast/rewriter/bool_rewriter.cpp | 2 +- src/ast/rewriter/mk_extract_proc.cpp | 7 +++++++ src/ast/rewriter/th_rewriter.h | 1 + src/tactic/core/solve_eqs_tactic.cpp | 7 ++----- src/tactic/core/solve_eqs_tactic.h | 3 +-- src/tactic/core/symmetry_reduce_tactic.cpp | 5 +++-- 9 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 6034d8428..c8605b297 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -904,6 +904,10 @@ template void euf::egraph::explain_todo(ptr_vector& justifications, cc_j template void euf::egraph::explain_eq(ptr_vector& justifications, cc_justification*, enode* a, enode* b); template unsigned euf::egraph::explain_diseq(ptr_vector& justifications, cc_justification*, enode* a, enode* b); +template void euf::egraph::explain(ptr_vector& justifications, cc_justification*); +template void euf::egraph::explain_todo(ptr_vector& justifications, cc_justification*); +template void euf::egraph::explain_eq(ptr_vector& justifications, cc_justification*, enode* a, enode* b); +template unsigned euf::egraph::explain_diseq(ptr_vector& justifications, cc_justification*, enode* a, enode* b); #if 0 diff --git a/src/ast/euf/euf_etable.cpp b/src/ast/euf/euf_etable.cpp index cfc99e4a0..e007297ef 100644 --- a/src/ast/euf/euf_etable.cpp +++ b/src/ast/euf/euf_etable.cpp @@ -201,8 +201,6 @@ namespace euf { enode_bool_pair etable::insert(enode * n) { // it doesn't make sense to insert a constant. SASSERT(n->num_args() > 0); - SASSERT(!m_manager.is_and(n->get_expr())); - SASSERT(!m_manager.is_or(n->get_expr())); enode * n_prime; void * t = get_table(n); switch (static_cast(GET_TAG(t))) { diff --git a/src/ast/justified_expr.h b/src/ast/justified_expr.h index 786061065..a599ff5a1 100644 --- a/src/ast/justified_expr.h +++ b/src/ast/justified_expr.h @@ -33,8 +33,7 @@ public: justified_expr(justified_expr const& other): m(other.m), m_fml(other.m_fml), - m_proof(other.m_proof) - { + m_proof(other.m_proof) { m.inc_ref(m_fml); m.inc_ref(m_proof); } @@ -42,8 +41,7 @@ public: justified_expr(justified_expr && other) noexcept : m(other.m), m_fml(nullptr), - m_proof(nullptr) - { + m_proof(nullptr) { std::swap(m_fml, other.m_fml); std::swap(m_proof, other.m_proof); } @@ -51,10 +49,11 @@ public: ~justified_expr() { m.dec_ref(m_fml); m.dec_ref(m_proof); - m_fml = nullptr; - m_proof = nullptr; + m_fml = nullptr; + m_proof = nullptr; } expr* get_fml() const { return m_fml; } + proof* get_proof() const { return m_proof; } }; diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 80495853a..1964e6528 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -290,7 +290,7 @@ br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, ast_lt lt; std::sort(flat_args.begin(), flat_args.end(), lt); } - result = m().mk_or(flat_args); + result = mk_or_app(flat_args.size(), flat_args.data()); } return BR_DONE; } diff --git a/src/ast/rewriter/mk_extract_proc.cpp b/src/ast/rewriter/mk_extract_proc.cpp index be61047a4..cc18ae176 100644 --- a/src/ast/rewriter/mk_extract_proc.cpp +++ b/src/ast/rewriter/mk_extract_proc.cpp @@ -32,8 +32,15 @@ mk_extract_proc::~mk_extract_proc() { } app * mk_extract_proc::operator()(unsigned high, unsigned low, expr * arg) { + unsigned l, h; + while (m_util.is_extract(arg, l, h, arg)) { + low += l; + high += l; + } ast_manager & m = m_util.get_manager(); sort * s = arg->get_sort(); + if (low == 0 && high + 1 == m_util.get_bv_size(arg) && is_app(arg)) + return to_app(arg); if (m_low == low && m_high == high && m_domain == s) return m.mk_app(m_f_cached, arg); // m_f_cached has a reference to m_domain, so, I don't need to inc_ref m_domain diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index 271500551..e432678a4 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -47,6 +47,7 @@ public: expr_ref operator()(expr * n, unsigned num_bindings, expr * const * bindings); expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args); + expr_ref mk_app(func_decl* f, ptr_vector const& args) { return mk_app(f, args.size(), args.data()); } bool reduce_quantifier(quantifier * old_q, expr * new_body, diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index a7b206d5b..5a076aa0f 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -1141,9 +1141,6 @@ public: }; -tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replacer * r) { - if (r == nullptr) - return clean(alloc(solve_eqs_tactic, m, p, mk_expr_simp_replacer(m, p), true)); - else - return clean(alloc(solve_eqs_tactic, m, p, r, false)); +tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(solve_eqs_tactic, m, p, mk_expr_simp_replacer(m, p), true)); } diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index d986b0dbf..d65a33046 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -21,9 +21,8 @@ Revision History: #include "util/params.h" class ast_manager; class tactic; -class expr_replacer; -tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref(), expr_replacer * r = nullptr); +tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("solve-eqs", "eliminate variables by solving equations.", "mk_solve_eqs_tactic(m, p)") diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index 9aa4c9448..e94e83679 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -25,6 +25,7 @@ Notes: #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" +#include "ast/ast_util.h" class symmetry_reduce_tactic : public tactic { class imp; @@ -608,12 +609,12 @@ private: return (j == A.size())?0:A[j]; } - app* mk_member(app* t, term_set const& C) { + expr* mk_member(app* t, term_set const& C) { expr_ref_vector eqs(m()); for (unsigned i = 0; i < C.size(); ++i) { eqs.push_back(m().mk_eq(t, C[i])); } - return m().mk_or(eqs.size(), eqs.data()); + return mk_or(m(), eqs.size(), eqs.data()); } }; From e57674490f37d027a500d30b272e3e337d637049 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 08:51:30 -0700 Subject: [PATCH 04/57] adding simplifiers layer simplifiers layer is a common substrate for global non-incremental and incremental processing. The first two layers are new, but others are to be ported form tactics. - bv::slice - rewrites equations to cut-dice-slice bit-vector extractions until they align. It creates opportunities for rewriting portions of bit-vectors to common sub-expressions, including values. - euf::completion - generalizes the KB simplifcation from asserted formulas to use the E-graph to establish a global and order-independent canonization. The interface dependent_expr_simplifier is amenable to forming tactics. Plugins for asserted-formulas is also possible but not yet realized. --- scripts/mk_project.py | 3 +- src/CMakeLists.txt | 3 +- src/ast/simplifiers/CMakeLists.txt | 8 + src/ast/simplifiers/bv_slice.cpp | 207 +++++++++++++ src/ast/simplifiers/bv_slice.h | 55 ++++ src/ast/simplifiers/dependent_expr.h | 75 +++++ src/ast/simplifiers/dependent_expr_state.h | 86 ++++++ src/ast/simplifiers/euf_completion.cpp | 330 +++++++++++++++++++++ src/ast/simplifiers/euf_completion.h | 63 ++++ src/tactic/CMakeLists.txt | 1 + src/tactic/bv/CMakeLists.txt | 2 + src/tactic/bv/bv_slice_tactic.h | 29 ++ src/tactic/core/CMakeLists.txt | 2 + src/tactic/core/euf_completion_tactic.cpp | 32 ++ src/tactic/core/euf_completion_tactic.h | 29 ++ src/tactic/dependent_expr_state_tactic.h | 101 +++++++ 16 files changed, 1024 insertions(+), 2 deletions(-) create mode 100644 src/ast/simplifiers/CMakeLists.txt create mode 100644 src/ast/simplifiers/bv_slice.cpp create mode 100644 src/ast/simplifiers/bv_slice.h create mode 100644 src/ast/simplifiers/dependent_expr.h create mode 100644 src/ast/simplifiers/dependent_expr_state.h create mode 100644 src/ast/simplifiers/euf_completion.cpp create mode 100644 src/ast/simplifiers/euf_completion.h create mode 100644 src/tactic/bv/bv_slice_tactic.h create mode 100644 src/tactic/core/euf_completion_tactic.cpp create mode 100644 src/tactic/core/euf_completion_tactic.h create mode 100644 src/tactic/dependent_expr_state_tactic.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 60cbdcc56..9c4cda4a1 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -31,10 +31,11 @@ def init_project_def(): add_lib('nlsat', ['polynomial', 'sat']) add_lib('lp', ['util', 'nlsat', 'grobner', 'interval', 'smt_params'], 'math/lp') add_lib('rewriter', ['ast', 'polynomial', 'automata', 'params'], 'ast/rewriter') + add_lib('simplifiers', ['euf', 'rewriter'], 'ast/simplifiers') add_lib('macros', ['rewriter'], 'ast/macros') add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') add_lib('model', ['rewriter', 'macros']) - add_lib('tactic', ['ast', 'model']) + add_lib('tactic', ['ast', 'model', 'simplifiers']) add_lib('substitution', ['ast', 'rewriter'], 'ast/substitution') add_lib('parser_util', ['ast'], 'parsers/util') add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b96638944..ad6fbcdb2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,9 +49,10 @@ add_subdirectory(ast/rewriter) add_subdirectory(ast/normal_forms) add_subdirectory(ast/macros) add_subdirectory(model) +add_subdirectory(ast/euf) +add_subdirectory(ast/simplifiers) add_subdirectory(tactic) add_subdirectory(ast/substitution) -add_subdirectory(ast/euf) add_subdirectory(smt/params) add_subdirectory(parsers/util) add_subdirectory(math/grobner) diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt new file mode 100644 index 000000000..650410415 --- /dev/null +++ b/src/ast/simplifiers/CMakeLists.txt @@ -0,0 +1,8 @@ +z3_add_component(simplifiers + SOURCES + euf_completion.cpp + bv_slice.cpp + COMPONENT_DEPENDENCIES + euf + rewriter +) diff --git a/src/ast/simplifiers/bv_slice.cpp b/src/ast/simplifiers/bv_slice.cpp new file mode 100644 index 000000000..f39fa932e --- /dev/null +++ b/src/ast/simplifiers/bv_slice.cpp @@ -0,0 +1,207 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bv_slice.cpp + +Abstract: + + simplifier for extracting bit-vector ranges + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + +#include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" +#include "ast/simplifiers/bv_slice.h" + +namespace bv { + + void slice::reduce() { + process_eqs(); + apply_subst(); + advance_qhead(m_fmls.size()); + } + + void slice::process_eqs() { + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + auto const [f, d] = m_fmls[i](); + process_eq(f); + } + } + + void slice::process_eq(expr* e) { + expr* x, * y; + if (!m.is_eq(e, x, y)) + return; + if (!m_bv.is_bv(x)) + return; + m_xs.reset(); + m_ys.reset(); + get_concats(x, m_xs); + get_concats(y, m_ys); + slice_eq(); + } + + void slice::slice_eq() { + unsigned i = m_xs.size(), j = m_ys.size(); + unsigned offx = 0, offy = 0; + while (0 < i) { + SASSERT(0 < j); + expr* x = m_xs[i - 1]; // least significant bits are last + expr* y = m_ys[j - 1]; + SASSERT(offx == 0 || offy == 0); + unsigned szx = m_bv.get_bv_size(x); + unsigned szy = m_bv.get_bv_size(y); + SASSERT(offx < szx); + SASSERT(offy < szy); + if (szx - offx == szy - offy) { + register_slice(offx, szx - 1, x); + register_slice(offy, szy - 1, y); + --i; + --j; + offx = 0; + offy = 0; + } + else if (szx - offx < szy - offy) { + register_slice(offx, szx - 1, x); + register_slice(offy, offy + szx - offx - 1, y); + offy += szx - offx; + offx = 0; + --i; + } + else { + register_slice(offy, szy - 1, y); + register_slice(offx, offx + szy - offy - 1, x); + offx += szy - offy; + offy = 0; + --j; + } + } + } + + void slice::register_slice(unsigned lo, unsigned hi, expr* x) { + SASSERT(lo <= hi && hi < m_bv.get_bv_size(x)); + unsigned l, h; + while (m_bv.is_extract(x, l, h, x)) { + // x[l:h][lo:hi] = x[l+lo:l+hi] + hi += l; + lo += l; + SASSERT(lo <= hi && hi < m_bv.get_bv_size(x)); + } + unsigned sz = m_bv.get_bv_size(x); + if (hi - lo + 1 == sz) + return; + SASSERT(0 < lo || hi + 1 < sz); + auto& b = m_boundaries.insert_if_not_there(x, uint_set()); + + struct remove_set : public trail { + uint_set& b; + unsigned i; + remove_set(uint_set& b, unsigned i) :b(b), i(i) {} + void undo() override { + b.remove(i); + } + }; + if (lo > 0 && !b.contains(lo)) { + b.insert(lo); + if (m_num_scopes > 0) + m_trail.push(remove_set(b, lo)); + } + if (hi + 1 < sz && !b.contains(hi + 1)) { + b.insert(hi + 1); + if (m_num_scopes > 0) + m_trail.push(remove_set(b, hi+ 1)); + } + } + + expr* slice::mk_extract(unsigned hi, unsigned lo, expr* x) { + unsigned l, h; + while (m_bv.is_extract(x, l, h, x)) { + lo += l; + hi += l; + } + if (lo == 0 && hi + 1 == m_bv.get_bv_size(x)) + return x; + else + return m_bv.mk_extract(hi, lo, x); + } + + void slice::apply_subst() { + if (m_boundaries.empty()) + return; + expr_ref_vector cache(m), pin(m); + ptr_vector todo, args; + expr* c; + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + auto const [f, d] = m_fmls[i](); + todo.push_back(f); + pin.push_back(f); + while (!todo.empty()) { + expr* e = todo.back(); + c = cache.get(e->get_id(), nullptr); + if (c) { + todo.pop_back(); + continue; + } + if (!is_app(e)) { + cache.setx(e->get_id(), e); + todo.pop_back(); + continue; + } + args.reset(); + unsigned sz = todo.size(); + bool change = false; + for (expr* arg : *to_app(e)) { + c = cache.get(arg->get_id(), nullptr); + if (c) { + args.push_back(c); + change |= c != arg; + SASSERT(c->get_sort() == arg->get_sort()); + } + else + todo.push_back(arg); + } + if (sz == todo.size()) { + todo.pop_back(); + if (change) + cache.setx(e->get_id(), m_rewriter.mk_app(to_app(e)->get_decl(), args)); + else + cache.setx(e->get_id(), e); + SASSERT(e->get_sort() == cache.get(e->get_id())->get_sort()); + uint_set b; + if (m_boundaries.find(e, b)) { + expr* r = cache.get(e->get_id()); + expr_ref_vector xs(m); + unsigned lo = 0; + for (unsigned hi : b) { + xs.push_back(mk_extract(hi - 1, lo, r)); + lo = hi; + } + xs.push_back(mk_extract(m_bv.get_bv_size(r) - 1, lo, r)); + xs.reverse(); + expr_ref xc(m_bv.mk_concat(xs), m); + cache.setx(e->get_id(), xc); + SASSERT(e->get_sort() == xc->get_sort()); + } + } + } + c = cache.get(f->get_id()); + if (c != f) + m_fmls.update(i, dependent_expr(m, c, d)); + } + } + + void slice::get_concats(expr* x, ptr_vector& xs) { + while (m_bv.is_concat(x)) { + xs.append(to_app(x)->get_num_args(), to_app(x)->get_args()); + x = xs.back(); + xs.pop_back(); + } + xs.push_back(x); + } +} diff --git a/src/ast/simplifiers/bv_slice.h b/src/ast/simplifiers/bv_slice.h new file mode 100644 index 000000000..cc0f48cfc --- /dev/null +++ b/src/ast/simplifiers/bv_slice.h @@ -0,0 +1,55 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bv_slice.h + +Abstract: + + simplifier for extracting bit-vector ranges + It rewrites a state using bit-vector slices. + Slices are extracted from bit-vector equality assertions + in the style of (but not fully implementing a full slicing) + Bjorner & Pichora, TACAS 1998 and Brutomesso et al 2008. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + + +#pragma once + +#include "util/uint_set.h" +#include "ast/bv_decl_plugin.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" + + +namespace bv { + + class slice : public dependent_expr_simplifier { + bv_util m_bv; + th_rewriter m_rewriter; + obj_map m_boundaries; + ptr_vector m_xs, m_ys; + + expr* mk_extract(unsigned hi, unsigned lo, expr* x); + void process_eqs(); + void process_eq(expr* e); + void slice_eq(); + void register_slice(unsigned lo, unsigned hi, expr* x); + void apply_subst(); + void get_concats(expr* x, ptr_vector& xs); + + public: + + slice(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_bv(m), m_rewriter(m) {} + + void push() override { dependent_expr_simplifier::push(); } + void pop(unsigned n) override { dependent_expr_simplifier::pop(n); } + void reduce() override; + }; +} diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h new file mode 100644 index 000000000..9d6d8625e --- /dev/null +++ b/src/ast/simplifiers/dependent_expr.h @@ -0,0 +1,75 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + dependent_expr.h + +Abstract: + + Container class for dependent expressions. + They represent how assertions are tracked in goals. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ +#pragma once + +#include "ast/ast.h" + +class dependent_expr { + ast_manager& m; + expr* m_fml; + expr_dependency* m_dep; +public: + dependent_expr(ast_manager& m, expr* fml, expr_dependency* d): + m(m), + m_fml(fml), + m_dep(d) { + SASSERT(fml); + m.inc_ref(fml); + m.inc_ref(d); + } + + dependent_expr& operator=(dependent_expr const& other) { + SASSERT(&m == &other.m); + if (this != &other) { + m.inc_ref(other.m_fml); + m.inc_ref(other.m_dep); + m.dec_ref(m_fml); + m.dec_ref(m_dep); + m_fml = other.m_fml; + m_dep = other.m_dep; + } + return *this; + } + + dependent_expr(dependent_expr const& other): + m(other.m), + m_fml(other.m_fml), + m_dep(other.m_dep) { + m.inc_ref(m_fml); + m.inc_ref(m_dep); + } + + dependent_expr(dependent_expr && other) noexcept : + m(other.m), + m_fml(nullptr), + m_dep(nullptr) { + std::swap(m_fml, other.m_fml); + std::swap(m_dep, other.m_dep); + } + + ~dependent_expr() { + m.dec_ref(m_fml); + m.dec_ref(m_dep); + m_fml = nullptr; + m_dep = nullptr; + } + + std::tuple operator()() const { + return { m_fml, m_dep }; + } +}; diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h new file mode 100644 index 000000000..5156d1126 --- /dev/null +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -0,0 +1,86 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + dependent_expr_state.h + +Abstract: + + abstraction for simplification of depenent expression states. + A dependent_expr_state is an interface to a set of dependent expressions. + Dependent expressions are formulas together with a set of dependencies that are coarse grained + proof hints or justifications for them. Input assumptions can be self-justified. + + The dependent_expr_simplifier implements main services: + - push, pop - that scope the local state + - reduce - to process formulas in a dependent_expr_state between the current value of m_qhead and the size() + of the depdenent_expr_state + + A dependent expr_simplifier can be used to: + - to build a tactic + - for incremental pre-processing + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + +#pragma once + +#include "util/trail.h" +#include "util/statistics.h" +#include "util/params.h" +#include "ast/simplifiers/dependent_expr.h" + +/** + abstract interface to state updated by simplifiers. + */ +class dependent_expr_state { +public: + virtual unsigned size() const = 0; + virtual dependent_expr const& operator[](unsigned i) = 0; + virtual void update(unsigned i, dependent_expr const& j) = 0; + virtual bool inconsistent() = 0; +}; + +/** + Shared interface of simplifiers. + */ +class dependent_expr_simplifier { +protected: + ast_manager& m; + dependent_expr_state& m_fmls; + unsigned m_qhead = 0; // pointer into last processed formula in m_fmls + unsigned m_num_scopes = 0; + trail_stack m_trail; + void advance_qhead(unsigned sz) { if (m_num_scopes > 0) m_trail.push(value_trail(m_qhead)); m_qhead = sz; } +public: + dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s) {} + virtual ~dependent_expr_simplifier() {} + virtual void push() { m_num_scopes++; m_trail.push_scope(); } + virtual void pop(unsigned n) { m_num_scopes -= n; m_trail.pop_scope(n); } + virtual void reduce() = 0; + virtual void collect_statistics(statistics& st) const {} + virtual void reset_statistics() {} + virtual void updt_params(params_ref const& p) {} +}; + +/** + Factory interface for creating simplifiers. + The use of a factory allows delaying the creation of the dependent_expr_state + argument until the point where the expression simplifier is created. + This is used in tactics where the dependent_expr_state is a reference to the + new tactic. + + Alternatively have a clone method on dependent_expr_simplifier. + */ +class dependent_expr_simplifier_factory { + unsigned m_ref = 0; +public: + virtual dependent_expr_simplifier* mk(ast_manager& m, params_ref const& p, dependent_expr_state& s) = 0; + virtual ~dependent_expr_simplifier_factory() {} + void inc_ref() { ++m_ref; } + void dec_ref() { if (--m_ref == 0) dealloc(this); } +}; diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp new file mode 100644 index 000000000..068f6598e --- /dev/null +++ b/src/ast/simplifiers/euf_completion.cpp @@ -0,0 +1,330 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + euf_completion.cpp + +Abstract: + + Ground completion for equalities + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-30 + +Notes: + +Create a congruence closure of E. +Select _simplest_ term in each equivalence class. A term is _simplest_ +if it is smallest in a well-order, such as a ground Knuth-Bendix order. +A basic approach is terms that are of smallest depth, are values can be chosen as simplest. +Ties between equal-depth terms can be resolved arbitrarily. + + +Algorithm for extracting canonical form from an E-graph: + +* Compute function canon(t) that maps every term in E to a canonical, least with respect to well-order relative to the congruence closure. + That is, terms that are equal modulo the congruence closure have the same canonical representative. + +* Each f(t) = g(s) in E: + * add f(canon(t)) = canon(f(t)), g(canon(s)) = canon(g(s)) where canon(f(t)) = canon(g(s)) by construction. + +* Each other g(t) in E: + * add g(canon(t)) to E. + * Note that canon(g(t)) = true because g(t) = true is added to congruence closure of E. +* We claim the new formula is equivalent. +* The dependencies for each rewrite can be computed by following the equality justification data-structure. + + +--*/ + +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/euf/euf_egraph.h" +#include "ast/simplifiers/euf_completion.h" + +namespace euf { + + completion::completion(ast_manager& m, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_egraph(m), + m_canonical(m), + m_eargs(m), + m_deps(m), + m_rewriter(m) { + m_tt = m_egraph.mk(m.mk_true(), 0, 0, nullptr); + m_ff = m_egraph.mk(m.mk_false(), 0, 0, nullptr); + } + + void completion::reduce() { + ++m_epoch; + add_egraph(); + map_canonical(); + read_egraph(); + } + + void completion::add_egraph() { + m_nodes.reset(); + unsigned sz = m_fmls.size(); + expr* x, *y; + for (unsigned i = m_qhead; i < sz; ++i) { + auto [f,d] = m_fmls[i](); + auto* n = mk_enode(f); + if (m.is_eq(f, x, y)) + m_egraph.merge(n->get_arg(0), n->get_arg(1), d); + if (m.is_not(f, x)) + m_egraph.merge(n->get_arg(0), m_ff, d); + else + m_egraph.merge(n, m_tt, d); + } + m_egraph.propagate(); + } + + void completion::read_egraph() { + + if (m_egraph.inconsistent()) { + auto* d = explain_conflict(); + dependent_expr de(m, m.mk_false(), d); + m_fmls.update(0, de); + return; + } + + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + auto [f, d] = m_fmls[i](); + auto* n = m_egraph.find(f); + SASSERT(n); + + expr_dependency_ref dep(d, m); + expr_ref g = canonize_fml(f, dep); + if (g != f) { + m_fmls.update(i, dependent_expr(m, g, dep)); + m_stats.m_num_rewrites++; + IF_VERBOSE(10, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); + } + CTRACE("euf_completion", g != f, tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); + } + advance_qhead(m_fmls.size()); + } + + enode* completion::mk_enode(expr* e) { + m_todo.push_back(e); + enode* n; + while (!m_todo.empty()) { + e = m_todo.back(); + if (m_egraph.find(e)) { + m_todo.pop_back(); + continue; + } + if (!is_app(e)) { + m_nodes.push_back(m_egraph.mk(e, 0, 0, nullptr)); + m_todo.pop_back(); + continue; + } + m_args.reset(); + unsigned sz = m_todo.size(); + for (expr* arg : *to_app(e)) { + n = m_egraph.find(arg); + if (n) + m_args.push_back(n); + else + m_todo.push_back(arg); + } + if (sz == m_todo.size()) { + m_nodes.push_back(m_egraph.mk(e, 0, m_args.size(), m_args.data())); + m_todo.pop_back(); + } + } + return m_egraph.find(e); + } + + expr_ref completion::canonize_fml(expr* f, expr_dependency_ref& d) { + + expr* x, * y; + if (m.is_eq(f, x, y)) { + expr_ref x1 = canonize(x, d); + expr_ref y1 = canonize(y, d); + + if (x == x1 && y == y1) + return expr_ref(f, m); + if (x1 == y1) + return expr_ref(m.mk_true(), m); + else { + expr* c = get_canonical(x, d); + if (c == x1) + return expr_ref(m.mk_eq(y1, c), m); + else if (c == y1) + return expr_ref(m.mk_eq(x1, c), m); + else + return expr_ref(m.mk_and(m.mk_eq(x1, c), m.mk_eq(y1, c)), m); + } + } + + if (m.is_not(f, x)) { + expr_ref x1 = canonize(x, d); + return expr_ref(mk_not(m, x1), m); + } + + return canonize(f, d); + } + + expr_ref completion::canonize(expr* f, expr_dependency_ref& d) { + if (!is_app(f)) + return expr_ref(f, m); // todo could normalize ground expressions under quantifiers + + m_eargs.reset(); + bool change = false; + for (expr* arg : *to_app(f)) { + m_eargs.push_back(get_canonical(arg, d)); + change = arg != m_eargs.back(); + } + + if (!change) + return expr_ref(f, m); + else + return expr_ref(m_rewriter.mk_app(to_app(f)->get_decl(), m_eargs.size(), m_eargs.data()), m); + } + + expr* completion::get_canonical(expr* f, expr_dependency_ref& d) { + enode* n = m_egraph.find(f); + enode* r = n->get_root(); + d = m.mk_join(d, explain_eq(n, r)); + d = m.mk_join(d, m_deps.get(r->get_id(), nullptr)); + return m_canonical.get(r->get_id()); + } + + expr* completion::get_canonical(enode* n) { + if (m_epochs.get(n->get_id(), 0) == m_epoch) + return m_canonical.get(n->get_id()); + else + return nullptr; + } + + void completion::set_canonical(enode* n, expr* e) { + class vtrail : public trail { + expr_ref_vector& c; + unsigned idx; + expr_ref old_value; + public: + vtrail(expr_ref_vector& c, unsigned idx) : + c(c), idx(idx), old_value(c.get(idx), c.m()) { + } + + void undo() override { + c[idx] = old_value; + old_value = nullptr; + } + }; + if (m_num_scopes > 0) + m_trail.push(vtrail(m_canonical, n->get_id())); + m_canonical.setx(n->get_id(), e); + m_epochs.setx(n->get_id(), m_epoch, 0); + } + + expr_dependency* completion::explain_eq(enode* a, enode* b) { + if (a == b) + return nullptr; + ptr_vector just; + m_egraph.begin_explain(); + m_egraph.explain_eq(just, nullptr, a, b); + m_egraph.end_explain(); + expr_dependency* d = nullptr; + for (expr_dependency* d2 : just) + d = m.mk_join(d, d2); + return d; + } + + expr_dependency* completion::explain_conflict() { + ptr_vector just; + m_egraph.begin_explain(); + m_egraph.explain(just, nullptr); + m_egraph.end_explain(); + expr_dependency* d = nullptr; + for (expr_dependency* d2 : just) + d = m.mk_join(d, d2); + return d; + } + + void completion::collect_statistics(statistics& st) const { + st.update("euf-completion-rewrites", m_stats.m_num_rewrites); + } + + void completion::map_canonical() { + m_todo.reset(); + enode_vector roots; + for (unsigned i = 0; i < m_nodes.size(); ++i) { + enode* n = m_nodes[i]->get_root(); + if (n->is_marked1()) + continue; + n->mark1(); + roots.push_back(n); + enode* rep = nullptr; + for (enode* k : enode_class(n)) + if (!rep || m.is_value(k->get_expr()) || get_depth(rep->get_expr()) > get_depth(k->get_expr())) + rep = k; + m_reps.setx(n->get_id(), rep, nullptr); + + TRACE("euf_completion", tout << "rep " << m_egraph.bpp(n) << " -> " << m_egraph.bpp(rep) << "\n"; + for (enode* k : enode_class(n)) tout << m_egraph.bpp(k) << "\n";); + m_todo.push_back(n->get_expr()); + for (enode* arg : enode_args(n)) { + arg = arg->get_root(); + if (!arg->is_marked1()) + m_nodes.push_back(arg); + } + } + for (enode* r : roots) + r->unmark1(); + + // explain dependencies when no nodes are marked. + // explain_eq uses both mark1 and mark2 on e-nodes so + // we cannot call it inside the previous loop where mark1 is used + // to track which roots have been processed. + for (enode* r : roots) { + enode* rep = m_reps[r->get_id()]; + auto* d = explain_eq(r, rep); + m_deps.setx(r->get_id(), d); + } + expr_ref new_expr(m); + while (!m_todo.empty()) { + expr* e = m_todo.back(); + enode* n = m_egraph.find(e); + SASSERT(n->is_root()); + enode* rep = m_reps[n->get_id()]; + if (get_canonical(n)) + m_todo.pop_back(); + else if (get_depth(rep->get_expr()) == 0 || !is_app(rep->get_expr())) { + set_canonical(n, rep->get_expr()); + m_todo.pop_back(); + } + else { + m_eargs.reset(); + unsigned sz = m_todo.size(); + bool new_arg = false; + expr_dependency* d = m_deps.get(n->get_id(), nullptr); + for (enode* arg : enode_args(rep)) { + enode* rarg = arg->get_root(); + expr* c = get_canonical(rarg); + if (c) { + m_eargs.push_back(c); + new_arg |= c != arg->get_expr(); + d = m.mk_join(d, m_deps.get(rarg->get_id(), nullptr)); + } + else + m_todo.push_back(rarg->get_expr()); + } + if (sz == m_todo.size()) { + m_todo.pop_back(); + if (new_arg) + new_expr = m_rewriter.mk_app(to_app(rep->get_expr())->get_decl(), m_eargs.size(), m_eargs.data()); + else + new_expr = rep->get_expr(); + set_canonical(n, new_expr); + m_deps.setx(n->get_id(), d); + } + } + } + } +} + + diff --git a/src/ast/simplifiers/euf_completion.h b/src/ast/simplifiers/euf_completion.h new file mode 100644 index 000000000..9e293ca0e --- /dev/null +++ b/src/ast/simplifiers/euf_completion.h @@ -0,0 +1,63 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + euf_completion.h + +Abstract: + + Ground completion for equalities + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-30 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/euf/euf_egraph.h" +#include "ast/rewriter/th_rewriter.h" + +namespace euf { + + class completion : public dependent_expr_simplifier { + + struct stats { + unsigned m_num_rewrites = 0; + void reset() { memset(this, 0, sizeof(*this)); } + }; + + egraph m_egraph; + enode* m_tt, *m_ff; + ptr_vector m_todo; + enode_vector m_args, m_reps, m_nodes; + expr_ref_vector m_canonical, m_eargs; + expr_dependency_ref_vector m_deps; + unsigned m_epoch = 0; + unsigned_vector m_epochs; + th_rewriter m_rewriter; + stats m_stats; + + enode* mk_enode(expr* e); + void add_egraph(); + void map_canonical(); + void read_egraph(); + expr_ref canonize(expr* f, expr_dependency_ref& dep); + expr_ref canonize_fml(expr* f, expr_dependency_ref& dep); + expr* get_canonical(expr* f, expr_dependency_ref& d); + expr* get_canonical(enode* n); + void set_canonical(enode* n, expr* e); + expr_dependency* explain_eq(enode* a, enode* b); + expr_dependency* explain_conflict(); + public: + completion(ast_manager& m, dependent_expr_state& fmls); + void push() override { m_egraph.push(); dependent_expr_simplifier::push(); } + void pop(unsigned n) override { dependent_expr_simplifier::pop(n); m_egraph.pop(n); } + void reduce() override; + void collect_statistics(statistics& st) const override; + void reset_statistics() override { m_stats.reset(); } + }; +} diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index 114e4f849..b9b4394d9 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -17,6 +17,7 @@ z3_add_component(tactic COMPONENT_DEPENDENCIES ast model + simplifiers TACTIC_HEADERS probe.h tactic.h diff --git a/src/tactic/bv/CMakeLists.txt b/src/tactic/bv/CMakeLists.txt index e9f0927d5..653571265 100644 --- a/src/tactic/bv/CMakeLists.txt +++ b/src/tactic/bv/CMakeLists.txt @@ -8,6 +8,7 @@ z3_add_component(bv_tactics bv_bound_chk_tactic.cpp bv_bounds_tactic.cpp bv_size_reduction_tactic.cpp + bv_slice_tactic.cpp dt2bv_tactic.cpp elim_small_bv_tactic.cpp max_bv_sharing_tactic.cpp @@ -21,6 +22,7 @@ z3_add_component(bv_tactics bv_bound_chk_tactic.h bv_bounds_tactic.h bv_size_reduction_tactic.h + bv_slice_tactic.h bvarray2uf_tactic.h dt2bv_tactic.h elim_small_bv_tactic.h diff --git a/src/tactic/bv/bv_slice_tactic.h b/src/tactic/bv/bv_slice_tactic.h new file mode 100644 index 000000000..23ed16680 --- /dev/null +++ b/src/tactic/bv/bv_slice_tactic.h @@ -0,0 +1,29 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + bv_slice_tactic.h + +Abstract: + + Tactic for simplifying with bit-vector slices + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-30 + +--*/ +#pragma once + +#include "util/params.h" +class ast_manager; +class tactic; + +tactic * mk_bv_slice_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("bv-slice", "simplify using bit-vector slices.", "mk_bv_slice_tactic(m, p)") +*/ + + diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index a247c7b20..e57510d4f 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -10,6 +10,7 @@ z3_add_component(core_tactics dom_simplify_tactic.cpp elim_term_ite_tactic.cpp elim_uncnstr_tactic.cpp + euf_completion_tactic.cpp injectivity_tactic.cpp nnf_tactic.cpp occf_tactic.cpp @@ -38,6 +39,7 @@ z3_add_component(core_tactics dom_simplify_tactic.h elim_term_ite_tactic.h elim_uncnstr_tactic.h + euf_completion_tactic.h injectivity_tactic.h nnf_tactic.h occf_tactic.h diff --git a/src/tactic/core/euf_completion_tactic.cpp b/src/tactic/core/euf_completion_tactic.cpp new file mode 100644 index 000000000..bdd940f17 --- /dev/null +++ b/src/tactic/core/euf_completion_tactic.cpp @@ -0,0 +1,32 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + euf_completion_tactic.cpp + +Abstract: + + Tactic for simplifying with equations. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-30 + +--*/ + +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/euf_completion.h" +#include "tactic/core/euf_completion_tactic.h" + +class euf_completion_tactic_factory : public dependent_expr_simplifier_factory { +public: + dependent_expr_simplifier* mk(ast_manager& m, params_ref const& p, dependent_expr_state& s) override { + return alloc(euf::completion, m, s); + } +}; + +tactic * mk_euf_completion_tactic(ast_manager& m, params_ref const& p) { + return alloc(dependent_expr_state_tactic, m, p, alloc(euf_completion_tactic_factory), "euf-completion"); +} diff --git a/src/tactic/core/euf_completion_tactic.h b/src/tactic/core/euf_completion_tactic.h new file mode 100644 index 000000000..36511bbe3 --- /dev/null +++ b/src/tactic/core/euf_completion_tactic.h @@ -0,0 +1,29 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + euf_completion_tactic.h + +Abstract: + + Tactic for simplifying with equations. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-30 + +--*/ +#pragma once + +#include "util/params.h" +class ast_manager; +class tactic; + +tactic * mk_euf_completion_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("euf-completion", "simplify using equalities.", "mk_euf_completion_tactic(m, p)") +*/ + + diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h new file mode 100644 index 000000000..c62125459 --- /dev/null +++ b/src/tactic/dependent_expr_state_tactic.h @@ -0,0 +1,101 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + dependent_expr_state_tactic.h + +Abstract: + + The dependent_expr_state_tactic creates a tactic from a dependent_expr_simplifier. + It relies on a factory for building simplifiers. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ +#include "tactic/tactic.h" +#include "ast/simplifiers/dependent_expr_state.h" + +class dependent_expr_state_tactic : public tactic, public dependent_expr_state { + ast_manager& m; + params_ref m_params; + std::string m_name; + ref m_factory; + scoped_ptr m_simp; + goal_ref m_goal; + dependent_expr m_dep; + + void init() { + if (!m_simp) + m_simp = m_factory->mk(m, m_params, *this); + } + +public: + + dependent_expr_state_tactic(ast_manager& m, params_ref const& p, dependent_expr_simplifier_factory* f, char const* name): + m(m), + m_params(p), + m_name(name), + m_factory(f), + m_simp(f->mk(m, p, *this)), + m_dep(m, m.mk_true(), nullptr) + {} + + /** + * size(), [](), update() and inconsisent() implement the abstract interface of dependent_expr_state + */ + unsigned size() const override { return m_goal->size(); } + + dependent_expr const& operator[](unsigned i) override { + m_dep = dependent_expr(m, m_goal->form(i), m_goal->dep(i)); + return m_dep; + } + void update(unsigned i, dependent_expr const& j) override { + auto [f, d] = j(); + m_goal->update(i, f, nullptr, d); + } + + bool inconsistent() override { + return m_goal->inconsistent(); + } + + char const* name() const override { return m_name.c_str(); } + + void updt_params(params_ref const & p) override { + m_params.append(p); + init(); + m_simp->updt_params(m_params); + } + + tactic * translate(ast_manager & m) override { + return alloc(dependent_expr_state_tactic, m, m_params, m_factory.get(), name()); + } + + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + if (in->proofs_enabled()) + throw tactic_exception("tactic does not support low level proofs"); + init(); + tactic_report report(name(), *in); + m_goal = in.get(); + m_simp->reduce(); + m_goal->inc_depth(); + result.push_back(in.get()); + } + + void cleanup() override { + } + + void collect_statistics(statistics & st) const override { + if (m_simp) + m_simp->collect_statistics(st); + } + + void reset_statistics() override { + if (m_simp) + m_simp->reset_statistics(); + } +}; + From 41b87b4c42382eedd96cc757a6bfcb9b6e3363f4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 08:51:43 -0700 Subject: [PATCH 05/57] Create bv_slice_tactic.cpp missing file --- src/tactic/bv/bv_slice_tactic.cpp | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/tactic/bv/bv_slice_tactic.cpp diff --git a/src/tactic/bv/bv_slice_tactic.cpp b/src/tactic/bv/bv_slice_tactic.cpp new file mode 100644 index 000000000..040068e39 --- /dev/null +++ b/src/tactic/bv/bv_slice_tactic.cpp @@ -0,0 +1,33 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bv_slice_tactic.cpp + +Abstract: + + Tactic for simplifying with bit-vector slices + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-30 + +--*/ + +#include "ast/simplifiers/bv_slice.h" +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "tactic/bv/bv_slice_tactic.h" + + +class bv_slice_factory : public dependent_expr_simplifier_factory { +public: + dependent_expr_simplifier* mk(ast_manager& m, params_ref const& p, dependent_expr_state& s) override { + return alloc(bv::slice, m, s); + } +}; + +tactic* mk_bv_slice_tactic(ast_manager& m, params_ref const& p) { + return alloc(dependent_expr_state_tactic, m, p, alloc(bv_slice_factory), "bv-slice"); +} From 0d97d2214c53bb71d091b78455ad90304df9bcd1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 09:37:55 -0700 Subject: [PATCH 06/57] adding virtual destructor Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/dependent_expr_state.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 5156d1126..e67aa56cc 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -39,6 +39,7 @@ Author: */ class dependent_expr_state { public: + virtual ~dependent_expr_state() {} virtual unsigned size() const = 0; virtual dependent_expr const& operator[](unsigned i) = 0; virtual void update(unsigned i, dependent_expr const& j) = 0; From ae707ffff7b3a4e4f33c4dae114362a2709d4376 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Wed, 2 Nov 2022 18:02:29 +0100 Subject: [PATCH 07/57] Added 64-bit "1" counting (#6434) * Memory leak in .NET user-propagator The user-propagator object has to be manually disposed (IDisposable), otherwise it stays in memory forever, as it cannot be garbage collected automatically * Throw an exception if variable passed to decide is already assigned instead of running in an assertion violation * Added 64-bit "1" counting --- src/util/util.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/util/util.h b/src/util/util.h index f08558f37..9d129f9a7 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -103,6 +103,7 @@ unsigned uint64_log2(uint64_t v); static_assert(sizeof(unsigned) == 4, "unsigned are 32 bits"); // Return the number of 1 bits in v. +// see e.g. http://en.wikipedia.org/wiki/Hamming_weight static inline unsigned get_num_1bits(unsigned v) { #ifdef __GNUC__ return __builtin_popcount(v); @@ -122,6 +123,25 @@ static inline unsigned get_num_1bits(unsigned v) { #endif } +static inline unsigned get_num_1bits(uint64_t v) { +#ifdef __GNUC__ + return __builtin_popcountll(v); +#else +#ifdef Z3DEBUG + unsigned c; + uint64_t v1 = v; + for (c = 0; v1; c++) { + v1 &= v1 - 1; + } +#endif + v = v - (v >> 1) & 0x5555555555555555; + v = (v & 0x3333333333333333) + ((v >> 2) & 0x3333333333333333); + v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0F; + uint64_t r = (v * 0x0101010101010101) >> 56; + SASSERT(c == r); +#endif +} + // Remark: on gcc, the operators << and >> do not produce zero when the second argument >= 64. // So, I'm using the following two definitions to fix the problem static inline uint64_t shift_right(uint64_t x, uint64_t y) { From df71e834280c493ec2d2cc2a621ad21435787736 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 17:32:09 -0700 Subject: [PATCH 08/57] remove incorrect assertion Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/euf_completion.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 068f6598e..e5b328d7f 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -92,8 +92,6 @@ namespace euf { for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { auto [f, d] = m_fmls[i](); - auto* n = m_egraph.find(f); - SASSERT(n); expr_dependency_ref dep(d, m); expr_ref g = canonize_fml(f, dep); From 6790f18132041040ffaa098b853afa57637c6ddf Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Thu, 3 Nov 2022 11:34:52 +0100 Subject: [PATCH 09/57] Added limit to "visit" to allow detecting multiple visits (#6435) * Memory leak in .NET user-propagator The user-propagator object has to be manually disposed (IDisposable), otherwise it stays in memory forever, as it cannot be garbage collected automatically * Throw an exception if variable passed to decide is already assigned instead of running in an assertion violation * Added limit to "visit" to allow detecting multiple visits * Putting visit in a separate class (Reason: We will probably need two of them in the sat::solver) * Bugfix --- src/sat/sat_gc.cpp | 4 ++-- src/sat/sat_lut_finder.cpp | 12 ++++++------ src/sat/sat_solver.cpp | 32 +++++++------------------------ src/sat/sat_solver.h | 13 ++++--------- src/sat/sat_xor_finder.cpp | 12 ++++++------ src/sat/smt/pb_solver.cpp | 8 ++++---- src/util/visit_helper.h | 39 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 52 deletions(-) create mode 100644 src/util/visit_helper.h diff --git a/src/sat/sat_gc.cpp b/src/sat/sat_gc.cpp index a655956db..69e91c745 100644 --- a/src/sat/sat_gc.cpp +++ b/src/sat/sat_gc.cpp @@ -406,9 +406,9 @@ namespace sat { auto gc_watch = [&](literal lit) { auto& wl1 = get_wlist(lit); for (auto w : get_wlist(lit)) { - if (w.is_binary_clause() && w.get_literal().var() < max_var && !is_visited(w.get_literal())) { + if (w.is_binary_clause() && w.get_literal().var() < max_var && !m_visited.is_visited(w.get_literal())) { m_aux_literals.push_back(w.get_literal()); - mark_visited(w.get_literal()); + m_visited.mark_visited(w.get_literal()); } } wl1.reset(); diff --git a/src/sat/sat_lut_finder.cpp b/src/sat/sat_lut_finder.cpp index 5459ab2a4..26ec80143 100644 --- a/src/sat/sat_lut_finder.cpp +++ b/src/sat/sat_lut_finder.cpp @@ -70,7 +70,7 @@ namespace sat { for (literal l : m_clause) { m_vars.push_back(l.var()); m_var_position[l.var()] = i; - s.mark_visited(l.var()); + s.m_visited.mark_visited(l.var()); mask |= (l.sign() << (i++)); } m_clauses_to_remove.reset(); @@ -91,7 +91,7 @@ namespace sat { // TBD: replace by BIG // loop over binary clauses in watch list for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_lut(~l, w.get_literal())) { add_lut(); return; @@ -100,7 +100,7 @@ namespace sat { } l.neg(); for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_lut(~l, w.get_literal())) { add_lut(); return; @@ -124,8 +124,8 @@ namespace sat { } bool lut_finder::extract_lut(literal l1, literal l2) { - SASSERT(s.is_visited(l1.var())); - SASSERT(s.is_visited(l2.var())); + SASSERT(s.m_visited.is_visited(l1.var())); + SASSERT(s.m_visited.is_visited(l2.var())); m_missing.reset(); unsigned mask = 0; for (unsigned i = 0; i < m_vars.size(); ++i) { @@ -144,7 +144,7 @@ namespace sat { bool lut_finder::extract_lut(clause& c2) { for (literal l : c2) { - if (!s.is_visited(l.var())) + if (!s.m_visited.is_visited(l.var())) return false; } if (c2.size() == m_vars.size()) { diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index eea3a2475..f97a08001 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -3441,10 +3441,10 @@ namespace sat { for (unsigned i = m_clauses_to_reinit.size(); i-- > old_sz; ) { clause_wrapper const& cw = m_clauses_to_reinit[i]; for (unsigned j = cw.size(); j-- > 0; ) - mark_visited(cw[j].var()); + m_visited.mark_visited(cw[j].var()); } for (literal lit : m_lemma) - mark_visited(lit.var()); + m_visited.mark_visited(lit.var()); auto is_active = [&](bool_var v) { return value(v) != l_undef && lvl(v) <= new_lvl; @@ -3452,7 +3452,7 @@ namespace sat { for (unsigned i = old_num_vars; i < sz; ++i) { bool_var v = m_active_vars[i]; - if (is_external(v) || is_visited(v) || is_active(v)) { + if (is_external(v) || m_visited.is_visited(v) || is_active(v)) { m_vars_to_reinit.push_back(v); m_active_vars[j++] = v; m_var_scope[v] = new_lvl; @@ -4697,10 +4697,10 @@ namespace sat { bool solver::all_distinct(literal_vector const& lits) { init_visited(); for (literal l : lits) { - if (is_visited(l.var())) { + if (m_visited.is_visited(l.var())) { return false; } - mark_visited(l.var()); + m_visited.mark_visited(l.var()); } return true; } @@ -4708,30 +4708,12 @@ namespace sat { bool solver::all_distinct(clause const& c) { init_visited(); for (literal l : c) { - if (is_visited(l.var())) { + if (m_visited.is_visited(l.var())) { return false; } - mark_visited(l.var()); + m_visited.mark_visited(l.var()); } return true; } - void solver::init_ts(unsigned n, svector& v, unsigned& ts) { - if (v.empty()) - ts = 0; - - ts++; - if (ts == 0) { - ts = 1; - v.reset(); - } - while (v.size() < n) - v.push_back(0); - } - - void solver::init_visited() { - init_ts(2 * num_vars(), m_visited, m_visited_ts); - } - - }; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 2e53e4620..b75950f88 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -28,6 +28,7 @@ Revision History: #include "util/rlimit.h" #include "util/scoped_ptr_vector.h" #include "util/scoped_limit_trail.h" +#include "util/visit_helper.h" #include "sat/sat_types.h" #include "sat/sat_clause.h" #include "sat/sat_watched.h" @@ -176,8 +177,7 @@ namespace sat { std::string m_reason_unknown; bool m_trim = false; - svector m_visited; - unsigned m_visited_ts; + visit_helper m_visited; struct scope { unsigned m_trail_lim; @@ -342,13 +342,8 @@ namespace sat { void detach_nary_clause(clause & c); void push_reinit_stack(clause & c); void push_reinit_stack(literal l1, literal l2); - - void init_ts(unsigned n, svector& v, unsigned& ts); - void init_visited(); - void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; } - void mark_visited(bool_var v) { mark_visited(literal(v, false)); } - bool is_visited(bool_var v) const { return is_visited(literal(v, false)); } - bool is_visited(literal l) const { return m_visited[l.index()] == m_visited_ts; } + + void init_visited(unsigned lim = 1) { m_visited.init_visited(num_vars(), lim); } bool all_distinct(literal_vector const& lits); bool all_distinct(clause const& cl); diff --git a/src/sat/sat_xor_finder.cpp b/src/sat/sat_xor_finder.cpp index dbe08d96c..0a20f4782 100644 --- a/src/sat/sat_xor_finder.cpp +++ b/src/sat/sat_xor_finder.cpp @@ -62,7 +62,7 @@ namespace sat { unsigned mask = 0, i = 0; for (literal l : c) { m_var_position[l.var()] = i; - s.mark_visited(l.var()); + s.m_visited.mark_visited(l.var()); parity ^= !l.sign(); mask |= (!l.sign() << (i++)); } @@ -84,7 +84,7 @@ namespace sat { } // loop over binary clauses in watch list for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_xor(parity, c, ~l, w.get_literal())) { add_xor(parity, c); return; @@ -93,7 +93,7 @@ namespace sat { } l.neg(); for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_xor(parity, c, ~l, w.get_literal())) { add_xor(parity, c); return; @@ -122,8 +122,8 @@ namespace sat { } bool xor_finder::extract_xor(bool parity, clause& c, literal l1, literal l2) { - SASSERT(s.is_visited(l1.var())); - SASSERT(s.is_visited(l2.var())); + SASSERT(s.m_visited.is_visited(l1.var())); + SASSERT(s.m_visited.is_visited(l2.var())); m_missing.reset(); unsigned mask = 0; for (unsigned i = 0; i < c.size(); ++i) { @@ -144,7 +144,7 @@ namespace sat { bool xor_finder::extract_xor(bool parity, clause& c, clause& c2) { bool parity2 = false; for (literal l : c2) { - if (!s.is_visited(l.var())) return false; + if (!s.m_visited.is_visited(l.var())) return false; parity2 ^= !l.sign(); } if (c2.size() == c.size() && parity2 != parity) { diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index 424b20d4e..5b2d851d3 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2709,10 +2709,10 @@ namespace pb { } void solver::init_visited() { s().init_visited(); } - void solver::mark_visited(literal l) { s().mark_visited(l); } - void solver::mark_visited(bool_var v) { s().mark_visited(v); } - bool solver::is_visited(bool_var v) const { return s().is_visited(v); } - bool solver::is_visited(literal l) const { return s().is_visited(l); } + void solver::mark_visited(literal l) { s().m_visited.mark_visited(l); } + void solver::mark_visited(bool_var v) { s().m_visited.mark_visited(v); } + bool solver::is_visited(bool_var v) const { return s().m_visited.is_visited(v); } + bool solver::is_visited(literal l) const { return s().m_visited.is_visited(l); } void solver::cleanup_clauses() { if (m_clause_removed) { diff --git a/src/util/visit_helper.h b/src/util/visit_helper.h new file mode 100644 index 000000000..1a0d4f5b9 --- /dev/null +++ b/src/util/visit_helper.h @@ -0,0 +1,39 @@ +#pragma once +#include "sat_literal.h" + +class visit_helper { + + unsigned_vector m_visited; + unsigned m_visited_begin = 0; + unsigned m_visited_end = 0; + + void init_ts(unsigned n, unsigned lim = 1) { + SASSERT(lim > 0); + if (m_visited_end >= m_visited_end + lim) { // overflow + m_visited_begin = 0; + m_visited_end = lim; + m_visited.reset(); + } + else { + m_visited_begin = m_visited_end; + m_visited_end = m_visited_end + lim; + } + while (m_visited.size() < n) + m_visited.push_back(0); + } + +public: + + void init_visited(unsigned num_vars, unsigned lim = 1) { + init_ts(2 * num_vars, lim); + } + void mark_visited(sat::literal l) { m_visited[l.index()] = m_visited_begin + 1; } + void mark_visited(sat::bool_var v) { mark_visited(sat::literal(v, false)); } + void inc_visited(sat::literal l) { + m_visited[l.index()] = std::min(m_visited_end, std::max(m_visited_begin, m_visited[l.index()]) + 1); + } + void inc_visited(sat::bool_var v) { inc_visited(sat::literal(v, false)); } + bool is_visited(sat::bool_var v) const { return is_visited(sat::literal(v, false)); } + bool is_visited(sat::literal l) const { return m_visited[l.index()] > m_visited_begin; } + unsigned num_visited(unsigned i) { return std::max(m_visited_begin, m_visited[i]) - m_visited_begin; } +}; \ No newline at end of file From e1417597688c8cd5057971316977db4841489b7d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 15:59:08 -0700 Subject: [PATCH 10/57] init solve_eqs --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/solve_eqs.cpp | 116 +++++++++++++++++++++++++++++ src/ast/simplifiers/solve_eqs.h | 75 +++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 src/ast/simplifiers/solve_eqs.cpp create mode 100644 src/ast/simplifiers/solve_eqs.h diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 650410415..d07220fb5 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -2,6 +2,7 @@ z3_add_component(simplifiers SOURCES euf_completion.cpp bv_slice.cpp + solve_eqs.cpp COMPONENT_DEPENDENCIES euf rewriter diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp new file mode 100644 index 000000000..77808c142 --- /dev/null +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -0,0 +1,116 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + solve_eqs.cpp + +Abstract: + + simplifier for solving equations + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + + + +#include "ast/simplifiers/solve_eqs.h" + + +namespace euf { + + + void solve_eqs::init() { + + } + + // initialize graph that maps variable ids to next ids + void solve_eqs::extract_dep_graph(dep_eq_vector& eqs) { + m_var2id.reset(); + m_id2var.reset(); + m_next.reset(); + unsigned sz = 0; + for (auto const& [v, t, d] : eqs) + sz = std::max(sz, v->get_id()); + m_var2id.resize(sz + 1, UINT_MAX); + for (auto const& [v, t, d] : eqs) { + if (is_var(v)) + continue; + m_var2id[v->get_id()] = m_id2var.size(); + m_id2var.push_back(v); + } + m_next.resize(m_id2var.size()); + + for (auto const& [v, t, d] : eqs) + m_next[var2id(v)].push_back(t); + } + + void solve_eqs::add_subst(app* v, expr* term) { + + } + + /** + * Build a substitution while assigning levels to terms. + * The substitution is well-formed when variables are replaced with terms whose + * Free variables have higher levels. + */ + void solve_eqs::extract_subst() { + m_var2level.reset(); + m_var2level.resize(m_id2var.size(), UINT_MAX); + auto is_explored = [&](unsigned id) { + return m_var2level[id] != UINT_MAX; + }; + auto is_safe = [&](unsigned lvl, expr* t) { + for (auto* e : subterms::all(expr_ref(t, m))) + if (is_var(e) && m_var2level[var2id(e)] < lvl) + return false; + }; + + unsigned init_level = UINT_MAX; + for (unsigned id = 0; id < m_id2var.size(); ++id) { + if (is_explored(id)) + continue; + // initialize current level to have enough room to assign different levels to all variables. + init_level -= m_id2var.size() + 1; + unsigned curr_level = init_level; + todo.push_back(id); + while (!todo.empty()) { + unsigned j = todo.back(); + todo.pop_back(); + if (is_explored(j)) + continue; + m_var2level[id] = curr_level++; + for (expr* t : m_next[j]) { + if (!is_safe(curr_level, t)) + continue; + add_subst(m_id2var[j], t); + for (auto* e : subterms::all(expr_ref(t, m))) + if (is_var(e) && !is_explored(var2id(e))) + todo.push_back(var2id(e)); + break; + } + } + } + } + + void solve_eqs::extract_subst(dep_eq_vector& eqs, dep_eq_vector& subst) { + + } + + void solve_eqs::apply_subst() { + + } + + void solve_eqs::reduce() { + init(); + dep_eq_vector eqs, subst; + get_eqs(eqs); + extract_subst(eqs, subst); + apply_subst(); + advance_qhead(m_fmls.size()); + } + +} diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h new file mode 100644 index 000000000..936816bc4 --- /dev/null +++ b/src/ast/simplifiers/solve_eqs.h @@ -0,0 +1,75 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + solve_eqs.h + +Abstract: + + simplifier for solving equations + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" + + +namespace euf { + + struct dependent_eq { + app* var; + expr_ref term; + expr_dependency* dep; + dependent_eq(app* var, expr_ref& term, expr_dependency* d) : var(var), term(term), dep(d) {} + }; + + typedef vector dep_eq_vector; + + class extract_eq { + pulic: + virtual ~extract_eq() {} + virtual void get_eqs(depdendent_expr const& e, dep_eq_vector& eqs) = 0; + }; + + class solve_eqs : public dependent_expr_simplifier { + th_rewriter m_rewriter; + scoped_ptr_vector m_extract_plugins; + unsigned_vector m_var2id; + ptr_vector m_id2var; + vector m_next; + + void init(); + + bool is_var(expr* v) const; + unsigned var2id(expr* v) const { return m_var2id[v->get_id()]; } + + void get_eqs(dep_eq_vector& eqs) { + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) + get_eqs(m_fmls[i](), eqs); + } + + void get_eqs(dependent_expr const& f, dep_eq_vector& eqs) { + for (auto* ex : m_extract_plugins) + ex->get_eqs(f, eqs); + } + + void extract_subst(dep_eq_vector& eqs, dep_eq_vector& subst); + void apply_subst(); + + public: + + solve_eqs(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_rewriter(m) {} + + void push() override { dependent_expr_simplifier::push(); } + void pop(unsigned n) override { dependent_expr_simplifier::pop(n); } + void reduce() override; + }; +} From c0f483528dd6ee2de51f02e5c4c0814552060d06 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 17:31:25 -0700 Subject: [PATCH 11/57] working on solve_eqs --- src/ast/simplifiers/solve_eqs.cpp | 149 +++++++++++++++++++++++++----- src/ast/simplifiers/solve_eqs.h | 23 +++-- 2 files changed, 141 insertions(+), 31 deletions(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 77808c142..a47a05cee 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -16,16 +16,69 @@ Author: --*/ - +#include "util/trace.h" +#include "ast/ast_util.h" +#include "ast/for_each_expr.h" +#include "ast/ast_pp.h" +#include "ast/arith_decl_plugin.h" +#include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/solve_eqs.h" namespace euf { + class basic_extract_eq : public extract_eq { + ast_manager& m; + public: + basic_extract_eq(ast_manager& m) : m(m) {} + void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) { + auto [f, d] = e(); + expr* x, * y; + if (m.is_eq(f, x, y)) { + if (is_uninterp_const(x)) + eqs.push_back(dependent_eq(to_app(x), expr_ref(y, m), d)); + if (is_uninterp_const(y)) + eqs.push_back(dependent_eq(to_app(y), expr_ref(x, m), d)); + } + expr* c, * th, * el, * x1, * y1, * x2, * y2; + if (m.is_ite(f, c, th, el)) { + if (m.is_eq(th, x1, y1) && m.is_eq(el, x2, y2)) { + if (x1 == y2 && is_uninterp_const(x1)) + std::swap(x2, y2); + if (x2 == y2 && is_uninterp_const(x2)) + std::swap(x2, y2), std::swap(x1, y1); + if (x2 == y1 && is_uninterp_const(x2)) + std::swap(x1, y1); + if (x1 == x2 && is_uninterp_const(x1)) + eqs.push_back(dependent_eq(to_app(x1), expr_ref(m.mk_ite(c, y1, y2), m), d)); + } + } + if (is_uninterp_const(f)) + eqs.push_back(dependent_eq(to_app(f), expr_ref(m.mk_true(), m), d)); + if (m.is_not(f, x) && is_uninterp_const(x)) + eqs.push_back(dependent_eq(to_app(x), expr_ref(m.mk_false(), m), d)); + } + }; - void solve_eqs::init() { + class arith_extract_eq : public extract_eq { + ast_manager& m; + arith_util a; +#if 0 + void solve_eq(expr* f, expr_depedency* d) { - } + } +#endif + public: + arith_extract_eq(ast_manager& m) : m(m), a(m) {} + void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) { +#if 0 + auto [f, d] = e(); + expr* x, * y; + if (m.is_eq(f, x, y) && a.is_int_real(x)) + ; +#endif + } + }; // initialize graph that maps variable ids to next ids void solve_eqs::extract_dep_graph(dep_eq_vector& eqs) { @@ -44,12 +97,8 @@ namespace euf { } m_next.resize(m_id2var.size()); - for (auto const& [v, t, d] : eqs) - m_next[var2id(v)].push_back(t); - } - - void solve_eqs::add_subst(app* v, expr* term) { - + for (auto const& eq : eqs) + m_next[var2id(eq.var)].push_back(eq); } /** @@ -58,22 +107,30 @@ namespace euf { * Free variables have higher levels. */ void solve_eqs::extract_subst() { - m_var2level.reset(); - m_var2level.resize(m_id2var.size(), UINT_MAX); + m_id2level.reset(); + m_id2level.resize(m_id2var.size(), UINT_MAX); + m_subst_ids.reset(); + m_subst = alloc(expr_substitution, m, false, false); + auto is_explored = [&](unsigned id) { - return m_var2level[id] != UINT_MAX; + return m_id2level[id] != UINT_MAX; }; + auto is_safe = [&](unsigned lvl, expr* t) { for (auto* e : subterms::all(expr_ref(t, m))) - if (is_var(e) && m_var2level[var2id(e)] < lvl) + if (is_var(e) && m_id2level[var2id(e)] < lvl) return false; + return true; }; unsigned init_level = UINT_MAX; + unsigned_vector todo; for (unsigned id = 0; id < m_id2var.size(); ++id) { if (is_explored(id)) continue; // initialize current level to have enough room to assign different levels to all variables. + if (init_level < m_id2var.size() + 1) + return; init_level -= m_id2var.size() + 1; unsigned curr_level = init_level; todo.push_back(id); @@ -82,12 +139,14 @@ namespace euf { todo.pop_back(); if (is_explored(j)) continue; - m_var2level[id] = curr_level++; - for (expr* t : m_next[j]) { + m_id2level[id] = curr_level++; + for (auto const& eq : m_next[j]) { + auto const& [v, t, d] = eq; if (!is_safe(curr_level, t)) continue; - add_subst(m_id2var[j], t); - for (auto* e : subterms::all(expr_ref(t, m))) + m_next[j][0] = eq; + m_subst_ids.push_back(id); + for (expr* e : subterms::all(expr_ref(t, m))) if (is_var(e) && !is_explored(var2id(e))) todo.push_back(var2id(e)); break; @@ -96,19 +155,65 @@ namespace euf { } } - void solve_eqs::extract_subst(dep_eq_vector& eqs, dep_eq_vector& subst) { + void solve_eqs::add_subst(dependent_eq const& eq) { + m_subst->insert(eq.var, eq.term, nullptr, eq.dep); + } + void solve_eqs::normalize() { + scoped_ptr rp = mk_default_expr_replacer(m, true); + m_subst->reset(); + rp->set_substitution(m_subst.get()); + + std::sort(m_subst_ids.begin(), m_subst_ids.end(), [&](unsigned u, unsigned v) { return m_id2level[u] > m_id2level[v]; }); + + expr_dependency_ref new_dep(m); + expr_ref new_def(m); + proof_ref new_pr(m); + + for (unsigned id : m_subst_ids) { + // checkpoint(); + auto const& [v, def, dep] = m_next[id][0]; + rp->operator()(def, new_def, new_pr, new_dep); + // m_num_steps += rp->get_num_steps() + 1; + new_dep = m.mk_join(dep, new_dep); + m_subst->insert(v, new_def, new_pr, new_dep); + // we updated the substitution, but we don't need to reset rp + // because all cached values there do not depend on v. + } + + TRACE("solve_eqs", + tout << "after normalizing variables\n"; + for (unsigned id : m_subst_ids) { + auto const& eq = m_next[id][0]; + expr* def = nullptr; + proof* pr = nullptr; + expr_dependency* dep = nullptr; + m_subst->find(eq.var, def, pr, dep); + tout << mk_pp(eq.var, m) << "\n----->\n" << mk_pp(def, m) << "\n\n"; + }); } void solve_eqs::apply_subst() { - + scoped_ptr rp = mk_default_expr_replacer(m, true); + rp->set_substitution(m_subst.get()); + expr_ref new_f(m); + proof_ref new_pr(m); + expr_dependency_ref new_dep(m); + for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) { + auto [f, d] = m_fmls[i](); + rp->operator()(f, new_f, new_pr, new_dep); + if (new_f == f) + continue; + new_dep = m.mk_join(d, new_dep); + m_fmls.update(i, dependent_expr(m, new_f, new_dep)); + } } void solve_eqs::reduce() { - init(); - dep_eq_vector eqs, subst; + dep_eq_vector eqs; get_eqs(eqs); - extract_subst(eqs, subst); + extract_dep_graph(eqs); + extract_subst(); apply_subst(); advance_qhead(m_fmls.size()); } diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 936816bc4..55cad7e67 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -20,6 +20,8 @@ Author: #include "ast/simplifiers/dependent_expr_state.h" #include "ast/rewriter/th_rewriter.h" +#include "ast/expr_substitution.h" +#include "util/scoped_ptr_vector.h" namespace euf { @@ -34,34 +36,37 @@ namespace euf { typedef vector dep_eq_vector; class extract_eq { - pulic: + public: virtual ~extract_eq() {} - virtual void get_eqs(depdendent_expr const& e, dep_eq_vector& eqs) = 0; + virtual void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) = 0; }; class solve_eqs : public dependent_expr_simplifier { th_rewriter m_rewriter; scoped_ptr_vector m_extract_plugins; - unsigned_vector m_var2id; + unsigned_vector m_var2id, m_id2level, m_subst_ids; ptr_vector m_id2var; - vector m_next; + vector m_next; + scoped_ptr m_subst; - void init(); + void add_subst(dependent_eq const& eq); - bool is_var(expr* v) const; + bool is_var(expr* e) const { return e->get_id() < m_var2id.size() && m_var2id[e->get_id()] != UINT_MAX; } unsigned var2id(expr* v) const { return m_var2id[v->get_id()]; } void get_eqs(dep_eq_vector& eqs) { for (unsigned i = m_qhead; i < m_fmls.size(); ++i) - get_eqs(m_fmls[i](), eqs); + get_eqs(m_fmls[i], eqs); } void get_eqs(dependent_expr const& f, dep_eq_vector& eqs) { - for (auto* ex : m_extract_plugins) + for (extract_eq* ex : m_extract_plugins) ex->get_eqs(f, eqs); } - void extract_subst(dep_eq_vector& eqs, dep_eq_vector& subst); + void extract_subst(); + void extract_dep_graph(dep_eq_vector& eqs); + void normalize(); void apply_subst(); public: From 6841ba3e57cc01839830025f53a3891cbe2928f8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Nov 2022 20:14:49 -0700 Subject: [PATCH 12/57] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 17465d9ae..936f977aa 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,4 @@ CMakeSettings.json *.swp .DS_Store dbg/** +*.wsp From 070c5c624a7148aba6e5b56a1283984796dc5fab Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 03:33:31 -0700 Subject: [PATCH 13/57] wip - converting the equation solver as a simplifier --- src/ast/arith_decl_plugin.h | 3 + src/ast/simplifiers/CMakeLists.txt | 3 +- src/ast/simplifiers/extract_eqs.cpp | 239 ++++++++++++++++++++++++++++ src/ast/simplifiers/extract_eqs.h | 47 ++++++ src/ast/simplifiers/solve_eqs.cpp | 74 +++------ src/ast/simplifiers/solve_eqs.h | 22 +-- 6 files changed, 316 insertions(+), 72 deletions(-) create mode 100644 src/ast/simplifiers/extract_eqs.cpp create mode 100644 src/ast/simplifiers/extract_eqs.h diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 781996662..0c77867d4 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -453,6 +453,9 @@ public: app * mk_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(arith_family_id, OP_MUL, arg1, arg2); } app * mk_mul(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(arith_family_id, OP_MUL, arg1, arg2, arg3); } app * mk_mul(unsigned num_args, expr * const * args) const { return num_args == 1 && is_app(args[0]) ? to_app(args[0]) : m_manager.mk_app(arith_family_id, OP_MUL, num_args, args); } + app * mk_mul(ptr_buffer const& args) const { return mk_mul(args.size(), args.data()); } + app * mk_mul(ptr_vector const& args) const { return mk_mul(args.size(), args.data()); } + app * mk_mul(expr_ref_vector const& args) const { return mk_mul(args.size(), args.data()); } app * mk_uminus(expr * arg) const { return m_manager.mk_app(arith_family_id, OP_UMINUS, arg); } app * mk_div(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_DIV, arg1, arg2); } app * mk_idiv(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_IDIV, arg1, arg2); } diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index d07220fb5..a260dd3b7 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -1,7 +1,8 @@ z3_add_component(simplifiers SOURCES - euf_completion.cpp bv_slice.cpp + euf_completion.cpp + extract_eqs.cpp solve_eqs.cpp COMPONENT_DEPENDENCIES euf diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp new file mode 100644 index 000000000..99be4268c --- /dev/null +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -0,0 +1,239 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + extract_eqs.cpp + +Abstract: + + simplifier for solving equations + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + + +#include "ast/ast_util.h" +#include "ast/for_each_expr.h" +#include "ast/ast_pp.h" +#include "ast/arith_decl_plugin.h" +#include "ast/simplifiers/extract_eqs.h" + + +namespace euf { + + class basic_extract_eq : public extract_eq { + ast_manager& m; + + public: + basic_extract_eq(ast_manager& m) : m(m) {} + + void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { + auto [f, d] = e(); + expr* x, * y; + if (m.is_eq(f, x, y)) { + if (is_uninterp_const(x)) + eqs.push_back(dependent_eq(to_app(x), expr_ref(y, m), d)); + if (is_uninterp_const(y)) + eqs.push_back(dependent_eq(to_app(y), expr_ref(x, m), d)); + } + expr* c, * th, * el, * x1, * y1, * x2, * y2; + if (m.is_ite(f, c, th, el)) { + if (m.is_eq(th, x1, y1) && m.is_eq(el, x2, y2)) { + if (x1 == y2 && is_uninterp_const(x1)) + std::swap(x2, y2); + if (x2 == y2 && is_uninterp_const(x2)) + std::swap(x2, y2), std::swap(x1, y1); + if (x2 == y1 && is_uninterp_const(x2)) + std::swap(x1, y1); + if (x1 == x2 && is_uninterp_const(x1)) + eqs.push_back(dependent_eq(to_app(x1), expr_ref(m.mk_ite(c, y1, y2), m), d)); + } + } + if (is_uninterp_const(f)) + eqs.push_back(dependent_eq(to_app(f), expr_ref(m.mk_true(), m), d)); + if (m.is_not(f, x) && is_uninterp_const(x)) + eqs.push_back(dependent_eq(to_app(x), expr_ref(m.mk_false(), m), d)); + } + }; + + class arith_extract_eq : public extract_eq { + ast_manager& m; + arith_util a; + expr_ref_vector m_args; + expr_sparse_mark m_nonzero; + + + // solve u mod r1 = y -> u = r1*mod!1 + y + void solve_mod(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + expr* u, * z; + rational r1, r2; + if (!a.is_mod(x, u, z)) + return; + if (!a.is_numeral(z, r1)) + return; + if (r1 <= 0) + return; + expr_ref term(m); + term = a.mk_add(a.mk_mul(z, m.mk_fresh_const("mod", a.mk_int())), y); + solve_eq(u, term, d, eqs); + } + + /*** + * Solve + * x + Y = Z -> x = Z - Y + * -1*x + Y = Z -> x = Y - Z + * a*x + Y = Z -> x = (Z - Y)/a for is-real(x) + */ + void solve_add(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + if (!a.is_add(x)) + return; + expr* u, * z; + rational r; + expr_ref term(m); + unsigned i = 0; + auto mk_term = [&](unsigned i) { + term = y; + unsigned j = 0; + for (expr* arg2 : *to_app(x)) { + if (i != j) + term = a.mk_sub(term, arg2); + ++j; + } + }; + for (expr* arg : *to_app(x)) { + if (is_uninterp_const(arg)) { + mk_term(i); + eqs.push_back(dependent_eq(to_app(arg), term, d)); + } + else if (a.is_mul(arg, u, z) && a.is_numeral(u, r) && is_uninterp_const(z)) { + if (r == -1) { + mk_term(i); + term = a.mk_uminus(term); + eqs.push_back(dependent_eq(to_app(z), term, d)); + } + else if (a.is_real(arg) && r != 0) { + mk_term(i); + term = a.mk_div(term, u); + eqs.push_back(dependent_eq(to_app(z), term, d)); + } + } + else if (a.is_real(arg) && a.is_mul(arg)) { + unsigned j = 0; + for (expr* xarg : *to_app(arg)) { + ++j; + if (!is_uninterp_const(xarg)) + continue; + unsigned k = 0; + bool nonzero = true; + for (expr* yarg : *to_app(arg)) { + ++k; + nonzero = k == j || m_nonzero.is_marked(yarg) || (a.is_numeral(yarg, r) && r != 0); + if (!nonzero) + break; + } + if (!nonzero) + continue; + + k = 0; + ptr_buffer args; + for (expr* yarg : *to_app(arg)) { + ++k; + if (k != j) + args.push_back(yarg); + } + mk_term(i); + term = a.mk_div(term, a.mk_mul(args.size(), args.data())); + eqs.push_back(dependent_eq(to_app(xarg), term, d)); + } + } + ++i; + } + } + + /*** + * Solve for x * Y = Z, where Y != 0 -> x = Z / Y + */ + void solve_mul(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + if (!a.is_mul(x)) + return; + rational r; + expr_ref term(m); + unsigned i = 0; + for (expr* arg : *to_app(x)) { + ++i; + if (!is_uninterp_const(arg)) + continue; + unsigned j = 0; + bool nonzero = true; + for (expr* arg2 : *to_app(x)) { + ++j; + nonzero = j == i || m_nonzero.is_marked(arg2) || (a.is_numeral(arg2, r) && r != 0); + if (!nonzero) + break; + } + if (!nonzero) + continue; + ptr_buffer args; + j = 0; + for (expr* arg2 : *to_app(x)) { + ++j; + if (j != i) + args.push_back(arg2); + } + term = a.mk_div(y, a.mk_mul(args)); + eqs.push_back(dependent_eq(to_app(arg), term, d)); + } + } + + void add_pos(expr* f) { + expr* lhs = nullptr, * rhs = nullptr; + rational val; + if (a.is_le(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_neg()) + m_nonzero.mark(lhs); + else if (a.is_ge(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_pos()) + m_nonzero.mark(lhs); + else if (m.is_not(f, f)) { + if (a.is_le(f, lhs, rhs) && a.is_numeral(rhs, val) && !val.is_neg()) + m_nonzero.mark(lhs); + else if (a.is_ge(f, lhs, rhs) && a.is_numeral(rhs, val) && !val.is_pos()) + m_nonzero.mark(lhs); + else if (m.is_eq(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_zero()) + m_nonzero.mark(lhs); + } + } + + void solve_eq(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + solve_add(x, y, d, eqs); + solve_mod(x, y, d, eqs); + solve_mul(x, y, d, eqs); + } + + public: + arith_extract_eq(ast_manager& m) : m(m), a(m), m_args(m) {} + void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { + auto [f, d] = e(); + expr* x, * y; + if (m.is_eq(f, x, y) && a.is_int_real(x)) { + solve_eq(x, y, d, eqs); + solve_eq(y, x, d, eqs); + } + } + + void pre_process(dependent_expr_state& fmls) override { + m_nonzero.reset(); + for (unsigned i = 0; i < fmls.size(); ++i) { + auto [f, d] = fmls[i](); + add_pos(f); + } + } + }; + + void register_extract_eqs(ast_manager& m, scoped_ptr_vector& ex) { + ex.push_back(alloc(arith_extract_eq, m)); + ex.push_back(alloc(basic_extract_eq, m)); + } +} diff --git a/src/ast/simplifiers/extract_eqs.h b/src/ast/simplifiers/extract_eqs.h new file mode 100644 index 000000000..e6c81bb20 --- /dev/null +++ b/src/ast/simplifiers/extract_eqs.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + extract_eqs.h + +Abstract: + + simplifier for solving equations + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/expr_substitution.h" +#include "util/scoped_ptr_vector.h" + + +namespace euf { + + struct dependent_eq { + app* var; + expr_ref term; + expr_dependency* dep; + dependent_eq(app* var, expr_ref& term, expr_dependency* d) : var(var), term(term), dep(d) {} + }; + + typedef vector dep_eq_vector; + + class extract_eq { + public: + virtual ~extract_eq() {} + virtual void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) = 0; + virtual void pre_process(dependent_expr_state& fmls) {} + }; + + void register_extract_eqs(ast_manager& m, scoped_ptr_vector& ex); + +} diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index a47a05cee..d9fbf9664 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -27,59 +27,6 @@ Author: namespace euf { - class basic_extract_eq : public extract_eq { - ast_manager& m; - public: - basic_extract_eq(ast_manager& m) : m(m) {} - void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) { - auto [f, d] = e(); - expr* x, * y; - if (m.is_eq(f, x, y)) { - if (is_uninterp_const(x)) - eqs.push_back(dependent_eq(to_app(x), expr_ref(y, m), d)); - if (is_uninterp_const(y)) - eqs.push_back(dependent_eq(to_app(y), expr_ref(x, m), d)); - } - expr* c, * th, * el, * x1, * y1, * x2, * y2; - if (m.is_ite(f, c, th, el)) { - if (m.is_eq(th, x1, y1) && m.is_eq(el, x2, y2)) { - if (x1 == y2 && is_uninterp_const(x1)) - std::swap(x2, y2); - if (x2 == y2 && is_uninterp_const(x2)) - std::swap(x2, y2), std::swap(x1, y1); - if (x2 == y1 && is_uninterp_const(x2)) - std::swap(x1, y1); - if (x1 == x2 && is_uninterp_const(x1)) - eqs.push_back(dependent_eq(to_app(x1), expr_ref(m.mk_ite(c, y1, y2), m), d)); - } - } - if (is_uninterp_const(f)) - eqs.push_back(dependent_eq(to_app(f), expr_ref(m.mk_true(), m), d)); - if (m.is_not(f, x) && is_uninterp_const(x)) - eqs.push_back(dependent_eq(to_app(x), expr_ref(m.mk_false(), m), d)); - } - }; - - class arith_extract_eq : public extract_eq { - ast_manager& m; - arith_util a; -#if 0 - void solve_eq(expr* f, expr_depedency* d) { - - } -#endif - public: - arith_extract_eq(ast_manager& m) : m(m), a(m) {} - void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) { -#if 0 - auto [f, d] = e(); - expr* x, * y; - if (m.is_eq(f, x, y) && a.is_int_real(x)) - ; -#endif - } - }; - // initialize graph that maps variable ids to next ids void solve_eqs::extract_dep_graph(dep_eq_vector& eqs) { m_var2id.reset(); @@ -210,6 +157,11 @@ namespace euf { } void solve_eqs::reduce() { + + for (extract_eq* ex : m_extract_plugins) + ex->pre_process(m_fmls); + + // TODO add a loop. dep_eq_vector eqs; get_eqs(eqs); extract_dep_graph(eqs); @@ -218,4 +170,20 @@ namespace euf { advance_qhead(m_fmls.size()); } + solve_eqs::solve_eqs(ast_manager& m, dependent_expr_state& fmls) : + dependent_expr_simplifier(m, fmls), m_rewriter(m) { + register_extract_eqs(m, m_extract_plugins); + } + + void solve_eqs::updt_params(params_ref const& p) { + // TODO +#if 0 + tactic_params tp(m_params); + m_ite_solver = p.get_bool("ite_solver", tp.solve_eqs_ite_solver()); + m_theory_solver = p.get_bool("theory_solver", tp.solve_eqs_theory_solver()); + m_max_occs = p.get_uint("solve_eqs_max_occs", tp.solve_eqs_max_occs()); + m_context_solve = p.get_bool("context_solve", tp.solve_eqs_context_solve()); +#endif + } + } diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 55cad7e67..942498a52 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -18,29 +18,13 @@ Author: #pragma once -#include "ast/simplifiers/dependent_expr_state.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_substitution.h" #include "util/scoped_ptr_vector.h" - +#include "ast/simplifiers/extract_eqs.h" namespace euf { - struct dependent_eq { - app* var; - expr_ref term; - expr_dependency* dep; - dependent_eq(app* var, expr_ref& term, expr_dependency* d) : var(var), term(term), dep(d) {} - }; - - typedef vector dep_eq_vector; - - class extract_eq { - public: - virtual ~extract_eq() {} - virtual void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) = 0; - }; - class solve_eqs : public dependent_expr_simplifier { th_rewriter m_rewriter; scoped_ptr_vector m_extract_plugins; @@ -71,10 +55,12 @@ namespace euf { public: - solve_eqs(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_rewriter(m) {} + solve_eqs(ast_manager& m, dependent_expr_state& fmls); void push() override { dependent_expr_simplifier::push(); } void pop(unsigned n) override { dependent_expr_simplifier::pop(n); } void reduce() override; + + void updt_params(params_ref const& p) override; }; } From 90490cb22f2600dd45826ba7070c97e091077298 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 03:54:39 -0700 Subject: [PATCH 14/57] make visited_helper independent of literals re-introduce shorthands in sat::solver for visited and have them convert literals to unsigned. --- src/sat/sat_gc.cpp | 4 ++-- src/sat/sat_lut_finder.cpp | 8 +++---- src/sat/sat_solver.cpp | 14 ++++++------ src/sat/sat_solver.h | 6 +++++- src/sat/sat_xor_finder.cpp | 8 +++---- src/sat/smt/pb_solver.cpp | 8 +++---- src/util/visit_helper.h | 44 +++++++++++++++++++++++--------------- 7 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/sat/sat_gc.cpp b/src/sat/sat_gc.cpp index 69e91c745..a655956db 100644 --- a/src/sat/sat_gc.cpp +++ b/src/sat/sat_gc.cpp @@ -406,9 +406,9 @@ namespace sat { auto gc_watch = [&](literal lit) { auto& wl1 = get_wlist(lit); for (auto w : get_wlist(lit)) { - if (w.is_binary_clause() && w.get_literal().var() < max_var && !m_visited.is_visited(w.get_literal())) { + if (w.is_binary_clause() && w.get_literal().var() < max_var && !is_visited(w.get_literal())) { m_aux_literals.push_back(w.get_literal()); - m_visited.mark_visited(w.get_literal()); + mark_visited(w.get_literal()); } } wl1.reset(); diff --git a/src/sat/sat_lut_finder.cpp b/src/sat/sat_lut_finder.cpp index 26ec80143..60143f91c 100644 --- a/src/sat/sat_lut_finder.cpp +++ b/src/sat/sat_lut_finder.cpp @@ -70,7 +70,7 @@ namespace sat { for (literal l : m_clause) { m_vars.push_back(l.var()); m_var_position[l.var()] = i; - s.m_visited.mark_visited(l.var()); + s.mark_visited(l.var()); mask |= (l.sign() << (i++)); } m_clauses_to_remove.reset(); @@ -91,7 +91,7 @@ namespace sat { // TBD: replace by BIG // loop over binary clauses in watch list for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_lut(~l, w.get_literal())) { add_lut(); return; @@ -100,7 +100,7 @@ namespace sat { } l.neg(); for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_lut(~l, w.get_literal())) { add_lut(); return; @@ -144,7 +144,7 @@ namespace sat { bool lut_finder::extract_lut(clause& c2) { for (literal l : c2) { - if (!s.m_visited.is_visited(l.var())) + if (!s.is_visited(l.var())) return false; } if (c2.size() == m_vars.size()) { diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index f97a08001..14e7e9775 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -3441,10 +3441,10 @@ namespace sat { for (unsigned i = m_clauses_to_reinit.size(); i-- > old_sz; ) { clause_wrapper const& cw = m_clauses_to_reinit[i]; for (unsigned j = cw.size(); j-- > 0; ) - m_visited.mark_visited(cw[j].var()); + mark_visited(cw[j].var()); } for (literal lit : m_lemma) - m_visited.mark_visited(lit.var()); + mark_visited(lit.var()); auto is_active = [&](bool_var v) { return value(v) != l_undef && lvl(v) <= new_lvl; @@ -3452,7 +3452,7 @@ namespace sat { for (unsigned i = old_num_vars; i < sz; ++i) { bool_var v = m_active_vars[i]; - if (is_external(v) || m_visited.is_visited(v) || is_active(v)) { + if (is_external(v) || is_visited(v) || is_active(v)) { m_vars_to_reinit.push_back(v); m_active_vars[j++] = v; m_var_scope[v] = new_lvl; @@ -4697,10 +4697,10 @@ namespace sat { bool solver::all_distinct(literal_vector const& lits) { init_visited(); for (literal l : lits) { - if (m_visited.is_visited(l.var())) { + if (is_visited(l.var())) { return false; } - m_visited.mark_visited(l.var()); + mark_visited(l.var()); } return true; } @@ -4708,10 +4708,10 @@ namespace sat { bool solver::all_distinct(clause const& c) { init_visited(); for (literal l : c) { - if (m_visited.is_visited(l.var())) { + if (is_visited(l.var())) { return false; } - m_visited.mark_visited(l.var()); + mark_visited(l.var()); } return true; } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index b75950f88..982a84307 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -343,7 +343,11 @@ namespace sat { void push_reinit_stack(clause & c); void push_reinit_stack(literal l1, literal l2); - void init_visited(unsigned lim = 1) { m_visited.init_visited(num_vars(), lim); } + void init_visited(unsigned lim = 1) { m_visited.init_visited(2 * num_vars(), lim); } + bool is_visited(sat::bool_var v) const { return is_visited(literal(v, false)); } + bool is_visited(literal lit) const { return m_visited.is_visited(lit.index()); } + void mark_visited(literal lit) { m_visited.mark_visited(lit.index()); } + void mark_visited(bool_var v) { mark_visited(literal(v, false)); } bool all_distinct(literal_vector const& lits); bool all_distinct(clause const& cl); diff --git a/src/sat/sat_xor_finder.cpp b/src/sat/sat_xor_finder.cpp index 0a20f4782..a34d1b7ad 100644 --- a/src/sat/sat_xor_finder.cpp +++ b/src/sat/sat_xor_finder.cpp @@ -62,7 +62,7 @@ namespace sat { unsigned mask = 0, i = 0; for (literal l : c) { m_var_position[l.var()] = i; - s.m_visited.mark_visited(l.var()); + s.mark_visited(l.var()); parity ^= !l.sign(); mask |= (!l.sign() << (i++)); } @@ -84,7 +84,7 @@ namespace sat { } // loop over binary clauses in watch list for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_xor(parity, c, ~l, w.get_literal())) { add_xor(parity, c); return; @@ -93,7 +93,7 @@ namespace sat { } l.neg(); for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.m_visited.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { + if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_xor(parity, c, ~l, w.get_literal())) { add_xor(parity, c); return; @@ -144,7 +144,7 @@ namespace sat { bool xor_finder::extract_xor(bool parity, clause& c, clause& c2) { bool parity2 = false; for (literal l : c2) { - if (!s.m_visited.is_visited(l.var())) return false; + if (!s.is_visited(l.var())) return false; parity2 ^= !l.sign(); } if (c2.size() == c.size() && parity2 != parity) { diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index 5b2d851d3..424b20d4e 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2709,10 +2709,10 @@ namespace pb { } void solver::init_visited() { s().init_visited(); } - void solver::mark_visited(literal l) { s().m_visited.mark_visited(l); } - void solver::mark_visited(bool_var v) { s().m_visited.mark_visited(v); } - bool solver::is_visited(bool_var v) const { return s().m_visited.is_visited(v); } - bool solver::is_visited(literal l) const { return s().m_visited.is_visited(l); } + void solver::mark_visited(literal l) { s().mark_visited(l); } + void solver::mark_visited(bool_var v) { s().mark_visited(v); } + bool solver::is_visited(bool_var v) const { return s().is_visited(v); } + bool solver::is_visited(literal l) const { return s().is_visited(l); } void solver::cleanup_clauses() { if (m_clause_removed) { diff --git a/src/util/visit_helper.h b/src/util/visit_helper.h index 1a0d4f5b9..a11d7bdc6 100644 --- a/src/util/visit_helper.h +++ b/src/util/visit_helper.h @@ -1,5 +1,21 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + visit_helper.h + +Abstract: + + Routine for marking and counting visited occurrences + +Author: + + Clemens Eisenhofer 2022-11-03 + +--*/ #pragma once -#include "sat_literal.h" + class visit_helper { @@ -7,7 +23,9 @@ class visit_helper { unsigned m_visited_begin = 0; unsigned m_visited_end = 0; - void init_ts(unsigned n, unsigned lim = 1) { +public: + + void init_visited(unsigned n, unsigned lim = 1) { SASSERT(lim > 0); if (m_visited_end >= m_visited_end + lim) { // overflow m_visited_begin = 0; @@ -18,22 +36,14 @@ class visit_helper { m_visited_begin = m_visited_end; m_visited_end = m_visited_end + lim; } - while (m_visited.size() < n) - m_visited.push_back(0); + while (m_visited.size() < n) + m_visited.push_back(0); } -public: - - void init_visited(unsigned num_vars, unsigned lim = 1) { - init_ts(2 * num_vars, lim); + void mark_visited(unsigned v) { m_visited[v] = m_visited_begin + 1; } + void inc_visited(unsigned v) { + m_visited[v] = std::min(m_visited_end, std::max(m_visited_begin, m_visited[v]) + 1); } - void mark_visited(sat::literal l) { m_visited[l.index()] = m_visited_begin + 1; } - void mark_visited(sat::bool_var v) { mark_visited(sat::literal(v, false)); } - void inc_visited(sat::literal l) { - m_visited[l.index()] = std::min(m_visited_end, std::max(m_visited_begin, m_visited[l.index()]) + 1); - } - void inc_visited(sat::bool_var v) { inc_visited(sat::literal(v, false)); } - bool is_visited(sat::bool_var v) const { return is_visited(sat::literal(v, false)); } - bool is_visited(sat::literal l) const { return m_visited[l.index()] > m_visited_begin; } - unsigned num_visited(unsigned i) { return std::max(m_visited_begin, m_visited[i]) - m_visited_begin; } + bool is_visited(unsigned v) const { return m_visited[v] > m_visited_begin; } + unsigned num_visited(unsigned v) { return std::max(m_visited_begin, m_visited[v]) - m_visited_begin; } }; \ No newline at end of file From 7b12a5c5a8b2ace6551fe084c0b766bab52e40b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 04:49:20 -0700 Subject: [PATCH 15/57] build fix --- src/ast/expr_substitution.h | 1 + src/ast/simplifiers/extract_eqs.h | 2 +- src/ast/simplifiers/solve_eqs.cpp | 35 ++++++++++++++++++++++++---- src/ast/simplifiers/solve_eqs.h | 13 +++++++++++ src/tactic/core/solve_eqs_tactic.cpp | 10 ++------ 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index bbd1c0e8e..6d4b1b618 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -45,6 +45,7 @@ public: unsigned size() const { return m_subst.size(); } void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr); void erase(expr * s); + expr* find(expr* s) { proof* pr; expr* def; VERIFY(find(s, def, pr)); SASSERT(def); return def; } bool find(expr * s, expr * & def, proof * & def_pr); bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep); bool contains(expr * s); diff --git a/src/ast/simplifiers/extract_eqs.h b/src/ast/simplifiers/extract_eqs.h index e6c81bb20..29c136028 100644 --- a/src/ast/simplifiers/extract_eqs.h +++ b/src/ast/simplifiers/extract_eqs.h @@ -30,7 +30,7 @@ namespace euf { app* var; expr_ref term; expr_dependency* dep; - dependent_eq(app* var, expr_ref& term, expr_dependency* d) : var(var), term(term), dep(d) {} + dependent_eq(app* var, expr_ref const& term, expr_dependency* d) : var(var), term(term), dep(d) {} }; typedef vector dep_eq_vector; diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index d9fbf9664..38100fcdc 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -20,7 +20,7 @@ Author: #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" -#include "ast/arith_decl_plugin.h" +#include "ast/recfun_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/solve_eqs.h" @@ -37,7 +37,7 @@ namespace euf { sz = std::max(sz, v->get_id()); m_var2id.resize(sz + 1, UINT_MAX); for (auto const& [v, t, d] : eqs) { - if (is_var(v)) + if (is_var(v) || !can_be_var(v)) continue; m_var2id[v->get_id()] = m_id2var.size(); m_id2var.push_back(v); @@ -103,7 +103,9 @@ namespace euf { } void solve_eqs::add_subst(dependent_eq const& eq) { + SASSERT(can_be_var(eq.var)); m_subst->insert(eq.var, eq.term, nullptr, eq.dep); + ++m_stats.m_num_elim_vars; } void solve_eqs::normalize() { @@ -118,10 +120,11 @@ namespace euf { proof_ref new_pr(m); for (unsigned id : m_subst_ids) { - // checkpoint(); + if (!m.inc()) + break; auto const& [v, def, dep] = m_next[id][0]; rp->operator()(def, new_def, new_pr, new_dep); - // m_num_steps += rp->get_num_steps() + 1; + m_stats.m_num_steps += rp->get_num_steps() + 1; new_dep = m.mk_join(dep, new_dep); m_subst->insert(v, new_def, new_pr, new_dep); // we updated the substitution, but we don't need to reset rp @@ -141,6 +144,8 @@ namespace euf { } void solve_eqs::apply_subst() { + if (!m.inc()) + return; scoped_ptr rp = mk_default_expr_replacer(m, true); rp->set_substitution(m_subst.get()); expr_ref new_f(m); @@ -170,6 +175,23 @@ namespace euf { advance_qhead(m_fmls.size()); } + void solve_eqs::filter_unsafe_vars() { + m_unsafe_vars.reset(); + recfun::util rec(m); + for (func_decl* f : rec.get_rec_funs()) + for (expr* term : subterms::all(expr_ref(rec.get_def(f).get_rhs(), m))) + m_unsafe_vars.mark(term); + } + +#if 0 + model_converter_ref solve_eqs::get_model_converter() { + model_converter_ref mc = alloc(gmc, m, "solve-eqs"); + for (unsigned id : m_subst_ids) + static_cast(mc.get())->add(id2var(id), m_subst->find(v)); + return mc; + } +#endif + solve_eqs::solve_eqs(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_rewriter(m) { register_extract_eqs(m, m_extract_plugins); @@ -186,4 +208,9 @@ namespace euf { #endif } + void solve_eqs::collect_statistics(statistics& st) const { + st.update("solve-eqs-steps", m_stats.m_num_steps); + st.update("solve-eqs-elim-vars", m_stats.m_num_elim_vars); + } + } diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 942498a52..62f24413a 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -26,6 +26,11 @@ Author: namespace euf { class solve_eqs : public dependent_expr_simplifier { + struct stats { + unsigned m_num_steps = 0; + unsigned m_num_elim_vars = 0; + }; + th_rewriter m_rewriter; scoped_ptr_vector m_extract_plugins; unsigned_vector m_var2id, m_id2level, m_subst_ids; @@ -33,6 +38,9 @@ namespace euf { vector m_next; scoped_ptr m_subst; + expr_mark m_unsafe_vars; // expressions that cannot be replaced + stats m_stats; + void add_subst(dependent_eq const& eq); bool is_var(expr* e) const { return e->get_id() < m_var2id.size() && m_var2id[e->get_id()] != UINT_MAX; } @@ -48,6 +56,8 @@ namespace euf { ex->get_eqs(f, eqs); } + void filter_unsafe_vars(); + bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e); } void extract_subst(); void extract_dep_graph(dep_eq_vector& eqs); void normalize(); @@ -62,5 +72,8 @@ namespace euf { void reduce() override; void updt_params(params_ref const& p) override; + void collect_statistics(statistics& st) const override; + + // model_converter_ref get_model_converter(); }; } diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index 5a076aa0f..0a0525e70 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -977,14 +977,8 @@ class solve_eqs_tactic : public tactic { if (m_produce_models) { if (!mc.get()) mc = alloc(gmc, m(), "solve-eqs"); - for (app* v : m_ordered_vars) { - expr * def = nullptr; - proof * pr; - expr_dependency * dep = nullptr; - m_norm_subst->find(v, def, pr, dep); - SASSERT(def); - static_cast(mc.get())->add(v, def); - } + for (app* v : m_ordered_vars) + static_cast(mc.get())->add(v, m_norm_subst->find(v)); } } From 1dca6402fba7ddb1ed1df914f7f6252c9cd2900a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 05:23:01 -0700 Subject: [PATCH 16/57] move model and proof converters to self-contained module --- src/CMakeLists.txt | 1 + .../ackermannize_bv_model_converter.h | 2 +- src/ackermannization/ackr_model_converter.h | 2 +- .../lackr_model_converter_lazy.h | 2 +- src/ast/converters/CMakeLists.txt | 8 +++ src/{tactic => ast/converters}/converter.h | 0 .../converters}/generic_model_converter.cpp | 2 +- .../converters}/generic_model_converter.h | 2 +- .../converters}/model_converter.cpp | 2 +- .../converters}/model_converter.h | 2 +- .../converters}/proof_converter.cpp | 38 +---------- .../converters}/proof_converter.h | 9 +-- src/ast/simplifiers/dependent_expr_state.h | 2 + src/ast/simplifiers/solve_eqs.cpp | 11 ++-- src/ast/simplifiers/solve_eqs.h | 6 +- src/cmd_context/cmd_context.cpp | 2 +- src/cmd_context/cmd_context.h | 2 +- src/muz/base/dl_context.h | 2 +- src/muz/base/dl_rule.cpp | 2 +- src/muz/base/dl_rule.h | 4 +- src/muz/base/hnf.h | 2 +- src/muz/fp/horn_tactic.cpp | 6 +- src/muz/spacer/spacer_manager.cpp | 2 +- src/muz/transforms/dl_mk_bit_blast.cpp | 2 +- src/muz/transforms/dl_mk_coi_filter.cpp | 2 +- .../transforms/dl_mk_subsumption_checker.cpp | 2 +- src/nlsat/tactic/goal2nlsat.h | 2 +- src/opt/opt_context.cpp | 2 +- src/opt/opt_context.h | 2 +- src/opt/opt_solver.h | 2 +- src/opt/sortmax.cpp | 2 +- src/qe/qsat.h | 2 +- src/sat/smt/euf_solver.h | 2 +- src/sat/tactic/goal2sat.cpp | 2 +- src/sat/tactic/sat2goal.cpp | 2 +- src/sat/tactic/sat2goal.h | 2 +- src/smt/tactic/smt_tactic_core.cpp | 2 +- src/smt/theory_arith_aux.h | 2 +- src/smt/theory_lra.cpp | 2 +- src/smt/theory_wmaxsat.h | 2 +- src/solver/check_sat_result.h | 2 +- src/solver/solver.cpp | 2 +- src/solver/solver2tactic.cpp | 2 +- src/solver/solver2tactic.h | 2 +- src/tactic/CMakeLists.txt | 4 +- src/tactic/arith/card2bv_tactic.cpp | 2 +- src/tactic/arith/degree_shift_tactic.cpp | 2 +- src/tactic/arith/fix_dl_var_tactic.cpp | 2 +- src/tactic/arith/lia2card_tactic.cpp | 2 +- src/tactic/arith/lia2pb_tactic.cpp | 2 +- src/tactic/arith/nla2bv_tactic.cpp | 2 +- src/tactic/arith/normalize_bounds_tactic.cpp | 2 +- src/tactic/arith/pb2bv_model_converter.h | 2 +- src/tactic/arith/pb2bv_tactic.cpp | 2 +- src/tactic/arith/purify_arith_tactic.cpp | 2 +- src/tactic/arith/recover_01_tactic.cpp | 2 +- src/tactic/bv/bit_blaster_model_converter.cpp | 2 +- src/tactic/bv/bit_blaster_model_converter.h | 2 +- src/tactic/bv/bv_size_reduction_tactic.cpp | 2 +- src/tactic/bv/bvarray2uf_rewriter.h | 2 +- src/tactic/bv/bvarray2uf_tactic.cpp | 2 +- src/tactic/bv/dt2bv_tactic.cpp | 2 +- src/tactic/bv/elim_small_bv_tactic.cpp | 2 +- src/tactic/core/elim_term_ite_tactic.cpp | 2 +- src/tactic/core/elim_uncnstr_tactic.cpp | 2 +- src/tactic/core/nnf_tactic.cpp | 2 +- src/tactic/core/occf_tactic.cpp | 2 +- src/tactic/core/pb_preprocess_tactic.cpp | 2 +- src/tactic/core/reduce_args_tactic.cpp | 2 +- src/tactic/core/reduce_invertible_tactic.cpp | 2 +- src/tactic/core/solve_eqs_tactic.cpp | 2 +- src/tactic/core/split_clause_tactic.cpp | 1 + src/tactic/core/tseitin_cnf_tactic.cpp | 2 +- src/tactic/dependency_converter.h | 2 +- src/tactic/dependent_expr_state_tactic.h | 2 + .../fd_solver/bounded_int2bv_solver.cpp | 2 +- src/tactic/fd_solver/enum2bv_solver.cpp | 2 +- src/tactic/fd_solver/pb2bv_solver.cpp | 2 +- src/tactic/fpa/fpa2bv_model_converter.h | 2 +- src/tactic/goal.h | 4 +- src/tactic/goal_proof_converter.h | 63 +++++++++++++++++++ src/tactic/horn_subsume_model_converter.h | 2 +- src/tactic/replace_proof_converter.h | 2 +- src/tactic/sls/sls_engine.h | 2 +- .../smtlogics/qfufbv_ackr_model_converter.h | 2 +- src/tactic/tactical.cpp | 1 + src/tactic/ufbv/macro_finder_tactic.cpp | 2 +- src/tactic/ufbv/quasi_macros_tactic.cpp | 2 +- 88 files changed, 170 insertions(+), 134 deletions(-) create mode 100644 src/ast/converters/CMakeLists.txt rename src/{tactic => ast/converters}/converter.h (100%) rename src/{tactic => ast/converters}/generic_model_converter.cpp (99%) rename src/{tactic => ast/converters}/generic_model_converter.h (97%) rename src/{tactic => ast/converters}/model_converter.cpp (99%) rename src/{tactic => ast/converters}/model_converter.h (98%) rename src/{tactic => ast/converters}/proof_converter.cpp (68%) rename src/{tactic => ast/converters}/proof_converter.h (78%) create mode 100644 src/tactic/goal_proof_converter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad6fbcdb2..fd4fa04b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,6 +50,7 @@ add_subdirectory(ast/normal_forms) add_subdirectory(ast/macros) add_subdirectory(model) add_subdirectory(ast/euf) +add_subdirectory(ast/converters) add_subdirectory(ast/simplifiers) add_subdirectory(tactic) add_subdirectory(ast/substitution) diff --git a/src/ackermannization/ackermannize_bv_model_converter.h b/src/ackermannization/ackermannize_bv_model_converter.h index 59dff3ed2..759ec3c13 100644 --- a/src/ackermannization/ackermannize_bv_model_converter.h +++ b/src/ackermannization/ackermannize_bv_model_converter.h @@ -16,7 +16,7 @@ --*/ #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_ackermannize_bv_model_converter(ast_manager & m, const ackr_info_ref& info); diff --git a/src/ackermannization/ackr_model_converter.h b/src/ackermannization/ackr_model_converter.h index 8fc8edecc..df134f227 100644 --- a/src/ackermannization/ackr_model_converter.h +++ b/src/ackermannization/ackr_model_converter.h @@ -15,7 +15,7 @@ Revision History: --*/ #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref & info, model_ref & abstr_model); diff --git a/src/ackermannization/lackr_model_converter_lazy.h b/src/ackermannization/lackr_model_converter_lazy.h index 9a713753b..a16722356 100644 --- a/src/ackermannization/lackr_model_converter_lazy.h +++ b/src/ackermannization/lackr_model_converter_lazy.h @@ -16,7 +16,7 @@ --*/ #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_lackr_model_converter_lazy(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model); diff --git a/src/ast/converters/CMakeLists.txt b/src/ast/converters/CMakeLists.txt new file mode 100644 index 000000000..c11b0dbe1 --- /dev/null +++ b/src/ast/converters/CMakeLists.txt @@ -0,0 +1,8 @@ +z3_add_component(converters + SOURCES + model_converter.cpp + proof_converter.cpp + generic_model_converter.cpp + COMPONENT_DEPENDENCIES + model +) diff --git a/src/tactic/converter.h b/src/ast/converters/converter.h similarity index 100% rename from src/tactic/converter.h rename to src/ast/converters/converter.h diff --git a/src/tactic/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp similarity index 99% rename from src/tactic/generic_model_converter.cpp rename to src/ast/converters/generic_model_converter.cpp index 2886eb6ab..f805e169b 100644 --- a/src/tactic/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -24,7 +24,7 @@ Notes: #include "ast/occurs.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "model/model_v2_pp.h" #include "model/model_evaluator.h" diff --git a/src/tactic/generic_model_converter.h b/src/ast/converters/generic_model_converter.h similarity index 97% rename from src/tactic/generic_model_converter.h rename to src/ast/converters/generic_model_converter.h index e809fe734..c20bfebb3 100644 --- a/src/tactic/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -19,7 +19,7 @@ Notes: --*/ #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" class generic_model_converter : public model_converter { enum instruction { HIDE, ADD }; diff --git a/src/tactic/model_converter.cpp b/src/ast/converters/model_converter.cpp similarity index 99% rename from src/tactic/model_converter.cpp rename to src/ast/converters/model_converter.cpp index 5c08da76f..bba18ecd6 100644 --- a/src/tactic/model_converter.cpp +++ b/src/ast/converters/model_converter.cpp @@ -16,7 +16,7 @@ Author: Notes: --*/ -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "model/model_v2_pp.h" #include "ast/ast_smt2_pp.h" diff --git a/src/tactic/model_converter.h b/src/ast/converters/model_converter.h similarity index 98% rename from src/tactic/model_converter.h rename to src/ast/converters/model_converter.h index 377ecce67..335e0d276 100644 --- a/src/tactic/model_converter.h +++ b/src/ast/converters/model_converter.h @@ -57,7 +57,7 @@ Notes: #include "util/ref.h" #include "ast/ast_pp_util.h" #include "model/model.h" -#include "tactic/converter.h" +#include "ast/converters/converter.h" class labels_vec : public svector {}; class smt2_pp_environment; diff --git a/src/tactic/proof_converter.cpp b/src/ast/converters/proof_converter.cpp similarity index 68% rename from src/tactic/proof_converter.cpp rename to src/ast/converters/proof_converter.cpp index d1905b383..88358b7c3 100644 --- a/src/tactic/proof_converter.cpp +++ b/src/ast/converters/proof_converter.cpp @@ -16,8 +16,7 @@ Author: Notes: --*/ -#include "tactic/proof_converter.h" -#include "tactic/goal.h" +#include "ast/converters/proof_converter.h" #include "ast/ast_smt2_pp.h" class concat_proof_converter : public concat_converter { @@ -46,41 +45,6 @@ proof_converter * concat(proof_converter * pc1, proof_converter * pc2) { return alloc(concat_proof_converter, pc1, pc2); } -class subgoal_proof_converter : public proof_converter { - proof_converter_ref m_pc; - goal_ref_buffer m_goals; -public: - subgoal_proof_converter(proof_converter* pc, unsigned n, goal * const* goals): - m_pc(pc) - { - for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); - } - - proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { - // ignore the proofs from the arguments, instead obtain the proofs fromt he subgoals. - SASSERT(num_source == 0); - proof_converter_ref_buffer pc_buffer; - for (goal_ref g : m_goals) { - pc_buffer.push_back(g->pc()); - - } - return apply(m, m_pc, pc_buffer); - } - - proof_converter* translate(ast_translation& tr) override { - proof_converter_ref pc1 = m_pc->translate(tr); - goal_ref_buffer goals; - for (goal_ref g : m_goals) goals.push_back(g->translate(tr)); - return alloc(subgoal_proof_converter, pc1.get(), goals.size(), goals.data()); - } - - void display(std::ostream& out) override {} - -}; - -proof_converter * concat(proof_converter *pc, unsigned n, goal* const* goals) { - return alloc(subgoal_proof_converter, pc, n, goals); -} class proof2pc : public proof_converter { proof_ref m_pr; diff --git a/src/tactic/proof_converter.h b/src/ast/converters/proof_converter.h similarity index 78% rename from src/tactic/proof_converter.h rename to src/ast/converters/proof_converter.h index 88152ce5b..d977f2563 100644 --- a/src/tactic/proof_converter.h +++ b/src/ast/converters/proof_converter.h @@ -20,8 +20,7 @@ Notes: #include "ast/ast.h" #include "util/ref.h" -#include "tactic/converter.h" -class goal; +#include "ast/converters/converter.h" class proof_converter : public converter { public: @@ -36,12 +35,6 @@ typedef sref_buffer proof_converter_ref_buffer; proof_converter * concat(proof_converter * pc1, proof_converter * pc2); -/** - \brief create a proof converter that takes a set of subgoals and converts their proofs to a proof of - the goal they were derived from. - */ -proof_converter * concat(proof_converter *pc1, unsigned n, goal* const* goals); - proof_converter * proof2proof_converter(ast_manager & m, proof * pr); void apply(ast_manager & m, proof_converter * pc, proof_ref & pr); diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index e67aa56cc..34517525f 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -33,6 +33,7 @@ Author: #include "util/statistics.h" #include "util/params.h" #include "ast/simplifiers/dependent_expr.h" +#include "ast/converters/model_converter.h" /** abstract interface to state updated by simplifiers. @@ -66,6 +67,7 @@ public: virtual void collect_statistics(statistics& st) const {} virtual void reset_statistics() {} virtual void updt_params(params_ref const& p) {} + virtual model_converter_ref get_model_converter() { return model_converter_ref(); } }; /** diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 38100fcdc..cc3ad5b7b 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -23,6 +23,7 @@ Author: #include "ast/recfun_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/solve_eqs.h" +#include "ast/converters/generic_model_converter.h" namespace euf { @@ -183,14 +184,16 @@ namespace euf { m_unsafe_vars.mark(term); } -#if 0 + typedef generic_model_converter gmc; + model_converter_ref solve_eqs::get_model_converter() { model_converter_ref mc = alloc(gmc, m, "solve-eqs"); - for (unsigned id : m_subst_ids) - static_cast(mc.get())->add(id2var(id), m_subst->find(v)); + for (unsigned id : m_subst_ids) { + auto* v = m_id2var[id]; + static_cast(mc.get())->add(v, m_subst->find(v)); + } return mc; } -#endif solve_eqs::solve_eqs(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_rewriter(m) { diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 62f24413a..02d20a50e 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -18,9 +18,9 @@ Author: #pragma once -#include "ast/rewriter/th_rewriter.h" -#include "ast/expr_substitution.h" #include "util/scoped_ptr_vector.h" +#include "ast/expr_substitution.h" +#include "ast/rewriter/th_rewriter.h" #include "ast/simplifiers/extract_eqs.h" namespace euf { @@ -74,6 +74,6 @@ namespace euf { void updt_params(params_ref const& p) override; void collect_statistics(statistics& st) const override; - // model_converter_ref get_model_converter(); + model_converter_ref get_model_converter() override; }; } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index a5c66f78b..2d90b70c3 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -47,7 +47,7 @@ Notes: #include "model/model_v2_pp.h" #include "model/model_params.hpp" #include "tactic/tactic_exception.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "solver/smt_logics.h" #include "cmd_context/basic_cmds.h" #include "cmd_context/cmd_context.h" diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 15b5df0d1..93d2c0d3e 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -33,7 +33,7 @@ Notes: #include "ast/datatype_decl_plugin.h" #include "ast/recfun_decl_plugin.h" #include "ast/rewriter/seq_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "solver/solver.h" #include "solver/check_logic.h" #include "solver/progress_callback.h" diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index eae846835..3479fef0d 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -30,7 +30,7 @@ Revision History: #include "util/statistics.h" #include "util/params.h" #include "util/trail.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "model/model2expr.h" #include "smt/params/smt_params.h" #include "muz/base/dl_rule_transformer.h" diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index d0c872c3c..a1864d3b9 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -41,7 +41,7 @@ Revision History: #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/expr_safe_replace.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/scoped_proof.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_util.h" diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index c9fa2f6b3..6a21a2621 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -23,8 +23,8 @@ Revision History: #include "muz/base/dl_costs.h" #include "muz/base/dl_util.h" #include "ast/used_vars.h" -#include "tactic/proof_converter.h" -#include "tactic/model_converter.h" +#include "ast/converters/proof_converter.h" +#include "ast/converters/model_converter.h" #include "ast/rewriter/ast_counter.h" #include "ast/rewriter/rewriter.h" #include "muz/base/hnf.h" diff --git a/src/muz/base/hnf.h b/src/muz/base/hnf.h index 45b651b56..0df0269d9 100644 --- a/src/muz/base/hnf.h +++ b/src/muz/base/hnf.h @@ -27,7 +27,7 @@ Copyright (c) 2015 Microsoft Corporation #include "ast/ast.h" #include "util/params.h" #include "ast/normal_forms/defined_names.h" -#include "tactic/proof_converter.h" +#include "ast/converters/proof_converter.h" class hnf { class imp; diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index 560202ab3..1a58bc92b 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -20,9 +20,9 @@ Revision History: #include "ast/rewriter/var_subst.h" #include "ast/rewriter/expr_replacer.h" #include "tactic/tactical.h" -#include "tactic/model_converter.h" -#include "tactic/proof_converter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/model_converter.h" +#include "ast/converters/proof_converter.h" +#include "ast/converters/generic_model_converter.h" #include "muz/fp/horn_tactic.h" #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index e8d769621..470901a96 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -28,7 +28,7 @@ Revision History: #include "ast/expr_abstract.h" #include "model/model2expr.h" #include "model/model_smt2_pp.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "smt/smt_solver.h" namespace spacer { diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 070432e52..439cb4540 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -22,7 +22,7 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "muz/base/fp_params.hpp" #include "ast/scoped_proof.h" diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index ba85e569a..73541b0cd 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -21,7 +21,7 @@ Author: #include "muz/dataflow/dataflow.h" #include "muz/dataflow/reachability.h" #include "ast/ast_pp.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/ast_util.h" namespace datalog { diff --git a/src/muz/transforms/dl_mk_subsumption_checker.cpp b/src/muz/transforms/dl_mk_subsumption_checker.cpp index b91b9e5c8..e8b1f4001 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.cpp +++ b/src/muz/transforms/dl_mk_subsumption_checker.cpp @@ -25,7 +25,7 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_subsumption_checker.h" #include "muz/base/fp_params.hpp" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" namespace datalog { diff --git a/src/nlsat/tactic/goal2nlsat.h b/src/nlsat/tactic/goal2nlsat.h index 8dda03105..52b975cc2 100644 --- a/src/nlsat/tactic/goal2nlsat.h +++ b/src/nlsat/tactic/goal2nlsat.h @@ -24,7 +24,7 @@ Notes: #pragma once #include "nlsat/nlsat_types.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" class goal; class expr2var; diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index ba31d74cf..5895643bd 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -39,7 +39,7 @@ Notes: #include "tactic/arith/card2bv_tactic.h" #include "tactic/arith/eq2bv_tactic.h" #include "tactic/bv/dt2bv_tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ackermannization/ackermannize_bv_tactic.h" #include "sat/sat_solver/inc_sat_solver.h" #include "sat/sat_params.hpp" diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index a93400592..9e61ae92c 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -20,7 +20,7 @@ Notes: #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "tactic/tactic.h" #include "qe/qsat.h" #include "opt/opt_solver.h" diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index bd83f06c8..84d31ed0f 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -29,7 +29,7 @@ Notes: #include "smt/params/smt_params.h" #include "smt/smt_types.h" #include "smt/theory_opt.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" namespace opt { diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index da25e2285..28448fbe4 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -24,7 +24,7 @@ Notes: #include "smt/smt_context.h" #include "opt/opt_context.h" #include "util/sorting_network.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" namespace opt { diff --git a/src/qe/qsat.h b/src/qe/qsat.h index 381d244e1..2f7502a67 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -21,7 +21,7 @@ Revision History: #pragma once #include "tactic/tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "qe/qe_mbp.h" namespace qe { diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index beb0809fb..f935ad9ff 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -22,7 +22,7 @@ Author: #include "ast/ast_util.h" #include "ast/euf/euf_egraph.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "sat/sat_extension.h" #include "sat/smt/atom2bool_var.h" #include "sat/smt/sat_th.h" diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 403cee684..97a766df9 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -37,7 +37,7 @@ Notes: #include "model/model_evaluator.h" #include "model/model_v2_pp.h" #include "tactic/tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "sat/sat_cut_simplifier.h" #include "sat/sat_drat.h" #include "sat/tactic/goal2sat.h" diff --git a/src/sat/tactic/sat2goal.cpp b/src/sat/tactic/sat2goal.cpp index 7614857cb..899345ad8 100644 --- a/src/sat/tactic/sat2goal.cpp +++ b/src/sat/tactic/sat2goal.cpp @@ -37,7 +37,7 @@ Notes: #include "model/model_evaluator.h" #include "model/model_v2_pp.h" #include "tactic/tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "sat/sat_cut_simplifier.h" #include "sat/sat_drat.h" #include "sat/tactic/sat2goal.h" diff --git a/src/sat/tactic/sat2goal.h b/src/sat/tactic/sat2goal.h index 1e1dfcd5e..8c0b1bf83 100644 --- a/src/sat/tactic/sat2goal.h +++ b/src/sat/tactic/sat2goal.h @@ -31,7 +31,7 @@ Notes: #include "tactic/goal.h" #include "sat/sat_model_converter.h" #include "sat/sat_solver.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "sat/smt/atom2bool_var.h" class sat2goal { diff --git a/src/smt/tactic/smt_tactic_core.cpp b/src/smt/tactic/smt_tactic_core.cpp index 5527f12f5..5ef52a54c 100644 --- a/src/smt/tactic/smt_tactic_core.cpp +++ b/src/smt/tactic/smt_tactic_core.cpp @@ -26,7 +26,7 @@ Notes: #include "smt/smt_solver.h" #include "tactic/tactic.h" #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "solver/solver2tactic.h" #include "solver/solver.h" #include "solver/mus.h" diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 4b57f043e..593c32d83 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -22,7 +22,7 @@ Revision History: #include "smt/theory_arith.h" #include "smt/smt_farkas_util.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" namespace smt { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c46331d07..4075f39dd 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -43,7 +43,7 @@ #include "smt/smt_model_generator.h" #include "smt/arith_eq_adapter.h" #include "util/nat_set.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "util/cancel_eh.h" diff --git a/src/smt/theory_wmaxsat.h b/src/smt/theory_wmaxsat.h index 03a205ca6..9cac6b96b 100644 --- a/src/smt/theory_wmaxsat.h +++ b/src/smt/theory_wmaxsat.h @@ -21,7 +21,7 @@ Notes: #include "smt/smt_theory.h" #include "smt/smt_clause.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" namespace smt { class theory_wmaxsat : public theory { diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index e00b53cc9..936f6d3df 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -23,7 +23,7 @@ Notes: #include "util/statistics.h" #include "util/event_handler.h" #include "util/timer.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" /** \brief Abstract interface for the result of a (check-sat) like command. diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index d582ec2db..bf05554af 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -22,7 +22,7 @@ Notes: #include "ast/ast_pp.h" #include "ast/ast_pp_util.h" #include "ast/display_dimacs.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "solver/solver.h" #include "params/solver_params.hpp" #include "model/model_evaluator.h" diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp index 81d21f959..b8e3dd37a 100644 --- a/src/solver/solver2tactic.cpp +++ b/src/solver/solver2tactic.cpp @@ -19,7 +19,7 @@ Notes: #include "solver/solver.h" #include "tactic/tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "solver/solver2tactic.h" #include "ast/ast_util.h" diff --git a/src/solver/solver2tactic.h b/src/solver/solver2tactic.h index a5b529f69..4640ee276 100644 --- a/src/solver/solver2tactic.h +++ b/src/solver/solver2tactic.h @@ -19,7 +19,7 @@ Notes: #pragma once #include "tactic/tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" class solver; tactic * mk_solver2tactic(solver* s); diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index b9b4394d9..f0910fcc2 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -2,15 +2,12 @@ z3_add_component(tactic SOURCES dependency_converter.cpp equiv_proof_converter.cpp - generic_model_converter.cpp goal.cpp goal_num_occurs.cpp goal_shared_occs.cpp goal_util.cpp horn_subsume_model_converter.cpp - model_converter.cpp probe.cpp - proof_converter.cpp replace_proof_converter.cpp tactical.cpp tactic.cpp @@ -18,6 +15,7 @@ z3_add_component(tactic ast model simplifiers + converters TACTIC_HEADERS probe.h tactic.h diff --git a/src/tactic/arith/card2bv_tactic.cpp b/src/tactic/arith/card2bv_tactic.cpp index 25c5620c2..4f79a887d 100644 --- a/src/tactic/arith/card2bv_tactic.cpp +++ b/src/tactic/arith/card2bv_tactic.cpp @@ -22,7 +22,7 @@ Notes: #include "ast/rewriter/pb2bv_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" class card2bv_tactic : public tactic { ast_manager & m; diff --git a/src/tactic/arith/degree_shift_tactic.cpp b/src/tactic/arith/degree_shift_tactic.cpp index e34910e78..26c3f9ef5 100644 --- a/src/tactic/arith/degree_shift_tactic.cpp +++ b/src/tactic/arith/degree_shift_tactic.cpp @@ -20,7 +20,7 @@ Revision History: --*/ #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "tactic/core/simplify_tactic.h" #include "ast/ast_smt2_pp.h" diff --git a/src/tactic/arith/fix_dl_var_tactic.cpp b/src/tactic/arith/fix_dl_var_tactic.cpp index 87061b189..13479e1bf 100644 --- a/src/tactic/arith/fix_dl_var_tactic.cpp +++ b/src/tactic/arith/fix_dl_var_tactic.cpp @@ -23,7 +23,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" diff --git a/src/tactic/arith/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp index 97c6f466f..cb6f6c228 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -25,7 +25,7 @@ Notes: #include "ast/ast_pp_util.h" #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" class lia2card_tactic : public tactic { diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index 46404ffb0..f78e85621 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -20,7 +20,7 @@ Revision History: #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index 36df89da5..bdcf57953 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -27,7 +27,7 @@ Notes: #include "util/optional.h" #include "tactic/arith/bv2int_rewriter.h" #include "tactic/arith/bv2real_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "tactic/arith/bound_manager.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_smt2_pp.h" diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index b7ef28f49..cba791ee1 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -21,7 +21,7 @@ Revision History: #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" diff --git a/src/tactic/arith/pb2bv_model_converter.h b/src/tactic/arith/pb2bv_model_converter.h index 5560ce7da..2de873ba2 100644 --- a/src/tactic/arith/pb2bv_model_converter.h +++ b/src/tactic/arith/pb2bv_model_converter.h @@ -18,7 +18,7 @@ Notes: --*/ #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "tactic/arith/bound_manager.h" class pb2bv_model_converter : public model_converter { diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index c418115a9..5ff7425ba 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -29,7 +29,7 @@ Notes: #include "ast/rewriter/pb2bv_rewriter.h" #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "tactic/arith/pb2bv_model_converter.h" #include "tactic/arith/pb2bv_tactic.h" diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index afcecd7d3..7c69ef12e 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -27,7 +27,7 @@ Revision History: #include "tactic/core/nnf_tactic.h" #include "tactic/core/simplify_tactic.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_replacer.h" diff --git a/src/tactic/arith/recover_01_tactic.cpp b/src/tactic/arith/recover_01_tactic.cpp index 251d78e72..d97f9b80f 100644 --- a/src/tactic/arith/recover_01_tactic.cpp +++ b/src/tactic/arith/recover_01_tactic.cpp @@ -57,7 +57,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "util/dec_ref_util.h" diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index 5c26fb2b5..88ff11683 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -18,7 +18,7 @@ Notes: --*/ #include "model/model.h" #include "model/model_pp.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" diff --git a/src/tactic/bv/bit_blaster_model_converter.h b/src/tactic/bv/bit_blaster_model_converter.h index debfdd526..dae3cd40e 100644 --- a/src/tactic/bv/bit_blaster_model_converter.h +++ b/src/tactic/bv/bit_blaster_model_converter.h @@ -18,7 +18,7 @@ Notes: --*/ #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index 788f562d3..286375b6a 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -24,7 +24,7 @@ Notes: #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/ast_smt2_pp.h" namespace { diff --git a/src/tactic/bv/bvarray2uf_rewriter.h b/src/tactic/bv/bvarray2uf_rewriter.h index df5c93a14..d6733d4a6 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.h +++ b/src/tactic/bv/bvarray2uf_rewriter.h @@ -20,7 +20,7 @@ Notes: #pragma once #include "ast/rewriter/rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" class bvarray2uf_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; diff --git a/src/tactic/bv/bvarray2uf_tactic.cpp b/src/tactic/bv/bvarray2uf_tactic.cpp index da86ed663..3a4971e04 100644 --- a/src/tactic/bv/bvarray2uf_tactic.cpp +++ b/src/tactic/bv/bvarray2uf_tactic.cpp @@ -20,7 +20,7 @@ Notes: #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "tactic/bv/bvarray2uf_tactic.h" diff --git a/src/tactic/bv/dt2bv_tactic.cpp b/src/tactic/bv/dt2bv_tactic.cpp index 650095207..190403349 100644 --- a/src/tactic/bv/dt2bv_tactic.cpp +++ b/src/tactic/bv/dt2bv_tactic.cpp @@ -21,7 +21,7 @@ Revision History: #include "tactic/bv/dt2bv_tactic.h" #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp index 02ec522c6..54f4dc915 100644 --- a/src/tactic/bv/elim_small_bv_tactic.cpp +++ b/src/tactic/bv/elim_small_bv_tactic.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/used_vars.h" #include "ast/well_sorted.h" diff --git a/src/tactic/core/elim_term_ite_tactic.cpp b/src/tactic/core/elim_term_ite_tactic.cpp index 2a0593ade..c67443862 100644 --- a/src/tactic/core/elim_term_ite_tactic.cpp +++ b/src/tactic/core/elim_term_ite_tactic.cpp @@ -20,7 +20,7 @@ Notes: #include "tactic/tactical.h" #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" class elim_term_ite_tactic : public tactic { diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index c97fa670e..cb60eb482 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -17,7 +17,7 @@ Notes: --*/ #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" diff --git a/src/tactic/core/nnf_tactic.cpp b/src/tactic/core/nnf_tactic.cpp index 959a1fc18..3a5ce8d0a 100644 --- a/src/tactic/core/nnf_tactic.cpp +++ b/src/tactic/core/nnf_tactic.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "ast/normal_forms/nnf.h" #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" class nnf_tactic : public tactic { params_ref m_params; diff --git a/src/tactic/core/occf_tactic.cpp b/src/tactic/core/occf_tactic.cpp index c3c027fef..1784a434d 100644 --- a/src/tactic/core/occf_tactic.cpp +++ b/src/tactic/core/occf_tactic.cpp @@ -23,7 +23,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/occf_tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" class occf_tactic : public tactic { struct imp { diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index 2c4b80b93..9f0717135 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -33,7 +33,7 @@ Notes: --*/ #include "tactic/core/pb_preprocess_tactic.h" #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/for_each_expr.h" #include "ast/pb_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index 7f0d82f2e..b2a10fafa 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -22,7 +22,7 @@ Notes: #include "ast/has_free_vars.h" #include "util/map.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" /** \brief Reduce the number of arguments in function applications. diff --git a/src/tactic/core/reduce_invertible_tactic.cpp b/src/tactic/core/reduce_invertible_tactic.cpp index df3de8219..ba9b5d752 100644 --- a/src/tactic/core/reduce_invertible_tactic.cpp +++ b/src/tactic/core/reduce_invertible_tactic.cpp @@ -29,7 +29,7 @@ Notes: #include "tactic/tactic.h" #include "tactic/core/reduce_invertible_tactic.h" #include "tactic/core/collect_occs.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include namespace { diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index 0a0525e70..977a0d614 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -27,7 +27,7 @@ Revision History: #include "ast/rewriter/hoist_rewriter.h" #include "tactic/goal_shared_occs.h" #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "tactic/tactic_params.hpp" class solve_eqs_tactic : public tactic { diff --git a/src/tactic/core/split_clause_tactic.cpp b/src/tactic/core/split_clause_tactic.cpp index 99a69395b..c29a2f3f2 100644 --- a/src/tactic/core/split_clause_tactic.cpp +++ b/src/tactic/core/split_clause_tactic.cpp @@ -18,6 +18,7 @@ Notes: --*/ #include "tactic/tactical.h" +#include "tactic/goal_proof_converter.h" #include "tactic/core/split_clause_tactic.h" class split_clause_tactic : public tactic { diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index eec05b604..e4476548a 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -52,7 +52,7 @@ Notes: #include "ast/ast_pp.h" #include "tactic/tactical.h" #include "tactic/goal_shared_occs.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/rewriter/bool_rewriter.h" #include "tactic/core/simplify_tactic.h" diff --git a/src/tactic/dependency_converter.h b/src/tactic/dependency_converter.h index 474767a98..1d86f8c39 100644 --- a/src/tactic/dependency_converter.h +++ b/src/tactic/dependency_converter.h @@ -22,7 +22,7 @@ Notes: #include "util/ref.h" #include "ast/ast_pp_util.h" #include "model/model.h" -#include "tactic/converter.h" +#include "ast/converters/converter.h" class goal; diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index c62125459..116baedf7 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -82,6 +82,8 @@ public: m_goal = in.get(); m_simp->reduce(); m_goal->inc_depth(); + if (in->models_enabled()) + in->set(m_simp->get_model_converter()); result.push_back(in.get()); } diff --git a/src/tactic/fd_solver/bounded_int2bv_solver.cpp b/src/tactic/fd_solver/bounded_int2bv_solver.cpp index ed10f7efb..7b7ca630e 100644 --- a/src/tactic/fd_solver/bounded_int2bv_solver.cpp +++ b/src/tactic/fd_solver/bounded_int2bv_solver.cpp @@ -20,7 +20,7 @@ Notes: #include "solver/solver_na2as.h" #include "tactic/tactic.h" #include "ast/rewriter/pb2bv_rewriter.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "tactic/arith/bound_manager.h" diff --git a/src/tactic/fd_solver/enum2bv_solver.cpp b/src/tactic/fd_solver/enum2bv_solver.cpp index 5e05fdf31..7ec5243e7 100644 --- a/src/tactic/fd_solver/enum2bv_solver.cpp +++ b/src/tactic/fd_solver/enum2bv_solver.cpp @@ -26,7 +26,7 @@ Notes: #include "ast/rewriter/enum2bv_rewriter.h" #include "model/model_smt2_pp.h" #include "tactic/tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "tactic/fd_solver/enum2bv_solver.h" #include "solver/solver_na2as.h" diff --git a/src/tactic/fd_solver/pb2bv_solver.cpp b/src/tactic/fd_solver/pb2bv_solver.cpp index 609ed173d..1a5f7d16a 100644 --- a/src/tactic/fd_solver/pb2bv_solver.cpp +++ b/src/tactic/fd_solver/pb2bv_solver.cpp @@ -22,7 +22,7 @@ Notes: #include "ast/rewriter/th_rewriter.h" #include "model/model_smt2_pp.h" #include "tactic/tactic.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "solver/solver_na2as.h" #include "tactic/fd_solver/pb2bv_solver.h" diff --git a/src/tactic/fpa/fpa2bv_model_converter.h b/src/tactic/fpa/fpa2bv_model_converter.h index 452a629c4..4debe781a 100644 --- a/src/tactic/fpa/fpa2bv_model_converter.h +++ b/src/tactic/fpa/fpa2bv_model_converter.h @@ -19,7 +19,7 @@ Notes: #pragma once #include "ast/fpa/fpa2bv_converter.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "ast/fpa/bv2fpa_converter.h" class fpa2bv_model_converter : public model_converter { diff --git a/src/tactic/goal.h b/src/tactic/goal.h index 5e7e0b2fe..b0b8d95f1 100644 --- a/src/tactic/goal.h +++ b/src/tactic/goal.h @@ -34,8 +34,8 @@ Revision History: #include "util/ref.h" #include "util/ref_vector.h" #include "util/ref_buffer.h" -#include "tactic/model_converter.h" -#include "tactic/proof_converter.h" +#include "ast/converters/model_converter.h" +#include "ast/converters/proof_converter.h" #include "tactic/dependency_converter.h" class goal { diff --git a/src/tactic/goal_proof_converter.h b/src/tactic/goal_proof_converter.h new file mode 100644 index 000000000..a17ff0ea1 --- /dev/null +++ b/src/tactic/goal_proof_converter.h @@ -0,0 +1,63 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + goal_proof_converter.h + +Abstract: + + Proof converter for goals + +Author: + + Nikolaj Bjorner (nbjorner) 2012-11-23 + +--*/ + +#pragma once + +#include "ast/converters/proof_converter.h" +class goal; + +/** + \brief create a proof converter that takes a set of subgoals and converts their proofs to a proof of + the goal they were derived from. + */ +proof_converter * concat(proof_converter *pc1, unsigned n, goal* const* goals); + +class subgoal_proof_converter : public proof_converter { + proof_converter_ref m_pc; + goal_ref_buffer m_goals; +public: + subgoal_proof_converter(proof_converter* pc, unsigned n, goal * const* goals): + m_pc(pc) + { + for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); + } + + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { + // ignore the proofs from the arguments, instead obtain the proofs fromt he subgoals. + SASSERT(num_source == 0); + proof_converter_ref_buffer pc_buffer; + for (goal_ref g : m_goals) { + pc_buffer.push_back(g->pc()); + + } + return apply(m, m_pc, pc_buffer); + } + + proof_converter* translate(ast_translation& tr) override { + proof_converter_ref pc1 = m_pc->translate(tr); + goal_ref_buffer goals; + for (goal_ref g : m_goals) goals.push_back(g->translate(tr)); + return alloc(subgoal_proof_converter, pc1.get(), goals.size(), goals.data()); + } + + void display(std::ostream& out) override {} + +}; + +inline proof_converter * concat(proof_converter *pc, unsigned n, goal* const* goals) { + return alloc(subgoal_proof_converter, pc, n, goals); +} diff --git a/src/tactic/horn_subsume_model_converter.h b/src/tactic/horn_subsume_model_converter.h index 41e59070e..2576ad1f9 100644 --- a/src/tactic/horn_subsume_model_converter.h +++ b/src/tactic/horn_subsume_model_converter.h @@ -34,7 +34,7 @@ Subsumption transformation (remove Horn clause): #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "ast/rewriter/th_rewriter.h" class horn_subsume_model_converter : public model_converter { diff --git a/src/tactic/replace_proof_converter.h b/src/tactic/replace_proof_converter.h index 37bbf55b3..6a877bc58 100644 --- a/src/tactic/replace_proof_converter.h +++ b/src/tactic/replace_proof_converter.h @@ -22,7 +22,7 @@ Revision History: #pragma once -#include "tactic/proof_converter.h" +#include "ast/converters/proof_converter.h" class replace_proof_converter : public proof_converter { ast_manager& m; diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index bf726beb9..5f290c626 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -20,7 +20,7 @@ Notes: #include "util/stopwatch.h" #include "util/lbool.h" -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "tactic/goal.h" #include "tactic/sls/sls_tracker.h" diff --git a/src/tactic/smtlogics/qfufbv_ackr_model_converter.h b/src/tactic/smtlogics/qfufbv_ackr_model_converter.h index 092e41634..c60902228 100644 --- a/src/tactic/smtlogics/qfufbv_ackr_model_converter.h +++ b/src/tactic/smtlogics/qfufbv_ackr_model_converter.h @@ -16,7 +16,7 @@ --*/ #pragma once -#include "tactic/model_converter.h" +#include "ast/converters/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_qfufbv_ackr_model_converter(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model); diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 0d1062d78..5b1ea9587 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -20,6 +20,7 @@ Notes: #include "util/cancel_eh.h" #include "util/scoped_ptr_vector.h" #include "tactic/tactical.h" +#include "tactic/goal_proof_converter.h" #ifndef SINGLE_THREAD #include #endif diff --git a/src/tactic/ufbv/macro_finder_tactic.cpp b/src/tactic/ufbv/macro_finder_tactic.cpp index 2358abcd1..3f45feb37 100644 --- a/src/tactic/ufbv/macro_finder_tactic.cpp +++ b/src/tactic/ufbv/macro_finder_tactic.cpp @@ -20,7 +20,7 @@ Notes: #include "ast/recfun_decl_plugin.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "tactic/ufbv/macro_finder_tactic.h" class macro_finder_tactic : public tactic { diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index b0eb113b8..051ee4fed 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -17,7 +17,7 @@ Notes: --*/ #include "tactic/tactical.h" -#include "tactic/generic_model_converter.h" +#include "ast/converters/generic_model_converter.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" #include "ast/macros/quasi_macros.h" From ba6b21d7d4d5b3faa46696b707a59afb4a8c5ed6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 05:23:38 -0700 Subject: [PATCH 17/57] Create solve_eqs2_tactic.h --- src/tactic/core/solve_eqs2_tactic.h | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/tactic/core/solve_eqs2_tactic.h diff --git a/src/tactic/core/solve_eqs2_tactic.h b/src/tactic/core/solve_eqs2_tactic.h new file mode 100644 index 000000000..91b3875ae --- /dev/null +++ b/src/tactic/core/solve_eqs2_tactic.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + solve_eqs2_tactic.h + +Abstract: + + Tactic for solving variables + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-30 + +--*/ +#pragma once + +#include "util/params.h" +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/solve_eqs.h" + + +class solve_eqs2_tactic_factory : public dependent_expr_simplifier_factory { +public: + dependent_expr_simplifier* mk(ast_manager& m, params_ref const& p, dependent_expr_state& s) override { + return alloc(euf::solve_eqs, m, s); + } +}; + +inline tactic * mk_solve_eqs2_tactic(ast_manager& m, params_ref const& p) { + return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs2_tactic_factory), "solve-eqs2"); +} + + +/* + ADD_TACTIC("solve-eqs2", "solve for variables.", "mk_solve_eqs2_tactic(m, p)") +*/ + + From 203652da740863a317d2024ecccd460ace86f8c2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 05:26:06 -0700 Subject: [PATCH 18/57] add converters module to python build --- scripts/mk_project.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 9c4cda4a1..e9f54be05 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -31,10 +31,11 @@ def init_project_def(): add_lib('nlsat', ['polynomial', 'sat']) add_lib('lp', ['util', 'nlsat', 'grobner', 'interval', 'smt_params'], 'math/lp') add_lib('rewriter', ['ast', 'polynomial', 'automata', 'params'], 'ast/rewriter') - add_lib('simplifiers', ['euf', 'rewriter'], 'ast/simplifiers') add_lib('macros', ['rewriter'], 'ast/macros') - add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') add_lib('model', ['rewriter', 'macros']) + add_lib('converters', ['model'], 'ast/converters') + add_lib('simplifiers', ['euf', 'rewriter', 'converters'], 'ast/simplifiers') + add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') add_lib('tactic', ['ast', 'model', 'simplifiers']) add_lib('substitution', ['ast', 'rewriter'], 'ast/substitution') add_lib('parser_util', ['ast'], 'parsers/util') From 06eb460c752184e8a1415ef171945d5eb91d6b89 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 05:50:46 -0700 Subject: [PATCH 19/57] move tactic_params to params --- src/ast/simplifiers/extract_eqs.cpp | 20 ++++++++++++++++- src/ast/simplifiers/extract_eqs.h | 1 + src/ast/simplifiers/solve_eqs.cpp | 22 ++++++++----------- src/ast/simplifiers/solve_eqs.h | 5 +++++ src/params/CMakeLists.txt | 1 + src/{tactic => params}/tactic_params.pyg | 0 src/tactic/CMakeLists.txt | 2 -- src/tactic/core/blast_term_ite_tactic.cpp | 2 +- src/tactic/core/propagate_values_tactic.cpp | 2 +- src/tactic/core/solve_eqs_tactic.cpp | 2 +- src/tactic/dependent_expr_state_tactic.h | 2 +- src/tactic/portfolio/smt_strategic_solver.cpp | 2 +- 12 files changed, 40 insertions(+), 21 deletions(-) rename src/{tactic => params}/tactic_params.pyg (100%) diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 99be4268c..1e9b576e1 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -21,12 +21,14 @@ Author: #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/simplifiers/extract_eqs.h" +#include "params/tactic_params.hpp" namespace euf { class basic_extract_eq : public extract_eq { ast_manager& m; + bool m_ite_solver = true; public: basic_extract_eq(ast_manager& m) : m(m) {} @@ -41,7 +43,7 @@ namespace euf { eqs.push_back(dependent_eq(to_app(y), expr_ref(x, m), d)); } expr* c, * th, * el, * x1, * y1, * x2, * y2; - if (m.is_ite(f, c, th, el)) { + if (m_ite_solver && m.is_ite(f, c, th, el)) { if (m.is_eq(th, x1, y1) && m.is_eq(el, x2, y2)) { if (x1 == y2 && is_uninterp_const(x1)) std::swap(x2, y2); @@ -58,6 +60,11 @@ namespace euf { if (m.is_not(f, x) && is_uninterp_const(x)) eqs.push_back(dependent_eq(to_app(x), expr_ref(m.mk_false(), m), d)); } + + void updt_params(params_ref const& p) { + tactic_params tp(p); + m_ite_solver = p.get_bool("ite_solver", tp.solve_eqs_ite_solver()); + } }; class arith_extract_eq : public extract_eq { @@ -65,6 +72,7 @@ namespace euf { arith_util a; expr_ref_vector m_args; expr_sparse_mark m_nonzero; + bool m_enabled = true; // solve u mod r1 = y -> u = r1*mod!1 + y @@ -215,6 +223,8 @@ namespace euf { public: arith_extract_eq(ast_manager& m) : m(m), a(m), m_args(m) {} void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { + if (!m_enabled) + return; auto [f, d] = e(); expr* x, * y; if (m.is_eq(f, x, y) && a.is_int_real(x)) { @@ -224,12 +234,20 @@ namespace euf { } void pre_process(dependent_expr_state& fmls) override { + if (!m_enabled) + return; m_nonzero.reset(); for (unsigned i = 0; i < fmls.size(); ++i) { auto [f, d] = fmls[i](); add_pos(f); } } + + + void updt_params(params_ref const& p) { + tactic_params tp(p); + m_enabled = p.get_bool("theory_solver", tp.solve_eqs_ite_solver()); + } }; void register_extract_eqs(ast_manager& m, scoped_ptr_vector& ex) { diff --git a/src/ast/simplifiers/extract_eqs.h b/src/ast/simplifiers/extract_eqs.h index 29c136028..00f96f59b 100644 --- a/src/ast/simplifiers/extract_eqs.h +++ b/src/ast/simplifiers/extract_eqs.h @@ -40,6 +40,7 @@ namespace euf { virtual ~extract_eq() {} virtual void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) = 0; virtual void pre_process(dependent_expr_state& fmls) {} + virtual void updt_params(params_ref const& p) {} }; void register_extract_eqs(ast_manager& m, scoped_ptr_vector& ex); diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index cc3ad5b7b..6b2a27d2d 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -24,6 +24,7 @@ Author: #include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/solve_eqs.h" #include "ast/converters/generic_model_converter.h" +#include "params/tactic_params.hpp" namespace euf { @@ -46,7 +47,8 @@ namespace euf { m_next.resize(m_id2var.size()); for (auto const& eq : eqs) - m_next[var2id(eq.var)].push_back(eq); + if (can_be_var(eq.var)) + m_next[var2id(eq.var)].push_back(eq); } /** @@ -136,10 +138,7 @@ namespace euf { tout << "after normalizing variables\n"; for (unsigned id : m_subst_ids) { auto const& eq = m_next[id][0]; - expr* def = nullptr; - proof* pr = nullptr; - expr_dependency* dep = nullptr; - m_subst->find(eq.var, def, pr, dep); + expr* def = m_subst->find(eq.var); tout << mk_pp(eq.var, m) << "\n----->\n" << mk_pp(def, m) << "\n\n"; }); } @@ -201,14 +200,11 @@ namespace euf { } void solve_eqs::updt_params(params_ref const& p) { - // TODO -#if 0 - tactic_params tp(m_params); - m_ite_solver = p.get_bool("ite_solver", tp.solve_eqs_ite_solver()); - m_theory_solver = p.get_bool("theory_solver", tp.solve_eqs_theory_solver()); - m_max_occs = p.get_uint("solve_eqs_max_occs", tp.solve_eqs_max_occs()); - m_context_solve = p.get_bool("context_solve", tp.solve_eqs_context_solve()); -#endif + tactic_params tp(p); + m_config.m_max_occs = p.get_uint("solve_eqs_max_occs", tp.solve_eqs_max_occs()); + m_config.m_context_solve = p.get_bool("context_solve", tp.solve_eqs_context_solve()); + for (auto* ex : m_extract_plugins) + ex->updt_params(p); } void solve_eqs::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 02d20a50e..49cd90ca2 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -30,6 +30,10 @@ namespace euf { unsigned m_num_steps = 0; unsigned m_num_elim_vars = 0; }; + struct config { + bool m_context_solve = true; + unsigned m_max_occs = UINT_MAX; + }; th_rewriter m_rewriter; scoped_ptr_vector m_extract_plugins; @@ -40,6 +44,7 @@ namespace euf { expr_mark m_unsafe_vars; // expressions that cannot be replaced stats m_stats; + config m_config; void add_subst(dependent_eq const& eq); diff --git a/src/params/CMakeLists.txt b/src/params/CMakeLists.txt index f420ddd6d..cdc21da97 100644 --- a/src/params/CMakeLists.txt +++ b/src/params/CMakeLists.txt @@ -16,6 +16,7 @@ z3_add_component(params rewriter_params.pyg seq_rewriter_params.pyg solver_params.pyg + tactic_params.pyg EXTRA_REGISTER_MODULE_HEADERS context_params.h ) diff --git a/src/tactic/tactic_params.pyg b/src/params/tactic_params.pyg similarity index 100% rename from src/tactic/tactic_params.pyg rename to src/params/tactic_params.pyg diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index f0910fcc2..ab81ebfe2 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -19,6 +19,4 @@ z3_add_component(tactic TACTIC_HEADERS probe.h tactic.h - PYG_FILES - tactic_params.pyg ) diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp index 38b4e172e..49e43e633 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -20,7 +20,7 @@ Notes: #include "ast/rewriter/rewriter_def.h" #include "ast/scoped_proof.h" #include "tactic/tactical.h" -#include "tactic/tactic_params.hpp" +#include "params/tactic_params.hpp" diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 5b7fcbf10..041e4d1c2 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -24,7 +24,7 @@ Revision History: #include "ast/ast_pp.h" #include "ast/expr_substitution.h" #include "tactic/goal_shared_occs.h" -#include "tactic/tactic_params.hpp" +#include "params/tactic_params.hpp" namespace { class propagate_values_tactic : public tactic { diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index 977a0d614..3e338b57e 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -28,7 +28,7 @@ Revision History: #include "tactic/goal_shared_occs.h" #include "tactic/tactical.h" #include "ast/converters/generic_model_converter.h" -#include "tactic/tactic_params.hpp" +#include "params/tactic_params.hpp" class solve_eqs_tactic : public tactic { struct imp { diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 116baedf7..ae3635c77 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -83,7 +83,7 @@ public: m_simp->reduce(); m_goal->inc_depth(); if (in->models_enabled()) - in->set(m_simp->get_model_converter()); + in->set(m_simp->get_model_converter().get()); result.push_back(in.get()); } diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 483be4cca..b521095be 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -44,7 +44,7 @@ Notes: #include "solver/solver2tactic.h" #include "solver/parallel_tactic.h" #include "solver/parallel_params.hpp" -#include "tactic/tactic_params.hpp" +#include "params/tactic_params.hpp" #include "parsers/smt2/smt2parser.h" From 25bb935793551d19791119f48181a2ccd1fb7316 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 20:18:21 -0700 Subject: [PATCH 20/57] move more converters --- src/ast/converters/CMakeLists.txt | 4 +++- src/{tactic => ast/converters}/equiv_proof_converter.cpp | 2 +- src/{tactic => ast/converters}/equiv_proof_converter.h | 2 +- src/{tactic => ast/converters}/replace_proof_converter.cpp | 2 +- src/{tactic => ast/converters}/replace_proof_converter.h | 0 src/muz/base/dl_util.h | 2 +- src/muz/transforms/dl_mk_array_blast.h | 2 +- src/muz/transforms/dl_mk_elim_term_ite.h | 2 +- src/tactic/CMakeLists.txt | 2 -- 9 files changed, 9 insertions(+), 9 deletions(-) rename src/{tactic => ast/converters}/equiv_proof_converter.cpp (93%) rename src/{tactic => ast/converters}/equiv_proof_converter.h (95%) rename src/{tactic => ast/converters}/replace_proof_converter.cpp (97%) rename src/{tactic => ast/converters}/replace_proof_converter.h (100%) diff --git a/src/ast/converters/CMakeLists.txt b/src/ast/converters/CMakeLists.txt index c11b0dbe1..5343c32a2 100644 --- a/src/ast/converters/CMakeLists.txt +++ b/src/ast/converters/CMakeLists.txt @@ -1,8 +1,10 @@ z3_add_component(converters SOURCES + equiv_proof_converter.cpp + generic_model_converter.cpp model_converter.cpp proof_converter.cpp - generic_model_converter.cpp + replace_proof_converter.cpp COMPONENT_DEPENDENCIES model ) diff --git a/src/tactic/equiv_proof_converter.cpp b/src/ast/converters/equiv_proof_converter.cpp similarity index 93% rename from src/tactic/equiv_proof_converter.cpp rename to src/ast/converters/equiv_proof_converter.cpp index 8bec082d3..d0ed94d8b 100644 --- a/src/tactic/equiv_proof_converter.cpp +++ b/src/ast/converters/equiv_proof_converter.cpp @@ -17,7 +17,7 @@ Revision History: --*/ -#include "tactic/equiv_proof_converter.h" +#include "ast/converters/equiv_proof_converter.h" #include "ast/ast_pp.h" #include "ast/scoped_proof.h" diff --git a/src/tactic/equiv_proof_converter.h b/src/ast/converters/equiv_proof_converter.h similarity index 95% rename from src/tactic/equiv_proof_converter.h rename to src/ast/converters/equiv_proof_converter.h index 87a8f7131..7f98d1e0c 100644 --- a/src/tactic/equiv_proof_converter.h +++ b/src/ast/converters/equiv_proof_converter.h @@ -23,7 +23,7 @@ Revision History: #pragma once -#include "tactic/replace_proof_converter.h" +#include "ast/converters/replace_proof_converter.h" class equiv_proof_converter : public proof_converter { ast_manager& m; diff --git a/src/tactic/replace_proof_converter.cpp b/src/ast/converters/replace_proof_converter.cpp similarity index 97% rename from src/tactic/replace_proof_converter.cpp rename to src/ast/converters/replace_proof_converter.cpp index 4a98110eb..81fe251a3 100644 --- a/src/tactic/replace_proof_converter.cpp +++ b/src/ast/converters/replace_proof_converter.cpp @@ -17,7 +17,7 @@ Revision History: --*/ -#include "tactic/replace_proof_converter.h" +#include "ast/converters/replace_proof_converter.h" #include "ast/expr_functors.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" diff --git a/src/tactic/replace_proof_converter.h b/src/ast/converters/replace_proof_converter.h similarity index 100% rename from src/tactic/replace_proof_converter.h rename to src/ast/converters/replace_proof_converter.h diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 46e0b42bf..3d5501b37 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -24,7 +24,7 @@ Revision History: #include "util/obj_hashtable.h" #include "util/uint_set.h" #include "tactic/horn_subsume_model_converter.h" -#include "tactic/replace_proof_converter.h" +#include "ast/converters/replace_proof_converter.h" #include "ast/substitution/substitution.h" #include "ast/rewriter/ast_counter.h" #include "util/statistics.h" diff --git a/src/muz/transforms/dl_mk_array_blast.h b/src/muz/transforms/dl_mk_array_blast.h index 6d1a69825..352c8a248 100644 --- a/src/muz/transforms/dl_mk_array_blast.h +++ b/src/muz/transforms/dl_mk_array_blast.h @@ -22,9 +22,9 @@ Revision History: #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" -#include "tactic/equiv_proof_converter.h" #include "ast/array_decl_plugin.h" #include "ast/rewriter/expr_safe_replace.h" +#include "ast/converters/equiv_proof_converter.h" namespace datalog { diff --git a/src/muz/transforms/dl_mk_elim_term_ite.h b/src/muz/transforms/dl_mk_elim_term_ite.h index 11bfb8f23..98acd12f1 100644 --- a/src/muz/transforms/dl_mk_elim_term_ite.h +++ b/src/muz/transforms/dl_mk_elim_term_ite.h @@ -21,7 +21,7 @@ Revision History: #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" -#include "tactic/equiv_proof_converter.h" +#include "ast/converters/equiv_proof_converter.h" namespace datalog { class mk_elim_term_ite : public rule_transformer::plugin { diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index ab81ebfe2..17f31cacf 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -1,14 +1,12 @@ z3_add_component(tactic SOURCES dependency_converter.cpp - equiv_proof_converter.cpp goal.cpp goal_num_occurs.cpp goal_shared_occs.cpp goal_util.cpp horn_subsume_model_converter.cpp probe.cpp - replace_proof_converter.cpp tactical.cpp tactic.cpp COMPONENT_DEPENDENCIES From 9007bdf780fa575a4787c121c6daf3b8ec3fea84 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 20:26:02 -0700 Subject: [PATCH 21/57] move horn_subsume_model_converter to ast/converters --- src/ast/converters/CMakeLists.txt | 1 + .../converters}/horn_subsume_model_converter.cpp | 10 +++++----- .../converters}/horn_subsume_model_converter.h | 0 src/muz/base/dl_util.h | 3 ++- src/tactic/CMakeLists.txt | 1 - 5 files changed, 8 insertions(+), 7 deletions(-) rename src/{tactic => ast/converters}/horn_subsume_model_converter.cpp (99%) rename src/{tactic => ast/converters}/horn_subsume_model_converter.h (100%) diff --git a/src/ast/converters/CMakeLists.txt b/src/ast/converters/CMakeLists.txt index 5343c32a2..52895f064 100644 --- a/src/ast/converters/CMakeLists.txt +++ b/src/ast/converters/CMakeLists.txt @@ -2,6 +2,7 @@ z3_add_component(converters SOURCES equiv_proof_converter.cpp generic_model_converter.cpp + horn_subsume_model_converter.cpp model_converter.cpp proof_converter.cpp replace_proof_converter.cpp diff --git a/src/tactic/horn_subsume_model_converter.cpp b/src/ast/converters/horn_subsume_model_converter.cpp similarity index 99% rename from src/tactic/horn_subsume_model_converter.cpp rename to src/ast/converters/horn_subsume_model_converter.cpp index 979359a46..a0c8b341e 100644 --- a/src/tactic/horn_subsume_model_converter.cpp +++ b/src/ast/converters/horn_subsume_model_converter.cpp @@ -18,14 +18,14 @@ Revision History: --*/ -#include "tactic/horn_subsume_model_converter.h" -#include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" -#include "model/model_smt2_pp.h" -#include "ast/rewriter/bool_rewriter.h" -#include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" +#include "ast/rewriter/var_subst.h" +#include "ast/rewriter/bool_rewriter.h" +#include "ast/rewriter/th_rewriter.h" +#include "model/model_smt2_pp.h" +#include "ast/converters/horn_subsume_model_converter.h" void horn_subsume_model_converter::insert(app* head, expr* body) { m_delay_head.push_back(head); diff --git a/src/tactic/horn_subsume_model_converter.h b/src/ast/converters/horn_subsume_model_converter.h similarity index 100% rename from src/tactic/horn_subsume_model_converter.h rename to src/ast/converters/horn_subsume_model_converter.h diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 3d5501b37..4688a67fd 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -18,12 +18,13 @@ Revision History: --*/ #pragma once + #include #include "ast/ast.h" #include "util/hashtable.h" #include "util/obj_hashtable.h" #include "util/uint_set.h" -#include "tactic/horn_subsume_model_converter.h" +#include "ast/converters/horn_subsume_model_converter.h" #include "ast/converters/replace_proof_converter.h" #include "ast/substitution/substitution.h" #include "ast/rewriter/ast_counter.h" diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index 17f31cacf..72bbbc303 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -5,7 +5,6 @@ z3_add_component(tactic goal_num_occurs.cpp goal_shared_occs.cpp goal_util.cpp - horn_subsume_model_converter.cpp probe.cpp tactical.cpp tactic.cpp From e8112a6564fa5891b8459ae477971282e5e7d5f4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 21:35:07 -0700 Subject: [PATCH 22/57] add initial stubs for model reconstruction trail --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/dependent_expr_state.h | 3 +- .../model_reconstruction_trail.cpp | 43 ++++++++++ .../simplifiers/model_reconstruction_trail.h | 80 +++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/ast/simplifiers/model_reconstruction_trail.cpp create mode 100644 src/ast/simplifiers/model_reconstruction_trail.h diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index a260dd3b7..dc7aa6fb6 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(simplifiers bv_slice.cpp euf_completion.cpp extract_eqs.cpp + model_reconstruction_trail.cpp solve_eqs.cpp COMPONENT_DEPENDENCIES euf diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 34517525f..32ad59681 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -32,8 +32,9 @@ Author: #include "util/trail.h" #include "util/statistics.h" #include "util/params.h" -#include "ast/simplifiers/dependent_expr.h" #include "ast/converters/model_converter.h" +#include "ast/simplifiers/dependent_expr.h" + /** abstract interface to state updated by simplifiers. diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp new file mode 100644 index 000000000..f99685854 --- /dev/null +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -0,0 +1,43 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + model_reconstruction_trail.cpp + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-3. + +--*/ + + +#include "ast/simplifiers/model_reconstruction_trail.h" +#include "ast/converters/generic_model_converter.h" + + +void model_reconstruction_trail::replay(dependent_expr const& d, vector& added) { + // accumulate a set of dependent exprs, updating m_trail to exclude loose + // substitutions that use variables from the dependent expressions. + ast_mark free_vars; + auto [f, dep] = d; + for (expr* t : subterms::all(expr_ref(f, m))) + free_vars.mark(t); + + NOT_IMPLEMENTED_YET(); +} + +/** + * retrieve the current model converter corresponding to chaining substitutions from the trail. + */ +model_converter_ref model_reconstruction_trail::get_model_converter() { + model_converter_ref mc = alloc(generic_model_converter, m, "dependent-expr-model"); + // walk the trail from the back. + // add substitutions from the back to the generic model converter + // after they have been normalized using a global replace that replaces + // substituted variables by their terms. + NOT_IMPLEMENTED_YET(); + return mc; + +} + diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h new file mode 100644 index 000000000..eeaf786d9 --- /dev/null +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -0,0 +1,80 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + model_reconstruction_trail.h + +Abstract: + + Model reconstruction trail + A model reconstruction trail comprises of a sequence of assignments + together with assertions that were removed in favor of the assignments. + The assignments satisfy the removed assertions but are not (necessarily) + equivalent to the removed assertions. For the case where assignments + are equivalent to removed assertions, we squash the removed assertions + and don't track them. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-3. + +--*/ + +#pragma once + +#include "util/scoped_ptr_vector.h" +#include "ast/rewriter/expr_replacer.h" +#include "ast/simplifiers/dependent_expr.h" +#include "ast/converters/model_converter.h" + +class model_reconstruction_trail { + + ast_manager& m; + + struct model_reconstruction_trail_entry { + scoped_ptr m_replace; + vector m_removed; + model_reconstruction_trail_entry(expr_replacer* r, vector const& rem) : + m_replace(r), m_removed(rem) {} + }; + + scoped_ptr_vector m_trail; + unsigned_vector m_limit; + +public: + + model_reconstruction_trail(ast_manager& m) : m(m) {} + + /** + * add a new substitution to the stack + */ + void push(expr_replacer* r, vector const& removed) { + m_trail.push_back(alloc(model_reconstruction_trail_entry, r, removed)); + } + + /** + * register a new depedent expression, update the trail + * by removing substitutions that are not equivalence preserving. + */ + void replay(dependent_expr const& d, vector& added); + + /** + * retrieve the current model converter corresponding to chaining substitutions from the trail. + */ + model_converter_ref get_model_converter(); + + /** + * push a context. Portions of the trail added within a context are removed after a context pop. + */ + void push() { + m_limit.push_back(m_trail.size()); + } + + void pop(unsigned n) { + unsigned old_sz = m_limit[m_limit.size() - n]; + m_trail.resize(old_sz); + m_limit.shrink(m_limit.size() - n); + } +}; + From 626380b3c7573da941bed303e4548cac49b17953 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Nov 2022 22:08:21 -0700 Subject: [PATCH 23/57] fixing build Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/model_reconstruction_trail.cpp | 7 ++++--- src/test/horn_subsume_model_converter.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index f99685854..2e8eab0e6 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -12,6 +12,7 @@ Author: --*/ +#include "ast/for_each_expr.h" #include "ast/simplifiers/model_reconstruction_trail.h" #include "ast/converters/generic_model_converter.h" @@ -20,9 +21,9 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vector Date: Fri, 4 Nov 2022 09:35:58 -0700 Subject: [PATCH 24/57] fixes #6439 #6436 --- src/api/api_ast.cpp | 8 +++++++- src/ast/ast.h | 1 + src/ast/recfun_decl_plugin.h | 3 ++- src/muz/base/dl_context.cpp | 6 ++++++ src/smt/smt_context_pp.cpp | 2 +- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index df3059d4b..a8f4d74b7 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -137,7 +137,7 @@ extern "C" { ast_manager& m = mk_c(c)->m(); recfun::decl::plugin& p = mk_c(c)->recfun().get_plugin(); if (!p.has_def(d)) { - std::string msg = "function " + mk_pp(d, m) + " needs to be defined using rec_func_decl"; + std::string msg = "function " + mk_pp(d, m) + " needs to be declared using rec_func_decl"; SET_ERROR_CODE(Z3_INVALID_ARG, msg.c_str()); return; } @@ -158,6 +158,12 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } + if (!pd.get_def()->get_cases().empty()) { + std::string msg = "function " + mk_pp(d, m) + " has already been given a definition"; + SET_ERROR_CODE(Z3_INVALID_ARG, msg.c_str()); + return; + } + if (abs_body->get_sort() != d->get_range()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; diff --git a/src/ast/ast.h b/src/ast/ast.h index ce9de96d4..7514055c5 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1387,6 +1387,7 @@ inline bool is_app_of(expr const * n, family_id fid, decl_kind k) { return n->ge inline bool is_sort_of(sort const * s, family_id fid, decl_kind k) { return s->is_sort_of(fid, k); } inline bool is_uninterp_const(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; } inline bool is_uninterp(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_family_id() == null_family_id; } +inline bool is_uninterp(func_decl const * n) { return n->get_family_id() == null_family_id; } inline bool is_decl_of(func_decl const * d, family_id fid, decl_kind k) { return d->get_family_id() == fid && d->get_decl_kind() == k; } inline bool is_ground(expr const * n) { return is_app(n) && to_app(n)->is_ground(); } inline bool is_non_ground(expr const * n) { return ( ! is_ground(n)); } diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index dcff35e82..8e1279c0a 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -203,8 +203,9 @@ namespace recfun { def const& get_def(func_decl* f) const { return *(m_defs[f]); } promise_def get_promise_def(func_decl* f) const { return promise_def(&u(), m_defs[f]); } def& get_def(func_decl* f) { return *(m_defs[f]); } - bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); } + bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); } case_def& get_case_def(func_decl* f) { SASSERT(has_case_def(f)); return *(m_case_defs[f]); } + bool is_defined(func_decl* f) {return has_case_def(f) && !get_def(f).get_cases().empty(); } func_decl_ref_vector get_rec_funs() { func_decl_ref_vector result(m()); diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 4efe79dd3..c9d2c7797 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -644,6 +644,12 @@ namespace datalog { } void context::add_table_fact(func_decl * pred, const table_fact & fact) { + if (!is_uninterp(pred)) { + std::stringstream strm; + strm << "Predicate " << pred->get_name() << " when used for facts should be uninterpreted"; + throw default_exception(strm.str()); + } + if (get_engine() == DATALOG_ENGINE) { ensure_engine(); m_rel->add_fact(pred, fact); diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 24bfb3355..40e789204 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -166,7 +166,7 @@ namespace smt { unsigned num = get_num_bool_vars(); for (unsigned v = 0; v < num; v++) { expr * n = m_bool_var2expr[v]; - ast_def_ll_pp(out, m, n, get_pp_visited(), true, false); + ast_def_ll_pp(out << v << " ", m, n, get_pp_visited(), true, false); } } From 28668c6efc5c6d6fa5cdfe630b56d7c370b13f53 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Nov 2022 11:25:05 -0700 Subject: [PATCH 25/57] set up model reconstruction trail --- src/ast/expr_substitution.h | 4 +- src/ast/rewriter/expr_replacer.cpp | 5 ++ src/ast/rewriter/expr_replacer.h | 7 +- src/ast/simplifiers/dependent_expr.h | 4 + .../model_reconstruction_trail.cpp | 76 +++++++++++++++++-- .../simplifiers/model_reconstruction_trail.h | 62 +++++++++------ src/ast/simplifiers/solve_eqs.cpp | 10 +-- src/muz/spacer/spacer_iuc_solver.cpp | 6 +- 8 files changed, 130 insertions(+), 44 deletions(-) diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index 6d4b1b618..0e285ff7d 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -44,8 +44,10 @@ public: bool empty() const { return m_subst.empty(); } unsigned size() const { return m_subst.size(); } void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr); + void insert(expr* s, expr* def, expr_dependency* def_dep) { insert(s, def, nullptr, def_dep); } void erase(expr * s); - expr* find(expr* s) { proof* pr; expr* def; VERIFY(find(s, def, pr)); SASSERT(def); return def; } + expr* find(expr* s) { return m_subst[s]; } + expr_dependency* dep(expr* s) { return (*m_subst_dep)[s]; } bool find(expr * s, expr * & def, proof * & def_pr); bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep); bool contains(expr * s); diff --git a/src/ast/rewriter/expr_replacer.cpp b/src/ast/rewriter/expr_replacer.cpp index 4fa83bed0..1007261ae 100644 --- a/src/ast/rewriter/expr_replacer.cpp +++ b/src/ast/rewriter/expr_replacer.cpp @@ -25,6 +25,11 @@ void expr_replacer::operator()(expr * t, expr_ref & result, proof_ref & result_p operator()(t, result, result_pr, result_dep); } +void expr_replacer::operator()(expr* t, expr_ref& result, expr_dependency_ref& result_dep) { + proof_ref result_pr(m()); + operator()(t, result, result_pr, result_dep); +} + void expr_replacer::operator()(expr * t, expr_ref & result) { proof_ref pr(m()); operator()(t, result, pr); diff --git a/src/ast/rewriter/expr_replacer.h b/src/ast/rewriter/expr_replacer.h index 82982adff..50831073b 100644 --- a/src/ast/rewriter/expr_replacer.h +++ b/src/ast/rewriter/expr_replacer.h @@ -34,9 +34,10 @@ public: virtual void set_substitution(expr_substitution * s) = 0; virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & deps) = 0; - virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr); - virtual void operator()(expr * t, expr_ref & result); - virtual void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); } + void operator()(expr* t, expr_ref& result, expr_dependency_ref& deps); + void operator()(expr * t, expr_ref & result, proof_ref & result_pr); + void operator()(expr * t, expr_ref & result); + void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); } virtual unsigned get_num_steps() const { return 0; } virtual void reset() = 0; diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h index 9d6d8625e..53f9cb9d8 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -68,6 +68,10 @@ public: m_fml = nullptr; m_dep = nullptr; } + + ast_manager& get_manager() const { return m; } + + expr* fml() const { return m_fml; } std::tuple operator()() const { return { m_fml, m_dep }; diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 2e8eab0e6..077443e7e 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -21,24 +21,86 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorm_active) + continue; + + // updates that have no intersections with current variables are skipped + if (!t->intersects(free_vars)) + continue; + + // loose entries that intersect with free vars are deleted from the trail + // and their removed formulas are added to the resulting constraints. + if (t->is_loose()) { + added.append(t->m_removed); + for (auto r : t->m_removed) + add_vars(r, free_vars); + m_trail_stack.push(value_trail(t->m_active)); + t->m_active = false; + continue; + } + + // rigid entries: + // apply substitution to added in case of rigid model convertions + for (auto& d : added) { + auto [f, dep1] = d(); + expr_ref g(m); + expr_dependency_ref dep2(m); + (*t->m_replace)(f, g, dep2); + d = dependent_expr(m, g, m.mk_join(dep1, dep2)); + } + } } /** * retrieve the current model converter corresponding to chaining substitutions from the trail. */ model_converter_ref model_reconstruction_trail::get_model_converter() { - model_converter_ref mc = alloc(generic_model_converter, m, "dependent-expr-model"); + + // // walk the trail from the back // add substitutions from the back to the generic model converter // after they have been normalized using a global replace that replaces // substituted variables by their terms. - NOT_IMPLEMENTED_YET(); - return mc; + // + + scoped_ptr rp = mk_default_expr_replacer(m, true); + scoped_ptr subst = alloc(expr_substitution, m, true, false); + rp->set_substitution(subst.get()); + generic_model_converter_ref mc = alloc(generic_model_converter, m, "dependent-expr-model"); + bool first = true; + for (unsigned i = m_trail.size(); i-- > 0; ) { + auto* t = m_trail[i]; + if (!t->m_active) + continue; + + if (first) { + first = false; + for (auto const& [v, def] : t->m_subst->sub()) { + expr_dependency* dep = t->m_subst->dep(v); + subst->insert(v, def, dep); + mc->add(v, def); + } + continue; + } + expr_dependency_ref new_dep(m); + expr_ref new_def(m); + + for (auto const& [v, def] : t->m_subst->sub()) { + rp->operator()(def, new_def, new_dep); + expr_dependency* dep = t->m_subst->dep(v); + new_dep = m.mk_join(dep, new_dep); + subst->insert(v, new_def, new_dep); + mc->add(v, new_def); + } + + } + return model_converter_ref(mc.get()); } diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index eeaf786d9..8f1ba4381 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -24,33 +24,62 @@ Author: #pragma once #include "util/scoped_ptr_vector.h" +#include "util/trail.h" +#include "ast/for_each_expr.h" #include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/dependent_expr.h" #include "ast/converters/model_converter.h" class model_reconstruction_trail { - ast_manager& m; + struct entry { + scoped_ptr m_replace; + scoped_ptr m_subst; + vector m_removed; + bool m_active = true; + + entry(expr_replacer* r, expr_substitution* s, vector const& rem) : + m_replace(r), m_subst(s), m_removed(rem) {} + + bool is_loose() const { return !m_removed.empty(); } + + bool intersects(ast_mark const& free_vars) const { + return std::any_of(m_subst->sub().begin(), m_subst->sub().end(), [&](auto const& kv) { return free_vars.is_marked(kv.m_key); }); + } + - struct model_reconstruction_trail_entry { - scoped_ptr m_replace; - vector m_removed; - model_reconstruction_trail_entry(expr_replacer* r, vector const& rem) : - m_replace(r), m_removed(rem) {} }; - scoped_ptr_vector m_trail; - unsigned_vector m_limit; + ast_manager& m; + trail_stack& m_trail_stack; + scoped_ptr_vector m_trail; + + void add_vars(dependent_expr const& d, ast_mark& free_vars) { + for (expr* t : subterms::all(expr_ref(d.fml(), d.get_manager()))) + free_vars.mark(t, true); + } + + bool intersects(ast_mark const& free_vars, dependent_expr const& d) { + expr_ref term(d.fml(), d.get_manager()); + auto iter = subterms::all(term); + return std::any_of(iter.begin(), iter.end(), [&](expr* t) { return free_vars.is_marked(t); }); + } + + bool intersects(ast_mark const& free_vars, vector const& added) { + return std::any_of(added.begin(), added.end(), [&](dependent_expr const& d) { return intersects(free_vars, d); }); + } public: - model_reconstruction_trail(ast_manager& m) : m(m) {} + model_reconstruction_trail(ast_manager& m, trail_stack& tr): + m(m), m_trail_stack(tr) {} /** * add a new substitution to the stack */ void push(expr_replacer* r, vector const& removed) { - m_trail.push_back(alloc(model_reconstruction_trail_entry, r, removed)); + m_trail.push_back(alloc(entry, r, nullptr, removed)); + m_trail_stack.push(push_back_vector(m_trail)); } /** @@ -63,18 +92,5 @@ public: * retrieve the current model converter corresponding to chaining substitutions from the trail. */ model_converter_ref get_model_converter(); - - /** - * push a context. Portions of the trail added within a context are removed after a context pop. - */ - void push() { - m_limit.push_back(m_trail.size()); - } - - void pop(unsigned n) { - unsigned old_sz = m_limit[m_limit.size() - n]; - m_trail.resize(old_sz); - m_limit.shrink(m_limit.size() - n); - } }; diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 6b2a27d2d..e4361ad97 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -60,7 +60,7 @@ namespace euf { m_id2level.reset(); m_id2level.resize(m_id2var.size(), UINT_MAX); m_subst_ids.reset(); - m_subst = alloc(expr_substitution, m, false, false); + m_subst = alloc(expr_substitution, m, true, false); auto is_explored = [&](unsigned id) { return m_id2level[id] != UINT_MAX; @@ -120,16 +120,15 @@ namespace euf { expr_dependency_ref new_dep(m); expr_ref new_def(m); - proof_ref new_pr(m); for (unsigned id : m_subst_ids) { if (!m.inc()) break; auto const& [v, def, dep] = m_next[id][0]; - rp->operator()(def, new_def, new_pr, new_dep); + rp->operator()(def, new_def, new_dep); m_stats.m_num_steps += rp->get_num_steps() + 1; new_dep = m.mk_join(dep, new_dep); - m_subst->insert(v, new_def, new_pr, new_dep); + m_subst->insert(v, new_def, nullptr, new_dep); // we updated the substitution, but we don't need to reset rp // because all cached values there do not depend on v. } @@ -149,11 +148,10 @@ namespace euf { scoped_ptr rp = mk_default_expr_replacer(m, true); rp->set_substitution(m_subst.get()); expr_ref new_f(m); - proof_ref new_pr(m); expr_dependency_ref new_dep(m); for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) { auto [f, d] = m_fmls[i](); - rp->operator()(f, new_f, new_pr, new_dep); + rp->operator()(f, new_f, new_dep); if (new_f == f) continue; new_dep = m.mk_join(d, new_dep); diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 4f7342590..b8b51c0c6 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -244,12 +244,10 @@ namespace spacer { } void iuc_solver::elim_proxies (expr_ref_vector &v) { - expr_ref f = mk_and (v); scoped_ptr rep = mk_expr_simp_replacer (m); rep->set_substitution (&m_elim_proxies_sub); - (*rep)(f); - v.reset(); - flatten_and(f, v); + (*rep)(v); + flatten_and(v); } void iuc_solver::get_iuc(expr_ref_vector &core) { From de9368bab05cc29b7f151bcc1987985d1f0456fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Nov 2022 11:25:34 -0700 Subject: [PATCH 26/57] Update expr_replacer.h --- src/ast/rewriter/expr_replacer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/rewriter/expr_replacer.h b/src/ast/rewriter/expr_replacer.h index 50831073b..c1bfabd12 100644 --- a/src/ast/rewriter/expr_replacer.h +++ b/src/ast/rewriter/expr_replacer.h @@ -38,6 +38,7 @@ public: void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * t, expr_ref & result); void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); } + void operator()(expr_ref_vector& v) { expr_ref t(m()); for (unsigned i = 0; i < v.size(); ++i) (*this)(v.get(i), t), v[i] = t; } virtual unsigned get_num_steps() const { return 0; } virtual void reset() = 0; From 49d149045461c5078b4cd86ec65081959a9c1d48 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Nov 2022 12:48:30 -0700 Subject: [PATCH 27/57] add ad-hoc any-of for cross compatibility and simplifying interface Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/model_reconstruction_trail.h | 8 +++++--- src/util/util.h | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 8f1ba4381..96d27e4c4 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -44,7 +44,10 @@ class model_reconstruction_trail { bool is_loose() const { return !m_removed.empty(); } bool intersects(ast_mark const& free_vars) const { - return std::any_of(m_subst->sub().begin(), m_subst->sub().end(), [&](auto const& kv) { return free_vars.is_marked(kv.m_key); }); + for (auto const& [k, v] : m_subst->sub()) + if (free_vars.is_marked(k)) + return true; + return false; } @@ -61,8 +64,7 @@ class model_reconstruction_trail { bool intersects(ast_mark const& free_vars, dependent_expr const& d) { expr_ref term(d.fml(), d.get_manager()); - auto iter = subterms::all(term); - return std::any_of(iter.begin(), iter.end(), [&](expr* t) { return free_vars.is_marked(t); }); + return any_of(subterms::all(term), [&](expr* t) { return free_vars.is_marked(t); }); } bool intersects(ast_mark const& free_vars, vector const& added) { diff --git a/src/util/util.h b/src/util/util.h index 9d129f9a7..c3f06d8d3 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -25,6 +25,7 @@ Revision History: #include #include #include +#include #ifndef SIZE_MAX #define SIZE_MAX std::numeric_limits::max() @@ -179,9 +180,8 @@ void display(std::ostream & out, const IT & begin, const IT & end, const char * template struct delete_proc { void operator()(T * ptr) { - if (ptr) { - dealloc(ptr); - } + if (ptr) + dealloc(ptr); } }; @@ -360,6 +360,15 @@ void fatal_error(int error_code); void set_fatal_error_handler(void (*pfn)(int error_code)); +template +bool any_of(S& set, T& p) { + for (auto const& s : set) + if (p(s)) + return true; + return false; +} +// #define any_of(S, p) { for (auto const& s : S) if (p(s)) return true; return false; } + /** \brief Iterator for the [0..sz[0]) X [0..sz[1]) X ... X [0..sz[n-1]). it contains the current value. From 7bb962d9340ca96c088bc8878f813dc4490823b8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Nov 2022 12:49:55 -0700 Subject: [PATCH 28/57] add ad-hoc any-of for cross compatibility and simplifying interface Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/model_reconstruction_trail.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 96d27e4c4..4aa8a54fd 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -68,7 +68,7 @@ class model_reconstruction_trail { } bool intersects(ast_mark const& free_vars, vector const& added) { - return std::any_of(added.begin(), added.end(), [&](dependent_expr const& d) { return intersects(free_vars, d); }); + return any_of(added, [&](dependent_expr const& d) { return intersects(free_vars, d); }); } public: From 154b09309b5ea5200b71a25966d1f733d5e10e6f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Nov 2022 14:04:44 -0700 Subject: [PATCH 29/57] fixing build, wip on model reconstruction integration into dependent-expr-state --- src/ast/rewriter/expr_replacer.h | 1 + src/ast/simplifiers/bv_slice.cpp | 4 +- src/ast/simplifiers/dependent_expr_state.h | 24 +++++--- src/ast/simplifiers/euf_completion.cpp | 2 +- .../model_reconstruction_trail.cpp | 22 ++++--- .../simplifiers/model_reconstruction_trail.h | 14 ++--- src/ast/simplifiers/solve_eqs.cpp | 58 +++++++++---------- src/ast/simplifiers/solve_eqs.h | 3 +- src/tactic/dependent_expr_state_tactic.h | 10 +++- src/util/util.h | 1 - 10 files changed, 74 insertions(+), 65 deletions(-) diff --git a/src/ast/rewriter/expr_replacer.h b/src/ast/rewriter/expr_replacer.h index c1bfabd12..96418f00b 100644 --- a/src/ast/rewriter/expr_replacer.h +++ b/src/ast/rewriter/expr_replacer.h @@ -39,6 +39,7 @@ public: void operator()(expr * t, expr_ref & result); void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); } void operator()(expr_ref_vector& v) { expr_ref t(m()); for (unsigned i = 0; i < v.size(); ++i) (*this)(v.get(i), t), v[i] = t; } + std::pair replace_with_dep(expr* t) { expr_ref r(m()); expr_dependency_ref d(m()); (*this)(t, r, d); return { r, d }; } virtual unsigned get_num_steps() const { return 0; } virtual void reset() = 0; diff --git a/src/ast/simplifiers/bv_slice.cpp b/src/ast/simplifiers/bv_slice.cpp index f39fa932e..75e0a890c 100644 --- a/src/ast/simplifiers/bv_slice.cpp +++ b/src/ast/simplifiers/bv_slice.cpp @@ -109,12 +109,12 @@ namespace bv { }; if (lo > 0 && !b.contains(lo)) { b.insert(lo); - if (m_num_scopes > 0) + if (num_scopes() > 0) m_trail.push(remove_set(b, lo)); } if (hi + 1 < sz && !b.contains(hi + 1)) { b.insert(hi + 1); - if (m_num_scopes > 0) + if (num_scopes() > 0) m_trail.push(remove_set(b, hi+ 1)); } } diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 32ad59681..803c58510 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -34,6 +34,7 @@ Author: #include "util/params.h" #include "ast/converters/model_converter.h" #include "ast/simplifiers/dependent_expr.h" +#include "ast/simplifiers/model_reconstruction_trail.h" /** @@ -46,6 +47,12 @@ public: virtual dependent_expr const& operator[](unsigned i) = 0; virtual void update(unsigned i, dependent_expr const& j) = 0; virtual bool inconsistent() = 0; + + trail_stack m_trail; + void push() { m_trail.push_scope(); } + void pop(unsigned n) { m_trail.pop_scope(n); } + + virtual model_reconstruction_trail* model_trail() { return nullptr; } }; /** @@ -55,20 +62,21 @@ class dependent_expr_simplifier { protected: ast_manager& m; dependent_expr_state& m_fmls; - unsigned m_qhead = 0; // pointer into last processed formula in m_fmls - unsigned m_num_scopes = 0; - trail_stack m_trail; - void advance_qhead(unsigned sz) { if (m_num_scopes > 0) m_trail.push(value_trail(m_qhead)); m_qhead = sz; } + trail_stack& m_trail; + unsigned m_qhead = 0; // pointer into last processed formula in m_fmls + + unsigned num_scopes() const { return m_trail.get_num_scopes(); } + + void advance_qhead(unsigned sz) { if (num_scopes() > 0) m_trail.push(value_trail(m_qhead)); m_qhead = sz; } public: - dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s) {} + dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {} virtual ~dependent_expr_simplifier() {} - virtual void push() { m_num_scopes++; m_trail.push_scope(); } - virtual void pop(unsigned n) { m_num_scopes -= n; m_trail.pop_scope(n); } + virtual void push() { } + virtual void pop(unsigned n) { } virtual void reduce() = 0; virtual void collect_statistics(statistics& st) const {} virtual void reset_statistics() {} virtual void updt_params(params_ref const& p) {} - virtual model_converter_ref get_model_converter() { return model_converter_ref(); } }; /** diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index e5b328d7f..e1360fa0b 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -213,7 +213,7 @@ namespace euf { old_value = nullptr; } }; - if (m_num_scopes > 0) + if (num_scopes() > 0) m_trail.push(vtrail(m_canonical, n->get_id())); m_canonical.setx(n->get_id(), e); m_epochs.setx(n->get_id(), m_epoch, 0); diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 077443e7e..a8e75bfa3 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -21,11 +21,12 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vector rp = mk_default_expr_replacer(m, false); add_vars(d, free_vars); added.push_back(d); + for (auto& t : m_trail) { if (!t->m_active) continue; @@ -45,13 +46,12 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorset_substitution(t->m_subst.get()); // rigid entries: // apply substitution to added in case of rigid model convertions for (auto& d : added) { auto [f, dep1] = d(); - expr_ref g(m); - expr_dependency_ref dep2(m); - (*t->m_replace)(f, g, dep2); + auto [g, dep2] = rp->replace_with_dep(f); d = dependent_expr(m, g, m.mk_join(dep1, dep2)); } } @@ -69,9 +69,9 @@ model_converter_ref model_reconstruction_trail::get_model_converter() { // substituted variables by their terms. // - scoped_ptr rp = mk_default_expr_replacer(m, true); - scoped_ptr subst = alloc(expr_substitution, m, true, false); - rp->set_substitution(subst.get()); + scoped_ptr rp = mk_default_expr_replacer(m, false); + expr_substitution subst(m, true, false); + rp->set_substitution(&subst); generic_model_converter_ref mc = alloc(generic_model_converter, m, "dependent-expr-model"); bool first = true; for (unsigned i = m_trail.size(); i-- > 0; ) { @@ -83,19 +83,17 @@ model_converter_ref model_reconstruction_trail::get_model_converter() { first = false; for (auto const& [v, def] : t->m_subst->sub()) { expr_dependency* dep = t->m_subst->dep(v); - subst->insert(v, def, dep); + subst.insert(v, def, dep); mc->add(v, def); } continue; } - expr_dependency_ref new_dep(m); - expr_ref new_def(m); for (auto const& [v, def] : t->m_subst->sub()) { - rp->operator()(def, new_def, new_dep); + auto [new_def, new_dep] = rp->replace_with_dep(def); expr_dependency* dep = t->m_subst->dep(v); new_dep = m.mk_join(dep, new_dep); - subst->insert(v, new_def, new_dep); + subst.insert(v, new_def, new_dep); mc->add(v, new_def); } diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 4aa8a54fd..c9b42bc92 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -33,13 +33,12 @@ Author: class model_reconstruction_trail { struct entry { - scoped_ptr m_replace; scoped_ptr m_subst; vector m_removed; bool m_active = true; - entry(expr_replacer* r, expr_substitution* s, vector const& rem) : - m_replace(r), m_subst(s), m_removed(rem) {} + entry(expr_substitution* s, vector const& rem) : + m_subst(s), m_removed(rem) {} bool is_loose() const { return !m_removed.empty(); } @@ -64,7 +63,8 @@ class model_reconstruction_trail { bool intersects(ast_mark const& free_vars, dependent_expr const& d) { expr_ref term(d.fml(), d.get_manager()); - return any_of(subterms::all(term), [&](expr* t) { return free_vars.is_marked(t); }); + auto iter = subterms::all(term); + return any_of(iter, [&](expr* t) { return free_vars.is_marked(t); }); } bool intersects(ast_mark const& free_vars, vector const& added) { @@ -77,10 +77,10 @@ public: m(m), m_trail_stack(tr) {} /** - * add a new substitution to the stack + * add a new substitution to the trail */ - void push(expr_replacer* r, vector const& removed) { - m_trail.push_back(alloc(entry, r, nullptr, removed)); + void push(expr_substitution* s, vector const& removed) { + m_trail.push_back(alloc(entry, s, removed)); m_trail_stack.push(push_back_vector(m_trail)); } diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index e4361ad97..b5b500a96 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -60,7 +60,7 @@ namespace euf { m_id2level.reset(); m_id2level.resize(m_id2var.size(), UINT_MAX); m_subst_ids.reset(); - m_subst = alloc(expr_substitution, m, true, false); + m_subst = alloc(expr_substitution, m, true, false); auto is_explored = [&](unsigned id) { return m_id2level[id] != UINT_MAX; @@ -105,30 +105,22 @@ namespace euf { } } - void solve_eqs::add_subst(dependent_eq const& eq) { - SASSERT(can_be_var(eq.var)); - m_subst->insert(eq.var, eq.term, nullptr, eq.dep); - ++m_stats.m_num_elim_vars; - } - void solve_eqs::normalize() { scoped_ptr rp = mk_default_expr_replacer(m, true); - m_subst->reset(); rp->set_substitution(m_subst.get()); std::sort(m_subst_ids.begin(), m_subst_ids.end(), [&](unsigned u, unsigned v) { return m_id2level[u] > m_id2level[v]; }); - expr_dependency_ref new_dep(m); - expr_ref new_def(m); - for (unsigned id : m_subst_ids) { if (!m.inc()) break; auto const& [v, def, dep] = m_next[id][0]; - rp->operator()(def, new_def, new_dep); + auto [new_def, new_dep] = rp->replace_with_dep(def); m_stats.m_num_steps += rp->get_num_steps() + 1; + ++m_stats.m_num_elim_vars; new_dep = m.mk_join(dep, new_dep); - m_subst->insert(v, new_def, nullptr, new_dep); + m_subst->insert(v, new_def, new_dep); + SASSERT(can_be_var(v)); // we updated the substitution, but we don't need to reset rp // because all cached values there do not depend on v. } @@ -147,11 +139,10 @@ namespace euf { return; scoped_ptr rp = mk_default_expr_replacer(m, true); rp->set_substitution(m_subst.get()); - expr_ref new_f(m); - expr_dependency_ref new_dep(m); + for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) { auto [f, d] = m_fmls[i](); - rp->operator()(f, new_f, new_dep); + auto [new_f, new_dep] = rp->replace_with_dep(f); if (new_f == f) continue; new_dep = m.mk_join(d, new_dep); @@ -164,13 +155,27 @@ namespace euf { for (extract_eq* ex : m_extract_plugins) ex->pre_process(m_fmls); - // TODO add a loop. - dep_eq_vector eqs; - get_eqs(eqs); - extract_dep_graph(eqs); - extract_subst(); - apply_subst(); + unsigned count = 0; + do { + m_subst_ids.reset(); + if (!m.inc()) + return; + dep_eq_vector eqs; + get_eqs(eqs); + extract_dep_graph(eqs); + extract_subst(); + apply_subst(); + ++count; + } + while (!m_subst_ids.empty() && count < 20); + advance_qhead(m_fmls.size()); + save_subst(); + } + + void solve_eqs::save_subst() { + if (!m_subst->empty()) + m_fmls.model_trail()->push(m_subst.detach(), {}); } void solve_eqs::filter_unsafe_vars() { @@ -181,16 +186,7 @@ namespace euf { m_unsafe_vars.mark(term); } - typedef generic_model_converter gmc; - model_converter_ref solve_eqs::get_model_converter() { - model_converter_ref mc = alloc(gmc, m, "solve-eqs"); - for (unsigned id : m_subst_ids) { - auto* v = m_id2var[id]; - static_cast(mc.get())->add(v, m_subst->find(v)); - } - return mc; - } solve_eqs::solve_eqs(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_rewriter(m) { diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 49cd90ca2..db7a1323b 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -67,6 +67,7 @@ namespace euf { void extract_dep_graph(dep_eq_vector& eqs); void normalize(); void apply_subst(); + void save_subst(); public: @@ -78,7 +79,5 @@ namespace euf { void updt_params(params_ref const& p) override; void collect_statistics(statistics& st) const override; - - model_converter_ref get_model_converter() override; }; } diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index ae3635c77..719a29eea 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -24,12 +24,16 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { std::string m_name; ref m_factory; scoped_ptr m_simp; + trail_stack m_trail; + scoped_ptr m_model_trail; goal_ref m_goal; dependent_expr m_dep; void init() { if (!m_simp) m_simp = m_factory->mk(m, m_params, *this); + if (!m_model_trail) + m_model_trail = alloc(model_reconstruction_trail, m, m_trail); } public: @@ -60,6 +64,10 @@ public: bool inconsistent() override { return m_goal->inconsistent(); } + + model_reconstruction_trail* model_trail() override { + return m_model_trail.get(); + } char const* name() const override { return m_name.c_str(); } @@ -83,7 +91,7 @@ public: m_simp->reduce(); m_goal->inc_depth(); if (in->models_enabled()) - in->set(m_simp->get_model_converter().get()); + in->set(m_model_trail->get_model_converter().get()); result.push_back(in.get()); } diff --git a/src/util/util.h b/src/util/util.h index c3f06d8d3..925e20186 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -367,7 +367,6 @@ bool any_of(S& set, T& p) { return true; return false; } -// #define any_of(S, p) { for (auto const& s : S) if (p(s)) return true; return false; } /** \brief Iterator for the [0..sz[0]) X [0..sz[1]) X ... X [0..sz[n-1]). From ae2672f132b6d35fea65e178db06bf2133d45513 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Nov 2022 14:11:24 -0700 Subject: [PATCH 30/57] fix build Signed-off-by: Nikolaj Bjorner --- src/util/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/util.h b/src/util/util.h index 925e20186..2a037770d 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -361,7 +361,7 @@ void set_fatal_error_handler(void (*pfn)(int error_code)); template -bool any_of(S& set, T& p) { +bool any_of(S& set, T const& p) { for (auto const& s : set) if (p(s)) return true; From 4d8860c0bc4f4a587e9e66b3494df446a52c3112 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 5 Nov 2022 10:34:57 -0700 Subject: [PATCH 31/57] wip - adding context equation solver the solve_eqs_tactic is to be replaced by a re-implementation that uses solve_eqs in the simplifiers directory. The re-implementation should address efficiency issues with the previous code. At this point it punts on low level proofs. The plan is to use coarser dependency tracking instead of low level proofs for pre-processing. Dependencies can be converted into a proof hint representation that can be checked using a stronger checker. --- src/ast/occurs.cpp | 43 +++++ src/ast/occurs.h | 9 +- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/dependent_expr.h | 2 + src/ast/simplifiers/dependent_expr_state.h | 3 +- src/ast/simplifiers/extract_eqs.cpp | 52 +++--- src/ast/simplifiers/extract_eqs.h | 7 +- src/ast/simplifiers/solve_context_eqs.cpp | 203 +++++++++++++++++++++ src/ast/simplifiers/solve_context_eqs.h | 58 ++++++ src/ast/simplifiers/solve_eqs.cpp | 39 +++- src/ast/simplifiers/solve_eqs.h | 45 ++--- src/tactic/core/solve_eqs_tactic.cpp | 51 +----- src/tactic/dependent_expr_state_tactic.h | 4 +- src/util/mpn.cpp | 8 +- src/util/util.h | 8 + 15 files changed, 416 insertions(+), 117 deletions(-) create mode 100644 src/ast/simplifiers/solve_context_eqs.cpp create mode 100644 src/ast/simplifiers/solve_context_eqs.h diff --git a/src/ast/occurs.cpp b/src/ast/occurs.cpp index 21e7f5906..2bcd98396 100644 --- a/src/ast/occurs.cpp +++ b/src/ast/occurs.cpp @@ -74,3 +74,46 @@ bool occurs(func_decl * d, expr * n) { return false; } +void mark_occurs(ptr_vector& to_check, expr* v, expr_mark& occ) { + expr_fast_mark2 visited; + occ.mark(v, true); + visited.mark(v, true); + while (!to_check.empty()) { + expr* e = to_check.back(); + if (visited.is_marked(e)) { + to_check.pop_back(); + continue; + } + if (is_app(e)) { + bool does_occur = false; + bool all_visited = true; + for (expr* arg : *to_app(e)) { + if (!visited.is_marked(arg)) { + to_check.push_back(arg); + all_visited = false; + } + else + does_occur |= occ.is_marked(arg); + } + if (all_visited) { + occ.mark(e, does_occur); + visited.mark(e, true); + to_check.pop_back(); + } + } + else if (is_quantifier(e)) { + expr* body = to_quantifier(e)->get_expr(); + if (visited.is_marked(body)) { + visited.mark(e, true); + occ.mark(e, occ.is_marked(body)); + to_check.pop_back(); + } + else + to_check.push_back(body); + } + else { + visited.mark(e, true); + to_check.pop_back(); + } + } +} \ No newline at end of file diff --git a/src/ast/occurs.h b/src/ast/occurs.h index 15a33ddf5..7475a292c 100644 --- a/src/ast/occurs.h +++ b/src/ast/occurs.h @@ -18,8 +18,8 @@ Revision History: --*/ #pragma once -class expr; -class func_decl; +#include "util/vector.h" +#include "ast/ast.h" /** \brief Return true if n1 occurs in n2 @@ -31,4 +31,9 @@ bool occurs(expr * n1, expr * n2); */ bool occurs(func_decl * d, expr * n); +/** +* \brief Mark sub-expressions of to_check by whether v occurs in these. +*/ +void mark_occurs(ptr_vector& to_check, expr* v, expr_mark& occurs); + diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index dc7aa6fb6..ef04cc433 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -4,6 +4,7 @@ z3_add_component(simplifiers euf_completion.cpp extract_eqs.cpp model_reconstruction_trail.cpp + solve_context_eqs.cpp solve_eqs.cpp COMPONENT_DEPENDENCIES euf diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h index 53f9cb9d8..f789bf332 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -72,6 +72,8 @@ public: ast_manager& get_manager() const { return m; } expr* fml() const { return m_fml; } + + expr_dependency* dep() const { return m_dep; } std::tuple operator()() const { return { m_fml, m_dep }; diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 803c58510..6bdd34626 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -47,12 +47,13 @@ public: virtual dependent_expr const& operator[](unsigned i) = 0; virtual void update(unsigned i, dependent_expr const& j) = 0; virtual bool inconsistent() = 0; + virtual model_reconstruction_trail& model_trail() = 0; trail_stack m_trail; void push() { m_trail.push_scope(); } void pop(unsigned n) { m_trail.pop_scope(n); } - virtual model_reconstruction_trail* model_trail() { return nullptr; } + }; /** diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 1e9b576e1..e77ce9e06 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -38,9 +38,9 @@ namespace euf { expr* x, * y; if (m.is_eq(f, x, y)) { if (is_uninterp_const(x)) - eqs.push_back(dependent_eq(to_app(x), expr_ref(y, m), d)); + eqs.push_back(dependent_eq(e.fml(), to_app(x), expr_ref(y, m), d)); if (is_uninterp_const(y)) - eqs.push_back(dependent_eq(to_app(y), expr_ref(x, m), d)); + eqs.push_back(dependent_eq(e.fml(), to_app(y), expr_ref(x, m), d)); } expr* c, * th, * el, * x1, * y1, * x2, * y2; if (m_ite_solver && m.is_ite(f, c, th, el)) { @@ -52,13 +52,13 @@ namespace euf { if (x2 == y1 && is_uninterp_const(x2)) std::swap(x1, y1); if (x1 == x2 && is_uninterp_const(x1)) - eqs.push_back(dependent_eq(to_app(x1), expr_ref(m.mk_ite(c, y1, y2), m), d)); + eqs.push_back(dependent_eq(e.fml(), to_app(x1), expr_ref(m.mk_ite(c, y1, y2), m), d)); } } if (is_uninterp_const(f)) - eqs.push_back(dependent_eq(to_app(f), expr_ref(m.mk_true(), m), d)); + eqs.push_back(dependent_eq(e.fml(), to_app(f), expr_ref(m.mk_true(), m), d)); if (m.is_not(f, x) && is_uninterp_const(x)) - eqs.push_back(dependent_eq(to_app(x), expr_ref(m.mk_false(), m), d)); + eqs.push_back(dependent_eq(e.fml(), to_app(x), expr_ref(m.mk_false(), m), d)); } void updt_params(params_ref const& p) { @@ -76,7 +76,7 @@ namespace euf { // solve u mod r1 = y -> u = r1*mod!1 + y - void solve_mod(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + void solve_mod(expr* orig, expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { expr* u, * z; rational r1, r2; if (!a.is_mod(x, u, z)) @@ -87,7 +87,11 @@ namespace euf { return; expr_ref term(m); term = a.mk_add(a.mk_mul(z, m.mk_fresh_const("mod", a.mk_int())), y); - solve_eq(u, term, d, eqs); + + if (is_uninterp_const(u)) + eqs.push_back(dependent_eq(orig, to_app(u), term, d)); + else + solve_eq(orig, u, term, d, eqs); } /*** @@ -96,7 +100,7 @@ namespace euf { * -1*x + Y = Z -> x = Y - Z * a*x + Y = Z -> x = (Z - Y)/a for is-real(x) */ - void solve_add(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + void solve_add(expr* orig, expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { if (!a.is_add(x)) return; expr* u, * z; @@ -115,18 +119,18 @@ namespace euf { for (expr* arg : *to_app(x)) { if (is_uninterp_const(arg)) { mk_term(i); - eqs.push_back(dependent_eq(to_app(arg), term, d)); + eqs.push_back(dependent_eq(orig, to_app(arg), term, d)); } else if (a.is_mul(arg, u, z) && a.is_numeral(u, r) && is_uninterp_const(z)) { if (r == -1) { mk_term(i); term = a.mk_uminus(term); - eqs.push_back(dependent_eq(to_app(z), term, d)); + eqs.push_back(dependent_eq(orig, to_app(z), term, d)); } else if (a.is_real(arg) && r != 0) { mk_term(i); term = a.mk_div(term, u); - eqs.push_back(dependent_eq(to_app(z), term, d)); + eqs.push_back(dependent_eq(orig, to_app(z), term, d)); } } else if (a.is_real(arg) && a.is_mul(arg)) { @@ -155,7 +159,7 @@ namespace euf { } mk_term(i); term = a.mk_div(term, a.mk_mul(args.size(), args.data())); - eqs.push_back(dependent_eq(to_app(xarg), term, d)); + eqs.push_back(dependent_eq(orig, to_app(xarg), term, d)); } } ++i; @@ -165,7 +169,7 @@ namespace euf { /*** * Solve for x * Y = Z, where Y != 0 -> x = Z / Y */ - void solve_mul(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + void solve_mul(expr* orig, expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { if (!a.is_mul(x)) return; rational r; @@ -193,7 +197,7 @@ namespace euf { args.push_back(arg2); } term = a.mk_div(y, a.mk_mul(args)); - eqs.push_back(dependent_eq(to_app(arg), term, d)); + eqs.push_back(dependent_eq(orig, to_app(arg), term, d)); } } @@ -214,22 +218,24 @@ namespace euf { } } - void solve_eq(expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { - solve_add(x, y, d, eqs); - solve_mod(x, y, d, eqs); - solve_mul(x, y, d, eqs); + void solve_eq(expr* orig, expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + solve_add(orig, x, y, d, eqs); + solve_mod(orig, x, y, d, eqs); + solve_mul(orig, x, y, d, eqs); } public: + arith_extract_eq(ast_manager& m) : m(m), a(m), m_args(m) {} + void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { if (!m_enabled) return; auto [f, d] = e(); expr* x, * y; if (m.is_eq(f, x, y) && a.is_int_real(x)) { - solve_eq(x, y, d, eqs); - solve_eq(y, x, d, eqs); + solve_eq(f, x, y, d, eqs); + solve_eq(f, y, x, d, eqs); } } @@ -237,10 +243,8 @@ namespace euf { if (!m_enabled) return; m_nonzero.reset(); - for (unsigned i = 0; i < fmls.size(); ++i) { - auto [f, d] = fmls[i](); - add_pos(f); - } + for (unsigned i = 0; i < fmls.size(); ++i) + add_pos(fmls[i].fml()); } diff --git a/src/ast/simplifiers/extract_eqs.h b/src/ast/simplifiers/extract_eqs.h index 00f96f59b..f38829dfc 100644 --- a/src/ast/simplifiers/extract_eqs.h +++ b/src/ast/simplifiers/extract_eqs.h @@ -27,10 +27,11 @@ Author: namespace euf { struct dependent_eq { - app* var; - expr_ref term; + expr* orig; // original expression that encoded equation + app* var; // isolated variable + expr_ref term; // defined term expr_dependency* dep; - dependent_eq(app* var, expr_ref const& term, expr_dependency* d) : var(var), term(term), dep(d) {} + dependent_eq(expr* orig, app* var, expr_ref const& term, expr_dependency* d) : orig(orig), var(var), term(term), dep(d) {} }; typedef vector dep_eq_vector; diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp new file mode 100644 index 000000000..766c18535 --- /dev/null +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -0,0 +1,203 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + solve_context_eqs.cpp + +Abstract: + + simplifier for solving equations within a context + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +Notes: + +The variable v is solved based on expression e. +Check that every occurrence of v uses e in conjunctive context. + +Walk formulas containing v in as and-or. +Equalities that occur within at least one alternation of or are +considered as candidates. + +To constrain how formulas are traversed, first +label sub-expressions that contain v. An equality eq is safe for v +if every occurrence of v occurs in the same conjunctive context as eq. + +--*/ + +#include "ast/ast.h" +#include "ast/ast_pp.h" +#include "ast/occurs.h" +#include "ast/simplifiers/solve_context_eqs.h" +#include "ast/simplifiers/solve_eqs.h" + +namespace euf { + + + solve_context_eqs::solve_context_eqs(solve_eqs& s): m(s.m), m_fmls(s.m_fmls), m_solve_eqs(s) {} + + bool solve_context_eqs::is_safe_eq(expr* e) { + m_and_pos.reset(); m_and_neg.reset(); m_or_pos.reset(); m_or_neg.reset(); + for (unsigned i = 0; i < m_fmls.size(); ++i) + if (!is_safe_eq(m_fmls[i].fml(), e)) + return false; + return true; + } + + /** + * Check if some conjunction of f contains equality 'e'. + * If this is not the case, then check that every conjunct that contains v + * recursively contains a disjunction that contains 'e'. + */ + bool solve_context_eqs::is_safe_eq(unsigned recursion_depth, expr* f, bool sign, expr* e) { + if (!contains_v(f)) + return true; + signed_expressions conjuncts; + if (contains_conjunctively(f, sign, e, conjuncts)) + return true; + if (recursion_depth > 3) + return false; + return all_of(conjuncts, [&](std::pair const& p) { return is_disjunctively_safe(recursion_depth, p.second, p.first, e); }); + } + + /* + * Every disjunction in f that contains v also contains the equation e. + */ + bool solve_context_eqs::is_disjunctively_safe(unsigned recursion_depth, expr* f, bool sign, expr* e) { + signed_expressions todo; + todo.push_back({sign, f}); + while (!todo.empty()) { + auto [s, f] = todo.back(); + todo.pop_back(); + if (s && m_or_neg.is_marked(f)) + continue; + if (!s && m_or_pos.is_marked(f)) + continue; + if (s) + m_or_neg.mark(f, true); + else + m_or_pos.mark(f, true); + if (!s && f == e) + continue; + else if (!contains_v(f)) + continue; + else if (s && m.is_and(f)) + for (auto* arg : *to_app(f)) + todo.push_back({s, arg}); + else if (!s && m.is_or(f)) + for (auto* arg : *to_app(f)) + todo.push_back({s, arg}); + else if (m.is_not(f, f)) + todo.push_back({!s, f}); + else if (!is_safe_eq(recursion_depth + 1, f, s, e)) + return false; + } + return true; + } + + /** + * Determine whether some conjunction in f contains e. + * If no conjunction contains e, then return the set of conjunctions that contain v. + */ + bool solve_context_eqs::contains_conjunctively(expr* f, bool sign, expr* e, signed_expressions& conjuncts) { + signed_expressions todo; + todo.push_back({sign, f}); + while (!todo.empty()) { + auto [s, f] = todo.back(); + todo.pop_back(); + if (!s && f == e) + return true; + if (!s && m_and_pos.is_marked(f)) + continue; + if (s && m_and_neg.is_marked(f)) + continue; + if (s) + m_and_neg.mark(f, true); + else + m_and_pos.mark(f, true); + if (!contains_v(f)) + continue; + if (!s && m.is_and(f)) + for (auto* arg : *to_app(f)) + todo.push_back({false, arg}); + else if (s && m.is_or(f)) + for (auto* arg : *to_app(f)) + todo.push_back({true, arg}); + else if (m.is_not(f, f)) + todo.push_back({!s, f}); + else + conjuncts.push_back({s, f}); + } + return false; + } + + void solve_context_eqs::init_contains(expr* v) { + m_contains_v.reset(); + for (unsigned i = 0; i < m_fmls.size(); ++i) + m_todo.push_back(m_fmls[i].fml()); + mark_occurs(m_todo, v, m_contains_v); + SASSERT(m_todo.empty()); + } + + void solve_context_eqs::collect_nested_equalities(dep_eq_vector& eqs) { + expr_mark visited; + for (unsigned i = m_solve_eqs.m_qhead; i < m_fmls.size(); ++i) + collect_nested_equalities(m_fmls[i], visited, eqs); + + unsigned j = 0; + for (auto const& eq : eqs) { + init_contains(eq.var); + if (is_safe_eq(eq.orig)) + eqs[j++] = eq; + } + eqs.shrink(j); + } + + void solve_context_eqs::collect_nested_equalities(dependent_expr const& df, expr_mark& visited, dep_eq_vector& eqs) { + + svector> todo; + todo.push_back({ false, 0, df.fml()}); + + // even depth is conjunctive context, odd is disjunctive + // when alternating between conjunctive and disjunctive context, increment depth. + auto inc_or = [](unsigned depth) { + return (0 == depth % 2) ? depth + 1 : depth; + }; + auto inc_and = [](unsigned depth) { + return (0 == depth % 2) ? depth : depth + 1; + }; + + while (!todo.empty()) { + auto [s, depth, f] = todo.back(); + todo.pop_back(); + if (visited.is_marked(f)) + continue; + visited.mark(f, true); + if (s && m.is_and(f)) { + for (auto* arg : *to_app(f)) + todo.push_back({ s, inc_or(depth), arg }); + } + else if (!s && m.is_or(f)) { + for (auto* arg : *to_app(f)) + todo.push_back({ s, inc_or(depth), arg }); + } + if (!s && m.is_and(f)) { + for (auto* arg : *to_app(f)) + todo.push_back({ s, inc_and(depth), arg }); + } + else if (s && m.is_or(f)) { + for (auto* arg : *to_app(f)) + todo.push_back({ s, inc_and(depth), arg }); + } + else if (m.is_not(f, f)) + todo.push_back({ !s, depth, f }); + else if (!s && 1 == depth % 2) { + for (extract_eq* ex : m_solve_eqs.m_extract_plugins) + ex->get_eqs(dependent_expr(m, f, df.dep()), eqs); + } + } + } +} diff --git a/src/ast/simplifiers/solve_context_eqs.h b/src/ast/simplifiers/solve_context_eqs.h new file mode 100644 index 000000000..b3db74127 --- /dev/null +++ b/src/ast/simplifiers/solve_context_eqs.h @@ -0,0 +1,58 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + solve_context_eqs.h + +Abstract: + + simplifier for solving equations within a context + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/simplifiers/extract_eqs.h" + +namespace euf { + + class solve_eqs; + + + class solve_context_eqs { + + ast_manager& m; + dependent_expr_state& m_fmls; + solve_eqs& m_solve_eqs; + expr_mark m_and_pos, m_and_neg, m_or_pos, m_or_neg; + expr_mark m_contains_v; + ptr_vector m_todo; + + typedef svector> signed_expressions; + + bool contains_v(expr* f) const { return m_contains_v.is_marked(f); } + bool is_safe_eq(expr* e); + bool is_safe_eq(unsigned recursion_depth, expr* f, bool sign, expr* e); + bool is_safe_eq(expr* f, expr* e) { return is_safe_eq(0, f, false, e); } + bool is_disjunctively_safe(unsigned recursion_depth, expr* f, bool sign, expr* e); + bool contains_conjunctively(expr* f, bool sign, expr* e, signed_expressions& conjuncts); + + void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs); + void init_contains(expr* v); + + + public: + + solve_context_eqs(solve_eqs& s); + + void collect_nested_equalities(dep_eq_vector& eqs); + + }; +} diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index b5b500a96..31e063119 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -23,22 +23,29 @@ Author: #include "ast/recfun_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/solve_eqs.h" +#include "ast/simplifiers/solve_context_eqs.h" #include "ast/converters/generic_model_converter.h" #include "params/tactic_params.hpp" namespace euf { + void solve_eqs::get_eqs(dep_eq_vector& eqs) { + for (extract_eq* ex : m_extract_plugins) + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) + ex->get_eqs(m_fmls[i], eqs); + } + // initialize graph that maps variable ids to next ids void solve_eqs::extract_dep_graph(dep_eq_vector& eqs) { m_var2id.reset(); m_id2var.reset(); m_next.reset(); unsigned sz = 0; - for (auto const& [v, t, d] : eqs) + for (auto const& [orig, v, t, d] : eqs) sz = std::max(sz, v->get_id()); m_var2id.resize(sz + 1, UINT_MAX); - for (auto const& [v, t, d] : eqs) { + for (auto const& [orig, v, t, d] : eqs) { if (is_var(v) || !can_be_var(v)) continue; m_var2id[v->get_id()] = m_id2var.size(); @@ -91,7 +98,7 @@ namespace euf { continue; m_id2level[id] = curr_level++; for (auto const& eq : m_next[j]) { - auto const& [v, t, d] = eq; + auto const& [orig, v, t, d] = eq; if (!is_safe(curr_level, t)) continue; m_next[j][0] = eq; @@ -114,7 +121,7 @@ namespace euf { for (unsigned id : m_subst_ids) { if (!m.inc()) break; - auto const& [v, def, dep] = m_next[id][0]; + auto const& [orig, v, def, dep] = m_next[id][0]; auto [new_def, new_dep] = rp->replace_with_dep(def); m_stats.m_num_steps += rp->get_num_steps() + 1; ++m_stats.m_num_elim_vars; @@ -134,7 +141,7 @@ namespace euf { }); } - void solve_eqs::apply_subst() { + void solve_eqs::apply_subst(vector& old_fmls) { if (!m.inc()) return; scoped_ptr rp = mk_default_expr_replacer(m, true); @@ -146,6 +153,7 @@ namespace euf { if (new_f == f) continue; new_dep = m.mk_join(d, new_dep); + old_fmls.push_back(m_fmls[i]); m_fmls.update(i, dependent_expr(m, new_f, new_dep)); } } @@ -157,6 +165,7 @@ namespace euf { unsigned count = 0; do { + vector old_fmls; m_subst_ids.reset(); if (!m.inc()) return; @@ -164,18 +173,30 @@ namespace euf { get_eqs(eqs); extract_dep_graph(eqs); extract_subst(); - apply_subst(); + apply_subst(old_fmls); ++count; } while (!m_subst_ids.empty() && count < 20); + save_subst({}); + + if (m_config.m_context_solve) { + vector old_fmls; + dep_eq_vector eqs; + m_subst_ids.reset(); + solve_context_eqs context_solve(*this); + context_solve.collect_nested_equalities(eqs); + extract_dep_graph(eqs); + extract_subst(); + apply_subst(old_fmls); + save_subst(old_fmls); + } advance_qhead(m_fmls.size()); - save_subst(); } - void solve_eqs::save_subst() { + void solve_eqs::save_subst(vector const& old_fmls) { if (!m_subst->empty()) - m_fmls.model_trail()->push(m_subst.detach(), {}); + m_fmls.model_trail().push(m_subst.detach(), old_fmls); } void solve_eqs::filter_unsafe_vars() { diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index db7a1323b..35044b373 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -26,58 +26,51 @@ Author: namespace euf { class solve_eqs : public dependent_expr_simplifier { + + friend class solve_context_eqs; + struct stats { unsigned m_num_steps = 0; unsigned m_num_elim_vars = 0; }; + struct config { bool m_context_solve = true; unsigned m_max_occs = UINT_MAX; }; - th_rewriter m_rewriter; - scoped_ptr_vector m_extract_plugins; - unsigned_vector m_var2id, m_id2level, m_subst_ids; - ptr_vector m_id2var; - vector m_next; - scoped_ptr m_subst; - - expr_mark m_unsafe_vars; // expressions that cannot be replaced stats m_stats; config m_config; - - void add_subst(dependent_eq const& eq); + th_rewriter m_rewriter; + scoped_ptr_vector m_extract_plugins; + unsigned_vector m_var2id; // app->get_id() |-> small numeral + ptr_vector m_id2var; // small numeral |-> app + unsigned_vector m_id2level; // small numeral |-> level in substitution ordering + unsigned_vector m_subst_ids; // sorted list of small numeral by level + vector m_next; // adjacency list for solved equations + scoped_ptr m_subst; // current substitution + expr_mark m_unsafe_vars; // expressions that cannot be replaced bool is_var(expr* e) const { return e->get_id() < m_var2id.size() && m_var2id[e->get_id()] != UINT_MAX; } unsigned var2id(expr* v) const { return m_var2id[v->get_id()]; } - - void get_eqs(dep_eq_vector& eqs) { - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) - get_eqs(m_fmls[i], eqs); - } - - void get_eqs(dependent_expr const& f, dep_eq_vector& eqs) { - for (extract_eq* ex : m_extract_plugins) - ex->get_eqs(f, eqs); - } - - void filter_unsafe_vars(); bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e); } + void get_eqs(dep_eq_vector& eqs); + void filter_unsafe_vars(); void extract_subst(); void extract_dep_graph(dep_eq_vector& eqs); void normalize(); - void apply_subst(); - void save_subst(); + void apply_subst(vector& old_fmls); + void save_subst(vector const& old_fmls); public: solve_eqs(ast_manager& m, dependent_expr_state& fmls); - void push() override { dependent_expr_simplifier::push(); } - void pop(unsigned n) override { dependent_expr_simplifier::pop(n); } void reduce() override; void updt_params(params_ref const& p) override; + void collect_statistics(statistics& st) const override; + }; } diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index 3e338b57e..cfc4d8eeb 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -479,52 +479,11 @@ class solve_eqs_tactic : public tactic { ptr_vector m_todo; void mark_occurs(expr_mark& occ, goal const& g, expr* v) { - expr_fast_mark2 visited; - occ.mark(v, true); - visited.mark(v, true); - for (unsigned j = 0; j < g.size(); ++j) { - m_todo.push_back(g.form(j)); - } - while (!m_todo.empty()) { - expr* e = m_todo.back(); - if (visited.is_marked(e)) { - m_todo.pop_back(); - continue; - } - if (is_app(e)) { - bool does_occur = false; - bool all_visited = true; - for (expr* arg : *to_app(e)) { - if (!visited.is_marked(arg)) { - m_todo.push_back(arg); - all_visited = false; - } - else { - does_occur |= occ.is_marked(arg); - } - } - if (all_visited) { - occ.mark(e, does_occur); - visited.mark(e, true); - m_todo.pop_back(); - } - } - else if (is_quantifier(e)) { - expr* body = to_quantifier(e)->get_expr(); - if (visited.is_marked(body)) { - visited.mark(e, true); - occ.mark(e, occ.is_marked(body)); - m_todo.pop_back(); - } - else { - m_todo.push_back(body); - } - } - else { - visited.mark(e, true); - m_todo.pop_back(); - } - } + SASSERT(m_todo.empty()); + for (unsigned j = 0; j < g.size(); ++j) + m_todo.push_back(g.form(j)); + ::mark_occurs(m_todo, v, occ); + SASSERT(m_todo.empty()); } expr_mark m_compatible_tried; diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 719a29eea..41b32baac 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -65,8 +65,8 @@ public: return m_goal->inconsistent(); } - model_reconstruction_trail* model_trail() override { - return m_model_trail.get(); + model_reconstruction_trail& model_trail() override { + return *m_model_trail; } char const* name() const override { return m_name.c_str(); } diff --git a/src/util/mpn.cpp b/src/util/mpn.cpp index 0cbe9e9f8..bc9017726 100644 --- a/src/util/mpn.cpp +++ b/src/util/mpn.cpp @@ -34,8 +34,8 @@ int mpn_manager::compare(mpn_digit const * a, unsigned lnga, trace(a, lnga); - unsigned j = max(lnga, lngb) - 1; - for (; j != -1u && res == 0; j--) { + unsigned j = max(lnga, lngb); + for (; j-- > 0 && res == 0;) { mpn_digit const & u_j = (j < lnga) ? a[j] : zero; mpn_digit const & v_j = (j < lngb) ? b[j] : zero; if (u_j > v_j) @@ -310,7 +310,7 @@ bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, mpn_double_digit q_hat, temp, r_hat; mpn_digit borrow; - for (unsigned j = m-1; j != -1u; j--) { + for (unsigned j = m; j-- > 0; ) { temp = (((mpn_double_digit)numer[j+n]) << DIGIT_BITS) | ((mpn_double_digit)numer[j+n-1]); q_hat = temp / (mpn_double_digit) denom[n-1]; r_hat = temp % (mpn_double_digit) denom[n-1]; @@ -388,7 +388,7 @@ char * mpn_manager::to_string(mpn_digit const * a, unsigned lng, char * buf, uns void mpn_manager::display_raw(std::ostream & out, mpn_digit const * a, unsigned lng) const { out << "["; - for (unsigned i = lng-1; i != -1u; i-- ) { out << a[i]; if (i != 0) out << "|"; } + for (unsigned i = lng; i-- > 0; ) { out << a[i]; if (i != 0) out << "|"; } out << "]"; } diff --git a/src/util/util.h b/src/util/util.h index 2a037770d..121031492 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -368,6 +368,14 @@ bool any_of(S& set, T const& p) { return false; } +template +bool all_of(S& set, T const& p) { + for (auto const& s : set) + if (!p(s)) + return false; + return true; +} + /** \brief Iterator for the [0..sz[0]) X [0..sz[1]) X ... X [0..sz[n-1]). it contains the current value. From 6c12aaad747d11b35113c3166029b6a33baaa916 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 5 Nov 2022 22:42:59 -0700 Subject: [PATCH 32/57] wip - testing solve-eqs2, added as tactic --- src/ast/expr_substitution.h | 4 ++ src/ast/for_each_expr.cpp | 41 +++++++++++-------- src/ast/for_each_expr.h | 26 ++++++------ src/ast/rewriter/bool_rewriter.cpp | 23 +++++++++-- src/ast/simplifiers/extract_eqs.cpp | 2 + src/ast/simplifiers/solve_context_eqs.cpp | 27 ++++++++----- src/ast/simplifiers/solve_context_eqs.h | 4 +- src/ast/simplifiers/solve_eqs.cpp | 48 +++++++++++++++-------- src/ast/simplifiers/solve_eqs.h | 2 + src/tactic/core/CMakeLists.txt | 1 + src/tactic/core/solve_eqs_tactic.cpp | 13 +++++- src/tactic/dependent_expr_state_tactic.h | 4 ++ 12 files changed, 135 insertions(+), 60 deletions(-) diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index 0e285ff7d..8f756e061 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -59,6 +59,10 @@ public: std::ostream& display(std::ostream& out); }; +inline std::ostream& operator<<(std::ostream& out, expr_substitution& s) { + return s.display(out); +} + class scoped_expr_substitution { expr_substitution& m_subst; expr_ref_vector m_trail; diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 54e176fc5..0790b418d 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -64,15 +64,22 @@ bool has_skolem_functions(expr * n) { return false; } -subterms::subterms(expr_ref_vector const& es, bool include_bound): m_include_bound(include_bound), m_es(es) {} -subterms::subterms(expr_ref const& e, bool include_bound) : m_include_bound(include_bound), m_es(e.m()) {if (e) m_es.push_back(e); } -subterms::iterator subterms::begin() { return iterator(*this, true); } -subterms::iterator subterms::end() { return iterator(*this, false); } -subterms::iterator::iterator(subterms& f, bool start): m_include_bound(f.m_include_bound), m_es(f.m_es) { - if (!start) m_es.reset(); +subterms::subterms(expr_ref_vector const& es, bool include_bound, ptr_vector* esp, expr_mark* vp): m_include_bound(include_bound), m_es(es), m_esp(esp), m_vp(vp) {} +subterms::subterms(expr_ref const& e, bool include_bound, ptr_vector* esp, expr_mark* vp) : m_include_bound(include_bound), m_es(e.m()), m_esp(esp), m_vp(vp) { if (e) m_es.push_back(e); } +subterms::iterator subterms::begin() { return iterator(* this, m_esp, m_vp, true); } +subterms::iterator subterms::end() { return iterator(*this, nullptr, nullptr, false); } +subterms::iterator::iterator(subterms& f, ptr_vector* esp, expr_mark* vp, bool start): m_include_bound(f.m_include_bound), m_esp(esp), m_visitedp(vp) { + if (!esp) + m_esp = &m_es; + else + m_esp->reset(); + if (!m_visitedp) + m_visitedp = &m_visited; + if (start) + m_esp->append(f.m_es.size(), f.m_es.data()); } expr* subterms::iterator::operator*() { - return m_es.back(); + return m_esp->back(); } subterms::iterator subterms::iterator::operator++(int) { iterator tmp = *this; @@ -80,27 +87,29 @@ subterms::iterator subterms::iterator::operator++(int) { return tmp; } subterms::iterator& subterms::iterator::operator++() { - expr* e = m_es.back(); - m_visited.mark(e, true); + expr* e = m_esp->back(); + // IF_VERBOSE(0, verbose_stream() << e->get_ref_count() << "\n"); + SASSERT(e->get_ref_count() > 0); + m_visitedp->mark(e, true); if (is_app(e)) for (expr* arg : *to_app(e)) - m_es.push_back(arg); + m_esp->push_back(arg); else if (is_quantifier(e) && m_include_bound) - m_es.push_back(to_quantifier(e)->get_expr()); + m_esp->push_back(to_quantifier(e)->get_expr()); - while (!m_es.empty() && m_visited.is_marked(m_es.back())) - m_es.pop_back(); + while (!m_esp->empty() && m_visitedp->is_marked(m_esp->back())) + m_esp->pop_back(); return *this; } bool subterms::iterator::operator==(iterator const& other) const { // ignore state of visited - if (other.m_es.size() != m_es.size()) { + if (other.m_esp->size() != m_esp->size()) { return false; } - for (unsigned i = m_es.size(); i-- > 0; ) { - if (m_es.get(i) != other.m_es.get(i)) + for (unsigned i = m_esp->size(); i-- > 0; ) { + if (m_esp->get(i) != other.m_esp->get(i)) return false; } return true; diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index b724bed86..99a0f6b9d 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -170,15 +170,20 @@ bool has_skolem_functions(expr * n); class subterms { bool m_include_bound = false; expr_ref_vector m_es; - subterms(expr_ref const& e, bool include_bound); - subterms(expr_ref_vector const& es, bool include_bound); + ptr_vector* m_esp = nullptr; + expr_mark* m_vp = nullptr; + subterms(expr_ref const& e, bool include_bound, ptr_vector* esp, expr_mark* vp); + subterms(expr_ref_vector const& es, bool include_bound, ptr_vector* esp, expr_mark* vp); public: + ~subterms() { if (m_vp) m_vp->reset(); } class iterator { - bool m_include_bound = false; - expr_ref_vector m_es; - expr_mark m_visited; + bool m_include_bound = false; + ptr_vector m_es; + ptr_vector* m_esp = nullptr; + expr_mark m_visited; + expr_mark* m_visitedp = nullptr; public: - iterator(subterms& f, bool start); + iterator(subterms& f, ptr_vector* esp, expr_mark* vp, bool start); expr* operator*(); iterator operator++(int); iterator& operator++(); @@ -186,11 +191,10 @@ public: bool operator!=(iterator const& other) const; }; - - static subterms all(expr_ref const& e) { return subterms(e, true); } - static subterms ground(expr_ref const& e) { return subterms(e, false); } - static subterms all(expr_ref_vector const& e) { return subterms(e, true); } - static subterms ground(expr_ref_vector const& e) { return subterms(e, false); } + static subterms all(expr_ref const& e, ptr_vector* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, true, esp, vp); } + static subterms ground(expr_ref const& e, ptr_vector* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); } + static subterms all(expr_ref_vector const& e, ptr_vector* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, true, esp, vp); } + static subterms ground(expr_ref_vector const& e, ptr_vector* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); } iterator begin(); iterator end(); }; diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 1964e6528..442bef855 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -244,10 +244,29 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args result = buffer.back(); return BR_DONE; default: +#if 0 + // stupid or removal. A very special case of circuit optimization. + expr* x, * y, * z, * u; + auto is_complement = [&](expr* a, expr* b) { + expr* c; + if (m().is_not(a, c) && c == b) + return true; + if (m().is_not(b, c) && c == a) + return true; + return false; + }; + + if (sz == 2 && m().is_and(buffer[0], x, y) && m().is_and(buffer[1], z, u) && x == z && is_complement(y, u)) { + result = x; + return BR_DONE; + } +#endif + if (m_local_ctx && m_local_ctx_cost <= m_local_ctx_limit) { if (local_ctx_simp(sz, buffer.data(), result)) return BR_DONE; } + if (s) { ast_lt lt; std::sort(buffer.begin(), buffer.end(), lt); @@ -556,9 +575,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ return true; \ } \ if (m_flat_and_or && m().is_or(arg)) { \ - unsigned sz = to_app(arg)->get_num_args(); \ - for (unsigned j = 0; j < sz; j++) { \ - expr * arg_arg = to_app(arg)->get_arg(j); \ + for (expr * arg_arg : *to_app(arg)) { \ push_new_arg(arg_arg, new_args, neg_lits, pos_lits); \ } \ } \ diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index e77ce9e06..543030e91 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -37,6 +37,8 @@ namespace euf { auto [f, d] = e(); expr* x, * y; if (m.is_eq(f, x, y)) { + if (x == y) + return; if (is_uninterp_const(x)) eqs.push_back(dependent_eq(e.fml(), to_app(x), expr_ref(y, m), d)); if (is_uninterp_const(y)) diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index 766c18535..c11505726 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -30,6 +30,7 @@ if every occurrence of v occurs in the same conjunctive context as eq. #include "ast/ast.h" #include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" #include "ast/occurs.h" #include "ast/simplifiers/solve_context_eqs.h" #include "ast/simplifiers/solve_eqs.h" @@ -134,14 +135,6 @@ namespace euf { return false; } - void solve_context_eqs::init_contains(expr* v) { - m_contains_v.reset(); - for (unsigned i = 0; i < m_fmls.size(); ++i) - m_todo.push_back(m_fmls[i].fml()); - mark_occurs(m_todo, v, m_contains_v); - SASSERT(m_todo.empty()); - } - void solve_context_eqs::collect_nested_equalities(dep_eq_vector& eqs) { expr_mark visited; for (unsigned i = m_solve_eqs.m_qhead; i < m_fmls.size(); ++i) @@ -149,7 +142,23 @@ namespace euf { unsigned j = 0; for (auto const& eq : eqs) { - init_contains(eq.var); + + m_contains_v.reset(); + + // first check if v is in term. If it is, then the substitution candidate is unsafe + m_todo.push_back(eq.term); + mark_occurs(m_todo, eq.var, m_contains_v); + SASSERT(m_todo.empty()); + if (m_contains_v.is_marked(eq.term)) + continue; + + // then mark occurrences + for (unsigned i = 0; i < m_fmls.size(); ++i) + m_todo.push_back(m_fmls[i].fml()); + mark_occurs(m_todo, eq.var, m_contains_v); + SASSERT(m_todo.empty()); + + // subject to occurrences, check if equality is safe if (is_safe_eq(eq.orig)) eqs[j++] = eq; } diff --git a/src/ast/simplifiers/solve_context_eqs.h b/src/ast/simplifiers/solve_context_eqs.h index b3db74127..fb330b57b 100644 --- a/src/ast/simplifiers/solve_context_eqs.h +++ b/src/ast/simplifiers/solve_context_eqs.h @@ -44,9 +44,7 @@ namespace euf { bool is_disjunctively_safe(unsigned recursion_depth, expr* f, bool sign, expr* e); bool contains_conjunctively(expr* f, bool sign, expr* e, signed_expressions& conjuncts); - void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs); - void init_contains(expr* v); - + void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs); public: diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 31e063119..b611e0144 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -20,6 +20,8 @@ Author: #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" +#include "ast/occurs.h" #include "ast/recfun_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/solve_eqs.h" @@ -75,13 +77,15 @@ namespace euf { auto is_safe = [&](unsigned lvl, expr* t) { for (auto* e : subterms::all(expr_ref(t, m))) + for (auto* e : subterms::all(expr_ref(t, m), &m_todo, &m_visited)) if (is_var(e) && m_id2level[var2id(e)] < lvl) - return false; + return false; return true; }; unsigned init_level = UINT_MAX; unsigned_vector todo; + for (unsigned id = 0; id < m_id2var.size(); ++id) { if (is_explored(id)) continue; @@ -96,14 +100,16 @@ namespace euf { todo.pop_back(); if (is_explored(j)) continue; - m_id2level[id] = curr_level++; + m_id2level[j] = curr_level++; for (auto const& eq : m_next[j]) { auto const& [orig, v, t, d] = eq; + SASSERT(j == var2id(v)); if (!is_safe(curr_level, t)) continue; + SASSERT(!occurs(v, t)); m_next[j][0] = eq; - m_subst_ids.push_back(id); - for (expr* e : subterms::all(expr_ref(t, m))) + m_subst_ids.push_back(j); + for (expr* e : subterms::all(expr_ref(t, m), &m_todo, &m_visited)) if (is_var(e) && !is_explored(var2id(e))) todo.push_back(var2id(e)); break; @@ -113,19 +119,20 @@ namespace euf { } void solve_eqs::normalize() { - scoped_ptr rp = mk_default_expr_replacer(m, true); + scoped_ptr rp = mk_default_expr_replacer(m, false); rp->set_substitution(m_subst.get()); std::sort(m_subst_ids.begin(), m_subst_ids.end(), [&](unsigned u, unsigned v) { return m_id2level[u] > m_id2level[v]; }); for (unsigned id : m_subst_ids) { if (!m.inc()) - break; + return; auto const& [orig, v, def, dep] = m_next[id][0]; auto [new_def, new_dep] = rp->replace_with_dep(def); m_stats.m_num_steps += rp->get_num_steps() + 1; ++m_stats.m_num_elim_vars; new_dep = m.mk_join(dep, new_dep); + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(v, m) << " -> " << mk_bounded_pp(new_def, m) << "\n"); m_subst->insert(v, new_def, new_dep); SASSERT(can_be_var(v)); // we updated the substitution, but we don't need to reset rp @@ -139,12 +146,14 @@ namespace euf { expr* def = m_subst->find(eq.var); tout << mk_pp(eq.var, m) << "\n----->\n" << mk_pp(def, m) << "\n\n"; }); + + } void solve_eqs::apply_subst(vector& old_fmls) { if (!m.inc()) return; - scoped_ptr rp = mk_default_expr_replacer(m, true); + scoped_ptr rp = mk_default_expr_replacer(m, false); rp->set_substitution(m_subst.get()); for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) { @@ -152,6 +161,7 @@ namespace euf { auto [new_f, new_dep] = rp->replace_with_dep(f); if (new_f == f) continue; + m_rewriter(new_f); new_dep = m.mk_join(d, new_dep); old_fmls.push_back(m_fmls[i]); m_fmls.update(i, dependent_expr(m, new_f, new_dep)); @@ -164,29 +174,35 @@ namespace euf { ex->pre_process(m_fmls); unsigned count = 0; + vector old_fmls; + dep_eq_vector eqs; do { - vector old_fmls; + old_fmls.reset(); m_subst_ids.reset(); - if (!m.inc()) - return; - dep_eq_vector eqs; + eqs.reset(); get_eqs(eqs); extract_dep_graph(eqs); extract_subst(); + normalize(); apply_subst(old_fmls); ++count; } - while (!m_subst_ids.empty() && count < 20); + while (!m_subst_ids.empty() && count < 20 && m.inc()); + + if (!m.inc()) + return; + save_subst({}); - if (m_config.m_context_solve) { - vector old_fmls; - dep_eq_vector eqs; + if (m_config.m_context_solve) { + old_fmls.reset(); m_subst_ids.reset(); + eqs.reset(); solve_context_eqs context_solve(*this); context_solve.collect_nested_equalities(eqs); extract_dep_graph(eqs); extract_subst(); + normalize(); apply_subst(old_fmls); save_subst(old_fmls); } @@ -203,7 +219,7 @@ namespace euf { m_unsafe_vars.reset(); recfun::util rec(m); for (func_decl* f : rec.get_rec_funs()) - for (expr* term : subterms::all(expr_ref(rec.get_def(f).get_rhs(), m))) + for (expr* term : subterms::all(expr_ref(rec.get_def(f).get_rhs(), m), &m_todo, &m_visited)) m_unsafe_vars.mark(term); } diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 35044b373..8f5988a38 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -50,6 +50,8 @@ namespace euf { vector m_next; // adjacency list for solved equations scoped_ptr m_subst; // current substitution expr_mark m_unsafe_vars; // expressions that cannot be replaced + ptr_vector m_todo; + expr_mark m_visited; bool is_var(expr* e) const { return e->get_id() < m_var2id.size() && m_var2id[e->get_id()] != UINT_MAX; } unsigned var2id(expr* v) const { return m_var2id[v->get_id()]; } diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index e57510d4f..38d2699f0 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -49,6 +49,7 @@ z3_add_component(core_tactics reduce_invertible_tactic.h simplify_tactic.h solve_eqs_tactic.h + solve_eqs2_tactic.h special_relations_tactic.h split_clause_tactic.h symmetry_reduce_tactic.h diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index cfc4d8eeb..fdba65b3f 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -425,6 +425,7 @@ class solve_eqs_tactic : public tactic { else pr = m().mk_modus_ponens(g.pr(idx), pr); } + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(var, m()) << " -> " << mk_bounded_pp(def, m()) << "\n"); m_subst->insert(var, def, pr, g.dep(idx)); } @@ -620,9 +621,11 @@ class solve_eqs_tactic : public tactic { expr* arg = args.get(i), *lhs = nullptr, *rhs = nullptr; if (m().is_eq(arg, lhs, rhs) && !m().is_bool(lhs)) { if (trivial_solve1(lhs, rhs, var, def, pr) && is_compatible(g, idx, path, var, arg)) { + IF_VERBOSE(11, verbose_stream() << "nested " << mk_bounded_pp(var.get(), m()) << " -> " << mk_bounded_pp(def, m()) << "\n"); insert_solution(g, idx, arg, var, def, pr); } else if (trivial_solve1(rhs, lhs, var, def, pr) && is_compatible(g, idx, path, var, arg)) { + IF_VERBOSE(11, verbose_stream() << "nested " << mk_bounded_pp(var.get(), m()) << " -> " << mk_bounded_pp(def, m()) << "\n"); insert_solution(g, idx, arg, var, def, pr); } else { @@ -981,6 +984,10 @@ class solve_eqs_tactic : public tactic { unsigned get_num_eliminated_vars() const { return m_num_eliminated_vars; } + + void collect_statistics(statistics& st) { + st.update("solve eqs elim vars", get_num_eliminated_vars()); + } // // TBD: rewrite the tactic to first apply a topological sorting that @@ -1033,6 +1040,9 @@ class solve_eqs_tactic : public tactic { g->inc_depth(); g->add(mc.get()); result.push_back(g.get()); + + + IF_VERBOSE(10, statistics st; collect_statistics(st); st.display_smt2(verbose_stream())); } }; @@ -1066,7 +1076,6 @@ public: void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); - report_tactic_progress(":num-elim-vars", m_imp->get_num_eliminated_vars()); } void cleanup() override { @@ -1085,7 +1094,7 @@ public: } void collect_statistics(statistics & st) const override { - st.update("eliminated vars", m_imp->get_num_eliminated_vars()); + m_imp->collect_statistics(st); } void reset_statistics() override { diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 41b32baac..01e135e8a 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -93,6 +93,10 @@ public: if (in->models_enabled()) in->set(m_model_trail->get_model_converter().get()); result.push_back(in.get()); + + statistics st; + collect_statistics(st); + IF_VERBOSE(10, st.display_smt2(verbose_stream())); } void cleanup() override { From d8133a47c209ff0140de0c823027da84507af862 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 5 Nov 2022 22:47:46 -0700 Subject: [PATCH 33/57] Update solve_eqs.cpp --- src/ast/simplifiers/solve_eqs.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index b611e0144..f364c31e1 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -76,7 +76,6 @@ namespace euf { }; auto is_safe = [&](unsigned lvl, expr* t) { - for (auto* e : subterms::all(expr_ref(t, m))) for (auto* e : subterms::all(expr_ref(t, m), &m_todo, &m_visited)) if (is_var(e) && m_id2level[var2id(e)] < lvl) return false; From 4c1a3fab647ca405198ef6cbb2ce06dc782f7f45 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 5 Nov 2022 23:15:03 -0700 Subject: [PATCH 34/57] fix #6442 --- src/api/api_seq.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index d64589cde..6a9d0f81c 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -212,6 +212,8 @@ extern "C" { buffer.push_back('\\'); buffer.push_back('u'); buffer.push_back('{'); + if (ch == 0) + buff.push_back('0'); while (ch > 0) { unsigned d = ch & 0xF; if (d < 10) From 78f9e6b31a9f2defc7be77ed263469649208dfe7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 6 Nov 2022 11:57:21 -0800 Subject: [PATCH 35/57] extend error type message with more information - display the arguments that are passed --- src/ast/ast.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 50b9e5a4d..7bb732a5d 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2250,7 +2250,9 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar if (type_error) { std::ostringstream buffer; buffer << "Wrong number of arguments (" << num_args - << ") passed to function " << mk_pp(decl, *this); + << ") passed to function " << mk_pp(decl, *this) << " "; + for (unsigned i = 0; i < num_args; ++i) + buffer << "\narg: " << mk_pp(args[i], *this) << "\n"; throw ast_exception(std::move(buffer).str()); } app * r = nullptr; From a4c2a2b22c105e1b9045ddc18cbf8ad652cee49d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 6 Nov 2022 11:57:46 -0800 Subject: [PATCH 36/57] use ast_util::mk_not to avoid redundant double negations during nff --- src/ast/normal_forms/nnf.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index d0398543b..a35956454 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -22,6 +22,7 @@ Notes: #include "ast/normal_forms/nnf.h" #include "ast/normal_forms/nnf_params.hpp" #include "ast/used_vars.h" +#include "ast/ast_util.h" #include "ast/well_sorted.h" #include "ast/act_cache.h" #include "ast/rewriter/var_subst.h" @@ -137,7 +138,7 @@ class skolemizer { if (is_sk_hack(p)) { expr * sk_hack = to_app(p)->get_arg(0); if (q->get_kind() == forall_k) // check whether is in negative/positive context. - tmp = m.mk_or(body, m.mk_not(sk_hack)); // negative context + tmp = m.mk_or(body, mk_not(m, sk_hack)); // negative context else tmp = m.mk_and(body, sk_hack); // positive context body = tmp; @@ -148,7 +149,7 @@ class skolemizer { p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) - p = m.mk_skolemization(m.mk_not(q), m.mk_not(r)); + p = m.mk_skolemization(mk_not(m, q), mk_not(m, r)); else p = m.mk_skolemization(q, r); } @@ -388,7 +389,7 @@ struct nnf::imp { } void skip(expr * t, bool pol) { - expr * r = pol ? t : m.mk_not(t); + expr * r = pol ? t : mk_not(m, t); m_result_stack.push_back(r); if (proofs_enabled()) { m_result_pr_stack.push_back(m.mk_oeq_reflexivity(r)); @@ -639,7 +640,7 @@ struct nnf::imp { m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); if (!fr.m_pol) - n2 = m.mk_not(n2); + n2 = mk_not(m, n2); m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { From 8ff1e44a9593d729a028bc93996a2b9032e5faeb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 6 Nov 2022 11:58:21 -0800 Subject: [PATCH 37/57] add discriminator to whether context contains recursive functions to avoid enabling recursive function solver when there are just macros --- src/ast/recfun_decl_plugin.cpp | 1 + src/ast/recfun_decl_plugin.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index bf865b393..755a3eccd 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -473,6 +473,7 @@ namespace recfun { } void plugin::set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) { + m_has_rec_defs |= !is_macro; u().set_definition(r, d, is_macro, n_vars, vars, rhs); for (case_def & c : d.get_def()->get_cases()) m_case_defs.insert(c.get_decl(), &c); diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index 8e1279c0a..c369e2827 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -165,6 +165,7 @@ namespace recfun { mutable scoped_ptr m_util; def_map m_defs; // function->def case_def_map m_case_defs; // case_pred->def + bool m_has_rec_defs = false; ast_manager & m() { return *m_manager; } @@ -200,6 +201,7 @@ namespace recfun { bool has_def(func_decl* f) const { return m_defs.contains(f); } bool has_defs() const; + bool has_rec_defs() const { return m_has_rec_defs; } def const& get_def(func_decl* f) const { return *(m_defs[f]); } promise_def get_promise_def(func_decl* f) const { return promise_def(&u(), m_defs[f]); } def& get_def(func_decl* f) { return *(m_defs[f]); } @@ -249,6 +251,8 @@ namespace recfun { //has_defs(); } + bool has_rec_defs() const { return m_plugin->has_rec_defs(); } + // Date: Sun, 6 Nov 2022 11:59:56 -0800 Subject: [PATCH 38/57] bypass built-in proof objects for clause trail the build-in proof constructors are not flexible when it comes to allowing alternation of justified lemmas and lemmas without justifications. --- src/smt/smt_clause_proof.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index a2bc381e4..ad3d3818c 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -147,18 +147,22 @@ namespace smt { for (auto& info : m_trail) { expr_ref fact = mk_or(info.m_clause); proof* pr = info.m_proof; + expr* args[2] = { pr, fact }; + unsigned num_args = 2, offset = 0; + if (!pr) + offset = 1; switch (info.m_status) { case status::assumption: - ps.push_back(m.mk_assumption_add(pr, fact)); + ps.push_back(m.mk_app(symbol("assumption"), num_args - offset, args + offset, m.mk_proof_sort())); break; case status::lemma: - ps.push_back(m.mk_lemma_add(pr, fact)); + ps.push_back(m.mk_app(symbol("lemma"), num_args - offset, args + offset, m.mk_proof_sort())); break; case status::th_assumption: - ps.push_back(m.mk_th_assumption_add(pr, fact)); + ps.push_back(m.mk_app(symbol("th-assumption"), num_args - offset, args + offset, m.mk_proof_sort())); break; case status::th_lemma: - ps.push_back(m.mk_th_lemma_add(pr, fact)); + ps.push_back(m.mk_app(symbol("th-lemma"), num_args - offset, args + offset, m.mk_proof_sort())); break; case status::deleted: ps.push_back(m.mk_redundant_del(fact)); From f004478565fe29eaf28cf48d328841e803e57a29 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 6 Nov 2022 12:00:25 -0800 Subject: [PATCH 39/57] produce tseitin justification for clause proofs when a clause is a "gate". --- src/smt/smt_internalizer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 2483b2ca4..63848418f 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1593,6 +1593,18 @@ namespace smt { TRACE("gate_clause", tout << mk_ll_pp(pr, m);); mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } + else if (m_clause_proof.on_clause_active()) { + ptr_buffer new_lits; + for (unsigned i = 0; i < num_lits; i++) { + literal l = lits[i]; + bool_var v = l.var(); + expr * atom = m_bool_var2expr[v]; + new_lits.push_back(l.sign() ? m.mk_not(atom) : atom); + } + // expr* fact = m.mk_or(new_lits); + proof* pr = m.mk_app(symbol("tseitin"), new_lits.size(), new_lits.data(), m.mk_proof_sort()); + mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); + } else { mk_clause(num_lits, lits, nullptr); } From cbc5b1f4f600cf5964b82a7aaedeb6cdfc9316c0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 6 Nov 2022 12:09:45 -0800 Subject: [PATCH 40/57] have theory_recfun use recursive function discriminator to control when it is enabled --- src/ast/recfun_decl_plugin.cpp | 9 +++++---- src/smt/theory_recfun.cpp | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 755a3eccd..558488592 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -442,17 +442,18 @@ namespace recfun { return promise_def(&u(), d); } - void plugin::inherit(decl_plugin* other, ast_translation& tr) { - for (auto [k, v] : static_cast(other)->m_defs) { + void plugin::inherit(decl_plugin* _other, ast_translation& tr) { + plugin* other = static_cast(_other); + for (auto [k, v] : other->m_defs) { func_decl_ref f(tr(k), tr.to()); if (m_defs.contains(f)) continue; def* d = v->copy(u(), tr); m_defs.insert(f, d); for (case_def & c : d->get_cases()) - m_case_defs.insert(c.get_decl(), &c); - + m_case_defs.insert(c.get_decl(), &c); } + m_has_rec_defs = other->m_has_rec_defs; } promise_def plugin::ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) { diff --git a/src/smt/theory_recfun.cpp b/src/smt/theory_recfun.cpp index 416275275..515a51a17 100644 --- a/src/smt/theory_recfun.cpp +++ b/src/smt/theory_recfun.cpp @@ -102,9 +102,8 @@ namespace smt { void theory_recfun::relevant_eh(app * n) { SASSERT(ctx.relevancy()); // TRACEFN("relevant_eh: (defined) " << u().is_defined(n) << " " << mk_pp(n, m)); - if (u().is_defined(n) && u().has_defs()) { + if (u().is_defined(n) && u().has_defs()) push_case_expand(n); - } } void theory_recfun::push_scope_eh() { @@ -418,7 +417,7 @@ namespace smt { } void theory_recfun::add_theory_assumptions(expr_ref_vector & assumptions) { - if (u().has_defs() || !m_disabled_guards.empty()) { + if (u().has_rec_defs() || !m_disabled_guards.empty()) { app_ref dlimit = m_util.mk_num_rounds_pred(m_num_rounds); TRACEFN("add_theory_assumption " << dlimit); assumptions.push_back(dlimit); From 10fb71cf93c6a1e92996c8eb220412c2b0fba0c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:18:45 -0800 Subject: [PATCH 41/57] better error description for configuring restart --- src/sat/sat_config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 32d764658..4ca223be6 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -46,7 +46,7 @@ namespace sat { else if (s == symbol("static")) m_restart = RS_STATIC; else - throw sat_param_exception("invalid restart strategy"); + throw sat_param_exception("invalid restart strategy. Use ema (default), luby, geometric, static"); m_fast_glue_avg = p.restart_emafastglue(); m_slow_glue_avg = p.restart_emaslowglue(); From 8afec86fe8850dab73dc6ac1d0fdfcf20f9cecdc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:19:27 -0800 Subject: [PATCH 42/57] add option for flat_and_or --- src/params/bool_rewriter_params.pyg | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/params/bool_rewriter_params.pyg b/src/params/bool_rewriter_params.pyg index 85583cbca..c8d7ddbb7 100644 --- a/src/params/bool_rewriter_params.pyg +++ b/src/params/bool_rewriter_params.pyg @@ -1,8 +1,9 @@ def_module_params(module_name='rewriter', class_name='bool_rewriter_params', export=True, - params=(("ite_extra_rules", BOOL, False, "extra ite simplifications, these additional simplifications may reduce size locally but increase globally"), - ("flat", BOOL, True, "create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor"), + params=(("ite_extra_rules", BOOL, True, "extra ite simplifications, these additional simplifications may reduce size locally but increase globally"), + ("flat", BOOL, True, "create nary applications for +,*,bvadd,bvmul,bvand,bvor,bvxor"), + ("flat_and_or", BOOL, True, "create nary applications for and,or"), ("elim_and", BOOL, False, "conjunctions are rewritten using negation and disjunctions"), ('elim_ite', BOOL, True, "eliminate ite in favor of and/or"), ("local_ctx", BOOL, False, "perform local (i.e., cheap) context simplifications"), From ab36f86843aead141e1865907538432d1426cc6e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:19:48 -0800 Subject: [PATCH 43/57] add handler for reporting statistics --- src/tactic/tactic.cpp | 12 ++++++++++++ src/tactic/tactic.h | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index b1609ed85..8bf6efac5 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -75,6 +75,18 @@ void report_tactic_progress(char const * id, unsigned val) { } } +statistics_report::~statistics_report() { + statistics st; + if (m_tactic) + m_tactic->collect_statistics(st); + else if (m_collector) + m_collector(st); + if (st.size() == 0) + return; + IF_VERBOSE(TACTIC_VERBOSITY_LVL, st.display_smt2(verbose_stream())); +} + + void skip_tactic::operator()(goal_ref const & in, goal_ref_buffer& result) { result.push_back(in.get()); } diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 0f55ea0fe..c43120943 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -115,6 +115,15 @@ public: void report_tactic_progress(char const * id, unsigned val); +class statistics_report { + tactic* m_tactic = nullptr; + std::function m_collector; +public: + statistics_report(tactic& t):m_tactic(&t) {} + statistics_report(std::function& coll): m_collector(coll) {} + ~statistics_report(); +}; + class skip_tactic : public tactic { public: void operator()(goal_ref const & in, goal_ref_buffer& result) override; From a34701471fc6d8b1c37eb2b63697e63a358c6f1b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:20:25 -0800 Subject: [PATCH 44/57] clean up hoist rewriter --- src/ast/rewriter/hoist_rewriter.cpp | 80 ++++++++++++----------------- src/ast/rewriter/hoist_rewriter.h | 16 +++--- 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 4116945f0..40ad4604a 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -13,8 +13,6 @@ Author: Nikolaj Bjorner (nbjorner) 2019-2-4 -Notes: - --*/ @@ -25,19 +23,17 @@ Notes: hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): - m_manager(m), m_args1(m), m_args2(m), m_subst(m) { + m(m), m_args1(m), m_args2(m), m_subst(m) { updt_params(p); } br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { - if (num_args < 2) { + if (num_args < 2) return BR_FAILED; - } - for (unsigned i = 0; i < num_args; ++i) { - if (!is_and(es[i], nullptr)) { + + for (unsigned i = 0; i < num_args; ++i) + if (!is_and(es[i], nullptr)) return BR_FAILED; - } - } bool turn = false; m_preds1.reset(); @@ -52,12 +48,10 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & VERIFY(is_and(es[0], args[turn])); expr* e1, *e2; for (expr* e : *(args[turn])) { - if (m().is_eq(e, e1, e2)) { + if (m.is_eq(e, e1, e2)) (*uf)[turn].merge(mk_var(e1), mk_var(e2)); - } - else { + else (*preds)[turn].insert(e); - } } unsigned round = 0; for (unsigned j = 1; j < num_args; ++j) { @@ -72,44 +66,39 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & VERIFY(is_and(es[j], args[turn])); for (expr* e : *args[turn]) { - if (m().is_eq(e, e1, e2)) { + if (m.is_eq(e, e1, e2)) { m_es.push_back(e1); m_uf0.merge(mk_var(e1), mk_var(e2)); } - else if ((*preds)[last].contains(e)) { + else if ((*preds)[last].contains(e)) (*preds)[turn].insert(e); - } } - if ((*preds)[turn].empty() && m_es.empty()) { + if ((*preds)[turn].empty() && m_es.empty()) return BR_FAILED; - } m_eqs.reset(); for (expr* e : m_es) { - if (m_mark.is_marked(e)) { + if (m_mark.is_marked(e)) continue; - } unsigned u = mk_var(e); unsigned v = u; m_roots.reset(); do { m_mark.mark(e); unsigned r = (*uf)[last].find(v); - if (m_roots.find(r, e2)) { - m_eqs.push_back(std::make_pair(e, e2)); - } - else { + if (m_roots.find(r, e2)) + m_eqs.push_back({e, e2}); + else m_roots.insert(r, e); - } v = m_uf0.next(v); e = mk_expr(v); } while (u != v); } reset((*uf)[turn]); - for (auto const& p : m_eqs) - (*uf)[turn].merge(mk_var(p.first), mk_var(p.second)); + for (auto const& [e1, e2] : m_eqs) + (*uf)[turn].merge(mk_var(e1), mk_var(e2)); if ((*preds)[turn].empty() && m_eqs.empty()) return BR_FAILED; } @@ -118,25 +107,23 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & return BR_DONE; } // p & eqs & (or fmls) - expr_ref_vector fmls(m()); + expr_ref_vector fmls(m); m_subst.reset(); for (expr * p : (*preds)[turn]) { expr* q = nullptr; - if (m().is_not(p, q)) { - m_subst.insert(q, m().mk_false()); - } - else { - m_subst.insert(p, m().mk_true()); - } + if (m.is_not(p, q)) + m_subst.insert(q, m.mk_false()); + else + m_subst.insert(p, m.mk_true()); fmls.push_back(p); } for (auto& p : m_eqs) { - if (m().is_value(p.first)) + if (m.is_value(p.first)) std::swap(p.first, p.second); m_subst.insert(p.first, p.second); - fmls.push_back(m().mk_eq(p.first, p.second)); + fmls.push_back(m.mk_eq(p.first, p.second)); } - expr_ref ors(::mk_or(m(), num_args, es), m()); + expr_ref ors(::mk_or(m, num_args, es), m); m_subst(ors); fmls.push_back(ors); result = mk_and(fmls); @@ -146,9 +133,8 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & unsigned hoist_rewriter::mk_var(expr* e) { unsigned v = 0; - if (m_expr2var.find(e, v)) { + if (m_expr2var.find(e, v)) return v; - } m_uf1.mk_var(); v = m_uf2.mk_var(); SASSERT(v == m_var2expr.size()); @@ -158,15 +144,14 @@ unsigned hoist_rewriter::mk_var(expr* e) { } expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsigned num_args, expr* const* es) { - expr_ref result(m()); - expr_ref_vector args(m()), fmls(m()); + expr_ref result(m); + expr_ref_vector args(m), fmls(m); for (unsigned i = 0; i < num_args; ++i) { VERIFY(is_and(es[i], &m_args1)); fmls.reset(); - for (expr* e : m_args1) { + for (expr* e : m_args1) if (!preds.contains(e)) fmls.push_back(e); - } args.push_back(::mk_and(fmls)); } fmls.reset(); @@ -188,19 +173,18 @@ br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c } bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { - if (m().is_and(e)) { + if (m.is_and(e)) { if (args) { args->reset(); args->append(to_app(e)->get_num_args(), to_app(e)->get_args()); } return true; } - if (m().is_not(e, e) && m().is_or(e)) { + if (m.is_not(e, e) && m.is_or(e)) { if (args) { args->reset(); - for (expr* arg : *to_app(e)) { - args->push_back(::mk_not(m(), arg)); - } + for (expr* arg : *to_app(e)) + args->push_back(::mk_not(m, arg)); } return true; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index 2c627ad59..cc83bfa56 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -26,7 +26,7 @@ Notes: #include "util/obj_hashtable.h" class hoist_rewriter { - ast_manager & m_manager; + ast_manager & m; expr_ref_vector m_args1, m_args2; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; @@ -34,11 +34,9 @@ class hoist_rewriter { svector> m_eqs; u_map m_roots; expr_safe_replace m_subst; - obj_map m_expr2var; - ptr_vector m_var2expr; - expr_mark m_mark; - - br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result); + obj_map m_expr2var; + ptr_vector m_var2expr; + expr_mark m_mark; bool is_and(expr* e, expr_ref_vector* args); @@ -52,12 +50,12 @@ class hoist_rewriter { public: hoist_rewriter(ast_manager & m, params_ref const & p = params_ref()); - ast_manager& m() const { return m_manager; } - family_id get_fid() const { return m().get_basic_family_id(); } - bool is_eq(expr * t) const { return m().is_eq(t); } + family_id get_fid() const { return m.get_basic_family_id(); } + bool is_eq(expr * t) const { return m.is_eq(t); } void updt_params(params_ref const & p) {} static void get_param_descrs(param_descrs & r) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); + br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result); }; struct hoist_rewriter_cfg : public default_rewriter_cfg { From 3a4b8e2334001362aae12e94fbd6c9c989538d6e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:20:51 -0800 Subject: [PATCH 45/57] add rewrite rules to bv-rewriter --- src/ast/rewriter/bv_rewriter.cpp | 44 +++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index a04436e63..209f7a13b 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -1513,11 +1513,14 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re bool fused_numeral = false; bool expanded = false; bool fused_extract = false; + bool eq_args = true; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; expr * prev = nullptr; - if (i > 0) + if (i > 0) { prev = new_args.back(); + eq_args &= prev == arg; + } if (is_numeral(arg, v1, sz1) && prev != nullptr && is_numeral(prev, v2, sz2)) { v2 *= rational::power_of_two(sz1); v2 += v1; @@ -1526,10 +1529,8 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re fused_numeral = true; } else if (m_flat && m_util.is_concat(arg)) { - unsigned num2 = to_app(arg)->get_num_args(); - for (unsigned j = 0; j < num2; j++) { - new_args.push_back(to_app(arg)->get_arg(j)); - } + for (expr* arg2 : *to_app(arg)) + new_args.push_back(arg2); expanded = true; } else if (m_util.is_extract(arg) && @@ -1539,8 +1540,8 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re m_util.get_extract_low(prev) == m_util.get_extract_high(arg) + 1) { // (concat (extract[h1,l1] a) (extract[h2,l2] a)) --> (extract[h1,l2] a) if l1 == h2+1 expr * new_arg = m_mk_extract(m_util.get_extract_high(prev), - m_util.get_extract_low(arg), - to_app(arg)->get_arg(0)); + m_util.get_extract_low(arg), + to_app(arg)->get_arg(0)); new_args.pop_back(); new_args.push_back(new_arg); fused_extract = true; @@ -1549,14 +1550,26 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re new_args.push_back(arg); } } - if (!fused_numeral && !expanded && !fused_extract) + if (!fused_numeral && !expanded && !fused_extract) { + expr* x, *y, *z; + if (eq_args) { + if (m().is_ite(new_args.back(), x, y, z)) { + ptr_buffer args1, args2; + for (expr* arg : new_args) + args1.push_back(y), args2.push_back(z); + result = m().mk_ite(x, m_util.mk_concat(args1), m_util.mk_concat(args2)); + return BR_REWRITE2; + } + } return BR_FAILED; + + } SASSERT(!new_args.empty()); if (new_args.size() == 1) { result = new_args.back(); return fused_extract ? BR_REWRITE1 : BR_DONE; } - result = m_util.mk_concat(new_args.size(), new_args.data()); + result = m_util.mk_concat(new_args); if (fused_extract) return BR_REWRITE2; else if (expanded) @@ -2013,6 +2026,19 @@ br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { return BR_REWRITE2; } + expr* x, *y, *z; + if (m().is_ite(arg, x, y, z) && m_util.is_numeral(y, val, bv_size)) { + val = bitwise_not(bv_size, val); + result = m().mk_ite(x, m_util.mk_numeral(val, bv_size), m_util.mk_bv_not(z)); + return BR_REWRITE2; + } + + if (m().is_ite(arg, x, y, z) && m_util.is_numeral(z, val, bv_size)) { + val = bitwise_not(bv_size, val); + result = m().mk_ite(x, m_util.mk_bv_not(y), m_util.mk_numeral(val, bv_size)); + return BR_REWRITE2; + } + if (m_bvnot_simpl) { expr *s(nullptr), *t(nullptr); if (m_util.is_bv_mul(arg, s, t)) { From 238ea0a26433d3149361cd3fa8148266b426ef78 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:21:25 -0800 Subject: [PATCH 46/57] add shorthands for concatentation --- src/ast/bv_decl_plugin.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index 1cb17900f..37531d936 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -430,6 +430,9 @@ public: } app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); } app * mk_concat(expr_ref_vector const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); } + app * mk_concat(expr_ref_buffer const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); } + app * mk_concat(ptr_buffer const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); } + app * mk_concat(ptr_vector const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); } app * mk_bv_or(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BOR, num, args); } app * mk_bv_and(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BAND, num, args); } app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); } From f769e2f1f66cc87266ab373bacdd12257df0c3d0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:21:50 -0800 Subject: [PATCH 47/57] have bool rewriter use flat_and_or, and integrate hoist rewriter --- src/ast/rewriter/bool_rewriter.cpp | 11 ++++++++++- src/ast/rewriter/bool_rewriter.h | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 442bef855..632b6c0f6 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -24,7 +24,7 @@ Notes: void bool_rewriter::updt_params(params_ref const & _p) { bool_rewriter_params p(_p); - m_flat_and_or = p.flat(); + m_flat_and_or = p.flat_and_or(); m_elim_and = p.elim_and(); m_elim_ite = p.elim_ite(); m_local_ctx = p.local_ctx(); @@ -267,6 +267,15 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args return BR_DONE; } +#if 1 + br_status st; + st = m_hoist.mk_or(buffer.size(), buffer.data(), result); + if (st == BR_DONE) + return BR_REWRITE1; + if (st != BR_FAILED) + return st; +#endif + if (s) { ast_lt lt; std::sort(buffer.begin(), buffer.end(), lt); diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index a25a0f8a3..2cec0b2ce 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -20,6 +20,7 @@ Notes: #include "ast/ast.h" #include "ast/rewriter/rewriter.h" +#include "ast/rewriter/hoist_rewriter.h" #include "util/params.h" /** @@ -50,6 +51,7 @@ Notes: */ class bool_rewriter { ast_manager & m_manager; + hoist_rewriter m_hoist; bool m_flat_and_or; bool m_local_ctx; bool m_elim_and; @@ -78,7 +80,7 @@ class bool_rewriter { void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits); public: - bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); } + bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { updt_params(p); } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } bool is_eq(expr * t) const { return m().is_eq(t); } From 3a37cfca307bce55e3dd31859c5664572d9b83bc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 12:23:36 -0800 Subject: [PATCH 48/57] switch to solve_eqs2 tactic --- src/ast/rewriter/th_rewriter.cpp | 5 ++ src/ast/rewriter/th_rewriter.h | 3 + src/ast/simplifiers/extract_eqs.cpp | 11 ++++ src/ast/simplifiers/extract_eqs.h | 6 ++ .../model_reconstruction_trail.cpp | 2 +- src/ast/simplifiers/solve_context_eqs.cpp | 63 +++++++++++++------ src/ast/simplifiers/solve_context_eqs.h | 1 + src/ast/simplifiers/solve_eqs.cpp | 15 +++-- src/nlsat/tactic/qfnra_nlsat_tactic.cpp | 1 + src/opt/opt_context.cpp | 1 + src/tactic/core/solve_eqs2_tactic.h | 12 +++- src/tactic/core/solve_eqs_tactic.cpp | 5 +- src/tactic/core/solve_eqs_tactic.h | 10 ++- src/tactic/dependent_expr_state_tactic.h | 31 +++++---- src/tactic/sls/sls_tactic.cpp | 1 + src/tactic/smtlogics/qfaufbv_tactic.cpp | 1 + src/tactic/smtlogics/qfauflia_tactic.cpp | 1 + src/tactic/smtlogics/qfbv_tactic.cpp | 11 +++- src/tactic/smtlogics/qfidl_tactic.cpp | 1 + src/tactic/smtlogics/qflia_tactic.cpp | 1 + src/tactic/smtlogics/qfuf_tactic.cpp | 1 + src/tactic/smtlogics/qfufbv_tactic.cpp | 16 +++-- src/tactic/smtlogics/quant_tactics.cpp | 1 + src/tactic/ufbv/ufbv_tactic.cpp | 1 + 24 files changed, 149 insertions(+), 52 deletions(-) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 96b69dbc3..9604f6d16 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -924,6 +924,11 @@ void th_rewriter::get_param_descrs(param_descrs & r) { rewriter_params::collect_param_descrs(r); } +void th_rewriter::set_flat_and_or(bool f) { + m_imp->cfg().m_b_rw.set_flat_and_or(f); +} + + th_rewriter::~th_rewriter() { dealloc(m_imp); } diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index e432678a4..b84164abc 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -38,6 +38,9 @@ public: void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); + + void set_flat_and_or(bool f); + unsigned get_cache_size() const; unsigned get_num_steps() const; diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 543030e91..775eb4d22 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -29,16 +29,23 @@ namespace euf { class basic_extract_eq : public extract_eq { ast_manager& m; bool m_ite_solver = true; + bool m_allow_bool = true; public: basic_extract_eq(ast_manager& m) : m(m) {} + virtual void set_allow_booleans(bool f) { + m_allow_bool = f; + } + void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { auto [f, d] = e(); expr* x, * y; if (m.is_eq(f, x, y)) { if (x == y) return; + if (!m_allow_bool && m.is_bool(x)) + return; if (is_uninterp_const(x)) eqs.push_back(dependent_eq(e.fml(), to_app(x), expr_ref(y, m), d)); if (is_uninterp_const(y)) @@ -47,6 +54,8 @@ namespace euf { expr* c, * th, * el, * x1, * y1, * x2, * y2; if (m_ite_solver && m.is_ite(f, c, th, el)) { if (m.is_eq(th, x1, y1) && m.is_eq(el, x2, y2)) { + if (!m_allow_bool && m.is_bool(x1)) + return; if (x1 == y2 && is_uninterp_const(x1)) std::swap(x2, y2); if (x2 == y2 && is_uninterp_const(x2)) @@ -57,6 +66,8 @@ namespace euf { eqs.push_back(dependent_eq(e.fml(), to_app(x1), expr_ref(m.mk_ite(c, y1, y2), m), d)); } } + if (!m_allow_bool) + return; if (is_uninterp_const(f)) eqs.push_back(dependent_eq(e.fml(), to_app(f), expr_ref(m.mk_true(), m), d)); if (m.is_not(f, x) && is_uninterp_const(x)) diff --git a/src/ast/simplifiers/extract_eqs.h b/src/ast/simplifiers/extract_eqs.h index f38829dfc..724425d6a 100644 --- a/src/ast/simplifiers/extract_eqs.h +++ b/src/ast/simplifiers/extract_eqs.h @@ -18,6 +18,7 @@ Author: #pragma once +#include "ast/ast_pp.h" #include "ast/simplifiers/dependent_expr_state.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_substitution.h" @@ -42,8 +43,13 @@ namespace euf { virtual void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) = 0; virtual void pre_process(dependent_expr_state& fmls) {} virtual void updt_params(params_ref const& p) {} + virtual void set_allow_booleans(bool f) {} }; void register_extract_eqs(ast_manager& m, scoped_ptr_vector& ex); } + +inline std::ostream& operator<<(std::ostream& out, euf::dependent_eq const& eq) { + return out << mk_pp(eq.var, eq.term.m()) << " = " << eq.term << "\n"; +} diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index a8e75bfa3..373a500bf 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -26,7 +26,6 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorm_active) continue; @@ -69,6 +68,7 @@ model_converter_ref model_reconstruction_trail::get_model_converter() { // substituted variables by their terms. // + scoped_ptr rp = mk_default_expr_replacer(m, false); expr_substitution subst(m, true, false); rp->set_substitution(&subst); diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index c11505726..37836db27 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -57,7 +57,7 @@ namespace euf { if (!contains_v(f)) return true; signed_expressions conjuncts; - if (contains_conjunctively(f, sign, e, conjuncts)) + if (contains_conjunctively(f, sign, e, conjuncts)) return true; if (recursion_depth > 3) return false; @@ -67,9 +67,9 @@ namespace euf { /* * Every disjunction in f that contains v also contains the equation e. */ - bool solve_context_eqs::is_disjunctively_safe(unsigned recursion_depth, expr* f, bool sign, expr* e) { + bool solve_context_eqs::is_disjunctively_safe(unsigned recursion_depth, expr* f0, bool sign, expr* e) { signed_expressions todo; - todo.push_back({sign, f}); + todo.push_back({sign, f0}); while (!todo.empty()) { auto [s, f] = todo.back(); todo.pop_back(); @@ -93,11 +93,21 @@ namespace euf { todo.push_back({s, arg}); else if (m.is_not(f, f)) todo.push_back({!s, f}); + else if (!is_conjunction(s, f)) + return false; else if (!is_safe_eq(recursion_depth + 1, f, s, e)) return false; } return true; } + + bool solve_context_eqs::is_conjunction(bool sign, expr* f) const { + if (!sign && m.is_and(f)) + return true; + if (sign && m.is_or(f)) + return true; + return false; + } /** * Determine whether some conjunction in f contains e. @@ -140,29 +150,43 @@ namespace euf { for (unsigned i = m_solve_eqs.m_qhead; i < m_fmls.size(); ++i) collect_nested_equalities(m_fmls[i], visited, eqs); + std::stable_sort(eqs.begin(), eqs.end(), [&](dependent_eq const& e1, dependent_eq const& e2) { + return e1.var->get_id() < e2.var->get_id(); }); unsigned j = 0; + expr* last_var = nullptr; for (auto const& eq : eqs) { - - m_contains_v.reset(); - // first check if v is in term. If it is, then the substitution candidate is unsafe - m_todo.push_back(eq.term); - mark_occurs(m_todo, eq.var, m_contains_v); - SASSERT(m_todo.empty()); - if (m_contains_v.is_marked(eq.term)) + SASSERT(!m.is_bool(eq.var)); + + if (eq.var != last_var) { + + m_contains_v.reset(); + + // first check if v is in term. If it is, then the substitution candidate is unsafe + m_todo.push_back(eq.term); + mark_occurs(m_todo, eq.var, m_contains_v); + SASSERT(m_todo.empty()); + last_var = eq.var; + if (m_contains_v.is_marked(eq.term)) + continue; + + // then mark occurrences + for (unsigned i = 0; i < m_fmls.size(); ++i) + m_todo.push_back(m_fmls[i].fml()); + mark_occurs(m_todo, eq.var, m_contains_v); + SASSERT(m_todo.empty()); + } + else if (m_contains_v.is_marked(eq.term)) continue; - // then mark occurrences - for (unsigned i = 0; i < m_fmls.size(); ++i) - m_todo.push_back(m_fmls[i].fml()); - mark_occurs(m_todo, eq.var, m_contains_v); - SASSERT(m_todo.empty()); - // subject to occurrences, check if equality is safe - if (is_safe_eq(eq.orig)) + if (is_safe_eq(eq.orig)) eqs[j++] = eq; } eqs.shrink(j); + TRACE("solve_eqs", + for (auto const& eq : eqs) + tout << eq << "\n"); } void solve_context_eqs::collect_nested_equalities(dependent_expr const& df, expr_mark& visited, dep_eq_vector& eqs) { @@ -204,8 +228,11 @@ namespace euf { else if (m.is_not(f, f)) todo.push_back({ !s, depth, f }); else if (!s && 1 == depth % 2) { - for (extract_eq* ex : m_solve_eqs.m_extract_plugins) + for (extract_eq* ex : m_solve_eqs.m_extract_plugins) { + ex->set_allow_booleans(false); ex->get_eqs(dependent_expr(m, f, df.dep()), eqs); + ex->set_allow_booleans(true); + } } } } diff --git a/src/ast/simplifiers/solve_context_eqs.h b/src/ast/simplifiers/solve_context_eqs.h index fb330b57b..8332d3a73 100644 --- a/src/ast/simplifiers/solve_context_eqs.h +++ b/src/ast/simplifiers/solve_context_eqs.h @@ -43,6 +43,7 @@ namespace euf { bool is_safe_eq(expr* f, expr* e) { return is_safe_eq(0, f, false, e); } bool is_disjunctively_safe(unsigned recursion_depth, expr* f, bool sign, expr* e); bool contains_conjunctively(expr* f, bool sign, expr* e, signed_expressions& conjuncts); + bool is_conjunction(bool sign, expr* f) const; void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs); diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index f364c31e1..96b0427f6 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -118,6 +118,8 @@ namespace euf { } void solve_eqs::normalize() { + if (m_subst_ids.empty()) + return; scoped_ptr rp = mk_default_expr_replacer(m, false); rp->set_substitution(m_subst.get()); @@ -152,15 +154,18 @@ namespace euf { void solve_eqs::apply_subst(vector& old_fmls) { if (!m.inc()) return; + if (m_subst_ids.empty()) + return; + scoped_ptr rp = mk_default_expr_replacer(m, false); rp->set_substitution(m_subst.get()); for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) { auto [f, d] = m_fmls[i](); auto [new_f, new_dep] = rp->replace_with_dep(f); + m_rewriter(new_f); if (new_f == f) continue; - m_rewriter(new_f); new_dep = m.mk_join(d, new_dep); old_fmls.push_back(m_fmls[i]); m_fmls.update(i, dependent_expr(m, new_f, new_dep)); @@ -185,14 +190,13 @@ namespace euf { normalize(); apply_subst(old_fmls); ++count; + save_subst({}); } while (!m_subst_ids.empty() && count < 20 && m.inc()); if (!m.inc()) return; - save_subst({}); - if (m_config.m_context_solve) { old_fmls.reset(); m_subst_ids.reset(); @@ -211,7 +215,7 @@ namespace euf { void solve_eqs::save_subst(vector const& old_fmls) { if (!m_subst->empty()) - m_fmls.model_trail().push(m_subst.detach(), old_fmls); + m_fmls.model_trail().push(m_subst.detach(), old_fmls); } void solve_eqs::filter_unsafe_vars() { @@ -222,11 +226,10 @@ namespace euf { m_unsafe_vars.mark(term); } - - solve_eqs::solve_eqs(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_rewriter(m) { register_extract_eqs(m, m_extract_plugins); + m_rewriter.set_flat_and_or(false); } void solve_eqs::updt_params(params_ref const& p) { diff --git a/src/nlsat/tactic/qfnra_nlsat_tactic.cpp b/src/nlsat/tactic/qfnra_nlsat_tactic.cpp index fc812c4f5..e41347c2b 100644 --- a/src/nlsat/tactic/qfnra_nlsat_tactic.cpp +++ b/src/nlsat/tactic/qfnra_nlsat_tactic.cpp @@ -27,6 +27,7 @@ Notes: #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/elim_term_ite_tactic.h" tactic * mk_qfnra_nlsat_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 5895643bd..66a391cb8 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -31,6 +31,7 @@ Notes: #include "tactic/tactic.h" #include "tactic/arith/lia2card_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" diff --git a/src/tactic/core/solve_eqs2_tactic.h b/src/tactic/core/solve_eqs2_tactic.h index 91b3875ae..fa095aad0 100644 --- a/src/tactic/core/solve_eqs2_tactic.h +++ b/src/tactic/core/solve_eqs2_tactic.h @@ -29,13 +29,19 @@ public: } }; -inline tactic * mk_solve_eqs2_tactic(ast_manager& m, params_ref const& p) { - return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs2_tactic_factory), "solve-eqs2"); +inline tactic * mk_solve_eqs2_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs2_tactic_factory), "solve-eqs"); } +#if 1 +inline tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()) { + return mk_solve_eqs2_tactic(m, p); +} +#endif + /* - ADD_TACTIC("solve-eqs2", "solve for variables.", "mk_solve_eqs2_tactic(m, p)") + ADD_TACTIC("solve-eqs", "solve for variables.", "mk_solve_eqs2_tactic(m, p)") */ diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index fdba65b3f..d445b68ac 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -997,6 +997,8 @@ class solve_eqs_tactic : public tactic { // void operator()(goal_ref const & g, goal_ref_buffer & result) { model_converter_ref mc; + std::function coll = [&](statistics& st) { collect_statistics(st); }; + statistics_report sreport(coll); tactic_report report("solve_eqs", *g); TRACE("goal", g->display(tout);); m_produce_models = g->models_enabled(); @@ -1042,7 +1044,6 @@ class solve_eqs_tactic : public tactic { result.push_back(g.get()); - IF_VERBOSE(10, statistics st; collect_statistics(st); st.display_smt2(verbose_stream())); } }; @@ -1103,6 +1104,6 @@ public: }; -tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p) { +tactic * mk_solve_eqs1_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(solve_eqs_tactic, m, p, mk_expr_simp_replacer(m, p), true)); } diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index d65a33046..4b26254a0 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -22,10 +22,16 @@ Revision History: class ast_manager; class tactic; -tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()); +tactic * mk_solve_eqs1_tactic(ast_manager & m, params_ref const & p = params_ref()); + +#if 0 +inline tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()) { + return mk_solve_eqs1_tactic(m, p); +} +#endif /* - ADD_TACTIC("solve-eqs", "eliminate variables by solving equations.", "mk_solve_eqs_tactic(m, p)") + ADD_TACTIC("solve-eqs1", "eliminate variables by solving equations.", "mk_solve_eqs1_tactic(m, p)") */ diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 01e135e8a..24f12aeae 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -22,16 +22,19 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { ast_manager& m; params_ref m_params; std::string m_name; - ref m_factory; - scoped_ptr m_simp; - trail_stack m_trail; - scoped_ptr m_model_trail; + trail_stack m_trail; goal_ref m_goal; dependent_expr m_dep; + statistics m_st; + ref m_factory; + scoped_ptr m_simp; + scoped_ptr m_model_trail; void init() { - if (!m_simp) + if (!m_simp) { m_simp = m_factory->mk(m, m_params, *this); + m_st.reset(); + } if (!m_model_trail) m_model_trail = alloc(model_reconstruction_trail, m, m_trail); } @@ -43,7 +46,7 @@ public: m_params(p), m_name(name), m_factory(f), - m_simp(f->mk(m, p, *this)), + m_simp(nullptr), m_dep(m, m.mk_true(), nullptr) {} @@ -86,30 +89,34 @@ public: if (in->proofs_enabled()) throw tactic_exception("tactic does not support low level proofs"); init(); + statistics_report sreport(*this); tactic_report report(name(), *in); m_goal = in.get(); m_simp->reduce(); m_goal->inc_depth(); if (in->models_enabled()) - in->set(m_model_trail->get_model_converter().get()); + in->add(m_model_trail->get_model_converter().get()); result.push_back(in.get()); - - statistics st; - collect_statistics(st); - IF_VERBOSE(10, st.display_smt2(verbose_stream())); } void cleanup() override { + if (m_simp) + m_simp->collect_statistics(m_st); + m_simp = nullptr; + m_model_trail = nullptr; } void collect_statistics(statistics & st) const override { - if (m_simp) + if (m_simp) m_simp->collect_statistics(st); + else + st.copy(m_st); } void reset_statistics() override { if (m_simp) m_simp->reset_statistics(); + m_st.reset(); } }; diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index e631c23e9..a09da60a9 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -18,6 +18,7 @@ Notes: --*/ #include "ast/normal_forms/nnf.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/core/simplify_tactic.h" diff --git a/src/tactic/smtlogics/qfaufbv_tactic.cpp b/src/tactic/smtlogics/qfaufbv_tactic.cpp index acad15fd6..6d44addf0 100644 --- a/src/tactic/smtlogics/qfaufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfaufbv_tactic.cpp @@ -17,6 +17,7 @@ Notes: --*/ #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" diff --git a/src/tactic/smtlogics/qfauflia_tactic.cpp b/src/tactic/smtlogics/qfauflia_tactic.cpp index 2f1879d58..9ca6b70ef 100644 --- a/src/tactic/smtlogics/qfauflia_tactic.cpp +++ b/src/tactic/smtlogics/qfauflia_tactic.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/smtlogics/smt_tactic.h" diff --git a/src/tactic/smtlogics/qfbv_tactic.cpp b/src/tactic/smtlogics/qfbv_tactic.cpp index 1366b701e..90df4fe42 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -20,6 +20,7 @@ Notes: #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/bv/bv1_blaster_tactic.h" @@ -39,6 +40,9 @@ static tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p) { // conservative gaussian elimination. solve_eq_p.set_uint("solve_eqs_max_occs", 2); + params_ref flat_and_or_p = p; + flat_and_or_p.set_bool("flat_and_or", false); + params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); @@ -47,15 +51,17 @@ static tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p) { simp2_p.set_uint("local_ctx_limit", 10000000); simp2_p.set_bool("flat", true); // required by som simp2_p.set_bool("hoist_mul", false); // required by som + simp2_p.set_bool("flat_and_or", false); params_ref hoist_p; hoist_p.set_bool("hoist_mul", true); hoist_p.set_bool("som", false); + hoist_p.set_bool("flat_and_or", false); return and_then( - mk_simplify_tactic(m), - mk_propagate_values_tactic(m), + using_params(mk_simplify_tactic(m), flat_and_or_p), + using_params(mk_propagate_values_tactic(m), flat_and_or_p), using_params(mk_solve_eqs_tactic(m), solve_eq_p), mk_elim_uncnstr_tactic(m), if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), @@ -87,6 +93,7 @@ static tactic * mk_qfbv_tactic(ast_manager& m, params_ref const & p, tactic* sat params_ref local_ctx_p = p; local_ctx_p.set_bool("local_ctx", true); local_ctx_p.set_bool("flat", false); + local_ctx_p.set_bool("flat_and_or", false); params_ref solver_p; solver_p.set_bool("preprocess", false); // preprocessor of smt::context is not needed. diff --git a/src/tactic/smtlogics/qfidl_tactic.cpp b/src/tactic/smtlogics/qfidl_tactic.cpp index c86789ed0..5c1ba5f44 100644 --- a/src/tactic/smtlogics/qfidl_tactic.cpp +++ b/src/tactic/smtlogics/qfidl_tactic.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/arith/normalize_bounds_tactic.h" #include "tactic/arith/fix_dl_var_tactic.h" diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index b8ebbd8a9..d116414ea 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -22,6 +22,7 @@ Notes: #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/arith/normalize_bounds_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/arith/add_bounds_tactic.h" #include "tactic/arith/pb2bv_tactic.h" diff --git a/src/tactic/smtlogics/qfuf_tactic.cpp b/src/tactic/smtlogics/qfuf_tactic.cpp index 609107a48..d9f723d67 100644 --- a/src/tactic/smtlogics/qfuf_tactic.cpp +++ b/src/tactic/smtlogics/qfuf_tactic.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/core/simplify_tactic.h" #include "tactic/core/symmetry_reduce_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/smtlogics/smt_tactic.h" diff --git a/src/tactic/smtlogics/qfufbv_tactic.cpp b/src/tactic/smtlogics/qfufbv_tactic.cpp index d93a17ce1..98e09b56a 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/bv/bv_size_reduction_tactic.h" @@ -136,22 +137,23 @@ private: }; static tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { - params_ref simp2_p = p; + params_ref simp2_p = p, flat_and_or_p = p; + flat_and_or_p.set_bool("flat_and_or", false); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); - simp2_p.set_bool("ite_extra_rules", true); simp2_p.set_bool("mul2concat", true); + simp2_p.set_bool("flat_and_or", false); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 32); ctx_simp_p.set_uint("max_steps", 5000000); return and_then( - mk_simplify_tactic(m), - mk_propagate_values_tactic(m), + using_params(mk_simplify_tactic(m), flat_and_or_p), + using_params(mk_propagate_values_tactic(m), flat_and_or_p), if_no_proofs(if_no_unsat_cores(mk_bv_bound_chk_tactic(m))), //using_params(mk_ctx_simplify_tactic(m_m), ctx_simp_p), mk_solve_eqs_tactic(m), @@ -163,8 +165,10 @@ static tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { } static tactic * mk_qfufbv_preamble(ast_manager & m, params_ref const & p) { - return and_then(mk_simplify_tactic(m), - mk_propagate_values_tactic(m), + params_ref simp2_p = p, flat_and_or_p = p; + flat_and_or_p.set_bool("flat_and_or", false); + return and_then(using_params(mk_simplify_tactic(m), flat_and_or_p), + using_params(mk_propagate_values_tactic(m), flat_and_or_p), mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))), diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index daf020a14..3bf6b658d 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -20,6 +20,7 @@ Revision History: #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "qe/lite/qe_lite.h" #include "qe/qsat.h" diff --git a/src/tactic/ufbv/ufbv_tactic.cpp b/src/tactic/ufbv/ufbv_tactic.cpp index e8495c013..728e6397d 100644 --- a/src/tactic/ufbv/ufbv_tactic.cpp +++ b/src/tactic/ufbv/ufbv_tactic.cpp @@ -20,6 +20,7 @@ Notes: #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" +#include "tactic/core/solve_eqs2_tactic.h" #include "tactic/core/distribute_forall_tactic.h" #include "tactic/core/der_tactic.h" #include "tactic/core/reduce_args_tactic.h" From 9ef78fcfa7b6163b18827a208dd2b0a1cc3210b0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 13:57:58 -0800 Subject: [PATCH 49/57] revert new solve-eqs Signed-off-by: Nikolaj Bjorner --- src/tactic/core/solve_eqs2_tactic.h | 2 +- src/tactic/core/solve_eqs_tactic.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/core/solve_eqs2_tactic.h b/src/tactic/core/solve_eqs2_tactic.h index fa095aad0..34c0befa4 100644 --- a/src/tactic/core/solve_eqs2_tactic.h +++ b/src/tactic/core/solve_eqs2_tactic.h @@ -33,7 +33,7 @@ inline tactic * mk_solve_eqs2_tactic(ast_manager& m, params_ref const& p = param return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs2_tactic_factory), "solve-eqs"); } -#if 1 +#if 0 inline tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()) { return mk_solve_eqs2_tactic(m, p); } diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 4b26254a0..29c11a9c3 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -24,7 +24,7 @@ class tactic; tactic * mk_solve_eqs1_tactic(ast_manager & m, params_ref const & p = params_ref()); -#if 0 +#if 1 inline tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()) { return mk_solve_eqs1_tactic(m, p); } From 3faca52c400f090062b69f8d6218a2cc5129d3ef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 14:17:17 -0800 Subject: [PATCH 50/57] re-enable new solve_eqs with bug fixes --- src/ast/simplifiers/extract_eqs.cpp | 2 ++ src/tactic/core/solve_eqs2_tactic.h | 4 ++-- src/tactic/core/solve_eqs_tactic.h | 4 ++-- src/tactic/dependent_expr_state_tactic.h | 6 +++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 775eb4d22..d65f9b167 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -192,6 +192,8 @@ namespace euf { ++i; if (!is_uninterp_const(arg)) continue; + if (!a.is_real(arg)) + continue; unsigned j = 0; bool nonzero = true; for (expr* arg2 : *to_app(x)) { diff --git a/src/tactic/core/solve_eqs2_tactic.h b/src/tactic/core/solve_eqs2_tactic.h index 34c0befa4..7d13b571e 100644 --- a/src/tactic/core/solve_eqs2_tactic.h +++ b/src/tactic/core/solve_eqs2_tactic.h @@ -33,7 +33,7 @@ inline tactic * mk_solve_eqs2_tactic(ast_manager& m, params_ref const& p = param return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs2_tactic_factory), "solve-eqs"); } -#if 0 +#if 1 inline tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()) { return mk_solve_eqs2_tactic(m, p); } @@ -41,7 +41,7 @@ inline tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = para /* - ADD_TACTIC("solve-eqs", "solve for variables.", "mk_solve_eqs2_tactic(m, p)") + ADD_TACTIC("solve-eqs2", "solve for variables.", "mk_solve_eqs2_tactic(m, p)") */ diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 29c11a9c3..7b97172a3 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -24,14 +24,14 @@ class tactic; tactic * mk_solve_eqs1_tactic(ast_manager & m, params_ref const & p = params_ref()); -#if 1 +#if 0 inline tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref()) { return mk_solve_eqs1_tactic(m, p); } #endif /* - ADD_TACTIC("solve-eqs1", "eliminate variables by solving equations.", "mk_solve_eqs1_tactic(m, p)") + ADD_TACTIC("solve-eqs", "eliminate variables by solving equations.", "mk_solve_eqs1_tactic(m, p)") */ diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 24f12aeae..4b89028f3 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -86,13 +86,13 @@ public: void operator()(goal_ref const & in, goal_ref_buffer & result) override { - if (in->proofs_enabled()) - throw tactic_exception("tactic does not support low level proofs"); init(); statistics_report sreport(*this); tactic_report report(name(), *in); m_goal = in.get(); - m_simp->reduce(); + if (!in->proofs_enabled()) + m_simp->reduce(); + m_goal->elim_true(); m_goal->inc_depth(); if (in->models_enabled()) in->add(m_model_trail->get_model_converter().get()); From 823cd23ecc7f8b86079486a4c11c82badc7b5e73 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 15:37:56 -0800 Subject: [PATCH 51/57] building x64 windows tests during ci is too slow, skipping tests Signed-off-by: Nikolaj Bjorner --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f338a5d98..0f2fee6a2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -195,14 +195,14 @@ jobs: setupCmd2: '' setupCmd3: '' bindings: '$(cmakePy)' - runTests: 'False' + runTests: 'True' x64: arch: 'x64' setupCmd1: 'julia -e "using Pkg; Pkg.add(PackageSpec(name=\"libcxxwrap_julia_jll\", version=\"0.7.0\"))"' setupCmd2: 'julia -e "using libcxxwrap_julia_jll; print(dirname(libcxxwrap_julia_jll.libcxxwrap_julia_path))" > tmp.env' setupCmd3: 'set /P JlCxxDir= Date: Tue, 8 Nov 2022 15:56:10 -0800 Subject: [PATCH 52/57] cleanup state to clear model trail during calls. --- src/tactic/dependent_expr_state_tactic.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 4b89028f3..56e27ee9a 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -96,7 +96,8 @@ public: m_goal->inc_depth(); if (in->models_enabled()) in->add(m_model_trail->get_model_converter().get()); - result.push_back(in.get()); + result.push_back(in.get()); + cleanup(); } void cleanup() override { From ff68df34510dc2e09030784e9e179f8ad8955131 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 16:10:50 -0800 Subject: [PATCH 53/57] update output of z3 doc --- src/api/python/z3/z3.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index bbe212453..05df8186e 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -10079,7 +10079,7 @@ def FPs(names, fpsort, ctx=None): >>> x.ebits() 8 >>> fpMul(RNE(), fpAdd(RNE(), x, y), z) - fpMul(RNE(), fpAdd(RNE(), x, y), z) + x + y * z """ ctx = _get_ctx(ctx) if isinstance(names, str): @@ -10186,9 +10186,9 @@ def fpAdd(rm, a, b, ctx=None): >>> x = FP('x', s) >>> y = FP('y', s) >>> fpAdd(rm, x, y) - fpAdd(RNE(), x, y) - >>> fpAdd(RTZ(), x, y) # default rounding mode is RTZ x + y + >>> fpAdd(RTZ(), x, y) # default rounding mode is RTZ + fpAdd(RTZ(), x, y) >>> fpAdd(rm, x, y).sort() FPSort(8, 24) """ @@ -10203,7 +10203,7 @@ def fpSub(rm, a, b, ctx=None): >>> x = FP('x', s) >>> y = FP('y', s) >>> fpSub(rm, x, y) - fpSub(RNE(), x, y) + x - y >>> fpSub(rm, x, y).sort() FPSort(8, 24) """ @@ -10218,7 +10218,7 @@ def fpMul(rm, a, b, ctx=None): >>> x = FP('x', s) >>> y = FP('y', s) >>> fpMul(rm, x, y) - fpMul(RNE(), x, y) + x * y >>> fpMul(rm, x, y).sort() FPSort(8, 24) """ @@ -10233,7 +10233,7 @@ def fpDiv(rm, a, b, ctx=None): >>> x = FP('x', s) >>> y = FP('y', s) >>> fpDiv(rm, x, y) - fpDiv(RNE(), x, y) + x / y >>> fpDiv(rm, x, y).sort() FPSort(8, 24) """ From 4d86d739429e11faf4b8ec93f53c4c4ab7bd6b16 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 17:15:59 -0800 Subject: [PATCH 54/57] disable also tests for Windows x86, does not work with CI pipeline --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0f2fee6a2..382c2efc9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -195,7 +195,7 @@ jobs: setupCmd2: '' setupCmd3: '' bindings: '$(cmakePy)' - runTests: 'True' + runTests: 'False' x64: arch: 'x64' setupCmd1: 'julia -e "using Pkg; Pkg.add(PackageSpec(name=\"libcxxwrap_julia_jll\", version=\"0.7.0\"))"' From 9a656772b437e02ede9bc1e9850c112a325cb4cd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 18:37:16 -0800 Subject: [PATCH 55/57] fix #6446 --- src/muz/fp/datalog_parser.cpp | 51 ++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 030d88d71..d748dca63 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -286,9 +286,8 @@ public: dtoken read_num() { - while(isdigit(m_curr_char)) { + while (isdigit(m_curr_char)) save_and_next(); - } return TK_NUM; } @@ -781,15 +780,29 @@ protected: symbol td1(td); expr_ref v1(m), v2(m); sort* s = nullptr; - dtoken tok2 = m_lexer->next_token(); - if (tok2 != TK_NEQ && tok2 != TK_GT && tok2 != TK_LT && tok2 != TK_EQ) { - return unexpected(tok2, "built-in infix operator"); + uint64_t num1(0), num3(0); + if (tok1 == TK_NUM) { + char const* data = m_lexer->get_token_data(); + rational num(data); + if (!num.is_uint64()) + return unexpected(tok1, "integer expected"); + num1 = num.get_uint64(); } + dtoken tok2 = m_lexer->next_token(); + if (tok2 != TK_NEQ && tok2 != TK_GT && tok2 != TK_LT && tok2 != TK_EQ) + return unexpected(tok2, "built-in infix operator"); dtoken tok3 = m_lexer->next_token(); td = m_lexer->get_token_data(); - if (tok3 != TK_STRING && tok3 != TK_NUM && !(tok3 == TK_ID && m_vars.contains(td))) { + if (tok3 != TK_STRING && tok3 != TK_NUM && !(tok3 == TK_ID && m_vars.contains(td))) return unexpected(tok3, "identifier"); + if (tok3 == TK_NUM) { + char const* data = m_lexer->get_token_data(); + rational num(data); + if (!num.is_uint64()) + return unexpected(tok1, "integer expected"); + num3 = num.get_uint64(); } + symbol td2(td); if (tok1 == TK_ID) { @@ -805,18 +818,21 @@ protected: if (!v1 && !v2) { return unexpected(tok3, "at least one argument should be a variable"); } - if (v1) { + if (v1) s = v1->get_sort(); - } - else { + else s = v2->get_sort(); - } - if (!v1) { + + if (tok1 == TK_NUM) + v1 = mk_symbol_const(num1, s); + + if (tok3 == TK_NUM) + v2 = mk_symbol_const(num3, s); + + if (!v1) v1 = mk_const(td1, s); - } - if (!v2) { + if (!v2) v2 = mk_const(td2, s); - } switch(tok2) { case TK_EQ: @@ -1126,8 +1142,11 @@ protected: if (m_arith.is_int(s)) return m_arith.mk_numeral(rational(el, rational::ui64()), s); else if (m_decl_util.try_get_size(s, sz)) { - if (el >= sz) - throw default_exception("numeric value out of bounds of domain"); + if (el >= sz) { + std::ostringstream ous; + ous << "numeric value " << el << " is out of bounds of domain size " << sz; + throw default_exception(ous.str()); + } return m_decl_util.mk_numeral(el, s); } else { From 8da13ae24a2b8ab9948fb413f2d8510691ee7354 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Nov 2022 18:37:30 -0800 Subject: [PATCH 56/57] add statistics to verbose output of asserted formulas --- src/solver/assertions/asserted_formulas.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/assertions/asserted_formulas.cpp b/src/solver/assertions/asserted_formulas.cpp index 90c84e7ee..2b20ebd80 100644 --- a/src/solver/assertions/asserted_formulas.cpp +++ b/src/solver/assertions/asserted_formulas.cpp @@ -306,7 +306,7 @@ void asserted_formulas::reduce() { if (!invoke(m_flatten_clauses)) return; // if (!invoke(m_propagate_values)) return; - IF_VERBOSE(10, verbose_stream() << "(smt.simplifier-done)\n";); + IF_VERBOSE(10, verbose_stream() << "(smt.simplifier-done :num-exprs " << get_total_size() << ")\n";); TRACE("after_reduce", display(tout);); TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); TRACE("macros", m_macro_manager.display(tout);); @@ -327,8 +327,8 @@ unsigned asserted_formulas::get_formulas_last_level() const { bool asserted_formulas::invoke(simplify_fmls& s) { if (!s.should_apply()) return true; - IF_VERBOSE(10, verbose_stream() << "(smt." << s.id() << ")\n";); s(); + IF_VERBOSE(10, verbose_stream() << "(smt." << s.id() << " :num-exprs " << get_total_size() << ")\n";); IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); CASSERT("well_sorted",check_well_sorted()); @@ -514,9 +514,9 @@ void asserted_formulas::simplify_fmls::operator()() { void asserted_formulas::reduce_and_solve() { - IF_VERBOSE(10, verbose_stream() << "(smt.reducing)\n";); flush_cache(); // collect garbage m_reduce_asserted_formulas(); + IF_VERBOSE(10, verbose_stream() << "(smt.reduced " << get_total_size() << ")\n";); } From 15be80c9548f335527a39c011bdd19b03a0fe62d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 9 Nov 2022 09:06:34 -0800 Subject: [PATCH 57/57] remove dependency on hash_compare --- src/test/hashtable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/hashtable.cpp b/src/test/hashtable.cpp index befd0b8e9..fb8042dc7 100644 --- a/src/test/hashtable.cpp +++ b/src/test/hashtable.cpp @@ -26,8 +26,8 @@ Revision History: struct int_hash_proc { unsigned operator()(int x) const { return x * 3; } }; typedef int_hashtable > int_set; -typedef std::unordered_set > > safe_int_set; // typedef safe_int_set int_set; +typedef std::unordered_set safe_int_set; inline bool contains(int_set & h, int i) { // return h.find(i) != h.end();