From 0da0fa2b2761f9cec1a9c1ce9837c14eaa4252e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 29 Oct 2022 13:42:57 -0700 Subject: [PATCH 001/597] #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 ac7e89d5bb0..9e7ac75ce72 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 e97eb4ef7f5..5d9d3c19c8c 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 002/597] 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 bbdedab8668..8da933fb286 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 306ff24933c..46a60815193 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 ab9bc88572f..a655956db19 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 a6086d8a775..223e58677ba 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 1fc97a38cc5..f83173aa7dd 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 4c4b9ecaf03..df55aecd72c 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 b67ef6a2a5d..9ba53609173 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 56dcdf0dfae..eea3a2475a7 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 88c404c7c23..2e53e46209b 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 7c4e4fb734d..4e119a2ae1a 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 dedbf45f0b9..5573212f53e 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 7e88c8c512f..6d91434dba4 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 c3e43bfbe6b..424b20d4e2c 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 003/597] 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 6034d8428d1..c8605b297c2 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 cfc99e4a0b8..e007297ef54 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 78606106575..a599ff5a1be 100644 --- a/src/ast/justified_expr.h +++ b/src/ast/justified_expr.h @@ -33,8 +33,7 @@ class justified_expr { 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 @@ class justified_expr { 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 @@ class justified_expr { ~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 80495853ae1..1964e652832 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 be61047a421..cc18ae176a9 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 2715005519a..e432678a481 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -47,6 +47,7 @@ class th_rewriter { 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 a7b206d5bd0..5a076aa0f62 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -1141,9 +1141,6 @@ class solve_eqs_tactic : public tactic { }; -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 d986b0dbfa4..d65a330462b 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 9aa4c944841..e94e8367973 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -25,6 +25,7 @@ Module Name: #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 @@ class symmetry_reduce_tactic::imp { 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 004/597] 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 60cbdcc5646..9c4cda4a131 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 b9663894473..ad6fbcdb2eb 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 00000000000..650410415d4 --- /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 00000000000..f39fa932e84 --- /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 00000000000..cc0f48cfcc6 --- /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 00000000000..9d6d8625eb4 --- /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 00000000000..5156d11265e --- /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 00000000000..068f6598e4d --- /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 00000000000..9e293ca0e0e --- /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 114e4f84909..b9b4394d98d 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 e9f0927d5bb..6535712653c 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 00000000000..23ed16680ec --- /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 a247c7b2056..e57510d4fd8 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 00000000000..bdd940f175f --- /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 00000000000..36511bbe343 --- /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 00000000000..c6212545991 --- /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 005/597] 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 00000000000..040068e3941 --- /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 006/597] 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 5156d11265e..e67aa56cc7f 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -39,6 +39,7 @@ Module Name: */ 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 007/597] 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 f08558f37b7..9d129f9a74a 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 008/597] 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 068f6598e4d..e5b328d7f77 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 009/597] 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 a655956db19..69e91c745bc 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 5459ab2a46e..26ec801435a 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 eea3a2475a7..f97a08001b3 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 2e53e46209b..b75950f88ea 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 dbe08d96cb2..0a20f47820c 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 424b20d4e2c..5b2d851d309 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 00000000000..1a0d4f5b9f3 --- /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 010/597] 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 650410415d4..d07220fb5ad 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 00000000000..77808c1429e --- /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 00000000000..936816bc452 --- /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 011/597] 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 77808c1429e..a47a05ceed4 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -16,16 +16,69 @@ Module Name: --*/ - +#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 936816bc452..55cad7e67d2 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -20,6 +20,8 @@ Module Name: #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 012/597] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 17465d9aee1..936f977aaa5 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 013/597] 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 78199666244..0c77867d4f0 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -453,6 +453,9 @@ class arith_util : public arith_recognizers { 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 d07220fb5ad..a260dd3b7e8 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 00000000000..99be4268c4e --- /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 00000000000..e6c81bb205b --- /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 a47a05ceed4..d9fbf9664eb 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -27,59 +27,6 @@ Module Name: 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 55cad7e67d2..942498a521e 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -18,29 +18,13 @@ Module Name: #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 014/597] 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 69e91c745bc..a655956db19 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 26ec801435a..60143f91cfc 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 f97a08001b3..14e7e977545 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 b75950f88ea..982a84307d8 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 0a20f47820c..a34d1b7ad95 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 5b2d851d309..424b20d4e2c 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 1a0d4f5b9f3..a11d7bdc673 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(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 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 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 015/597] 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 bbd1c0e8e53..6d4b1b618dd 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -45,6 +45,7 @@ class expr_substitution { 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 e6c81bb205b..29c13602881 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 d9fbf9664eb..38100fcdc00 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -20,7 +20,7 @@ Module Name: #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 942498a521e..62f24413a9b 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -26,6 +26,11 @@ Module Name: 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 5a076aa0f62..0a0525e705a 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 016/597] 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 ad6fbcdb2eb..fd4fa04b53a 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 59dff3ed25d..759ec3c13b7 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 8fc8edecc45..df134f2278b 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 9a713753b85..a1672235635 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 00000000000..c11b0dbe100 --- /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 2886eb6abac..f805e169b45 100644 --- a/src/tactic/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -24,7 +24,7 @@ Module Name: #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 e809fe734c5..c20bfebb34d 100644 --- a/src/tactic/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -19,7 +19,7 @@ Module Name: --*/ #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 5c08da76f65..bba18ecd69c 100644 --- a/src/tactic/model_converter.cpp +++ b/src/ast/converters/model_converter.cpp @@ -16,7 +16,7 @@ Module Name: 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 377ecce6755..335e0d276ec 100644 --- a/src/tactic/model_converter.h +++ b/src/ast/converters/model_converter.h @@ -57,7 +57,7 @@ Module Name: #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 d1905b38333..88358b7c342 100644 --- a/src/tactic/proof_converter.cpp +++ b/src/ast/converters/proof_converter.cpp @@ -16,8 +16,7 @@ Module Name: 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 88152ce5b2c..d977f2563d0 100644 --- a/src/tactic/proof_converter.h +++ b/src/ast/converters/proof_converter.h @@ -20,8 +20,7 @@ Module Name: #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 e67aa56cc7f..34517525f22 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -33,6 +33,7 @@ Module Name: #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 @@ class dependent_expr_simplifier { 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 38100fcdc00..cc3ad5b7bc7 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -23,6 +23,7 @@ Module Name: #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 62f24413a9b..02d20a50e81 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -18,9 +18,9 @@ Module Name: #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 a5c66f78b11..2d90b70c35e 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -47,7 +47,7 @@ Module Name: #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 15b5df0d115..93d2c0d3e15 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -33,7 +33,7 @@ Module Name: #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 eae846835c6..3479fef0de7 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 d0c872c3c20..a1864d3b903 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 c9fa2f6b3e5..6a21a2621e8 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 45b651b568a..0df0269d9b6 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 560202ab3e5..1a58bc92b92 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 e8d7696213e..470901a9653 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 070432e5270..439cb4540af 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 ba85e569a2a..73541b0cd74 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -21,7 +21,7 @@ Module Name: #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 b91b9e5c804..e8b1f40015e 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 8dda0310537..52b975cc2cf 100644 --- a/src/nlsat/tactic/goal2nlsat.h +++ b/src/nlsat/tactic/goal2nlsat.h @@ -24,7 +24,7 @@ Module Name: #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 ba31d74cf56..5895643bd06 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -39,7 +39,7 @@ Module Name: #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 a9340059249..9e61ae92cd6 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -20,7 +20,7 @@ Module Name: #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 bd83f06c87c..84d31ed0f41 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -29,7 +29,7 @@ Module Name: #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 da25e22858a..28448fbe424 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -24,7 +24,7 @@ Module Name: #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 381d244e1bb..2f7502a675b 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 beb0809fb07..f935ad9ffe4 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -22,7 +22,7 @@ Module Name: #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 403cee684c6..97a766df91b 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -37,7 +37,7 @@ Module Name: #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 7614857cb48..899345ad88f 100644 --- a/src/sat/tactic/sat2goal.cpp +++ b/src/sat/tactic/sat2goal.cpp @@ -37,7 +37,7 @@ Module Name: #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 1e1dfcd5e03..8c0b1bf8383 100644 --- a/src/sat/tactic/sat2goal.h +++ b/src/sat/tactic/sat2goal.h @@ -31,7 +31,7 @@ Module Name: #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 5527f12f541..5ef52a54cc4 100644 --- a/src/smt/tactic/smt_tactic_core.cpp +++ b/src/smt/tactic/smt_tactic_core.cpp @@ -26,7 +26,7 @@ Module Name: #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 4b57f043e3e..593c32d831a 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 c46331d0754..4075f39ddd2 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 03a205ca6f0..9cac6b96b90 100644 --- a/src/smt/theory_wmaxsat.h +++ b/src/smt/theory_wmaxsat.h @@ -21,7 +21,7 @@ Module Name: #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 e00b53cc9ef..936f6d3dfa3 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -23,7 +23,7 @@ Module Name: #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 d582ec2db0f..bf05554affc 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -22,7 +22,7 @@ Module Name: #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 81d21f959f4..b8e3dd37a6e 100644 --- a/src/solver/solver2tactic.cpp +++ b/src/solver/solver2tactic.cpp @@ -19,7 +19,7 @@ Module Name: #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 a5b529f6978..4640ee276c4 100644 --- a/src/solver/solver2tactic.h +++ b/src/solver/solver2tactic.h @@ -19,7 +19,7 @@ Module Name: #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 b9b4394d98d..f0910fcc2f3 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 25c5620c21a..4f79a887dd3 100644 --- a/src/tactic/arith/card2bv_tactic.cpp +++ b/src/tactic/arith/card2bv_tactic.cpp @@ -22,7 +22,7 @@ Module Name: #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 e34910e78a5..26c3f9ef528 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 87061b189cf..13479e1bffe 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 97c6f466f58..cb6f6c228dd 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -25,7 +25,7 @@ Module Name: #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 46404ffb0d4..f78e8562168 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 36df89da543..bdcf5795380 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -27,7 +27,7 @@ Module Name: #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 b7ef28f4970..cba791ee16b 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 5560ce7da17..2de873ba2ae 100644 --- a/src/tactic/arith/pb2bv_model_converter.h +++ b/src/tactic/arith/pb2bv_model_converter.h @@ -18,7 +18,7 @@ Module Name: --*/ #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 c418115a9e8..5ff7425ba72 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -29,7 +29,7 @@ Module Name: #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 afcecd7d362..7c69ef12e82 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 251d78e7298..d97f9b80fc6 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 5c26fb2b518..88ff1168312 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -18,7 +18,7 @@ Module Name: --*/ #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 debfdd52623..dae3cd40ed5 100644 --- a/src/tactic/bv/bit_blaster_model_converter.h +++ b/src/tactic/bv/bit_blaster_model_converter.h @@ -18,7 +18,7 @@ Module Name: --*/ #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 788f562d316..286375b6a85 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -24,7 +24,7 @@ Module Name: #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 df5c93a147b..d6733d4a62b 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.h +++ b/src/tactic/bv/bvarray2uf_rewriter.h @@ -20,7 +20,7 @@ Module Name: #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 da86ed663a0..3a4971e0472 100644 --- a/src/tactic/bv/bvarray2uf_tactic.cpp +++ b/src/tactic/bv/bvarray2uf_tactic.cpp @@ -20,7 +20,7 @@ Module Name: #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 650095207e2..190403349d2 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 02ec522c6ff..54f4dc915c3 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 2a0593ade8f..c67443862f7 100644 --- a/src/tactic/core/elim_term_ite_tactic.cpp +++ b/src/tactic/core/elim_term_ite_tactic.cpp @@ -20,7 +20,7 @@ Module Name: #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 c97fa670e81..cb60eb482fc 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -17,7 +17,7 @@ Module Name: --*/ #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 959a1fc18b6..3a5ce8d0a55 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 c3c027fef1c..1784a434d19 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 2c4b80b930e..9f071713504 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -33,7 +33,7 @@ Module Name: --*/ #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 7f0d82f2e86..b2a10fafa08 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -22,7 +22,7 @@ Module Name: #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 df3de821933..ba9b5d752b9 100644 --- a/src/tactic/core/reduce_invertible_tactic.cpp +++ b/src/tactic/core/reduce_invertible_tactic.cpp @@ -29,7 +29,7 @@ Module Name: #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 0a0525e705a..977a0d61487 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 99a69395b38..c29a2f3f254 100644 --- a/src/tactic/core/split_clause_tactic.cpp +++ b/src/tactic/core/split_clause_tactic.cpp @@ -18,6 +18,7 @@ Module Name: --*/ #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 eec05b604d3..e4476548a0c 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -52,7 +52,7 @@ Module Name: #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 474767a98fe..1d86f8c3921 100644 --- a/src/tactic/dependency_converter.h +++ b/src/tactic/dependency_converter.h @@ -22,7 +22,7 @@ Module Name: #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 c6212545991..116baedf720 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -82,6 +82,8 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 ed10f7efb49..7b7ca630e5a 100644 --- a/src/tactic/fd_solver/bounded_int2bv_solver.cpp +++ b/src/tactic/fd_solver/bounded_int2bv_solver.cpp @@ -20,7 +20,7 @@ Module Name: #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 5e05fdf31d0..7ec5243e71a 100644 --- a/src/tactic/fd_solver/enum2bv_solver.cpp +++ b/src/tactic/fd_solver/enum2bv_solver.cpp @@ -26,7 +26,7 @@ Module Name: #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 609ed173d8c..1a5f7d16a50 100644 --- a/src/tactic/fd_solver/pb2bv_solver.cpp +++ b/src/tactic/fd_solver/pb2bv_solver.cpp @@ -22,7 +22,7 @@ Module Name: #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 452a629c445..4debe781acd 100644 --- a/src/tactic/fpa/fpa2bv_model_converter.h +++ b/src/tactic/fpa/fpa2bv_model_converter.h @@ -19,7 +19,7 @@ Module Name: #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 5e7e0b2fedd..b0b8d95f19c 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 00000000000..a17ff0ea1ea --- /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 41e59070ecb..2576ad1f989 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 37bbf55b394..6a877bc58a7 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 bf726beb901..5f290c626bb 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -20,7 +20,7 @@ Module Name: #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 092e416348b..c6090222859 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 0d1062d78f5..5b1ea95875f 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -20,6 +20,7 @@ Module Name: #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 2358abcd1b8..3f45feb3777 100644 --- a/src/tactic/ufbv/macro_finder_tactic.cpp +++ b/src/tactic/ufbv/macro_finder_tactic.cpp @@ -20,7 +20,7 @@ Module Name: #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 b0eb113b8cc..051ee4fed78 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -17,7 +17,7 @@ Module Name: --*/ #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 017/597] 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 00000000000..91b3875ae37 --- /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 018/597] 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 9c4cda4a131..e9f54be0567 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 019/597] 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 99be4268c4e..1e9b576e16f 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -21,12 +21,14 @@ Module Name: #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 29c13602881..00f96f59bcb 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 cc3ad5b7bc7..6b2a27d2d19 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -24,6 +24,7 @@ Module Name: #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 02d20a50e81..49cd90ca296 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 f420ddd6d99..cdc21da979a 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 f0910fcc2f3..ab81ebfe2f1 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 38b4e172e9f..49e43e6335d 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -20,7 +20,7 @@ Module Name: #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 5b7fcbf105a..041e4d1c25c 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 977a0d61487..3e338b57ec1 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 116baedf720..ae3635c779e 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -83,7 +83,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 483be4cca5b..b521095beeb 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -44,7 +44,7 @@ Module Name: #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 020/597] 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 c11b0dbe100..5343c32a262 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 8bec082d313..d0ed94d8b71 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 87a8f7131a5..7f98d1e0cce 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 4a98110eb13..81fe251a390 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 46e0b42bf95..3d5501b3781 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 6d1a69825ac..352c8a24886 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 11bfb8f2364..98acd12f15f 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 ab81ebfe2f1..17f31cacffc 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 021/597] 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 5343c32a262..52895f06486 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 979359a46c3..a0c8b341eba 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 3d5501b3781..4688a67fd1d 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 17f31cacffc..72bbbc30361 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 022/597] 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 a260dd3b7e8..dc7aa6fb6c6 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 34517525f22..32ad59681f6 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -32,8 +32,9 @@ Module Name: #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 00000000000..f99685854f3 --- /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 00000000000..eeaf786d9d7 --- /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 023/597] 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 f99685854f3..2e8eab0e691 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -12,6 +12,7 @@ Module Name: --*/ +#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 024/597] 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 df3059d4ba3..a8f4d74b740 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 ce9de96d4ac..7514055c574 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 dcff35e82ee..8e1279c0a3c 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 4efe79dd3d5..c9d2c779745 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 24bfb3355fe..40e789204f8 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 025/597] 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 6d4b1b618dd..0e285ff7dcb 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -44,8 +44,10 @@ class expr_substitution { 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 4fa83bed013..1007261aef1 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 82982adff4c..50831073bab 100644 --- a/src/ast/rewriter/expr_replacer.h +++ b/src/ast/rewriter/expr_replacer.h @@ -34,9 +34,10 @@ class expr_replacer { 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 9d6d8625eb4..53f9cb9d866 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -68,6 +68,10 @@ class dependent_expr { 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 2e8eab0e691..077443e7efe 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 eeaf786d9d7..8f1ba4381d8 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -24,33 +24,62 @@ Module Name: #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 @@ class model_reconstruction_trail { * 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 6b2a27d2d19..e4361ad97be 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 4f734259001..b8b51c0c658 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 026/597] 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 50831073bab..c1bfabd121a 100644 --- a/src/ast/rewriter/expr_replacer.h +++ b/src/ast/rewriter/expr_replacer.h @@ -38,6 +38,7 @@ class expr_replacer { 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 027/597] 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 8f1ba4381d8..96d27e4c452 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 9d129f9a74a..c3f06d8d3f5 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 028/597] 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 96d27e4c452..4aa8a54fdaa 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 029/597] 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 c1bfabd121a..96418f00b9b 100644 --- a/src/ast/rewriter/expr_replacer.h +++ b/src/ast/rewriter/expr_replacer.h @@ -39,6 +39,7 @@ class expr_replacer { 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 f39fa932e84..75e0a890c8b 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 32ad59681f6..803c5851083 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -34,6 +34,7 @@ Module Name: #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 @@ class dependent_expr_state { 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 e5b328d7f77..e1360fa0ba2 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 077443e7efe..a8e75bfa3a5 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 4aa8a54fdaa..c9b42bc9211 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -33,13 +33,12 @@ Module Name: 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 @@ class model_reconstruction_trail { 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 e4361ad97be..b5b500a96bb 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 49cd90ca296..db7a1323b85 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 ae3635c779e..719a29eeaf1 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 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 c3f06d8d3f5..925e20186f2 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 030/597] 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 925e20186f2..2a037770d8f 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 031/597] 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 | 43 ++--- 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, 415 insertions(+), 116 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 21e7f590611..2bcd9839655 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 15a33ddf5c9..7475a292c3a 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 dc7aa6fb6c6..ef04cc43371 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 53f9cb9d866..f789bf332ff 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -72,6 +72,8 @@ class dependent_expr { 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 803c5851083..6bdd3462697 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -47,12 +47,13 @@ class dependent_expr_state { 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 1e9b576e16f..e77ce9e06d7 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 00f96f59bcb..f38829dfc4f 100644 --- a/src/ast/simplifiers/extract_eqs.h +++ b/src/ast/simplifiers/extract_eqs.h @@ -27,10 +27,11 @@ Module Name: 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 00000000000..766c185351c --- /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 00000000000..b3db7412798 --- /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 b5b500a96bb..31e063119db 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -23,22 +23,29 @@ Module Name: #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 db7a1323b85..35044b37315 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -26,58 +26,51 @@ Module Name: 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; }; + stats m_stats; + config m_config; 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; - + 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 - stats m_stats; - config m_config; - - 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; } 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 3e338b57ec1..cfc4d8eeb89 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 719a29eeaf1..41b32baac1f 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -65,8 +65,8 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 0cbe9e9f801..bc9017726e2 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 2a037770d8f..1210314924c 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 032/597] 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 0e285ff7dcb..8f756e06124 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -59,6 +59,10 @@ class expr_substitution { 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 54e176fc5dc..0790b418df8 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 b724bed8659..99a0f6b9d27 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 @@ class subterms { 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 1964e652832..442bef855db 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 e77ce9e06d7..543030e911f 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 766c185351c..c1150572647 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 b3db7412798..fb330b57b06 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 31e063119db..b611e014481 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -20,6 +20,8 @@ Module Name: #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 35044b37315..8f5988a3842 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 e57510d4fd8..38d2699f0a2 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 cfc4d8eeb89..fdba65b3f9a 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 @@ class solve_eqs_tactic : public tactic { 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 @@ class solve_eqs_tactic : public tactic { } 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 41b32baac1f..01e135e8a62 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -93,6 +93,10 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 033/597] 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 b611e014481..f364c31e1ee 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 034/597] 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 d64589cdecd..6a9d0f81c1d 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 035/597] 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 50b9e5a4da1..7bb732a5d84 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 036/597] 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 d0398543b78..a3595645471 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -22,6 +22,7 @@ Module Name: #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 037/597] 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 bf865b39357..755a3eccd5a 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 8e1279c0a3c..c369e282722 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 038/597] 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 a2bc381e4d8..ad3d3818c85 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 039/597] 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 2483b2ca466..63848418fb8 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 040/597] 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 755a3eccd5a..5584885926e 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 4162752759e..515a51a17d1 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 041/597] 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 32d7646580d..4ca223be63a 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 042/597] 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 85583cbca90..c8d7ddbb74b 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 043/597] 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 b1609ed85fc..8bf6efac5ee 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 0f55ea0fe86..c43120943dd 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -115,6 +115,15 @@ class tactic_report { 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 044/597] 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 4116945f0b5..40ad4604a6b 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -13,8 +13,6 @@ Module Name: Nikolaj Bjorner (nbjorner) 2019-2-4 -Notes: - --*/ @@ -25,19 +23,17 @@ Module Name: 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 2c627ad5957..cc83bfa5622 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -26,7 +26,7 @@ Module Name: #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 045/597] 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 a04436e6393..209f7a13b16 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 046/597] 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 1cb17900fbc..37531d9363f 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -430,6 +430,9 @@ class bv_util : public bv_recognizers { } 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 047/597] 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 442bef855db..632b6c0f606 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -24,7 +24,7 @@ Module Name: 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 a25a0f8a31e..2cec0b2ce82 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -20,6 +20,7 @@ Module Name: #include "ast/ast.h" #include "ast/rewriter/rewriter.h" +#include "ast/rewriter/hoist_rewriter.h" #include "util/params.h" /** @@ -50,6 +51,7 @@ Module Name: */ 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 048/597] 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 | 65 +++++++++++++------ 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 | 29 +++++---- 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 96b69dbc3ad..9604f6d16a3 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 e432678a481..b84164abc61 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -38,6 +38,9 @@ class th_rewriter { 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 543030e911f..775eb4d22f5 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 f38829dfc4f..724425d6afd 100644 --- a/src/ast/simplifiers/extract_eqs.h +++ b/src/ast/simplifiers/extract_eqs.h @@ -18,6 +18,7 @@ Module Name: #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 a8e75bfa3a5..373a500bf21 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 c1150572647..37836db271c 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)) - 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()); + 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; // 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 fb330b57b06..8332d3a73c8 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 f364c31e1ee..96b0427f6af 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 fc812c4f56b..e41347c2bf7 100644 --- a/src/nlsat/tactic/qfnra_nlsat_tactic.cpp +++ b/src/nlsat/tactic/qfnra_nlsat_tactic.cpp @@ -27,6 +27,7 @@ Module Name: #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 5895643bd06..66a391cb804 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -31,6 +31,7 @@ Module Name: #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 91b3875ae37..fa095aad0c3 100644 --- a/src/tactic/core/solve_eqs2_tactic.h +++ b/src/tactic/core/solve_eqs2_tactic.h @@ -29,13 +29,19 @@ class solve_eqs2_tactic_factory : public dependent_expr_simplifier_factory { } }; -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 fdba65b3f9a..d445b68ac17 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 @@ class solve_eqs_tactic : public tactic { }; -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 d65a330462b..4b26254a0f2 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 01e135e8a62..24f12aeaece 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; + trail_stack m_trail; + goal_ref m_goal; + dependent_expr m_dep; + statistics m_st; 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) + 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 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 e631c23e986..a09da60a973 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -18,6 +18,7 @@ Module Name: --*/ #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 acad15fd671..6d44addf028 100644 --- a/src/tactic/smtlogics/qfaufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfaufbv_tactic.cpp @@ -17,6 +17,7 @@ Module Name: --*/ #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 2f1879d5878..9ca6b70ef33 100644 --- a/src/tactic/smtlogics/qfauflia_tactic.cpp +++ b/src/tactic/smtlogics/qfauflia_tactic.cpp @@ -21,6 +21,7 @@ Module Name: #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 1366b701ecb..90df4fe4276 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -20,6 +20,7 @@ Module Name: #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 c86789ed00d..5c1ba5f4430 100644 --- a/src/tactic/smtlogics/qfidl_tactic.cpp +++ b/src/tactic/smtlogics/qfidl_tactic.cpp @@ -21,6 +21,7 @@ Module Name: #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 b8ebbd8a916..d116414eade 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -22,6 +22,7 @@ Module Name: #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 609107a4829..d9f723d67da 100644 --- a/src/tactic/smtlogics/qfuf_tactic.cpp +++ b/src/tactic/smtlogics/qfuf_tactic.cpp @@ -21,6 +21,7 @@ Module Name: #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 d93a17ce155..98e09b56af2 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -21,6 +21,7 @@ Module Name: #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 @@ class qfufbv_ackr_tactic : public tactic { }; 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 daf020a14e5..3bf6b658d7e 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 e8495c01354..728e6397da6 100644 --- a/src/tactic/ufbv/ufbv_tactic.cpp +++ b/src/tactic/ufbv/ufbv_tactic.cpp @@ -20,6 +20,7 @@ Module Name: #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 049/597] 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 fa095aad0c3..34c0befa431 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 4b26254a0f2..29c11a9c341 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 050/597] 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 775eb4d22f5..d65f9b16757 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 34c0befa431..7d13b571e11 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 29c11a9c341..7b97172a3b9 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 24f12aeaece..4b89028f39e 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -86,13 +86,13 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 051/597] 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 f338a5d98dd..0f2fee6a234 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 052/597] 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 4b89028f39e..56e27ee9ac8 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -96,7 +96,8 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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 053/597] 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 bbe21245387..05df8186eed 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 054/597] 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 0f2fee6a234..382c2efc9e5 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 055/597] fix #6446 --- src/muz/fp/datalog_parser.cpp | 49 ++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 030d88d71d1..d748dca63fe 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -286,9 +286,8 @@ class dlexer { dtoken read_num() { - while(isdigit(m_curr_char)) { + while (isdigit(m_curr_char)) save_and_next(); - } return TK_NUM; } @@ -781,15 +780,29 @@ class dparser : public parser { symbol td1(td); expr_ref v1(m), v2(m); sort* s = nullptr; + 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) { + 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 @@ class dparser : public parser { 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 @@ class dparser : public parser { 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 056/597] 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 90c84e7eea9..2b20ebd8068 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 057/597] 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 befd0b8e937..fb8042dc72b 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(); From 689af3b4df2abcc0f45be9521066311e3654c980 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 10 Nov 2022 16:42:09 -0800 Subject: [PATCH 058/597] add comments to elim_unconstr_tactic Signed-off-by: Nikolaj Bjorner --- src/tactic/core/elim_uncnstr_tactic.cpp | 93 +++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index cb60eb482fc..34aa748d97c 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -270,7 +270,36 @@ class elim_uncnstr_tactic : public tactic { } return nullptr; } - + + /** + * if (c, x, x') -> fresh + * x := fresh + * x' := fresh + * + * if (x, x', e) -> fresh + * x := true + * x' := fresh + * + * if (x, t, x') -> fresh + * x := false + * x' := fresh + * + * not x -> fresh + * x := not fresh + * + * x & x' -> fresh + * x := fresh + * x' := true + * + * x or x' -> fresh + * x := fresh + * x' := false + * + * x = t -> fresh + * x := if(fresh, t, diff(t)) + * where diff is a diagnonalization function available in domains of size > 1. + * + */ app * process_basic_app(func_decl * f, unsigned num, expr * const * args) { SASSERT(f->get_family_id() == m().get_basic_family_id()); switch (f->get_decl_kind()) { @@ -434,6 +463,10 @@ class elim_uncnstr_tactic : public tactic { } return nullptr; } + + /** + * similar as for bit-vectors + */ app * process_arith_app(func_decl * f, unsigned num, expr * const * args) { @@ -466,7 +499,7 @@ class elim_uncnstr_tactic : public tactic { add_defs(num, args, r, m_bv_util.mk_numeral(rational(1), s)); return r; } - // c * v (c is even) case + // c * v (c is odd) case unsigned bv_size; rational val; rational inv; @@ -595,7 +628,46 @@ class elim_uncnstr_tactic : public tactic { } return nullptr; } - + + /** + * x + t -> fresh + * x := fresh - t + * + * x * x' * x'' -> fresh + * x := fresh + * x', x'' := 1 + * + * c * x -> fresh, c is odd + * x := fresh*c^-1 + * + * x[sz-1:0] -> fresh + * x := fresh + * + * x[hi:lo] -> fresh + * x := fresh1 ++ fresh ++ fresh2 + * + * x udiv x', x sdiv x' -> fresh + * x' := 1 + * x := fresh + * + * x ++ x' ++ x'' -> fresh + * x := fresh[hi1:lo1] + * x' := fresh[hi2:lo2] + * x'' := fresh[hi3:lo3] + * + * x <= t -> fresh or t == MAX + * x := if(fresh, t, t + 1) + * t <= x -> fresh or t == MIN + * x := if(fresh, t, t - 1) + * + * ~x -> fresh + * x := ~fresh + * + * x | y -> fresh + * x := fresh + * y := 0 + * + */ app * process_bv_app(func_decl * f, unsigned num, expr * const * args) { SASSERT(f->get_family_id() == m_bv_util.get_family_id()); switch (f->get_decl_kind()) { @@ -646,6 +718,15 @@ class elim_uncnstr_tactic : public tactic { return nullptr; } } + + /** + * F[select(x, i)] -> F[fresh] + * x := const(fresh) + + * F[store(x, ..., x')] -> F[fresh] + * x' := select(x, ...) + * x := fresh + */ app * process_array_app(func_decl * f, unsigned num, expr * const * args) { SASSERT(f->get_family_id() == m_ar_util.get_family_id()); @@ -676,7 +757,11 @@ class elim_uncnstr_tactic : public tactic { return nullptr; } } - + + /** + * head(x) -> fresh + * x := cons(fresh, arb) + */ app * process_datatype_app(func_decl * f, unsigned num, expr * const * args) { if (m_dt_util.is_accessor(f)) { SASSERT(num == 1); From efbe0a655414ba8e64c5a0b3c62e02730f622691 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 12 Nov 2022 17:56:45 -0800 Subject: [PATCH 059/597] wip - updated version of elim_uncstr_tactic - remove reduce_invertible. It is subsumed by reduce_uncstr(2) - introduce a simplifier for reduce_unconstrained. It uses reference counting to deal with inefficiency bug of legacy reduce_uncstr. It decomposes theory plugins into expr_inverter. reduce_invertible is a tactic used in most built-in scenarios. It is useful for removing subterms that can be eliminated using "cheap" quantifier elimination. Specifically variables that occur only once can be removed in many cases by computing an expression that represents the effect computing a value for the eliminated occurrence. The theory plugins for variable elimination are very partial and should be augmented by extensions, esp. for the case of bit-vectors where the invertibility conditions are thoroughly documented by Niemetz and Preiner. --- src/ast/arith_decl_plugin.h | 2 + src/ast/array_decl_plugin.h | 4 + src/ast/bv_decl_plugin.h | 8 + src/ast/converters/CMakeLists.txt | 1 + src/ast/converters/expr_inverter.cpp | 791 ++++++++++++++++++ src/ast/converters/expr_inverter.h | 57 ++ src/ast/converters/generic_model_converter.h | 4 + src/ast/for_each_expr.cpp | 13 +- src/ast/for_each_expr.h | 12 +- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/elim_unconstrained.cpp | 297 +++++++ src/ast/simplifiers/elim_unconstrained.h | 84 ++ .../model_reconstruction_trail.cpp | 8 + .../simplifiers/model_reconstruction_trail.h | 13 +- src/tactic/core/CMakeLists.txt | 3 +- src/tactic/core/elim_uncnstr2_tactic.h | 41 + src/tactic/core/elim_uncnstr_tactic.cpp | 7 +- src/tactic/core/reduce_invertible_tactic.cpp | 576 ------------- src/tactic/core/reduce_invertible_tactic.h | 32 - src/tactic/dependent_expr_state_tactic.h | 1 + 20 files changed, 1334 insertions(+), 621 deletions(-) create mode 100644 src/ast/converters/expr_inverter.cpp create mode 100644 src/ast/converters/expr_inverter.h create mode 100644 src/ast/simplifiers/elim_unconstrained.cpp create mode 100644 src/ast/simplifiers/elim_unconstrained.h create mode 100644 src/tactic/core/elim_uncnstr2_tactic.h delete mode 100644 src/tactic/core/reduce_invertible_tactic.cpp delete mode 100644 src/tactic/core/reduce_invertible_tactic.h diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 0c77867d4f0..93d0edf2ff6 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -447,6 +447,8 @@ class arith_util : public arith_recognizers { app * mk_add(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(arith_family_id, OP_ADD, arg1, arg2, arg3); } app * mk_add(expr_ref_vector const& args) const { return mk_add(args.size(), args.data()); } app * mk_add(expr_ref_buffer const& args) const { return mk_add(args.size(), args.data()); } + app * mk_add(ptr_buffer const& args) const { return mk_add(args.size(), args.data()); } + app * mk_add(ptr_vector const& args) const { return mk_add(args.size(), args.data()); } app * mk_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(arith_family_id, OP_SUB, arg1, arg2); } app * mk_sub(unsigned num_args, expr * const * args) const { return m_manager.mk_app(arith_family_id, OP_SUB, num_args, args); } diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 83d54109726..79c6e682e65 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -207,6 +207,10 @@ class array_util : public array_recognizers { return mk_store(args.size(), args.data()); } + app* mk_store(ptr_buffer const& args) const { + return mk_store(args.size(), args.data()); + } + app * mk_select(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args); } diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index 37531d9363f..ee618b42cbb 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -448,9 +448,17 @@ class bv_util : public bv_recognizers { app * mk_bv_srem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); } app * mk_bv_smod(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSMOD, arg1, arg2); } app * mk_bv_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); } + app * mk_bv_add(ptr_buffer const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); } + app * mk_bv_add(ptr_vector const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); } + app * mk_bv_add(expr_ref_vector const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); } + app * mk_bv_add(expr_ref_buffer const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); } app * mk_bv_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); } app * mk_bv_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); } app * mk_bv_mul(unsigned n, expr* const* args) const { return m_manager.mk_app(get_fid(), OP_BMUL, n, args); } + app* mk_bv_mul(ptr_buffer const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); } + app* mk_bv_mul(ptr_vector const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); } + app* mk_bv_mul(expr_ref_vector const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); } + app* mk_bv_mul(expr_ref_buffer const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); } app * mk_bv_udiv(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV, arg1, arg2); } app * mk_bv_udiv_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); } app * mk_bv_udiv0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BUDIV0, arg); } diff --git a/src/ast/converters/CMakeLists.txt b/src/ast/converters/CMakeLists.txt index 52895f06486..64f5060e791 100644 --- a/src/ast/converters/CMakeLists.txt +++ b/src/ast/converters/CMakeLists.txt @@ -1,5 +1,6 @@ z3_add_component(converters SOURCES + expr_inverter.cpp equiv_proof_converter.cpp generic_model_converter.cpp horn_subsume_model_converter.cpp diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp new file mode 100644 index 00000000000..504c04ab0c4 --- /dev/null +++ b/src/ast/converters/expr_inverter.cpp @@ -0,0 +1,791 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + expr_inverter.cpp + +Abstract: + + inverter interface and instance + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-11 + +--*/ + +#include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" +#include "ast/ast_util.h" +#include "ast/arith_decl_plugin.h" +#include "ast/converters/expr_inverter.h" + +class basic_expr_inverter : public iexpr_inverter { +public: + + basic_expr_inverter(ast_manager& m) : iexpr_inverter(m) {} + + /** + * if (c, x, x') -> fresh + * x := fresh + * x' := fresh + * + * if (x, x', e) -> fresh + * x := true + * x' := fresh + * + * if (x, t, x') -> fresh + * x := false + * x' := fresh + * + * not x -> fresh + * x := not fresh + * + * x & x' -> fresh + * x := fresh + * x' := true + * + * x or x' -> fresh + * x := fresh + * x' := false + * + * x = t -> fresh + * x := if(fresh, t, diff(t)) + * where diff is a diagnonalization function available in domains of size > 1. + * + */ + + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) { + SASSERT(f->get_family_id() == m.get_basic_family_id()); + switch (f->get_decl_kind()) { + case OP_ITE: + SASSERT(num == 3); + if (uncnstr(args[1]) && uncnstr(args[2])) { + mk_fresh_uncnstr_var_for(f, r); + add_def(args[1], r); + add_def(args[2], r); + return true; + } + if (uncnstr(args[0]) && uncnstr(args[1])) { + mk_fresh_uncnstr_var_for(f, r); + add_def(args[0], m.mk_true()); + add_def(args[1], r); + return true; + } + if (uncnstr(args[0]) && uncnstr(args[2])) { + mk_fresh_uncnstr_var_for(f, r); + add_def(args[0], m.mk_false()); + add_def(args[2], r); + return true; + } + return false; + case OP_NOT: + SASSERT(num == 1); + if (uncnstr(args[0])) { + mk_fresh_uncnstr_var_for(f, r); + add_def(args[0], m.mk_not(r)); + return true; + } + return false; + case OP_AND: + if (num > 0 && uncnstr(num, args)) { + mk_fresh_uncnstr_var_for(f, r); + add_defs(num, args, r, m.mk_true()); + return true; + } + return false; + case OP_OR: + if (num > 0 && uncnstr(num, args)) { + mk_fresh_uncnstr_var_for(f, r); + add_defs(num, args, r, m.mk_false()); + return true; + } + return false; + case OP_EQ: + SASSERT(num == 2); + // return process_eq(f, args[0], args[1]); + default: + return false; + } + return false; + } + + bool mk_diff(expr* t, expr_ref& r) override { + SASSERT(m.is_bool(t)); + r = mk_not(m, t); + return true; + } +}; + +class arith_expr_inverter : public iexpr_inverter { + arith_util a; +public: + + arith_expr_inverter(ast_manager& m) : iexpr_inverter(m), a(m) {} + + family_id get_fid() const { return a.get_family_id(); } + + bool process_le_ge(func_decl* f, expr* arg1, expr* arg2, bool le, expr_ref& r) { + expr* v; + expr* t; + if (uncnstr(arg1)) { + v = arg1; + t = arg2; + } + else if (uncnstr(arg2)) { + v = arg2; + t = arg1; + le = !le; + } + else + return false; + + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + // v = ite(u, t, t + 1) if le + // v = ite(u, t, t - 1) if !le + add_def(v, m.mk_ite(r, t, a.mk_add(t, a.mk_numeral(rational(le ? 1 : -1), arg1->get_sort())))); + } + return true; + } + + bool process_add(unsigned num, expr* const* args, expr_ref& u) { + if (num == 0) + return false; + unsigned i; + expr* v = nullptr; + for (i = 0; i < num; i++) { + expr* arg = args[i]; + if (uncnstr(arg)) { + v = arg; + break; + } + } + if (v == nullptr) + return false; + mk_fresh_uncnstr_var_for(v->get_sort(), u); + if (!m_mc) + return true; + ptr_buffer new_args; + for (unsigned j = 0; j < num; j++) + if (j != i) + new_args.push_back(args[j]); + + if (new_args.empty()) + add_def(v, u); + else { + expr* rest = a.mk_add(new_args); + add_def(v, a.mk_sub(u, rest)); + } + return true; + } + + bool process_arith_mul(unsigned num, expr* const* args, expr_ref & u) { + if (num == 0) + return false; + sort* s = args[0]->get_sort(); + if (uncnstr(num, args)) { + mk_fresh_uncnstr_var_for(s, u); + if (m_mc) + add_defs(num, args, u, a.mk_numeral(rational(1), s)); + return true; + } + // c * v case for reals + bool is_int; + rational val; + if (num == 2 && uncnstr(args[1]) && a.is_numeral(args[0], val, is_int) && !is_int) { + if (val.is_zero()) + return false; + mk_fresh_uncnstr_var_for(s, u); + if (m_mc) { + val = rational(1) / val; + add_def(args[1], a.mk_mul(a.mk_numeral(val, false), u)); + } + return true; + } + return false; + } + + + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + SASSERT(f->get_family_id() == a.get_family_id()); + switch (f->get_decl_kind()) { + case OP_ADD: + return process_add(num, args, r); + case OP_MUL: + return process_arith_mul(num, args, r); + case OP_LE: + SASSERT(num == 2); + return process_le_ge(f, args[0], args[1], true, r); + case OP_GE: + SASSERT(num == 2); + return process_le_ge(f, args[0], args[1], false, r); + default: + return false; + } + } + + + bool mk_diff(expr* t, expr_ref& r) override { + SASSERT(a.is_int_real(t)); + r = a.mk_add(t, a.mk_numeral(rational(1), a.is_int(t))); + return true; + } +}; + +class bv_expr_inverter : public iexpr_inverter { + bv_util bv; + + bool process_add(unsigned num, expr* const* args, expr_ref& u) { + if (num == 0) + return false; + unsigned i; + expr* v = nullptr; + for (i = 0; i < num; i++) { + expr* arg = args[i]; + if (uncnstr(arg)) { + v = arg; + break; + } + } + if (!v) + return false; + mk_fresh_uncnstr_var_for(v->get_sort(), u); + if (!m_mc) + return true; + ptr_buffer new_args; + for (unsigned j = 0; j < num; j++) + if (j != i) + new_args.push_back(args[j]); + + if (new_args.empty()) + add_def(v, u); + else { + expr* rest = bv.mk_bv_add(new_args); + add_def(v, bv.mk_bv_sub(u, rest)); + } + return true; + } + + bool process_bv_mul(func_decl* f, unsigned num, expr* const* args, expr_ref& r) { + if (num == 0) + return nullptr; + if (uncnstr(num, args)) { + sort* s = args[0]->get_sort(); + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) + add_defs(num, args, r, bv.mk_numeral(rational(1), s)); + return true; + } + // c * v (c is odd) case + unsigned sz; + rational val; + rational inv; + if (num == 2 && + uncnstr(args[1]) && + bv.is_numeral(args[0], val, sz) && + val.mult_inverse(sz, inv)) { + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) + add_def(args[1], bv.mk_bv_mul(bv.mk_numeral(inv, sz), r)); + return true; + } + + // + // x * K -> fresh[hi-sh-1:0] ++ 0...0 + // where sh = parity of K + // then x -> J^-1*fresh + // where J = K >> sh + // Because x * K = fresh * K * J^-1 = fresh * 2^sh = fresh[hi-sh-1:0] ++ 0...0 + // + if (num == 2 && + uncnstr(args[1]) && + bv.is_numeral(args[0], val, sz) && + val.is_pos()) { + unsigned sh = 0; + while (val.is_even()) { + val /= rational(2); + ++sh; + } + mk_fresh_uncnstr_var_for(f, r); + if (sh > 0) + r = bv.mk_concat(bv.mk_extract(sz - sh - 1, 0, r), bv.mk_numeral(0, sh)); + + if (m_mc) { + rational inv_r; + VERIFY(val.mult_inverse(sz, inv_r)); + add_def(args[1], bv.mk_bv_mul(bv.mk_numeral(inv_r, sz), r)); + } + return true; + } + + // + // assume x is unconstrained, we can handle general multiplication as follows: + // x * y -> if y = 0 then y else fresh << parity(y) + // and x -> if y = 0 then y else (y >> parity(y))^-1 + // parity can be defined using a "giant" ite expression. + // + + return false; + } + + + bool process_extract(func_decl* f, expr* arg, expr_ref& r) { + if (!uncnstr(arg)) + return false; + mk_fresh_uncnstr_var_for(f, r); + if (!m_mc) + return true; + unsigned high = bv.get_extract_high(f); + unsigned low = bv.get_extract_low(f); + unsigned bv_size = bv.get_bv_size(arg->get_sort()); + if (bv_size == high - low + 1) + add_def(arg, r); + else { + ptr_buffer args; + if (high < bv_size - 1) + args.push_back(bv.mk_numeral(rational(0), bv_size - high - 1)); + args.push_back(r); + if (low > 0) + args.push_back(bv.mk_numeral(rational(0), low)); + add_def(arg, bv.mk_concat(args.size(), args.data())); + } + return true; + } + + bool process_bv_div(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) { + if (uncnstr(arg1) && uncnstr(arg2)) { + sort* s = arg1->get_sort(); + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(arg1, r); + add_def(arg2, bv.mk_numeral(rational(1), s)); + } + return true; + } + return false; + } + + bool process_concat(func_decl* f, unsigned num, expr* const* args, expr_ref& r) { + if (num == 0) + return false; + if (!uncnstr(num, args)) + return false; + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + unsigned i = num; + unsigned low = 0; + while (i > 0) { + --i; + expr* arg = args[i]; + unsigned sz = bv.get_bv_size(arg); + add_def(arg, bv.mk_extract(low + sz - 1, low, r)); + low += sz; + } + } + return true; + } + + bool process_bv_le(func_decl* f, expr* arg1, expr* arg2, bool is_signed, expr_ref& r) { + if (uncnstr(arg1)) { + // v <= t + expr* v = arg1; + expr* t = arg2; + // v <= t ---> (u or t == MAX) u is fresh + // add definition v = ite(u or t == MAX, t, t+1) + unsigned bv_sz = bv.get_bv_size(arg1); + rational MAX; + if (is_signed) + MAX = rational::power_of_two(bv_sz - 1) - rational(1); + else + MAX = rational::power_of_two(bv_sz) - rational(1); + mk_fresh_uncnstr_var_for(f, r); + r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MAX, bv_sz))); + if (m_mc) + add_def(v, m.mk_ite(r, t, bv.mk_bv_add(t, bv.mk_numeral(rational(1), bv_sz)))); + return true; + } + if (uncnstr(arg2)) { + // v >= t + expr* v = arg2; + expr* t = arg1; + // v >= t ---> (u ot t == MIN) u is fresh + // add definition v = ite(u or t == MIN, t, t-1) + unsigned bv_sz = bv.get_bv_size(arg1); + rational MIN; + if (is_signed) + MIN = -rational::power_of_two(bv_sz - 1); + else + MIN = rational(0); + mk_fresh_uncnstr_var_for(f, r); + r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MIN, bv_sz))); + if (m_mc) + add_def(v, m.mk_ite(r, t, bv.mk_bv_sub(t, bv.mk_numeral(rational(1), bv_sz)))); + return true; + } + return false; + } + + bool process_bvnot(expr* e, expr_ref& r) { + if (!uncnstr(e)) + return false; + mk_fresh_uncnstr_var_for(e->get_sort(), r); + if (m_mc) + add_def(e, bv.mk_bv_not(r)); + return true; + } + + public: + bv_expr_inverter(ast_manager& m) : iexpr_inverter(m), bv(m) {} + + family_id get_fid() const { return bv.get_family_id(); } + + /** + * x + t -> fresh + * x := fresh - t + * + * x * x' * x'' -> fresh + * x := fresh + * x', x'' := 1 + * + * c * x -> fresh, c is odd + * x := fresh*c^-1 + * + * x[sz-1:0] -> fresh + * x := fresh + * + * x[hi:lo] -> fresh + * x := fresh1 ++ fresh ++ fresh2 + * + * x udiv x', x sdiv x' -> fresh + * x' := 1 + * x := fresh + * + * x ++ x' ++ x'' -> fresh + * x := fresh[hi1:lo1] + * x' := fresh[hi2:lo2] + * x'' := fresh[hi3:lo3] + * + * x <= t -> fresh or t == MAX + * x := if(fresh, t, t + 1) + * t <= x -> fresh or t == MIN + * x := if(fresh, t, t - 1) + * + * ~x -> fresh + * x := ~fresh + * + * x | y -> fresh + * x := fresh + * y := 0 + * + */ + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + SASSERT(f->get_family_id() == bv.get_family_id()); + switch (f->get_decl_kind()) { + case OP_BADD: + return process_add(num, args, r); + case OP_BMUL: + return process_bv_mul(f, num, args, r); + case OP_BSDIV: + case OP_BUDIV: + case OP_BSDIV_I: + case OP_BUDIV_I: + SASSERT(num == 2); + return process_bv_div(f, args[0], args[1], r); + case OP_SLEQ: + SASSERT(num == 2); + return process_bv_le(f, args[0], args[1], true, r); + case OP_ULEQ: + SASSERT(num == 2); + return process_bv_le(f, args[0], args[1], false, r); + case OP_CONCAT: + return process_concat(f, num, args, r); + case OP_EXTRACT: + SASSERT(num == 1); + return process_extract(f, args[0], r); + case OP_BNOT: + SASSERT(num == 1); + return process_bvnot(args[0], r); + case OP_BOR: + if (num > 0 && uncnstr(num, args)) { + sort* s = args[0]->get_sort(); + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) + add_defs(num, args, r, bv.mk_numeral(rational(0), s)); + return true; + } + return false; + default: + return false; + } + } + + bool mk_diff(expr* t, expr_ref& r) override { + r = bv.mk_bv_not(t); + return true; + } +}; + + + +/** + * F[select(x, i)] -> F[fresh] + * x := const(fresh) + + * F[store(x, ..., x')] -> F[fresh] + * x' := select(x, ...) + * x := fresh + */ + +class array_expr_inverter : public iexpr_inverter { + array_util a; + iexpr_inverter& inv; +public: + array_expr_inverter(ast_manager& m, iexpr_inverter& s) : iexpr_inverter(m), a(m), inv(s) {} + + family_id get_fid() const { return a.get_family_id(); } + + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + SASSERT(f->get_family_id() == a.get_family_id()); + switch (f->get_decl_kind()) { + case OP_SELECT: + if (uncnstr(args[0])) { + mk_fresh_uncnstr_var_for(f, r); + sort* s = args[0]->get_sort(); + if (m_mc) + add_def(args[0], a.mk_const_array(s, r)); + return true; + } + return false; + case OP_STORE: + if (uncnstr(args[0]) && uncnstr(args[num - 1])) { + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(args[num - 1], m.mk_app(a.get_family_id(), OP_SELECT, num - 1, args)); + add_def(args[0], r); + } + return true; + } + default: + return false; + } + } + + bool mk_diff(expr* t, expr_ref& r) override { + sort* s = t->get_sort(); + SASSERT(a.is_array(s)); + if (m.is_uninterp(get_array_range(s))) + return false; + unsigned arity = get_array_arity(s); + for (unsigned i = 0; i < arity; i++) + if (m.is_uninterp(get_array_domain(s, i))) + return false; + // building + // r = (store t i1 ... in d) + // where i1 ... in are arbitrary values + // and d is a term different from (select t i1 ... in) + expr_ref_vector new_args(m); + new_args.push_back(t); + for (unsigned i = 0; i < arity; i++) + new_args.push_back(m.get_some_value(get_array_domain(s, i))); + expr_ref sel(m); + sel = a.mk_select(new_args); + expr_ref diff_sel(m); + if (!inv.mk_diff(sel, diff_sel)) + return false; + new_args.push_back(diff_sel); + r = a.mk_store(new_args); + return true; + } +}; + + + +class dt_expr_inverter : public iexpr_inverter { + datatype_util dt; +public: + + dt_expr_inverter(ast_manager& m) : iexpr_inverter(m), dt(m) {} + + family_id get_fid() const { return dt.get_family_id(); } + /** + * head(x) -> fresh + * x := cons(fresh, arb) + */ + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + if (dt.is_accessor(f)) { + SASSERT(num == 1); + if (uncnstr(args[0])) { + if (!m_mc) { + mk_fresh_uncnstr_var_for(f, r); + return true; + } + func_decl* c = dt.get_accessor_constructor(f); + for (unsigned i = 0; i < c->get_arity(); i++) + if (!m.is_fully_interp(c->get_domain(i))) + return false; + mk_fresh_uncnstr_var_for(f, r); + ptr_vector const& accs = *dt.get_constructor_accessors(c); + ptr_buffer new_args; + for (unsigned i = 0; i < accs.size(); i++) { + if (accs[i] == f) + new_args.push_back(r); + else + new_args.push_back(m.get_some_value(c->get_domain(i))); + } + add_def(args[0], m.mk_app(c, new_args)); + return true; + } + } + return false; + } + + bool mk_diff(expr* t, expr_ref& r) override { + // In the current implementation, I only handle the case where + // the datatype has a recursive constructor. + sort* s = t->get_sort(); + ptr_vector const& constructors = *dt.get_datatype_constructors(s); + for (func_decl* constructor : constructors) { + unsigned num = constructor->get_arity(); + unsigned target = UINT_MAX; + for (unsigned i = 0; i < num; i++) { + sort* s_arg = constructor->get_domain(i); + if (s == s_arg) { + target = i; + continue; + } + if (m.is_uninterp(s_arg)) + break; + } + if (target == UINT_MAX) + continue; + // use the constructor the distinct term constructor(...,t,...) + ptr_buffer new_args; + for (unsigned i = 0; i < num; i++) { + if (i == target) + new_args.push_back(t); + else + new_args.push_back(m.get_some_value(constructor->get_domain(i))); + } + r = m.mk_app(constructor, new_args); + return true; + } + return false; + } +}; + + + +expr_inverter::~expr_inverter() { + for (auto* v : m_inverters) + dealloc(v); +} + + +bool iexpr_inverter::uncnstr(unsigned num, expr * const * args) const { + for (unsigned i = 0; i < num; i++) + if (!m_is_var(args[i])) + return false; + return true; +} + +/** + \brief Create a fresh variable for abstracting (f args[0] ... args[num-1]) + Return true if it a new variable was created, and false if the variable already existed for this + application. Store the variable in v +*/ +void iexpr_inverter::mk_fresh_uncnstr_var_for(sort * s, expr_ref & v) { + v = m.mk_fresh_const(nullptr, s); + if (m_mc) + m_mc->hide(v); +} + +void iexpr_inverter::add_def(expr * v, expr * def) { + expr_ref _v(v, m); + expr_ref _d(def, m); + if (!m_mc) + return; + SASSERT(uncnstr(v)); + SASSERT(to_app(v)->get_num_args() == 0); + m_mc->add(to_app(v)->get_decl(), def); +} + +void iexpr_inverter::add_defs(unsigned num, expr* const* args, expr* u, expr* identity) { + expr_ref _id(identity, m); + if (!m_mc) + return; + add_def(args[0], u); + for (unsigned i = 1; i < num; i++) + add_def(args[i], identity); +} + + +expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { + auto* a = alloc(arith_expr_inverter, m); + auto* b = alloc(bv_expr_inverter, m); + auto* ar = alloc(array_expr_inverter, m, *this); + auto* dt = alloc(dt_expr_inverter, m); + m_inverters.setx(m.get_basic_family_id(), alloc(basic_expr_inverter, m), nullptr); + m_inverters.setx(a->get_fid(), a, nullptr); + m_inverters.setx(b->get_fid(), b, nullptr); + m_inverters.setx(ar->get_fid(), ar, nullptr); + m_inverters.setx(dt->get_fid(), dt, nullptr); +} + +bool expr_inverter::is_converted(func_decl* f, unsigned num, expr* const* args) { + return false; +} + +bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) { + if (num == 0) + return false; + + for (unsigned i = 0; i < num; i++) + if (!is_ground(args[i])) + return false; + + family_id fid = f->get_family_id(); + if (fid == null_family_id) + return false; + + if (is_converted(f, num, args)) + return false; + + auto* p = m_inverters.get(fid, nullptr); + return p && (*p)(f, num, args, new_expr, side_cond); +} + +bool expr_inverter::mk_diff(expr* t, expr_ref& r) { + sort * s = t->get_sort(); + if (!m.is_fully_interp(s)) + return false; + + // If the interpreted sort has only one element, + // then it is unsound to eliminate the unconstrained variable in the equality + sort_size sz = s->get_num_elements(); + if (sz.is_finite() && sz.size() <= 1) + return false; + + if (!m_mc) { + // easy case, model generation is disabled. + mk_fresh_uncnstr_var_for(s, r); + return true; + } + + family_id fid = s->get_family_id(); + auto* p = m_inverters.get(fid, nullptr); + return p && p->mk_diff(t, r); +} + +void expr_inverter::set_is_var(std::function& is_var) { + for (auto* p : m_inverters) + if (p) + p->set_is_var(is_var); +} + +void expr_inverter::set_model_converter(generic_model_converter* mc) { + for (auto* p : m_inverters) + if (p) + p->set_model_converter(mc); +} \ No newline at end of file diff --git a/src/ast/converters/expr_inverter.h b/src/ast/converters/expr_inverter.h new file mode 100644 index 00000000000..1ca77dbabe4 --- /dev/null +++ b/src/ast/converters/expr_inverter.h @@ -0,0 +1,57 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + expr_inverter.h + +Abstract: + + inverter interface and instance + +Author: + + Nikolaj Bjorner (nbjorner) 2022-10-11 + + +--*/ +#pragma once + +#include "ast/converters/generic_model_converter.h" + +class iexpr_inverter { +protected: + ast_manager& m; + std::function m_is_var; + generic_model_converter_ref m_mc; + + bool uncnstr(expr* e) const { return m_is_var(e); } + bool uncnstr(unsigned num, expr * const * args) const; + void mk_fresh_uncnstr_var_for(sort* s, expr_ref& v); + void mk_fresh_uncnstr_var_for(func_decl* f, expr_ref& v) { mk_fresh_uncnstr_var_for(f->get_range(), v); } + void add_def(expr * v, expr * def); + void add_defs(unsigned num, expr * const * args, expr * u, expr * identity); + +public: + iexpr_inverter(ast_manager& m): m(m) {} + virtual ~iexpr_inverter() {} + virtual void set_is_var(std::function& is_var) { m_is_var = is_var; } + virtual void set_model_converter(generic_model_converter* mc) { m_mc = mc; } + + virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) = 0; + virtual bool mk_diff(expr* t, expr_ref& r) = 0; +}; + +class expr_inverter : public iexpr_inverter { + ptr_vector m_inverters; + + bool is_converted(func_decl* f, unsigned num, expr* const* args); + +public: + expr_inverter(ast_manager& m); + ~expr_inverter() override; + bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) override; + bool mk_diff(expr* t, expr_ref& r) override; + void set_is_var(std::function& is_var) override; + void set_model_converter(generic_model_converter* mc) override; +}; diff --git a/src/ast/converters/generic_model_converter.h b/src/ast/converters/generic_model_converter.h index c20bfebb34d..85e8d03904d 100644 --- a/src/ast/converters/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -22,6 +22,7 @@ Module Name: #include "ast/converters/model_converter.h" class generic_model_converter : public model_converter { +public: enum instruction { HIDE, ADD }; struct entry { func_decl_ref m_f; @@ -30,6 +31,7 @@ class generic_model_converter : public model_converter { entry(func_decl* f, expr* d, ast_manager& m, instruction i): m_f(f, m), m_def(d, m), m_instruction(i) {} }; +private: ast_manager& m; std::string m_orig; vector m_entries; @@ -67,6 +69,8 @@ class generic_model_converter : public model_converter { void set_env(ast_pp_util* visitor) override; void get_units(obj_map& units) override; + + vector const& entries() const { return m_entries; } }; typedef ref generic_model_converter_ref; diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 0790b418df8..1e7b6da3b89 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -120,11 +120,11 @@ bool subterms::iterator::operator!=(iterator const& other) const { } -subterms_postorder::subterms_postorder(expr_ref_vector const& es): m_es(es) {} -subterms_postorder::subterms_postorder(expr_ref const& e) : m_es(e.m()) { if (e) m_es.push_back(e); } +subterms_postorder::subterms_postorder(expr_ref_vector const& es, bool include_bound): m_include_bound(include_bound), m_es(es) {} +subterms_postorder::subterms_postorder(expr_ref const& e, bool include_bound) : m_include_bound(include_bound), m_es(e.m()) { if (e) m_es.push_back(e); } subterms_postorder::iterator subterms_postorder::begin() { return iterator(*this, true); } subterms_postorder::iterator subterms_postorder::end() { return iterator(*this, false); } -subterms_postorder::iterator::iterator(subterms_postorder& f, bool start): m_es(f.m_es) { +subterms_postorder::iterator::iterator(subterms_postorder& f, bool start): m_include_bound(f.m_include_bound), m_es(f.m_es) { if (!start) m_es.reset(); next(); } @@ -153,6 +153,13 @@ void subterms_postorder::iterator::next() { } } } + else if (is_quantifier(e) && m_include_bound) { + expr* body = to_quantifier(e)->get_expr(); + if (!m_visited.is_marked(body)) { + m_es.push_back(body); + all_visited = false; + } + } if (all_visited) { m_visited.mark(e, true); break; diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 99a0f6b9d27..2d5ed05ae4f 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -200,9 +200,15 @@ class subterms { }; class subterms_postorder { + bool m_include_bound; expr_ref_vector m_es; + subterms_postorder(expr_ref_vector const& es, bool include_bound); + subterms_postorder(expr_ref const& e, bool include_bound); + + public: class iterator { + bool m_include_bound = false; expr_ref_vector m_es; expr_mark m_visited, m_seen; void next(); @@ -214,8 +220,10 @@ class subterms_postorder { bool operator==(iterator const& other) const; bool operator!=(iterator const& other) const; }; - subterms_postorder(expr_ref_vector const& es); - subterms_postorder(expr_ref const& e); + static subterms_postorder all(expr_ref_vector const& es) { return subterms_postorder(es, true); } + static subterms_postorder ground(expr_ref_vector const& es) { return subterms_postorder(es, false); } + static subterms_postorder all(expr_ref const& e) { return subterms_postorder(e, true); } + static subterms_postorder ground(expr_ref const& e) { return subterms_postorder(e, false); } iterator begin(); iterator end(); }; diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index ef04cc43371..75aa1ec7a87 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -1,6 +1,7 @@ z3_add_component(simplifiers SOURCES bv_slice.cpp + elim_unconstrained.cpp euf_completion.cpp extract_eqs.cpp model_reconstruction_trail.cpp diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp new file mode 100644 index 00000000000..cbd575b6af7 --- /dev/null +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -0,0 +1,297 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + elim_unconstrained.cpp + +Abstract: + + Incremental, modular and more efficient version of elim_unconstr_tactic and + reduce_invertible_tactic. + + reduce_invertible_tactic should be subsumed by elim_unconstr_tactic + elim_unconstr_tactic has some built-in limitations that are not easy to fix with small changes: + - it is inefficient for examples like x <= y, y <= z, z <= u, ... + All variables x, y, z, .. can eventually be eliminated, but the tactic requires a global + analysis between each elimination. We address this by using reference counts and maintaining + a heap of reference counts. + - it does not accomodate side constraints. The more general invertibility reduction methods, such + as those introduced for bit-vectors use side constraints. + - it is not modular: we detach the expression invertion routines to self-contained code. + + Maintain a representation of terms as a set of nodes. + Each node has: + + - reference count = number of parents that are live + - orig - original term, the orig->get_id() is the index to the node + - term - current term representing the node after rewriting + - parents - list of parents where orig occurs. + + Subterms have reference counts + Elegible variables are inserted into a heap ordered by reference counts. + Variables that have reference count 1 are examined for invertibility. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-11. + +--*/ + + + +#include "ast/ast_ll_pp.h" +#include "ast/ast_pp.h" +#include "ast/recfun_decl_plugin.h" +#include "ast/simplifiers/elim_unconstrained.h" + +elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) : + dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m) { + + std::function is_var = [&](expr* e) { + return is_uninterp_const(e) && !m_frozen.is_marked(e) && get_node(e).m_refcount == 1; + }; + + m_inverter.set_is_var(is_var); +} + + +void elim_unconstrained::eliminate() { + + while (!m_heap.empty()) { + expr_ref r(m), side_cond(m); + int v = m_heap.erase_min(); + node& n = get_node(v); + IF_VERBOSE(11, verbose_stream() << mk_pp(n.m_orig, m) << " @ " << n.m_refcount << "\n"); + if (n.m_refcount == 0) + continue; + if (n.m_refcount > 1) + return; + + if (n.m_parents.empty()) + continue; + expr* e = get_parent(v); + for (expr* p : n.m_parents) + IF_VERBOSE(11, verbose_stream() << "parent " << mk_pp(p, m) << "\n"); + if (!is_app(e)) + continue; + if (!is_ground(e)) + continue; + app* t = to_app(e); + m_args.reset(); + for (expr* arg : *to_app(t)) + m_args.push_back(get_node(arg).m_term); + if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) + continue; + + m_stats.m_num_eliminated++; + n.m_refcount = 0; + SASSERT(r); + m_trail.push_back(r); + gc(e); + + get_node(e).m_term = r; + get_node(e).m_refcount++; + IF_VERBOSE(11, verbose_stream() << mk_pp(e, m) << "\n"); + SASSERT(!m_heap.contains(e->get_id())); + if (is_uninterp_const(r)) + m_heap.insert(e->get_id()); + + IF_VERBOSE(11, verbose_stream() << mk_pp(n.m_orig, m) << " " << mk_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n"); + + SASSERT(!side_cond && "not implemented to add side conditions\n"); + } +} + +void elim_unconstrained::add_term(expr* t) { + expr_ref_vector terms(m); + terms.push_back(t); + init_terms(terms); +} + +expr* elim_unconstrained::get_parent(unsigned n) const { + for (expr* p : get_node(n).m_parents) + if (get_node(p).m_refcount > 0 && get_node(p).m_term == get_node(p).m_orig) + return p; + IF_VERBOSE(0, verbose_stream() << "term " << mk_pp(get_node(n).m_term, m) << "\n"); + for (expr* p : get_node(n).m_parents) + IF_VERBOSE(0, verbose_stream() << "parent " << mk_pp(p, m) << "\n"); + UNREACHABLE(); + return nullptr; +} +/** + * initialize node structure + */ +void elim_unconstrained::init_nodes() { + expr_ref_vector terms(m); + for (unsigned i = 0; i < m_fmls.size(); ++i) + terms.push_back(m_fmls[i].fml()); + m_trail.append(terms); + m_heap.reset(); + + init_terms(terms); + + for (expr* e : terms) + inc_ref(e); + + // freeze subterms before the head. + terms.reset(); + for (unsigned i = 0; i < m_qhead; ++i) + terms.push_back(m_fmls[i].fml()); + for (expr* e : subterms::all(terms)) + m_frozen.mark(e, true); + + + recfun::util rec(m); + if (rec.has_rec_defs()) { + for (func_decl* f : rec.get_rec_funs()) { + expr* rhs = rec.get_def(f).get_rhs(); + for (expr* t : subterms::all(expr_ref(rhs, m))) + m_frozen.mark(t); + } + } +} + +void elim_unconstrained::init_terms(expr_ref_vector const& terms) { + unsigned max_id = 0; + for (expr* e : subterms::all(terms)) + max_id = std::max(max_id, e->get_id()); + + m_nodes.reserve(max_id + 1); + m_heap.reserve(max_id + 1); + + for (expr* e : subterms_postorder::all(terms)) { + node& n = get_node(e); + if (n.m_term) + continue; + n.m_orig = e; + n.m_term = e; + n.m_refcount = 0; + if (is_uninterp_const(e)) + m_heap.insert(e->get_id()); + if (is_quantifier(e)) { + expr* body = to_quantifier(e)->get_expr(); + get_node(body).m_parents.push_back(e); + inc_ref(body); + } + else if (is_app(e)) { + for (expr* arg : *to_app(e)) { + get_node(arg).m_parents.push_back(e); + inc_ref(arg); + } + } + } +} + +void elim_unconstrained::gc(expr* t) { + ptr_vector todo; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + node& n = get_node(t); + if (n.m_refcount == 0) + continue; + dec_ref(t); + if (n.m_refcount != 0) + continue; + if (is_app(t)) { + for (expr* arg : *to_app(t)) + todo.push_back(arg); + } + else if (is_quantifier(t)) + todo.push_back(to_quantifier(t)->get_expr()); + } +} + +/** + * walk nodes starting from lowest depth and reconstruct their normalized forms. + */ +void elim_unconstrained::reconstruct_terms() { + expr_ref_vector terms(m); + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) + terms.push_back(m_fmls[i].fml()); + + for (expr* e : subterms_postorder::all(terms)) { + node& n = get_node(e); + expr* t = n.m_term; + if (t != n.m_orig) + continue; + if (is_app(t)) { + bool change = false; + m_args.reset(); + for (expr* arg : *to_app(t)) { + node& n2 = get_node(arg); + m_args.push_back(n2.m_term); + change |= n2.m_term != n2.m_orig; + } + if (change) { + n.m_term = m.mk_app(to_app(t)->get_decl(), m_args); + m_trail.push_back(n.m_term); + } + } + else if (is_quantifier(t)) { + node& n2 = get_node(to_quantifier(t)->get_expr()); + if (n2.m_term != n2.m_orig) { + n.m_term = m.update_quantifier(to_quantifier(t), n2.m_term); + m_trail.push_back(n.m_term); + } + } + } +} + +void elim_unconstrained::assert_normalized(vector& old_fmls) { + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + auto [f, d] = m_fmls[i](); + node& n = get_node(f); + expr* g = n.m_term; + if (f == g) + continue; + old_fmls.push_back(m_fmls[i]); + m_fmls.update(i, dependent_expr(m, g, d)); + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); + TRACE("elim_unconstrained", tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); + } +} + +void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector const& old_fmls) { + auto& trail = m_fmls.model_trail(); + scoped_ptr rp = mk_default_expr_replacer(m, false); + scoped_ptr sub = alloc(expr_substitution, m, true, false); + rp->set_substitution(sub.get()); + expr_ref new_def(m); + for (auto const& entry : mc.entries()) { + switch (entry.m_instruction) { + case generic_model_converter::instruction::HIDE: + break; + case generic_model_converter::instruction::ADD: + new_def = entry.m_def; + (*rp)(new_def); + sub->insert(m.mk_const(entry.m_f), new_def, nullptr, nullptr); + break; + } + } + trail.push(sub.detach(), old_fmls); + + for (auto const& entry : mc.entries()) { + switch (entry.m_instruction) { + case generic_model_converter::instruction::HIDE: + trail.push(entry.m_f); + break; + case generic_model_converter::instruction::ADD: + break; + } + } +} + +void elim_unconstrained::reduce() { + generic_model_converter_ref mc = alloc(generic_model_converter, m, "elim-unconstrained"); + m_inverter.set_model_converter(mc.get()); + init_nodes(); + eliminate(); + reconstruct_terms(); + vector old_fmls; + assert_normalized(old_fmls); + update_model_trail(*mc, old_fmls); + advance_qhead(m_fmls.size()); +} diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h new file mode 100644 index 00000000000..327ceeff0b4 --- /dev/null +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -0,0 +1,84 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + elim_unconstrained.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + + +#pragma once + +#include "util/heap.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/converters/expr_inverter.h" + + +class elim_unconstrained : public dependent_expr_simplifier { + + struct node { + unsigned m_refcount; + expr* m_term; + expr* m_orig; + ptr_vector m_parents; + }; + struct var_lt { + elim_unconstrained& s; + var_lt(elim_unconstrained& s) : s(s) {} + bool operator()(int v1, int v2) const { + return s.get_node(v1).m_refcount < s.get_node(v2).m_refcount; + } + }; + struct stats { + unsigned m_num_eliminated = 0; + void reset() { m_num_eliminated = 0; } + }; + expr_inverter m_inverter; + vector m_nodes; + var_lt m_lt; + heap m_heap; + expr_ref_vector m_trail; + ptr_vector m_args; + expr_mark m_frozen; + stats m_stats; + + bool operator()(int v1, int v2) const { return get_node(v1).m_refcount < get_node(v2).m_refcount; } + + node& get_node(unsigned n) { return m_nodes[n]; } + node const& get_node(unsigned n) const { return m_nodes[n]; } + node& get_node(expr* t) { return m_nodes[t->get_id()]; } + node const& get_node(expr* t) const { return m_nodes[t->get_id()]; } + unsigned get_refcount(expr* t) const { return get_node(t).m_refcount; } + void inc_ref(expr* t) { ++get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.increased(t->get_id()); } + void dec_ref(expr* t) { --get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.decreased(t->get_id()); } + void gc(expr* t); + + expr* get_parent(unsigned n) const; + void init_refcounts(); + void dec_refcounts(expr* t); + + void add_term(expr* t); + void init_terms(expr_ref_vector const& terms); + + void init_nodes(); + void eliminate(); + void reconstruct_terms(); + void assert_normalized(vector& old_fmls); + void update_model_trail(generic_model_converter& mc, vector const& old_fmls); + +public: + + elim_unconstrained(ast_manager& m, dependent_expr_state& fmls); + + void reduce() override; + + void collect_statistics(statistics& st) const override { st.update("elim-unconstr", m_stats.m_num_eliminated); } + + void reset_statistics() override { m_stats.reset(); } +}; diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 373a500bf21..a5f1cf8b030 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -30,6 +30,9 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorm_active) continue; + if (t->m_hide) + continue; + // updates that have no intersections with current variables are skipped if (!t->intersects(free_vars)) continue; @@ -79,6 +82,11 @@ model_converter_ref model_reconstruction_trail::get_model_converter() { if (!t->m_active) continue; + if (t->m_hide) { + mc->hide(t->m_hide); + continue; + } + if (first) { first = false; for (auto const& [v, def] : t->m_subst->sub()) { diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index c9b42bc9211..eeef136ee95 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -35,11 +35,14 @@ class model_reconstruction_trail { struct entry { scoped_ptr m_subst; vector m_removed; + func_decl* m_hide = nullptr; bool m_active = true; entry(expr_substitution* s, vector const& rem) : m_subst(s), m_removed(rem) {} + entry(func_decl* h) : m_hide(h) {} + bool is_loose() const { return !m_removed.empty(); } bool intersects(ast_mark const& free_vars) const { @@ -48,8 +51,6 @@ class model_reconstruction_trail { return true; return false; } - - }; ast_manager& m; @@ -81,6 +82,14 @@ class model_reconstruction_trail { */ 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)); + } + + /** + * add declaration to hide + */ + void push(func_decl* f) { + m_trail.push_back(alloc(entry, f)); m_trail_stack.push(push_back_vector(m_trail)); } diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index 38d2699f0a2..bab61afce87 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -17,7 +17,6 @@ z3_add_component(core_tactics pb_preprocess_tactic.cpp propagate_values_tactic.cpp reduce_args_tactic.cpp - reduce_invertible_tactic.cpp simplify_tactic.cpp solve_eqs_tactic.cpp special_relations_tactic.cpp @@ -39,6 +38,7 @@ z3_add_component(core_tactics dom_simplify_tactic.h elim_term_ite_tactic.h elim_uncnstr_tactic.h + elim_uncnstr2_tactic.h euf_completion_tactic.h injectivity_tactic.h nnf_tactic.h @@ -46,7 +46,6 @@ z3_add_component(core_tactics pb_preprocess_tactic.h propagate_values_tactic.h reduce_args_tactic.h - reduce_invertible_tactic.h simplify_tactic.h solve_eqs_tactic.h solve_eqs2_tactic.h diff --git a/src/tactic/core/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h new file mode 100644 index 00000000000..2c5f81d5e9d --- /dev/null +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + elim_unconstr2_tactic.h + +Abstract: + + Tactic for eliminating unconstrained terms. + +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/elim_unconstrained.h" +#include "ast/simplifiers/elim_unconstrained.h" + +class elim_uncnstr2_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(elim_unconstrained, m, s); + } +}; + +inline tactic * mk_elim_uncnstr2_tactic(ast_manager & m, params_ref const & p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(elim_uncnstr2_tactic_factory), "elim-unconstr2"); +} + + +/* + ADD_TACTIC("elim-uncnstr2", "eliminate unconstrained variables.", "mk_elim_uncnstr2_tactic(m, p)") +*/ + + diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 34aa748d97c..5f0963bfda8 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -856,9 +856,8 @@ class elim_uncnstr_tactic : public tactic { void init_mc(bool produce_models) { m_mc = nullptr; - if (produce_models) { - m_mc = alloc(mc, m(), "elim_uncstr"); - } + if (produce_models) + m_mc = alloc(mc, m(), "elim_uncstr"); } void init_rw(bool produce_proofs) { @@ -872,7 +871,7 @@ class elim_uncnstr_tactic : public tactic { m_vars.reset(); collect_occs p; p(*g, m_vars); - if (m_vars.empty() || recfun::util(m()).has_defs()) { + if (m_vars.empty() || recfun::util(m()).has_rec_defs()) { result.push_back(g.get()); // did not increase depth since it didn't do anything. return; diff --git a/src/tactic/core/reduce_invertible_tactic.cpp b/src/tactic/core/reduce_invertible_tactic.cpp deleted file mode 100644 index ba9b5d752b9..00000000000 --- a/src/tactic/core/reduce_invertible_tactic.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/*++ -Copyright (c) 2018 Microsoft Corporation - -Module Name: - - reduce_invertible_tactic.cpp - -Abstract: - - Reduce invertible variables. - -Author: - - Nuno Lopes (nlopes) 2018-6-30 - Nikolaj Bjorner (nbjorner) - -Notes: - - 1. Walk through top-level uninterpreted constants. - ---*/ - -#include "ast/bv_decl_plugin.h" -#include "ast/arith_decl_plugin.h" -#include "ast/ast_pp.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/rewriter/rewriter_def.h" -#include "ast/rewriter/th_rewriter.h" -#include "tactic/tactic.h" -#include "tactic/core/reduce_invertible_tactic.h" -#include "tactic/core/collect_occs.h" -#include "ast/converters/generic_model_converter.h" -#include - -namespace { -class reduce_invertible_tactic : public tactic { - ast_manager& m; - bv_util m_bv; - arith_util m_arith; - -public: - reduce_invertible_tactic(ast_manager & m): - m(m), - m_bv(m), - m_arith(m) - {} - - char const* name() const override { return "reduce_invertible"; } - - tactic * translate(ast_manager & m) override { - return alloc(reduce_invertible_tactic, m); - } - - void operator()(goal_ref const & g, goal_ref_buffer & result) override { - tactic_report report("reduce-invertible", *g); - bool change = true; - while (change) { - change = false; - m_inverted.reset(); - m_parents.reset(); - collect_parents(g); - collect_occs occs; - obj_hashtable vars; - generic_model_converter_ref mc; - occs(*g, vars); - expr_safe_replace sub(m); - expr_ref new_v(m); - expr * p; - for (expr* v : vars) { - if (is_invertible(v, p, new_v, &mc)) { - mark_inverted(p); - sub.insert(p, new_v); - TRACE("invertible_tactic", tout << mk_pp(p, m) << " " << new_v << "\n";); - change = true; - break; - } - } - reduce_q_rw rw(*this); - unsigned sz = g->size(); - for (unsigned idx = 0; !g->inconsistent() && idx < sz; idx++) { - checkpoint(); - expr* f = g->form(idx); - expr_ref f_new(m); - sub(f, f_new); - rw(f_new, f_new); - if (f == f_new) continue; - proof_ref new_pr(m); - if (g->proofs_enabled()) { - proof * pr = g->pr(idx); - new_pr = m.mk_rewrite(f, f_new); - new_pr = m.mk_modus_ponens(pr, new_pr); - } - g->update(idx, f_new, new_pr, g->dep(idx)); - } - if (mc) g->add(mc.get()); - TRACE("invertible_tactic", g->display(tout);); - g->inc_depth(); - } - result.push_back(g.get()); - CTRACE("invertible_tactic", g->mc(), g->mc()->display(tout);); - } - - void cleanup() override {} - -private: - void checkpoint() { - tactic::checkpoint(m); - } - - bool is_bv_neg(expr * e) { - if (m_bv.is_bv_neg(e)) - return true; - - expr *a, *b; - if (m_bv.is_bv_mul(e, a, b)) { - return m_bv.is_allone(a) || m_bv.is_allone(b); - } - return false; - } - - expr_mark m_inverted; - void mark_inverted(expr *p) { - ptr_buffer todo; - todo.push_back(p); - while (!todo.empty()) { - p = todo.back(); - todo.pop_back(); - if (!m_inverted.is_marked(p)) { - m_inverted.mark(p, true); - if (is_app(p)) { - for (expr* arg : *to_app(p)) { - todo.push_back(arg); - } - } - else if (is_quantifier(p)) { - todo.push_back(to_quantifier(p)->get_expr()); - } - } - } - } - - // store one parent of expression, or null if multiple - struct parents { - parents(): m_p(0) {} - uintptr_t m_p; - - void set(expr * e) { - SASSERT((uintptr_t)e != 1); - if (!m_p) m_p = (uintptr_t)e; - else m_p = 1; - } - - expr * get() const { - return m_p == 1 ? nullptr : (expr*)m_p; - } - }; - svector m_parents; - struct parent_collector { - reduce_invertible_tactic& c; - parent_collector(reduce_invertible_tactic& c):c(c) {} - void operator()(app* n) { - for (expr* arg : *n) { - c.m_parents.reserve(arg->get_id() + 1); - c.m_parents[arg->get_id()].set(n); - } - } - - void operator()(var* v) { - c.m_parents.reserve(v->get_id() + 1); - } - - void operator()(quantifier* q) {} - }; - - void collect_parents(goal_ref const& g) { - parent_collector proc(*this); - expr_fast_mark1 visited; - unsigned sz = g->size(); - for (unsigned i = 0; i < sz; i++) { - checkpoint(); - quick_for_each_expr(proc, visited, g->form(i)); - } - } - - void ensure_mc(generic_model_converter_ref* mc) { - if (mc && !(*mc)) *mc = alloc(generic_model_converter, m, "reduce-invertible"); - } - - bool is_full_domain_var(expr* v, rational& model) { - auto f = is_app(v) ? to_app(v)->get_decl() : nullptr; - if (!f || f->get_family_id() != m_bv.get_family_id() || f->get_arity() == 0) - return false; - - switch (f->get_decl_kind()) { - case OP_BADD: - case OP_BSUB: - model = rational::zero(); - return true; - - case OP_BAND: - model = rational::power_of_two(m_bv.get_bv_size(v)) - rational::one(); - return true; - - case OP_BMUL: - model = rational::one(); - return true; - - case OP_BSDIV: - case OP_BSDIV0: - case OP_BSDIV_I: - case OP_BUDIV: - case OP_BUDIV0: - case OP_BUDIV_I: - default: - return false; - } - } - - bool rewrite_unconstr(expr* v, expr_ref& new_v, generic_model_converter_ref* mc, unsigned max_var) { - rational mdl; - if (!is_full_domain_var(v, mdl)) - return false; - - rational r; - app* a = to_app(v); - expr* fst_arg = a->get_arg(0); - - for (expr* arg : *a) - if (!m_parents[arg->get_id()].get()) - return false; - - if (is_var(fst_arg)) { - for (expr* arg : *a) { - if (!is_var(arg)) - return false; - if (to_var(arg)->get_idx() >= max_var) - return false; - } - } - else { - if (!is_uninterp_const(fst_arg)) - return false; - bool first = true; - for (expr* arg : *a) { - if (!is_app(arg)) - return false; - if (is_uninterp_const(arg)) - continue; - if (m_bv.is_numeral(arg, r) && r == mdl) { - if (first || mdl.is_zero()) { - first = false; - continue; - } - else - return false; - } - return false; - } - } - - if (mc) { - ensure_mc(mc); - expr_ref num(m_bv.mk_numeral(mdl, fst_arg->get_sort()), m); - for (unsigned i = 1, n = a->get_num_args(); i != n; ++i) { - expr* arg = a->get_arg(i); - if (m_bv.is_numeral(arg)) - continue; - (*mc)->add(arg, num); - } - } - new_v = fst_arg; - return true; - } - - // TBD: could be made to be recursive, by walking multiple layers of parents. - - bool is_invertible(expr* v, expr*& p, expr_ref& new_v, generic_model_converter_ref* mc, unsigned max_var = 0) { - rational r; - if (m_parents.size() <= v->get_id()) { - return false; - } - p = m_parents[v->get_id()].get(); - if (!p || m_inverted.is_marked(p) || (mc && !is_ground(p))) { - return false; - } - - if (m_bv.is_bv_xor(p) || - m_bv.is_bv_not(p) || - is_bv_neg(p)) { - if (mc) { - ensure_mc(mc); - (*mc)->add(v, p); - } - new_v = v; - return true; - } - - if (rewrite_unconstr(p, new_v, mc, max_var)) - return true; - - if (m_bv.is_bv_add(p)) { - if (mc) { - ensure_mc(mc); - // if we solve for v' := v + t - // then the value for v is v' - t - expr_ref def(v, m); - for (expr* arg : *to_app(p)) { - if (arg != v) def = m_bv.mk_bv_sub(def, arg); - } - (*mc)->add(v, def); - } - new_v = v; - return true; - } - - if (m_bv.is_bv_mul(p)) { - expr_ref rest(m); - for (expr* arg : *to_app(p)) { - if (arg != v) { - if (rest) - rest = m_bv.mk_bv_mul(rest, arg); - else - rest = arg; - } - } - if (!rest) return false; - - // so far just support numeral - if (!m_bv.is_numeral(rest, r)) - return false; - - // create case split on - // divisbility of 2 - // v * t -> - // if t = 0, set v' := 0 and the solution for v is 0. - // otherwise, - // let i be the position of the least bit of t set to 1 - // then extract[sz-1:i](v) ++ zero[i-1:0] is the invertible of v * t - // thus - // extract[i+1:0](t) = 1 ++ zero[i-1:0] -> extract[sz-1:i](v) ++ zero[i-1:0] - // to reproduce the original v from t - // solve for v*t = extract[sz-1:i](v') ++ zero[i-1:0] - // using values for t and v' - // thus let t' = t / 2^i - // and t'' = the multiplicative inverse of t' - // then t'' * v' * t = t'' * v' * t' * 2^i = v' * 2^i = extract[sz-1:i](v') ++ zero[i-1:0] - // so t'' *v' works - // - unsigned sz = m_bv.get_bv_size(p); - expr_ref bit1(m_bv.mk_numeral(1, 1), m); - - - unsigned sh = 0; - while (r.is_pos() && r.is_even()) { - r /= rational(2); - ++sh; - } - if (r.is_pos() && sh > 0) - new_v = m_bv.mk_concat(m_bv.mk_extract(sz-sh-1, 0, v), m_bv.mk_numeral(0, sh)); - else - new_v = v; - if (mc && !r.is_zero()) { - ensure_mc(mc); - expr_ref def(m); - rational inv_r; - VERIFY(r.mult_inverse(sz, inv_r)); - def = m_bv.mk_bv_mul(m_bv.mk_numeral(inv_r, sz), v); - (*mc)->add(v, def); - TRACE("invertible_tactic", tout << def << "\n";); - } - return true; - } - if (m_bv.is_bv_sub(p)) { - // TBD - } - if (m_bv.is_bv_udivi(p)) { - // TBD - } - // sdivi, sremi, uremi, smodi - // TBD - - if (m_arith.is_mul(p) && m_arith.is_real(p)) { - expr_ref rest(m); - for (expr* arg : *to_app(p)) { - if (arg != v) { - if (rest) - rest = m_arith.mk_mul(rest, arg); - else - rest = arg; - } - } - if (!rest) return false; - if (!m_arith.is_numeral(rest, r) || r.is_zero()) - return false; - expr_ref zero(m_arith.mk_real(0), m); - new_v = m.mk_ite(m.mk_eq(zero, rest), zero, v); - if (mc) { - ensure_mc(mc); - expr_ref def(m_arith.mk_div(v, rest), m); - (*mc)->add(v, def); - } - return true; - } - - - expr* e1 = nullptr, *e2 = nullptr; - - // v / t unless t != 0 - if (m_arith.is_div(p, e1, e2) && e1 == v && m_arith.is_numeral(e2, r) && !r.is_zero()) { - new_v = v; - if (mc) { - ensure_mc(mc); - (*mc)->add(v, m_arith.mk_mul(e1, e2)); - } - return true; - } - - if (m.is_eq(p, e1, e2)) { - TRACE("invertible_tactic", tout << mk_pp(v, m) << "\n";); - if (mc && has_diagonal(e1)) { - ensure_mc(mc); - new_v = m.mk_fresh_const("eq", m.mk_bool_sort()); - SASSERT(v == e1 || v == e2); - expr* other = (v == e1) ? e2 : e1; - (*mc)->hide(new_v); - (*mc)->add(v, m.mk_ite(new_v, other, mk_diagonal(other))); - return true; - } - else if (mc) { - // diagonal functions for other types depend on theory. - return false; - } - else if (is_var(v) && is_non_singleton_sort(v->get_sort())) { - new_v = m.mk_var(to_var(v)->get_idx(), m.mk_bool_sort()); - return true; - } - } - - // - // v <= u - // => u + 1 == 0 or delta - // v := delta ? u : u + 1 - // - if (m_bv.is_bv_ule(p, e1, e2) && e1 == v && mc) { - ensure_mc(mc); - unsigned sz = m_bv.get_bv_size(e2); - expr_ref delta(m.mk_fresh_const("ule", m.mk_bool_sort()), m); - expr_ref succ_e2(m_bv.mk_bv_add(e2, m_bv.mk_numeral(1, sz)), m); - new_v = m.mk_or(delta, m.mk_eq(succ_e2, m_bv.mk_numeral(0, sz))); - (*mc)->hide(delta); - (*mc)->add(v, m.mk_ite(delta, e2, succ_e2)); - return true; - } - - // - // u <= v - // => u == 0 or delta - // v := delta ? u : u - 1 - // - if (m_bv.is_bv_ule(p, e1, e2) && e2 == v && mc) { - ensure_mc(mc); - unsigned sz = m_bv.get_bv_size(e1); - expr_ref delta(m.mk_fresh_const("ule", m.mk_bool_sort()), m); - expr_ref pred_e1(m_bv.mk_bv_sub(e1, m_bv.mk_numeral(1, sz)), m); - new_v = m.mk_or(delta, m.mk_eq(e1, m_bv.mk_numeral(0, sz))); - (*mc)->hide(delta); - (*mc)->add(v, m.mk_ite(delta, e1, pred_e1)); - return true; - } - return false; - } - - bool has_diagonal(expr* e) { - return - m_bv.is_bv(e) || - m.is_bool(e) || - m_arith.is_int_real(e); - } - - expr * mk_diagonal(expr* e) { - if (m_bv.is_bv(e)) return m_bv.mk_bv_not(e); - if (m.is_bool(e)) return m.mk_not(e); - if (m_arith.is_int(e)) return m_arith.mk_add(m_arith.mk_int(1), e); - if (m_arith.is_real(e)) return m_arith.mk_add(m_arith.mk_real(1), e); - UNREACHABLE(); - return e; - } - - bool is_non_singleton_sort(sort* s) { - if (m.is_uninterp(s)) return false; - sort_size sz = s->get_num_elements(); - if (sz.is_finite() && sz.size() == 1) return false; - return true; - } - - struct reduce_q_rw_cfg : public default_rewriter_cfg { - ast_manager& m; - reduce_invertible_tactic& t; - - reduce_q_rw_cfg(reduce_invertible_tactic& t): m(t.m), t(t) {} - - bool reduce_quantifier(quantifier * old_q, - expr * new_body, - expr * const * new_patterns, - expr * const * new_no_patterns, - expr_ref & result, - proof_ref & result_pr) { - if (is_lambda(old_q)) return false; - if (has_quantifiers(new_body)) return false; - ref_buffer vars(m); - ptr_buffer new_sorts; - unsigned n = old_q->get_num_decls(); - for (unsigned i = 0; i < n; ++i) { - sort* srt = old_q->get_decl_sort(i); - vars.push_back(m.mk_var(n - i - 1, srt)); - new_sorts.push_back(srt); - } - // for each variable, collect parents, - // ensure they are in unique location and not under other quantifiers. - // if they are invertible, then produce inverting expression. - // - expr_safe_replace sub(m); - t.m_parents.reset(); - t.m_inverted.reset(); - expr_ref new_v(m); - expr * p; - - { - parent_collector proc(t); - expr_fast_mark1 visited; - quick_for_each_expr(proc, visited, new_body); - } - bool has_new_var = false; - for (unsigned i = 0; i < vars.size(); ++i) { - var* v = vars[i]; - if (!occurs_under_nested_q(v, new_body) && t.is_invertible(v, p, new_v, nullptr, vars.size())) { - TRACE("invertible_tactic", tout << mk_pp(v, m) << " " << mk_pp(p, m) << "\n";); - t.mark_inverted(p); - sub.insert(p, new_v); - new_sorts[i] = new_v->get_sort(); - has_new_var |= new_v != v; - } - } - if (has_new_var) { - sub(new_body, result); - result = m.mk_quantifier(old_q->get_kind(), new_sorts.size(), new_sorts.data(), old_q->get_decl_names(), result, old_q->get_weight()); - result_pr = nullptr; - return true; - } - if (!sub.empty()) { - sub(new_body, result); - result = m.update_quantifier(old_q, old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns, result); - result_pr = nullptr; - return true; - } - return false; - } - - bool occurs_under_nested_q(var* v, expr* body) { - return has_quantifiers(body); - } - }; - - struct reduce_q_rw : rewriter_tpl { - reduce_q_rw_cfg m_cfg; - public: - reduce_q_rw(reduce_invertible_tactic& t): - rewriter_tpl(t.m, false, m_cfg), - m_cfg(t) {} - }; -}; -} - -tactic * mk_reduce_invertible_tactic(ast_manager & m, params_ref const &) { - return alloc(reduce_invertible_tactic, m); -} diff --git a/src/tactic/core/reduce_invertible_tactic.h b/src/tactic/core/reduce_invertible_tactic.h deleted file mode 100644 index d40bf8a59d4..00000000000 --- a/src/tactic/core/reduce_invertible_tactic.h +++ /dev/null @@ -1,32 +0,0 @@ -/*++ -Copyright (c) 2018 Microsoft Corporation - -Module Name: - - reduce_invertible_tactic.h - -Abstract: - - Reduce invertible variables. - -Author: - - Nuno Lopes (nlopes) 2018-6-30 - Nikolaj Bjorner (nbjorner) - -Notes: - ---*/ - -#pragma once -#include "util/params.h" - -class tactic; -class ast_manager; - -tactic * mk_reduce_invertible_tactic(ast_manager & m, params_ref const & p = params_ref()); - -/* - ADD_TACTIC("reduce-invertible", "reduce invertible variable occurrences.", "mk_reduce_invertible_tactic(m, p)") -*/ - diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 56e27ee9ac8..4190378396b 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -15,6 +15,7 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-11-2. --*/ +#pragma once #include "tactic/tactic.h" #include "ast/simplifiers/dependent_expr_state.h" From 9d09064ad08211bfb436b02bf08ee7cc0ee56ce1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 12 Nov 2022 18:01:38 -0800 Subject: [PATCH 060/597] add comments to elim_unconstrained and remove unused function --- src/ast/simplifiers/elim_unconstrained.cpp | 16 ++++++++-------- src/ast/simplifiers/elim_unconstrained.h | 4 ---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index cbd575b6af7..2cdca3074a9 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -103,12 +103,6 @@ void elim_unconstrained::eliminate() { } } -void elim_unconstrained::add_term(expr* t) { - expr_ref_vector terms(m); - terms.push_back(t); - init_terms(terms); -} - expr* elim_unconstrained::get_parent(unsigned n) const { for (expr* p : get_node(n).m_parents) if (get_node(p).m_refcount > 0 && get_node(p).m_term == get_node(p).m_orig) @@ -128,20 +122,23 @@ void elim_unconstrained::init_nodes() { terms.push_back(m_fmls[i].fml()); m_trail.append(terms); m_heap.reset(); + m_frozen.reset(); + // initialize nodes for terms in the original goal init_terms(terms); + // top-level terms have reference count > 0 for (expr* e : terms) inc_ref(e); - // freeze subterms before the head. + // freeze subterms before the already processed head terms.reset(); for (unsigned i = 0; i < m_qhead; ++i) terms.push_back(m_fmls[i].fml()); for (expr* e : subterms::all(terms)) m_frozen.mark(e, true); - + // freeze subterms that occur with recursive function definitions recfun::util rec(m); if (rec.has_rec_defs()) { for (func_decl* f : rec.get_rec_funs()) { @@ -152,6 +149,9 @@ void elim_unconstrained::init_nodes() { } } +/** +* Create nodes for all terms in the goal +*/ void elim_unconstrained::init_terms(expr_ref_vector const& terms) { unsigned max_id = 0; for (expr* e : subterms::all(terms)) diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 327ceeff0b4..67d64027bf5 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -58,14 +58,10 @@ class elim_unconstrained : public dependent_expr_simplifier { void inc_ref(expr* t) { ++get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.increased(t->get_id()); } void dec_ref(expr* t) { --get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.decreased(t->get_id()); } void gc(expr* t); - expr* get_parent(unsigned n) const; void init_refcounts(); void dec_refcounts(expr* t); - - void add_term(expr* t); void init_terms(expr_ref_vector const& terms); - void init_nodes(); void eliminate(); void reconstruct_terms(); From f4e17ecc653a11ccd4efe3117f030c05949414b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 9 Nov 2022 19:50:22 -0800 Subject: [PATCH 061/597] add logging and diagnostics --- src/sat/sat_solver.cpp | 4 +++- src/solver/assertions/asserted_formulas.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 14e7e977545..0e5d7aa73e1 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -282,6 +282,7 @@ namespace sat { m_model_is_current = false; m_stats.m_mk_var++; bool_var v = m_justification.size(); + if (!m_free_vars.empty()) { v = m_free_vars.back(); m_free_vars.pop_back(); @@ -301,7 +302,7 @@ namespace sat { m_external.push_back(ext); m_var_scope.push_back(scope_lvl()); m_touched.push_back(0); - m_activity.push_back(0); + m_activity.push_back(0); m_mark.push_back(false); m_lit_mark.push_back(false); m_lit_mark.push_back(false); @@ -1262,6 +1263,7 @@ namespace sat { if (check_inconsistent()) return l_false; if (m_config.m_force_cleanup) do_cleanup(true); TRACE("sat", display(tout);); + TRACE("before_search", display(tout);); if (m_config.m_gc_burst) { // force gc diff --git a/src/solver/assertions/asserted_formulas.cpp b/src/solver/assertions/asserted_formulas.cpp index 2b20ebd8068..4e64ee39fd0 100644 --- a/src/solver/assertions/asserted_formulas.cpp +++ b/src/solver/assertions/asserted_formulas.cpp @@ -279,6 +279,8 @@ void asserted_formulas::reduce() { TRACE("before_reduce", display(tout);); CASSERT("well_sorted", check_well_sorted()); + IF_VERBOSE(10, verbose_stream() << "(smt.simplify-begin :num-exprs " << get_total_size() << ")\n";); + set_eliminate_and(false); // do not eliminate and before nnf. if (!invoke(m_propagate_values)) return; if (!invoke(m_find_macros)) return; From e33e66212c1a860c9bf5c48aa6f9926e3c0f2630 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 12 Nov 2022 18:03:38 -0800 Subject: [PATCH 062/597] propagate values should not flatten and/or also, elim_uncstr should only be disabled on recursive functions --- src/sat/sat_params.pyg | 2 +- src/tactic/core/propagate_values_tactic.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index c35e97b9214..7466a1126f9 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -47,7 +47,7 @@ def_module_params('sat', ('threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), ('drat.disable', BOOL, False, 'override anything that enables DRAT'), - ('smt.proof', SYMBOL, '', 'add SMT proof to file'), + ('smt.proof', SYMBOL, '', 'add SMT proof log to file'), ('smt.proof.check', BOOL, False, 'check SMT proof while it is created'), ('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'), ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 041e4d1c25c..6b8395fd860 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -213,6 +213,7 @@ class propagate_values_tactic : public tactic { m_occs(m, true /* track atoms */), m_params(p) { updt_params_core(p); + m_r.set_flat_and_or(false); } tactic * translate(ast_manager & m) override { From 343603f64341676d059411af62c4145c48f8268f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 12 Nov 2022 18:34:04 -0800 Subject: [PATCH 063/597] fix build Signed-off-by: Nikolaj Bjorner --- src/ast/converters/expr_inverter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 504c04ab0c4..0c433b10d8f 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -270,7 +270,7 @@ class bv_expr_inverter : public iexpr_inverter { bool process_bv_mul(func_decl* f, unsigned num, expr* const* args, expr_ref& r) { if (num == 0) - return nullptr; + return false; if (uncnstr(num, args)) { sort* s = args[0]->get_sort(); mk_fresh_uncnstr_var_for(f, r); From 0b83732b82a549b93e54ecf513c4992ba95df85f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 12 Nov 2022 18:35:41 -0800 Subject: [PATCH 064/597] missing override specifier --- src/ast/converters/expr_inverter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 0c433b10d8f..553e3c591ae 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -56,7 +56,7 @@ class basic_expr_inverter : public iexpr_inverter { * */ - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { SASSERT(f->get_family_id() == m.get_basic_family_id()); switch (f->get_decl_kind()) { case OP_ITE: From 3d570aaa0af2f81fc19cf08af64d76d1c8ea9f22 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 12 Nov 2022 18:43:57 -0800 Subject: [PATCH 065/597] add missing process_eq Signed-off-by: Nikolaj Bjorner --- src/ast/converters/expr_inverter.cpp | 29 +++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 553e3c591ae..0ef910a97c5 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -22,9 +22,32 @@ Module Name: #include "ast/converters/expr_inverter.h" class basic_expr_inverter : public iexpr_inverter { + iexpr_inverter& inv; + + bool process_eq(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) { + expr* v; + expr* t; + if (uncnstr(arg1)) + v = arg1, t = arg2; + else if (uncnstr(arg2)) + v = arg2, t = arg1; + else + return false; + + expr_ref d(m); + if (!inv.mk_diff(t, d)) + return false; + + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) + add_def(v, m.mk_ite(t, t, d)); + + return true; + } + public: - basic_expr_inverter(ast_manager& m) : iexpr_inverter(m) {} + basic_expr_inverter(ast_manager& m, iexpr_inverter& inv) : iexpr_inverter(m), inv(inv) {} /** * if (c, x, x') -> fresh @@ -104,7 +127,7 @@ class basic_expr_inverter : public iexpr_inverter { return false; case OP_EQ: SASSERT(num == 2); - // return process_eq(f, args[0], args[1]); + return process_eq(f, args[0], args[1], r); default: return false; } @@ -726,7 +749,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { auto* b = alloc(bv_expr_inverter, m); auto* ar = alloc(array_expr_inverter, m, *this); auto* dt = alloc(dt_expr_inverter, m); - m_inverters.setx(m.get_basic_family_id(), alloc(basic_expr_inverter, m), nullptr); + m_inverters.setx(m.get_basic_family_id(), alloc(basic_expr_inverter, m, *this), nullptr); m_inverters.setx(a->get_fid(), a, nullptr); m_inverters.setx(b->get_fid(), b, nullptr); m_inverters.setx(ar->get_fid(), ar, nullptr); From ce76e3138d9625601c738e7eab29cd0408116360 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Nov 2022 11:48:32 -0800 Subject: [PATCH 066/597] streamlining expr-inverter code --- src/ast/converters/expr_inverter.cpp | 36 +++++++++++++--------------- src/ast/converters/expr_inverter.h | 4 ++-- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 0ef910a97c5..95bfca98ad9 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -49,6 +49,8 @@ class basic_expr_inverter : public iexpr_inverter { basic_expr_inverter(ast_manager& m, iexpr_inverter& inv) : iexpr_inverter(m), inv(inv) {} + family_id get_fid() const override { return m.get_basic_family_id(); } + /** * if (c, x, x') -> fresh * x := fresh @@ -147,7 +149,7 @@ class arith_expr_inverter : public iexpr_inverter { arith_expr_inverter(ast_manager& m) : iexpr_inverter(m), a(m) {} - family_id get_fid() const { return a.get_family_id(); } + family_id get_fid() const override { return a.get_family_id(); } bool process_le_ge(func_decl* f, expr* arg1, expr* arg2, bool le, expr_ref& r) { expr* v; @@ -350,6 +352,9 @@ class bv_expr_inverter : public iexpr_inverter { // parity can be defined using a "giant" ite expression. // + if (uncnstr(args[i])) + IF_VERBOSE(11, verbose_stream() << "MISSED mult-unconstrained " << mk_bounded_pp(args[i], m) << "\n"); + return false; } @@ -462,7 +467,7 @@ class bv_expr_inverter : public iexpr_inverter { public: bv_expr_inverter(ast_manager& m) : iexpr_inverter(m), bv(m) {} - family_id get_fid() const { return bv.get_family_id(); } + family_id get_fid() const override { return bv.get_family_id(); } /** * x + t -> fresh @@ -567,7 +572,7 @@ class array_expr_inverter : public iexpr_inverter { public: array_expr_inverter(ast_manager& m, iexpr_inverter& s) : iexpr_inverter(m), a(m), inv(s) {} - family_id get_fid() const { return a.get_family_id(); } + family_id get_fid() const override { return a.get_family_id(); } bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { SASSERT(f->get_family_id() == a.get_family_id()); @@ -631,7 +636,7 @@ class dt_expr_inverter : public iexpr_inverter { dt_expr_inverter(ast_manager& m) : iexpr_inverter(m), dt(m) {} - family_id get_fid() const { return dt.get_family_id(); } + family_id get_fid() const override { return dt.get_family_id(); } /** * head(x) -> fresh * x := cons(fresh, arb) @@ -745,20 +750,16 @@ void iexpr_inverter::add_defs(unsigned num, expr* const* args, expr* u, expr* id expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { - auto* a = alloc(arith_expr_inverter, m); - auto* b = alloc(bv_expr_inverter, m); - auto* ar = alloc(array_expr_inverter, m, *this); - auto* dt = alloc(dt_expr_inverter, m); - m_inverters.setx(m.get_basic_family_id(), alloc(basic_expr_inverter, m, *this), nullptr); - m_inverters.setx(a->get_fid(), a, nullptr); - m_inverters.setx(b->get_fid(), b, nullptr); - m_inverters.setx(ar->get_fid(), ar, nullptr); - m_inverters.setx(dt->get_fid(), dt, nullptr); + auto add = [&](iexpr_inverter* i) { + m_inverters.setx(i->get_fid(), i, nullptr); + }; + add(alloc(arith_expr_inverter, m)); + add(alloc(bv_expr_inverter, m)); + add(alloc(array_expr_inverter, m, *this)); + add(alloc(dt_expr_inverter, m)); + add(alloc(basic_expr_inverter, m, *this)); } -bool expr_inverter::is_converted(func_decl* f, unsigned num, expr* const* args) { - return false; -} bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) { if (num == 0) @@ -771,9 +772,6 @@ bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, ex family_id fid = f->get_family_id(); if (fid == null_family_id) return false; - - if (is_converted(f, num, args)) - return false; auto* p = m_inverters.get(fid, nullptr); return p && (*p)(f, num, args, new_expr, side_cond); diff --git a/src/ast/converters/expr_inverter.h b/src/ast/converters/expr_inverter.h index 1ca77dbabe4..5b796547891 100644 --- a/src/ast/converters/expr_inverter.h +++ b/src/ast/converters/expr_inverter.h @@ -40,13 +40,12 @@ class iexpr_inverter { virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) = 0; virtual bool mk_diff(expr* t, expr_ref& r) = 0; + virtual family_id get_fid() const = 0; }; class expr_inverter : public iexpr_inverter { ptr_vector m_inverters; - bool is_converted(func_decl* f, unsigned num, expr* const* args); - public: expr_inverter(ast_manager& m); ~expr_inverter() override; @@ -54,4 +53,5 @@ class expr_inverter : public iexpr_inverter { bool mk_diff(expr* t, expr_ref& r) override; void set_is_var(std::function& is_var) override; void set_model_converter(generic_model_converter* mc) override; + family_id get_fid() const override { return null_family_id; } }; From 196788a0915d69b8599bd7a89dd4d644f324a537 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Nov 2022 12:09:56 -0800 Subject: [PATCH 067/597] bug fix for equality solving --- src/ast/simplifiers/elim_unconstrained.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 2cdca3074a9..94201dde7b6 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -81,9 +81,9 @@ void elim_unconstrained::eliminate() { m_args.reset(); for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); - if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) + if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) continue; - + SASSERT(r->get_sort() == t->get_sort()); m_stats.m_num_eliminated++; n.m_refcount = 0; SASSERT(r); From 38cde14e087e0470cce4d6134860007e3f8798fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Nov 2022 12:10:43 -0800 Subject: [PATCH 068/597] wip missing updates --- src/ast/converters/expr_inverter.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 95bfca98ad9..2c4c960a7fe 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -40,7 +40,7 @@ class basic_expr_inverter : public iexpr_inverter { mk_fresh_uncnstr_var_for(f, r); if (m_mc) - add_def(v, m.mk_ite(t, t, d)); + add_def(v, m.mk_ite(r, t, d)); return true; } @@ -352,8 +352,11 @@ class bv_expr_inverter : public iexpr_inverter { // parity can be defined using a "giant" ite expression. // +#if 0 + for (unsigned i = 0; i < num; ++i) if (uncnstr(args[i])) IF_VERBOSE(11, verbose_stream() << "MISSED mult-unconstrained " << mk_bounded_pp(args[i], m) << "\n"); +#endif return false; } @@ -550,6 +553,7 @@ class bv_expr_inverter : public iexpr_inverter { } bool mk_diff(expr* t, expr_ref& r) override { + SASSERT(bv.is_bv(t)); r = bv.mk_bv_not(t); return true; } @@ -806,7 +810,8 @@ void expr_inverter::set_is_var(std::function& is_var) { } void expr_inverter::set_model_converter(generic_model_converter* mc) { + m_mc = mc; for (auto* p : m_inverters) if (p) p->set_model_converter(mc); -} \ No newline at end of file +} From 3fa81d65270731290a4e03293d92eeaaa1a38469 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Nov 2022 13:25:19 -0800 Subject: [PATCH 069/597] bug fixes to elim-uncnstr2 tactic Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/elim_unconstrained.cpp | 36 ++++++++++++++-------- src/ast/simplifiers/elim_unconstrained.h | 12 +++----- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 94201dde7b6..caeaf295872 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -55,6 +55,12 @@ elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fml m_inverter.set_is_var(is_var); } +bool elim_unconstrained::is_var_lt(int v1, int v2) const { + node const& n1 = get_node(v1); + node const& n2 = get_node(v2); + return n1.m_refcount < n2.m_refcount; +} + void elim_unconstrained::eliminate() { @@ -62,34 +68,39 @@ void elim_unconstrained::eliminate() { expr_ref r(m), side_cond(m); int v = m_heap.erase_min(); node& n = get_node(v); - IF_VERBOSE(11, verbose_stream() << mk_pp(n.m_orig, m) << " @ " << n.m_refcount << "\n"); + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(n.m_orig, m) << " @ " << n.m_refcount << "\n"); if (n.m_refcount == 0) continue; if (n.m_refcount > 1) return; - if (n.m_parents.empty()) + if (n.m_parents.empty()) { + --n.m_refcount; continue; + } expr* e = get_parent(v); for (expr* p : n.m_parents) - IF_VERBOSE(11, verbose_stream() << "parent " << mk_pp(p, m) << "\n"); - if (!is_app(e)) - continue; - if (!is_ground(e)) + IF_VERBOSE(11, verbose_stream() << "parent " << mk_bounded_pp(p, m) << "\n"); + if (!e || !is_app(e) || !is_ground(e)) { + --n.m_refcount; continue; + } app* t = to_app(e); m_args.reset(); for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); - if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) + if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) { + --n.m_refcount; continue; + } + --n.m_refcount; SASSERT(r->get_sort() == t->get_sort()); m_stats.m_num_eliminated++; n.m_refcount = 0; SASSERT(r); - m_trail.push_back(r); gc(e); + m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; get_node(e).m_refcount++; IF_VERBOSE(11, verbose_stream() << mk_pp(e, m) << "\n"); @@ -107,10 +118,6 @@ expr* elim_unconstrained::get_parent(unsigned n) const { for (expr* p : get_node(n).m_parents) if (get_node(p).m_refcount > 0 && get_node(p).m_term == get_node(p).m_orig) return p; - IF_VERBOSE(0, verbose_stream() << "term " << mk_pp(get_node(n).m_term, m) << "\n"); - for (expr* p : get_node(n).m_parents) - IF_VERBOSE(0, verbose_stream() << "parent " << mk_pp(p, m) << "\n"); - UNREACHABLE(); return nullptr; } /** @@ -123,6 +130,7 @@ void elim_unconstrained::init_nodes() { m_trail.append(terms); m_heap.reset(); m_frozen.reset(); + m_root.reset(); // initialize nodes for terms in the original goal init_terms(terms); @@ -159,11 +167,13 @@ void elim_unconstrained::init_terms(expr_ref_vector const& terms) { m_nodes.reserve(max_id + 1); m_heap.reserve(max_id + 1); + m_root.reserve(max_id + 1, UINT_MAX); for (expr* e : subterms_postorder::all(terms)) { + m_root.setx(e->get_id(), e->get_id(), UINT_MAX); node& n = get_node(e); if (n.m_term) - continue; + continue; n.m_orig = e; n.m_term = e; n.m_refcount = 0; diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 67d64027bf5..964c56244a2 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -32,7 +32,7 @@ class elim_unconstrained : public dependent_expr_simplifier { elim_unconstrained& s; var_lt(elim_unconstrained& s) : s(s) {} bool operator()(int v1, int v2) const { - return s.get_node(v1).m_refcount < s.get_node(v2).m_refcount; + return s.is_var_lt(v1, v2); } }; struct stats { @@ -47,20 +47,18 @@ class elim_unconstrained : public dependent_expr_simplifier { ptr_vector m_args; expr_mark m_frozen; stats m_stats; + unsigned_vector m_root; - bool operator()(int v1, int v2) const { return get_node(v1).m_refcount < get_node(v2).m_refcount; } - + bool is_var_lt(int v1, int v2) const; node& get_node(unsigned n) { return m_nodes[n]; } node const& get_node(unsigned n) const { return m_nodes[n]; } - node& get_node(expr* t) { return m_nodes[t->get_id()]; } - node const& get_node(expr* t) const { return m_nodes[t->get_id()]; } + node& get_node(expr* t) { return m_nodes[m_root[t->get_id()]]; } + node const& get_node(expr* t) const { return m_nodes[m_root[t->get_id()]]; } unsigned get_refcount(expr* t) const { return get_node(t).m_refcount; } void inc_ref(expr* t) { ++get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.increased(t->get_id()); } void dec_ref(expr* t) { --get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.decreased(t->get_id()); } void gc(expr* t); expr* get_parent(unsigned n) const; - void init_refcounts(); - void dec_refcounts(expr* t); void init_terms(expr_ref_vector const& terms); void init_nodes(); void eliminate(); From ce6cfeaa6873e7facf0fb00ce87c1d24381fd6b3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Nov 2022 18:01:17 -0800 Subject: [PATCH 070/597] fix bug in euf-completion relating to missed normalization --- src/ast/simplifiers/elim_unconstrained.cpp | 7 +++---- src/ast/simplifiers/euf_completion.cpp | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index caeaf295872..3d94cb0ef91 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -75,14 +75,14 @@ void elim_unconstrained::eliminate() { return; if (n.m_parents.empty()) { - --n.m_refcount; + n.m_refcount = 0; continue; } expr* e = get_parent(v); for (expr* p : n.m_parents) IF_VERBOSE(11, verbose_stream() << "parent " << mk_bounded_pp(p, m) << "\n"); if (!e || !is_app(e) || !is_ground(e)) { - --n.m_refcount; + n.m_refcount = 0; continue; } app* t = to_app(e); @@ -90,10 +90,9 @@ void elim_unconstrained::eliminate() { for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) { - --n.m_refcount; + n.m_refcount = 0; continue; } - --n.m_refcount; SASSERT(r->get_sort() == t->get_sort()); m_stats.m_num_eliminated++; n.m_refcount = 0; diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index e1360fa0ba2..0f03d7fe1c8 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -98,7 +98,7 @@ namespace euf { 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"); + IF_VERBOSE(11, 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"); } @@ -174,7 +174,7 @@ namespace euf { bool change = false; for (expr* arg : *to_app(f)) { m_eargs.push_back(get_canonical(arg, d)); - change = arg != m_eargs.back(); + change |= arg != m_eargs.back(); } if (!change) From 3d2bf13577547588cf22a197dbd020324596daa2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Nov 2022 20:30:00 -0800 Subject: [PATCH 071/597] streamline statistics, fix bug in updating goals --- src/ast/simplifiers/elim_unconstrained.cpp | 5 ++++- src/ast/simplifiers/elim_unconstrained.h | 2 +- src/tactic/core/elim_uncnstr_tactic.cpp | 5 +++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 3d94cb0ef91..d0eb2a53f48 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -96,6 +96,7 @@ void elim_unconstrained::eliminate() { SASSERT(r->get_sort() == t->get_sort()); m_stats.m_num_eliminated++; n.m_refcount = 0; + m_trail.push_back(r); SASSERT(r); gc(e); @@ -250,7 +251,9 @@ void elim_unconstrained::reconstruct_terms() { } void elim_unconstrained::assert_normalized(vector& old_fmls) { - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + + unsigned sz = m_fmls.size(); + for (unsigned i = m_qhead; i < sz; ++i) { auto [f, d] = m_fmls[i](); node& n = get_node(f); expr* g = n.m_term; diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 964c56244a2..b32ab367b5a 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -72,7 +72,7 @@ class elim_unconstrained : public dependent_expr_simplifier { void reduce() override; - void collect_statistics(statistics& st) const override { st.update("elim-unconstr", m_stats.m_num_eliminated); } + void collect_statistics(statistics& st) const override { st.update("elim-unconstrained", m_stats.m_num_eliminated); } void reset_statistics() override { m_stats.reset(); } }; diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 5f0963bfda8..60bdd2710a1 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -867,6 +867,8 @@ class elim_uncnstr_tactic : public tactic { void run(goal_ref const & g, goal_ref_buffer & result) { bool produce_proofs = g->proofs_enabled(); TRACE("goal", g->display(tout);); + std::function coll = [&](statistics& st) { collect_statistics(st); }; + statistics_report sreport(coll); tactic_report report("elim-uncnstr", *g); m_vars.reset(); collect_occs p; @@ -959,7 +961,6 @@ class elim_uncnstr_tactic : public tactic { void operator()(goal_ref const & g, goal_ref_buffer & result) override { run(g, result); - report_tactic_progress(":num-elim-apps", m_num_elim_apps); } void cleanup() override { @@ -969,7 +970,7 @@ class elim_uncnstr_tactic : public tactic { } void collect_statistics(statistics & st) const override { - st.update("eliminated applications", m_num_elim_apps); + st.update("elim-unconstrained", m_num_elim_apps); } void reset_statistics() override { From 3f2bbe558948f5c3cbaa5ca77e65c6d8687e8c46 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Nov 2022 08:54:08 -0800 Subject: [PATCH 072/597] harness del_object #6452 Signed-off-by: Nikolaj Bjorner --- src/api/api_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index cef63621d60..2b7a4ce43a5 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -51,6 +51,8 @@ namespace api { } void context::del_object(api::object* o) { + if (!o) + return; #ifndef SINGLE_THREAD if (m_concurrent_dec_ref) { lock_guard lock(m_mux); From 6297c001eed19b11f4871d764c2f93ed1d94d086 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Nov 2022 18:57:16 -0800 Subject: [PATCH 073/597] remove legacy solve_eqs_tactic entirely also, bug fixes to elim_unconstrained (elim_uncnstr2) which is to replace legacy tactic for eliminating unconstrained constants. --- src/ast/simplifiers/dependent_expr_state.h | 5 +- src/ast/simplifiers/elim_unconstrained.cpp | 19 +- src/ast/simplifiers/elim_unconstrained.h | 15 +- src/ast/simplifiers/solve_eqs.cpp | 7 + src/ast/simplifiers/solve_eqs.h | 2 + src/nlsat/tactic/qfnra_nlsat_tactic.cpp | 1 - src/opt/opt_context.cpp | 1 - src/tactic/core/CMakeLists.txt | 2 - src/tactic/core/elim_uncnstr2_tactic.h | 2 +- src/tactic/core/solve_eqs2_tactic.h | 47 - src/tactic/core/solve_eqs_tactic.cpp | 1109 -------------------- src/tactic/core/solve_eqs_tactic.h | 34 +- src/tactic/dependent_expr_state_tactic.h | 5 + 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 | 1 - 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 | 1 - src/tactic/smtlogics/quant_tactics.cpp | 1 - src/tactic/ufbv/ufbv_tactic.cpp | 1 - 23 files changed, 56 insertions(+), 1203 deletions(-) delete mode 100644 src/tactic/core/solve_eqs2_tactic.h delete mode 100644 src/tactic/core/solve_eqs_tactic.cpp diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 6bdd3462697..fa4bbdd498c 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -61,9 +61,9 @@ class dependent_expr_state { */ class dependent_expr_simplifier { protected: - ast_manager& m; + ast_manager& m; dependent_expr_state& m_fmls; - trail_stack& m_trail; + 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(); } @@ -78,6 +78,7 @@ class dependent_expr_simplifier { virtual void collect_statistics(statistics& st) const {} virtual void reset_statistics() {} virtual void updt_params(params_ref const& p) {} + virtual void collect_param_descrs(param_descrs& r) {} }; /** diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index d0eb2a53f48..d590f3e590a 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -47,11 +47,9 @@ Module Name: elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m) { - std::function is_var = [&](expr* e) { - return is_uninterp_const(e) && !m_frozen.is_marked(e) && get_node(e).m_refcount == 1; + return is_uninterp_const(e) && !m_frozen.is_marked(e) && get_node(e).m_refcount <= 1; }; - m_inverter.set_is_var(is_var); } @@ -61,7 +59,6 @@ bool elim_unconstrained::is_var_lt(int v1, int v2) const { return n1.m_refcount < n2.m_refcount; } - void elim_unconstrained::eliminate() { while (!m_heap.empty()) { @@ -79,8 +76,7 @@ void elim_unconstrained::eliminate() { continue; } expr* e = get_parent(v); - for (expr* p : n.m_parents) - IF_VERBOSE(11, verbose_stream() << "parent " << mk_bounded_pp(p, m) << "\n"); + IF_VERBOSE(11, for (expr* p : n.m_parents) verbose_stream() << "parent " << mk_bounded_pp(p, m) << " @ " << get_node(p).m_refcount << "\n";); if (!e || !is_app(e) || !is_ground(e)) { n.m_refcount = 0; continue; @@ -90,6 +86,7 @@ void elim_unconstrained::eliminate() { for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) { + IF_VERBOSE(11, verbose_stream() << "not inverted " << mk_bounded_pp(e, m) << "\n"); n.m_refcount = 0; continue; } @@ -103,12 +100,12 @@ void elim_unconstrained::eliminate() { m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; get_node(e).m_refcount++; - IF_VERBOSE(11, verbose_stream() << mk_pp(e, m) << "\n"); - SASSERT(!m_heap.contains(e->get_id())); + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); + SASSERT(!m_heap.contains(root(e))); if (is_uninterp_const(r)) - m_heap.insert(e->get_id()); + m_heap.insert(root(e)); - IF_VERBOSE(11, verbose_stream() << mk_pp(n.m_orig, m) << " " << mk_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n"); + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(n.m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n";); SASSERT(!side_cond && "not implemented to add side conditions\n"); } @@ -178,7 +175,7 @@ void elim_unconstrained::init_terms(expr_ref_vector const& terms) { n.m_term = e; n.m_refcount = 0; if (is_uninterp_const(e)) - m_heap.insert(e->get_id()); + m_heap.insert(root(e)); if (is_quantifier(e)) { expr* body = to_quantifier(e)->get_expr(); get_node(body).m_parents.push_back(e); diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index b32ab367b5a..89f28fe33e2 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -23,9 +23,9 @@ Module Name: class elim_unconstrained : public dependent_expr_simplifier { struct node { - unsigned m_refcount; - expr* m_term; - expr* m_orig; + unsigned m_refcount = 0; + expr* m_term = nullptr; + expr* m_orig = nullptr; ptr_vector m_parents; }; struct var_lt { @@ -52,11 +52,12 @@ class elim_unconstrained : public dependent_expr_simplifier { bool is_var_lt(int v1, int v2) const; node& get_node(unsigned n) { return m_nodes[n]; } node const& get_node(unsigned n) const { return m_nodes[n]; } - node& get_node(expr* t) { return m_nodes[m_root[t->get_id()]]; } - node const& get_node(expr* t) const { return m_nodes[m_root[t->get_id()]]; } + node& get_node(expr* t) { return m_nodes[root(t)]; } + unsigned root(expr* t) const { return m_root[t->get_id()]; } + node const& get_node(expr* t) const { return m_nodes[root(t)]; } unsigned get_refcount(expr* t) const { return get_node(t).m_refcount; } - void inc_ref(expr* t) { ++get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.increased(t->get_id()); } - void dec_ref(expr* t) { --get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.decreased(t->get_id()); } + void inc_ref(expr* t) { ++get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.increased(root(t)); } + void dec_ref(expr* t) { --get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.decreased(root(t)); } void gc(expr* t); expr* get_parent(unsigned n) const; void init_terms(expr_ref_vector const& terms); diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 96b0427f6af..0b6f1216007 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -240,6 +240,13 @@ namespace euf { ex->updt_params(p); } + void solve_eqs::collect_param_descrs(param_descrs& r) { + r.insert("solve_eqs_max_occs", CPK_UINT, "(default: infty) maximum number of occurrences for considering a variable for gaussian eliminations."); + r.insert("theory_solver", CPK_BOOL, "(default: true) use theory solvers."); + r.insert("ite_solver", CPK_BOOL, "(default: true) use if-then-else solver."); + r.insert("context_solve", CPK_BOOL, "(default: false) solve equalities under disjunctions."); + } + 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 8f5988a3842..a2afd6e5892 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -72,6 +72,8 @@ namespace euf { void updt_params(params_ref const& p) override; + void collect_param_descrs(param_descrs& r) override; + void collect_statistics(statistics& st) const override; }; diff --git a/src/nlsat/tactic/qfnra_nlsat_tactic.cpp b/src/nlsat/tactic/qfnra_nlsat_tactic.cpp index e41347c2bf7..fc812c4f56b 100644 --- a/src/nlsat/tactic/qfnra_nlsat_tactic.cpp +++ b/src/nlsat/tactic/qfnra_nlsat_tactic.cpp @@ -27,7 +27,6 @@ Module Name: #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 66a391cb804..5895643bd06 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -31,7 +31,6 @@ Module Name: #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/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index bab61afce87..6bb6793fd68 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -18,7 +18,6 @@ z3_add_component(core_tactics propagate_values_tactic.cpp reduce_args_tactic.cpp simplify_tactic.cpp - solve_eqs_tactic.cpp special_relations_tactic.cpp split_clause_tactic.cpp symmetry_reduce_tactic.cpp @@ -48,7 +47,6 @@ z3_add_component(core_tactics reduce_args_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/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h index 2c5f81d5e9d..65b5d342698 100644 --- a/src/tactic/core/elim_uncnstr2_tactic.h +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -30,7 +30,7 @@ class elim_uncnstr2_tactic_factory : public dependent_expr_simplifier_factory { }; inline tactic * mk_elim_uncnstr2_tactic(ast_manager & m, params_ref const & p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(elim_uncnstr2_tactic_factory), "elim-unconstr2"); + return alloc(dependent_expr_state_tactic, m, p, alloc(elim_uncnstr2_tactic_factory), "elim-uncnstr2"); } diff --git a/src/tactic/core/solve_eqs2_tactic.h b/src/tactic/core/solve_eqs2_tactic.h deleted file mode 100644 index 7d13b571e11..00000000000 --- a/src/tactic/core/solve_eqs2_tactic.h +++ /dev/null @@ -1,47 +0,0 @@ -/*++ -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 = 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)") -*/ - - diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp deleted file mode 100644 index d445b68ac17..00000000000 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ /dev/null @@ -1,1109 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - solve_eqs_tactic.cpp - -Abstract: - - Tactic for solving equations and performing gaussian elimination. - -Author: - - Leonardo de Moura (leonardo) 2011-12-29. - -Revision History: - ---*/ -#include "ast/rewriter/expr_replacer.h" -#include "ast/occurs.h" -#include "ast/ast_util.h" -#include "ast/ast_pp.h" -#include "ast/pb_decl_plugin.h" -#include "ast/recfun_decl_plugin.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "ast/rewriter/hoist_rewriter.h" -#include "tactic/goal_shared_occs.h" -#include "tactic/tactical.h" -#include "ast/converters/generic_model_converter.h" -#include "params/tactic_params.hpp" - -class solve_eqs_tactic : public tactic { - struct imp { - typedef generic_model_converter gmc; - - ast_manager & m_manager; - expr_replacer * m_r; - params_ref m_params; - bool m_r_owner; - arith_util m_a_util; - obj_map m_num_occs; - unsigned m_num_steps; - unsigned m_num_eliminated_vars; - bool m_theory_solver; - bool m_ite_solver; - unsigned m_max_occs; - bool m_context_solve; - scoped_ptr m_subst; - scoped_ptr m_norm_subst; - expr_sparse_mark m_candidate_vars; - expr_sparse_mark m_candidate_set; - ptr_vector m_candidates; - expr_ref_vector m_marked_candidates; - ptr_vector m_vars; - expr_sparse_mark m_nonzero; - ptr_vector m_ordered_vars; - bool m_produce_proofs; - bool m_produce_unsat_cores; - bool m_produce_models; - - imp(ast_manager & m, params_ref const & p, expr_replacer * r, bool owner): - m_manager(m), - m_r(r), - m_r_owner(r == nullptr || owner), - m_a_util(m), - m_num_steps(0), - m_num_eliminated_vars(0), - m_marked_candidates(m), - m_var_trail(m) { - updt_params(p); - if (m_r == nullptr) - m_r = mk_default_expr_replacer(m, true); - } - - ~imp() { - if (m_r_owner) - dealloc(m_r); - } - - ast_manager & m() const { return m_manager; } - - void updt_params(params_ref const & p) { - m_params.append(p); - 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()); - } - - void checkpoint() { - tactic::checkpoint(m()); - } - - // Check if the number of occurrences of t is below the specified threshold :solve-eqs-max-occs - bool check_occs(expr * t) const { - if (m_max_occs == UINT_MAX) - return true; - unsigned num = 0; - m_num_occs.find(t, num); - TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m_manager) << " num_occs: " << num << " max: " << m_max_occs << "\n";); - return num <= m_max_occs; - } - - // Use: (= x def) and (= def x) - - bool trivial_solve1(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { - - if (is_uninterp_const(lhs) && !m_candidate_vars.is_marked(lhs) && !occurs(lhs, rhs) && check_occs(lhs)) { - var = to_app(lhs); - def = rhs; - pr = nullptr; - return true; - } - else { - return false; - } - } - bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { - if (trivial_solve1(lhs, rhs, var, def, pr)) - return true; - if (trivial_solve1(rhs, lhs, var, def, pr)) { - if (m_produce_proofs) { - pr = m().mk_commutativity(m().mk_eq(lhs, rhs)); - } - return true; - } - return false; - } - - // (ite c (= x t1) (= x t2)) --> (= x (ite c t1 t2)) - bool solve_ite_core(app * ite, expr * lhs1, expr * rhs1, expr * lhs2, expr * rhs2, app_ref & var, expr_ref & def, proof_ref & pr) { - if (lhs1 != lhs2) - return false; - if (!is_uninterp_const(lhs1) || m_candidate_vars.is_marked(lhs1)) - return false; - if (occurs(lhs1, ite->get_arg(0)) || occurs(lhs1, rhs1) || occurs(lhs1, rhs2)) - return false; - if (!check_occs(lhs1)) - return false; - var = to_app(lhs1); - def = m().mk_ite(ite->get_arg(0), rhs1, rhs2); - - if (m_produce_proofs) - pr = m().mk_rewrite(ite, m().mk_eq(var, def)); - return true; - } - - // (ite c (= x t1) (= x t2)) --> (= x (ite c t1 t2)) - bool solve_ite(app * ite, app_ref & var, expr_ref & def, proof_ref & pr) { - expr * t = ite->get_arg(1); - expr * e = ite->get_arg(2); - - if (!m().is_eq(t) || !m().is_eq(e)) - return false; - - expr * lhs1 = to_app(t)->get_arg(0); - expr * rhs1 = to_app(t)->get_arg(1); - expr * lhs2 = to_app(e)->get_arg(0); - expr * rhs2 = to_app(e)->get_arg(1); - - return - solve_ite_core(ite, lhs1, rhs1, lhs2, rhs2, var, def, pr) || - solve_ite_core(ite, rhs1, lhs1, lhs2, rhs2, var, def, pr) || - solve_ite_core(ite, lhs1, rhs1, rhs2, lhs2, var, def, pr) || - solve_ite_core(ite, rhs1, lhs1, rhs2, lhs2, var, def, pr); - } - - bool is_pos_literal(expr * n) { - return is_app(n) && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; - } - - bool is_neg_literal(expr * n) { - if (m_manager.is_not(n)) - return is_pos_literal(to_app(n)->get_arg(0)); - return false; - } - - - /** - \brief Given t of the form (f s_0 ... s_n), - return true if x occurs in some s_j for j != i - */ - bool occurs_except(expr * x, app * t, unsigned i) { - unsigned num = t->get_num_args(); - for (unsigned j = 0; j < num; j++) { - if (i != j && occurs(x, t->get_arg(j))) - return true; - } - return false; - } - - void add_pos(expr* f) { - expr* lhs = nullptr, *rhs = nullptr; - rational val; - if (m_a_util.is_le(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_neg()) { - m_nonzero.mark(lhs); - } - else if (m_a_util.is_ge(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_pos()) { - m_nonzero.mark(lhs); - } - else if (m().is_not(f, f)) { - if (m_a_util.is_le(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && !val.is_neg()) { - m_nonzero.mark(lhs); - } - else if (m_a_util.is_ge(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && !val.is_pos()) { - m_nonzero.mark(lhs); - } - else if (m().is_eq(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_zero()) { - m_nonzero.mark(lhs); - } - } - } - - bool is_nonzero(expr* e) { - return m_nonzero.is_marked(e); - } - - bool isolate_var(app* arg, app_ref& var, expr_ref& div, unsigned i, app* lhs, expr* rhs) { - if (!m_a_util.is_mul(arg)) return false; - unsigned n = arg->get_num_args(); - for (unsigned j = 0; j < n; ++j) { - expr* e = arg->get_arg(j); - bool ok = is_uninterp_const(e) && check_occs(e) && !occurs(e, rhs) && !occurs_except(e, lhs, i); - if (!ok) continue; - var = to_app(e); - for (unsigned k = 0; ok && k < n; ++k) { - expr* arg_k = arg->get_arg(k); - ok = k == j || (!occurs(var, arg_k) && is_nonzero(arg_k)); - } - if (!ok) continue; - ptr_vector args; - for (unsigned k = 0; k < n; ++k) { - if (k != j) args.push_back(arg->get_arg(k)); - } - div = m_a_util.mk_mul(args.size(), args.data()); - return true; - } - return false; - } - - bool solve_nl(app * lhs, expr * rhs, expr* eq, app_ref& var, expr_ref & def, proof_ref & pr) { - SASSERT(m_a_util.is_add(lhs)); - if (m_a_util.is_int(lhs)) return false; - unsigned num = lhs->get_num_args(); - expr_ref div(m()); - for (unsigned i = 0; i < num; i++) { - expr * arg = lhs->get_arg(i); - if (is_app(arg) && isolate_var(to_app(arg), var, div, i, lhs, rhs)) { - ptr_vector args; - for (unsigned k = 0; k < num; ++k) { - if (k != i) args.push_back(lhs->get_arg(k)); - } - def = m_a_util.mk_sub(rhs, m_a_util.mk_add(args.size(), args.data())); - def = m_a_util.mk_div(def, div); - if (m_produce_proofs) - pr = m().mk_rewrite(eq, m().mk_eq(var, def)); - return true; - } - } - return false; - } - - bool solve_arith_core(app * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { - SASSERT(m_a_util.is_add(lhs)); - bool is_int = m_a_util.is_int(lhs); - expr * a = nullptr; - expr * v = nullptr; - rational a_val; - unsigned num = lhs->get_num_args(); - unsigned i; - for (i = 0; i < num; i++) { - expr * arg = lhs->get_arg(i); - if (is_uninterp_const(arg) && !m_candidate_vars.is_marked(arg) && check_occs(arg) && !occurs(arg, rhs) && !occurs_except(arg, lhs, i)) { - a_val = rational(1); - v = arg; - break; - } - else if (m_a_util.is_mul(arg, a, v) && - is_uninterp_const(v) && - !m_candidate_vars.is_marked(v) && - m_a_util.is_numeral(a, a_val) && - !a_val.is_zero() && - (!is_int || a_val.is_minus_one()) && - check_occs(v) && - !occurs(v, rhs) && - !occurs_except(v, lhs, i)) { - break; - } - } - if (i == num) - return false; - var = to_app(v); - expr_ref inv_a(m()); - if (!a_val.is_one()) { - inv_a = m_a_util.mk_numeral(rational(1)/a_val, is_int); - rhs = m_a_util.mk_mul(inv_a, rhs); - } - - ptr_buffer other_args; - for (unsigned j = 0; j < num; j++) { - if (i != j) { - if (inv_a) - other_args.push_back(m_a_util.mk_mul(inv_a, lhs->get_arg(j))); - else - other_args.push_back(lhs->get_arg(j)); - } - } - switch (other_args.size()) { - case 0: - def = rhs; - break; - case 1: - def = m_a_util.mk_sub(rhs, other_args[0]); - break; - default: - def = m_a_util.mk_sub(rhs, m_a_util.mk_add(other_args.size(), other_args.data())); - break; - } - if (m_produce_proofs) - pr = m().mk_rewrite(eq, m().mk_eq(var, def)); - return true; - } - - bool solve_mod(expr * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { - rational r1, r2; - expr* arg1; - if (m_produce_proofs) - return false; - - auto fresh = [&]() { return m().mk_fresh_const("mod", m_a_util.mk_int()); }; - auto mk_int = [&](rational const& r) { return m_a_util.mk_int(r); }; - auto add = [&](expr* a, expr* b) { return m_a_util.mk_add(a, b); }; - auto mul = [&](expr* a, expr* b) { return m_a_util.mk_mul(a, b); }; - - VERIFY(m_a_util.is_mod(lhs, lhs, arg1)); - if (!m_a_util.is_numeral(arg1, r1) || !r1.is_pos()) { - return false; - } - // - // solve lhs mod r1 = r2 - // as lhs = r1*mod!1 + r2 - // - if (m_a_util.is_numeral(rhs, r2) && !r2.is_neg() && r2 < r1) { - expr_ref def0(m()); - def0 = add(mk_int(r2), mul(fresh(), mk_int(r1))); - return solve_eq(lhs, def0, eq, var, def, pr); - } - return false; - } - - bool solve_arith(expr * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { - return - (m_a_util.is_add(lhs) && solve_arith_core(to_app(lhs), rhs, eq, var, def, pr)) || - (m_a_util.is_add(rhs) && solve_arith_core(to_app(rhs), lhs, eq, var, def, pr)) || - (m_a_util.is_mod(lhs) && solve_mod(lhs, rhs, eq, var, def, pr)) || - (m_a_util.is_mod(rhs) && solve_mod(rhs, lhs, eq, var, def, pr)); - } - - - bool solve_eq(expr* arg1, expr* arg2, expr* eq, app_ref& var, expr_ref & def, proof_ref& pr) { - if (trivial_solve(arg1, arg2, var, def, pr)) - return true; - if (m_theory_solver) { - if (solve_arith(arg1, arg2, eq, var, def, pr)) - return true; - } - return false; - } - - bool solve(expr * f, app_ref & var, expr_ref & def, proof_ref & pr) { - expr* arg1 = nullptr, *arg2 = nullptr; - if (m().is_eq(f, arg1, arg2)) { - return solve_eq(arg1, arg2, f, var, def, pr); - } - - if (m_ite_solver && m().is_ite(f)) - return solve_ite(to_app(f), var, def, pr); - - if (is_pos_literal(f)) { - if (m_candidate_vars.is_marked(f)) - return false; - var = to_app(f); - def = m().mk_true(); - if (m_produce_proofs) { - // [rewrite]: (iff (iff l true) l) - // [symmetry T1]: (iff l (iff l true)) - pr = m().mk_rewrite(m().mk_eq(var, def), var); - pr = m().mk_symmetry(pr); - } - TRACE("solve_eqs_bug2", tout << "eliminating: " << mk_ismt2_pp(f, m()) << "\n";); - return true; - } - - if (is_neg_literal(f)) { - var = to_app(to_app(f)->get_arg(0)); - if (m_candidate_vars.is_marked(var)) - return false; - def = m().mk_false(); - if (m_produce_proofs) { - // [rewrite]: (iff (iff l false) ~l) - // [symmetry T1]: (iff ~l (iff l false)) - pr = m().mk_rewrite(m().mk_eq(var, def), f); - pr = m().mk_symmetry(pr); - } - return true; - } - - return false; - } - - void insert_solution(goal const& g, unsigned idx, expr* f, app* var, expr* def, proof* pr) { - - if (!is_safe(var)) - return; - m_vars.push_back(var); - m_candidates.push_back(f); - m_candidate_set.mark(f); - m_candidate_vars.mark(var); - m_marked_candidates.push_back(f); - if (m_produce_proofs) { - if (!pr) - pr = g.pr(idx); - 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)); - } - - /** - \brief Start collecting candidates - */ - void collect(goal const & g) { - m_subst->reset(); - m_norm_subst->reset(); - m_r->set_substitution(nullptr); - m_candidate_vars.reset(); - m_candidate_set.reset(); - m_candidates.reset(); - m_marked_candidates.reset(); - m_vars.reset(); - m_nonzero.reset(); - app_ref var(m()); - expr_ref def(m()); - proof_ref pr(m()); - unsigned size = g.size(); - for (unsigned idx = 0; idx < size; idx++) { - add_pos(g.form(idx)); - } - for (unsigned idx = 0; idx < size; idx++) { - checkpoint(); - expr * f = g.form(idx); - pr = nullptr; - if (solve(f, var, def, pr)) { - insert_solution(g, idx, f, var, def, pr); - } - m_num_steps++; - } - - TRACE("solve_eqs", - tout << "candidate vars:\n"; - for (app* v : m_vars) { - tout << mk_ismt2_pp(v, m()) << " "; - } - tout << "\n";); - } - - struct nnf_context { - bool m_is_and; - expr_ref_vector m_args; - unsigned m_index; - nnf_context(bool is_and, expr_ref_vector const& args, unsigned idx): - m_is_and(is_and), - m_args(args), - m_index(idx) - {} - }; - - ptr_vector m_todo; - void mark_occurs(expr_mark& occ, goal const& g, expr* v) { - 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; - expr_ref_vector m_var_trail; - - bool is_compatible(goal const& g, unsigned idx, vector const & path, expr* v, expr* eq) { - if (m_compatible_tried.is_marked(v)) - return false; - m_compatible_tried.mark(v); - m_var_trail.push_back(v); - expr_mark occ; - svector cache; - mark_occurs(occ, g, v); - return is_goal_compatible(g, occ, cache, idx, v, eq) && is_path_compatible(occ, cache, path, v, eq); - } - - bool is_goal_compatible(goal const& g, expr_mark& occ, svector& cache, unsigned idx, expr* v, expr* eq) { - bool all_e = false; - for (unsigned j = 0; j < g.size(); ++j) { - if (j != idx && !check_eq_compat_rec(occ, cache, g.form(j), v, eq, all_e)) { - TRACE("solve_eqs", tout << "occurs goal " << mk_pp(eq, m()) << "\n";); - return false; - } - } - return true; - } - - // - // all_e := all disjunctions contain eq - // - // or, all_e -> skip if all disjunctions contain eq - // or, all_e -> fail if some disjunction contains v but not eq - // or, all_e -> all_e := false if some disjunction does not contain v - // and, all_e -> all_e - // - - bool is_path_compatible(expr_mark& occ, svector& cache, vector const & path, expr* v, expr* eq) { - bool all_e = true; - auto is_marked = [&](expr* e) { - if (occ.is_marked(e)) - return true; - if (m().is_not(e, e) && occ.is_marked(e)) - return true; - return false; - }; - for (unsigned i = path.size(); i-- > 0; ) { - auto const& p = path[i]; - auto const& args = p.m_args; - if (p.m_is_and && !all_e) { - for (unsigned j = 0; j < args.size(); ++j) { - if (j != p.m_index && is_marked(args[j])) { - TRACE("solve_eqs", tout << "occurs and " << mk_pp(eq, m()) << " " << mk_pp(args[j], m()) << "\n";); - return false; - } - } - } - else if (!p.m_is_and) { - for (unsigned j = 0; j < args.size(); ++j) { - if (j != p.m_index) { - if (occurs(v, args[j])) { - if (!check_eq_compat_rec(occ, cache, args[j], v, eq, all_e)) { - TRACE("solve_eqs", tout << "occurs or " << mk_pp(eq, m()) << " " << mk_pp(args[j], m()) << "\n";); - return false; - } - } - else { - all_e = false; - } - } - } - } - } - return true; - } - - bool check_eq_compat_rec(expr_mark& occ, svector& cache, expr* f, expr* v, expr* eq, bool& all) { - expr_ref_vector args(m()); - expr* f1 = nullptr; - // flattening may introduce fresh negations, - // occ is not defined on these negations - if (!m().is_not(f) && !occ.is_marked(f)) { - all = false; - return true; - } - unsigned idx = f->get_id(); - if (cache.size() > idx && cache[idx] != l_undef) { - return cache[idx] == l_true; - } - if (m().is_not(f, f1) && m().is_or(f1)) { - flatten_and(f, args); - for (expr* arg : args) { - if (arg == eq) { - cache.reserve(idx+1, l_undef); - cache[idx] = l_true; - return true; - } - } - } - else if (m().is_or(f)) { - flatten_or(f, args); - } - else { - return false; - } - - for (expr* arg : args) { - if (!check_eq_compat_rec(occ, cache, arg, v, eq, all)) { - cache.reserve(idx+1, l_undef); - cache[idx] = l_false; - return false; - } - } - cache.reserve(idx+1, l_undef); - cache[idx] = l_true; - return true; - } - - void hoist_nnf(goal const& g, expr* f, vector & path, unsigned idx, unsigned depth, ast_mark& mark) { - if (depth > 3 || mark.is_marked(f)) { - return; - } - mark.mark(f, true); - checkpoint(); - app_ref var(m()); - expr_ref def(m()); - proof_ref pr(m()); - expr_ref_vector args(m()); - expr* f1 = nullptr; - - if (m().is_not(f, f1) && m().is_or(f1)) { - flatten_and(f, args); - for (unsigned i = 0; i < args.size(); ++i) { - pr = nullptr; - 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 { - IF_VERBOSE(10000, - verbose_stream() << "eq not solved " << mk_pp(arg, m()) << "\n"; - verbose_stream() << is_uninterp_const(lhs) << " " << !m_candidate_vars.is_marked(lhs) << " " - << !occurs(lhs, rhs) << " " << check_occs(lhs) << "\n";); - } - } - else { - path.push_back(nnf_context(true, args, i)); - hoist_nnf(g, arg, path, idx, depth + 1, mark); - path.pop_back(); - } - } - } - else if (m().is_or(f)) { - flatten_or(f, args); - for (unsigned i = 0; i < args.size(); ++i) { - path.push_back(nnf_context(false, args, i)); - hoist_nnf(g, args.get(i), path, idx, depth + 1, mark); - path.pop_back(); - } - } - } - - void collect_hoist(goal const& g) { - unsigned size = g.size(); - ast_mark mark; - vector path; - for (unsigned idx = 0; idx < size; idx++) { - checkpoint(); - hoist_nnf(g, g.form(idx), path, idx, 0, mark); - } - } - - void distribute_and_or(goal & g) { - if (m_produce_proofs) - return; - unsigned size = g.size(); - hoist_rewriter_star rw(m(), m_params); - th_rewriter thrw(m(), m_params); - expr_ref tmp(m()), tmp2(m()); - - TRACE("solve_eqs", g.display(tout);); - for (unsigned idx = 0; !g.inconsistent() && idx < size; idx++) { - checkpoint(); - if (g.is_decided_unsat()) break; - expr* f = g.form(idx); - proof_ref pr1(m()), pr2(m()); - thrw(f, tmp, pr1); - rw(tmp, tmp2, pr2); - TRACE("solve_eqs", tout << mk_pp(f, m()) << "\n->\n" << tmp << "\n->\n" << tmp2 - << "\n" << pr1 << "\n" << pr2 << "\n" << mk_pp(g.pr(idx), m()) << "\n";); - pr1 = m().mk_transitivity(pr1, pr2); - if (!pr1) pr1 = g.pr(idx); else pr1 = m().mk_modus_ponens(g.pr(idx), pr1); - g.update(idx, tmp2, pr1, g.dep(idx)); - } - } - - expr_mark m_unsafe_vars; - - void 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); - } - - bool is_safe(expr* f) { - return !m_unsafe_vars.is_marked(f); - } - - void sort_vars() { - SASSERT(m_candidates.size() == m_vars.size()); - TRACE("solve_eqs_bug", tout << "sorting vars...\n";); - m_ordered_vars.reset(); - - - // The variables (and its definitions) in m_subst must remain alive until the end of this procedure. - // Reason: they are scheduled for unmarking in visiting/done. - // They should remain alive while they are on the stack. - // To make sure this is the case, whenever a variable (and its definition) is removed from m_subst, - // I add them to the saved vector. - - expr_ref_vector saved(m()); - - expr_fast_mark1 visiting; - expr_fast_mark2 done; - - typedef std::pair frame; - svector todo; - unsigned num = 0; - for (app* v : m_vars) { - checkpoint(); - if (!m_candidate_vars.is_marked(v)) - continue; - todo.push_back(frame(v, 0)); - while (!todo.empty()) { - start: - frame & fr = todo.back(); - expr * t = fr.first; - m_num_steps++; - TRACE("solve_eqs_bug", tout << "processing:\n" << mk_ismt2_pp(t, m()) << "\n";); - if (t->get_ref_count() > 1 && done.is_marked(t)) { - todo.pop_back(); - continue; - } - switch (t->get_kind()) { - case AST_VAR: - todo.pop_back(); - break; - case AST_QUANTIFIER: - num = to_quantifier(t)->get_num_children(); - while (fr.second < num) { - expr * c = to_quantifier(t)->get_child(fr.second); - fr.second++; - if (c->get_ref_count() > 1 && done.is_marked(c)) - continue; - todo.push_back(frame(c, 0)); - goto start; - } - if (t->get_ref_count() > 1) - done.mark(t); - todo.pop_back(); - break; - case AST_APP: - num = to_app(t)->get_num_args(); - if (num == 0) { - if (fr.second == 0) { - if (m_candidate_vars.is_marked(t)) { - if (visiting.is_marked(t)) { - // cycle detected: remove t - visiting.reset_mark(t); - m_candidate_vars.mark(t, false); - SASSERT(!m_candidate_vars.is_marked(t)); - - // Must save t and its definition. - // See comment in the beginning of the function - expr * def = nullptr; - proof * pr; - expr_dependency * dep; - m_subst->find(to_app(t), def, pr, dep); - SASSERT(def != 0); - saved.push_back(t); - saved.push_back(def); - // - - m_subst->erase(t); - } - else { - visiting.mark(t); - fr.second = 1; - expr * def = nullptr; - proof * pr; - expr_dependency * dep; - m_subst->find(to_app(t), def, pr, dep); - SASSERT(def != 0); - todo.push_back(frame(def, 0)); - goto start; - } - } - } - else { - SASSERT(fr.second == 1); - if (m_candidate_vars.is_marked(t)) { - visiting.reset_mark(t); - m_ordered_vars.push_back(to_app(t)); - } - else { - // var was removed from the list of candidate vars to elim cycle - // do nothing - } - } - } - else { - while (fr.second < num) { - expr * arg = to_app(t)->get_arg(fr.second); - fr.second++; - if (arg->get_ref_count() > 1 && done.is_marked(arg)) - continue; - todo.push_back(frame(arg, 0)); - goto start; - } - } - if (t->get_ref_count() > 1) - done.mark(t); - todo.pop_back(); - break; - default: - UNREACHABLE(); - todo.pop_back(); - break; - } - } - } - - // cleanup - unsigned idx = 0; - for (expr* v : m_vars) { - if (!m_candidate_vars.is_marked(v)) { - m_candidate_set.mark(m_candidates[idx], false); - m_marked_candidates.push_back(m_candidates[idx]); - m_marked_candidates.push_back(v); - } - ++idx; - } - - IF_VERBOSE(10000, - verbose_stream() << "ordered vars: "; - for (app* v : m_ordered_vars) verbose_stream() << mk_pp(v, m()) << " "; - verbose_stream() << "\n";); - TRACE("solve_eqs", - tout << "ordered vars:\n"; - for (app* v : m_ordered_vars) { - SASSERT(m_candidate_vars.is_marked(v)); - tout << mk_ismt2_pp(v, m()) << " "; - } - tout << "\n";); - m_candidate_vars.reset(); - } - - void normalize() { - m_norm_subst->reset(); - m_r->set_substitution(m_norm_subst.get()); - - - expr_dependency_ref new_dep(m()); - for (app * v : m_ordered_vars) { - checkpoint(); - expr_ref new_def(m()); - proof_ref new_pr(m()); - expr * def = nullptr; - proof * pr = nullptr; - expr_dependency * dep = nullptr; - m_subst->find(v, def, pr, dep); - SASSERT(def); - m_r->operator()(def, new_def, new_pr, new_dep); - m_num_steps += m_r->get_num_steps() + 1; - if (m_produce_proofs) - new_pr = m().mk_transitivity(pr, new_pr); - new_dep = m().mk_join(dep, new_dep); - m_norm_subst->insert(v, new_def, new_pr, new_dep); - // we updated the substituting, but we don't need to reset m_r - // because all cached values there do not depend on v. - } - m_subst->reset(); - TRACE("solve_eqs", - tout << "after normalizing variables\n"; - for (expr * v : m_ordered_vars) { - expr * def = 0; - proof * pr = 0; - expr_dependency * dep = 0; - m_norm_subst->find(v, def, pr, dep); - tout << mk_ismt2_pp(v, m()) << "\n----->\n" << mk_ismt2_pp(def, m()) << "\n\n"; - }); - } - - void substitute(goal & g) { - // force the cache of m_r to be reset. - m_r->set_substitution(m_norm_subst.get()); - - expr_ref new_f(m()); - proof_ref new_pr(m()); - expr_dependency_ref new_dep(m()); - unsigned size = g.size(); - for (unsigned idx = 0; idx < size; idx++) { - checkpoint(); - expr * f = g.form(idx); - TRACE("gaussian_leak", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); - if (m_candidate_set.is_marked(f)) { - m_marked_candidates.push_back(f); - // f may be deleted after the following update. - // so, we must remove the mark before doing the update - m_candidate_set.mark(f, false); - SASSERT(!m_candidate_set.is_marked(f)); - g.update(idx, m().mk_true(), m().mk_true_proof(), nullptr); - m_num_steps ++; - continue; - } - - m_r->operator()(f, new_f, new_pr, new_dep); - - TRACE("solve_eqs_subst", tout << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n";); - m_num_steps += m_r->get_num_steps() + 1; - if (m_produce_proofs) - new_pr = m().mk_modus_ponens(g.pr(idx), new_pr); - if (m_produce_unsat_cores) - new_dep = m().mk_join(g.dep(idx), new_dep); - - g.update(idx, new_f, new_pr, new_dep); - if (g.inconsistent()) - return; - } - g.elim_true(); - TRACE("solve_eqs", g.display(tout << "after applying substitution\n");); -#if 0 - DEBUG_CODE({ - for (expr* v : m_ordered_vars) { - for (unsigned j = 0; j < g.size(); j++) { - CASSERT("solve_eqs_bug", !occurs(v, g.form(j))); - } - }}); -#endif - } - - void save_elim_vars(model_converter_ref & mc) { - IF_VERBOSE(100, if (!m_ordered_vars.empty()) verbose_stream() << "num. eliminated vars: " << m_ordered_vars.size() << "\n";); - m_num_eliminated_vars += m_ordered_vars.size(); - if (m_produce_models) { - if (!mc.get()) - mc = alloc(gmc, m(), "solve-eqs"); - for (app* v : m_ordered_vars) - static_cast(mc.get())->add(v, m_norm_subst->find(v)); - } - } - - void collect_num_occs(expr * t, expr_fast_mark1 & visited) { - ptr_buffer stack; - - auto visit = [&](expr* arg) { - if (is_uninterp_const(arg)) { - m_num_occs.insert_if_not_there(arg, 0)++; - } - if (!visited.is_marked(arg) && is_app(arg)) { - visited.mark(arg, true); - stack.push_back(to_app(arg)); - } - }; - - visit(t); - - while (!stack.empty()) { - app * t = stack.back(); - stack.pop_back(); - for (expr* arg : *t) - visit(arg); - } - } - - void collect_num_occs(goal const & g) { - if (m_max_occs == UINT_MAX) - return; // no need to compute num occs - m_num_occs.reset(); - expr_fast_mark1 visited; - unsigned sz = g.size(); - for (unsigned i = 0; i < sz; i++) - collect_num_occs(g.form(i), visited); - } - - unsigned get_num_steps() const { - return m_num_steps; - } - - 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 - // approximates the dependencies between variables. Then apply - // simplification on top of this sorting, so that it can apply sub-quadratic - // equality and unit propagation. - // - 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(); - m_produce_proofs = g->proofs_enabled(); - m_produce_unsat_cores = g->unsat_core_enabled(); - - if (!g->inconsistent()) { - m_subst = alloc(expr_substitution, m(), m_produce_unsat_cores, m_produce_proofs); - m_norm_subst = alloc(expr_substitution, m(), m_produce_unsat_cores, m_produce_proofs); - unsigned rounds = 0; - - filter_unsafe_vars(); - while (rounds < 20) { - ++rounds; - if (!m_produce_proofs && m_context_solve && rounds < 3) { - distribute_and_or(*(g.get())); - } - collect_num_occs(*g); - collect(*g); - if (!m_produce_proofs && m_context_solve && rounds < 3) { - collect_hoist(*g); - } - if (m_subst->empty()) { - break; - } - sort_vars(); - if (m_ordered_vars.empty()) { - break; - } - normalize(); - substitute(*(g.get())); - if (g->inconsistent()) { - break; - } - save_elim_vars(mc); - TRACE("solve_eqs_round", g->display(tout); if (mc) mc->display(tout);); - if (rounds > 10 && m_ordered_vars.size() == 1) - break; - } - } - g->inc_depth(); - g->add(mc.get()); - result.push_back(g.get()); - - - } - }; - - imp * m_imp; -public: - solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replacer * r, bool owner) { - m_imp = alloc(imp, m, p, r, owner); - } - - tactic * translate(ast_manager & m) override { - return alloc(solve_eqs_tactic, m, m_imp->m_params, mk_expr_simp_replacer(m, m_imp->m_params), true); - } - - ~solve_eqs_tactic() override { - dealloc(m_imp); - } - - char const* name() const override { return "solve_eqs"; } - - void updt_params(params_ref const & p) override { - m_imp->updt_params(p); - } - - void collect_param_descrs(param_descrs & r) override { - r.insert("solve_eqs_max_occs", CPK_UINT, "(default: infty) maximum number of occurrences for considering a variable for gaussian eliminations."); - r.insert("theory_solver", CPK_BOOL, "(default: true) use theory solvers."); - r.insert("ite_solver", CPK_BOOL, "(default: true) use if-then-else solver."); - r.insert("context_solve", CPK_BOOL, "(default: false) solve equalities under disjunctions."); - } - - void operator()(goal_ref const & in, - goal_ref_buffer & result) override { - (*m_imp)(in, result); - } - - void cleanup() override { - unsigned num_elim_vars = m_imp->m_num_eliminated_vars; - ast_manager & m = m_imp->m(); - expr_replacer * r = m_imp->m_r; - if (r) - r->set_substitution(nullptr); - bool owner = m_imp->m_r_owner; - m_imp->m_r_owner = false; // stole replacer - - imp * d = alloc(imp, m, m_imp->m_params, r, owner); - d->m_num_eliminated_vars = num_elim_vars; - std::swap(d, m_imp); - dealloc(d); - } - - void collect_statistics(statistics & st) const override { - m_imp->collect_statistics(st); - } - - void reset_statistics() override { - m_imp->m_num_eliminated_vars = 0; - } - -}; - -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 7b97172a3b9..fa095aad0c3 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -1,37 +1,47 @@ /*++ -Copyright (c) 2011 Microsoft Corporation +Copyright (c) 2022 Microsoft Corporation Module Name: - solve_eqs_tactic.h + solve_eqs2_tactic.h Abstract: - Tactic for solving equations and performing gaussian elimination. + Tactic for solving variables Author: - Leonardo de Moura (leonardo) 2011-12-29. - -Revision History: + Nikolaj Bjorner (nbjorner) 2022-10-30 --*/ #pragma once #include "util/params.h" -class ast_manager; -class tactic; +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/solve_eqs.h" + -tactic * mk_solve_eqs1_tactic(ast_manager & m, params_ref const & p = params_ref()); +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); + } +}; -#if 0 +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_eqs1_tactic(m, p); + return mk_solve_eqs2_tactic(m, p); } #endif + /* - ADD_TACTIC("solve-eqs", "eliminate variables by solving equations.", "mk_solve_eqs1_tactic(m, p)") + ADD_TACTIC("solve-eqs", "solve for variables.", "mk_solve_eqs2_tactic(m, p)") */ diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 4190378396b..aaf744d5fbf 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -81,6 +81,11 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { m_simp->updt_params(m_params); } + void collect_param_descrs(param_descrs& r) override { + init(); + m_simp->collect_param_descrs(r); + } + tactic * translate(ast_manager & m) override { return alloc(dependent_expr_state_tactic, m, m_params, m_factory.get(), name()); } diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index a09da60a973..e631c23e986 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -18,7 +18,6 @@ Module Name: --*/ #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 6d44addf028..acad15fd671 100644 --- a/src/tactic/smtlogics/qfaufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfaufbv_tactic.cpp @@ -17,7 +17,6 @@ Module Name: --*/ #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 9ca6b70ef33..2f1879d5878 100644 --- a/src/tactic/smtlogics/qfauflia_tactic.cpp +++ b/src/tactic/smtlogics/qfauflia_tactic.cpp @@ -21,7 +21,6 @@ Module Name: #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 90df4fe4276..07784eb3b5d 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -20,7 +20,6 @@ Module Name: #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" diff --git a/src/tactic/smtlogics/qfidl_tactic.cpp b/src/tactic/smtlogics/qfidl_tactic.cpp index 5c1ba5f4430..c86789ed00d 100644 --- a/src/tactic/smtlogics/qfidl_tactic.cpp +++ b/src/tactic/smtlogics/qfidl_tactic.cpp @@ -21,7 +21,6 @@ Module Name: #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 d116414eade..b8ebbd8a916 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -22,7 +22,6 @@ Module Name: #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 d9f723d67da..609107a4829 100644 --- a/src/tactic/smtlogics/qfuf_tactic.cpp +++ b/src/tactic/smtlogics/qfuf_tactic.cpp @@ -21,7 +21,6 @@ Module Name: #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 98e09b56af2..6ba3e8cc9a6 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -21,7 +21,6 @@ Module Name: #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" diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index 3bf6b658d7e..daf020a14e5 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -20,7 +20,6 @@ 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 728e6397da6..e8495c01354 100644 --- a/src/tactic/ufbv/ufbv_tactic.cpp +++ b/src/tactic/ufbv/ufbv_tactic.cpp @@ -20,7 +20,6 @@ Module Name: #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 95e07ffe8e96ed18fda51f0ccdabfbe5f32d8b68 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Nov 2022 19:14:51 -0800 Subject: [PATCH 074/597] disable unsound context equality solving Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_eqs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 0b6f1216007..18928dca8bb 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -197,7 +197,7 @@ namespace euf { if (!m.inc()) return; - if (m_config.m_context_solve) { + if (m_config.m_context_solve && false) { old_fmls.reset(); m_subst_ids.reset(); eqs.reset(); From 3eeb59db34172f673d4ea54c1ec9ca191997fb5e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Nov 2022 19:23:27 -0800 Subject: [PATCH 075/597] fix #6451 missing occurrence marking when there is an unsafe equality already Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_context_eqs.cpp | 11 +++++++++-- src/ast/simplifiers/solve_eqs.cpp | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index 37836db271c..1ccab0f6c1f 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -154,10 +154,12 @@ namespace euf { return e1.var->get_id() < e2.var->get_id(); }); unsigned j = 0; expr* last_var = nullptr; + bool was_unsafe = false; for (auto const& eq : eqs) { SASSERT(!m.is_bool(eq.var)); + if (eq.var != last_var) { m_contains_v.reset(); @@ -167,8 +169,11 @@ namespace euf { 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)) + was_unsafe = false; + if (m_contains_v.is_marked(eq.term)) { + was_unsafe = true; continue; + } // then mark occurrences for (unsigned i = 0; i < m_fmls.size(); ++i) @@ -178,7 +183,9 @@ namespace euf { } else if (m_contains_v.is_marked(eq.term)) continue; - + else if (was_unsafe) + continue; + // subject to occurrences, check if equality is safe if (is_safe_eq(eq.orig)) eqs[j++] = eq; diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 18928dca8bb..0b6f1216007 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -197,7 +197,7 @@ namespace euf { if (!m.inc()) return; - if (m_config.m_context_solve && false) { + if (m_config.m_context_solve) { old_fmls.reset(); m_subst_ids.reset(); eqs.reset(); From 48c0f8694facce9a645cf81beee2a9a7449450da Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Nov 2022 20:01:00 -0800 Subject: [PATCH 076/597] euf-completion bug fix, streamline name to solve_eqs --- src/ast/simplifiers/euf_completion.cpp | 3 ++- src/tactic/core/solve_eqs_tactic.h | 15 ++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 0f03d7fe1c8..7c27a01bb92 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -90,7 +90,8 @@ namespace euf { return; } - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + unsigned sz = m_fmls.size(); + for (unsigned i = m_qhead; i < sz; ++i) { auto [f, d] = m_fmls[i](); expr_dependency_ref dep(d, m); diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index fa095aad0c3..188f37f3414 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -22,26 +22,19 @@ Module Name: #include "ast/simplifiers/solve_eqs.h" -class solve_eqs2_tactic_factory : public dependent_expr_simplifier_factory { +class solve_eqs_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 = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs2_tactic_factory), "solve-eqs"); +inline tactic * mk_solve_eqs_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs_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-eqs", "solve for variables.", "mk_solve_eqs2_tactic(m, p)") + ADD_TACTIC("solve-eqs", "solve for variables.", "mk_solve_eqs_tactic(m, p)") */ From 041b5f9ef01be585c27796dda4f892e272b089b0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Nov 2022 20:01:37 -0800 Subject: [PATCH 077/597] rename away solve_eqs2 to solve_eqs Signed-off-by: Nikolaj Bjorner --- src/tactic/core/solve_eqs_tactic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 188f37f3414..5d6da2e9a1e 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -3,7 +3,7 @@ Copyright (c) 2022 Microsoft Corporation Module Name: - solve_eqs2_tactic.h + solve_eqs_tactic.h Abstract: From bfae8b216278d74cc69d5a415527380d7a50fd75 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 15 Nov 2022 05:47:28 -0800 Subject: [PATCH 078/597] set flat_and_or to false in bv rewriter --- src/smt/theory_bv.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 922eafcf6d7..adcafb2e4fa 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1489,6 +1489,7 @@ namespace smt { m_approximates_large_bvs(false) { memset(m_eq_activity, 0, sizeof(m_eq_activity)); memset(m_diseq_activity, 0, sizeof(m_diseq_activity)); + m_bb.set_flat_and_or(false); } theory_bv::~theory_bv() { From 9845c33236e944c3435b7303267d3acc5fc7b8e9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 15 Nov 2022 09:13:13 -0800 Subject: [PATCH 079/597] add shortcuts in rewriter, eliminate redundancies in dependent_expr tactic --- src/ast/rewriter/bool_rewriter.cpp | 12 +++-- src/ast/rewriter/bv_rewriter.cpp | 4 +- src/ast/rewriter/th_rewriter.cpp | 57 +++++++++++++++--------- src/ast/rewriter/th_rewriter.h | 1 + src/tactic/dependent_expr_state_tactic.h | 1 + 5 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 632b6c0f606..1e0b6318aea 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -686,6 +686,10 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) app* bool_rewriter::mk_eq(expr* lhs, expr* rhs) { + if (m().are_distinct(lhs, rhs)) + return m().mk_false(); + if (lhs == rhs) + return m().mk_true(); // degrades simplification // if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); return m().mk_eq(lhs, rhs); @@ -785,7 +789,7 @@ br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args if (num_args == 2) { expr_ref tmp(m()); - result = m().mk_not(mk_eq(args[0], args[1])); + result = mk_not(mk_eq(args[0], args[1])); return BR_REWRITE2; // mk_eq may be dispatched to other rewriters. } @@ -827,7 +831,7 @@ br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args ptr_buffer new_diseqs; for (unsigned i = 0; i < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) - new_diseqs.push_back(m().mk_not(mk_eq(args[i], args[j]))); + new_diseqs.push_back(mk_not(mk_eq(args[i], args[j]))); } result = m().mk_and(new_diseqs); return BR_REWRITE3; @@ -937,13 +941,13 @@ br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & re if (m().is_not(t, t1) && m().is_eq(t1, t1, t2) && e == t1) { expr_ref a(m()); mk_and(c, t2, a); - result = m().mk_not(m().mk_eq(t1, a)); + result = mk_not(mk_eq(t1, a)); return BR_REWRITE3; } if (m().is_not(t, t1) && m().is_eq(t1, t2, t1) && e == t1) { expr_ref a(m()); mk_and(c, t2, a); - result = m().mk_eq(t1, a); + result = mk_eq(t1, a); return BR_REWRITE3; } #endif diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 209f7a13b16..c594233102e 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -19,6 +19,7 @@ Module Name: #include "params/bv_rewriter_params.hpp" #include "ast/rewriter/bv_rewriter.h" #include "ast/rewriter/poly_rewriter_def.h" +#include "ast/rewriter/bool_rewriter.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_lt.h" @@ -2386,7 +2387,8 @@ br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { expr* a = nullptr, *b = nullptr, *c = nullptr; if (m().is_ite(lhs, a, b, c)) { - result = m().mk_ite(a, m().mk_eq(b, rhs), m().mk_eq(c, rhs)); + bool_rewriter rw(m()); + result = rw.mk_ite(a, rw.mk_eq(b, rhs), rw.mk_eq(c, rhs)); return BR_REWRITE2; } diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 9604f6d16a3..4c7d4dc4921 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -183,22 +183,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { if (k == OP_EQ) { // theory dispatch for = SASSERT(num == 2); - family_id s_fid = args[0]->get_sort()->get_family_id(); - if (s_fid == m_a_rw.get_fid()) - st = m_a_rw.mk_eq_core(args[0], args[1], result); - else if (s_fid == m_bv_rw.get_fid()) - st = m_bv_rw.mk_eq_core(args[0], args[1], result); - else if (s_fid == m_dt_rw.get_fid()) - st = m_dt_rw.mk_eq_core(args[0], args[1], result); - else if (s_fid == m_f_rw.get_fid()) - st = m_f_rw.mk_eq_core(args[0], args[1], result); - else if (s_fid == m_ar_rw.get_fid()) - st = m_ar_rw.mk_eq_core(args[0], args[1], result); - else if (s_fid == m_seq_rw.get_fid()) - st = m_seq_rw.mk_eq_core(args[0], args[1], result); - if (st != BR_FAILED) - return st; - st = apply_tamagotchi(args[0], args[1], result); + st = reduce_eq(args[0], args[1], result); if (st != BR_FAILED) return st; } @@ -695,9 +680,35 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args) { expr_ref result(m()); proof_ref pr(m()); - if (BR_FAILED == reduce_app(f, num_args, args, result, pr)) { + if (BR_FAILED == reduce_app(f, num_args, args, result, pr)) result = m().mk_app(f, num_args, args); - } + return result; + } + + br_status reduce_eq(expr* a, expr* b, expr_ref& result) { + family_id s_fid = a->get_sort()->get_family_id(); + br_status st = BR_FAILED; + if (s_fid == m_a_rw.get_fid()) + st = m_a_rw.mk_eq_core(a, b, result); + else if (s_fid == m_bv_rw.get_fid()) + st = m_bv_rw.mk_eq_core(a, b, result); + else if (s_fid == m_dt_rw.get_fid()) + st = m_dt_rw.mk_eq_core(a, b, result); + else if (s_fid == m_f_rw.get_fid()) + st = m_f_rw.mk_eq_core(a, b, result); + else if (s_fid == m_ar_rw.get_fid()) + st = m_ar_rw.mk_eq_core(a, b, result); + else if (s_fid == m_seq_rw.get_fid()) + st = m_seq_rw.mk_eq_core(a, b, result); + if (st != BR_FAILED) + return st; + return apply_tamagotchi(a, b, result); + } + + expr_ref mk_eq(expr* a, expr* b) { + expr_ref result(m()); + if (BR_FAILED == reduce_eq(a, b, result)) + result = m().mk_eq(a, b); return result; } @@ -897,6 +908,10 @@ struct th_rewriter::imp : public rewriter_tpl { return m_cfg.mk_app(f, sz, args); } + expr_ref mk_eq(expr* a, expr* b) { + return m_cfg.mk_eq(a, b); + } + void set_solver(expr_solver* solver) { m_cfg.m_seq_rw.set_solver(solver); } @@ -928,7 +943,6 @@ 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); } @@ -941,7 +955,6 @@ unsigned th_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } - void th_rewriter::cleanup() { ast_manager & m = m_imp->m(); m_imp->~imp(); @@ -991,6 +1004,10 @@ expr_ref th_rewriter::mk_app(func_decl* f, unsigned num_args, expr* const* args) return m_imp->mk_app(f, num_args, args); } +expr_ref th_rewriter::mk_eq(expr* a, expr* b) { + return m_imp->mk_eq(a, b); +} + void th_rewriter::set_solver(expr_solver* solver) { m_imp->set_solver(solver); } diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index b84164abc61..2c08c247d35 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -51,6 +51,7 @@ class th_rewriter { 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()); } + expr_ref mk_eq(expr* a, expr* b); bool reduce_quantifier(quantifier * old_q, expr * new_body, diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index aaf744d5fbf..d3f77b7732e 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -99,6 +99,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { if (!in->proofs_enabled()) m_simp->reduce(); m_goal->elim_true(); + m_goal->elim_redundancies(); m_goal->inc_depth(); if (in->models_enabled()) in->add(m_model_trail->get_model_converter().get()); From 255414f4a9558c2ce43fcfd097ef02c8e201e4db Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 15 Nov 2022 11:20:12 -0800 Subject: [PATCH 080/597] fix regression crash Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bool_rewriter.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 1e0b6318aea..84552282b62 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -647,11 +647,13 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) SASSERT(m().is_value(val)); if (m().are_distinct(val, e)) { - result = m().mk_and(mk_eq(t, val), cond); + mk_eq(t, val, result); + result = m().mk_and(result, cond); return BR_REWRITE2; } if (m().are_distinct(val, t)) { - result = m().mk_and(mk_eq(e, val), m().mk_not(cond)); + mk_eq(e, val, result); + result = m().mk_and(result, m().mk_not(cond)); return BR_REWRITE2; } if (m().are_equal(val, t)) { @@ -660,12 +662,14 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) return BR_DONE; } else { - result = m().mk_or(mk_eq(e, val), cond); + mk_eq(e, val, result); + result = m().mk_or(result, cond); } return BR_REWRITE2; } if (m().are_equal(val, e)) { - result = m().mk_or(mk_eq(t, val), m().mk_not(cond)); + mk_eq(t, val, result); + result = m().mk_or(result, m().mk_not(cond)); return BR_REWRITE2; } @@ -686,10 +690,6 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) app* bool_rewriter::mk_eq(expr* lhs, expr* rhs) { - if (m().are_distinct(lhs, rhs)) - return m().mk_false(); - if (lhs == rhs) - return m().mk_true(); // degrades simplification // if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); return m().mk_eq(lhs, rhs); @@ -761,7 +761,7 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { } if (unfolded) { - result = mk_eq(lhs, rhs); + result = m().mk_eq(lhs, rhs); return BR_REWRITE1; } @@ -789,7 +789,8 @@ br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args if (num_args == 2) { expr_ref tmp(m()); - result = mk_not(mk_eq(args[0], args[1])); + mk_eq(args[0], args[1], tmp); + mk_not(tmp, result); return BR_REWRITE2; // mk_eq may be dispatched to other rewriters. } @@ -828,10 +829,10 @@ br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args } if (m_blast_distinct && num_args < m_blast_distinct_threshold) { - ptr_buffer new_diseqs; + expr_ref_vector new_diseqs(m()); for (unsigned i = 0; i < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) - new_diseqs.push_back(mk_not(mk_eq(args[i], args[j]))); + new_diseqs.push_back(m().mk_not(m().mk_eq(args[i], args[j]))); } result = m().mk_and(new_diseqs); return BR_REWRITE3; From d70dbdad501f3782e99d4d364fc7424f1bda6a6f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 15 Nov 2022 20:17:30 -0800 Subject: [PATCH 081/597] wip euf-completion - debugging --- src/ast/simplifiers/euf_completion.cpp | 98 ++++++++++++++++++++++---- src/ast/simplifiers/euf_completion.h | 4 ++ 2 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 7c27a01bb92..bf3e4ce7a4d 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -58,22 +58,25 @@ namespace euf { } void completion::reduce() { - ++m_epoch; - add_egraph(); - map_canonical(); - read_egraph(); + do { + ++m_epoch; + m_has_new_eq = false; + add_egraph(); + map_canonical(); + read_egraph(); + } + while (m_has_new_eq); } 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)) + if (m.is_eq(f)) m_egraph.merge(n->get_arg(0), n->get_arg(1), d); - if (m.is_not(f, x)) + if (m.is_not(f)) m_egraph.merge(n->get_arg(0), m_ff, d); else m_egraph.merge(n, m_tt, d); @@ -94,16 +97,39 @@ namespace euf { for (unsigned i = m_qhead; i < sz; ++i) { auto [f, d] = m_fmls[i](); + if (m.is_and(f)) + IF_VERBOSE(0, verbose_stream() << "is-and " << mk_bounded_pp(f, m) << "\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(11, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); + expr* x, * y; + if (m.is_eq(g, x, y) && find(x) != find(y)) + m_has_new_eq = true; + if (m.is_and(g) && !m_has_new_eq) + for (expr* arg : *to_app(g)) + if (m.is_eq(arg, x, y)) + m_has_new_eq |= find(x) != find(y); + else + m_has_new_eq = true; + if (m.is_and(g)) + IF_VERBOSE(0, verbose_stream() << 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()); + if (m_has_new_eq) + IF_VERBOSE(0, verbose_stream() << "new round\n"); + if (!m_has_new_eq) + advance_qhead(m_fmls.size()); + } + + enode* completion::find(expr* e) { + enode* r = m_egraph.find(e); + if (r) + return r; + return mk_enode(e); } enode* completion::mk_enode(expr* e) { @@ -139,23 +165,54 @@ namespace euf { expr_ref completion::canonize_fml(expr* f, expr_dependency_ref& d) { + auto is_nullary = [&](expr* e) { + return is_app(e) && to_app(e)->get_num_args() == 0; + }; expr* x, * y; if (m.is_eq(f, x, y)) { expr_ref x1 = canonize(x, d); expr_ref y1 = canonize(y, d); + if (is_nullary(x)) { + SASSERT(x1 == x); + x1 = get_canonical(x, d); + } + if (is_nullary(y)) { + SASSERT(y1 == y); + y1 = get_canonical(y, d); + } + + if (x == y) + return expr_ref(m.mk_true(), m); + if (x == x1 && y == y1) return expr_ref(f, m); + + if (is_nullary(x) && is_nullary(y)) + return mk_and(m_rewriter.mk_eq(x, x1), m_rewriter.mk_eq(y, x1)); + + if (x == x1 && is_nullary(x)) + return m_rewriter.mk_eq(y1, x1); + + if (y == y1 && is_nullary(y)) + return m_rewriter.mk_eq(x1, y1); + + if (is_nullary(x)) + return mk_and(m_rewriter.mk_eq(x, x1), m_rewriter.mk_eq(y1, x1)); + + if (is_nullary(y)) + return mk_and(m_rewriter.mk_eq(y, y1), m_rewriter.mk_eq(x1, y1)); + 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); + return m_rewriter.mk_eq(y1, c); else if (c == y1) - return expr_ref(m.mk_eq(x1, c), m); + return m_rewriter.mk_eq(x1, c); else - return expr_ref(m.mk_and(m.mk_eq(x1, c), m.mk_eq(y1, c)), m); + return mk_and(m_rewriter.mk_eq(x1, c), m_rewriter.mk_eq(y1, c)); } } @@ -167,6 +224,14 @@ namespace euf { return canonize(f, d); } + expr_ref completion::mk_and(expr* a, expr* b) { + if (m.is_true(a)) + return expr_ref(b, m); + if (m.is_true(b)) + return expr_ref(a, m); + return expr_ref(m.mk_and(a, b), m); + } + 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 @@ -188,7 +253,10 @@ namespace euf { 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)); + d = m.mk_join(d, m_deps.get(r->get_id(), nullptr)); + if (!m_canonical.get(r->get_id())) + IF_VERBOSE(0, verbose_stream() << r->get_id() << " " << m_egraph.bpp(n) << " " << m_egraph.bpp(r) << "\n"); + SASSERT(m_canonical.get(r->get_id())); return m_canonical.get(r->get_id()); } @@ -214,6 +282,7 @@ namespace euf { old_value = nullptr; } }; + SASSERT(e); if (num_scopes() > 0) m_trail.push(vtrail(m_canonical, n->get_id())); m_canonical.setx(n->get_id(), e); @@ -263,6 +332,8 @@ namespace euf { rep = k; m_reps.setx(n->get_id(), rep, nullptr); + if (n->get_id() == 87507) + IF_VERBOSE(0, verbose_stream() << "set-canon " << n->get_id() << "\n"); 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()); @@ -323,7 +394,8 @@ namespace euf { } } } - } + } + } diff --git a/src/ast/simplifiers/euf_completion.h b/src/ast/simplifiers/euf_completion.h index 9e293ca0e0e..2cc0dcc5837 100644 --- a/src/ast/simplifiers/euf_completion.h +++ b/src/ast/simplifiers/euf_completion.h @@ -40,10 +40,14 @@ namespace euf { unsigned_vector m_epochs; th_rewriter m_rewriter; stats m_stats; + bool m_has_new_eq = false; enode* mk_enode(expr* e); + enode* find(expr* e); + expr_ref mk_and(expr* a, expr* b); void add_egraph(); void map_canonical(); + void saturate(); void read_egraph(); expr_ref canonize(expr* f, expr_dependency_ref& dep); expr_ref canonize_fml(expr* f, expr_dependency_ref& dep); From 55ab7778f4b8e7653a0e9bb8d159962b70e9997a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 16 Nov 2022 03:46:17 -0800 Subject: [PATCH 082/597] fix perf bug in new solve_eqs. --- src/ast/simplifiers/solve_context_eqs.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index 1ccab0f6c1f..dab09a7a95a 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -150,13 +150,32 @@ namespace euf { for (unsigned i = m_solve_eqs.m_qhead; i < m_fmls.size(); ++i) collect_nested_equalities(m_fmls[i], visited, eqs); + if (eqs.empty()) + return; + std::stable_sort(eqs.begin(), eqs.end(), [&](dependent_eq const& e1, dependent_eq const& e2) { return e1.var->get_id() < e2.var->get_id(); }); + + // quickly weed out variables that occur in more than two assertions. + unsigned_vector refcount; + { + expr_mark visited; + for (unsigned i = m_solve_eqs.m_qhead; i < m_fmls.size(); ++i) { + visited.reset(); + expr* f = m_fmls[i].fml(); + for (expr* t : subterms::all(expr_ref(f, m), &m_todo, &visited)) + refcount.setx(t->get_id(), refcount.get(t->get_id(), 0) + 1, 0); + } + } + unsigned j = 0; expr* last_var = nullptr; bool was_unsafe = false; for (auto const& eq : eqs) { + if (refcount.get(eq.var->get_id(), 0) > 1) + continue; + SASSERT(!m.is_bool(eq.var)); From 98fc8c99db60f5ec8a79a4dbeaf029fca05befc3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 16 Nov 2022 03:47:01 -0800 Subject: [PATCH 083/597] add shortcut to equality mk utility --- src/ast/rewriter/bool_rewriter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 84552282b62..ffceec277ed 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -690,8 +690,10 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) app* bool_rewriter::mk_eq(expr* lhs, expr* rhs) { - // degrades simplification - // if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); + if (m().are_equal(lhs, rhs)) + return m().mk_true(); + if (m().are_distinct(lhs, rhs)) + return m().mk_false(); return m().mk_eq(lhs, rhs); } From 2c7799939e319ba48bc8e1d7a9d27d4bdbb13954 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 16 Nov 2022 03:47:38 -0800 Subject: [PATCH 084/597] wip - tuning and fixes to euf-completion --- src/ast/simplifiers/euf_completion.cpp | 32 +++++++++++--------------- src/ast/simplifiers/euf_completion.h | 2 +- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index bf3e4ce7a4d..beaee4a27ac 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -74,8 +74,11 @@ namespace euf { for (unsigned i = m_qhead; i < sz; ++i) { auto [f,d] = m_fmls[i](); auto* n = mk_enode(f); - if (m.is_eq(f)) + if (m.is_eq(f)) { m_egraph.merge(n->get_arg(0), n->get_arg(1), d); + m_nodes.push_back(n->get_arg(0)); + m_nodes.push_back(n->get_arg(1)); + } if (m.is_not(f)) m_egraph.merge(n->get_arg(0), m_ff, d); else @@ -97,8 +100,6 @@ namespace euf { for (unsigned i = m_qhead; i < sz; ++i) { auto [f, d] = m_fmls[i](); - if (m.is_and(f)) - IF_VERBOSE(0, verbose_stream() << "is-and " << mk_bounded_pp(f, m) << "\n"); expr_dependency_ref dep(d, m); expr_ref g = canonize_fml(f, dep); if (g != f) { @@ -106,30 +107,25 @@ namespace euf { m_stats.m_num_rewrites++; IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); expr* x, * y; - if (m.is_eq(g, x, y) && find(x) != find(y)) + if (m.is_eq(g, x, y) && new_eq(x, y)) m_has_new_eq = true; if (m.is_and(g) && !m_has_new_eq) for (expr* arg : *to_app(g)) if (m.is_eq(arg, x, y)) - m_has_new_eq |= find(x) != find(y); - else + m_has_new_eq |= new_eq(x, y); + else if (!m.is_true(arg)) m_has_new_eq = true; - if (m.is_and(g)) - IF_VERBOSE(0, verbose_stream() << mk_bounded_pp(g, m, 3) << "\n"); } CTRACE("euf_completion", g != f, tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); } - if (m_has_new_eq) - IF_VERBOSE(0, verbose_stream() << "new round\n"); if (!m_has_new_eq) advance_qhead(m_fmls.size()); } - enode* completion::find(expr* e) { - enode* r = m_egraph.find(e); - if (r) - return r; - return mk_enode(e); + bool completion::new_eq(expr* a, expr* b) { + enode* na = m_egraph.find(a); + enode* nb = m_egraph.find(b); + return !na || !nb || na->get_root() != nb->get_root(); } enode* completion::mk_enode(expr* e) { @@ -254,8 +250,6 @@ namespace euf { 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)); - if (!m_canonical.get(r->get_id())) - IF_VERBOSE(0, verbose_stream() << r->get_id() << " " << m_egraph.bpp(n) << " " << m_egraph.bpp(r) << "\n"); SASSERT(m_canonical.get(r->get_id())); return m_canonical.get(r->get_id()); } @@ -320,6 +314,8 @@ namespace euf { void completion::map_canonical() { m_todo.reset(); enode_vector roots; + if (m_nodes.empty()) + return; for (unsigned i = 0; i < m_nodes.size(); ++i) { enode* n = m_nodes[i]->get_root(); if (n->is_marked1()) @@ -332,8 +328,6 @@ namespace euf { rep = k; m_reps.setx(n->get_id(), rep, nullptr); - if (n->get_id() == 87507) - IF_VERBOSE(0, verbose_stream() << "set-canon " << n->get_id() << "\n"); 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()); diff --git a/src/ast/simplifiers/euf_completion.h b/src/ast/simplifiers/euf_completion.h index 2cc0dcc5837..b629399da87 100644 --- a/src/ast/simplifiers/euf_completion.h +++ b/src/ast/simplifiers/euf_completion.h @@ -43,7 +43,7 @@ namespace euf { bool m_has_new_eq = false; enode* mk_enode(expr* e); - enode* find(expr* e); + bool new_eq(expr* a, expr* b); expr_ref mk_and(expr* a, expr* b); void add_egraph(); void map_canonical(); From 6662afdd2632cb2c2f313796fdd0746c28a7a9cb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 16 Nov 2022 22:15:02 -0800 Subject: [PATCH 085/597] perf improvements to solve-eqs and euf-completion --- src/ast/simplifiers/euf_completion.cpp | 70 ++++++++++++++++------- src/ast/simplifiers/euf_completion.h | 6 +- src/ast/simplifiers/solve_context_eqs.cpp | 54 ++++++++++++----- src/ast/simplifiers/solve_eqs.cpp | 50 ++++++++++++---- 4 files changed, 129 insertions(+), 51 deletions(-) diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index beaee4a27ac..9860b48b2bf 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -58,31 +58,43 @@ namespace euf { } void completion::reduce() { + unsigned rounds = 0; do { ++m_epoch; + ++rounds; m_has_new_eq = false; add_egraph(); map_canonical(); read_egraph(); + IF_VERBOSE(11, verbose_stream() << "(euf.completion :rounds " << rounds << ")\n"); } - while (m_has_new_eq); + while (m_has_new_eq && rounds <= 3); } void completion::add_egraph() { - m_nodes.reset(); + m_nodes_to_canonize.reset(); unsigned sz = m_fmls.size(); + auto add_children = [&](enode* n) { + for (auto* ch : enode_args(n)) + m_nodes_to_canonize.push_back(ch); + }; + for (unsigned i = m_qhead; i < sz; ++i) { auto [f,d] = m_fmls[i](); auto* n = mk_enode(f); if (m.is_eq(f)) { m_egraph.merge(n->get_arg(0), n->get_arg(1), d); - m_nodes.push_back(n->get_arg(0)); - m_nodes.push_back(n->get_arg(1)); + add_children(n->get_arg(0)); + add_children(n->get_arg(1)); } - if (m.is_not(f)) + if (m.is_not(f)) { m_egraph.merge(n->get_arg(0), m_ff, d); - else + add_children(n->get_arg(0)); + } + else { m_egraph.merge(n, m_tt, d); + add_children(n); + } } m_egraph.propagate(); } @@ -106,15 +118,7 @@ namespace euf { m_fmls.update(i, dependent_expr(m, g, dep)); m_stats.m_num_rewrites++; IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); - expr* x, * y; - if (m.is_eq(g, x, y) && new_eq(x, y)) - m_has_new_eq = true; - if (m.is_and(g) && !m_has_new_eq) - for (expr* arg : *to_app(g)) - if (m.is_eq(arg, x, y)) - m_has_new_eq |= new_eq(x, y); - else if (!m.is_true(arg)) - m_has_new_eq = true; + update_has_new_eq(g); } CTRACE("euf_completion", g != f, tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); } @@ -122,12 +126,34 @@ namespace euf { advance_qhead(m_fmls.size()); } - bool completion::new_eq(expr* a, expr* b) { + bool completion::is_new_eq(expr* a, expr* b) { enode* na = m_egraph.find(a); enode* nb = m_egraph.find(b); + if (!na) + IF_VERBOSE(11, verbose_stream() << "not internalied " << mk_bounded_pp(a, m) << "\n"); + if (!nb) + IF_VERBOSE(11, verbose_stream() << "not internalied " << mk_bounded_pp(b, m) << "\n"); + if (na && nb && na->get_root() != nb->get_root()) + IF_VERBOSE(11, verbose_stream() << m_egraph.bpp(na) << " " << m_egraph.bpp(nb) << "\n"); return !na || !nb || na->get_root() != nb->get_root(); } + void completion::update_has_new_eq(expr* g) { + expr* x, * y; + if (m_has_new_eq) + return; + else if (m.is_eq(g, x, y)) + m_has_new_eq |= is_new_eq(x, y); + else if (m.is_and(g)) { + for (expr* arg : *to_app(g)) + update_has_new_eq(arg); + } + else if (m.is_not(g, g)) + m_has_new_eq |= is_new_eq(g, m.mk_false()); + else + m_has_new_eq |= is_new_eq(g, m.mk_true()); + } + enode* completion::mk_enode(expr* e) { m_todo.push_back(e); enode* n; @@ -138,7 +164,7 @@ namespace euf { continue; } if (!is_app(e)) { - m_nodes.push_back(m_egraph.mk(e, 0, 0, nullptr)); + m_nodes_to_canonize.push_back(m_egraph.mk(e, 0, 0, nullptr)); m_todo.pop_back(); continue; } @@ -152,7 +178,7 @@ namespace euf { 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_nodes_to_canonize.push_back(m_egraph.mk(e, 0, m_args.size(), m_args.data())); m_todo.pop_back(); } } @@ -314,10 +340,10 @@ namespace euf { void completion::map_canonical() { m_todo.reset(); enode_vector roots; - if (m_nodes.empty()) + if (m_nodes_to_canonize.empty()) return; - for (unsigned i = 0; i < m_nodes.size(); ++i) { - enode* n = m_nodes[i]->get_root(); + for (unsigned i = 0; i < m_nodes_to_canonize.size(); ++i) { + enode* n = m_nodes_to_canonize[i]->get_root(); if (n->is_marked1()) continue; n->mark1(); @@ -334,7 +360,7 @@ namespace euf { for (enode* arg : enode_args(n)) { arg = arg->get_root(); if (!arg->is_marked1()) - m_nodes.push_back(arg); + m_nodes_to_canonize.push_back(arg); } } for (enode* r : roots) diff --git a/src/ast/simplifiers/euf_completion.h b/src/ast/simplifiers/euf_completion.h index b629399da87..f02e3324569 100644 --- a/src/ast/simplifiers/euf_completion.h +++ b/src/ast/simplifiers/euf_completion.h @@ -33,7 +33,7 @@ namespace euf { egraph m_egraph; enode* m_tt, *m_ff; ptr_vector m_todo; - enode_vector m_args, m_reps, m_nodes; + enode_vector m_args, m_reps, m_nodes_to_canonize; expr_ref_vector m_canonical, m_eargs; expr_dependency_ref_vector m_deps; unsigned m_epoch = 0; @@ -43,11 +43,11 @@ namespace euf { bool m_has_new_eq = false; enode* mk_enode(expr* e); - bool new_eq(expr* a, expr* b); + bool is_new_eq(expr* a, expr* b); + void update_has_new_eq(expr* g); expr_ref mk_and(expr* a, expr* b); void add_egraph(); void map_canonical(); - void saturate(); void read_egraph(); expr_ref canonize(expr* f, expr_dependency_ref& dep); expr_ref canonize_fml(expr* f, expr_dependency_ref& dep); diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index dab09a7a95a..ef52d009c52 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -147,7 +147,8 @@ namespace euf { 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) + unsigned sz = m_fmls.size(); + for (unsigned i = m_solve_eqs.m_qhead; i < sz; ++i) collect_nested_equalities(m_fmls[i], visited, eqs); if (eqs.empty()) @@ -156,15 +157,37 @@ namespace euf { std::stable_sort(eqs.begin(), eqs.end(), [&](dependent_eq const& e1, dependent_eq const& e2) { return e1.var->get_id() < e2.var->get_id(); }); - // quickly weed out variables that occur in more than two assertions. - unsigned_vector refcount; + + // record the first and last occurrence of variables + // if the first and last occurrence coincide, the variable occurs in only one formula. + // otherwise it occurs in multiple formulas and should not be considered for solving. + unsigned_vector occurs1(m.get_num_asts() + 1, sz); + unsigned_vector occurs2(m.get_num_asts() + 1, sz); + + struct visitor { + unsigned_vector& occurrence; + unsigned i = 0; + unsigned sz = 0; + visitor(unsigned_vector& occurrence) : occurrence(occurrence), i(0), sz(0) {} + void operator()(expr* t) { + occurrence.setx(t->get_id(), i, sz); + } + }; + { - expr_mark visited; - for (unsigned i = m_solve_eqs.m_qhead; i < m_fmls.size(); ++i) { - visited.reset(); - expr* f = m_fmls[i].fml(); - for (expr* t : subterms::all(expr_ref(f, m), &m_todo, &visited)) - refcount.setx(t->get_id(), refcount.get(t->get_id(), 0) + 1, 0); + visitor visitor1(occurs1); + visitor visitor2(occurs2); + visitor1.sz = sz; + visitor2.sz = sz; + expr_fast_mark1 fast_visited; + for (unsigned i = 0; i < sz; ++i) { + visitor1.i = i; + quick_for_each_expr(visitor1, fast_visited, m_fmls[i].fml()); + } + fast_visited.reset(); + for (unsigned i = sz; i-- > 0; ) { + visitor2.i = i; + quick_for_each_expr(visitor2, fast_visited, m_fmls[i].fml()); } } @@ -172,13 +195,17 @@ namespace euf { expr* last_var = nullptr; bool was_unsafe = false; for (auto const& eq : eqs) { - - if (refcount.get(eq.var->get_id(), 0) > 1) + if (!eq.var) + continue; + unsigned occ1 = occurs1.get(eq.var->get_id(), sz); + unsigned occ2 = occurs2.get(eq.var->get_id(), sz); + if (occ1 >= sz) + continue; + if (occ1 != occ2) continue; SASSERT(!m.is_bool(eq.var)); - if (eq.var != last_var) { m_contains_v.reset(); @@ -195,8 +222,7 @@ namespace euf { } // then mark occurrences - for (unsigned i = 0; i < m_fmls.size(); ++i) - m_todo.push_back(m_fmls[i].fml()); + m_todo.push_back(m_fmls[occ1].fml()); mark_occurs(m_todo, eq.var, m_contains_v); SASSERT(m_todo.empty()); } diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 0b6f1216007..f66f64b6d85 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -75,13 +75,6 @@ namespace euf { return m_id2level[id] != UINT_MAX; }; - auto is_safe = [&](unsigned lvl, expr* t) { - 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 true; - }; - unsigned init_level = UINT_MAX; unsigned_vector todo; @@ -94,26 +87,59 @@ namespace euf { 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_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)) + bool is_safe = true; + unsigned todo_sz = todo.size(); + + // determine if substitution is safe. + // all time-stamps must be at or above current level + // unexplored variables that are part of substitution are appended to work list. + SASSERT(m_todo.empty()); + m_todo.push_back(t); + expr_fast_mark1 visited; + while (!m_todo.empty()) { + expr* e = m_todo.back(); + m_todo.pop_back(); + if (visited.is_marked(e)) + continue; + visited.mark(e, true); + if (is_app(e)) { + for (expr* arg : *to_app(e)) + m_todo.push_back(arg); + } + else if (is_quantifier(e)) + m_todo.push_back(to_quantifier(e)->get_expr()); + if (!is_var(e)) + continue; + if (m_id2level[var2id(e)] < curr_level) { + is_safe = false; + break; + } + if (!is_explored(var2id(e))) + todo.push_back(var2id(e)); + } + m_todo.reset(); + + if (!is_safe) { + todo.shrink(todo_sz); continue; + } SASSERT(!occurs(v, t)); m_next[j][0] = eq; 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; } - } + } } } From 59b7845c7dc765a9c19074c43d288daccd753d68 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 Nov 2022 17:36:21 +0900 Subject: [PATCH 086/597] reset visited (fast mark) to not clash with occurs --- src/ast/euf/euf_egraph.cpp | 6 +++--- src/ast/euf/euf_egraph.h | 2 +- src/ast/simplifiers/euf_completion.cpp | 22 +++++++++++++--------- src/ast/simplifiers/solve_eqs.cpp | 3 ++- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index c8605b297c2..666a7fad690 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -451,7 +451,7 @@ namespace euf { add_literal(n1, false); if (n1->is_equality() && n1->value() == l_false) new_diseq(n1); - remove_parents(r1, r2); + remove_parents(r1); push_eq(r1, n1, r2->num_parents()); merge_justification(n1, n2, j); for (enode* c : enode_class(n1)) @@ -464,8 +464,8 @@ namespace euf { cb(r2, r1); } - void egraph::remove_parents(enode* r1, enode* r2) { - for (enode* p : enode_parents(r1)) { + void egraph::remove_parents(enode* r) { + for (enode* p : enode_parents(r)) { if (p->is_marked1()) continue; if (p->merge_enabled()) { diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index 53aaf481ada..d6bcb9cd31c 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -220,7 +220,7 @@ namespace euf { void merge_th_eq(enode* n, enode* root); void merge_justification(enode* n1, enode* n2, justification j); void reinsert_parents(enode* r1, enode* r2); - void remove_parents(enode* r1, enode* r2); + void remove_parents(enode* r); void unmerge_justification(enode* n1); void reinsert_equality(enode* p); void update_children(enode* n); diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 9860b48b2bf..ac1ecc47aad 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -80,18 +80,22 @@ namespace euf { }; for (unsigned i = m_qhead; i < sz; ++i) { - auto [f,d] = m_fmls[i](); - auto* n = mk_enode(f); - if (m.is_eq(f)) { - m_egraph.merge(n->get_arg(0), n->get_arg(1), d); - add_children(n->get_arg(0)); - add_children(n->get_arg(1)); + expr* x, * y; + auto [f, d] = m_fmls[i](); + if (m.is_eq(f, x, y)) { + enode* a = mk_enode(x); + enode* b = mk_enode(y); + m_egraph.merge(a, b, d); + add_children(a); + add_children(b); } - if (m.is_not(f)) { - m_egraph.merge(n->get_arg(0), m_ff, d); - add_children(n->get_arg(0)); + else if (m.is_not(f, f)) { + enode* n = mk_enode(f); + m_egraph.merge(n, m_ff, d); + add_children(n); } else { + enode* n = mk_enode(f); m_egraph.merge(n, m_tt, d); add_children(n); } diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index f66f64b6d85..5090e1b438c 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -129,7 +129,8 @@ namespace euf { todo.push_back(var2id(e)); } m_todo.reset(); - + visited.reset(); + if (!is_safe) { todo.shrink(todo_sz); continue; From 7da91f4313980aebd98c21146962ef77cbd5a713 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:43:21 +0700 Subject: [PATCH 087/597] allow printing declarations with reverse variable order --- src/ast/ast_smt2_pp.cpp | 35 ++++++++++++++++++++--------------- src/ast/ast_smt2_pp.h | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 6ed647a2704..927660fbe4c 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -561,15 +561,18 @@ class smt2_printer { void pp_var(var * v) { format * f; - if (v->get_idx() < m_var_names.size()) { - symbol s = m_var_names[m_var_names.size() - v->get_idx() - 1]; + unsigned idx = v->get_idx(); + if (idx < m_var_names.size()) { + symbol s; + if (m_reverse && idx < m_arity) + s = m_var_names[m_var_names.size() - m_arity + idx]; + else + s = m_var_names[m_var_names.size() - idx - 1]; std::string vname; - if (is_smt2_quoted_symbol (s)) { - vname = mk_smt2_quoted_symbol (s); - } - else { - vname = s.str(); - } + if (is_smt2_quoted_symbol (s)) + vname = mk_smt2_quoted_symbol (s); + else + vname = s.str(); f = mk_string(m(), vname); } else { @@ -1139,9 +1142,13 @@ class smt2_printer { r = mk_seq1(m(), args, args+3, f2f(), cmd); } + bool m_reverse = false; + unsigned m_arity = 0; - void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd) { + void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd, bool reverse) { unsigned len; + flet _reverse(m_reverse, reverse); + m_arity = f->get_arity(); format * fname = m_env.pp_fdecl_name(f, len); register_var_names(f->get_arity()); format * args[4]; @@ -1202,9 +1209,9 @@ void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & pr(f, r, cmd); } -void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { +void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd, bool reverse) { smt2_printer pr(env, p); - pr(f, e, r, cmd); + pr(f, e, r, cmd, reverse); } void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, @@ -1251,7 +1258,6 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen if (!f) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); - sbuffer var_names; mk_smt2_format(f, env, p, r, cmd); if (indent > 0) r = mk_indent(m, indent, r.get()); @@ -1259,12 +1265,11 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen return out; } -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd, bool reverse) { if (!f) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); - sbuffer var_names; - mk_smt2_format(f, e, env, p, r, cmd); + mk_smt2_format(f, e, env, p, r, cmd, reverse); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 47649b9b265..2b0b2f37115 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -104,7 +104,7 @@ std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & e unsigned num_vars = 0, char const * var_prefix = nullptr); std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun"); -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun"); +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun", bool reverse = false); std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref()); std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector> const& funs, smt2_pp_environment & env, params_ref const & p = params_ref()); From ba68652c72d5b61157b87d88dadee2948397e343 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:43:46 +0700 Subject: [PATCH 088/597] add destructive equality resolution to existentials --- src/ast/rewriter/der.cpp | 89 ++++++++++++++++++++++++++++++---------- src/ast/rewriter/der.h | 6 ++- 2 files changed, 72 insertions(+), 23 deletions(-) diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index 93b7c6226ba..fd79529f9f6 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -81,14 +81,54 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { } // VAR - if (is_var(e, num_decls)) { + if (is_var(e, num_decls)) return set_result(to_var(e), m.mk_false()); - } + // (not VAR) - if (is_neg_var(m, e, v, num_decls)) { + if (is_neg_var(m, e, v, num_decls)) return set_result(v, m.mk_true()); + + return false; +} + +bool der::is_var_eq(expr* e, unsigned num_decls, var*& v, expr_ref& t) { + expr* lhs, * rhs; + auto set_result = [&](var* w, expr* s) { + v = w; + t = s; + TRACE("der", tout << mk_pp(e, m) << "\n";); + return true; + }; + + // (= VAR t) + if (m.is_eq(e, lhs, rhs)) { + if (!is_var(lhs, num_decls)) + std::swap(lhs, rhs); + if (!is_var(lhs, num_decls)) + return false; + return set_result(to_var(lhs), rhs); } + + if (m.is_eq(e, lhs, rhs) && m.is_bool(lhs)) { + // (iff VAR t) case + if (!is_var(lhs, num_decls)) + std::swap(lhs, rhs); + if (is_var(lhs, num_decls)) { + m_new_exprs.push_back(rhs); + return set_result(to_var(lhs), rhs); + } + return false; + } + + // VAR + if (is_var(e, num_decls)) + return set_result(to_var(e), m.mk_true()); + + // (not VAR) + if (is_neg_var(m, e, v, num_decls)) + return set_result(v, m.mk_false()); + return false; } @@ -99,6 +139,7 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { TRACE("der", tout << mk_pp(q, m) << "\n";); + auto k = q->get_kind(); // Keep applying it until r doesn't change anymore do { proof_ref curr_pr(m); @@ -106,14 +147,13 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { reduce1(q, r, curr_pr); if (q != r) reduced = true; - if (m.proofs_enabled()) { - pr = m.mk_transitivity(pr, curr_pr); - } + if (m.proofs_enabled()) + pr = m.mk_transitivity(pr, curr_pr); } while (q != r && is_quantifier(r)); // Eliminate variables that have become unused - if (reduced && is_forall(r)) { + if (reduced && is_quantifier(r) && k == to_quantifier(r)->get_kind()) { quantifier * q = to_quantifier(r); r = elim_unused_vars(m, q, params_ref()); if (m.proofs_enabled()) { @@ -125,7 +165,7 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { } void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { - if (!is_forall(q)) { + if (!is_forall(q) && !is_exists(q)) { pr = nullptr; r = q; return; @@ -136,14 +176,20 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { var * v = nullptr; expr_ref t(m); - if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t)) + if (is_forall(q) && is_var_diseq(e, num_decls, v, t) && !occurs(v, t)) r = m.mk_false(); + else if (is_exists(q) && is_var_eq(e, num_decls, v, t) && !occurs(v, t)) + r = m.mk_true(); else { - expr_ref_vector ors(m); - flatten_or(e, ors); - unsigned num_args = ors.size(); + expr_ref_vector literals(m); + if (is_forall(q)) + flatten_or(e, literals); + else + flatten_and(e, literals); + unsigned num_args = literals.size(); unsigned diseq_count = 0; unsigned largest_vinx = 0; + bool is_eq = false; m_map.reset(); m_pos2var.reset(); @@ -153,7 +199,8 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { // Find all disequalities for (unsigned i = 0; i < num_args; i++) { - if (is_var_diseq(ors.get(i), num_decls, v, t)) { + is_eq = is_forall(q) ? is_var_diseq(literals.get(i), num_decls, v, t) : is_var_eq(literals.get(i), num_decls, v, t); + if (is_eq) { unsigned idx = v->get_idx(); if (m_map.get(idx, nullptr) == nullptr) { m_map.reserve(idx + 1); @@ -174,7 +221,7 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { if (!m_order.empty()) { create_substitution(largest_vinx + 1); - apply_substitution(q, ors, r); + apply_substitution(q, literals, is_forall(q), r); } } else { @@ -185,9 +232,9 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { // Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses. // So, we must perform a occurs check here. - if (m.proofs_enabled()) { + if (m.proofs_enabled()) pr = r == q ? nullptr : m.mk_der(q, r); - } + } static void der_sort_vars(ptr_vector & vars, expr_ref_vector & definitions, unsigned_vector & order) { @@ -326,20 +373,20 @@ void der::create_substitution(unsigned sz) { } } -void der::apply_substitution(quantifier * q, expr_ref_vector& ors, expr_ref & r) { - unsigned num_args = ors.size(); +void der::apply_substitution(quantifier * q, expr_ref_vector& literals, bool is_or, expr_ref & r) { + unsigned num_args = literals.size(); // get a new expression m_new_args.reset(); - for(unsigned i = 0; i < num_args; i++) { + for (unsigned i = 0; i < num_args; i++) { int x = m_pos2var[i]; if (x != -1 && m_map.get(x) != nullptr) continue; // this is a disequality with definition (vanishes) - m_new_args.push_back(ors.get(i)); + m_new_args.push_back(literals.get(i)); } - expr_ref t(mk_or(m, m_new_args.size(), m_new_args.data()), m); + expr_ref t(is_or ? mk_or(m_new_args) : mk_and(m_new_args), m); expr_ref new_e = m_subst(t, m_subst_map); // don't forget to update the quantifier patterns diff --git a/src/ast/rewriter/der.h b/src/ast/rewriter/der.h index ec45994a95e..bd21e54d0b5 100644 --- a/src/ast/rewriter/der.h +++ b/src/ast/rewriter/der.h @@ -131,7 +131,7 @@ class der { ptr_vector m_inx2var; unsigned_vector m_order; expr_ref_vector m_subst_map; - expr_ref_buffer m_new_args; + expr_ref_vector m_new_args; /** \brief Return true if e can be viewed as a variable disequality. @@ -145,9 +145,11 @@ class der { */ bool is_var_diseq(expr * e, unsigned num_decls, var *& v, expr_ref & t); + bool is_var_eq(expr* e, unsigned num_decls, var*& v, expr_ref& t); + void get_elimination_order(); void create_substitution(unsigned sz); - void apply_substitution(quantifier * q, expr_ref_vector& ors, expr_ref & r); + void apply_substitution(quantifier * q, expr_ref_vector& lits, bool is_or, expr_ref & r); void reduce1(quantifier * q, expr_ref & r, proof_ref & pr); From c2e9016d04b67cd9d33bb72be59960ccf0f4aa06 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:44:52 +0700 Subject: [PATCH 089/597] display model-add parameters in correct order --- src/ast/converters/model_converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/converters/model_converter.cpp b/src/ast/converters/model_converter.cpp index bba18ecd69c..1033cc57b21 100644 --- a/src/ast/converters/model_converter.cpp +++ b/src/ast/converters/model_converter.cpp @@ -26,7 +26,7 @@ Module Name: void model_converter::display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e) { VERIFY(e); VERIFY(f->get_range() == e->get_sort()); - ast_smt2_pp(out, f, e, env, params_ref(), 0, "model-add") << "\n"; + ast_smt2_pp(out, f, e, env, params_ref(), 0, "model-add", true) << "\n"; } void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const { From 41b40c3a51d1239d08698d0f8fd05a2d740e97c1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:45:07 +0700 Subject: [PATCH 090/597] remove dead code --- src/ast/macros/macro_manager.cpp | 6 ------ src/ast/macros/macro_manager.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index 032c724e117..bbe7f245ce7 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -175,12 +175,6 @@ namespace macro_manager_ns { /** \brief Mark all func_decls used in exprs as forbidden. */ -void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) { - expr_mark visited; - macro_manager_ns::proc p(m_forbidden_set, m_forbidden); - for (unsigned i = 0; i < n; i++) - for_each_expr(p, visited, exprs[i]); -} void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) { expr_mark visited; diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h index 57583b67b48..a3c1a8d97e9 100644 --- a/src/ast/macros/macro_manager.h +++ b/src/ast/macros/macro_manager.h @@ -73,9 +73,7 @@ class macro_manager { void push_scope(); void pop_scope(unsigned num_scopes); void reset(); - void mark_forbidden(unsigned n, expr * const * exprs); void mark_forbidden(unsigned n, justified_expr const * exprs); - void mark_forbidden(expr * e) { mark_forbidden(1, &e); } bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); } obj_hashtable const & get_forbidden_set() const { return m_forbidden_set; } void display(std::ostream & out); From dcc995f0e502b103ccb3f15b4012c0d6f2a8da4e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:45:54 +0700 Subject: [PATCH 091/597] code simplification --- src/ast/converters/expr_inverter.cpp | 2 +- src/tactic/core/elim_uncnstr_tactic.cpp | 2 +- src/tactic/core/reduce_args_tactic.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 2c4c960a7fe..2874abd6881 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -740,7 +740,7 @@ void iexpr_inverter::add_def(expr * v, expr * def) { return; SASSERT(uncnstr(v)); SASSERT(to_app(v)->get_num_args() == 0); - m_mc->add(to_app(v)->get_decl(), def); + m_mc->add(v, def); } void iexpr_inverter::add_defs(unsigned num, expr* const* args, expr* u, expr* identity) { diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 60bdd2710a1..ed54b67680d 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -121,7 +121,7 @@ class elim_uncnstr_tactic : public tactic { SASSERT(uncnstr(v)); SASSERT(to_app(v)->get_num_args() == 0); if (m_mc) - m_mc->add(to_app(v)->get_decl(), def); + m_mc->add(v, def); } void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) { diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index b2a10fafa08..785409e2701 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -414,7 +414,7 @@ struct reduce_args_tactic::imp { for (auto const& [t, new_def] : *map) { f_mc->hide(new_def); SASSERT(new_def->get_arity() == new_args.size()); - app * new_t = m.mk_app(new_def, new_args.size(), new_args.data()); + app * new_t = m.mk_app(new_def, new_args); if (def == nullptr) { def = new_t; } @@ -429,7 +429,7 @@ struct reduce_args_tactic::imp { if (new_eqs.size() == 1) cond = new_eqs[0]; else - cond = m.mk_and(new_eqs.size(), new_eqs.data()); + cond = m.mk_and(new_eqs); def = m.mk_ite(cond, new_t, def); } } From a81a5ec68c6479155a7fe82bba27b74765342e1c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:46:31 +0700 Subject: [PATCH 092/597] add virtual function requirement to dependent_expr_state --- src/ast/simplifiers/dependent_expr_state.h | 1 + src/tactic/dependent_expr_state_tactic.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index fa4bbdd498c..b94b422ad73 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -46,6 +46,7 @@ class 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; + virtual void add(dependent_expr const& j) = 0; virtual bool inconsistent() = 0; virtual model_reconstruction_trail& model_trail() = 0; diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index d3f77b7732e..efe28c30d40 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -60,10 +60,16 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { 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); } + + void add(dependent_expr const& j) override { + auto [f, d] = j(); + m_goal->assert_expr(f, nullptr, d); + } bool inconsistent() override { return m_goal->inconsistent(); From d735faae4eaeb981694b8bf0229374a85f016305 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:50:37 +0700 Subject: [PATCH 093/597] add isolated hide/add model converter functions --- .../model_reconstruction_trail.cpp | 47 +++++-------------- .../simplifiers/model_reconstruction_trail.h | 33 ++++++++++--- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index a5f1cf8b030..55d04621ff6 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -30,8 +30,9 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorm_active) continue; - if (t->m_hide) + if (t->is_hide()) continue; + // updates that have no intersections with current variables are skipped if (!t->intersects(free_vars)) @@ -48,6 +49,9 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectoris_def()) + NOT_IMPLEMENTED_YET(); + rp->set_substitution(t->m_subst.get()); // rigid entries: // apply substitution to added in case of rigid model convertions @@ -63,48 +67,23 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vector 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; ) { + for (unsigned i = 0; i < m_trail.size(); ++i) { auto* t = m_trail[i]; if (!t->m_active) continue; - - if (t->m_hide) { - mc->hide(t->m_hide); - 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); + else if (t->is_hide()) + mc->hide(t->m_decl); + else if (t->is_def()) + mc->add(t->m_decl, t->m_def); + else { + for (auto const& [v, def] : t->m_subst->sub()) mc->add(v, def); - } - continue; - } - - for (auto const& [v, def] : t->m_subst->sub()) { - 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); - 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 eeef136ee95..2ff11227e45 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -35,22 +35,35 @@ class model_reconstruction_trail { struct entry { scoped_ptr m_subst; vector m_removed; - func_decl* m_hide = nullptr; + func_decl_ref m_decl; + expr_ref m_def; bool m_active = true; - entry(expr_substitution* s, vector const& rem) : - m_subst(s), m_removed(rem) {} - entry(func_decl* h) : m_hide(h) {} + entry(ast_manager& m, expr_substitution* s, vector const& rem) : + m_subst(s), m_removed(rem), m_decl(m), m_def(m) {} + + entry(ast_manager& m, func_decl* h) : m_decl(h, m), m_def(m) {} + + entry(ast_manager& m, func_decl* f, expr* def, vector const& rem) : + m_decl(f, m), m_def(def, m), m_removed(rem) {} bool is_loose() const { return !m_removed.empty(); } bool intersects(ast_mark const& free_vars) const { + if (is_hide()) + return false; + if (is_def()) + return free_vars.is_marked(m_decl); for (auto const& [k, v] : m_subst->sub()) if (free_vars.is_marked(k)) return true; return false; } + + bool is_hide() const { return m_decl && !m_def; } + bool is_def() const { return m_decl && m_def; } + bool is_subst() const { return !m_decl; } }; ast_manager& m; @@ -81,7 +94,7 @@ class model_reconstruction_trail { * add a new substitution to the trail */ void push(expr_substitution* s, vector const& removed) { - m_trail.push_back(alloc(entry, s, removed)); + m_trail.push_back(alloc(entry, m, s, removed)); m_trail_stack.push(push_back_vector(m_trail)); } @@ -89,7 +102,15 @@ class model_reconstruction_trail { * add declaration to hide */ void push(func_decl* f) { - m_trail.push_back(alloc(entry, f)); + m_trail.push_back(alloc(entry, m, f)); + m_trail_stack.push(push_back_vector(m_trail)); + } + + /** + * add definition + */ + void push(func_decl* f, expr* def, vector const& removed) { + m_trail.push_back(alloc(entry, m, f, def, removed)); m_trail_stack.push(push_back_vector(m_trail)); } From 771157696b451ded4040de9ab42467dc483f8445 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:51:20 +0700 Subject: [PATCH 094/597] new simplifier/tactic eliminate_predicates finds macros and eliminates predicates from formulas as pre-processing. --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/eliminate_predicates.cpp | 664 ++++++++++++++++++ src/ast/simplifiers/eliminate_predicates.h | 143 ++++ src/tactic/core/CMakeLists.txt | 1 + src/tactic/core/eliminate_predicates_tactic.h | 40 ++ 5 files changed, 849 insertions(+) create mode 100644 src/ast/simplifiers/eliminate_predicates.cpp create mode 100644 src/ast/simplifiers/eliminate_predicates.h create mode 100644 src/tactic/core/eliminate_predicates_tactic.h diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 75aa1ec7a87..b636b8b4214 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -2,6 +2,7 @@ z3_add_component(simplifiers SOURCES bv_slice.cpp elim_unconstrained.cpp + eliminate_predicates.cpp euf_completion.cpp extract_eqs.cpp model_reconstruction_trail.cpp diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp new file mode 100644 index 00000000000..57fe4b4f071 --- /dev/null +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -0,0 +1,664 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + eliminate_predicates.cpp + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-17. + +Notes: + +The simplifier +- detects macros of the form p(x) = q(x) + - other more general macro detection is TBD. + For example {~p, a} {~p, b} {p, ~a, ~b} {p, C} {~p, D} defines p as a conjunction + and we can obbtain {a, C}, {b, C} {~a, ~b, D } similar to propositional case. + Instead the case is handled by predicate elimination when p only occurs positively + outside of {~p, a} {~p, b} {p, ~a, ~b} + - other SMT-based macro detection could be made here as well. + The (legacy) macro finder is not very flexible and could be replaced + by a module building on this one. +- eliminates predicates p(x) that occur at most once in each clause and the + number of occurrences is small. + +Two sets of disabled functions are tracked: + +forbidden from macros vs forbidden from elimination + - forbidden from macros: uninterpreted functions in recursive definitions + predicates before m_qhead + arguments to as-array + - forbidden from elimination: + - forbidden from macros, + - occurs more than once in some clause, or in nested occurrence. + +--*/ + + +#include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" +#include "ast/ast_util.h" +#include "ast/for_each_ast.h" +#include "ast/recfun_decl_plugin.h" +#include "ast/occurs.h" +#include "ast/array_decl_plugin.h" +#include "ast/rewriter/var_subst.h" +#include "ast/rewriter/rewriter_def.h" +#include "ast/simplifiers/eliminate_predicates.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/macros/macro_util.h" + + +/** +* Rewriting formulas using macro definitions. +*/ +struct eliminate_predicates::macro_expander_cfg : public default_rewriter_cfg { + ast_manager& m; + eliminate_predicates& ep; + expr_dependency_ref& m_used_macro_dependencies; + expr_ref_vector m_trail; + + macro_expander_cfg(ast_manager& m, eliminate_predicates& ep, expr_dependency_ref& deps) : + m(m), + ep(ep), + m_used_macro_dependencies(deps), + m_trail(m) + {} + + bool rewrite_patterns() const { return false; } + bool flat_assoc(func_decl* f) const { return false; } + br_status reduce_app(func_decl* f, unsigned num, expr* const* args, expr_ref& result, proof_ref& result_pr) { + result_pr = nullptr; + return BR_FAILED; + } + + /** + * adapted from macro_manager.cpp + */ + bool reduce_quantifier(quantifier* old_q, + expr* new_body, + expr* const* new_patterns, + expr* const* new_no_patterns, + expr_ref& result, + proof_ref& result_pr) { + + bool erase_patterns = false; + for (unsigned i = 0; !erase_patterns && i < old_q->get_num_patterns(); i++) + if (old_q->get_pattern(i) != new_patterns[i]) + erase_patterns = true; + + for (unsigned i = 0; !erase_patterns && i < old_q->get_num_no_patterns(); i++) + if (old_q->get_no_pattern(i) != new_no_patterns[i]) + erase_patterns = true; + + if (erase_patterns) + result = m.update_quantifier(old_q, 0, nullptr, 0, nullptr, new_body); + + if (erase_patterns && m.proofs_enabled()) + result_pr = m.mk_rewrite(old_q, result); + + return erase_patterns; + } + + bool get_subst(expr* _n, expr*& r, proof*& p) { + if (!is_app(_n)) + return false; + p = nullptr; + app* n = to_app(_n); + quantifier* q = nullptr; + func_decl* d = n->get_decl(), * d2 = nullptr; + app_ref head(m); + expr_ref def(m); + expr_dependency_ref dep(m); + if (ep.has_macro(d, head, def, dep)) { + unsigned num = head->get_num_args(); + ptr_buffer subst_args; + subst_args.resize(num, 0); + // TODO: we can exploit that variables occur in "non-standard" order + // that is in order (:var 0) (:var 1) (:var 2) + // then substitution just takes n->get_args() instead of this renaming. + for (unsigned i = 0; i < num; i++) { + var* v = to_var(head->get_arg(i)); + VERIFY(v->get_idx() < num); + unsigned nidx = num - v->get_idx() - 1; + SASSERT(subst_args[nidx] == 0); + subst_args[nidx] = n->get_arg(i); + } + var_subst s(m); + expr_ref rr = s(def, num, subst_args.data()); + r = rr; + m_trail.push_back(rr); + m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, dep); + return true; + } + + return false; + } +}; + +struct eliminate_predicates::macro_expander_rw : public rewriter_tpl { + eliminate_predicates::macro_expander_cfg m_cfg; + + macro_expander_rw(ast_manager& m, eliminate_predicates& ep, expr_dependency_ref& deps) : + rewriter_tpl(m, false, m_cfg), + m_cfg(m, ep, deps) + {} +}; + + +std::ostream& eliminate_predicates::clause::display(std::ostream& out) const { + ast_manager& m = m_dep.get_manager(); + for (sort* s : m_bound) + out << mk_pp(s, m) << " "; + for (auto const& [atom, sign] : m_literals) + out << (sign ? "~" : "") << mk_bounded_pp(atom, m) << " "; + return out; +} + +eliminate_predicates::eliminate_predicates(ast_manager& m, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), m_der(m), m_rewriter(m) {} + + +void eliminate_predicates::add_use_list(clause& cl) { + ast_mark seen; + for (auto const& [atom, sign] : cl.m_literals) { + if (!is_uninterp(atom)) { + m_to_exclude.push_back(atom); + continue; + } + + func_decl* p = to_app(atom)->get_decl(); + m_use_list.get(p, sign).push_back(&cl); + + if (!m_predicate_decls.is_marked(p)) { + m_predicates.push_back(p); + m_predicate_decls.mark(p, true); + } + if (seen.is_marked(p)) + m_to_exclude.push_back(atom); + else { + seen.mark(p, true); + m_to_exclude.append(to_app(atom)->get_num_args(), to_app(atom)->get_args()); + } + } +} + +/** +* cheap/simplistic heuristic to find definitions that are based on binary clauses +* (or (head x) (not (def x)) +* (or (not (head x)) (def x)) +*/ +bool eliminate_predicates::try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep) { + if (m_disable_macro.is_marked(p)) + return false; + expr_mark binary_pos, binary_neg; + macro_util mutil(m); + obj_map deps; + auto is_def_predicate = [&](expr* atom) { + return is_app(atom) && to_app(atom)->get_decl() == p && mutil.is_macro_head(atom, p->get_arity()); + }; + auto add_def = [&](clause& cl, expr* atom1, bool sign1, expr* atom2, bool sign2) { + if (is_def_predicate(atom1) && !sign1) { + if (sign2) + binary_neg.mark(atom2); + else + binary_pos.mark(atom2); + if (cl.m_dep) + deps.insert(atom1, cl.m_dep); + } + }; + + for (auto* cl : m_use_list.get(p, false)) { + if (cl->m_alive && cl->m_literals.size() == 2) { + auto const& [atom1, sign1] = cl->m_literals[0]; + auto const& [atom2, sign2] = cl->m_literals[1]; + add_def(*cl, atom1, sign1, atom2, sign2); + add_def(*cl, atom2, sign2, atom1, sign1); + } + } + + auto is_def = [&](unsigned i, unsigned j, clause& cl) { + auto const& [atom1, sign1] = cl.m_literals[i]; + auto const& [atom2, sign2] = cl.m_literals[j]; + expr_dependency* d = nullptr; + if (is_def_predicate(atom1) && sign1) { + if (sign2 && binary_pos.is_marked(atom2) && is_macro_safe(atom2) && !occurs(p, atom2)) { + head = to_app(atom1); + def = m.mk_not(atom2); + dep = cl.m_dep; + if (deps.find(atom1, d)) + dep = m.mk_join(dep, d); + return true; + } + if (!sign2 && binary_neg.is_marked(atom2) && is_macro_safe(atom2) && !occurs(p, atom2)) { + head = to_app(atom1); + def = atom2; + dep = cl.m_dep; + if (deps.find(atom1, d)) + dep = m.mk_join(dep, d); + return true; + } + } + return false; + }; + + for (auto* cl : m_use_list.get(p, true)) { + if (cl->m_alive && cl->m_literals.size() == 2) { + if (is_def(0, 1, *cl)) + return true; + if (is_def(1, 0, *cl)) + return true; + } + } + return false; +} + +bool eliminate_predicates::is_macro_safe(expr* e) { + for (expr* arg : subterms::all(expr_ref(e, m))) + if (is_app(arg) && m_is_macro.is_marked(to_app(arg)->get_decl())) + return false; + return true; +} + +void eliminate_predicates::insert_macro(app_ref& head, expr_ref& def, expr_dependency_ref& dep) { + unsigned num = head->get_num_args(); + ptr_buffer vars, subst_args; + subst_args.resize(num, nullptr); + vars.resize(num, nullptr); + for (unsigned i = 0; i < num; i++) { + var* v = to_var(head->get_arg(i)); + var* w = m.mk_var(i, v->get_sort()); + unsigned idx = v->get_idx(); + VERIFY(idx < num); + SASSERT(subst_args[idx] == 0); + subst_args[idx] = w; + vars[i] = w; + } + var_subst sub(m, false); + def = sub(def, subst_args.size(), subst_args.data()); + head = m.mk_app(head->get_decl(), vars); + auto* info = alloc(macro_def, head, def, dep); + m_macros.insert(head->get_decl(), info); + m_fmls.model_trail().push(head->get_decl(), def, {}); + m_is_macro.mark(head->get_decl(), true); + TRACE("elim_predicates", tout << "insert " << head << " " << def << "\n"); + ++m_stats.m_num_macros; +} + +void eliminate_predicates::try_resolve_definition(func_decl* p) { + app_ref head(m); + expr_ref def(m); + expr_dependency_ref dep(m); + if (try_find_binary_definition(p, head, def, dep)) + insert_macro(head, def, dep); +} + +bool eliminate_predicates::has_macro(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep) { + macro_def* md = nullptr; + if (m_macros.find(p, md)) { + head = md->m_head; + def = md->m_def; + dep = md->m_dep; + return true; + } + return false; +} + +void eliminate_predicates::find_definitions() { + for (auto* p : m_predicates) + try_resolve_definition(p); +} + +void eliminate_predicates::rewrite(expr_ref& t) { + proof_ref pr(m); + m_der(t, t, pr); + m_rewriter(t); +} + +void eliminate_predicates::reduce_definitions() { + if (m_macros.empty()) + return; + + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + auto [f, d] = m_fmls[i](); + expr_ref fml(f, m), new_fml(m); + expr_dependency_ref dep(m); + while (true) { + macro_expander_rw macro_expander(m, *this, dep); + macro_expander(fml, new_fml); + if (new_fml == fml) + break; + rewrite(new_fml); + fml = new_fml; + } + if (fml != f) { + dep = m.mk_join(d, dep); + m_fmls.update(i, dependent_expr(m, fml, dep)); + } + } + reset(); + init_clauses(); +} + +void eliminate_predicates::try_resolve(func_decl* p) { + if (m_disable_elimination.is_marked(p)) + return; + if (m_disable_macro.is_marked(p)) + return; + + unsigned num_pos = 0, num_neg = 0; + for (auto* cl : m_use_list.get(p, false)) + if (cl->m_alive) + ++num_pos; + for (auto* cl : m_use_list.get(p, true)) + if (cl->m_alive) + ++num_neg; + + TRACE("elim_predicates", tout << "try resolve " << p->get_name() << " " << num_pos << " " << num_neg << "\n"); + IF_VERBOSE(0, verbose_stream() << "try resolve " << p->get_name() << " " << num_pos << " " << num_neg << "\n"); + // TODO - probe for a definition + // generally, probe for binary clause equivalences in binary implication graph + + if (num_pos >= 4 && num_neg >= 2) + return; + if (num_neg >= 4 && num_pos >= 2) + return; + if (num_neg >= 3 && num_pos >= 3) + return; + + for (auto* pos : m_use_list.get(p, false)) { + for (auto* neg : m_use_list.get(p, true)) { + clause* cl = resolve(p, *pos, *neg); + if (!cl) + continue; + m_clauses.push_back(cl); + add_use_list(*cl); + process_to_exclude(m_disable_elimination); + IF_VERBOSE(11, verbose_stream() << "resolve " << p->get_name() << "\n" << *pos << "\n" << *neg << "\n------\n" << *cl << "\n\n"); + } + } + + update_model(p); + + for (auto* pos : m_use_list.get(p, false)) + pos->m_alive = false; + for (auto* neg : m_use_list.get(p, true)) + neg->m_alive = false; + + ++m_stats.m_num_eliminated; +} + +// +// update model for p +// +// Example, ground case: +// {p, a} {p, b} {-p, c}, {-p, d} +// p <=> !(a & b) +// p <=> c & d +// +// Example non-ground cases +// {p(t)} {p(s)} {~p(u)} +// p(x) <=> (x = t or x = s) +// p(x) <=> x != u +// +// {p(t), a, b} +// p(x) <=> (x = t & !(a or b)) +// +// {~p(t), a, b} +// ~p(x) <=> (x = t & !(a or b)) +// p(x) <=> x = t => a or b +// + +void eliminate_predicates::update_model(func_decl* p) { + expr_ref_vector fmls(m); + expr_ref def(m); + unsigned numpos = 0, numneg = 0; + vector deleted; + for (auto* pos : m_use_list.get(p, false)) + if (pos->m_alive) + ++numpos; + for (auto* neg : m_use_list.get(p, true)) + if (neg->m_alive) + ++numneg; + + if (numpos < numneg) { + for (auto* pos : m_use_list.get(p, false)) + if (pos->m_alive) + fmls.push_back(create_residue_formula(p, *pos)); + def = mk_or(fmls); + } + else { + for (auto* neg : m_use_list.get(p, true)) + if (neg->m_alive) + fmls.push_back(mk_not(m, create_residue_formula(p, *neg))); + def = mk_and(fmls); + } + + IF_VERBOSE(0, verbose_stream() << p->get_name() << " " << def << "\n"); + rewrite(def); + m_fmls.model_trail().push(p, def, deleted); +} + +/** +* Convert a clause that contains p(t) into a definition for p +* forall y . (or p(t) C) +* Into +* exists y . x = t[y] & !(or C) +*/ + +expr_ref eliminate_predicates::create_residue_formula(func_decl* p, clause& cl) { + unsigned num_args = p->get_arity(); + unsigned num_bound = cl.m_bound.size(); + expr_ref_vector ors(m), ands(m); + expr_ref fml(m); + app_ref patom(m); + for (auto const& [atom, sign] : cl.m_literals) { + if (is_app(atom) && to_app(atom)->get_decl() == p) { + SASSERT(!patom); + patom = to_app(atom); + continue; + } + fml = sign ? m.mk_not(atom) : atom.get(); + ors.push_back(fml); + } + if (!ors.empty()) { + fml = mk_not(m, mk_or(ors)); + ands.push_back(fml); + } + for (unsigned i = 0; i < num_args; ++i) { + SASSERT(patom->get_arg(i)->get_sort() == p->get_domain(i)); + ands.push_back(m.mk_eq(patom->get_arg(i), m.mk_var(num_bound + i, p->get_domain(i)))); + } + fml = m.mk_and(ands); + if (num_bound > 0) { + svector names; + for (unsigned i = 0; i < num_bound; ++i) + names.push_back(symbol(i)); + fml = m.mk_exists(num_bound, cl.m_bound.data(), names.data(), fml, 1); + } + IF_VERBOSE(0, verbose_stream() << fml << "\n"); + return fml; +} + +/** +* Resolve p in clauses pos, neg where p occurs only once. +*/ +eliminate_predicates::clause* eliminate_predicates::resolve(func_decl* p, clause& pos, clause& neg) { + var_shifter sh(m); + expr_dependency_ref dep(m); + dep = m.mk_join(pos.m_dep, neg.m_dep); + expr_ref new_lit(m); + expr_ref_vector lits(m); + expr* plit = nullptr, * nlit = nullptr; + + for (auto const& [lit, sign] : pos.m_literals) + if (is_app(lit) && to_app(lit)->get_decl() == p) + plit = lit; + else + lits.push_back(sign ? m.mk_not(lit) : lit.get()); + for (auto const & [lit, sign] : neg.m_literals) { + if (is_app(lit) && to_app(lit)->get_decl() == p) + nlit = lit; + else { + sh(lit, pos.m_bound.size(), new_lit); + lits.push_back(sign ? m.mk_not(new_lit) : new_lit.get()); + } + } + sh(nlit, pos.m_bound.size(), new_lit); + for (unsigned i = 0; i < p->get_arity(); ++i) { + expr* a = to_app(plit)->get_arg(i); + expr* b = to_app(new_lit)->get_arg(i); + if (a != b) + lits.push_back(m.mk_not(m.mk_eq(a, b))); + } + + expr_ref cl = mk_or(lits); + ptr_vector bound; + bound.append(neg.m_bound); + bound.append(pos.m_bound); + if (!bound.empty()) { + svector names; + for (unsigned i = 0; i < bound.size(); ++i) + names.push_back(symbol(i)); + cl = m.mk_forall(bound.size(), bound.data(), names.data(), cl, 1); + } + rewrite(cl); + if (m.is_true(cl)) + return nullptr; + return init_clause(cl, dep, UINT_MAX); +} + +void eliminate_predicates::try_resolve() { + for (auto* f : m_predicates) + try_resolve(f); +} + +/** +* Process the terms m_to_exclude, walk all subterms. +* Uninterpreted function declarations in these terms are added to 'exclude_set' +* Uninterpreted function declarations from as-array terms are added to 'm_disable_macro' +*/ +void eliminate_predicates::process_to_exclude(ast_mark& exclude_set) { + ast_mark visited; + array_util a(m); + + struct proc { + array_util& a; + ast_mark& to_exclude; + ast_mark& to_disable; + proc(array_util& a, ast_mark& f, ast_mark& d) : + a(a), to_exclude(f), to_disable(d) {} + void operator()(func_decl* f) { + if (is_uninterp(f)) + to_exclude.mark(f, true); + } + void operator()(app* e) { + func_decl* f; + if (a.is_as_array(e, f) && is_uninterp(f)) + to_disable.mark(f, true); + } + void operator()(ast* s) {} + }; + proc proc(a, exclude_set, m_disable_macro); + + for (expr* e : m_to_exclude) + for_each_ast(proc, visited, e); + m_to_exclude.reset(); +} + + +eliminate_predicates::clause* eliminate_predicates::init_clause(unsigned i) { + auto [f, d] = m_fmls[i](); + return init_clause(f, d, i); +} + +/** +* Create a clause from a formula. +*/ +eliminate_predicates::clause* eliminate_predicates::init_clause(expr* f, expr_dependency* d, unsigned i) { + clause* cl = alloc(clause, m, d); + cl->m_fml = f; + cl->m_fml_index = i; + while (is_forall(f)) { + cl->m_bound.append(to_quantifier(f)->get_num_decls(), to_quantifier(f)->get_decl_sorts()); + f = to_quantifier(f)->get_expr(); + } + expr_ref_vector ors(m); + flatten_or(f, ors); + for (expr* lit : ors) { + bool sign = m.is_not(lit, lit); + cl->m_literals.push_back({ expr_ref(lit, m), sign }); + } + return cl; +} + +/** +* functions in the prefix of qhead are fully disabled +* Create clauses from the suffix, and process subeterms of clauses to be disabled from +* eliminations. +*/ +void eliminate_predicates::init_clauses() { + for (unsigned i = 0; i < m_qhead; ++i) + m_to_exclude.push_back(m_fmls[i].fml()); + recfun::util rec(m); + if (rec.has_rec_defs()) + for (auto& d : rec.get_rec_funs()) + m_to_exclude.push_back(rec.get_def(d).get_rhs()); + + process_to_exclude(m_disable_macro); + + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + clause* cl = init_clause(i); + add_use_list(*cl); + m_clauses.push_back(cl); + } + process_to_exclude(m_disable_elimination); +} + +/** +* Convert clauses to m_fmls +*/ +void eliminate_predicates::decompile() { + for (clause* cl : m_clauses) { + if (m_fmls.inconsistent()) + break; + if (cl->m_fml_index != UINT_MAX) { + if (cl->m_alive) + continue; + dependent_expr de(m, m.mk_true(), nullptr); + m_fmls.update(cl->m_fml_index, de); + } + else if (cl->m_alive) { + expr_ref new_cl = cl->m_fml; + dependent_expr de(m, new_cl, cl->m_dep); + m_fmls.add(de); + } + } +} + +void eliminate_predicates::reset() { + m_predicates.reset(); + m_predicate_decls.reset(); + m_to_exclude.reset(); + m_disable_macro.reset(); + m_disable_elimination.reset(); + m_is_macro.reset(); + for (auto const& [k, v] : m_macros) + dealloc(v); + m_macros.reset(); + m_clauses.reset(); + m_use_list.reset(); +} + + +void eliminate_predicates::reduce() { + reset(); + init_clauses(); + find_definitions(); + reduce_definitions(); + try_resolve(); + decompile(); + reset(); +} diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h new file mode 100644 index 00000000000..5c7cf7e958c --- /dev/null +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -0,0 +1,143 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + eliminate_predicates.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +Notes: + + Eliminate predicates through Davis-Putnam rules + + (forall (x y) (or (P x) Q)) (forall (x y) (or (not (P x)) R)) +is converted to + (forall (x y) (or Q R)) +when P occurs only in positive or only in negative polarities and the +expansion does not increase the formula size. + +Macros are also eliminated + + +create clause abstractions, index into fmls, indicator if it was removed +map from predicates to clauses where they occur in unitary role. +process predicates to check if they can be eliminated, creating new clauses and updated use-list. + + +--*/ + + +#pragma once + +#include "util/scoped_ptr_vector.h" +#include "ast/rewriter/der.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/simplifiers/dependent_expr_state.h" + + +class eliminate_predicates : public dependent_expr_simplifier { + +public: + struct clause { + ptr_vector m_bound; // bound variables + vector> m_literals; // clause literals + expr_dependency_ref m_dep; // dependencies + expr_ref m_fml; // formula corresponding to clause + unsigned m_fml_index = UINT_MAX; // index of formula where clause came from + bool m_alive = true; + + clause(ast_manager& m, expr_dependency* d) : + m_dep(d, m), m_fml(m) + {} + + std::ostream& display(std::ostream& out) const; + }; +private: + struct stats { + unsigned m_num_eliminated = 0; + unsigned m_num_macros = 0; + void reset() { m_num_eliminated = 0; m_num_macros = 0; } + }; + + struct macro_def { + app_ref m_head; + expr_ref m_def; + expr_dependency_ref m_dep; + macro_def(app_ref& head, expr_ref& def, expr_dependency_ref& dep) : + m_head(head), m_def(def), m_dep(dep) {} + }; + + typedef ptr_vector clause_use_list; + + class use_list { + vector m_use_list; + unsigned index(func_decl* f, bool sign) const { return 2*f->get_small_id() + sign; } + void reserve(func_decl* f, bool sign) { + m_use_list.reserve(index(f, sign) + 3); + } + public: + clause_use_list& get(func_decl* f, bool sign) { reserve(f, sign); return m_use_list[index(f, sign)]; } + void reset() { m_use_list.reset(); } + }; + + scoped_ptr_vector m_clauses; + ast_mark m_disable_elimination, m_disable_macro, m_predicate_decls, m_is_macro; + ptr_vector m_predicates; + ptr_vector m_to_exclude; + stats m_stats; + use_list m_use_list; + der_rewriter m_der; + th_rewriter m_rewriter; + obj_map m_macros; + + struct macro_expander_cfg; + struct macro_expander_rw; + + void rewrite(expr_ref& t); + + clause* init_clause(unsigned i); + clause* init_clause(expr* f, expr_dependency* d, unsigned i); + clause* resolve(func_decl* p, clause& pos, clause& neg); + void add_use_list(clause& cl); + + bool try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); + void try_resolve_definition(func_decl* p); + void insert_macro(app_ref& head, expr_ref& def, expr_dependency_ref& dep); + bool has_macro(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); + bool is_macro_safe(expr* e); + + void try_resolve(func_decl* p); + void update_model(func_decl* p); + expr_ref create_residue_formula(func_decl* p, clause& cl); + void process_to_exclude(ast_mark&); + + void init_clauses(); + void find_definitions(); + void reduce_definitions(); + void try_resolve(); + void decompile(); + void reset(); + +public: + + eliminate_predicates(ast_manager& m, dependent_expr_state& fmls); + + ~eliminate_predicates() override { reset(); } + + void reduce() override; + + void collect_statistics(statistics& st) const override { + st.update("elim-predicates", m_stats.m_num_eliminated); + st.update("elim-predicates-macros", m_stats.m_num_macros); + } + + void reset_statistics() override { m_stats.reset(); } +}; + + +inline std::ostream& operator<<(std::ostream& out, eliminate_predicates::clause const& c) { + return c.display(out); +} \ No newline at end of file diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index 6bb6793fd68..39e8def031b 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -38,6 +38,7 @@ z3_add_component(core_tactics elim_term_ite_tactic.h elim_uncnstr_tactic.h elim_uncnstr2_tactic.h + eliminate_predicates_tactic.h euf_completion_tactic.h injectivity_tactic.h nnf_tactic.h diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h new file mode 100644 index 00000000000..3daffb1f32d --- /dev/null +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + eliminate_predicates_tactic.h + +Abstract: + + Tactic for eliminating macros and predicates + +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/eliminate_predicates.h" + + +class eliminate_predicates_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(eliminate_predicates, m, s); + } +}; + +inline tactic * mk_eliminate_predicates_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(eliminate_predicates_tactic_factory), "elim-predicates"); +} + +/* + ADD_TACTIC("elim-predicates", "eliminate predicates.", "mk_eliminate_predicates_tactic(m, p)") +*/ + + From 3f1093322501908e919fa62b90444aa3620fad60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:55:01 +0700 Subject: [PATCH 095/597] remove VERBOSE 0 --- src/ast/simplifiers/eliminate_predicates.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 57fe4b4f071..3b14c8fa5a2 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -357,7 +357,6 @@ void eliminate_predicates::try_resolve(func_decl* p) { ++num_neg; TRACE("elim_predicates", tout << "try resolve " << p->get_name() << " " << num_pos << " " << num_neg << "\n"); - IF_VERBOSE(0, verbose_stream() << "try resolve " << p->get_name() << " " << num_pos << " " << num_neg << "\n"); // TODO - probe for a definition // generally, probe for binary clause equivalences in binary implication graph @@ -436,7 +435,6 @@ void eliminate_predicates::update_model(func_decl* p) { def = mk_and(fmls); } - IF_VERBOSE(0, verbose_stream() << p->get_name() << " " << def << "\n"); rewrite(def); m_fmls.model_trail().push(p, def, deleted); } @@ -478,7 +476,6 @@ expr_ref eliminate_predicates::create_residue_formula(func_decl* p, clause& cl) names.push_back(symbol(i)); fml = m.mk_exists(num_bound, cl.m_bound.data(), names.data(), fml, 1); } - IF_VERBOSE(0, verbose_stream() << fml << "\n"); return fml; } From 251d49d13317998df7e353cdea889a71dbb3569b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 18:55:30 +0700 Subject: [PATCH 096/597] remove outdated comment --- src/ast/simplifiers/eliminate_predicates.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 3b14c8fa5a2..605ed1b1029 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -357,8 +357,6 @@ void eliminate_predicates::try_resolve(func_decl* p) { ++num_neg; TRACE("elim_predicates", tout << "try resolve " << p->get_name() << " " << num_pos << " " << num_neg << "\n"); - // TODO - probe for a definition - // generally, probe for binary clause equivalences in binary implication graph if (num_pos >= 4 && num_neg >= 2) return; From c3c45f495a359975bb616375c3a7e0029f37c7ee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 19:45:25 +0700 Subject: [PATCH 097/597] add some comments to elim_predicates --- src/ast/simplifiers/eliminate_predicates.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 605ed1b1029..43486dcb043 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -18,9 +18,17 @@ The simplifier and we can obbtain {a, C}, {b, C} {~a, ~b, D } similar to propositional case. Instead the case is handled by predicate elimination when p only occurs positively outside of {~p, a} {~p, b} {p, ~a, ~b} + The SAT based definition detection scheme creates clauses + {a}, {b}, {~a,~b}, C, D and checks for an unsat core. + The core {a}, {b}, {~a, ~b} maps back to a definition for p + Then {p, C}, {~p, D} clauses are replaced based on the definition. + (a task for a "Kitten"?) + - Handle various permutations of variables that are arguments to p? - other SMT-based macro detection could be made here as well. The (legacy) macro finder is not very flexible and could be replaced by a module building on this one. + + - eliminates predicates p(x) that occur at most once in each clause and the number of occurrences is small. @@ -76,6 +84,7 @@ struct eliminate_predicates::macro_expander_cfg : public default_rewriter_cfg { /** * adapted from macro_manager.cpp + * Perhaps hoist and combine? */ bool reduce_quantifier(quantifier* old_q, expr* new_body, @@ -131,6 +140,7 @@ struct eliminate_predicates::macro_expander_cfg : public default_rewriter_cfg { r = rr; m_trail.push_back(rr); m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, dep); + // skip proof terms for simplifiers return true; } From 86f37024033d419fd65844db4a3cdc077e57c024 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Nov 2022 19:46:34 +0700 Subject: [PATCH 098/597] prevent re-declaration of enumeration sort names preventing redeclaration of all ADT cases is not part of this update. --- src/api/api_datatype.cpp | 10 +++++++++- src/ast/datatype_decl_plugin.cpp | 31 +++++++++++++++++-------------- src/ast/datatype_decl_plugin.h | 2 ++ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp index f1c65b626aa..5802c1e644f 100644 --- a/src/api/api_datatype.cpp +++ b/src/api/api_datatype.cpp @@ -102,6 +102,13 @@ extern "C" { sort* e; ptr_vector constrs; + symbol sname = to_symbol(name); + + if (mk_c(c)->get_dt_plugin()->is_declared(sname)) { + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + RETURN_Z3(nullptr); + } + for (unsigned i = 0; i < n; ++i) { symbol e_name(to_symbol(enum_names[i])); std::string recognizer_s("is_"); @@ -112,8 +119,9 @@ extern "C" { } + { - datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, n, constrs.data()); + datatype_decl * dt = mk_datatype_decl(dt_util, sname, 0, nullptr, n, constrs.data()); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, nullptr, sorts); del_datatype_decl(dt); diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index 041d6740ab5..d0214d44f79 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -832,6 +832,10 @@ namespace datatype { bool util::is_declared(sort* s) const { return plugin().is_declared(s); } + + bool util::is_declared(symbol const& n) const { + return plugin().is_declared(n); + } void util::compute_datatype_size_functions(svector const& names) { map already_found; @@ -1087,11 +1091,9 @@ namespace datatype { sort * datatype = con->get_range(); def const& dd = get_def(datatype); symbol r; - for (constructor const* c : dd) { - if (c->name() == con->get_name()) { - r = c->recognizer(); - } - } + for (constructor const* c : dd) + if (c->name() == con->get_name()) + r = c->recognizer(); parameter ps[2] = { parameter(con), parameter(r) }; d = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype); SASSERT(d); @@ -1142,17 +1144,15 @@ namespace datatype { } bool util::is_enum_sort(sort* s) { - if (!is_datatype(s)) { - return false; - } + if (!is_datatype(s)) + return false; bool r = false; if (m_is_enum.find(s, r)) return r; ptr_vector const& cnstrs = *get_datatype_constructors(s); r = true; - for (unsigned i = 0; r && i < cnstrs.size(); ++i) { - r = cnstrs[i]->get_arity() == 0; - } + for (unsigned i = 0; r && i < cnstrs.size(); ++i) + r = cnstrs[i]->get_arity() == 0; m_is_enum.insert(s, r); m_asts.push_back(s); return r; @@ -1284,11 +1284,14 @@ namespace datatype { unsigned idx = 0; def const& d = get_def(f->get_range()); for (constructor* c : d) { - if (c->name() == f->get_name()) { - return idx; - } + if (c->name() == f->get_name()) + return idx; ++idx; } + IF_VERBOSE(0, verbose_stream() << f->get_name() << "\n"); + for (constructor* c : d) + IF_VERBOSE(0, verbose_stream() << "!= " << c->name() << "\n"); + SASSERT(false); UNREACHABLE(); return 0; } diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 4561dfacf35..7229636cb40 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -253,6 +253,7 @@ namespace datatype { ptr_vector get_constructors(symbol const& s) const; ptr_vector get_accessors(symbol const& s) const; bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); } + bool is_declared(symbol const& n) const { return m_defs.contains(n); } unsigned get_axiom_base_id(symbol const& s) { return m_axiom_bases[s]; } util & u() const; @@ -375,6 +376,7 @@ namespace datatype { bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f); void reset(); bool is_declared(sort* s) const; + bool is_declared(symbol const& n) const; void display_datatype(sort *s, std::ostream& strm); bool is_fully_interp(sort * s) const; sort_ref_vector datatype_params(sort * s) const; From fcaa85d7a8e48e78c739c40e7c16fb4898a83216 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 20 Nov 2022 11:27:39 +0700 Subject: [PATCH 099/597] #6456 - elaborate on error message --- src/api/api_datatype.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp index 5802c1e644f..71d1de21210 100644 --- a/src/api/api_datatype.cpp +++ b/src/api/api_datatype.cpp @@ -105,7 +105,7 @@ extern "C" { symbol sname = to_symbol(name); if (mk_c(c)->get_dt_plugin()->is_declared(sname)) { - SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + SET_ERROR_CODE(Z3_INVALID_ARG, "enumeration sort name is already declared"); RETURN_Z3(nullptr); } From b9f34286a7fd23a89d7d8735daad67b2c2676f38 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 20 Nov 2022 11:36:45 +0700 Subject: [PATCH 100/597] generalize macro head detection and elaboration --- src/ast/simplifiers/eliminate_predicates.cpp | 59 +++++++++++++++++--- src/ast/simplifiers/eliminate_predicates.h | 2 + 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 43486dcb043..a46eda102a8 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -22,6 +22,7 @@ The simplifier {a}, {b}, {~a,~b}, C, D and checks for an unsat core. The core {a}, {b}, {~a, ~b} maps back to a definition for p Then {p, C}, {~p, D} clauses are replaced based on the definition. + Claim: {C, D} is a consequence when we have created resolvents {C,X}, {D,Y}, where X => p => Y => X (a task for a "Kitten"?) - Handle various permutations of variables that are arguments to p? - other SMT-based macro detection could be made here as well. @@ -45,6 +46,7 @@ forbidden from macros vs forbidden from elimination --*/ +#include "util/uint_set.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" @@ -56,7 +58,6 @@ forbidden from macros vs forbidden from elimination #include "ast/rewriter/rewriter_def.h" #include "ast/simplifiers/eliminate_predicates.h" #include "ast/rewriter/th_rewriter.h" -#include "ast/macros/macro_util.h" /** @@ -195,6 +196,49 @@ void eliminate_predicates::add_use_list(clause& cl) { } } +/** +* Check that all arguments are distinct variables that are bound. +*/ + +bool eliminate_predicates::can_be_macro_head(app* head, unsigned num_bound) { + uint_set indices; + if (head->get_decl()->is_associative()) + return false; + for (expr* arg : *head) { + if (!is_var(arg)) + return false; + unsigned idx = to_var(arg)->get_idx(); + if (indices.contains(idx)) + return false; + if (idx >= num_bound) + return false; + indices.insert(idx); + } + return true; +} + +expr_ref eliminate_predicates::bind_free_variables_in_def(clause& cl, app* head, expr* def) { + unsigned num_bound = cl.m_bound.size(); + if (head->get_num_args() == num_bound) + return expr_ref(def, m); + + // head(x) <=> forall yx', x = x' => def(yx') + svector names; + expr_ref_vector ors(m); + expr_ref result(m); + ors.push_back(def); + + for (unsigned i = 0; i < num_bound; ++i) + names.push_back(symbol(i)); + for (expr* arg : *head) { + unsigned idx = to_var(arg)->get_idx(); + ors.push_back(m.mk_not(m.mk_eq(arg, m.mk_var(idx + num_bound, arg->get_sort())))); + } + result = mk_or(ors); + result = m.mk_forall(num_bound, cl.m_bound.data(), names.data(), result); + rewrite(result); + return result; +} /** * cheap/simplistic heuristic to find definitions that are based on binary clauses * (or (head x) (not (def x)) @@ -204,13 +248,12 @@ bool eliminate_predicates::try_find_binary_definition(func_decl* p, app_ref& hea if (m_disable_macro.is_marked(p)) return false; expr_mark binary_pos, binary_neg; - macro_util mutil(m); obj_map deps; - auto is_def_predicate = [&](expr* atom) { - return is_app(atom) && to_app(atom)->get_decl() == p && mutil.is_macro_head(atom, p->get_arity()); + auto is_def_predicate = [&](clause& cl, expr* atom) { + return is_app(atom) && to_app(atom)->get_decl() == p && can_be_macro_head(to_app(atom), cl.m_bound.size()); }; auto add_def = [&](clause& cl, expr* atom1, bool sign1, expr* atom2, bool sign2) { - if (is_def_predicate(atom1) && !sign1) { + if (is_def_predicate(cl, atom1) && !sign1) { if (sign2) binary_neg.mark(atom2); else @@ -233,10 +276,10 @@ bool eliminate_predicates::try_find_binary_definition(func_decl* p, app_ref& hea auto const& [atom1, sign1] = cl.m_literals[i]; auto const& [atom2, sign2] = cl.m_literals[j]; expr_dependency* d = nullptr; - if (is_def_predicate(atom1) && sign1) { + if (is_def_predicate(cl, atom1) && sign1) { if (sign2 && binary_pos.is_marked(atom2) && is_macro_safe(atom2) && !occurs(p, atom2)) { head = to_app(atom1); - def = m.mk_not(atom2); + def = bind_free_variables_in_def(cl, head, m.mk_not(atom2)); dep = cl.m_dep; if (deps.find(atom1, d)) dep = m.mk_join(dep, d); @@ -244,7 +287,7 @@ bool eliminate_predicates::try_find_binary_definition(func_decl* p, app_ref& hea } if (!sign2 && binary_neg.is_marked(atom2) && is_macro_safe(atom2) && !occurs(p, atom2)) { head = to_app(atom1); - def = atom2; + def = bind_free_variables_in_def(cl, head, atom2); dep = cl.m_dep; if (deps.find(atom1, d)) dep = m.mk_join(dep, d); diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index 5c7cf7e958c..ff11bfd3fde 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -107,6 +107,8 @@ class eliminate_predicates : public dependent_expr_simplifier { void try_resolve_definition(func_decl* p); void insert_macro(app_ref& head, expr_ref& def, expr_dependency_ref& dep); bool has_macro(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); + expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); + bool can_be_macro_head(app* head, unsigned num_bound); bool is_macro_safe(expr* e); void try_resolve(func_decl* p); From 0445d6f264a666ce38a96bf07ff980da2386ccee Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Nov 2022 19:03:32 +0000 Subject: [PATCH 101/597] FPA->BV fix unused vars --- src/ast/fpa/fpa2bv_converter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 18baba57c2d..21c71c8a955 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3288,7 +3288,7 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex void fpa2bv_converter::mk_to_ieee_bv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_IEEE_BV, 0, nullptr, num, args), m); - mk_to_bv(f, num, args, true, result); + mk_to_bv(fu, num, args, true, result); } void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) { @@ -3475,12 +3475,12 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg void fpa2bv_converter::mk_to_ubv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_UBV, 0, nullptr, num, args), m); - mk_to_bv(f, num, args, false, result); + mk_to_bv(fu, num, args, false, result); } void fpa2bv_converter::mk_to_sbv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_SBV, 0, nullptr, num, args), m); - mk_to_bv(f, num, args, true, result); + mk_to_bv(fu, num, args, true, result); } expr_ref fpa2bv_converter::nan_wrap(expr * n) { @@ -3529,7 +3529,7 @@ void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr void fpa2bv_converter::mk_to_real_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_REAL, 0, nullptr, num, args), m); - mk_to_real(f, num, args, result); + mk_to_real(fu, num, args, result); } void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { From 477b90228e57d619cd848c19feee646109c4a2c9 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Nov 2022 19:19:12 +0000 Subject: [PATCH 102/597] fix #6460: crash in mk_to_ieee_bv_i --- src/ast/fpa/fpa2bv_converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 21c71c8a955..c456da29b7d 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3288,7 +3288,7 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex void fpa2bv_converter::mk_to_ieee_bv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_IEEE_BV, 0, nullptr, num, args), m); - mk_to_bv(fu, num, args, true, result); + mk_to_ieee_bv(fu, num, args, result); } void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) { From 5c5673bc09435a0477b6e168731d0d67fc3f7f2c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 07:55:49 +0700 Subject: [PATCH 103/597] make sure parser context within solver object has its parameters updated this is to enable use cases like: ``` from z3 import * s = Solver() OnClause(s, print) s.set("solver.proof.check", False) s.from_file("../satproof.smt2") ``` instead of setting global parameters before the proof replay is initialized. --- src/api/api_solver.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 94d72462fb8..b93fb42afdb 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -406,7 +406,11 @@ extern "C" { params.validate(r); to_solver_ref(s)->updt_params(params); } - to_solver(s)->m_params.append(params); + auto& solver = *to_solver(s); + solver.m_params.append(params); + + if (solver.m_cmd_context && solver.m_cmd_context->get_proof_cmds()) + solver.m_cmd_context->get_proof_cmds()->updt_params(solver.m_params); init_solver_log(c, s); @@ -937,8 +941,10 @@ extern "C" { install_proof_cmds(*solver.m_cmd_context); } - if (!solver.m_cmd_context->get_proof_cmds()) + if (!solver.m_cmd_context->get_proof_cmds()) { init_proof_cmds(*solver.m_cmd_context); + solver.m_cmd_context->get_proof_cmds()->updt_params(solver.m_params); + } solver.m_cmd_context->get_proof_cmds()->register_on_clause(user_context, _on_clause); Z3_CATCH; } From cd0d52acec68ce1c23553da9edc255e4c4cf5215 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 07:56:28 +0700 Subject: [PATCH 104/597] using C++11 features to simplify for-loops --- src/ast/macros/macro_util.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index a28d9c6086f..f7377061b3f 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -417,13 +417,11 @@ bool macro_util::is_quasi_macro_ok(expr * n, unsigned num_decls, expr * def) con if (is_app(n) && to_app(n)->get_family_id() == null_family_id && to_app(n)->get_num_args() >= num_decls) { - unsigned num_args = to_app(n)->get_num_args(); sbuffer found_vars; found_vars.resize(num_decls, false); unsigned num_found_vars = 0; expr_free_vars fv; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(n)->get_arg(i); + for (expr* arg : *to_app(n)) { if (occurs(to_app(n)->get_decl(), arg)) return false; else @@ -553,12 +551,9 @@ bool is_hint_head(expr * n, ptr_buffer & vars) { return false; if (to_app(n)->get_decl()->is_associative() || to_app(n)->get_family_id() != null_family_id) return false; - unsigned num_args = to_app(n)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(n)->get_arg(i); + for (expr * arg : *to_app(n)) if (is_var(arg)) vars.push_back(to_var(arg)); - } return !vars.empty(); } @@ -579,9 +574,7 @@ bool vars_of_is_subset(expr * n, ptr_buffer const & vars) { return false; } else if (is_app(curr)) { - unsigned num_args = to_app(curr)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(curr)->get_arg(i); + for (expr * arg : *to_app(curr)) { if (is_ground(arg)) continue; if (visited.contains(arg)) @@ -611,13 +604,11 @@ bool is_hint_atom(expr * lhs, expr * rhs) { } void hint_to_macro_head(ast_manager & m, app * head, unsigned & num_decls, app_ref & new_head) { - unsigned num_args = head->get_num_args(); ptr_buffer new_args; sbuffer found_vars; found_vars.resize(num_decls, false); unsigned next_var_idx = num_decls; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = head->get_arg(i); + for (expr * arg : *head) { if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); SASSERT(idx < num_decls); From c7781f346d5d723a8c9edfe6534d1a1b18067b2e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 07:59:04 +0700 Subject: [PATCH 105/597] move parameter sat.smt.proof to solver.proof.log this update breaks use cases that set sat.smt.proof to True. As it is such a new feature and the change affects possibly at most the tutorial it is made without compatibility layers. --- src/params/solver_params.pyg | 1 + src/sat/sat_config.cpp | 3 +-- src/sat/sat_config.h | 1 - src/sat/sat_params.pyg | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/params/solver_params.pyg b/src/params/solver_params.pyg index 9ff13864d44..1c2be32134e 100644 --- a/src/params/solver_params.pyg +++ b/src/params/solver_params.pyg @@ -8,6 +8,7 @@ def_module_params('solver', ('lemmas2console', BOOL, False, 'print lemmas during search'), ('instantiations2console', BOOL, False, 'print quantifier instantiations to the console'), ('axioms2files', BOOL, False, 'print negated theory axioms to separate files during search'), + ('proof.log', SYMBOL, '', 'log clause proof trail into a file'), ('proof.check', BOOL, True, 'check proof logs'), ('proof.save', BOOL, False, 'save proof log into a proof object that can be extracted using (get-proof)'), ('proof.trim', BOOL, False, 'trim and save proof into a proof object that an be extracted using (get-proof)'), diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 4ca223be63a..eb2d0071dea 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -197,7 +197,6 @@ namespace sat { m_drat_check_unsat = p.drat_check_unsat(); m_drat_check_sat = p.drat_check_sat(); m_drat_file = p.drat_file(); - m_smt_proof = p.smt_proof(); m_smt_proof_check = p.smt_proof_check(); m_smt_proof_check_rup = p.smt_proof_check_rup(); m_drat_disable = p.drat_disable(); @@ -206,7 +205,7 @@ namespace sat { (sp.lemmas2console() || m_drat_check_unsat || m_drat_file.is_non_empty_string() || - m_smt_proof.is_non_empty_string() || + sp.proof_log().is_non_empty_string() || m_smt_proof_check || m_drat_check_sat); m_drat_binary = p.drat_binary(); diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index ae19c63ea6a..8adfc13edb6 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -178,7 +178,6 @@ namespace sat { bool m_drat_disable; bool m_drat_binary; symbol m_drat_file; - symbol m_smt_proof; bool m_smt_proof_check; bool m_smt_proof_check_rup; bool m_drat_check_unsat; diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 7466a1126f9..20a41544195 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -47,7 +47,6 @@ def_module_params('sat', ('threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), ('drat.disable', BOOL, False, 'override anything that enables DRAT'), - ('smt.proof', SYMBOL, '', 'add SMT proof log to file'), ('smt.proof.check', BOOL, False, 'check SMT proof while it is created'), ('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'), ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), From 5374142e3e1b0edee4d5ace5cdf2ba1d3ab82738 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 07:59:32 +0700 Subject: [PATCH 106/597] continue updates for adding proof-log to smt core --- src/smt/params/smt_params.cpp | 2 ++ src/smt/params/smt_params.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 4e9afaa8d5b..8143bc31b21 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -64,6 +64,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_axioms2files = sp.axioms2files(); m_lemmas2console = sp.lemmas2console(); m_instantiations2console = sp.instantiations2console(); + m_proof_log = sp.proof_log(); } void smt_params::updt_params(params_ref const & p) { @@ -126,6 +127,7 @@ void smt_params::display(std::ostream & out) const { DISPLAY_PARAM(m_ematching); DISPLAY_PARAM(m_induction); DISPLAY_PARAM(m_clause_proof); + DISPLAY_PARAM(m_proof_log); DISPLAY_PARAM(m_case_split_strategy); DISPLAY_PARAM(m_rel_case_split_order); diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 62e1ec8437a..73f556eb889 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -112,6 +112,7 @@ struct smt_params : public preprocessor_params, bool m_ematching = true; bool m_induction = false; bool m_clause_proof = false; + symbol m_proof_log; // ----------------------------------- // From 6188c536ef852633571b34e74efe962b942f6ad7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 08:01:15 +0700 Subject: [PATCH 107/597] add logging of propagations to smt core log theory propagations with annotation "smt". It allows tracking theory propagations (when used in conflicts) in the clause logs similar to the new core. --- src/smt/qi_queue.cpp | 2 +- src/smt/smt_clause_proof.cpp | 80 ++++++++++++++++++++++++++--- src/smt/smt_clause_proof.h | 16 ++++-- src/smt/smt_conflict_resolution.cpp | 1 + src/smt/smt_context.cpp | 5 +- src/smt/smt_context.h | 4 +- src/smt/smt_internalizer.cpp | 4 +- 7 files changed, 95 insertions(+), 17 deletions(-) diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp index cb28f87f845..582bcc66452 100644 --- a/src/smt/qi_queue.cpp +++ b/src/smt/qi_queue.cpp @@ -304,7 +304,7 @@ namespace smt { } m_instances.push_back(pr1); } - else if (m_context.on_clause_active()) { + else if (m_context.clause_proof_active()) { expr_ref_vector bindings_e(m), args(m); arith_util a(m); expr_ref gen(a.mk_int(generation), m); diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index ad3d3818c85..096a0994ffe 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -18,7 +18,17 @@ Revision History: #include "ast/ast_ll_pp.h" namespace smt { - clause_proof::clause_proof(context& ctx): ctx(ctx), m(ctx.get_manager()), m_lits(m) {} + + clause_proof::clause_proof(context& ctx): + ctx(ctx), m(ctx.get_manager()), m_lits(m), m_pp(m) { + auto proof_log = ctx.get_fparams().m_proof_log; + m_enabled = ctx.get_fparams().m_clause_proof || proof_log.is_non_empty_string(); + if (proof_log.is_non_empty_string()) { + m_pp_out = alloc(std::ofstream, proof_log.str()); + if (!*m_pp_out) + throw default_exception(std::string("Could not open file ") + proof_log.str()); + } + } clause_proof::status clause_proof::kind2st(clause_kind k) { switch (k) { @@ -42,7 +52,7 @@ namespace smt { r = j->mk_proof(ctx.get_cr()); if (r) return r; - if (!m_on_clause_active) + if (!is_enabled()) return nullptr; switch (st) { case status::assumption: @@ -60,7 +70,7 @@ namespace smt { } void clause_proof::add(clause& c) { - if (!ctx.get_fparams().m_clause_proof && !m_on_clause_active) + if (!is_enabled()) return; justification* j = c.get_justification(); auto st = kind2st(c.get_kind()); @@ -70,7 +80,7 @@ namespace smt { } void clause_proof::add(unsigned n, literal const* lits, clause_kind k, justification* j) { - if (!ctx.get_fparams().m_clause_proof && !m_on_clause_active) + if (!is_enabled()) return; auto st = kind2st(k); proof_ref pr(justification2proof(st, j), m); @@ -83,7 +93,7 @@ namespace smt { void clause_proof::shrink(clause& c, unsigned new_size) { - if (!ctx.get_fparams().m_clause_proof && !m_on_clause_active) + if (!is_enabled()) return; m_lits.reset(); for (unsigned i = 0; i < new_size; ++i) @@ -97,7 +107,7 @@ namespace smt { } void clause_proof::add(literal lit, clause_kind k, justification* j) { - if (!ctx.get_fparams().m_clause_proof && !m_on_clause_active) + if (!is_enabled()) return; m_lits.reset(); m_lits.push_back(ctx.literal2expr(lit)); @@ -107,7 +117,7 @@ namespace smt { } void clause_proof::add(literal lit1, literal lit2, clause_kind k, justification* j) { - if (!ctx.get_fparams().m_clause_proof && !m_on_clause_active) + if (!is_enabled()) return; m_lits.reset(); m_lits.push_back(ctx.literal2expr(lit1)); @@ -117,21 +127,75 @@ namespace smt { update(st, m_lits, pr); } + void clause_proof::propagate(literal lit, justification const& jst, literal_vector const& ante) { + if (!is_enabled()) + return; + m_lits.reset(); + for (literal l : ante) + m_lits.push_back(ctx.literal2expr(~l)); + m_lits.push_back(ctx.literal2expr(lit)); + proof_ref pr(m.mk_app(symbol("smt"), 0, nullptr, m.mk_proof_sort()), m); + update(clause_proof::status::th_lemma, m_lits, pr); + } void clause_proof::del(clause& c) { update(c, status::deleted, justification2proof(status::deleted, nullptr)); } + std::ostream& clause_proof::display_literals(std::ostream& out, expr_ref_vector const& v) { + for (expr* e : v) + if (m.is_not(e, e)) + m_pp.display_expr_def(out << " (not ", e) << ")"; + else + m_pp.display_expr_def(out << " ", e); + return out; + } + + std::ostream& clause_proof::display_hint(std::ostream& out, proof* p) { + if (p) + m_pp.display_expr_def(out << " ", p); + return out; + } + + void clause_proof::declare(std::ostream& out, expr* e) { + m_pp.collect(e); + m_pp.display_decls(out); + m.is_not(e, e); + m_pp.define_expr(out, e); + } + void clause_proof::update(status st, expr_ref_vector& v, proof* p) { TRACE("clause_proof", tout << m_trail.size() << " " << st << " " << v << "\n";); if (ctx.get_fparams().m_clause_proof) m_trail.push_back(info(st, v, p)); if (m_on_clause_eh) m_on_clause_eh(m_on_clause_ctx, p, v.size(), v.data()); + if (m_pp_out) { + auto& out = *m_pp_out; + for (auto* e : v) + declare(out, e); + switch (st) { + case clause_proof::status::assumption: + display_literals(out << "(assume", v) << ")\n"; + break; + case clause_proof::status::lemma: + case clause_proof::status::th_lemma: + case clause_proof::status::th_assumption: + if (p) + declare(out, p); + display_hint(display_literals(out << "(infer", v), p) << ")\n"; + break; + case clause_proof::status::deleted: + display_literals(out << "(del", v) << ")\n"; + break; + default: + UNREACHABLE(); + } + } } void clause_proof::update(clause& c, status st, proof* p) { - if (!ctx.get_fparams().m_clause_proof && !m_on_clause_active) + if (!is_enabled()) return; m_lits.reset(); for (literal lit : c) diff --git a/src/smt/smt_clause_proof.h b/src/smt/smt_clause_proof.h index f15d0ffe01b..42f283f1dd7 100644 --- a/src/smt/smt_clause_proof.h +++ b/src/smt/smt_clause_proof.h @@ -26,8 +26,10 @@ Revision History: --*/ #pragma once +#include "ast/ast_pp_util.h" #include "smt/smt_theory.h" #include "smt/smt_clause.h" +#include "smt/smt_justification.h" #include "tactic/user_propagator_base.h" namespace smt { @@ -55,13 +57,20 @@ namespace smt { ast_manager& m; expr_ref_vector m_lits; vector m_trail; - bool m_on_clause_active = false; + bool m_enabled = false; user_propagator::on_clause_eh_t m_on_clause_eh; void* m_on_clause_ctx = nullptr; + ast_pp_util m_pp; + scoped_ptr m_pp_out; + void update(status st, expr_ref_vector& v, proof* p); void update(clause& c, status st, proof* p); status kind2st(clause_kind k); proof* justification2proof(status st, justification* j); + void log(status st, proof* p); + void declare(std::ostream& out, expr* e); + std::ostream& display_literals(std::ostream& out, expr_ref_vector const& v); + std::ostream& display_hint(std::ostream& out, proof* p); public: clause_proof(context& ctx); void shrink(clause& c, unsigned new_size); @@ -69,13 +78,14 @@ namespace smt { void add(literal lit1, literal lit2, clause_kind k, justification* j); void add(clause& c); void add(unsigned n, literal const* lits, clause_kind k, justification* j); + void propagate(literal lit, justification const& j, literal_vector const& ante); void del(clause& c); proof_ref get_proof(bool inconsistent); - bool on_clause_active() const { return m_on_clause_active; } + bool is_enabled() const { return m_enabled; } void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause) { m_on_clause_eh = on_clause; m_on_clause_ctx = ctx; - m_on_clause_active = !!m_on_clause_eh; + m_enabled |= !!m_on_clause_eh; } }; diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 45ff1900d95..d075c06522b 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -350,6 +350,7 @@ namespace smt { literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); justification2literals_core(js, antecedents); + m_ctx.get_clause_proof().propagate(consequent, *js, antecedents); for (literal l : antecedents) process_antecedent(l, num_marks); (void)consequent; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 84b4906ca09..a8d2f268d4d 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -284,7 +284,7 @@ namespace smt { TRACE("assign_core", tout << (decision?"decision: ":"propagating: ") << l << " "; display_literal_smt2(tout, l) << "\n"; tout << "relevant: " << is_relevant_core(l) << " level: " << m_scope_lvl << " is atom " << d.is_atom() << "\n"; - /*display(tout, j);*/ + display(tout, j); ); TRACE("phase_selection", tout << "saving phase, is_pos: " << d.m_phase << " l: " << l << "\n";); @@ -639,7 +639,6 @@ namespace smt { if (val != l_true) { if (val == l_false && js.get_kind() == eq_justification::CONGRUENCE) m_dyn_ack_manager.cg_conflict_eh(n1->get_expr(), n2->get_expr()); - assign(literal(v), mk_justification(eq_propagation_justification(lhs, rhs))); } // It is not necessary to reinsert the equality to the congruence table @@ -1347,6 +1346,7 @@ namespace smt { TRACE("add_diseq", display_eq_detail(tout, bool_var2enode(v));); if (!add_diseq(get_enode(lhs), get_enode(rhs)) && !inconsistent()) { literal n_eq = literal(l.var(), true); + IF_VERBOSE(0, verbose_stream() << "eq-conflict @" << m_scope_lvl << "\n"); set_conflict(b_justification(mk_justification(eq_propagation_justification(get_enode(lhs), get_enode(rhs)))), n_eq); } } @@ -3732,6 +3732,7 @@ namespace smt { flet l(m_searching, true); TRACE("after_init_search", display(tout);); IF_VERBOSE(2, verbose_stream() << "(smt.searching)\n";); + log_stats(); TRACE("search_lite", tout << "searching...\n";); lbool status = l_undef; unsigned curr_lvl = m_scope_lvl; diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 4a5f6492683..2b3a343f7a3 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1706,7 +1706,9 @@ namespace smt { void get_units(expr_ref_vector& result); - bool on_clause_active() const { return m_clause_proof.on_clause_active(); } + bool clause_proof_active() const { return m_clause_proof.is_enabled(); } + + clause_proof& get_clause_proof() { return m_clause_proof; } void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause) { m_clause_proof.register_on_clause(ctx, on_clause); diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 63848418fb8..26e23d92d93 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1593,7 +1593,7 @@ 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()) { + else if (clause_proof_active()) { ptr_buffer new_lits; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; @@ -1638,7 +1638,7 @@ namespace smt { } mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } - else if (pr && on_clause_active()) + else if (pr && clause_proof_active()) // support logging of quantifier instantiations and other more detailed information mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); else From 11b712fee0d06a6457f901cc29eff7a819415543 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 08:02:02 +0700 Subject: [PATCH 108/597] switch to new configuration convention in solver object --- src/sat/smt/euf_proof.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 877375246f1..5fbd632cd34 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -34,11 +34,11 @@ namespace euf { if (!get_config().m_lemmas2console && !s().get_config().m_smt_proof_check && !m_on_clause && - !s().get_config().m_smt_proof.is_non_empty_string()) + !m_config.m_proof_log.is_non_empty_string()) return; - if (s().get_config().m_smt_proof.is_non_empty_string()) - m_proof_out = alloc(std::ofstream, s().get_config().m_smt_proof.str(), std::ios_base::out); + if (m_config.m_proof_log.is_non_empty_string()) + m_proof_out = alloc(std::ofstream, m_config.m_proof_log.str(), std::ios_base::out); get_drat().set_clause_eh(*this); m_proof_initialized = true; } From 22353c2d6cd90186133654796564384b5b60bfba Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 11:37:07 +0700 Subject: [PATCH 109/597] new core perf - add merge_tf and enable_cgc distinction perf fix for propagation behavior for equalities in the new core. The old behavior was not to allow congruence closure on equalities. The new behavior is to just not allow merging tf with equalities unless they appear somewhere in a foreign context (not under a Boolean operator) The change re-surfaces merge_tf and enable_cgc distinction from the old core. They can both be turned on or off. merge_enabled renamed to cgc_enabled The change is highly likely to introduce regressions in the new core. Change propagation of literals from congruence: - track antecedent enode. There are four ways to propagate literals from the egraph. - the literal is an equality and the two arguments are congruent - the antecedent is merged with node n and the antecedent has a Boolean variable assignment. - the antecedent is true or false, they are merged. - the merge_tf flag is toggled to true but the node n has not been merged with true/false --- src/ast/euf/euf_egraph.cpp | 108 +++++++++++++++---------- src/ast/euf/euf_egraph.h | 23 +++--- src/ast/euf/euf_enode.cpp | 4 +- src/ast/euf/euf_enode.h | 17 ++-- src/ast/rewriter/th_rewriter.cpp | 2 + src/ast/simplifiers/euf_completion.cpp | 7 +- src/sat/smt/arith_internalize.cpp | 2 +- src/sat/smt/euf_internalize.cpp | 47 +++-------- src/sat/smt/euf_solver.cpp | 93 ++++++++++++++------- src/sat/smt/euf_solver.h | 6 +- 10 files changed, 177 insertions(+), 132 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 666a7fad690..1448ee8d7bf 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -36,8 +36,10 @@ namespace euf { } m_expr2enode.setx(f->get_id(), n, nullptr); push_node(n); - for (unsigned i = 0; i < num_args; ++i) - set_merge_enabled(args[i], true); + for (unsigned i = 0; i < num_args; ++i) { + set_cgc_enabled(args[i], true); + set_merge_tf_enabled(args[i], true); + } return n; } @@ -78,9 +80,8 @@ namespace euf { void egraph::reinsert_equality(enode* p) { SASSERT(p->is_equality()); - if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) { - add_literal(p, true); - } + if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) + add_literal(p, nullptr); } void egraph::force_push() { @@ -116,18 +117,16 @@ namespace euf { m_on_make(n); if (num_args == 0) return n; - if (m.is_eq(f)) { + if (m.is_eq(f) && !m.is_iff(f)) { n->set_is_equality(); - update_children(n); reinsert_equality(n); } - else { - auto [n2, comm] = insert_table(n); - if (n2 == n) - update_children(n); - else - merge(n, n2, justification::congruence(comm, m_congruence_timestamp++)); - } + auto [n2, comm] = insert_table(n); + if (n2 == n) + update_children(n); + else + merge(n, n2, justification::congruence(comm, m_congruence_timestamp++)); + return n; } @@ -158,11 +157,11 @@ namespace euf { ++m_stats.m_num_th_diseqs; } - void egraph::add_literal(enode* n, bool is_eq) { + void egraph::add_literal(enode* n, enode* ante) { TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";); - m_new_lits.push_back(enode_bool_pair(n, is_eq)); + m_new_lits.push_back(enode_pair(n, ante)); m_updates.push_back(update_record(update_record::new_lit())); - if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits; + if (!ante) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits; } void egraph::new_diseq(enode* n) { @@ -173,7 +172,7 @@ namespace euf { enode* r2 = arg2->get_root(); TRACE("euf", tout << "new-diseq: " << bpp(r1) << " " << bpp(r2) << ": " << r1->has_th_vars() << " " << r2->has_th_vars() << "\n";); if (r1 == r2) { - add_literal(n, true); + add_literal(n, nullptr); return; } if (!r1->has_th_vars()) @@ -264,10 +263,26 @@ namespace euf { root->del_th_var(tid); } - void egraph::set_merge_enabled(enode* n, bool enable_merge) { - if (enable_merge != n->merge_enabled()) { - toggle_merge_enabled(n, false); - m_updates.push_back(update_record(n, update_record::toggle_merge())); + void egraph::set_merge_tf_enabled(enode* n, bool enable_merge_tf) { + if (!m.is_bool(n->get_sort())) + return; + if (enable_merge_tf != n->merge_tf()) { + n->set_merge_tf(enable_merge_tf); + m_updates.push_back(update_record(n, update_record::toggle_merge_tf())); + if (enable_merge_tf && n->value() != l_undef && !m.is_value(n->get_root()->get_expr())) { + expr* b = n->value() == l_true ? m.mk_true() : m.mk_false(); + enode* tf = find(b); + if (!tf) + tf = mk(b, 0, 0, nullptr); + add_literal(n, tf); + } + } + } + + void egraph::set_cgc_enabled(enode* n, bool enable_merge) { + if (enable_merge != n->cgc_enabled()) { + toggle_cgc_enabled(n, false); + m_updates.push_back(update_record(n, update_record::toggle_cgc())); } } @@ -278,9 +293,9 @@ namespace euf { m_updates.push_back(update_record(n, update_record::set_relevant())); } - void egraph::toggle_merge_enabled(enode* n, bool backtracking) { - bool enable_merge = !n->merge_enabled(); - n->set_merge_enabled(enable_merge); + void egraph::toggle_cgc_enabled(enode* n, bool backtracking) { + bool enable_merge = !n->cgc_enabled(); + n->set_cgc_enabled(enable_merge); if (n->num_args() > 0) { if (enable_merge) { auto [n2, comm] = insert_table(n); @@ -290,7 +305,7 @@ namespace euf { else if (n->is_cgr()) erase_from_table(n); } - VERIFY(n->num_args() == 0 || !n->merge_enabled() || m_table.contains(n)); + VERIFY(n->num_args() == 0 || !n->cgc_enabled() || m_table.contains(n)); } void egraph::set_value(enode* n, lbool value, justification j) { @@ -300,6 +315,8 @@ namespace euf { n->set_value(value); n->m_lit_justification = j; m_updates.push_back(update_record(n, update_record::value_assignment())); + if (n->is_equality() && n->value() == l_false) + new_diseq(n); } } @@ -352,8 +369,11 @@ namespace euf { case update_record::tag_t::is_add_node: undo_node(); break; - case update_record::tag_t::is_toggle_merge: - toggle_merge_enabled(p.r1, true); + case update_record::tag_t::is_toggle_cgc: + toggle_cgc_enabled(p.r1, true); + break; + case update_record::tag_t::is_toggle_merge_tf: + p.r1->set_merge_tf(!p.r1->merge_tf()); break; case update_record::tag_t::is_set_parent: undo_eq(p.r1, p.n1, p.r2_num_parents); @@ -419,7 +439,7 @@ namespace euf { void egraph::merge(enode* n1, enode* n2, justification j) { - if (!n1->merge_enabled() && !n2->merge_enabled()) + if (!n1->cgc_enabled() && !n2->cgc_enabled()) return; SASSERT(n1->get_sort() == n2->get_sort()); enode* r1 = n1->get_root(); @@ -436,6 +456,7 @@ namespace euf { set_conflict(n1, n2, j); return; } + if (r1->value() != r2->value() && r1->value() != l_undef && r2->value() != l_undef) { SASSERT(m.is_bool(r1->get_expr())); set_conflict(n1, n2, j); @@ -448,9 +469,11 @@ namespace euf { } if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr()))) - add_literal(n1, false); - if (n1->is_equality() && n1->value() == l_false) - new_diseq(n1); + add_literal(n1, r2); + if (r2->value() != l_undef && n1->value() == l_undef) + add_literal(n1, r2); + else if (r1->value() != l_undef && n2->value() == l_undef) + add_literal(n2, r1); remove_parents(r1); push_eq(r1, n1, r2->num_parents()); merge_justification(n1, n2, j); @@ -468,7 +491,7 @@ namespace euf { for (enode* p : enode_parents(r)) { if (p->is_marked1()) continue; - if (p->merge_enabled()) { + if (p->cgc_enabled()) { if (!p->is_cgr()) continue; SASSERT(m_table.contains_ptr(p)); @@ -486,8 +509,8 @@ namespace euf { if (!p->is_marked1()) continue; p->unmark1(); - TRACE("euf", tout << "reinsert " << bpp(r1) << " " << bpp(r2) << " " << bpp(p) << " " << p->merge_enabled() << "\n";); - if (p->merge_enabled()) { + TRACE("euf", tout << "reinsert " << bpp(r1) << " " << bpp(r2) << " " << bpp(p) << " " << p->cgc_enabled() << "\n";); + if (p->cgc_enabled()) { auto [p_other, comm] = insert_table(p); SASSERT(m_table.contains_ptr(p) == (p_other == p)); TRACE("euf", tout << "other " << bpp(p_other) << "\n";); @@ -531,9 +554,9 @@ namespace euf { for (auto it = begin; it != end; ++it) { enode* p = *it; TRACE("euf", tout << "erase " << bpp(p) << "\n";); - SASSERT(!p->merge_enabled() || m_table.contains_ptr(p)); - SASSERT(!p->merge_enabled() || p->is_cgr()); - if (p->merge_enabled()) + SASSERT(!p->cgc_enabled() || m_table.contains_ptr(p)); + SASSERT(!p->cgc_enabled() || p->is_cgr()); + if (p->cgc_enabled()) erase_from_table(p); } @@ -541,7 +564,7 @@ namespace euf { c->m_root = r1; for (enode* p : enode_parents(r1)) - if (p->merge_enabled() && (p->is_cgr() || !p->congruent(p->m_cg))) + if (p->cgc_enabled() && (p->is_cgr() || !p->congruent(p->m_cg))) insert_table(p); r2->m_parents.shrink(r2_num_parents); unmerge_justification(n1); @@ -783,7 +806,7 @@ namespace euf { for (enode* n : m_nodes) n->invariant(*this); for (enode* n : m_nodes) - if (n->merge_enabled() && n->num_args() > 0 && (!m_table.find(n) || n->get_root() != m_table.find(n)->get_root())) { + if (n->cgc_enabled() && n->num_args() > 0 && (!m_table.find(n) || n->get_root() != m_table.find(n)->get_root())) { CTRACE("euf", !m_table.find(n), tout << "node is not in table\n";); CTRACE("euf", m_table.find(n), tout << "root " << bpp(n->get_root()) << " table root " << bpp(m_table.find(n)->get_root()) << "\n";); TRACE("euf", display(tout << bpp(n) << " is not closed under congruence\n");); @@ -818,7 +841,7 @@ namespace euf { } }; if (n->bool_var() != sat::null_bool_var) - out << "[b" << n->bool_var() << " := " << value_of() << (n->merge_tf() ? "" : " no merge") << "] "; + out << "[b" << n->bool_var() << " := " << value_of() << (n->cgc_enabled() ? "" : " no-cgc") << (n->merge_tf()? " merge-tf" : "") << "] "; if (n->has_th_vars()) { out << "[t"; for (auto const& v : enode_th_vars(n)) @@ -873,7 +896,8 @@ namespace euf { n2->set_value(n1->value()); n2->m_bool_var = n1->m_bool_var; n2->m_commutative = n1->m_commutative; - n2->m_merge_enabled = n1->m_merge_enabled; + n2->m_cgc_enabled = n1->m_cgc_enabled; + n2->m_merge_tf_enabled = n1->m_merge_tf_enabled; n2->m_is_equality = n1->m_is_equality; } for (unsigned i = 0; i < src.m_nodes.size(); ++i) { diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index d6bcb9cd31c..1686be38469 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -101,7 +101,8 @@ namespace euf { void reset() { memset(this, 0, sizeof(*this)); } }; struct update_record { - struct toggle_merge {}; + struct toggle_cgc {}; + struct toggle_merge_tf {}; struct add_th_var {}; struct replace_th_var {}; struct new_lit {}; @@ -114,7 +115,7 @@ namespace euf { struct lbl_set {}; struct update_children {}; struct set_relevant {}; - enum class tag_t { is_set_parent, is_add_node, is_toggle_merge, is_update_children, + enum class tag_t { is_set_parent, is_add_node, is_toggle_cgc, is_toggle_merge_tf, is_update_children, is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq, is_lbl_hash, is_new_th_eq_qhead, is_new_lits_qhead, is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant }; @@ -136,8 +137,10 @@ namespace euf { tag(tag_t::is_set_parent), r1(r1), n1(n1), r2_num_parents(r2_num_parents) {} update_record(enode* n) : tag(tag_t::is_add_node), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} - update_record(enode* n, toggle_merge) : - tag(tag_t::is_toggle_merge), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} + update_record(enode* n, toggle_cgc) : + tag(tag_t::is_toggle_cgc), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} + update_record(enode* n, toggle_merge_tf) : + tag(tag_t::is_toggle_merge_tf), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} update_record(enode* n, unsigned id, add_th_var) : tag(tag_t::is_add_th_var), r1(n), n1(nullptr), r2_num_parents(id) {} update_record(enode* n, theory_id id, theory_var v, replace_th_var) : @@ -186,7 +189,7 @@ namespace euf { justification m_justification; unsigned m_new_lits_qhead = 0; unsigned m_new_th_eqs_qhead = 0; - svector m_new_lits; + svector m_new_lits; svector m_new_th_eqs; bool_vector m_th_propagates_diseqs; enode_vector m_todo; @@ -210,7 +213,7 @@ namespace euf { void add_th_diseqs(theory_id id, theory_var v1, enode* r); bool th_propagates_diseqs(theory_id id) const; - void add_literal(enode* n, bool is_eq); + void add_literal(enode* n, enode* ante); void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents); void undo_add_th_var(enode* n, theory_id id); enode* mk_enode(expr* f, unsigned generation, unsigned num_args, enode * const* args); @@ -229,7 +232,7 @@ namespace euf { void push_to_lca(enode* a, enode* lca); void push_congruence(enode* n1, enode* n2, bool commutative); void push_todo(enode* n); - void toggle_merge_enabled(enode* n, bool backtracking); + void toggle_cgc_enabled(enode* n, bool backtracking); enode_bool_pair insert_table(enode* p); void erase_from_table(enode* p); @@ -289,7 +292,7 @@ namespace euf { void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq); bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); } bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); } - enode_bool_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; } + enode_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; } th_eq get_th_eq() const { return m_new_th_eqs[m_new_th_eqs_qhead]; } void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; } void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; } @@ -299,7 +302,9 @@ namespace euf { void add_th_var(enode* n, theory_var v, theory_id id); void set_th_propagates_diseqs(theory_id id); - void set_merge_enabled(enode* n, bool enable_merge); + void set_cgc_enabled(enode* n, bool enable_cgc); + void set_merge_tf_enabled(enode* n, bool enable_merge_tf); + void set_value(enode* n, lbool value, justification j); void set_bool_var(enode* n, unsigned v) { n->set_bool_var(v); } void set_relevant(enode* n); diff --git a/src/ast/euf/euf_enode.cpp b/src/ast/euf/euf_enode.cpp index 03832579048..08df9f4939a 100644 --- a/src/ast/euf/euf_enode.cpp +++ b/src/ast/euf/euf_enode.cpp @@ -36,7 +36,7 @@ namespace euf { if (is_root()) { VERIFY(!m_target); for (enode* p : enode_parents(this)) { - if (!p->merge_enabled()) + if (!p->cgc_enabled()) continue; bool found = false; for (enode* arg : enode_args(p)) { @@ -49,7 +49,7 @@ namespace euf { if (c == this) continue; for (enode* p : enode_parents(c)) { - if (!p->merge_enabled()) + if (!p->cgc_enabled()) continue; bool found = false; for (enode* q : enode_parents(this)) { diff --git a/src/ast/euf/euf_enode.h b/src/ast/euf/euf_enode.h index 18a1a86afb3..d9ae45074e3 100644 --- a/src/ast/euf/euf_enode.h +++ b/src/ast/euf/euf_enode.h @@ -48,7 +48,8 @@ namespace euf { bool m_mark3 = false; bool m_commutative = false; bool m_interpreted = false; - bool m_merge_enabled = true; + bool m_cgc_enabled = true; + bool m_merge_tf_enabled = false; bool m_is_equality = false; // Does the expression represent an equality bool m_is_relevant = false; lbool m_value = l_undef; // Assignment by SAT solver for Boolean node @@ -91,7 +92,7 @@ namespace euf { n->m_generation = generation, n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative(); n->m_num_args = num_args; - n->m_merge_enabled = true; + n->m_cgc_enabled = true; for (unsigned i = 0; i < num_args; ++i) { SASSERT(to_app(f)->get_arg(i) == args[i]->get_expr()); n->m_args[i] = args[i]; @@ -107,7 +108,7 @@ namespace euf { n->m_root = n; n->m_commutative = true; n->m_num_args = 2; - n->m_merge_enabled = true; + n->m_cgc_enabled = true; for (unsigned i = 0; i < num_args; ++i) n->m_args[i] = nullptr; return n; @@ -121,7 +122,7 @@ namespace euf { n->m_root = n; n->m_commutative = true; n->m_num_args = 2; - n->m_merge_enabled = true; + n->m_cgc_enabled = true; for (unsigned i = 0; i < num_args; ++i) n->m_args[i] = nullptr; return n; @@ -132,7 +133,8 @@ namespace euf { void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); } void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); } void del_th_var(theory_id id) { m_th_vars.del_var(id); } - void set_merge_enabled(bool m) { m_merge_enabled = m; } + void set_cgc_enabled(bool m) { m_cgc_enabled = m; } + void set_merge_tf(bool m) { m_merge_tf_enabled = m; } void set_value(lbool v) { m_value = v; } void set_justification(justification j) { m_justification = j; } void set_is_equality() { m_is_equality = true; } @@ -152,14 +154,13 @@ namespace euf { bool is_relevant() const { return m_is_relevant; } void set_relevant(bool b) { m_is_relevant = b; } lbool value() const { return m_value; } - bool value_conflict() const { return value() != l_undef && get_root()->value() != l_undef && value() != get_root()->value(); } sat::bool_var bool_var() const { return m_bool_var; } bool is_cgr() const { return this == m_cg; } enode* get_cg() const { return m_cg; } bool commutative() const { return m_commutative; } void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; } - bool merge_enabled() const { return m_merge_enabled; } - bool merge_tf() const { return merge_enabled() && (class_size() > 1 || num_parents() > 0 || num_args() > 0); } + bool cgc_enabled() const { return m_cgc_enabled; } + bool merge_tf() const { return m_merge_tf_enabled && (class_size() > 1 || num_parents() > 0 || num_args() > 0); } enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; } unsigned hash() const { return m_expr->get_id(); } diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 4c7d4dc4921..9bc8c22cf6e 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -707,6 +707,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr_ref mk_eq(expr* a, expr* b) { expr_ref result(m()); + if (a->get_id() > b->get_id()) + std::swap(a, b); if (BR_FAILED == reduce_eq(a, b, result)) result = m().mk_eq(a, b); return result; diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index ac1ecc47aad..1ca980d0739 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -211,8 +211,8 @@ namespace euf { if (x == y) return expr_ref(m.mk_true(), m); - if (x == x1 && y == y1) - return expr_ref(f, m); + if (x == x1 && y == y1) + return m_rewriter.mk_eq(x, y); if (is_nullary(x) && is_nullary(y)) return mk_and(m_rewriter.mk_eq(x, x1), m_rewriter.mk_eq(y, x1)); @@ -268,7 +268,8 @@ namespace euf { m_eargs.push_back(get_canonical(arg, d)); change |= arg != m_eargs.back(); } - + if (m.is_eq(f)) + return m_rewriter.mk_eq(m_eargs.get(0), m_eargs.get(1)); if (!change) return expr_ref(f, m); else diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 04a3ae4ef3a..d35f7995476 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -372,7 +372,7 @@ namespace arith { enode* n = ctx.get_enode(atom); theory_var w = mk_var(n); ctx.attach_th_var(n, this, w); - ctx.get_egraph().set_merge_enabled(n, false); + ctx.get_egraph().set_cgc_enabled(n, false); if (is_int(v) && !r.is_int()) r = (k == lp_api::upper_t) ? floor(r) : ceil(r); api_bound* b = mk_var_bound(lit, v, k, r); diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index 9f747090af2..3aaafa36cee 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -74,10 +74,8 @@ namespace euf { } if (auto* ext = expr2solver(e)) return ext->internalize(e, sign, root); - if (!visit_rec(m, e, sign, root)) { - TRACE("euf", tout << "visit-rec\n";); + if (!visit_rec(m, e, sign, root)) return sat::null_literal; - } SASSERT(get_enode(e)); if (m.is_bool(e)) return literal(si.to_bool_var(e), sign); @@ -119,7 +117,7 @@ namespace euf { SASSERT(!get_enode(e)); if (auto* s = expr2solver(e)) s->internalize(e); - else + else attach_node(mk_enode(e, num, m_args.data())); return true; } @@ -188,6 +186,7 @@ namespace euf { return lit; } + set_bool_var2expr(v, e); enode* n = m_egraph.find(e); if (!n) @@ -195,8 +194,8 @@ namespace euf { CTRACE("euf", n->bool_var() != sat::null_bool_var && n->bool_var() != v, display(tout << bpp(n) << " " << n->bool_var() << " vs " << v << "\n")); SASSERT(n->bool_var() == sat::null_bool_var || n->bool_var() == v); m_egraph.set_bool_var(n, v); - if (m.is_eq(e) || m.is_or(e) || m.is_and(e) || m.is_not(e)) - m_egraph.set_merge_enabled(n, false); + if (si.is_bool_op(e)) + m_egraph.set_cgc_enabled(n, false); lbool val = s().value(lit); if (val != l_undef) m_egraph.set_value(n, val, justification::external(to_ptr(val == l_true ? lit : ~lit))); @@ -349,15 +348,6 @@ namespace euf { else if (m.is_eq(e, th, el) && !m.is_iff(e)) { sat::literal lit1 = expr2literal(e); s().set_phase(lit1); - expr_ref e2(m.mk_eq(el, th), m); - enode* n2 = m_egraph.find(e2); - if (n2) { - sat::literal lit2 = expr2literal(e2); - add_root(~lit1, lit2); - add_root(lit1, ~lit2); - s().add_clause(~lit1, lit2, mk_distinct_status(~lit1, lit2)); - s().add_clause(lit1, ~lit2, mk_distinct_status(lit1, ~lit2)); - } } } @@ -476,26 +466,15 @@ namespace euf { return n; } - euf::enode* solver::mk_enode(expr* e, unsigned n, enode* const* args) { - euf::enode* r = m_egraph.mk(e, m_generation, n, args); - for (unsigned i = 0; i < n; ++i) - ensure_merged_tf(args[i]); - return r; - } + euf::enode* solver::mk_enode(expr* e, unsigned num, enode* const* args) { - void solver::ensure_merged_tf(euf::enode* n) { - switch (n->value()) { - case l_undef: - break; - case l_true: - if (n->get_root() != mk_true()) - m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var()))); - break; - case l_false: - if (n->get_root() != mk_false()) - m_egraph.merge(n, mk_false(), to_ptr(~sat::literal(n->bool_var()))); - break; - } + if (si.is_bool_op(e)) + num = 0; + + enode* n = m_egraph.mk(e, m_generation, num, args); + if (si.is_bool_op(e)) + m_egraph.set_cgc_enabled(n, false); + return n; } } diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 7b02509ee77..9b346543f56 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -316,13 +316,23 @@ namespace euf { SASSERT(!l.sign()); m_egraph.explain_eq(m_explain, cc, n->get_arg(0), n->get_arg(1)); break; - case constraint::kind_t::lit: + case constraint::kind_t::lit: { e = m_bool_var2expr[l.var()]; n = m_egraph.find(e); + enode* ante = j.node(); SASSERT(n); SASSERT(m.is_bool(n->get_expr())); - m_egraph.explain_eq(m_explain, cc, n, (l.sign() ? mk_false() : mk_true())); + SASSERT(ante->get_root() == n->get_root()); + m_egraph.explain_eq(m_explain, cc, n, ante); + if (!m.is_true(ante->get_expr()) && !m.is_false(ante->get_expr())) { + bool_var v = ante->bool_var(); + lbool val = ante->value(); + SASSERT(val != l_undef); + literal ante(v, val == l_false); + m_explain.push_back(to_ptr(ante)); + } break; + } default: IF_VERBOSE(0, verbose_stream() << (unsigned)j.kind() << "\n"); UNREACHABLE(); @@ -345,24 +355,30 @@ namespace euf { euf::enode* n = m_egraph.find(e); if (!n) return; - bool sign = l.sign(); - m_egraph.set_value(n, sign ? l_false : l_true, justification::external(to_ptr(l))); + bool sign = l.sign(); + lbool old_value = n->value(); + lbool new_value = sign ? l_false : l_true; + m_egraph.set_value(n, new_value, justification::external(to_ptr(l))); + if (old_value == l_undef && n->cgc_enabled()) { + for (enode* k : enode_class(n)) { + if (k->bool_var() == sat::null_bool_var) + continue; + if (k->value() == new_value) + continue; + auto& c = lit_constraint(n); + propagate(literal(k->bool_var(), sign), c.to_index()); + if (k->value() == l_undef) + m_egraph.set_value(k, new_value, justification::external(to_ptr(l))); + else + return; + } + } for (auto const& th : enode_th_vars(n)) m_id2solver[th.get_id()]->asserted(l); size_t* c = to_ptr(l); SASSERT(is_literal(c)); SASSERT(l == get_literal(c)); - if (n->value_conflict()) { - euf::enode* nb = sign ? mk_false() : mk_true(); - euf::enode* r = n->get_root(); - euf::enode* rb = sign ? mk_true() : mk_false(); - sat::literal rl(r->bool_var(), r->value() == l_false); - m_egraph.merge(n, nb, c); - m_egraph.merge(r, rb, to_ptr(rl)); - SASSERT(m_egraph.inconsistent()); - return; - } if (n->merge_tf()) { euf::enode* nb = sign ? mk_false() : mk_true(); m_egraph.merge(n, nb, c); @@ -374,10 +390,18 @@ namespace euf { m_egraph.new_diseq(n); else m_egraph.merge(n->get_arg(0), n->get_arg(1), c); - } + } + } + + constraint& solver::lit_constraint(enode* n) { + void* mem = get_region().allocate(sat::constraint_base::obj_size(sizeof(constraint))); + auto* c = new (sat::constraint_base::ptr2mem(mem)) constraint(n); + sat::constraint_base::initialize(mem, this); + return *c; } + bool solver::unit_propagate() { bool propagated = false; while (!s().inconsistent()) { @@ -412,37 +436,44 @@ namespace euf { void solver::propagate_literals() { for (; m_egraph.has_literal() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_literal()) { - auto [n, is_eq] = m_egraph.get_literal(); + auto [n, ante] = m_egraph.get_literal(); expr* e = n->get_expr(); expr* a = nullptr, *b = nullptr; bool_var v = n->bool_var(); SASSERT(m.is_bool(e)); size_t cnstr; - literal lit; - if (is_eq) { + literal lit; + if (!ante) { VERIFY(m.is_eq(e, a, b)); cnstr = eq_constraint().to_index(); lit = literal(v, false); } else { - lbool val = n->get_root()->value(); - if (val == l_undef && m.is_false(n->get_root()->get_expr())) - val = l_false; - if (val == l_undef && m.is_true(n->get_root()->get_expr())) - val = l_true; - a = e; - b = (val == l_true) ? m.mk_true() : m.mk_false(); - SASSERT(val != l_undef); - cnstr = lit_constraint().to_index(); + // + // There are the following three cases for propagation of literals + // + // 1. n == ante is true from equallity, ante = true/false + // 2. n == ante is true from equality, value(ante) != l_undef + // 3. value(n) != l_undef, ante = true/false, merge_tf is set on n + // + lbool val = ante->value(); + if (val == l_undef) { + SASSERT(m.is_value(ante->get_expr())); + val = m.is_true(ante->get_expr()) ? l_true : l_false; + } + auto& c = lit_constraint(ante); + cnstr = c.to_index(); lit = literal(v, val == l_false); } unsigned lvl = s().scope_lvl(); - CTRACE("euf", s().value(lit) != l_true, tout << lit << " " << s().value(lit) << "@" << lvl << " " << is_eq << " " << mk_bounded_pp(a, m) << " = " << mk_bounded_pp(b, m) << "\n";); - if (s().value(lit) == l_false && m_ackerman) + CTRACE("euf", s().value(lit) != l_true, tout << lit << " " << s().value(lit) << "@" << lvl << " " << mk_bounded_pp(a, m) << " = " << mk_bounded_pp(b, m) << "\n";); + if (s().value(lit) == l_false && m_ackerman && a && b) m_ackerman->cg_conflict_eh(a, b); switch (s().value(lit)) { case l_true: + if (n->merge_tf() && !m.is_value(n->get_root()->get_expr())) + m_egraph.merge(n, ante, to_ptr(lit)); break; case l_undef: case l_false: @@ -889,7 +920,7 @@ namespace euf { if (m.is_eq(e) && !m.is_iff(e)) ok = false; euf::enode* n = get_enode(e); - if (n && n->merge_enabled()) + if (n && n->cgc_enabled()) ok = false; (void)ok; @@ -938,7 +969,7 @@ namespace euf { case constraint::kind_t::eq: return out << "euf equality propagation"; case constraint::kind_t::lit: - return out << "euf literal propagation"; + return out << "euf literal propagation " << m_egraph.bpp(c.node()) ; default: UNREACHABLE(); return out; diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index f935ad9ffe4..5b09e6a465b 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -45,9 +45,12 @@ namespace euf { enum class kind_t { conflict, eq, lit }; private: kind_t m_kind; + enode* m_node = nullptr; public: constraint(kind_t k) : m_kind(k) {} + constraint(enode* n): m_kind(kind_t::lit), m_node(n) {} kind_t kind() const { return m_kind; } + enode* node() const { SASSERT(kind() == kind_t::lit); return m_node; } static constraint& from_idx(size_t z) { return *reinterpret_cast(sat::constraint_base::idx2mem(z)); } @@ -171,7 +174,6 @@ namespace euf { void add_not_distinct_axiom(app* e, euf::enode* const* args); void axiomatize_basic(enode* n); bool internalize_root(app* e, bool sign, ptr_vector const& args); - void ensure_merged_tf(euf::enode* n); euf::enode* mk_true(); euf::enode* mk_false(); @@ -250,7 +252,7 @@ namespace euf { constraint& mk_constraint(constraint*& c, constraint::kind_t k); constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); } constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); } - constraint& lit_constraint() { return mk_constraint(m_lit, constraint::kind_t::lit); } + constraint& lit_constraint(enode* n); // user propagator void check_for_user_propagator() { From 9a2693bb72f8973f891618622fdfd0e78f831f41 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 16:39:20 +0700 Subject: [PATCH 110/597] tune euf-completion --- src/ast/rewriter/bool_rewriter.cpp | 4 ++ src/ast/rewriter/bool_rewriter.h | 10 +-- src/ast/rewriter/th_rewriter.cpp | 11 +++- src/ast/rewriter/th_rewriter.h | 1 + src/ast/simplifiers/euf_completion.cpp | 88 ++++++++++++++++++++++++-- src/ast/simplifiers/euf_completion.h | 1 + 6 files changed, 104 insertions(+), 11 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index ffceec277ed..5abfe01a62f 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -780,6 +780,10 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { } } } + if (m_order_eq && lhs->get_id() > rhs->get_id()) { + result = m().mk_eq(rhs, lhs); + return BR_DONE; + } return BR_FAILED; } diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 2cec0b2ce82..8f2221a8cee 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -52,10 +52,11 @@ Module Name: class bool_rewriter { ast_manager & m_manager; hoist_rewriter m_hoist; - bool m_flat_and_or; - bool m_local_ctx; - bool m_elim_and; - bool m_blast_distinct; + bool m_flat_and_or = false; + bool m_local_ctx = false; + bool m_elim_and = false; + bool m_blast_distinct = false; + bool m_order_eq = false; unsigned m_blast_distinct_threshold; bool m_ite_extra_rules; unsigned m_local_ctx_limit; @@ -90,6 +91,7 @@ class bool_rewriter { bool elim_and() const { return m_elim_and; } void set_elim_and(bool f) { m_elim_and = f; } void reset_local_ctx_cost() { m_local_ctx_cost = 0; } + void set_order_eq(bool f) { m_order_eq = f; } void updt_params(params_ref const & p); diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 9bc8c22cf6e..456a7afba13 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -707,9 +707,10 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr_ref mk_eq(expr* a, expr* b) { expr_ref result(m()); - if (a->get_id() > b->get_id()) - std::swap(a, b); - if (BR_FAILED == reduce_eq(a, b, result)) + br_status st = reduce_eq(a, b, result); + if (BR_FAILED == st) + st = m_b_rw.mk_eq_core(a, b, result); + if (BR_FAILED == st) result = m().mk_eq(a, b); return result; } @@ -945,6 +946,10 @@ void th_rewriter::set_flat_and_or(bool f) { m_imp->cfg().m_b_rw.set_flat_and_or(f); } +void th_rewriter::set_order_eq(bool f) { + m_imp->cfg().m_b_rw.set_order_eq(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 2c08c247d35..a3f0037992e 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -40,6 +40,7 @@ class th_rewriter { static void get_param_descrs(param_descrs & r); void set_flat_and_or(bool f); + void set_order_eq(bool f); unsigned get_cache_size() const; unsigned get_num_steps() const; diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 1ca980d0739..2825f3244de 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -43,6 +43,7 @@ Algorithm for extracting canonical form from an E-graph: #include "ast/ast_util.h" #include "ast/euf/euf_egraph.h" #include "ast/simplifiers/euf_completion.h" +#include "ast/shared_occs.h" namespace euf { @@ -55,20 +56,99 @@ namespace euf { 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); + m_rewriter.set_order_eq(true); + m_rewriter.set_flat_and_or(false); } void completion::reduce() { - unsigned rounds = 0; - do { + + propagate_values(); + if (m_fmls.inconsistent()) + return; + m_has_new_eq = true; + for (unsigned rounds = 0; m_has_new_eq && rounds <= 3 && !m_fmls.inconsistent(); ++rounds) { ++m_epoch; - ++rounds; m_has_new_eq = false; add_egraph(); map_canonical(); read_egraph(); IF_VERBOSE(11, verbose_stream() << "(euf.completion :rounds " << rounds << ")\n"); } - while (m_has_new_eq && rounds <= 3); + } + + /** + * Propagate writes into values first. It is cheaper to propagate values directly than using + * the E-graph. The E-graph suffers from the following overhead: it prefers interpreted nodes + * as roots and therefore the "merge" function ignores the heuristic of choosing the node to appoint root + * as the one with the fewest parents. Merging a constant value with multiple terms then has a compounding + * quadratic time overhead since the parents of the value are removed and re-inserted into the congruence + * table repeatedly and with growing size (exceeding the n*log(n) overhead when choosing the root to + * have the fewest parents). + */ + void completion::propagate_values() { + shared_occs shared(m, true); + expr_substitution subst(m, true, false); + expr* x, * y; + expr_ref_buffer args(m); + auto add_shared = [&]() { + shared_occs_mark visited; + shared.reset(); + for (unsigned i = 0; i < m_fmls.size(); ++i) + shared(m_fmls[i].fml(), visited); + }; + + auto add_sub = [&](dependent_expr const& de) { + auto const& [f, dep] = de(); + if (m.is_not(f, x) && shared.is_shared(x)) + subst.insert(x, m.mk_false(), dep); + else if (shared.is_shared(f)) + subst.insert(f, m.mk_true(), dep); + if (m.is_eq(f, x, y) && m.is_value(x) && shared.is_shared(y)) + subst.insert(y, x, dep); + else if (m.is_eq(f, x, y) && m.is_value(y) && shared.is_shared(x)) + subst.insert(x, y, dep); + }; + + auto process_fml = [&](unsigned i) { + expr* f = m_fmls[i].fml(); + expr_dependency* dep = m_fmls[i].dep(); + expr_ref fml(m); + proof_ref pr(m); + m_rewriter(f, fml, pr); + if (fml != f) { + dep = m.mk_join(dep, m_rewriter.get_used_dependencies()); + m_fmls.update(i, dependent_expr(m, fml, dep)); + ++m_stats.m_num_rewrites; + } + m_rewriter.reset_used_dependencies(); + add_sub(m_fmls[i]); + }; + + unsigned rw = m_stats.m_num_rewrites + 1; + for (unsigned r = 0; r < 4 && rw != m_stats.m_num_rewrites; ++r) { + rw = m_stats.m_num_rewrites; + add_shared(); + subst.reset(); + m_rewriter.reset(); + m_rewriter.set_substitution(&subst); + for (unsigned i = 0; i < m_qhead; ++i) + add_sub(m_fmls[i]); + for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) + process_fml(i); + add_shared(); + subst.reset(); + m_rewriter.reset(); + m_rewriter.set_substitution(&subst); + for (unsigned i = 0; i < m_qhead; ++i) + add_sub(m_fmls[i]); + for (unsigned i = m_fmls.size(); i-- > m_qhead && !m_fmls.inconsistent();) + process_fml(i); + if (subst.empty()) + break; + } + + m_rewriter.set_substitution(nullptr); + m_rewriter.reset(); } void completion::add_egraph() { diff --git a/src/ast/simplifiers/euf_completion.h b/src/ast/simplifiers/euf_completion.h index f02e3324569..830f8655ef1 100644 --- a/src/ast/simplifiers/euf_completion.h +++ b/src/ast/simplifiers/euf_completion.h @@ -46,6 +46,7 @@ namespace euf { bool is_new_eq(expr* a, expr* b); void update_has_new_eq(expr* g); expr_ref mk_and(expr* a, expr* b); + void propagate_values(); void add_egraph(); void map_canonical(); void read_egraph(); From 0a28bacd0f6d46a8ac61229fe8bd118b158b1bba Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 16:42:36 +0700 Subject: [PATCH 111/597] remove debug out --- src/smt/smt_context.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index a8d2f268d4d..45c469bde50 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1346,7 +1346,6 @@ namespace smt { TRACE("add_diseq", display_eq_detail(tout, bool_var2enode(v));); if (!add_diseq(get_enode(lhs), get_enode(rhs)) && !inconsistent()) { literal n_eq = literal(l.var(), true); - IF_VERBOSE(0, verbose_stream() << "eq-conflict @" << m_scope_lvl << "\n"); set_conflict(b_justification(mk_justification(eq_propagation_justification(get_enode(lhs), get_enode(rhs)))), n_eq); } } From 0a671f2f44f7a041db0948b7c53719a0396e1c9c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 17:21:51 +0700 Subject: [PATCH 112/597] fix #6464 --- src/smt/theory_bv.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index adcafb2e4fa..62b7a2a20b1 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1318,7 +1318,7 @@ namespace smt { SASSERT(consequent.var() != antecedent.var()); TRACE("bv_bit_prop", tout << "assigning: " << consequent << " @ " << ctx.get_scope_level(); tout << " using "; ctx.display_literal(tout, antecedent); - tout << " #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << " idx: " << idx << "\n"; + tout << " " << enode_pp(get_enode(v1), ctx) << " " << enode_pp(get_enode(v2), ctx) << " idx: " << idx << "\n"; tout << "propagate_eqc: " << propagate_eqc << "\n";); if (consequent == false_literal) { m_stats.m_num_conflicts++; @@ -1358,6 +1358,9 @@ namespace smt { // So, we need to propagate the assignment to other bits. bool_var bv = consequent.var(); atom * a = get_bv2a(bv); + CTRACE("bv", !a, tout << ctx.literal2expr(literal(bv, false)) << "\n"); + if (!a) + return; SASSERT(a->is_bit()); bit_atom * b = static_cast(a); var_pos_occ * curr = b->m_occs; From f87e187b629891cf1ec3e6e43d29f87457520c63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 17:52:14 +0700 Subject: [PATCH 113/597] #6429 --- src/ast/euf/euf_egraph.cpp | 7 ++++--- src/sat/smt/euf_model.cpp | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 1448ee8d7bf..037020a2ec8 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -158,6 +158,8 @@ namespace euf { } void egraph::add_literal(enode* n, enode* ante) { + if (n->bool_var() == sat::null_bool_var) + return; TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";); m_new_lits.push_back(enode_pair(n, ante)); m_updates.push_back(update_record(update_record::new_lit())); @@ -272,9 +274,8 @@ namespace euf { if (enable_merge_tf && n->value() != l_undef && !m.is_value(n->get_root()->get_expr())) { expr* b = n->value() == l_true ? m.mk_true() : m.mk_false(); enode* tf = find(b); - if (!tf) - tf = mk(b, 0, 0, nullptr); - add_literal(n, tf); + if (tf) + add_literal(n, tf); } } } diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index eef873afa2a..0fd021d7045 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -348,6 +348,8 @@ namespace euf { continue; if (!is_relevant(n)) continue; + if (n->bool_var() == sat::null_bool_var) + continue; bool tt = l_true == s().value(n->bool_var()); if (tt && !mdl.is_false(e)) continue; From 4ac5e51e3ac7d986eee455f5cf4ccf9909046acf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Nov 2022 18:35:17 +0700 Subject: [PATCH 114/597] #6429 --- src/sat/smt/euf_solver.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 9b346543f56..7473be61dc5 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -365,11 +365,12 @@ namespace euf { continue; if (k->value() == new_value) continue; + literal litk(k->bool_var(), sign); + if (s().value(litk) == l_true) + continue; auto& c = lit_constraint(n); - propagate(literal(k->bool_var(), sign), c.to_index()); - if (k->value() == l_undef) - m_egraph.set_value(k, new_value, justification::external(to_ptr(l))); - else + propagate(litk, c.to_index()); + if (s().value(litk) == l_false) return; } } @@ -591,15 +592,18 @@ namespace euf { euf::enode* n = m_egraph.nodes()[i]; if (!m.is_bool(n->get_expr()) || !is_shared(n)) continue; - if (n->value() == l_true && !m.is_true(n->get_root()->get_expr())) { + if (n->value() == l_true && n->cgc_enabled() && !m.is_true(n->get_root()->get_expr())) { + TRACE("euf", tout << "merge " << bpp(n) << "\n"); m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var()))); merged = true; } - if (n->value() == l_false && !m.is_false(n->get_root()->get_expr())) { + if (n->value() == l_false && n->cgc_enabled() && !m.is_false(n->get_root()->get_expr())) { + TRACE("euf", tout << "merge " << bpp(n) << "\n"); m_egraph.merge(n, mk_false(), to_ptr(~sat::literal(n->bool_var()))); merged = true; } } + CTRACE("euf", merged, tout << "shared bools merged\n"); return merged; } From eceeb295fc1f006b30c0932b3adad4173b65f327 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 14:41:50 +0700 Subject: [PATCH 115/597] fix #6457 --- src/ast/rewriter/bv_rewriter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index c594233102e..972259bd724 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -2388,7 +2388,9 @@ br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { expr* a = nullptr, *b = nullptr, *c = nullptr; if (m().is_ite(lhs, a, b, c)) { bool_rewriter rw(m()); - result = rw.mk_ite(a, rw.mk_eq(b, rhs), rw.mk_eq(c, rhs)); + expr_ref e1(rw.mk_eq(b, rhs), m()); + expr_ref e2(rw.mk_eq(c, rhs), m()); + result = rw.mk_ite(a, e1, e2); return BR_REWRITE2; } From 15dc7b78a0747619b3e4ab0c800a08ec001fcc38 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 15:09:13 +0700 Subject: [PATCH 116/597] Move merge_tf handling to euf_internalize literals true/false are not necessarily created when the merge flag is set. Also disable merge_tf for if-then-else expressions Perhaps even not insert children of if expressions into congruence table? --- src/ast/euf/euf_egraph.cpp | 12 ++---------- src/sat/smt/euf_internalize.cpp | 27 +++++++++++++++++++++++++++ src/sat/smt/euf_solver.h | 2 -- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 037020a2ec8..ad88f839fc8 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -36,10 +36,8 @@ namespace euf { } m_expr2enode.setx(f->get_id(), n, nullptr); push_node(n); - for (unsigned i = 0; i < num_args; ++i) { - set_cgc_enabled(args[i], true); - set_merge_tf_enabled(args[i], true); - } + for (unsigned i = 0; i < num_args; ++i) + set_cgc_enabled(args[i], true); return n; } @@ -271,12 +269,6 @@ namespace euf { if (enable_merge_tf != n->merge_tf()) { n->set_merge_tf(enable_merge_tf); m_updates.push_back(update_record(n, update_record::toggle_merge_tf())); - if (enable_merge_tf && n->value() != l_undef && !m.is_value(n->get_root()->get_expr())) { - expr* b = n->value() == l_true ? m.mk_true() : m.mk_false(); - enode* tf = find(b); - if (tf) - add_literal(n, tf); - } } } diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index 3aaafa36cee..792ffcf1db3 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -474,6 +474,33 @@ namespace euf { enode* n = m_egraph.mk(e, m_generation, num, args); if (si.is_bool_op(e)) m_egraph.set_cgc_enabled(n, false); + + // + // (if p th el) (non-Boolean case) produces clauses + // (=> p (= (if p th el) th)) + // and (=> (not p) (= (if p th el) el)) + // The clauses establish equalities between the ite term and + // the th or el sub-terms. + // + if (m.is_ite(e)) + num = 0; + + // + // To track congruences of Boolean children under non-Boolean + // functions set the merge_tf flag to true. + // + for (unsigned i = 0; i < num; ++i) { + if (!m.is_bool(args[i]->get_sort())) + continue; + bool was_enabled = args[i]->merge_tf(); + m_egraph.set_merge_tf_enabled(args[i], true); + if (!was_enabled && n->value() != l_undef && !m.is_value(n->get_root()->get_expr())) { + if (n->value() == l_true) + m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var()))); + else + m_egraph.merge(n, mk_false(), to_ptr(~sat::literal(n->bool_var()))); + } + } return n; } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 5b09e6a465b..675ec150099 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -146,7 +146,6 @@ namespace euf { constraint* m_conflict = nullptr; constraint* m_eq = nullptr; - constraint* m_lit = nullptr; // proofs bool m_proof_initialized = false; @@ -266,7 +265,6 @@ namespace euf { ~solver() override { if (m_conflict) dealloc(sat::constraint_base::mem2base_ptr(m_conflict)); if (m_eq) dealloc(sat::constraint_base::mem2base_ptr(m_eq)); - if (m_lit) dealloc(sat::constraint_base::mem2base_ptr(m_lit)); m_trail.reset(); } From 5fe2ff84e9cac0ecabe2a7d86cdf36691f69dce4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 19:45:16 +0700 Subject: [PATCH 117/597] change functionality to not track ite terms for congruence closure --- src/sat/smt/euf_internalize.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index 792ffcf1db3..a1d383e4578 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -468,12 +468,13 @@ namespace euf { euf::enode* solver::mk_enode(expr* e, unsigned num, enode* const* args) { + // + // Don't track congruences of Boolean connectives or arguments. + // The assignments to associated literals is sufficient + // + if (si.is_bool_op(e)) num = 0; - - enode* n = m_egraph.mk(e, m_generation, num, args); - if (si.is_bool_op(e)) - m_egraph.set_cgc_enabled(n, false); // // (if p th el) (non-Boolean case) produces clauses @@ -484,6 +485,10 @@ namespace euf { // if (m.is_ite(e)) num = 0; + + enode* n = m_egraph.mk(e, m_generation, num, args); + if (si.is_bool_op(e)) + m_egraph.set_cgc_enabled(n, false); // // To track congruences of Boolean children under non-Boolean From caf204ab95dbf1c5f88b3ce89a478248c88865ba Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 19:45:51 +0700 Subject: [PATCH 118/597] hoist macro-replacer as shared utility, update eliminate-predicates and model reconstruction --- src/ast/rewriter/CMakeLists.txt | 1 + src/ast/rewriter/macro_replacer.cpp | 142 ++++++++++ src/ast/rewriter/macro_replacer.h | 44 +++ src/ast/simplifiers/eliminate_predicates.cpp | 268 ++++++++++-------- .../model_reconstruction_trail.cpp | 30 +- .../simplifiers/model_reconstruction_trail.h | 13 +- 6 files changed, 367 insertions(+), 131 deletions(-) create mode 100644 src/ast/rewriter/macro_replacer.cpp create mode 100644 src/ast/rewriter/macro_replacer.h diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index 4bd6d07c4f3..c785804c127 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -25,6 +25,7 @@ z3_add_component(rewriter hoist_rewriter.cpp inj_axiom.cpp label_rewriter.cpp + macro_replacer.cpp maximize_ac_sharing.cpp mk_simplified_app.cpp pb_rewriter.cpp diff --git a/src/ast/rewriter/macro_replacer.cpp b/src/ast/rewriter/macro_replacer.cpp new file mode 100644 index 00000000000..da0131bf7d7 --- /dev/null +++ b/src/ast/rewriter/macro_replacer.cpp @@ -0,0 +1,142 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + macro_replacer.cpp + +Abstract: + + Abstract (functor) for applying macro replacement. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +Notes: + +--*/ + +#include "ast/rewriter/macro_replacer.h" +#include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/var_subst.h" + +/** +* Rewriting formulas using macro definitions. +*/ +struct macro_replacer::macro_replacer_cfg : public default_rewriter_cfg { + ast_manager& m; + macro_replacer& ep; + expr_dependency_ref& m_used_macro_dependencies; + expr_ref_vector m_trail; + + macro_replacer_cfg(ast_manager& m, macro_replacer& ep, expr_dependency_ref& deps) : + m(m), + ep(ep), + m_used_macro_dependencies(deps), + m_trail(m) + {} + + bool rewrite_patterns() const { return false; } + bool flat_assoc(func_decl* f) const { return false; } + br_status reduce_app(func_decl* f, unsigned num, expr* const* args, expr_ref& result, proof_ref& result_pr) { + result_pr = nullptr; + return BR_FAILED; + } + + /** + * adapted from macro_manager.cpp + * Perhaps hoist and combine? + */ + bool reduce_quantifier(quantifier* old_q, + expr* new_body, + expr* const* new_patterns, + expr* const* new_no_patterns, + expr_ref& result, + proof_ref& result_pr) { + + bool erase_patterns = false; + for (unsigned i = 0; !erase_patterns && i < old_q->get_num_patterns(); i++) + if (old_q->get_pattern(i) != new_patterns[i]) + erase_patterns = true; + + for (unsigned i = 0; !erase_patterns && i < old_q->get_num_no_patterns(); i++) + if (old_q->get_no_pattern(i) != new_no_patterns[i]) + erase_patterns = true; + + if (erase_patterns) + result = m.update_quantifier(old_q, 0, nullptr, 0, nullptr, new_body); + + if (erase_patterns && m.proofs_enabled()) + result_pr = m.mk_rewrite(old_q, result); + + return erase_patterns; + } + + bool get_subst(expr* _n, expr*& r, proof*& p) { + if (!is_app(_n)) + return false; + p = nullptr; + app* n = to_app(_n); + quantifier* q = nullptr; + func_decl* d = n->get_decl(), * d2 = nullptr; + app_ref head(m); + expr_ref def(m); + expr_dependency_ref dep(m); + if (ep.has_macro(d, head, def, dep)) { + unsigned num = head->get_num_args(); + ptr_buffer subst_args; + subst_args.resize(num, 0); + for (unsigned i = 0; i < num; i++) { + var* v = to_var(head->get_arg(i)); + VERIFY(v->get_idx() < num); + unsigned nidx = num - v->get_idx() - 1; + SASSERT(!subst_args[nidx]); + subst_args[nidx] = n->get_arg(i); + } + var_subst s(m); + expr_ref rr = s(def, num, subst_args.data()); + r = rr; + m_trail.push_back(rr); + m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, dep); + // skip proof terms for simplifiers + return true; + } + + return false; + } +}; + +struct macro_replacer::macro_replacer_rw : public rewriter_tpl { + macro_replacer::macro_replacer_cfg m_cfg; + + macro_replacer_rw(ast_manager& m, macro_replacer& ep, expr_dependency_ref& deps) : + rewriter_tpl(m, false, m_cfg), + m_cfg(m, ep, deps) + {} +}; + + +void macro_replacer::insert(app* head, expr* def, expr_dependency* dep) { + func_decl* f = head->get_decl(); + m_trail.push_back(head); + m_trail.push_back(def); + m_deps.push_back(dep); + m_map.insert(f, std::tuple(head, def, dep)); +} + +void macro_replacer::operator()(expr* t, expr_ref& result, expr_dependency_ref& dep) { + macro_replacer_rw exp(m, *this, dep); + exp(t, result); +} + +bool macro_replacer::has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& dep) { + std::tuple v; + if (!m_map.find(f, v)) + return false; + auto const& [h, d, dp] = v; + head = h; + def = d; + dep = dp; + return true; +} diff --git a/src/ast/rewriter/macro_replacer.h b/src/ast/rewriter/macro_replacer.h new file mode 100644 index 00000000000..a0cc5242b07 --- /dev/null +++ b/src/ast/rewriter/macro_replacer.h @@ -0,0 +1,44 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + macro_replacer.h + +Abstract: + + Abstract (functor) for applying macro replacement. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +Notes: + +--*/ +#pragma once + +#include "ast/ast.h" +#include "util/obj_hashtable.h" + + +class macro_replacer { + ast_manager& m; + ast_ref_vector m_trail; + expr_dependency_ref_vector m_deps; + obj_map> m_map; + struct macro_replacer_cfg; + struct macro_replacer_rw; + +public: + + macro_replacer(ast_manager& m): m(m), m_trail(m), m_deps(m) {} + + void insert(app* head, expr* def, expr_dependency* dep); + void operator()(expr* t, expr_ref& result, expr_dependency_ref& dep); + void operator()(expr* t, expr_ref & result) { expr_dependency_ref dep(m); (*this)(t, result, dep); } + void operator()(expr_ref & t) { expr_ref s(t, m); (*this)(s, t); } + + bool has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& d); +}; + diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index a46eda102a8..052a44397eb 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -58,105 +58,7 @@ forbidden from macros vs forbidden from elimination #include "ast/rewriter/rewriter_def.h" #include "ast/simplifiers/eliminate_predicates.h" #include "ast/rewriter/th_rewriter.h" - - -/** -* Rewriting formulas using macro definitions. -*/ -struct eliminate_predicates::macro_expander_cfg : public default_rewriter_cfg { - ast_manager& m; - eliminate_predicates& ep; - expr_dependency_ref& m_used_macro_dependencies; - expr_ref_vector m_trail; - - macro_expander_cfg(ast_manager& m, eliminate_predicates& ep, expr_dependency_ref& deps) : - m(m), - ep(ep), - m_used_macro_dependencies(deps), - m_trail(m) - {} - - bool rewrite_patterns() const { return false; } - bool flat_assoc(func_decl* f) const { return false; } - br_status reduce_app(func_decl* f, unsigned num, expr* const* args, expr_ref& result, proof_ref& result_pr) { - result_pr = nullptr; - return BR_FAILED; - } - - /** - * adapted from macro_manager.cpp - * Perhaps hoist and combine? - */ - bool reduce_quantifier(quantifier* old_q, - expr* new_body, - expr* const* new_patterns, - expr* const* new_no_patterns, - expr_ref& result, - proof_ref& result_pr) { - - bool erase_patterns = false; - for (unsigned i = 0; !erase_patterns && i < old_q->get_num_patterns(); i++) - if (old_q->get_pattern(i) != new_patterns[i]) - erase_patterns = true; - - for (unsigned i = 0; !erase_patterns && i < old_q->get_num_no_patterns(); i++) - if (old_q->get_no_pattern(i) != new_no_patterns[i]) - erase_patterns = true; - - if (erase_patterns) - result = m.update_quantifier(old_q, 0, nullptr, 0, nullptr, new_body); - - if (erase_patterns && m.proofs_enabled()) - result_pr = m.mk_rewrite(old_q, result); - - return erase_patterns; - } - - bool get_subst(expr* _n, expr*& r, proof*& p) { - if (!is_app(_n)) - return false; - p = nullptr; - app* n = to_app(_n); - quantifier* q = nullptr; - func_decl* d = n->get_decl(), * d2 = nullptr; - app_ref head(m); - expr_ref def(m); - expr_dependency_ref dep(m); - if (ep.has_macro(d, head, def, dep)) { - unsigned num = head->get_num_args(); - ptr_buffer subst_args; - subst_args.resize(num, 0); - // TODO: we can exploit that variables occur in "non-standard" order - // that is in order (:var 0) (:var 1) (:var 2) - // then substitution just takes n->get_args() instead of this renaming. - for (unsigned i = 0; i < num; i++) { - var* v = to_var(head->get_arg(i)); - VERIFY(v->get_idx() < num); - unsigned nidx = num - v->get_idx() - 1; - SASSERT(subst_args[nidx] == 0); - subst_args[nidx] = n->get_arg(i); - } - var_subst s(m); - expr_ref rr = s(def, num, subst_args.data()); - r = rr; - m_trail.push_back(rr); - m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, dep); - // skip proof terms for simplifiers - return true; - } - - return false; - } -}; - -struct eliminate_predicates::macro_expander_rw : public rewriter_tpl { - eliminate_predicates::macro_expander_cfg m_cfg; - - macro_expander_rw(ast_manager& m, eliminate_predicates& ep, expr_dependency_ref& deps) : - rewriter_tpl(m, false, m_cfg), - m_cfg(m, ep, deps) - {} -}; +#include "ast/rewriter/macro_replacer.h" std::ostream& eliminate_predicates::clause::display(std::ostream& out) const { @@ -200,10 +102,18 @@ void eliminate_predicates::add_use_list(clause& cl) { * Check that all arguments are distinct variables that are bound. */ -bool eliminate_predicates::can_be_macro_head(app* head, unsigned num_bound) { - uint_set indices; - if (head->get_decl()->is_associative()) +bool eliminate_predicates::can_be_macro_head(expr* _head, unsigned num_bound) { + if (!is_app(_head)) + return false; + app* head = to_app(_head); + func_decl* f = head->get_decl(); + if (m_disable_macro.is_marked(f)) + return false; + if (m_is_macro.is_marked(f)) return false; + if (f->is_associative()) + return false; + uint_set indices; for (expr* arg : *head) { if (!is_var(arg)) return false; @@ -315,7 +225,7 @@ bool eliminate_predicates::is_macro_safe(expr* e) { return true; } -void eliminate_predicates::insert_macro(app_ref& head, expr_ref& def, expr_dependency_ref& dep) { +void eliminate_predicates::insert_macro(app* head, expr* def, expr_dependency* dep) { unsigned num = head->get_num_args(); ptr_buffer vars, subst_args; subst_args.resize(num, nullptr); @@ -330,13 +240,17 @@ void eliminate_predicates::insert_macro(app_ref& head, expr_ref& def, expr_depen vars[i] = w; } var_subst sub(m, false); - def = sub(def, subst_args.size(), subst_args.data()); - head = m.mk_app(head->get_decl(), vars); - auto* info = alloc(macro_def, head, def, dep); + app_ref _head(m); + expr_ref _def(m); + expr_dependency_ref _dep(dep, m); + _def = sub(def, subst_args.size(), subst_args.data()); + _head = m.mk_app(head->get_decl(), vars); + + auto* info = alloc(macro_def, _head, _def, _dep); m_macros.insert(head->get_decl(), info); - m_fmls.model_trail().push(head->get_decl(), def, {}); + m_fmls.model_trail().push(head->get_decl(), _def, _dep, {}); // augment with definition for head m_is_macro.mark(head->get_decl(), true); - TRACE("elim_predicates", tout << "insert " << head << " " << def << "\n"); + TRACE("elim_predicates", tout << "insert " << _head << " " << _def << "\n"); ++m_stats.m_num_macros; } @@ -348,20 +262,124 @@ void eliminate_predicates::try_resolve_definition(func_decl* p) { insert_macro(head, def, dep); } -bool eliminate_predicates::has_macro(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep) { - macro_def* md = nullptr; - if (m_macros.find(p, md)) { - head = md->m_head; - def = md->m_def; - dep = md->m_dep; - return true; +/** +* Port of macros handled by macro_finder/macro_util +*/ +void eliminate_predicates::try_find_macro(clause& cl) { + if (!cl.m_alive) + return; + expr* x, * y; + auto can_be_def = [&](expr* _x, expr* y) { + if (!is_app(_x)) + return false; + app* x = to_app(_x); + return + can_be_macro_head(x, cl.m_bound.size()) && + is_macro_safe(y) && + x->get_num_args() == cl.m_bound.size() && + !occurs(x->get_decl(), y); + }; + // (= (f x) t) + if (cl.is_unit() && !cl.sign(0) && m.is_eq(cl.atom(0), x, y)) { + if (can_be_def(x, y)) { + insert_macro(to_app(x), y, cl.m_dep); + cl.m_alive = false; + return; + } + if (can_be_def(y, x)) { + insert_macro(to_app(y), x, cl.m_dep); + cl.m_alive = false; + return; + } } - return false; + // not (= (p x) t) -> (p x) = (not t) + if (cl.is_unit() && cl.sign(0) && m.is_iff(cl.atom(0), x, y)) { + if (can_be_def(x, y)) { + insert_macro(to_app(x), m.mk_not(y), cl.m_dep); + cl.m_alive = false; + return; + } + if (can_be_def(y, x)) { + insert_macro(to_app(y), m.mk_not(x), cl.m_dep); + cl.m_alive = false; + return; + } + } + + // pseudo-macros: + // (iff (= (f x) t) cond) + // rewrites to (f x) = (if cond t else (k x)) + // add clause (not (= (k x) t)) + // + // we will call them _conditioned_ macros + + auto can_be_conditioned = [&](expr* f, expr* t, expr* cond) { + return + can_be_def(f, t) && + !occurs(to_app(f)->get_decl(), cond) && + is_macro_safe(cond); + }; + + auto make_conditioned = [&](app* f, expr* t, expr* cond) { + func_decl* df = f->get_decl(); + app_ref def(m), k(m), fml(m); + func_decl_ref fn(m); + fn = m.mk_fresh_func_decl(df->get_arity(), df->get_domain(), df->get_range()); + m_fmls.model_trail().push(fn); // hide definition of fn + k = m.mk_app(fn, f->get_num_args(), f->get_args()); + def = m.mk_ite(cond, t, k); + insert_macro(f, def, cl.m_dep); + cl.m_alive = false; + fml = m.mk_not(m.mk_eq(k, t)); + init_clause(fml, cl.m_dep, UINT_MAX); + + }; + if (cl.is_unit() && !cl.sign(0) && m.is_iff(cl.atom(0), x, y)) { + expr* z, * u; + if (m.is_eq(x, z, u) && can_be_conditioned(z, u, y)) { + make_conditioned(to_app(z), u, y); + return; + } + if (m.is_eq(x, u, z) && can_be_conditioned(z, u, y)) { + make_conditioned(to_app(z), u, y); + return; + } + if (m.is_eq(y, z, u) && can_be_conditioned(z, u, x)) { + make_conditioned(to_app(z), u, x); + return; + } + if (m.is_eq(y, u, z) && can_be_conditioned(z, u, x)) { + make_conditioned(to_app(z), u, x); + return; + } + } + + // + // other macros handled by macro_finder: + // + + // arithmetic/bit-vectors + // (= (+ (f x) s) t) + // becomes (= (f x) (- t s)) + + // + // macro_finder also has: + // (>= (+ (f x) s) t) + // becomes (= (f x) (- t s (k x)) + // add (>= (k x) 0) + // why is this a real improvement? + // + // To review: quasi-macros + // (= (f x y (+ x y)) s), where x y are all bound variables. + // then ...? } + void eliminate_predicates::find_definitions() { for (auto* p : m_predicates) try_resolve_definition(p); + for (auto* cl : m_clauses) + try_find_macro(*cl); } void eliminate_predicates::rewrite(expr_ref& t) { @@ -374,13 +392,16 @@ void eliminate_predicates::reduce_definitions() { if (m_macros.empty()) return; + macro_replacer macro_expander(m); + for (auto const& [k, v] : m_macros) + macro_expander.insert(v->m_head, v->m_def, v->m_dep); + for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { auto [f, d] = m_fmls[i](); expr_ref fml(f, m), new_fml(m); expr_dependency_ref dep(m); while (true) { - macro_expander_rw macro_expander(m, *this, dep); - macro_expander(fml, new_fml); + macro_expander(fml, new_fml, dep); if (new_fml == fml) break; rewrite(new_fml); @@ -464,6 +485,7 @@ void eliminate_predicates::try_resolve(func_decl* p) { void eliminate_predicates::update_model(func_decl* p) { expr_ref_vector fmls(m); expr_ref def(m); + expr_dependency_ref dep(m); unsigned numpos = 0, numneg = 0; vector deleted; for (auto* pos : m_use_list.get(p, false)) @@ -475,19 +497,23 @@ void eliminate_predicates::update_model(func_decl* p) { if (numpos < numneg) { for (auto* pos : m_use_list.get(p, false)) - if (pos->m_alive) + if (pos->m_alive) { fmls.push_back(create_residue_formula(p, *pos)); + dep = m.mk_join(dep, pos->m_dep); + } def = mk_or(fmls); } else { for (auto* neg : m_use_list.get(p, true)) - if (neg->m_alive) + if (neg->m_alive) { fmls.push_back(mk_not(m, create_residue_formula(p, *neg))); + dep = m.mk_join(dep, neg->m_dep); + } def = mk_and(fmls); } rewrite(def); - m_fmls.model_trail().push(p, def, deleted); + m_fmls.model_trail().push(p, def, dep, deleted); } /** diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 55d04621ff6..8ccfbdf6e09 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -13,6 +13,7 @@ Module Name: #include "ast/for_each_expr.h" +#include "ast/rewriter/macro_replacer.h" #include "ast/simplifiers/model_reconstruction_trail.h" #include "ast/converters/generic_model_converter.h" @@ -32,11 +33,10 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectoris_hide()) continue; - // updates that have no intersections with current variables are skipped if (!t->intersects(free_vars)) - continue; + continue; // loose entries that intersect with free vars are deleted from the trail // and their removed formulas are added to the resulting constraints. @@ -49,8 +49,29 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectoris_def()) - NOT_IMPLEMENTED_YET(); + + if (t->is_def()) { + macro_replacer mrp(m); + app_ref head(m); + func_decl* d = t->m_decl; + ptr_buffer args; + for (unsigned i = 0; i < d->get_arity(); ++i) + args.push_back(m.mk_var(i, d->get_domain(i))); + head = m.mk_app(d, args); + mrp.insert(head, t->m_def, t->m_dep); + dependent_expr de(m, t->m_def, t->m_dep); + add_vars(de, free_vars); + + for (auto& d : added) { + auto [f, dep1] = d(); + expr_ref g(m); + expr_dependency_ref dep2(m); + mrp(f, g, dep2); + d = dependent_expr(m, g, m.mk_join(dep1, dep2)); + } + continue; + } + rp->set_substitution(t->m_subst.get()); // rigid entries: @@ -59,6 +80,7 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorreplace_with_dep(f); d = dependent_expr(m, g, m.mk_join(dep1, dep2)); + add_vars(d, free_vars); } } } diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 2ff11227e45..40db7dfbaee 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -37,16 +37,17 @@ class model_reconstruction_trail { vector m_removed; func_decl_ref m_decl; expr_ref m_def; + expr_dependency_ref m_dep; bool m_active = true; entry(ast_manager& m, expr_substitution* s, vector const& rem) : - m_subst(s), m_removed(rem), m_decl(m), m_def(m) {} + m_subst(s), m_removed(rem), m_decl(m), m_def(m), m_dep(m) {} - entry(ast_manager& m, func_decl* h) : m_decl(h, m), m_def(m) {} + entry(ast_manager& m, func_decl* h) : m_decl(h, m), m_def(m), m_dep(m) {} - entry(ast_manager& m, func_decl* f, expr* def, vector const& rem) : - m_decl(f, m), m_def(def, m), m_removed(rem) {} + entry(ast_manager& m, func_decl* f, expr* def, expr_dependency* dep, vector const& rem) : + m_decl(f, m), m_def(def, m), m_removed(rem), m_dep(dep, m) {} bool is_loose() const { return !m_removed.empty(); } @@ -109,8 +110,8 @@ class model_reconstruction_trail { /** * add definition */ - void push(func_decl* f, expr* def, vector const& removed) { - m_trail.push_back(alloc(entry, m, f, def, removed)); + void push(func_decl* f, expr* def, expr_dependency* dep, vector const& removed) { + m_trail.push_back(alloc(entry, m, f, def, dep, removed)); m_trail_stack.push(push_back_vector(m_trail)); } From 34791295829f35b6a3a2f880bcaa4ece25cf7e14 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 19:47:26 +0700 Subject: [PATCH 119/597] remove unused structs --- src/ast/simplifiers/eliminate_predicates.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index ff11bfd3fde..b2d62860262 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -92,9 +92,6 @@ class eliminate_predicates : public dependent_expr_simplifier { der_rewriter m_der; th_rewriter m_rewriter; obj_map m_macros; - - struct macro_expander_cfg; - struct macro_expander_rw; void rewrite(expr_ref& t); From decb3d3907eaa7949b59ffb7b63b39a743ed2887 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 19:51:26 +0700 Subject: [PATCH 120/597] stashed header file --- src/ast/simplifiers/eliminate_predicates.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index b2d62860262..f5d1cab962f 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -54,6 +54,10 @@ class eliminate_predicates : public dependent_expr_simplifier { {} std::ostream& display(std::ostream& out) const; + + expr* atom(unsigned i) const { return m_literals[i].first; } + bool sign(unsigned i) const { return m_literals[i].second; } + bool is_unit() const { return m_literals.size() == 1; } }; private: struct stats { @@ -102,11 +106,12 @@ class eliminate_predicates : public dependent_expr_simplifier { bool try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); void try_resolve_definition(func_decl* p); - void insert_macro(app_ref& head, expr_ref& def, expr_dependency_ref& dep); + void insert_macro(app* head, expr* def, expr_dependency* dep); bool has_macro(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); - bool can_be_macro_head(app* head, unsigned num_bound); + bool can_be_macro_head(expr* head, unsigned num_bound); bool is_macro_safe(expr* e); + void try_find_macro(clause& cl); void try_resolve(func_decl* p); void update_model(func_decl* p); From a64c7c5d19b0475b1872cf9118816d28c553155d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 21:52:55 +0700 Subject: [PATCH 121/597] add incremental version of value propagate --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/euf_completion.cpp | 79 -------------- src/ast/simplifiers/euf_completion.h | 1 - src/ast/simplifiers/propagate_values.cpp | 115 +++++++++++++++++++++ src/ast/simplifiers/propagate_values.h | 45 ++++++++ src/tactic/core/CMakeLists.txt | 1 + src/tactic/core/elim_uncnstr2_tactic.h | 1 - src/tactic/core/propagate_values2_tactic.h | 41 ++++++++ 8 files changed, 203 insertions(+), 81 deletions(-) create mode 100644 src/ast/simplifiers/propagate_values.cpp create mode 100644 src/ast/simplifiers/propagate_values.h create mode 100644 src/tactic/core/propagate_values2_tactic.h diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index b636b8b4214..9361937f7eb 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -6,6 +6,7 @@ z3_add_component(simplifiers euf_completion.cpp extract_eqs.cpp model_reconstruction_trail.cpp + propagate_values.cpp solve_context_eqs.cpp solve_eqs.cpp COMPONENT_DEPENDENCIES diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 2825f3244de..b768180cfdb 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -61,10 +61,6 @@ namespace euf { } void completion::reduce() { - - propagate_values(); - if (m_fmls.inconsistent()) - return; m_has_new_eq = true; for (unsigned rounds = 0; m_has_new_eq && rounds <= 3 && !m_fmls.inconsistent(); ++rounds) { ++m_epoch; @@ -76,81 +72,6 @@ namespace euf { } } - /** - * Propagate writes into values first. It is cheaper to propagate values directly than using - * the E-graph. The E-graph suffers from the following overhead: it prefers interpreted nodes - * as roots and therefore the "merge" function ignores the heuristic of choosing the node to appoint root - * as the one with the fewest parents. Merging a constant value with multiple terms then has a compounding - * quadratic time overhead since the parents of the value are removed and re-inserted into the congruence - * table repeatedly and with growing size (exceeding the n*log(n) overhead when choosing the root to - * have the fewest parents). - */ - void completion::propagate_values() { - shared_occs shared(m, true); - expr_substitution subst(m, true, false); - expr* x, * y; - expr_ref_buffer args(m); - auto add_shared = [&]() { - shared_occs_mark visited; - shared.reset(); - for (unsigned i = 0; i < m_fmls.size(); ++i) - shared(m_fmls[i].fml(), visited); - }; - - auto add_sub = [&](dependent_expr const& de) { - auto const& [f, dep] = de(); - if (m.is_not(f, x) && shared.is_shared(x)) - subst.insert(x, m.mk_false(), dep); - else if (shared.is_shared(f)) - subst.insert(f, m.mk_true(), dep); - if (m.is_eq(f, x, y) && m.is_value(x) && shared.is_shared(y)) - subst.insert(y, x, dep); - else if (m.is_eq(f, x, y) && m.is_value(y) && shared.is_shared(x)) - subst.insert(x, y, dep); - }; - - auto process_fml = [&](unsigned i) { - expr* f = m_fmls[i].fml(); - expr_dependency* dep = m_fmls[i].dep(); - expr_ref fml(m); - proof_ref pr(m); - m_rewriter(f, fml, pr); - if (fml != f) { - dep = m.mk_join(dep, m_rewriter.get_used_dependencies()); - m_fmls.update(i, dependent_expr(m, fml, dep)); - ++m_stats.m_num_rewrites; - } - m_rewriter.reset_used_dependencies(); - add_sub(m_fmls[i]); - }; - - unsigned rw = m_stats.m_num_rewrites + 1; - for (unsigned r = 0; r < 4 && rw != m_stats.m_num_rewrites; ++r) { - rw = m_stats.m_num_rewrites; - add_shared(); - subst.reset(); - m_rewriter.reset(); - m_rewriter.set_substitution(&subst); - for (unsigned i = 0; i < m_qhead; ++i) - add_sub(m_fmls[i]); - for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) - process_fml(i); - add_shared(); - subst.reset(); - m_rewriter.reset(); - m_rewriter.set_substitution(&subst); - for (unsigned i = 0; i < m_qhead; ++i) - add_sub(m_fmls[i]); - for (unsigned i = m_fmls.size(); i-- > m_qhead && !m_fmls.inconsistent();) - process_fml(i); - if (subst.empty()) - break; - } - - m_rewriter.set_substitution(nullptr); - m_rewriter.reset(); - } - void completion::add_egraph() { m_nodes_to_canonize.reset(); unsigned sz = m_fmls.size(); diff --git a/src/ast/simplifiers/euf_completion.h b/src/ast/simplifiers/euf_completion.h index 830f8655ef1..f02e3324569 100644 --- a/src/ast/simplifiers/euf_completion.h +++ b/src/ast/simplifiers/euf_completion.h @@ -46,7 +46,6 @@ namespace euf { bool is_new_eq(expr* a, expr* b); void update_has_new_eq(expr* g); expr_ref mk_and(expr* a, expr* b); - void propagate_values(); void add_egraph(); void map_canonical(); void read_egraph(); diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp new file mode 100644 index 00000000000..5979f1f2ad7 --- /dev/null +++ b/src/ast/simplifiers/propagate_values.cpp @@ -0,0 +1,115 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + propagate_values.h + +Abstract: + + relatively cheap value propagation + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +Notes: + + Incremental version of propagate_values_tactic + +--*/ + +#include "params/tactic_params.hpp" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/shared_occs.h" +#include "ast/simplifiers/propagate_values.h" + +propagate_values::propagate_values(ast_manager& m, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_rewriter(m) { + m_rewriter.set_order_eq(true); + m_rewriter.set_flat_and_or(false); +} + +void propagate_values::reduce() { + shared_occs shared(m, true); + expr_substitution subst(m, true, false); + expr* x, * y; + expr_ref_buffer args(m); + auto add_shared = [&]() { + shared_occs_mark visited; + shared.reset(); + for (unsigned i = 0; i < m_fmls.size(); ++i) + shared(m_fmls[i].fml(), visited); + }; + + auto add_sub = [&](dependent_expr const& de) { + auto const& [f, dep] = de(); + if (m.is_not(f, x) && shared.is_shared(x)) + subst.insert(x, m.mk_false(), dep); + else if (shared.is_shared(f)) + subst.insert(f, m.mk_true(), dep); + if (m.is_eq(f, x, y) && m.is_value(x) && shared.is_shared(y)) + subst.insert(y, x, dep); + else if (m.is_eq(f, x, y) && m.is_value(y) && shared.is_shared(x)) + subst.insert(x, y, dep); + }; + + auto process_fml = [&](unsigned i) { + expr* f = m_fmls[i].fml(); + expr_dependency* dep = m_fmls[i].dep(); + expr_ref fml(m); + proof_ref pr(m); + m_rewriter(f, fml, pr); + if (fml != f) { + dep = m.mk_join(dep, m_rewriter.get_used_dependencies()); + m_fmls.update(i, dependent_expr(m, fml, dep)); + ++m_stats.m_num_rewrites; + } + m_rewriter.reset_used_dependencies(); + add_sub(m_fmls[i]); + }; + + unsigned rw = m_stats.m_num_rewrites + 1; + for (unsigned r = 0; r < m_max_rounds && rw != m_stats.m_num_rewrites; ++r) { + rw = m_stats.m_num_rewrites; + add_shared(); + subst.reset(); + m_rewriter.reset(); + m_rewriter.set_substitution(&subst); + for (unsigned i = 0; i < m_qhead; ++i) + add_sub(m_fmls[i]); + for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) + process_fml(i); + add_shared(); + subst.reset(); + m_rewriter.reset(); + m_rewriter.set_substitution(&subst); + for (unsigned i = 0; i < m_qhead; ++i) + add_sub(m_fmls[i]); + for (unsigned i = m_fmls.size(); i-- > m_qhead && !m_fmls.inconsistent();) + process_fml(i); + if (subst.empty()) + break; + } + + m_rewriter.set_substitution(nullptr); + m_rewriter.reset(); + + advance_qhead(m_fmls.size()); +} + +void propagate_values::collect_statistics(statistics& st) const { + st.update("propagate-values-rewrites", m_stats.m_num_rewrites); +} + +void propagate_values::updt_params(params_ref const& p) { + tactic_params tp(p); + m_max_rounds = p.get_uint("max_rounds", tp.propagate_values_max_rounds()); +} + +void propagate_values::collect_param_descrs(param_descrs& r) { + th_rewriter::get_param_descrs(r); + r.insert("max_rounds", CPK_UINT, "(default: 4) maximum number of rounds."); +} \ No newline at end of file diff --git a/src/ast/simplifiers/propagate_values.h b/src/ast/simplifiers/propagate_values.h new file mode 100644 index 00000000000..3219f979691 --- /dev/null +++ b/src/ast/simplifiers/propagate_values.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + propagate_values.h + +Abstract: + + relatively cheap value propagation + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +Notes: + incremental version of propagate_values_tactic, to be replaced + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" + + +class propagate_values : public dependent_expr_simplifier { + + struct stats { + unsigned m_num_rewrites = 0; + void reset() { memset(this, 0, sizeof(*this)); } + }; + + th_rewriter m_rewriter; + stats m_stats; + unsigned m_max_rounds = 4; + +public: + propagate_values(ast_manager& m, dependent_expr_state& fmls); + void reduce() override; + void collect_statistics(statistics& st) const override; + void reset_statistics() override { m_stats.reset(); } + void updt_params(params_ref const& p) override; + void collect_param_descrs(param_descrs& r) override; +}; diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index 39e8def031b..bc8c4ab719c 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -45,6 +45,7 @@ z3_add_component(core_tactics occf_tactic.h pb_preprocess_tactic.h propagate_values_tactic.h + propagate_values2_tactic.h reduce_args_tactic.h simplify_tactic.h solve_eqs_tactic.h diff --git a/src/tactic/core/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h index 65b5d342698..d9f6196f2f7 100644 --- a/src/tactic/core/elim_uncnstr2_tactic.h +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -20,7 +20,6 @@ Module Name: #include "tactic/tactic.h" #include "tactic/dependent_expr_state_tactic.h" #include "ast/simplifiers/elim_unconstrained.h" -#include "ast/simplifiers/elim_unconstrained.h" class elim_uncnstr2_tactic_factory : public dependent_expr_simplifier_factory { public: diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h new file mode 100644 index 00000000000..2ca534a4983 --- /dev/null +++ b/src/tactic/core/propagate_values2_tactic.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + propagate_values2_tactic.h + +Abstract: + + Tactic for propagating equalities (= t v) where v is a value + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#include "util/params.h" +#pragma once + +#include "util/params.h" +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/propagate_values.h" + +class propagate_values2_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(propagate_values, m, s); + } +}; + +inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(propagate_values2_tactic_factory), "propagate-values2"); +} + + +/* + ADD_TACTIC("propagate-valuesx2", "propagate constants.", "mk_propagate_values2_tactic(m, p)") +*/ + From 1815812889c484b79d8a57e97834c0d8a8deb47a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 22:05:30 +0700 Subject: [PATCH 122/597] fix typo in name of tactic --- src/tactic/core/propagate_values2_tactic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h index 2ca534a4983..ab96461287a 100644 --- a/src/tactic/core/propagate_values2_tactic.h +++ b/src/tactic/core/propagate_values2_tactic.h @@ -36,6 +36,6 @@ inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & /* - ADD_TACTIC("propagate-valuesx2", "propagate constants.", "mk_propagate_values2_tactic(m, p)") + ADD_TACTIC("propagate-values2", "propagate constants.", "mk_propagate_values2_tactic(m, p)") */ From b0247d8201cc7c1076c411618882034a9a5fe49b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 22:20:25 +0700 Subject: [PATCH 123/597] add exception handling for rewriter exceptions --- src/tactic/dependent_expr_state_tactic.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index efe28c30d40..f16bb0ff3d1 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -102,8 +102,13 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { statistics_report sreport(*this); tactic_report report(name(), *in); m_goal = in.get(); - if (!in->proofs_enabled()) - m_simp->reduce(); + try { + if (!in->proofs_enabled()) + m_simp->reduce(); + } + catch (rewriter_exception& ex) { + throw tactic_exception(ex.msg()); + } m_goal->elim_true(); m_goal->elim_redundancies(); m_goal->inc_depth(); From eb812e47bea4a9aee8de58a7a1796d08ec8e064f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 22:46:35 +0700 Subject: [PATCH 124/597] cleanup --- src/ast/shared_occs.h | 7 ++--- src/ast/simplifiers/propagate_values.cpp | 35 ++++++++++++------------ 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/ast/shared_occs.h b/src/ast/shared_occs.h index 59ff99569b6..649a29e98ce 100644 --- a/src/ast/shared_occs.h +++ b/src/ast/shared_occs.h @@ -32,11 +32,8 @@ class shared_occs_mark { void reset_mark(ast * n) { n->reset_mark_so(); } void mark(ast * n) { if (is_marked(n)) return; n->mark_so(true); m_to_unmark.push_back(n); } void reset() { - ptr_buffer::iterator it = m_to_unmark.begin(); - ptr_buffer::iterator end = m_to_unmark.end(); - for (; it != end; ++it) { - reset_mark(*it); - } + for (auto* t : m_to_unmark) + reset_mark(t); m_to_unmark.reset(); } void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 5979f1f2ad7..b7bd6eb37cc 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -36,7 +36,7 @@ void propagate_values::reduce() { shared_occs shared(m, true); expr_substitution subst(m, true, false); expr* x, * y; - expr_ref_buffer args(m); + auto add_shared = [&]() { shared_occs_mark visited; shared.reset(); @@ -50,15 +50,16 @@ void propagate_values::reduce() { subst.insert(x, m.mk_false(), dep); else if (shared.is_shared(f)) subst.insert(f, m.mk_true(), dep); - if (m.is_eq(f, x, y) && m.is_value(x) && shared.is_shared(y)) - subst.insert(y, x, dep); - else if (m.is_eq(f, x, y) && m.is_value(y) && shared.is_shared(x)) - subst.insert(x, y, dep); + if (m.is_eq(f, x, y)) { + if (m.is_value(x) && shared.is_shared(y)) + subst.insert(y, x, dep); + else if (m.is_value(y) && shared.is_shared(x)) + subst.insert(x, y, dep); + } }; auto process_fml = [&](unsigned i) { - expr* f = m_fmls[i].fml(); - expr_dependency* dep = m_fmls[i].dep(); + auto [f, dep] = m_fmls[i](); expr_ref fml(m); proof_ref pr(m); m_rewriter(f, fml, pr); @@ -70,24 +71,23 @@ void propagate_values::reduce() { m_rewriter.reset_used_dependencies(); add_sub(m_fmls[i]); }; - - unsigned rw = m_stats.m_num_rewrites + 1; - for (unsigned r = 0; r < m_max_rounds && rw != m_stats.m_num_rewrites; ++r) { - rw = m_stats.m_num_rewrites; + + auto init_sub = [&]() { add_shared(); subst.reset(); m_rewriter.reset(); m_rewriter.set_substitution(&subst); for (unsigned i = 0; i < m_qhead; ++i) add_sub(m_fmls[i]); + }; + + unsigned rw = m_stats.m_num_rewrites + 1; + for (unsigned r = 0; r < m_max_rounds && rw != m_stats.m_num_rewrites; ++r) { + rw = m_stats.m_num_rewrites; + init_sub(); for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) process_fml(i); - add_shared(); - subst.reset(); - m_rewriter.reset(); - m_rewriter.set_substitution(&subst); - for (unsigned i = 0; i < m_qhead; ++i) - add_sub(m_fmls[i]); + init_sub(); for (unsigned i = m_fmls.size(); i-- > m_qhead && !m_fmls.inconsistent();) process_fml(i); if (subst.empty()) @@ -107,6 +107,7 @@ void propagate_values::collect_statistics(statistics& st) const { void propagate_values::updt_params(params_ref const& p) { tactic_params tp(p); m_max_rounds = p.get_uint("max_rounds", tp.propagate_values_max_rounds()); + m_rewriter.updt_params(p); } void propagate_values::collect_param_descrs(param_descrs& r) { From cb789f6ca8abb33fdc546452729732047ca337c0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Nov 2022 23:44:35 +0700 Subject: [PATCH 125/597] add arithmetical macros Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/eliminate_predicates.cpp | 111 ++++++++++++++++++- src/ast/simplifiers/eliminate_predicates.h | 1 - src/ast/simplifiers/propagate_values.cpp | 3 +- 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 052a44397eb..afa07a3a374 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -52,6 +52,8 @@ forbidden from macros vs forbidden from elimination #include "ast/ast_util.h" #include "ast/for_each_ast.h" #include "ast/recfun_decl_plugin.h" +#include "ast/bv_decl_plugin.h" +#include "ast/arith_decl_plugin.h" #include "ast/occurs.h" #include "ast/array_decl_plugin.h" #include "ast/rewriter/var_subst.h" @@ -331,9 +333,11 @@ void eliminate_predicates::try_find_macro(clause& cl) { insert_macro(f, def, cl.m_dep); cl.m_alive = false; fml = m.mk_not(m.mk_eq(k, t)); - init_clause(fml, cl.m_dep, UINT_MAX); - + clause* new_cl = init_clause(fml, cl.m_dep, UINT_MAX); + add_use_list(*new_cl); + m_clauses.push_back(new_cl); }; + if (cl.is_unit() && !cl.sign(0) && m.is_iff(cl.atom(0), x, y)) { expr* z, * u; if (m.is_eq(x, z, u) && can_be_conditioned(z, u, y)) { @@ -357,10 +361,111 @@ void eliminate_predicates::try_find_macro(clause& cl) { // // other macros handled by macro_finder: // - // arithmetic/bit-vectors // (= (+ (f x) s) t) // becomes (= (f x) (- t s)) + // + // TBD: + // (= (+ (* -1 (f x)) x) t) + // becomes (= (f x) (- (- t s))) + + bv_util bv(m); + arith_util a(m); + auto is_add = [&](expr * e) { + rational n; + return a.is_add(e) || bv.is_bv_add(e); + }; + + auto sub = [&](expr* t, expr* s) { + if (a.is_int_real(t)) + return expr_ref(a.mk_sub(t, s), m); + else + return expr_ref(bv.mk_bv_sub(t, s), m); + }; + + auto subtract = [&](expr* t, app* s, unsigned i) { + expr_ref result(t, m); + unsigned j = 0; + for (expr* arg : *s) { + ++j; + if (i != j) + result = sub(result, arg); + } + return result; + }; + + auto uminus = [&](expr* t) { + if (a.is_int_real(t)) + return expr_ref(a.mk_uminus(t), m); + else + return expr_ref(bv.mk_bv_neg(t), m); + }; + + auto is_inverse = [&](expr*& t) { + expr* x, * y; + rational n; + if (a.is_mul(t, x, y) && a.is_numeral(x, n) && n == -1) { + t = y; + return true; + } + if (bv.is_bv_mul(t, x, y) && bv.is_numeral(x, n) && n + 1 == rational::power_of_two(bv.get_bv_size(t))) { + t = y; + return true; + } + return false; + }; + + auto find_arith_macro = [&](expr* x, expr* y) { + if (!is_add(x)) + return false; + + if (!is_macro_safe(y)) + return false; + + unsigned i = 0; + for (expr* arg : *to_app(x)) { + ++i; + bool inv = is_inverse(arg); + if (!can_be_macro_head(arg, cl.m_bound.size())) + continue; + app* head = to_app(arg); + func_decl* f = head->get_decl(); + if (head->get_num_args() != cl.m_bound.size()) + continue; + if (occurs(f, y)) + continue; + unsigned j = 0; + for (expr* arg2 : *head) { + ++j; + if (i == j) + continue; + if (occurs(f, arg2)) + goto next; + if (!is_macro_safe(arg2)) + goto next; + } + { + // arg = y - x - arg; + expr_ref y1 = subtract(y, to_app(x), i); + if (inv) + y1 = uminus(y1); + insert_macro(to_app(arg), y1, cl.m_dep); + cl.m_alive = false; + return true; + } + next: + ; + } + return false; + }; + + if (cl.is_unit() && !cl.sign(0) && m.is_eq(cl.atom(0), x, y)) { + if (find_arith_macro(x, y)) + return; + if (find_arith_macro(y, x)) + return; + } + // // macro_finder also has: diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index f5d1cab962f..6afd0886a8c 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -107,7 +107,6 @@ class eliminate_predicates : public dependent_expr_simplifier { bool try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); void try_resolve_definition(func_decl* p); void insert_macro(app* head, expr* def, expr_dependency* dep); - bool has_macro(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); bool can_be_macro_head(expr* head, unsigned num_bound); bool is_macro_safe(expr* e); diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index b7bd6eb37cc..6a179674ba6 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -96,7 +96,6 @@ void propagate_values::reduce() { m_rewriter.set_substitution(nullptr); m_rewriter.reset(); - advance_qhead(m_fmls.size()); } @@ -113,4 +112,4 @@ void propagate_values::updt_params(params_ref const& p) { void propagate_values::collect_param_descrs(param_descrs& r) { th_rewriter::get_param_descrs(r); r.insert("max_rounds", CPK_UINT, "(default: 4) maximum number of rounds."); -} \ No newline at end of file +} From db74e23de1efe92170f3e6e9083516e6af652bc1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 11:07:31 +0700 Subject: [PATCH 126/597] make card2bv a simplifier --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/card2bv.cpp | 61 ++++++++++ src/ast/simplifiers/card2bv.h | 42 +++++++ src/ast/simplifiers/elim_unconstrained.cpp | 2 +- src/ast/simplifiers/eliminate_predicates.cpp | 2 +- .../simplifiers/model_reconstruction_trail.h | 2 +- src/tactic/arith/CMakeLists.txt | 1 - src/tactic/arith/card2bv_tactic.cpp | 105 ------------------ src/tactic/arith/card2bv_tactic.h | 97 +++------------- 9 files changed, 121 insertions(+), 192 deletions(-) create mode 100644 src/ast/simplifiers/card2bv.cpp create mode 100644 src/ast/simplifiers/card2bv.h delete mode 100644 src/tactic/arith/card2bv_tactic.cpp diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 9361937f7eb..d10bf87e7f4 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -1,6 +1,7 @@ z3_add_component(simplifiers SOURCES bv_slice.cpp + card2bv.cpp elim_unconstrained.cpp eliminate_predicates.cpp euf_completion.cpp diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp new file mode 100644 index 00000000000..d9c387c5ae2 --- /dev/null +++ b/src/ast/simplifiers/card2bv.cpp @@ -0,0 +1,61 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + card2bv.cpp + +Abstract: + + convert cardinality constraints to bit-vectors + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + + +#include "ast/simplifiers/card2bv.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/rewriter/pb2bv_rewriter.h" + +card2bv::card2bv(ast_manager& m, params_ref const& p, dependent_expr_state& fmls) : + dependent_expr_simplifier(m, fmls), m_params(p) {} + +void card2bv::reduce() { + th_rewriter rw1(m, m_params); + pb2bv_rewriter rw2(m, m_params); + + expr_ref new_f1(m), new_f2(m); + proof_ref new_pr(m); + for (unsigned idx = 0; !m_fmls.inconsistent() && idx < m_fmls.size(); idx++) { + auto [f, d] = m_fmls[idx](); + rw1(f, new_f1); + rw2(false, new_f1, new_f2, new_pr); + if (new_f2 != f) { + TRACE("card2bv", tout << "Rewriting " << new_f1 << "\n" << new_f2 << "\n"); + m_fmls.update(idx, dependent_expr(m, new_f2, d)); + ++m_stats.m_num_rewrites; + } + } + + expr_ref_vector fmls(m); + rw2.flush_side_constraints(fmls); + for (expr* e : fmls) + m_fmls.add(dependent_expr(m, e, nullptr)); + + func_decl_ref_vector const& fns = rw2.fresh_constants(); + for (func_decl* f : fns) + m_fmls.model_trail().hide(f); +} + +void card2bv::collect_statistics(statistics& st) const { + st.update("card2bv-rewrites", m_stats.m_num_rewrites); +} + +void card2bv::collect_param_descrs(param_descrs& r) { + r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints for solver"); + pb2bv_rewriter rw(m, m_params); + rw.collect_param_descrs(r); +} diff --git a/src/ast/simplifiers/card2bv.h b/src/ast/simplifiers/card2bv.h new file mode 100644 index 00000000000..e089fa84c87 --- /dev/null +++ b/src/ast/simplifiers/card2bv.h @@ -0,0 +1,42 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + card2bv.h + +Abstract: + + convert cardinality constraints to bit-vectors + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" + + +class card2bv : public dependent_expr_simplifier { + + struct stats { + unsigned m_num_rewrites = 0; + void reset() { memset(this, 0, sizeof(*this)); } + }; + + stats m_stats; + params_ref m_params; + +public: + card2bv(ast_manager& m, params_ref const& p, dependent_expr_state& fmls); + void reduce() override; + void collect_statistics(statistics& st) const override; + void reset_statistics() override { m_stats.reset(); } + void updt_params(params_ref const& p) override { m_params.append(p); } + void collect_param_descrs(param_descrs& r) override; +}; diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index d590f3e590a..52dc95f0567 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -285,7 +285,7 @@ void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector< for (auto const& entry : mc.entries()) { switch (entry.m_instruction) { case generic_model_converter::instruction::HIDE: - trail.push(entry.m_f); + trail.hide(entry.m_f); break; case generic_model_converter::instruction::ADD: break; diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index afa07a3a374..6d1288c5d70 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -327,7 +327,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { app_ref def(m), k(m), fml(m); func_decl_ref fn(m); fn = m.mk_fresh_func_decl(df->get_arity(), df->get_domain(), df->get_range()); - m_fmls.model_trail().push(fn); // hide definition of fn + m_fmls.model_trail().hide(fn); // hide definition of fn k = m.mk_app(fn, f->get_num_args(), f->get_args()); def = m.mk_ite(cond, t, k); insert_macro(f, def, cl.m_dep); diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 40db7dfbaee..906aaa73837 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -102,7 +102,7 @@ class model_reconstruction_trail { /** * add declaration to hide */ - void push(func_decl* f) { + void hide(func_decl* f) { m_trail.push_back(alloc(entry, m, f)); m_trail_stack.push(push_back_vector(m_trail)); } diff --git a/src/tactic/arith/CMakeLists.txt b/src/tactic/arith/CMakeLists.txt index cb025b20655..d5c7557e6c9 100644 --- a/src/tactic/arith/CMakeLists.txt +++ b/src/tactic/arith/CMakeLists.txt @@ -6,7 +6,6 @@ z3_add_component(arith_tactics bound_propagator.cpp bv2int_rewriter.cpp bv2real_rewriter.cpp - card2bv_tactic.cpp degree_shift_tactic.cpp diff_neq_tactic.cpp eq2bv_tactic.cpp diff --git a/src/tactic/arith/card2bv_tactic.cpp b/src/tactic/arith/card2bv_tactic.cpp deleted file mode 100644 index 4f79a887dd3..00000000000 --- a/src/tactic/arith/card2bv_tactic.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - card2bv_tactic.cpp - -Abstract: - - Tactic for converting Pseudo-Boolean constraints to BV - -Author: - - Nikolaj Bjorner (nbjorner) 2014-03-20 - -Notes: - ---*/ -#include "tactic/tactical.h" -#include "ast/ast_smt2_pp.h" -#include "tactic/arith/card2bv_tactic.h" -#include "ast/rewriter/pb2bv_rewriter.h" -#include "ast/ast_util.h" -#include "ast/ast_pp.h" -#include "ast/converters/generic_model_converter.h" - -class card2bv_tactic : public tactic { - ast_manager & m; - params_ref m_params; - -public: - - card2bv_tactic(ast_manager & m, params_ref const & p): - m(m), - m_params(p) { - } - - tactic * translate(ast_manager & m) override { - return alloc(card2bv_tactic, m, m_params); - } - - char const* name() const override { return "card2bv"; } - - void updt_params(params_ref const & p) override { - m_params.append(p); - } - - void collect_param_descrs(param_descrs & r) override { - r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints for solver"); - pb2bv_rewriter rw(m, m_params); - rw.collect_param_descrs(r); - } - - - void operator()(goal_ref const & g, - goal_ref_buffer & result) override { - TRACE("card2bv-before", g->display(tout);); - result.reset(); - tactic_report report("card2bv", *g); - th_rewriter rw1(m, m_params); - pb2bv_rewriter rw2(m, m_params); - - if (g->inconsistent()) { - result.push_back(g.get()); - return; - } - - expr_ref new_f1(m), new_f2(m); - for (unsigned idx = 0; !g->inconsistent() && idx < g->size(); idx++) { - proof_ref new_pr1(m), new_pr2(m); - rw1(g->form(idx), new_f1, new_pr1); - TRACE("card2bv", tout << "Rewriting " << new_f1 << "\n" << new_pr1 << std::endl;); - rw2(false, new_f1, new_f2, new_pr2); - TRACE("card2bv", tout << "Rewriting " << new_f2 << "\n" << new_pr2 << std::endl;); - if (m.proofs_enabled()) { - new_pr1 = m.mk_transitivity(new_pr1, new_pr2); - new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1); - } - g->update(idx, new_f2, new_pr1, g->dep(idx)); - } - expr_ref_vector fmls(m); - rw2.flush_side_constraints(fmls); - for (expr* e : fmls) { - g->assert_expr(e); - } - - func_decl_ref_vector const& fns = rw2.fresh_constants(); - if (!fns.empty()) { - generic_model_converter* filter = alloc(generic_model_converter, m, "card2bv"); - for (func_decl* f : fns) filter->hide(f); - g->add(filter); - } - - g->inc_depth(); - result.push_back(g.get()); - } - - void cleanup() override { - } -}; - -tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(card2bv_tactic, m, p)); -} - diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index 81296f18d8d..95282d93cce 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -13,95 +13,26 @@ Module Name: Nikolaj Bjorner (nbjorner) 2014-03-20 -Notes: - --*/ #pragma once -#include "util/params.h" -#include "ast/pb_decl_plugin.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/rewriter/rewriter.h" -#include -#include "util/sorting_network.h" - - -class ast_manager; -class tactic; - -namespace pb { - - class card2bv_rewriter { - public: - typedef expr* pliteral; - typedef ptr_vector pliteral_vector; - private: - ast_manager& m; - arith_util au; - pb_util pb; - bv_util bv; - psort_nw m_sort; - expr_ref_vector m_lemmas; - expr_ref_vector m_trail; - unsigned get_num_bits(func_decl* f); - void mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); - br_status mk_shannon(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); - expr* negate(expr* e); - expr* mk_ite(expr* c, expr* hi, expr* lo); - bool is_or(func_decl* f); - bool is_and(func_decl* f); - bool is_atmost1(func_decl* f, unsigned sz, expr * const* args, expr_ref& result); - expr_ref mk_atmost1(unsigned sz, expr * const* args); - void mk_at_most_1_small(bool last, unsigned n, pliteral const* xs, expr_ref_vector& result, expr_ref_vector& ors); - - public: - card2bv_rewriter(ast_manager& m); - br_status mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); - void mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas); - - // definitions used for sorting network - pliteral mk_false() { return m.mk_false(); } - pliteral mk_true() { return m.mk_true(); } - pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } - pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } - pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, pliteral lit); - pliteral fresh(); - pliteral trail(pliteral l); - void mk_clause(unsigned n, pliteral const* lits); - - }; - - struct card2bv_rewriter_cfg : public default_rewriter_cfg { - card2bv_rewriter m_r; - bool rewrite_patterns() const { return false; } - bool flat_assoc(func_decl * f) const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = nullptr; - return m_r.mk_app_core(f, num, args, result); - } - card2bv_rewriter_cfg(ast_manager & m):m_r(m) {} - }; - - class card_pb_rewriter : public rewriter_tpl { - card2bv_rewriter_cfg m_cfg; - pb_util pb; - expr_ref_vector m_lemmas; - public: - card_pb_rewriter(ast_manager & m): - rewriter_tpl(m, false, m_cfg), - m_cfg(m), - pb(m), - m_lemmas(m) {} - - void rewrite(expr* e, expr_ref& result); - - expr_ref_vector& lemmas() { return m_lemmas; } - }; +#include "util/params.h" +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/card2bv.h" + +class card2bv_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(card2bv, m, p, s); + } }; -tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); +inline tactic* mk_card2bv_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(card2bv_tactic_factory), "card2bv"); +} + /* ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)") */ From 5af6e1a046c803fbae472e84dc1f8e5c341b827d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 11:38:41 +0700 Subject: [PATCH 127/597] make max_bv_sharing a simplifier --- src/ast/simplifiers/CMakeLists.txt | 1 + .../simplifiers/max_bv_sharing.cpp} | 98 ++++++------------- src/ast/simplifiers/max_bv_sharing.h | 25 +++++ src/tactic/bv/CMakeLists.txt | 1 - src/tactic/bv/max_bv_sharing_tactic.h | 17 +++- 5 files changed, 68 insertions(+), 74 deletions(-) rename src/{tactic/bv/max_bv_sharing_tactic.cpp => ast/simplifiers/max_bv_sharing.cpp} (79%) create mode 100644 src/ast/simplifiers/max_bv_sharing.h diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index d10bf87e7f4..d5a8fd2a436 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -6,6 +6,7 @@ z3_add_component(simplifiers eliminate_predicates.cpp euf_completion.cpp extract_eqs.cpp + max_bv_sharing.cpp model_reconstruction_trail.cpp propagate_values.cpp solve_context_eqs.cpp diff --git a/src/tactic/bv/max_bv_sharing_tactic.cpp b/src/ast/simplifiers/max_bv_sharing.cpp similarity index 79% rename from src/tactic/bv/max_bv_sharing_tactic.cpp rename to src/ast/simplifiers/max_bv_sharing.cpp index 2bc99806e82..b28b5ebdbb2 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -3,7 +3,7 @@ Copyright (c) 2011 Microsoft Corporation Module Name: - max_bv_sharing_tactic.cpp + max_bv_sharing.cpp Abstract: @@ -12,7 +12,7 @@ Module Name: This rewriter is particularly useful for reducing the number of Adders and Multipliers before "bit-blasting". -Author: +Author Leonardo de Moura (leonardo) 2011-12-29. @@ -23,9 +23,10 @@ Revision History: #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "util/obj_pair_hashtable.h" +#include "ast/simplifiers/dependent_expr_state.h" #include "ast/ast_lt.h" -class max_bv_sharing_tactic : public tactic { +class max_bv_sharing : public dependent_expr_simplifier { struct rw_cfg : public default_rewriter_cfg { typedef std::pair expr_pair; @@ -224,64 +225,22 @@ class max_bv_sharing_tactic : public tactic { } }; - struct imp { - rw m_rw; - unsigned m_num_steps; + rw m_rw; + unsigned m_num_steps = 0; - imp(ast_manager & m, params_ref const & p): - m_rw(m, p) { - } - - ast_manager & m() const { return m_rw.m(); } - - void operator()(goal_ref const & g, - goal_ref_buffer & result) { - tactic_report report("max-bv-sharing", *g); - bool produce_proofs = g->proofs_enabled(); - - expr_ref new_curr(m()); - proof_ref new_pr(m()); - unsigned size = g->size(); - for (unsigned idx = 0; idx < size; idx++) { - if (g->inconsistent()) - break; - expr * curr = g->form(idx); - m_rw(curr, new_curr, new_pr); - m_num_steps += m_rw.get_num_steps(); - - if (produce_proofs) { - proof * pr = g->pr(idx); - new_pr = m().mk_modus_ponens(pr, new_pr); - } - g->update(idx, new_curr, new_pr, g->dep(idx)); - } - m_rw.cfg().cleanup(); - g->inc_depth(); - result.push_back(g.get()); - } - }; - - imp * m_imp; - params_ref m_params; -public: - max_bv_sharing_tactic(ast_manager & m, params_ref const & p): - m_params(p) { - m_imp = alloc(imp, m, p); - } - tactic * translate(ast_manager & m) override { - return alloc(max_bv_sharing_tactic, m, m_params); - } - - ~max_bv_sharing_tactic() override { - dealloc(m_imp); - } + params_ref m_params; - char const* name() const override { return "max_bv_sharing"; } +public: + max_bv_sharing(ast_manager & m, params_ref const & p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_params(p), + m_rw(m, p) { + } void updt_params(params_ref const & p) override { m_params.append(p); - m_imp->m_rw.cfg().updt_params(m_params); + m_rw.cfg().updt_params(m_params); } void collect_param_descrs(param_descrs & r) override { @@ -290,21 +249,22 @@ class max_bv_sharing_tactic : public tactic { r.insert("max_args", CPK_UINT, "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } - - void operator()(goal_ref const & in, - goal_ref_buffer & result) override { - (*m_imp)(in, result); - } - - void cleanup() override { - ast_manager & m = m_imp->m(); - params_ref p = std::move(m_params); - m_imp->~imp(); - new (m_imp) imp(m, p); - } + + void reduce() override { + expr_ref new_curr(m); + proof_ref new_pr(m); + for (unsigned idx = 0; idx < m_fmls.size() && !m_fmls.inconsistent(); idx++) { + auto [curr, d] = m_fmls[idx](); + m_rw(curr, new_curr, new_pr); + // Proof reconstruction: new_pr = m.mk_modus_ponens(old_pr, new_pr); + m_num_steps += m_rw.get_num_steps(); + m_fmls.update(idx, dependent_expr(m, new_curr, d)); + } + m_rw.cfg().cleanup(); + } }; -tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(max_bv_sharing_tactic, m, p)); +dependent_expr_simplifier * mk_max_bv_sharing(ast_manager & m, params_ref const & p, dependent_expr_state& fmls) { + return alloc(max_bv_sharing, m, p, fmls); } diff --git a/src/ast/simplifiers/max_bv_sharing.h b/src/ast/simplifiers/max_bv_sharing.h new file mode 100644 index 00000000000..bfc8f447259 --- /dev/null +++ b/src/ast/simplifiers/max_bv_sharing.h @@ -0,0 +1,25 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + max_bv_sharing.h + +Abstract: + + Rewriter for "maximing" the number of shared terms. + The idea is to rewrite AC terms to maximize sharing. + This rewriter is particularly useful for reducing + the number of Adders and Multipliers before "bit-blasting". + +Author: + + Leonardo de Moura (leonardo) 2011-12-29. + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" + +dependent_expr_simplifier * mk_max_bv_sharing(ast_manager & m, params_ref const & p, dependent_expr_state& fmls); diff --git a/src/tactic/bv/CMakeLists.txt b/src/tactic/bv/CMakeLists.txt index 6535712653c..50dd941e0e9 100644 --- a/src/tactic/bv/CMakeLists.txt +++ b/src/tactic/bv/CMakeLists.txt @@ -11,7 +11,6 @@ z3_add_component(bv_tactics bv_slice_tactic.cpp dt2bv_tactic.cpp elim_small_bv_tactic.cpp - max_bv_sharing_tactic.cpp COMPONENT_DEPENDENCIES bit_blaster core_tactics diff --git a/src/tactic/bv/max_bv_sharing_tactic.h b/src/tactic/bv/max_bv_sharing_tactic.h index 00de4125638..ebd050aa514 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.h +++ b/src/tactic/bv/max_bv_sharing_tactic.h @@ -21,11 +21,20 @@ Revision History: --*/ #pragma once -#include "util/params.h" -class ast_manager; -class tactic; +#include "ast/simplifiers/max_bv_sharing.h" +#include "tactic/dependent_expr_state_tactic.h" + +class max_bv_sharing_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 mk_max_bv_sharing(m, p, s); + } +}; + +inline tactic* mk_max_bv_sharing_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(max_bv_sharing_tactic_factory), "max-bv-sharing"); +} -tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("max-bv-sharing", "use heuristics to maximize the sharing of bit-vector expressions such as adders and multipliers.", "mk_max_bv_sharing_tactic(m, p)") */ From 8184e7fe0a24cb06f2f56e9b3702c12764b4b12e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 11:42:16 +0700 Subject: [PATCH 128/597] keep track of qhead --- src/ast/simplifiers/card2bv.cpp | 4 +++- src/ast/simplifiers/max_bv_sharing.cpp | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index d9c387c5ae2..145ea71f565 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -29,7 +29,7 @@ void card2bv::reduce() { expr_ref new_f1(m), new_f2(m); proof_ref new_pr(m); - for (unsigned idx = 0; !m_fmls.inconsistent() && idx < m_fmls.size(); idx++) { + for (unsigned idx = m_qhead; !m_fmls.inconsistent() && idx < m_fmls.size(); idx++) { auto [f, d] = m_fmls[idx](); rw1(f, new_f1); rw2(false, new_f1, new_f2, new_pr); @@ -48,6 +48,8 @@ void card2bv::reduce() { func_decl_ref_vector const& fns = rw2.fresh_constants(); for (func_decl* f : fns) m_fmls.model_trail().hide(f); + + advance_qhead(m_fmls.size()); } void card2bv::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index b28b5ebdbb2..ca11f9a1b19 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -253,7 +253,7 @@ class max_bv_sharing : public dependent_expr_simplifier { void reduce() override { expr_ref new_curr(m); proof_ref new_pr(m); - for (unsigned idx = 0; idx < m_fmls.size() && !m_fmls.inconsistent(); idx++) { + for (unsigned idx = m_qhead; idx < m_fmls.size() && !m_fmls.inconsistent(); idx++) { auto [curr, d] = m_fmls[idx](); m_rw(curr, new_curr, new_pr); // Proof reconstruction: new_pr = m.mk_modus_ponens(old_pr, new_pr); @@ -261,6 +261,7 @@ class max_bv_sharing : public dependent_expr_simplifier { m_fmls.update(idx, dependent_expr(m, new_curr, d)); } m_rw.cfg().cleanup(); + advance_qhead(m_fmls.size()); } }; From e95b0bd2cd7bdb15fdc8b6e52437fbe5c811d9e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 11:47:38 +0700 Subject: [PATCH 129/597] remove include of tactical --- src/ast/simplifiers/max_bv_sharing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index ca11f9a1b19..86021fea6e2 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -19,7 +19,7 @@ Author Revision History: --*/ -#include "tactic/tactical.h" + #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "util/obj_pair_hashtable.h" From f0570fbc0ef7e6179f5a5ccff1701ebd11e88871 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 11:48:44 +0700 Subject: [PATCH 130/597] remove tactic exception dependency --- src/ast/simplifiers/max_bv_sharing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index 86021fea6e2..2abacb7f7f7 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -62,7 +62,7 @@ class max_bv_sharing : public dependent_expr_simplifier { bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > m_max_memory) - throw tactic_exception(TACTIC_MAX_MEMORY_MSG); + throw rewriter_exception(Z3_MAX_MEMORY_MSG); return num_steps > m_max_steps; } From a152f9cfd6470674fba04cffbb84e01348009fa6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 13:37:16 +0700 Subject: [PATCH 131/597] port bit-blaster to simplifiers inc_sat_solver uses bit-blaster, card2bv and max_bv_sharing. By turning these into simplifiers it will be possible to remove dependencies on tactics and goals in inc_sat_simplifier and instead use a modular and general incremental pre-processing infrastructure. --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/bit_blaster.cpp | 80 +++++++++++++++++++++++++++++ src/ast/simplifiers/bit_blaster.h | 51 ++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/ast/simplifiers/bit_blaster.cpp create mode 100644 src/ast/simplifiers/bit_blaster.h diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index d5a8fd2a436..042c29a0e3f 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -1,5 +1,6 @@ z3_add_component(simplifiers SOURCES + bit_blaster.cpp bv_slice.cpp card2bv.cpp elim_unconstrained.cpp diff --git a/src/ast/simplifiers/bit_blaster.cpp b/src/ast/simplifiers/bit_blaster.cpp new file mode 100644 index 00000000000..ceb3c56a6f2 --- /dev/null +++ b/src/ast/simplifiers/bit_blaster.cpp @@ -0,0 +1,80 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + bit_blaster.cpp + +Abstract: + + Apply bit-blasting + +Author: + + Leonardo (leonardo) 2011-10-25 + +--*/ + +#include "ast/simplifiers/bit_blaster.h" + + +void bit_blaster::updt_params(params_ref const & p) { + m_params.append(p); + m_rewriter.updt_params(m_params); +} + +void bit_blaster::collect_param_descrs(param_descrs & r) { + insert_max_memory(r); + insert_max_steps(r); + r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders)."); + r.insert("blast_add", CPK_BOOL, "(default: true) bit-blast adders."); + r.insert("blast_quant", CPK_BOOL, "(default: false) bit-blast quantified variables."); + r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms."); +} + +void bit_blaster::reduce() { + m_rewriter.start_rewrite(); + expr_ref new_curr(m); + proof_ref new_pr(m); + bool change = false; + for (unsigned idx = m_qhead; idx < m_fmls.size(); idx++) { + if (m_fmls.inconsistent()) + break; + auto [curr, d] = m_fmls[idx](); + m_rewriter(curr, new_curr, new_pr); + m_num_steps += m_rewriter.get_num_steps(); + if (curr != new_curr) { + change = true; + TRACE("bit_blaster", tout << mk_pp(curr, m) << " -> " << new_curr << "\n";); + m_fmls.update(idx, dependent_expr(m, new_curr, d)); + } + } + + if (change) { + obj_map const2bits; + ptr_vector newbits; + m_rewriter.end_rewrite(const2bits, newbits); + for (auto* f : newbits) + m_fmls.model_trail().hide(f); + for (auto const& [f, v] : const2bits) + m_fmls.model_trail().push(f, v, nullptr, {}); + } + m_rewriter.cleanup(); + + advance_qhead(m_fmls.size()); +} + + +void bit_blaster::collect_statistics(statistics& st) const { + st.update("bit-blaster-num-steps", m_num_steps); +} + +void bit_blaster::push() { + m_rewriter.push(); + dependent_expr_simplifier::push(); +} + +void bit_blaster::pop(unsigned n) { + dependent_expr_simplifier::pop(n); + m_rewriter.pop(n); +} diff --git a/src/ast/simplifiers/bit_blaster.h b/src/ast/simplifiers/bit_blaster.h new file mode 100644 index 00000000000..70446918b4e --- /dev/null +++ b/src/ast/simplifiers/bit_blaster.h @@ -0,0 +1,51 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + bit_blaster.h + +Abstract: + + Apply bit-blasting + +Author: + + Leonardo (leonardo) 2011-10-25 + +--*/ +#include "ast/rewriter/bit_blaster/bit_blaster_rewriter.h" +#include "ast/ast_pp.h" +#include "model/model_pp.h" +#include "ast/rewriter/rewriter_types.h" +#include "ast/simplifiers/dependent_expr_state.h" + + +class bit_blaster : public dependent_expr_simplifier { + + bit_blaster_rewriter m_rewriter; + unsigned m_num_steps = 0; + params_ref m_params; + +public: + bit_blaster(ast_manager & m, params_ref const & p, dependent_expr_state& s): + dependent_expr_simplifier(m, s), + m_rewriter(m, p) { + updt_params(p); + } + + void updt_params(params_ref const & p) override; + void collect_param_descrs(param_descrs & r) override; + void reduce() override; + void collect_statistics(statistics& st) const override; + void push() override; + void pop(unsigned n) override; + + /* + * Expose the bit-blaster rewriter so that assumptions and implied bit-vectors can be reconstructed + * after bit-blasting. + */ + bit_blaster_rewriter& rewriter() { return m_rewriter; } + +}; + From 4e9f21c2a1cf3aa53efe8d23361ce86a2aa7573c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 15:16:14 +0700 Subject: [PATCH 132/597] add rewriter and seq simplifiers --- scripts/mk_project.py | 22 +++---- src/CMakeLists.txt | 2 +- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/card2bv.cpp | 2 +- src/ast/simplifiers/rewriter_simplifier.h | 53 +++++++++++++++++ src/ast/simplifiers/seq_simplifier.h | 72 +++++++++++++++++++++++ 6 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 src/ast/simplifiers/rewriter_simplifier.h create mode 100644 src/ast/simplifiers/seq_simplifier.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index e9f54be0567..497aa350fb8 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -20,26 +20,27 @@ def init_project_def(): add_lib('simplex', ['util'], 'math/simplex') add_lib('hilbert', ['util'], 'math/hilbert') add_lib('automata', ['util'], 'math/automata') + add_lib('params', ['util']) + add_lib('smt_params', ['params'], 'smt/params') add_lib('realclosure', ['interval'], 'math/realclosure') add_lib('subpaving', ['interval'], 'math/subpaving') add_lib('ast', ['util', 'polynomial']) - add_lib('euf', ['ast', 'util'], 'ast/euf') - add_lib('params', ['util']) - add_lib('smt_params', ['params'], 'smt/params') + add_lib('parser_util', ['ast'], 'parsers/util') + add_lib('euf', ['ast'], 'ast/euf') add_lib('grobner', ['ast', 'dd', 'simplex'], 'math/grobner') add_lib('sat', ['params', 'util', 'dd', 'grobner']) 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('bit_blaster', ['rewriter'], 'ast/rewriter/bit_blaster') + add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') + add_lib('substitution', ['rewriter'], 'ast/substitution') + add_lib('proofs', ['rewriter'], 'ast/proofs') add_lib('macros', ['rewriter'], 'ast/macros') - add_lib('model', ['rewriter', 'macros']) + add_lib('model', ['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') - add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') + add_lib('simplifiers', ['euf', 'rewriter', 'bit_blaster', 'converters'], 'ast/simplifiers') + add_lib('tactic', ['simplifiers']) add_lib('solver', ['params', 'model', 'tactic', 'proofs']) add_lib('cmd_context', ['solver', 'rewriter', 'params']) add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') @@ -47,7 +48,6 @@ def init_project_def(): add_lib('aig_tactic', ['tactic'], 'tactic/aig') add_lib('ackermannization', ['model', 'rewriter', 'ast', 'solver', 'tactic'], 'ackermannization') add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa') - add_lib('bit_blaster', ['rewriter', 'params'], 'ast/rewriter/bit_blaster') add_lib('core_tactics', ['tactic', 'macros', 'normal_forms', 'rewriter', 'pattern'], 'tactic/core') add_lib('arith_tactics', ['core_tactics', 'sat'], 'tactic/arith') add_lib('mbp', ['model', 'simplex'], 'qe/mbp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fd4fa04b53a..dadd70bba8e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,7 @@ add_subdirectory(math/subpaving) add_subdirectory(ast) add_subdirectory(params) add_subdirectory(ast/rewriter) +add_subdirectory(ast/rewriter/bit_blaster) add_subdirectory(ast/normal_forms) add_subdirectory(ast/macros) add_subdirectory(model) @@ -71,7 +72,6 @@ add_subdirectory(qe/mbp) add_subdirectory(qe/lite) add_subdirectory(solver/assertions) add_subdirectory(ast/pattern) -add_subdirectory(ast/rewriter/bit_blaster) add_subdirectory(math/lp) add_subdirectory(sat/smt) add_subdirectory(sat/tactic) diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 042c29a0e3f..c488a626995 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -15,4 +15,5 @@ z3_add_component(simplifiers COMPONENT_DEPENDENCIES euf rewriter + bit_blaster ) diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index 145ea71f565..2da9b6e44ed 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -49,7 +49,7 @@ void card2bv::reduce() { for (func_decl* f : fns) m_fmls.model_trail().hide(f); - advance_qhead(m_fmls.size()); + advance_qhead(m_fmls.size()); } void card2bv::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h new file mode 100644 index 00000000000..938e30b83c8 --- /dev/null +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -0,0 +1,53 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + rewriter_simplifier.h + +Abstract: + + rewriter simplifier + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" + + +class rewriter_simplifier : public dependent_expr_simplifier { + + unsigned m_num_steps = 0; + params_ref m_params; + +public: + rewriter_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls) { + updt_params(p); + } + + void reduce() override { + m_num_steps = 0; + expr_ref new_curr(m); + proof_ref new_pr(m); + for (unsigned idx = m_qhead; idx < m_fmls.size(); idx++) { + if (m_fmls.inconsistent()) + break; + auto [f, d] = m_fmls[i](); + m_rewriter(f, new_curr, new_pr); + m_num_steps += m_rewriter.get_num_steps(); + m_fmls.update(idx, dependent_expr(m, new_curr, d)); + } + advance_qhead(m_fmls.size()); + } + void collect_statistics(statistics& st) const override { st.update("simplifier", m_num_steps); } + void reset_statistics() override { m_stats.reset(); } + void updt_params(params_ref const& p) override { m_params.append(p); m_rewriter.updt_params(m_params); } + void collect_param_descrs(param_descrs& r) override { th_rewriter::get_param_descrs(r); } +}; diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h new file mode 100644 index 00000000000..5a3a7da6082 --- /dev/null +++ b/src/ast/simplifiers/seq_simplifier.h @@ -0,0 +1,72 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + seq_simplifier.h + +Abstract: + + create a simplifier from a sequence of simplifiers + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" + + +class seq_simplifier : public dependent_expr_simplifier { + scoped_ptr_vector m_simplifiers; + +public: + seq_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls) { + } + + void add_simplifier(dependent_expr_simplifier* s) { + m_simplifiers.push_back(s); + } + + void reduce() override { + for (auto* s : m_simplifiers) { + if (m_fmls.inconsistent()) + break; + s->reduce(); + } + } + + void collect_statistics(statistics& st) const override { + for (auto* s : m_simplifiers) + s->collect_statistics(st); + } + + void reset_statistics() override { + for (auto* s : m_simplifiers) + s->reset_statistics(st); + } + + void updt_params(params_ref const& p) override { + for (auto* s : m_simplifiers) + s->updt_params(p); + } + + void collect_param_descrs(param_descrs& r) override { + for (auto* s : m_simplifiers) + s->collect_param_descrs(r); + } + + void push() override { + for (auto* s : m_simplifiers) + s->push(); + } + + void pop(unsigned n) override { + for (auto* s : m_simplifiers) + s->pop(n); + } +}; From 6454014119cd03f64754406e4f917ed1ddac4e92 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Nov 2022 15:28:38 +0700 Subject: [PATCH 133/597] enable incrementality for model reconstruction --- .../model_reconstruction_trail.cpp | 41 +++++++++++-------- .../simplifiers/model_reconstruction_trail.h | 6 +++ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 8ccfbdf6e09..cfc65ce675b 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -18,9 +18,12 @@ Module Name: #include "ast/converters/generic_model_converter.h" +// accumulate a set of dependent exprs, updating m_trail to exclude loose +// substitutions that use variables from the dependent expressions. +// TODO: add filters to skip sections of the trail that do not touch the current free variables. + 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; scoped_ptr rp = mk_default_expr_replacer(m, false); add_vars(d, free_vars); @@ -47,8 +50,7 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorm_active)); t->m_active = false; continue; - } - + } if (t->is_def()) { macro_replacer mrp(m); @@ -72,7 +74,6 @@ 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 @@ -89,25 +90,29 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vector 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"); - for (unsigned i = 0; i < m_trail.size(); ++i) { + unsigned i = 0; + append(*mc, i); + return model_converter_ref(mc.get()); +} + +/** +* Append model conversions starting at index i +*/ +void model_reconstruction_trail::append(generic_model_converter& mc, unsigned& i) { + for (; i < m_trail.size(); ++i) { auto* t = m_trail[i]; if (!t->m_active) continue; - else if (t->is_hide()) - mc->hide(t->m_decl); - else if (t->is_def()) - mc->add(t->m_decl, t->m_def); + else if (t->is_hide()) + mc.hide(t->m_decl); + else if (t->is_def()) + mc.add(t->m_decl, t->m_def); else { for (auto const& [v, def] : t->m_subst->sub()) - mc->add(v, def); - } + mc.add(v, 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 906aaa73837..6aa91e55050 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -29,6 +29,7 @@ Module Name: #include "ast/rewriter/expr_replacer.h" #include "ast/simplifiers/dependent_expr.h" #include "ast/converters/model_converter.h" +#include "ast/converters/generic_model_converter.h" class model_reconstruction_trail { @@ -125,5 +126,10 @@ class model_reconstruction_trail { * retrieve the current model converter corresponding to chaining substitutions from the trail. */ model_converter_ref get_model_converter(); + + /** + * Append new updates to model converter, update the current index into the trail in the process. + */ + void append(generic_model_converter& mc, unsigned& index); }; From 85f9c7eefaa721ea9eeb6484f310681db257967b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Nov 2022 11:45:56 +0700 Subject: [PATCH 134/597] replace restore_size_trail by more generic restore_vector other updates: - change signature of advance_qhead to simplify call sites - have model reconstruction replay work on a tail of dependent_expr state, while adding formulas to the tail. --- .../converters/generic_model_converter.cpp | 1 - src/ast/converters/generic_model_converter.h | 3 +- src/ast/converters/model_converter.h | 4 +++ src/ast/simplifiers/bit_blaster.cpp | 2 +- src/ast/simplifiers/bv_slice.cpp | 2 +- src/ast/simplifiers/card2bv.cpp | 2 +- src/ast/simplifiers/dependent_expr_state.h | 3 +- src/ast/simplifiers/elim_unconstrained.cpp | 2 +- src/ast/simplifiers/euf_completion.cpp | 2 +- src/ast/simplifiers/max_bv_sharing.cpp | 2 +- .../model_reconstruction_trail.cpp | 33 +++++++++++-------- .../simplifiers/model_reconstruction_trail.h | 17 +++++++--- src/ast/simplifiers/propagate_values.cpp | 5 +-- src/ast/simplifiers/propagate_values.h | 2 +- src/ast/simplifiers/seq_simplifier.h | 1 + src/ast/simplifiers/solve_eqs.cpp | 2 +- src/sat/sat_solver.h | 1 + src/sat/smt/arith_solver.cpp | 4 +-- src/sat/smt/bv_solver.cpp | 2 +- src/sat/smt/euf_proof.cpp | 16 ++++----- src/sat/smt/euf_solver.cpp | 2 +- src/smt/theory_arith_aux.h | 4 +-- src/smt/theory_lra.cpp | 2 +- src/tactic/core/propagate_values2_tactic.h | 2 +- src/util/trail.h | 23 ++++++------- 25 files changed, 80 insertions(+), 59 deletions(-) diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index f805e169b45..50c3b071a79 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -36,7 +36,6 @@ generic_model_converter::~generic_model_converter() { void generic_model_converter::add(func_decl * d, expr* e) { VERIFY(e); VERIFY(d->get_range() == e->get_sort()); - m_first_idx.insert_if_not_there(d, m_entries.size()); m_entries.push_back(entry(d, e, m, ADD)); } diff --git a/src/ast/converters/generic_model_converter.h b/src/ast/converters/generic_model_converter.h index 85e8d03904d..0706b181f1e 100644 --- a/src/ast/converters/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -35,7 +35,6 @@ class generic_model_converter : public model_converter { ast_manager& m; std::string m_orig; vector m_entries; - obj_map m_first_idx; expr_ref simplify_def(entry const& e); @@ -71,6 +70,8 @@ class generic_model_converter : public model_converter { void get_units(obj_map& units) override; vector const& entries() const { return m_entries; } + + void shrink(unsigned j) { m_entries.shrink(j); } }; typedef ref generic_model_converter_ref; diff --git a/src/ast/converters/model_converter.h b/src/ast/converters/model_converter.h index 335e0d276ec..164becc09d1 100644 --- a/src/ast/converters/model_converter.h +++ b/src/ast/converters/model_converter.h @@ -101,6 +101,10 @@ typedef sref_buffer model_converter_ref_buffer; model_converter * concat(model_converter * mc1, model_converter * mc2); +inline model_converter * concat(model_converter * mc1, model_converter * mc2, model_converter* mc3) { + return concat(mc1, concat(mc2, mc3)); +} + model_converter * model2model_converter(model * m); model_converter * model_and_labels2model_converter(model * m, labels_vec const &r); diff --git a/src/ast/simplifiers/bit_blaster.cpp b/src/ast/simplifiers/bit_blaster.cpp index ceb3c56a6f2..218765d283b 100644 --- a/src/ast/simplifiers/bit_blaster.cpp +++ b/src/ast/simplifiers/bit_blaster.cpp @@ -61,7 +61,7 @@ void bit_blaster::reduce() { } m_rewriter.cleanup(); - advance_qhead(m_fmls.size()); + advance_qhead(); } diff --git a/src/ast/simplifiers/bv_slice.cpp b/src/ast/simplifiers/bv_slice.cpp index 75e0a890c8b..995231b3475 100644 --- a/src/ast/simplifiers/bv_slice.cpp +++ b/src/ast/simplifiers/bv_slice.cpp @@ -24,7 +24,7 @@ namespace bv { void slice::reduce() { process_eqs(); apply_subst(); - advance_qhead(m_fmls.size()); + advance_qhead(); } void slice::process_eqs() { diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index 2da9b6e44ed..44bc8e589e8 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -49,7 +49,7 @@ void card2bv::reduce() { for (func_decl* f : fns) m_fmls.model_trail().hide(f); - advance_qhead(m_fmls.size()); + advance_qhead(); } void card2bv::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index b94b422ad73..90b95ab7d06 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -69,7 +69,7 @@ class dependent_expr_simplifier { 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; } + void advance_qhead() { if (num_scopes() > 0) m_trail.push(value_trail(m_qhead)); m_qhead = m_fmls.size(); } public: dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {} virtual ~dependent_expr_simplifier() {} @@ -80,6 +80,7 @@ class dependent_expr_simplifier { virtual void reset_statistics() {} virtual void updt_params(params_ref const& p) {} virtual void collect_param_descrs(param_descrs& r) {} + unsigned qhead() const { return m_qhead; } }; /** diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 52dc95f0567..ac5cc339d65 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -302,5 +302,5 @@ void elim_unconstrained::reduce() { vector old_fmls; assert_normalized(old_fmls); update_model_trail(*mc, old_fmls); - advance_qhead(m_fmls.size()); + advance_qhead(); } diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index b768180cfdb..5056d818c2c 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -128,7 +128,7 @@ namespace euf { CTRACE("euf_completion", g != f, tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); } if (!m_has_new_eq) - advance_qhead(m_fmls.size()); + advance_qhead(); } bool completion::is_new_eq(expr* a, expr* b) { diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index 2abacb7f7f7..3003a47a020 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -261,7 +261,7 @@ class max_bv_sharing : public dependent_expr_simplifier { m_fmls.update(idx, dependent_expr(m, new_curr, d)); } m_rw.cfg().cleanup(); - advance_qhead(m_fmls.size()); + advance_qhead(); } }; diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index cfc65ce675b..19c7d93811e 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -15,6 +15,7 @@ Module Name: #include "ast/for_each_expr.h" #include "ast/rewriter/macro_replacer.h" #include "ast/simplifiers/model_reconstruction_trail.h" +#include "ast/simplifiers/dependent_expr_state.h" #include "ast/converters/generic_model_converter.h" @@ -22,13 +23,11 @@ Module Name: // substitutions that use variables from the dependent expressions. // TODO: add filters to skip sections of the trail that do not touch the current free variables. -void model_reconstruction_trail::replay(dependent_expr const& d, vector& added) { - +void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st) { ast_mark free_vars; scoped_ptr rp = mk_default_expr_replacer(m, false); - add_vars(d, free_vars); - - added.push_back(d); + for (unsigned i = qhead; i < st.size(); ++i) + add_vars(st[i], free_vars); for (auto& t : m_trail) { if (!t->m_active) @@ -44,9 +43,10 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectoris_loose()) { - added.append(t->m_removed); - for (auto r : t->m_removed) - add_vars(r, free_vars); + for (auto r : t->m_removed) { + add_vars(r, free_vars); + st.add(r); + } m_trail_stack.push(value_trail(t->m_active)); t->m_active = false; continue; @@ -64,12 +64,12 @@ void model_reconstruction_trail::replay(dependent_expr const& d, vectorm_def, t->m_dep); add_vars(de, free_vars); - for (auto& d : added) { - auto [f, dep1] = d(); + for (unsigned i = qhead; i < st.size(); ++i) { + auto [f, dep1] = st[i](); expr_ref g(m); expr_dependency_ref dep2(m); mrp(f, g, dep2); - d = dependent_expr(m, g, m.mk_join(dep1, dep2)); + st.update(i, dependent_expr(m, g, m.mk_join(dep1, dep2))); } continue; } @@ -77,11 +77,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(); + for (unsigned i = qhead; i < st.size(); ++i) { + auto [f, dep1] = st[i](); auto [g, dep2] = rp->replace_with_dep(f); - d = dependent_expr(m, g, m.mk_join(dep1, dep2)); + dependent_expr d(m, g, m.mk_join(dep1, dep2)); add_vars(d, free_vars); + st.update(i, d); } } } @@ -116,3 +117,7 @@ void model_reconstruction_trail::append(generic_model_converter& mc, unsigned& i } +void model_reconstruction_trail::append(generic_model_converter& mc) { + m_trail_stack.push(value_trail(m_trail_index)); + append(mc, m_trail_index); +} diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 6aa91e55050..5ad204bf7d4 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -31,6 +31,8 @@ Module Name: #include "ast/converters/model_converter.h" #include "ast/converters/generic_model_converter.h" +class dependent_expr_state; + class model_reconstruction_trail { struct entry { @@ -41,7 +43,6 @@ class model_reconstruction_trail { expr_dependency_ref m_dep; bool m_active = true; - entry(ast_manager& m, expr_substitution* s, vector const& rem) : m_subst(s), m_removed(rem), m_decl(m), m_def(m), m_dep(m) {} @@ -71,6 +72,7 @@ class model_reconstruction_trail { ast_manager& m; trail_stack& m_trail_stack; scoped_ptr_vector m_trail; + unsigned m_trail_index = 0; void add_vars(dependent_expr const& d, ast_mark& free_vars) { for (expr* t : subterms::all(expr_ref(d.fml(), d.get_manager()))) @@ -87,6 +89,10 @@ class model_reconstruction_trail { return any_of(added, [&](dependent_expr const& d) { return intersects(free_vars, d); }); } + /** + * Append new updates to model converter, update the current index into the trail in the process. + */ + void append(generic_model_converter& mc, unsigned& index); public: model_reconstruction_trail(ast_manager& m, trail_stack& tr): @@ -120,16 +126,17 @@ class model_reconstruction_trail { * register a new depedent expression, update the trail * by removing substitutions that are not equivalence preserving. */ - void replay(dependent_expr const& d, vector& added); - + void replay(unsigned qhead, dependent_expr_state& fmls); + /** * retrieve the current model converter corresponding to chaining substitutions from the trail. */ model_converter_ref get_model_converter(); + /** - * Append new updates to model converter, update the current index into the trail in the process. + * Append new updates to model converter, update m_trail_index in the process. */ - void append(generic_model_converter& mc, unsigned& index); + void append(generic_model_converter& mc); }; diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 6a179674ba6..6d6bc976f14 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -25,11 +25,12 @@ Module Name: #include "ast/shared_occs.h" #include "ast/simplifiers/propagate_values.h" -propagate_values::propagate_values(ast_manager& m, dependent_expr_state& fmls): +propagate_values::propagate_values(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls), m_rewriter(m) { m_rewriter.set_order_eq(true); m_rewriter.set_flat_and_or(false); + updt_params(p); } void propagate_values::reduce() { @@ -96,7 +97,7 @@ void propagate_values::reduce() { m_rewriter.set_substitution(nullptr); m_rewriter.reset(); - advance_qhead(m_fmls.size()); + advance_qhead(); } void propagate_values::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/propagate_values.h b/src/ast/simplifiers/propagate_values.h index 3219f979691..d263377b112 100644 --- a/src/ast/simplifiers/propagate_values.h +++ b/src/ast/simplifiers/propagate_values.h @@ -36,7 +36,7 @@ class propagate_values : public dependent_expr_simplifier { unsigned m_max_rounds = 4; public: - propagate_values(ast_manager& m, dependent_expr_state& fmls); + propagate_values(ast_manager& m, params_ref const& p, dependent_expr_state& fmls); void reduce() override; void collect_statistics(statistics& st) const override; void reset_statistics() override { m_stats.reset(); } diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h index 5a3a7da6082..4288a34753b 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/seq_simplifier.h @@ -38,6 +38,7 @@ class seq_simplifier : public dependent_expr_simplifier { break; s->reduce(); } + advance_qhead(); } void collect_statistics(statistics& st) const override { diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 5090e1b438c..1bea283ac9e 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -237,7 +237,7 @@ namespace euf { save_subst(old_fmls); } - advance_qhead(m_fmls.size()); + advance_qhead(); } void solve_eqs::save_subst(vector const& old_fmls) { diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 982a84307d8..227568f3d86 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -485,6 +485,7 @@ namespace sat { // ----------------------- public: lbool check(unsigned num_lits = 0, literal const* lits = nullptr); + lbool check(literal_vector const& lits) { return check(lits.size(), lits.data()); } // retrieve model if solver return sat model const & get_model() const { return m_model; } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 8be98edfb4d..a69f3604ea7 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -893,11 +893,11 @@ namespace arith { theory_var other = m_model_eqs.insert_if_not_there(v); TRACE("arith", tout << "insert: v" << v << " := " << get_value(v) << " found: v" << other << "\n";); if (!is_equal(other, v)) - m_assume_eq_candidates.push_back(std::make_pair(v, other)); + m_assume_eq_candidates.push_back({ v, other }); } if (m_assume_eq_candidates.size() > old_sz) - ctx.push(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); + ctx.push(restore_vector(m_assume_eq_candidates, old_sz)); return delayed_assume_eqs(); } diff --git a/src/sat/smt/bv_solver.cpp b/src/sat/smt/bv_solver.cpp index a5a35878e3b..a0bcea43b96 100644 --- a/src/sat/smt/bv_solver.cpp +++ b/src/sat/smt/bv_solver.cpp @@ -419,7 +419,7 @@ namespace bv { } ctx.push(value_trail(m_lit_tail)); - ctx.push(restore_size_trail(m_proof_literals)); + ctx.push(restore_vector(m_proof_literals)); sat::literal_vector lits; switch (c.m_kind) { diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 5fbd632cd34..ac9e8131145 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -84,7 +84,7 @@ namespace euf { return nullptr; push(value_trail(m_lit_tail)); push(value_trail(m_cc_tail)); - push(restore_size_trail(m_proof_literals)); + push(restore_vector(m_proof_literals)); if (conseq != sat::null_literal) m_proof_literals.push_back(~conseq); m_proof_literals.append(r); @@ -101,8 +101,8 @@ namespace euf { SASSERT(a->get_decl() == b->get_decl()); push(value_trail(m_lit_tail)); push(value_trail(m_cc_tail)); - push(restore_size_trail(m_proof_literals)); - push(restore_size_trail(m_explain_cc, m_explain_cc.size())); + push(restore_vector(m_proof_literals)); + push(restore_vector(m_explain_cc)); for (auto lit : ante) m_proof_literals.push_back(~lit); @@ -121,7 +121,7 @@ namespace euf { return nullptr; push(value_trail(m_lit_tail)); push(value_trail(m_cc_tail)); - push(restore_size_trail(m_proof_literals)); + push(restore_vector(m_proof_literals)); for (unsigned i = 0; i < 3; ++i) m_proof_literals.push_back(~clause[i]); @@ -171,7 +171,7 @@ namespace euf { if (!use_drat()) return nullptr; push(value_trail(m_lit_tail)); - push(restore_size_trail(m_proof_literals)); + push(restore_vector(m_proof_literals)); for (unsigned i = 0; i < nl; ++i) m_proof_literals.push_back(~lits[i]); @@ -190,7 +190,7 @@ namespace euf { if (!use_drat()) return nullptr; push(value_trail(m_lit_tail)); - push(restore_size_trail(m_proof_literals)); + push(restore_vector(m_proof_literals)); for (unsigned i = 0; i < nl; ++i) if (sat::null_literal != lits[i]) { @@ -203,11 +203,11 @@ namespace euf { } push(value_trail(m_eq_tail)); - push(restore_size_trail(m_proof_eqs)); + push(restore_vector(m_proof_eqs)); m_proof_eqs.append(ne, eqs); push(value_trail(m_deq_tail)); - push(restore_size_trail(m_proof_deqs)); + push(restore_vector(m_proof_deqs)); m_proof_deqs.append(nd, deqs); m_lit_head = m_lit_tail; diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 7473be61dc5..8656083392d 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -225,7 +225,7 @@ namespace euf { m_egraph.begin_explain(); m_explain.reset(); if (use_drat() && !probing) { - push(restore_size_trail(m_explain_cc, m_explain_cc.size())); + push(restore_vector(m_explain_cc)); } auto* ext = sat::constraint_base::to_extension(idx); th_proof_hint* hint = nullptr; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 593c32d831a..d5eca0bc47e 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -2223,12 +2223,12 @@ namespace smt { continue; } TRACE("func_interp_bug", tout << "adding to assume_eq queue #" << n->get_owner_id() << " #" << n2->get_owner_id() << "\n";); - m_assume_eq_candidates.push_back(std::make_pair(other, v)); + m_assume_eq_candidates.push_back({ other , v }); result = true; } if (result) - ctx.push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); + ctx.push_trail(restore_vector(m_assume_eq_candidates, old_sz)); return delayed_assume_eqs(); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 4075f39ddd2..1309724d2ef 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1515,7 +1515,7 @@ class theory_lra::imp { } if (num_candidates > 0) { - ctx().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); + ctx().push_trail(restore_vector(m_assume_eq_candidates, old_sz)); } return delayed_assume_eqs(); diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h index ab96461287a..58e263e8021 100644 --- a/src/tactic/core/propagate_values2_tactic.h +++ b/src/tactic/core/propagate_values2_tactic.h @@ -26,7 +26,7 @@ Module Name: class propagate_values2_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(propagate_values, m, s); + return alloc(propagate_values, m, p, s); } }; diff --git a/src/util/trail.h b/src/util/trail.h index 20a525cf7bd..1aa7e44418a 100644 --- a/src/util/trail.h +++ b/src/util/trail.h @@ -98,20 +98,21 @@ class set_ptr_trail : public trail { } }; -template -class restore_size_trail : public trail { - vector & m_vector; - unsigned m_old_size; +template +class restore_vector : public trail { + V& m_vector; + unsigned m_old_size; public: - restore_size_trail(vector & v, unsigned sz): + restore_vector(V& v): m_vector(v), - m_old_size(sz) { - } - restore_size_trail(vector & v): + m_old_size(v.size()) + {} + + restore_vector(V& v, unsigned sz): m_vector(v), - m_old_size(v.size()) { - } - + m_old_size(sz) + {} + void undo() override { m_vector.shrink(m_old_size); } From 500626e814f680a42d5cf6c18b1024faec581bd2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Nov 2022 12:13:00 +0700 Subject: [PATCH 135/597] add sat-smt-preprocess module self-contained pre-processing initialization --- src/ast/simplifiers/rewriter_simplifier.h | 14 +++-- src/ast/simplifiers/seq_simplifier.h | 2 +- src/sat/sat_solver/CMakeLists.txt | 1 + src/sat/sat_solver/sat_smt_preprocess.cpp | 69 +++++++++++++++++++++++ src/sat/sat_solver/sat_smt_preprocess.h | 25 ++++++++ 5 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 src/sat/sat_solver/sat_smt_preprocess.cpp create mode 100644 src/sat/sat_solver/sat_smt_preprocess.h diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h index 938e30b83c8..23ebb4f84b4 100644 --- a/src/ast/simplifiers/rewriter_simplifier.h +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -25,10 +25,12 @@ class rewriter_simplifier : public dependent_expr_simplifier { unsigned m_num_steps = 0; params_ref m_params; + th_rewriter m_rewriter; public: rewriter_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): - dependent_expr_simplifier(m, fmls) { + dependent_expr_simplifier(m, fmls), + m_rewriter(m) { updt_params(p); } @@ -39,15 +41,15 @@ class rewriter_simplifier : public dependent_expr_simplifier { for (unsigned idx = m_qhead; idx < m_fmls.size(); idx++) { if (m_fmls.inconsistent()) break; - auto [f, d] = m_fmls[i](); - m_rewriter(f, new_curr, new_pr); + auto d = m_fmls[idx]; + m_rewriter(d.fml(), new_curr, new_pr); m_num_steps += m_rewriter.get_num_steps(); - m_fmls.update(idx, dependent_expr(m, new_curr, d)); + m_fmls.update(idx, dependent_expr(m, new_curr, d.dep())); } - advance_qhead(m_fmls.size()); + advance_qhead(); } void collect_statistics(statistics& st) const override { st.update("simplifier", m_num_steps); } - void reset_statistics() override { m_stats.reset(); } + void reset_statistics() override { m_num_steps = 0; } void updt_params(params_ref const& p) override { m_params.append(p); m_rewriter.updt_params(m_params); } void collect_param_descrs(param_descrs& r) override { th_rewriter::get_param_descrs(r); } }; diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h index 4288a34753b..0d5483b71aa 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/seq_simplifier.h @@ -48,7 +48,7 @@ class seq_simplifier : public dependent_expr_simplifier { void reset_statistics() override { for (auto* s : m_simplifiers) - s->reset_statistics(st); + s->reset_statistics(); } void updt_params(params_ref const& p) override { diff --git a/src/sat/sat_solver/CMakeLists.txt b/src/sat/sat_solver/CMakeLists.txt index 45a67336772..924937b94b5 100644 --- a/src/sat/sat_solver/CMakeLists.txt +++ b/src/sat/sat_solver/CMakeLists.txt @@ -1,6 +1,7 @@ z3_add_component(sat_solver SOURCES inc_sat_solver.cpp + sat_smt_preprocess.cpp COMPONENT_DEPENDENCIES aig_tactic arith_tactics diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp new file mode 100644 index 00000000000..7efe0cadc39 --- /dev/null +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -0,0 +1,69 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + sat_smt_preprocess.cpp + +Abstract: + + SAT pre-process + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-28 + +--*/ + +#include "sat/sat_params.hpp" +#include "sat/sat_solver/sat_smt_preprocess.h" +#include "ast/simplifiers/bit_blaster.h" +#include "ast/simplifiers/max_bv_sharing.h" +#include "ast/simplifiers/card2bv.h" +#include "ast/simplifiers/propagate_values.h" +#include "ast/simplifiers/rewriter_simplifier.h" +#include "ast/simplifiers/solve_eqs.h" +#include "ast/simplifiers/eliminate_predicates.h" + +void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { + params_ref simp1_p = p; + simp1_p.set_bool("som", true); + simp1_p.set_bool("pull_cheap_ite", true); + simp1_p.set_bool("push_ite_bv", false); + simp1_p.set_bool("local_ctx", true); + simp1_p.set_uint("local_ctx_limit", 10000000); + simp1_p.set_bool("flat", true); // required by som + simp1_p.set_bool("hoist_mul", false); // required by som + simp1_p.set_bool("elim_and", true); + simp1_p.set_bool("blast_distinct", true); + simp1_p.set_bool("flat_and_or", false); + + params_ref simp2_p = p; + simp2_p.set_bool("flat", false); + simp2_p.set_bool("flat_and_or", false); + + sat_params sp(p); + if (sp.euf()) { + s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); + s.add_simplifier(alloc(propagate_values, m, p, st)); + // + // add: + // solve_eqs + // elim_predicates + // elim_uncnstr + // euf_completion? + // + // add: make it externally configurable + // + } + else { + s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); + s.add_simplifier(alloc(propagate_values, m, p, st)); + s.add_simplifier(alloc(card2bv, m, p, st)); + s.add_simplifier(alloc(rewriter_simplifier, m, simp1_p, st)); + s.add_simplifier(mk_max_bv_sharing(m, p, st)); + s.add_simplifier(alloc(bit_blaster, m, p, st)); + s.add_simplifier(alloc(rewriter_simplifier, m, simp2_p, st)); + } +} + diff --git a/src/sat/sat_solver/sat_smt_preprocess.h b/src/sat/sat_solver/sat_smt_preprocess.h new file mode 100644 index 00000000000..e7b40a8a34b --- /dev/null +++ b/src/sat/sat_solver/sat_smt_preprocess.h @@ -0,0 +1,25 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + sat_smt_preprocess.h + +Abstract: + + SAT pre-process initialization + It collects the functionality associated with + initializing pre-processing for the sat-smt solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-28 + +--*/ + +#pragma once + +#include "ast/simplifiers/seq_simplifier.h" + +void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st); + From 82d9e4a4fc09e401995865487de22c905a3dbb3d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Nov 2022 15:04:12 +0700 Subject: [PATCH 136/597] update goal2sat interface to use explicit initialization --- src/ast/simplifiers/dependent_expr.h | 10 ++++++++++ src/sat/sat_solver/inc_sat_solver.cpp | 6 ++++-- src/sat/sat_solver/sat_smt_preprocess.cpp | 5 +++-- src/sat/tactic/goal2sat.cpp | 13 +++++++++---- src/sat/tactic/goal2sat.h | 5 +++-- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h index f789bf332ff..4d16bc2bb67 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -18,6 +18,7 @@ Module Name: #pragma once #include "ast/ast.h" +#include "ast/ast_translation.h" class dependent_expr { ast_manager& m; @@ -32,6 +33,15 @@ class dependent_expr { m.inc_ref(fml); m.inc_ref(d); } + + dependent_expr(ast_translation& tr, dependent_expr const& src) : + m(tr.to()) { + m_fml = tr(src.fml()); + m.inc_ref(m_fml); + expr_dependency_translation dtr(tr); + m_dep = dtr(src.dep()); + m.inc_ref(m_dep); + } dependent_expr& operator=(dependent_expr const& other) { SASSERT(&m == &other.m); diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 6fb9364353a..41b8e609c9c 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -721,7 +721,8 @@ class inc_sat_solver : public solver { if (m_solver.inconsistent()) return l_false; m_pc.reset(); - m_goal2sat(m, sz, fmls, m_params, m_solver, m_map, m_dep2asm, is_incremental()); + m_goal2sat.init(m, m_params, m_solver, m_map, m_dep2asm, is_incremental()); + m_goal2sat(sz, fmls); if (!m_sat_mc) m_sat_mc = alloc(sat2goal::mc, m); m_sat_mc->flush_smc(m_solver, m_map); return check_uninterpreted(); @@ -798,7 +799,8 @@ class inc_sat_solver : public solver { fmls.append(sz, asms); for (unsigned i = 0; i < get_num_assumptions(); ++i) fmls.push_back(get_assumption(i)); - m_goal2sat.assumptions(m, fmls.size(), fmls.data(), m_params, m_solver, m_map, m_dep2asm, is_incremental()); + m_goal2sat.init(m, m_params, m_solver, m_map, m_dep2asm, is_incremental()); + m_goal2sat.assumptions(fmls.size(), fmls.data()); extract_assumptions(fmls.size(), fmls.data()); return l_true; } diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index 7efe0cadc39..72c7c52323c 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -15,8 +15,7 @@ Module Name: --*/ -#include "sat/sat_params.hpp" -#include "sat/sat_solver/sat_smt_preprocess.h" + #include "ast/simplifiers/bit_blaster.h" #include "ast/simplifiers/max_bv_sharing.h" #include "ast/simplifiers/card2bv.h" @@ -24,6 +23,8 @@ Module Name: #include "ast/simplifiers/rewriter_simplifier.h" #include "ast/simplifiers/solve_eqs.h" #include "ast/simplifiers/eliminate_predicates.h" +#include "sat/sat_params.hpp" +#include "sat/sat_solver/sat_smt_preprocess.h" void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { params_ref simp1_p = p; diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 97a766df91b..0f23527dd1a 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -1060,16 +1060,21 @@ void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver_core (*m_imp)(g); } -void goal2sat::operator()(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external) { - init(m, p, t, map, dep2asm, default_external); +void goal2sat::operator()(unsigned n, expr* const* fmls) { + SASSERT(m_imp); (*m_imp)(n, fmls); } -void goal2sat::assumptions(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external) { - init(m, p, t, map, dep2asm, default_external); +void goal2sat::assumptions(unsigned n, expr* const* fmls) { + SASSERT(m_imp); m_imp->assumptions(n, fmls); } +sat::literal goal2sat::internalize(expr* a) { + SASSERT(m_imp); + return m_imp->internalize(a); +} + void goal2sat::get_interpreted_funs(func_decl_ref_vector& funs) { if (m_imp) diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index 5f85d59cea4..d68467868ea 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -67,12 +67,13 @@ class goal2sat { */ void operator()(goal const & g, params_ref const & p, sat::solver_core & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false); - void operator()(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external = false); + void operator()(unsigned n, expr* const* fmls); void init(ast_manager& m, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external); + void assumptions(unsigned n, expr* const* fmls); - void assumptions(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external = false); + sat::literal internalize(expr* a); void get_interpreted_funs(func_decl_ref_vector& funs); From ac023935a326c8ef33383422ccf406ccdf571062 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Nov 2022 15:06:31 +0700 Subject: [PATCH 137/597] introduce sat-smt-solver in an iteration of inc-sat-solver introduce sat-smt-solver to allow incremental pre-processing. The aim is to allow incrementally handling formulas while at the same time retaining the main benefits of global in/pre-processing that change models. Previous incremental solving capabilities have been limited to use pre-processing that does not require model conversion. --- src/sat/sat_solver/CMakeLists.txt | 1 + src/sat/sat_solver/sat_smt_solver.cpp | 774 ++++++++++++++++++ src/sat/sat_solver/sat_smt_solver.h | 25 + src/tactic/portfolio/smt_strategic_solver.cpp | 1 + 4 files changed, 801 insertions(+) create mode 100644 src/sat/sat_solver/sat_smt_solver.cpp create mode 100644 src/sat/sat_solver/sat_smt_solver.h diff --git a/src/sat/sat_solver/CMakeLists.txt b/src/sat/sat_solver/CMakeLists.txt index 924937b94b5..725ca16f799 100644 --- a/src/sat/sat_solver/CMakeLists.txt +++ b/src/sat/sat_solver/CMakeLists.txt @@ -2,6 +2,7 @@ z3_add_component(sat_solver SOURCES inc_sat_solver.cpp sat_smt_preprocess.cpp + sat_smt_solver.cpp COMPONENT_DEPENDENCIES aig_tactic arith_tactics diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp new file mode 100644 index 00000000000..3ea0543ec49 --- /dev/null +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -0,0 +1,774 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sat_smt_solver.cpp + +Abstract: + + incremental solver based on SAT core. + It uses the ast/simplifiers to allow incremental pre-processing that + produce model converters. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-28 + +Notes: + + - proper handling of dependencies + pre-processing + - literals used in dependencies should not be eliminated by pre-processing routines + This has to be enforced. + - add translation for preprocess state. + - If the pre-processors are stateful, they need to be properly translated. + - add back get_consequences, maybe or just have them handled by inc_sat_solver + - could also port the layered solver used by smtfd and used by get_consequences to simplifiers + + - port various pre-processing to simplifiers + - qe-lite, fm-elimination, ite-lifting, other from asserted_formulas + +--*/ + + +#include "util/gparams.h" +#include "ast/ast_pp.h" +#include "ast/ast_translation.h" +#include "ast/ast_util.h" +#include "solver/solver.h" +#include "solver/parallel_params.hpp" +#include "solver/parallel_tactic.h" +#include "model/model_smt2_pp.h" +#include "model/model_evaluator.h" +#include "sat/sat_solver.h" +#include "sat/sat_solver/sat_smt_preprocess.h" +#include "sat/sat_params.hpp" +#include "sat/smt/euf_solver.h" +#include "sat/tactic/goal2sat.h" +#include "sat/tactic/sat2goal.h" +#include "sat/tactic/sat_tactic.h" +#include "sat/sat_simplifier_params.hpp" + +// incremental SAT solver. +class sat_smt_solver : public solver { + + struct dep_expr_state : public dependent_expr_state { + sat_smt_solver& s; + model_reconstruction_trail m_reconstruction_trail; + dep_expr_state(sat_smt_solver& s):s(s), m_reconstruction_trail(s.m, m_trail) {} + ~dep_expr_state() override {} + virtual unsigned size() const override { return s.m_fmls.size(); } + dependent_expr const& operator[](unsigned i) override { return s.m_fmls[i]; } + void update(unsigned i, dependent_expr const& j) override { s.m_fmls[i] = j; } + void add(dependent_expr const& j) override { s.m_fmls.push_back(j); } + bool inconsistent() override { return s.m_solver.inconsistent(); } + model_reconstruction_trail& model_trail() override { return m_reconstruction_trail; } + void append(generic_model_converter& mc) { model_trail().append(mc); } + void replay(unsigned qhead) { m_reconstruction_trail.replay(qhead, *this); } + }; + + struct dependency2assumptions { + ast_manager& m; + trail_stack& m_trail; + expr_ref_vector m_refs; + obj_map m_dep2orig; // map original dependency to uninterpeted literal + + u_map m_lit2dep; // map from literal assumption to original expression + obj_map m_dep2lit; // map uninterpreted literal to sat literal + sat::literal_vector m_literals; + uint_set m_seen; + + dependency2assumptions(ast_manager& m, trail_stack& t): + m(m), + m_trail(t), + m_refs(m) + {} + + void reset() { + m_seen.reset(); + m_literals.reset(); + m_dep2lit.reset(); + m_lit2dep.reset(); + } + + // inserted incrementally + void insert(expr* orig, expr* lit) { + m_trail.push(restore_vector(m_refs)); + m_trail.push(insert_obj_map(m_dep2orig, lit)); + m_refs.push_back(lit); + m_refs.push_back(orig); + m_dep2orig.insert(lit, orig); + } + + // inserted on every check-sat + void insert(expr* dep, sat::literal lit) { + if (m_seen.contains(lit.index())) + return; + m_seen.insert(lit.index()); + m_literals.push_back(lit); + m_dep2lit.insert(dep, lit); + m_lit2dep.insert(lit.index(), dep); + } + + expr* lit2orig(sat::literal lit) { + expr* e = m_lit2dep[lit.index()]; + m_dep2orig.find(e, e); + return e; + } + + void copy(ast_translation& tr, dependency2assumptions const& src) { + for (auto const& [k, v] : src.m_dep2orig) + m_dep2orig.insert(tr(k), tr(v)); + } + }; + + mutable sat::solver m_solver; + params_ref m_params; + vector m_fmls; + dep_expr_state m_preprocess_state; + seq_simplifier m_preprocess; + trail_stack& m_trail; + dependency2assumptions m_dep; + goal2sat m_goal2sat; + expr_ref_vector m_assumptions, m_core, m_ors, m_aux_fmls; + atom2bool_var m_map; + generic_model_converter_ref m_mc; + unsigned m_mc_size = 0; + mutable model_converter_ref m_cached_mc; + mutable ref m_sat_mc; + std::string m_unknown = "no reason given"; + // access formulas after they have been pre-processed and handled by the sat solver. + // this allows to access the internal state of the SAT solver and carry on partial results. + bool m_internalized_converted = false; // have internalized formulas been converted back + expr_ref_vector m_internalized_fmls; // formulas in internalized format + + bool is_internalized() const { return m_preprocess.qhead() == m_fmls.size(); } + +public: + sat_smt_solver(ast_manager& m, params_ref const& p): + solver(m), + m_preprocess_state(*this), + m_preprocess(m, p, m_preprocess_state), + m_trail(m_preprocess_state.m_trail), + m_dep(m, m_trail), + m_solver(p, m.limit()), + m_assumptions(m), + m_core(m), + m_ors(m), + m_aux_fmls(m), + m_map(m), + m_internalized_fmls(m) { + updt_params(p); + init_preprocess(); + m_solver.set_incremental(true); + m_mc = alloc(generic_model_converter, m, "sat-smt-solver"); + } + + solver* translate(ast_manager& dst_m, params_ref const& p) override { + if (m_trail.get_num_scopes() > 0) + throw default_exception("Cannot translate sat solver at non-base level"); + + ast_translation tr(m, dst_m); + m_solver.pop_to_base_level(); + sat_smt_solver* result = alloc(sat_smt_solver, dst_m, p); + auto* ext = get_euf(); + if (ext) { + auto& si = result->m_goal2sat.si(dst_m, m_params, result->m_solver, result->m_map, result->m_dep.m_dep2lit, true); + euf::solver::scoped_set_translate st(*ext, dst_m, si); + result->m_solver.copy(m_solver); + } + else { + result->m_solver.copy(m_solver); + } + // TODO: copy preprocess state + for (auto const& [k, v] : m_dep.m_dep2orig) result->m_dep.insert(tr(v), tr(k)); + for (dependent_expr const& f : m_fmls) result->m_fmls.push_back(dependent_expr(tr, f)); + for (expr* f : m_assumptions) result->m_assumptions.push_back(tr(f)); + for (auto & kv : m_map) result->m_map.insert(tr(kv.m_key), kv.m_value); + for (expr* f : m_internalized_fmls) result->m_internalized_fmls.push_back(tr(f)); + if (m_mc) result->m_mc = dynamic_cast(m_mc->translate(tr)); + result->m_dep.copy(tr, m_dep); + result->m_mc_size = m_mc_size; + if (m_sat_mc) result->m_sat_mc = dynamic_cast(m_sat_mc->translate(tr)); + result->m_internalized_converted = m_internalized_converted; + return result; + } + + void set_progress_callback(progress_callback * callback) override {} + + void init_check_sat() { + m_solver.pop_to_base_level(); + m_core.reset(); + m_dep.reset(); + m_cached_mc = nullptr; + init_reason_unknown(); + m_internalized_converted = false; + } + + lbool check_sat_core(unsigned sz, expr * const * _assumptions) override { + init_check_sat(); + + if (m_solver.inconsistent()) + return l_false; + + expr_ref_vector assumptions(m); + for (unsigned i = 0; i < sz; ++i) + assumptions.push_back(ensure_literal(_assumptions[i])); + TRACE("sat", tout << _assumptions << "\n";); + lbool r = internalize_formulas(); + if (r != l_true) + return r; + + internalize_assumptions(assumptions); + + try { + r = m_solver.check(m_dep.m_literals); + } + catch (z3_exception& ex) { + IF_VERBOSE(1, verbose_stream() << "exception: " << ex.msg() << "\n";); + if (m.inc()) { + set_reason_unknown(std::string("(sat.giveup ") + ex.msg() + ')'); + return l_undef; + } + r = l_undef; + } + switch (r) { + case l_true: + check_assumptions(); + break; + case l_false: + extract_core(); + break; + default: + set_reason_unknown(m_solver.get_reason_unknown()); + break; + } + return r; + } + + void push() override { + try { + internalize_formulas(); + } + catch (...) { + push_internal(); + throw; + } + push_internal(); + } + + void push_internal() { + m_trail.push_scope(); + m_solver.user_push(); + m_goal2sat.user_push(); + m_map.push(); + m_preprocess_state.push(); + m_preprocess.push(); + } + + void pop(unsigned n) override { + if (n > m_trail.get_num_scopes()) // allow inc_sat_solver to + n = m_trail.get_num_scopes(); // take over for another solver. + + m_preprocess.pop(n); + m_preprocess_state.pop(n); + m_map.pop(n); + m_goal2sat.user_pop(n); + m_solver.user_pop(n); + m_trail.pop_scope(n); + m_mc->shrink(m_mc_size); + } + + void set_phase(expr* e) override { + bool is_not = m.is_not(e, e); + sat::bool_var b = m_map.to_bool_var(e); + if (b != sat::null_bool_var) + m_solver.set_phase(sat::literal(b, is_not)); + } + + class sat_phase : public phase, public sat::literal_vector {}; + + phase* get_phase() override { + sat_phase* p = alloc(sat_phase); + for (unsigned v = m_solver.num_vars(); v-- > 0; ) + p->push_back(sat::literal(v, !m_solver.get_phase(v))); + return p; + } + + void set_phase(phase* p) override { + for (auto lit : *static_cast(p)) + m_solver.set_phase(lit); + } + + void move_to_front(expr* e) override { + m.is_not(e, e); + sat::bool_var b = m_map.to_bool_var(e); + if (b != sat::null_bool_var) + m_solver.move_to_front(b); + } + + unsigned get_scope_level() const override { + return m_trail.get_num_scopes(); + } + + bool is_literal(expr* a) const { + m.is_not(a, a); + return is_uninterp_const(a); + } + + /* + * Ensure dependencies are literals so that pre-processing can apply to them. + */ + expr* ensure_literal(expr* a) { + if (is_literal(a)) + return a; + expr* new_dep = m.mk_fresh_const("dep", m.mk_bool_sort()); + expr* fml = m.mk_iff(new_dep, a); + m_fmls.push_back(dependent_expr(m, fml, nullptr)); + m_dep.insert(a, new_dep); + return new_dep; + } + + void assert_expr_core2(expr * t, expr * a) override { + a = ensure_literal(a); + m_fmls.push_back(dependent_expr(m, t, m.mk_leaf(a))); + } + + void assert_expr_core(expr * t) override { + m_fmls.push_back(dependent_expr(m, t, nullptr)); + } + + ast_manager& get_manager() const override { return m; } + + void set_produce_models(bool f) override {} + + void collect_param_descrs(param_descrs & r) override { + solver::collect_param_descrs(r); + goal2sat::collect_param_descrs(r); + sat::solver::collect_param_descrs(r); + m_preprocess.collect_param_descrs(r); + } + + void updt_params(params_ref const & p) override { + m_params.append(p); + sat_params sp(p); + m_params.set_bool("keep_cardinality_constraints", sp.cardinality_solver()); + m_params.set_sym("pb.solver", sp.pb_solver()); + m_solver.updt_params(m_params); + m_solver.set_incremental(true); + m_preprocess.updt_params(m_params); + if (sp.euf()) + ensure_euf(); + } + + void collect_statistics(statistics & st) const override { + m_preprocess.collect_statistics(st); + m_solver.collect_statistics(st); + } + + void get_unsat_core(expr_ref_vector & r) override { + r.reset(); + r.append(m_core.size(), m_core.data()); + } + + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + unsigned sz = vars.size(); + depth.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + auto bv = m_map.to_bool_var(vars[i]); + depth[i] = bv == sat::null_bool_var ? UINT_MAX : m_solver.lvl(bv); + } + } + + expr_ref_vector get_trail(unsigned max_level) override { + expr_ref_vector result(m), lit2expr(m); + unsigned sz = m_solver.trail_size(); + lit2expr.resize(m_solver.num_vars() * 2); + m_map.mk_inv(lit2expr); + for (unsigned i = 0; i < sz; ++i) { + sat::literal lit = m_solver.trail_literal(i); + if (m_solver.lvl(lit) > max_level) + continue; + expr_ref e(lit2expr.get(lit.index()), m); + if (e) + result.push_back(e); + } + return result; + } + + proof * get_proof_core() override { + return nullptr; + } + + expr_ref_vector last_cube(bool is_sat) { + expr_ref_vector result(m); + result.push_back(is_sat ? m.mk_true() : m.mk_false()); + return result; + } + + expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override { + if (!is_internalized()) { + lbool r = internalize_formulas(); + if (r != l_true) { + IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n"); + return expr_ref_vector(m); + } + } + convert_internalized(); + if (m_solver.inconsistent()) + return last_cube(false); + obj_hashtable _vs; + for (expr* v : vs) + _vs.insert(v); + sat::bool_var_vector vars; + for (auto& kv : m_map) + if (_vs.empty() || _vs.contains(kv.m_key)) + vars.push_back(kv.m_value); + sat::literal_vector lits; + lbool result = m_solver.cube(vars, lits, backtrack_level); + expr_ref_vector fmls(m); + expr_ref_vector lit2expr(m); + lit2expr.resize(m_solver.num_vars() * 2); + m_map.mk_inv(lit2expr); + for (sat::literal l : lits) { + expr* e = lit2expr.get(l.index()); + SASSERT(e); + fmls.push_back(e); + } + vs.reset(); + for (sat::bool_var v : vars) { + expr* x = lit2expr[sat::literal(v, false).index()].get(); + if (x) + vs.push_back(x); + } + switch (result) { + case l_true: + return last_cube(true); + case l_false: + return last_cube(false); + default: + break; + } + if (lits.empty()) + set_reason_unknown(m_solver.get_reason_unknown()); + return fmls; + } + + + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { + sat::literal_vector ls; + u_map lit2var; + for (expr * e : vars) { + expr* atom = e;; + bool neg = m.is_not(e, atom); + sat::bool_var v = m_map.to_bool_var(atom); + if (v != sat::null_bool_var) { + sat::literal lit(v, neg); + ls.push_back(lit); + lit2var.insert(lit.index(), e); + } + } + vector ls_mutexes; + m_solver.find_mutexes(ls, ls_mutexes); + for (sat::literal_vector const& ls_mutex : ls_mutexes) { + expr_ref_vector mutex(m); + for (sat::literal l : ls_mutex) + mutex.push_back(lit2var.find(l.index())); + mutexes.push_back(mutex); + } + return l_true; + } + + void init_reason_unknown() { + m_unknown = "no reason given"; + } + + std::string reason_unknown() const override { + return m_unknown; + } + + void set_reason_unknown(char const* msg) override { + m_unknown = msg; + } + + void set_reason_unknown(std::string &&msg) { + m_unknown = std::move(msg); + } + + void get_labels(svector & r) override { + } + + unsigned get_num_assertions() const override { + const_cast(this)->convert_internalized(); + if (is_internalized() && m_internalized_converted) + return m_internalized_fmls.size(); + else + return m_fmls.size(); + } + + expr * get_assertion(unsigned idx) const override { + if (is_internalized() && m_internalized_converted) + return m_internalized_fmls[idx]; + return m_fmls[idx].fml(); + } + + unsigned get_num_assumptions() const override { + return m_assumptions.size(); + } + + expr * get_assumption(unsigned idx) const override { + return m_assumptions[idx]; + } + + model_converter_ref get_model_converter() const override { + const_cast(this)->convert_internalized(); + if (m_cached_mc) + return m_cached_mc; + if (is_internalized() && m_internalized_converted) { + if (m_sat_mc) m_sat_mc->flush_smc(m_solver, m_map); + m_cached_mc = concat(solver::get_model_converter().get(), m_mc.get(), m_sat_mc.get()); + TRACE("sat", m_cached_mc->display(tout);); + return m_cached_mc; + } + else { + return solver::get_model_converter(); + } + } + + void convert_internalized() { + m_solver.pop_to_base_level(); + if (!is_internalized() && m_preprocess.qhead() > 0) + internalize_formulas(); + if (!is_internalized() || m_internalized_converted) + return; + sat2goal s2g; + m_cached_mc = nullptr; + goal g(m, false, true, false); + s2g(m_solver, m_map, m_params, g, m_sat_mc); + m_internalized_fmls.reset(); + g.get_formulas(m_internalized_fmls); + TRACE("sat", m_solver.display(tout); tout << m_internalized_fmls << "\n";); + m_internalized_converted = true; + } + + void init_preprocess() { + ::init_preprocess(m, m_params, m_preprocess, m_preprocess_state); + } + + euf::solver* get_euf() { + return dynamic_cast(m_solver.get_extension()); + } + + void init_goal2sat() { + m_goal2sat.init(m, m_params, m_solver, m_map, m_dep.m_dep2lit, true); + } + + euf::solver* ensure_euf() { + init_goal2sat(); + return m_goal2sat.ensure_euf(); + } + + void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause) override { + ensure_euf()->register_on_clause(ctx, on_clause); + } + + void user_propagate_init( + void* ctx, + user_propagator::push_eh_t& push_eh, + user_propagator::pop_eh_t& pop_eh, + user_propagator::fresh_eh_t& fresh_eh) override { + ensure_euf()->user_propagate_init(ctx, push_eh, pop_eh, fresh_eh); + } + + void user_propagate_register_fixed(user_propagator::fixed_eh_t& fixed_eh) override { + ensure_euf()->user_propagate_register_fixed(fixed_eh); + } + + void user_propagate_register_final(user_propagator::final_eh_t& final_eh) override { + ensure_euf()->user_propagate_register_final(final_eh); + } + + void user_propagate_register_eq(user_propagator::eq_eh_t& eq_eh) override { + ensure_euf()->user_propagate_register_eq(eq_eh); + } + + void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh) override { + ensure_euf()->user_propagate_register_diseq(diseq_eh); + } + + void user_propagate_register_expr(expr* e) override { + ensure_euf()->user_propagate_register_expr(e); + } + + void user_propagate_register_created(user_propagator::created_eh_t& r) override { + ensure_euf()->user_propagate_register_created(r); + } + +private: + + void add_assumption(expr* a) { + init_goal2sat(); + m_dep.insert(a, m_goal2sat.internalize(a)); + } + + void internalize_assumptions(expr_ref_vector const& asms) { + for (expr* a : asms) + add_assumption(a); + for (expr* a : m_assumptions) + add_assumption(a); + } + + lbool internalize_formulas() { + + if (is_internalized()) + return l_true; + + unsigned qhead = m_preprocess.qhead(); + m_trail.push(restore_vector(m_assumptions)); + m_trail.push(restore_vector(m_fmls)); + m_trail.push(value_trail(m_mc_size)); + + m_internalized_converted = false; + + m_preprocess_state.replay(qhead); + m_preprocess.reduce(); + m_preprocess_state.append(*m_mc); + m_solver.pop_to_base_level(); + m_aux_fmls.reset(); + for (; qhead < m_fmls.size(); ++qhead) + add_with_dependency(m_fmls[qhead]); + init_goal2sat(); + m_goal2sat(m_aux_fmls.size(), m_aux_fmls.data()); + if (!m_sat_mc) + m_sat_mc = alloc(sat2goal::mc, m); + m_sat_mc->flush_smc(m_solver, m_map); + return m.inc() ? l_true : l_undef; + } + + ptr_vector m_deps; + void add_with_dependency(dependent_expr const& de) { + if (!de.dep()) { + m_aux_fmls.push_back(de.fml()); + return; + } + m_deps.reset(); + m.linearize(de.dep(), m_deps); + m_ors.reset(); + m_ors.push_back(de.fml()); + flatten_or(m_ors); + for (expr* d : m_deps) { + SASSERT(m.is_bool(d)); + SASSERT(is_literal(d)); + m_assumptions.push_back(d); + m_ors.push_back(mk_not(m, d)); + } + m_aux_fmls.push_back(mk_or(m_ors)); + } + + void extract_core() { + m_core.reset(); + if (m_dep.m_literals.empty()) + return; + for (sat::literal c : m_solver.get_core()) + m_core.push_back(m_dep.lit2orig(c)); + TRACE("sat", + tout << "core: " << m_solver.get_core() << "\n"; + tout << "core: " << m_core << "\n"; + m_solver.display(tout)); + } + + void check_assumptions() { + sat::model const& ll_m = m_solver.get_model(); + for (auto const& [k, lit] : m_dep.m_dep2lit) { + if (sat::value_at(lit, ll_m) == l_true) + continue; + IF_VERBOSE(0, verbose_stream() << mk_pp(k, m) << " does not evaluate to true\n"; + verbose_stream() << m_dep.m_literals << "\n"; + m_solver.display_assignment(verbose_stream()); + m_solver.display(verbose_stream());); + throw default_exception("bad state"); + } + } + + void get_model_core(model_ref & mdl) override { + TRACE("sat", tout << "retrieve model " << (m_solver.model_is_current()?"present":"absent") << "\n";); + mdl = nullptr; + if (!m_solver.model_is_current()) + return; + if (m_fmls.size() > m_preprocess.qhead()) + return; + TRACE("sat", m_solver.display_model(tout);); + CTRACE("sat", m_sat_mc, m_sat_mc->display(tout);); + sat::model ll_m = m_solver.get_model(); + mdl = alloc(model, m); + if (m_sat_mc) + (*m_sat_mc)(ll_m); + expr_ref_vector var2expr(m); + m_map.mk_var_inv(var2expr); + + for (unsigned v = 0; v < var2expr.size(); ++v) { + expr * n = var2expr.get(v); + if (!n || !is_uninterp_const(n)) { + continue; + } + switch (sat::value_at(v, ll_m)) { + case l_true: + mdl->register_decl(to_app(n)->get_decl(), m.mk_true()); + break; + case l_false: + mdl->register_decl(to_app(n)->get_decl(), m.mk_false()); + break; + default: + break; + } + } + + TRACE("sat", m_solver.display(tout);); + if (m_sat_mc) + (*m_sat_mc)(mdl); + m_goal2sat.update_model(mdl); + TRACE("sat", m_mc->display(tout);); + (*m_mc)(mdl); + + TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); + + if (!gparams::get_ref().get_bool("model_validate", false)) { + return; + } + IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";); + model_evaluator eval(*mdl); + eval.set_model_completion(true); + bool all_true = true; + for (dependent_expr const& d : m_fmls) { + if (has_quantifiers(d.fml())) + continue; + expr_ref tmp(m); + eval(d.fml(), tmp); + if (m.limit().is_canceled()) + return; + CTRACE("sat", !m.is_true(tmp), + tout << "Evaluation failed: " << mk_pp(d.fml(), m) << " to " << tmp << "\n"; + model_smt2_pp(tout, m, *(mdl.get()), 0);); + if (m.is_false(tmp)) { + IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(d.fml(), m) << "\n"); + IF_VERBOSE(0, verbose_stream() << "evaluated to " << tmp << "\n"); + all_true = false; + } + } + if (!all_true) { + IF_VERBOSE(0, verbose_stream() << m_params << "\n"); + IF_VERBOSE(0, if (m_mc) m_mc->display(verbose_stream() << "mc0\n")); + IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); + exit(0); + } + else { + IF_VERBOSE(1, verbose_stream() << "solution verified\n"); + } + } +}; + + +solver* mk_sat_smt_solver(ast_manager& m, params_ref const& p) { + return alloc(sat_smt_solver, m, p); +} + diff --git a/src/sat/sat_solver/sat_smt_solver.h b/src/sat/sat_solver/sat_smt_solver.h new file mode 100644 index 00000000000..6d784b4016b --- /dev/null +++ b/src/sat/sat_solver/sat_smt_solver.h @@ -0,0 +1,25 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sat_smt_solver.h + +Abstract: + + incremental solver based on SAT core. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-7-30 + +Notes: + +--*/ + +#pragma once + +#include "solver/solver.h" + +solver* mk_sat_smt_solver(ast_manager& m, params_ref const& p); + diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index b521095beeb..1a9fc0f56ca 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -40,6 +40,7 @@ Module Name: #include "muz/fp/horn_tactic.h" #include "smt/smt_solver.h" #include "sat/sat_solver/inc_sat_solver.h" +#include "sat/sat_solver/sat_smt_solver.h" #include "ast/rewriter/bv_rewriter.h" #include "solver/solver2tactic.h" #include "solver/parallel_tactic.h" From dd1ca8f6bd1e2bc8dada167eb0056cfd4abc3dd1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 29 Nov 2022 16:36:02 +0700 Subject: [PATCH 138/597] move qhead to attribute on the state instead of the simplifier, - add sat.smt option to enable the new incremental core (it is not ready for mainstream consumption as cloning and other features are not implemented and it hasn't been tested in any detail yet). - move "name" into attribute on simplifier so it can be reused for diagnostics by the seq-simplifier. --- src/ast/simplifiers/bit_blaster.cpp | 4 +-- src/ast/simplifiers/bit_blaster.h | 2 +- src/ast/simplifiers/bv_slice.cpp | 5 ++- src/ast/simplifiers/bv_slice.h | 2 +- src/ast/simplifiers/card2bv.cpp | 4 +-- src/ast/simplifiers/card2bv.h | 1 + src/ast/simplifiers/dependent_expr_state.h | 18 +++++++--- src/ast/simplifiers/elim_unconstrained.cpp | 7 ++-- src/ast/simplifiers/elim_unconstrained.h | 2 ++ src/ast/simplifiers/eliminate_predicates.cpp | 6 ++-- src/ast/simplifiers/eliminate_predicates.h | 2 ++ src/ast/simplifiers/euf_completion.cpp | 6 ++-- src/ast/simplifiers/euf_completion.h | 1 + src/ast/simplifiers/max_bv_sharing.cpp | 5 +-- src/ast/simplifiers/propagate_values.cpp | 7 ++-- src/ast/simplifiers/propagate_values.h | 1 + src/ast/simplifiers/rewriter_simplifier.h | 5 +-- src/ast/simplifiers/seq_simplifier.h | 36 +++++++++++++++++-- src/ast/simplifiers/solve_context_eqs.cpp | 2 +- src/ast/simplifiers/solve_eqs.cpp | 6 ++-- src/ast/simplifiers/solve_eqs.h | 2 ++ src/sat/sat_params.pyg | 9 ++--- src/sat/sat_solver/sat_smt_preprocess.cpp | 2 +- src/sat/sat_solver/sat_smt_solver.cpp | 30 ++++++++-------- src/sat/smt/q_mbi.cpp | 2 +- src/sat/tactic/goal2sat.cpp | 2 +- src/solver/solver.h | 2 +- src/tactic/arith/card2bv_tactic.h | 2 +- src/tactic/bv/bv_slice_tactic.cpp | 2 +- src/tactic/bv/max_bv_sharing_tactic.h | 2 +- src/tactic/core/elim_uncnstr2_tactic.h | 2 +- src/tactic/core/eliminate_predicates_tactic.h | 4 +-- src/tactic/core/euf_completion_tactic.cpp | 2 +- src/tactic/core/propagate_values2_tactic.h | 2 +- src/tactic/core/solve_eqs_tactic.h | 2 +- src/tactic/dependent_expr_state_tactic.h | 12 +++---- src/tactic/portfolio/smt_strategic_solver.cpp | 16 ++++++--- 37 files changed, 132 insertions(+), 85 deletions(-) diff --git a/src/ast/simplifiers/bit_blaster.cpp b/src/ast/simplifiers/bit_blaster.cpp index 218765d283b..dc086c62843 100644 --- a/src/ast/simplifiers/bit_blaster.cpp +++ b/src/ast/simplifiers/bit_blaster.cpp @@ -37,7 +37,7 @@ void bit_blaster::reduce() { expr_ref new_curr(m); proof_ref new_pr(m); bool change = false; - for (unsigned idx = m_qhead; idx < m_fmls.size(); idx++) { + for (unsigned idx = m_fmls.qhead(); idx < m_fmls.size(); idx++) { if (m_fmls.inconsistent()) break; auto [curr, d] = m_fmls[idx](); @@ -60,8 +60,6 @@ void bit_blaster::reduce() { m_fmls.model_trail().push(f, v, nullptr, {}); } m_rewriter.cleanup(); - - advance_qhead(); } diff --git a/src/ast/simplifiers/bit_blaster.h b/src/ast/simplifiers/bit_blaster.h index 70446918b4e..231a1ca68ed 100644 --- a/src/ast/simplifiers/bit_blaster.h +++ b/src/ast/simplifiers/bit_blaster.h @@ -33,7 +33,7 @@ class bit_blaster : public dependent_expr_simplifier { m_rewriter(m, p) { updt_params(p); } - + char const* name() const override { return "bit-blaster"; } void updt_params(params_ref const & p) override; void collect_param_descrs(param_descrs & r) override; void reduce() override; diff --git a/src/ast/simplifiers/bv_slice.cpp b/src/ast/simplifiers/bv_slice.cpp index 995231b3475..14d6edb99f0 100644 --- a/src/ast/simplifiers/bv_slice.cpp +++ b/src/ast/simplifiers/bv_slice.cpp @@ -24,11 +24,10 @@ namespace bv { void slice::reduce() { process_eqs(); apply_subst(); - advance_qhead(); } void slice::process_eqs() { - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { auto const [f, d] = m_fmls[i](); process_eq(f); } @@ -137,7 +136,7 @@ namespace bv { expr_ref_vector cache(m), pin(m); ptr_vector todo, args; expr* c; - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { auto const [f, d] = m_fmls[i](); todo.push_back(f); pin.push_back(f); diff --git a/src/ast/simplifiers/bv_slice.h b/src/ast/simplifiers/bv_slice.h index cc0f48cfcc6..3bf514ac394 100644 --- a/src/ast/simplifiers/bv_slice.h +++ b/src/ast/simplifiers/bv_slice.h @@ -47,7 +47,7 @@ namespace bv { public: slice(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_bv(m), m_rewriter(m) {} - + char const* name() const override { return "bv-slice"; } 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/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index 44bc8e589e8..3feb8647410 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -29,7 +29,7 @@ void card2bv::reduce() { expr_ref new_f1(m), new_f2(m); proof_ref new_pr(m); - for (unsigned idx = m_qhead; !m_fmls.inconsistent() && idx < m_fmls.size(); idx++) { + for (unsigned idx = m_fmls.qhead(); !m_fmls.inconsistent() && idx < m_fmls.size(); idx++) { auto [f, d] = m_fmls[idx](); rw1(f, new_f1); rw2(false, new_f1, new_f2, new_pr); @@ -48,8 +48,6 @@ void card2bv::reduce() { func_decl_ref_vector const& fns = rw2.fresh_constants(); for (func_decl* f : fns) m_fmls.model_trail().hide(f); - - advance_qhead(); } void card2bv::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/card2bv.h b/src/ast/simplifiers/card2bv.h index e089fa84c87..4c081c8cdf8 100644 --- a/src/ast/simplifiers/card2bv.h +++ b/src/ast/simplifiers/card2bv.h @@ -34,6 +34,7 @@ class card2bv : public dependent_expr_simplifier { public: card2bv(ast_manager& m, params_ref const& p, dependent_expr_state& fmls); + char const* name() const override { return "card2bv"; } void reduce() override; void collect_statistics(statistics& st) const override; void reset_statistics() override { m_stats.reset(); } diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 90b95ab7d06..54ee5626b99 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -41,6 +41,7 @@ Module Name: abstract interface to state updated by simplifiers. */ class dependent_expr_state { + unsigned m_qhead = 0; public: virtual ~dependent_expr_state() {} virtual unsigned size() const = 0; @@ -53,8 +54,15 @@ class dependent_expr_state { trail_stack m_trail; void push() { m_trail.push_scope(); } void pop(unsigned n) { m_trail.pop_scope(n); } - - + unsigned qhead() const { return m_qhead; } + void advance_qhead() { m_qhead = size(); } + unsigned num_exprs() { + expr_fast_mark1 visited; + unsigned r = 0; + for (unsigned i = 0; i < size(); i++) + r += get_num_exprs((*this)[i].fml(), visited); + return r; + } }; /** @@ -65,14 +73,13 @@ class dependent_expr_simplifier { ast_manager& m; dependent_expr_state& m_fmls; 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() { if (num_scopes() > 0) m_trail.push(value_trail(m_qhead)); m_qhead = m_fmls.size(); } public: 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 char const* name() const = 0; virtual void push() { } virtual void pop(unsigned n) { } virtual void reduce() = 0; @@ -80,7 +87,8 @@ class dependent_expr_simplifier { virtual void reset_statistics() {} virtual void updt_params(params_ref const& p) {} virtual void collect_param_descrs(param_descrs& r) {} - unsigned qhead() const { return m_qhead; } + ast_manager& get_manager() { return m; } + dependent_expr_state& get_fmls() { return m_fmls; } }; /** diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index ac5cc339d65..93a84dba644 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -138,7 +138,7 @@ void elim_unconstrained::init_nodes() { // freeze subterms before the already processed head terms.reset(); - for (unsigned i = 0; i < m_qhead; ++i) + for (unsigned i = 0; i < m_fmls.qhead(); ++i) terms.push_back(m_fmls[i].fml()); for (expr* e : subterms::all(terms)) m_frozen.mark(e, true); @@ -216,7 +216,7 @@ void elim_unconstrained::gc(expr* t) { */ void elim_unconstrained::reconstruct_terms() { expr_ref_vector terms(m); - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) + for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) terms.push_back(m_fmls[i].fml()); for (expr* e : subterms_postorder::all(terms)) { @@ -250,7 +250,7 @@ void elim_unconstrained::reconstruct_terms() { void elim_unconstrained::assert_normalized(vector& old_fmls) { unsigned sz = m_fmls.size(); - for (unsigned i = m_qhead; i < sz; ++i) { + for (unsigned i = m_fmls.qhead(); i < sz; ++i) { auto [f, d] = m_fmls[i](); node& n = get_node(f); expr* g = n.m_term; @@ -302,5 +302,4 @@ void elim_unconstrained::reduce() { vector old_fmls; assert_normalized(old_fmls); update_model_trail(*mc, old_fmls); - advance_qhead(); } diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 89f28fe33e2..b66d0b72748 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -71,6 +71,8 @@ class elim_unconstrained : public dependent_expr_simplifier { elim_unconstrained(ast_manager& m, dependent_expr_state& fmls); + char const* name() const override { return "elim-unconstrained"; } + void reduce() override; void collect_statistics(statistics& st) const override { st.update("elim-unconstrained", m_stats.m_num_eliminated); } diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 6d1288c5d70..fa615048f5b 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -501,7 +501,7 @@ void eliminate_predicates::reduce_definitions() { for (auto const& [k, v] : m_macros) macro_expander.insert(v->m_head, v->m_def, v->m_dep); - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { auto [f, d] = m_fmls[i](); expr_ref fml(f, m), new_fml(m); expr_dependency_ref dep(m); @@ -779,7 +779,7 @@ eliminate_predicates::clause* eliminate_predicates::init_clause(expr* f, expr_de * eliminations. */ void eliminate_predicates::init_clauses() { - for (unsigned i = 0; i < m_qhead; ++i) + for (unsigned i = 0; i < m_fmls.qhead(); ++i) m_to_exclude.push_back(m_fmls[i].fml()); recfun::util rec(m); if (rec.has_rec_defs()) @@ -788,7 +788,7 @@ void eliminate_predicates::init_clauses() { process_to_exclude(m_disable_macro); - for (unsigned i = m_qhead; i < m_fmls.size(); ++i) { + for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { clause* cl = init_clause(i); add_use_list(*cl); m_clauses.push_back(cl); diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index 6afd0886a8c..ca4110e99f2 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -129,6 +129,8 @@ class eliminate_predicates : public dependent_expr_simplifier { eliminate_predicates(ast_manager& m, dependent_expr_state& fmls); ~eliminate_predicates() override { reset(); } + + char const* name() const override { return "elim-predicates"; } void reduce() override; diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 5056d818c2c..f077f19182d 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -80,7 +80,7 @@ namespace euf { m_nodes_to_canonize.push_back(ch); }; - for (unsigned i = m_qhead; i < sz; ++i) { + for (unsigned i = m_fmls.qhead(); i < sz; ++i) { expr* x, * y; auto [f, d] = m_fmls[i](); if (m.is_eq(f, x, y)) { @@ -114,7 +114,7 @@ namespace euf { } unsigned sz = m_fmls.size(); - for (unsigned i = m_qhead; i < sz; ++i) { + for (unsigned i = m_fmls.qhead(); i < sz; ++i) { auto [f, d] = m_fmls[i](); expr_dependency_ref dep(d, m); @@ -127,8 +127,6 @@ namespace euf { } CTRACE("euf_completion", g != f, tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); } - if (!m_has_new_eq) - advance_qhead(); } bool completion::is_new_eq(expr* a, expr* b) { diff --git a/src/ast/simplifiers/euf_completion.h b/src/ast/simplifiers/euf_completion.h index f02e3324569..da0fb7276f7 100644 --- a/src/ast/simplifiers/euf_completion.h +++ b/src/ast/simplifiers/euf_completion.h @@ -58,6 +58,7 @@ namespace euf { expr_dependency* explain_conflict(); public: completion(ast_manager& m, dependent_expr_state& fmls); + char const* name() const override { return "euf-reduce"; } 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; diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index 3003a47a020..4744c473c5d 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -250,10 +250,12 @@ class max_bv_sharing : public dependent_expr_simplifier { "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } + char const* name() const override { return "max-bv-sharing"; } + void reduce() override { expr_ref new_curr(m); proof_ref new_pr(m); - for (unsigned idx = m_qhead; idx < m_fmls.size() && !m_fmls.inconsistent(); idx++) { + for (unsigned idx = m_fmls.qhead(); idx < m_fmls.size() && !m_fmls.inconsistent(); idx++) { auto [curr, d] = m_fmls[idx](); m_rw(curr, new_curr, new_pr); // Proof reconstruction: new_pr = m.mk_modus_ponens(old_pr, new_pr); @@ -261,7 +263,6 @@ class max_bv_sharing : public dependent_expr_simplifier { m_fmls.update(idx, dependent_expr(m, new_curr, d)); } m_rw.cfg().cleanup(); - advance_qhead(); } }; diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 6d6bc976f14..2572cc31002 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -78,7 +78,7 @@ void propagate_values::reduce() { subst.reset(); m_rewriter.reset(); m_rewriter.set_substitution(&subst); - for (unsigned i = 0; i < m_qhead; ++i) + for (unsigned i = 0; i < m_fmls.qhead(); ++i) add_sub(m_fmls[i]); }; @@ -86,10 +86,10 @@ void propagate_values::reduce() { for (unsigned r = 0; r < m_max_rounds && rw != m_stats.m_num_rewrites; ++r) { rw = m_stats.m_num_rewrites; init_sub(); - for (unsigned i = m_qhead; i < m_fmls.size() && !m_fmls.inconsistent(); ++i) + for (unsigned i = m_fmls.qhead(); i < m_fmls.size() && !m_fmls.inconsistent(); ++i) process_fml(i); init_sub(); - for (unsigned i = m_fmls.size(); i-- > m_qhead && !m_fmls.inconsistent();) + for (unsigned i = m_fmls.size(); i-- > m_fmls.qhead() && !m_fmls.inconsistent();) process_fml(i); if (subst.empty()) break; @@ -97,7 +97,6 @@ void propagate_values::reduce() { m_rewriter.set_substitution(nullptr); m_rewriter.reset(); - advance_qhead(); } void propagate_values::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/propagate_values.h b/src/ast/simplifiers/propagate_values.h index d263377b112..9ad59d1b34d 100644 --- a/src/ast/simplifiers/propagate_values.h +++ b/src/ast/simplifiers/propagate_values.h @@ -37,6 +37,7 @@ class propagate_values : public dependent_expr_simplifier { public: propagate_values(ast_manager& m, params_ref const& p, dependent_expr_state& fmls); + char const* name() const override { return "propagate-values2"; } void reduce() override; void collect_statistics(statistics& st) const override; void reset_statistics() override { m_stats.reset(); } diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h index 23ebb4f84b4..f956ec80828 100644 --- a/src/ast/simplifiers/rewriter_simplifier.h +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -33,12 +33,14 @@ class rewriter_simplifier : public dependent_expr_simplifier { m_rewriter(m) { updt_params(p); } + + char const* name() const override { return "simplifier"; } void reduce() override { m_num_steps = 0; expr_ref new_curr(m); proof_ref new_pr(m); - for (unsigned idx = m_qhead; idx < m_fmls.size(); idx++) { + for (unsigned idx = m_fmls.qhead(); idx < m_fmls.size(); idx++) { if (m_fmls.inconsistent()) break; auto d = m_fmls[idx]; @@ -46,7 +48,6 @@ class rewriter_simplifier : public dependent_expr_simplifier { m_num_steps += m_rewriter.get_num_steps(); m_fmls.update(idx, dependent_expr(m, new_curr, d.dep())); } - advance_qhead(); } void collect_statistics(statistics& st) const override { st.update("simplifier", m_num_steps); } void reset_statistics() override { m_num_steps = 0; } diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h index 0d5483b71aa..0fa5990ddc4 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/seq_simplifier.h @@ -17,16 +17,46 @@ Module Name: #pragma once +#include "util/stopwatch.h" #include "ast/simplifiers/dependent_expr_state.h" class seq_simplifier : public dependent_expr_simplifier { scoped_ptr_vector m_simplifiers; + struct collect_stats { + stopwatch m_watch; + double m_start_memory = 0; + dependent_expr_simplifier& s; + collect_stats(dependent_expr_simplifier& s) : + m_start_memory(static_cast(memory::get_allocation_size()) / static_cast(1024 * 1024)), + s(s) { + m_watch.start(); + } + ~collect_stats() { + m_watch.stop(); + double end_memory = static_cast(memory::get_allocation_size()) / static_cast(1024 * 1024); + IF_VERBOSE(10, + statistics st; + verbose_stream() << "(" << s.name() + << " :num-exprs " << s.get_fmls().num_exprs() + << " :num-asts " << s.get_manager().get_num_asts() + << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() + << " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory + << " :after-memory " << std::fixed << std::setprecision(2) << end_memory + << ")" << "\n"; + s.collect_statistics(st); + verbose_stream() << st); + } + }; + public: + seq_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls) { } + + char const* name() const override { return "and-then"; } void add_simplifier(dependent_expr_simplifier* s) { m_simplifiers.push_back(s); @@ -36,9 +66,11 @@ class seq_simplifier : public dependent_expr_simplifier { for (auto* s : m_simplifiers) { if (m_fmls.inconsistent()) break; + if (!m.inc()) + break; + collect_stats _cs(*s); s->reduce(); - } - advance_qhead(); + } } void collect_statistics(statistics& st) const override { diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index ef52d009c52..7eaa8ad2fc9 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -148,7 +148,7 @@ namespace euf { void solve_context_eqs::collect_nested_equalities(dep_eq_vector& eqs) { expr_mark visited; unsigned sz = m_fmls.size(); - for (unsigned i = m_solve_eqs.m_qhead; i < sz; ++i) + for (unsigned i = m_fmls.qhead(); i < sz; ++i) collect_nested_equalities(m_fmls[i], visited, eqs); if (eqs.empty()) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 1bea283ac9e..f6055af50bb 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -34,7 +34,7 @@ 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) + for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) ex->get_eqs(m_fmls[i], eqs); } @@ -187,7 +187,7 @@ namespace euf { 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) { + for (unsigned i = m_fmls.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); @@ -236,8 +236,6 @@ namespace euf { apply_subst(old_fmls); save_subst(old_fmls); } - - advance_qhead(); } void solve_eqs::save_subst(vector const& old_fmls) { diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index a2afd6e5892..4b2905b2a1c 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -68,6 +68,8 @@ namespace euf { solve_eqs(ast_manager& m, dependent_expr_state& fmls); + char const* name() const override { return "solve-eqs"; } + void reduce() override; void updt_params(params_ref const& p) override; diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 20a41544195..f9d7c643ac6 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -47,6 +47,7 @@ def_module_params('sat', ('threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), ('drat.disable', BOOL, False, 'override anything that enables DRAT'), + ('smt', BOOL, False, 'use the SAT solver based incremental SMT core'), ('smt.proof.check', BOOL, False, 'check SMT proof while it is created'), ('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'), ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), @@ -73,11 +74,11 @@ def_module_params('sat', ('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'), ('local_search_dbg_flips', BOOL, False, 'write debug information for number of flips'), ('binspr', BOOL, False, 'enable SPR inferences of binary propagation redundant clauses. This inprocessing step eliminates models'), - ('anf', BOOL, False, 'enable ANF based simplification in-processing'), - ('anf.delay', UINT, 2, 'delay ANF simplification by in-processing round'), + ('anf', BOOL, False, 'enable ANF based simplification in-processing'), + ('anf.delay', UINT, 2, 'delay ANF simplification by in-processing round'), ('anf.exlin', BOOL, False, 'enable extended linear simplification'), - ('cut', BOOL, False, 'enable AIG based simplification in-processing'), - ('cut.delay', UINT, 2, 'delay cut simplification by in-processing round'), + ('cut', BOOL, False, 'enable AIG based simplification in-processing'), + ('cut.delay', UINT, 2, 'delay cut simplification by in-processing round'), ('cut.aig', BOOL, False, 'extract aigs (and ites) from cluases for cut simplification'), ('cut.lut', BOOL, False, 'extract luts from clauses for cut simplification'), ('cut.xor', BOOL, False, 'extract xors from clauses for cut simplification'), diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index 72c7c52323c..f05fc098ff6 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -44,7 +44,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep simp2_p.set_bool("flat_and_or", false); sat_params sp(p); - if (sp.euf()) { + if (sp.euf() || sp.smt()) { s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); s.add_simplifier(alloc(propagate_values, m, p, st)); // diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 3ea0543ec49..2d95dab6922 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -130,7 +130,7 @@ class sat_smt_solver : public solver { trail_stack& m_trail; dependency2assumptions m_dep; goal2sat m_goal2sat; - expr_ref_vector m_assumptions, m_core, m_ors, m_aux_fmls; + expr_ref_vector m_assumptions, m_core, m_ors, m_aux_fmls, m_internalized_fmls; atom2bool_var m_map; generic_model_converter_ref m_mc; unsigned m_mc_size = 0; @@ -140,9 +140,8 @@ class sat_smt_solver : public solver { // access formulas after they have been pre-processed and handled by the sat solver. // this allows to access the internal state of the SAT solver and carry on partial results. bool m_internalized_converted = false; // have internalized formulas been converted back - expr_ref_vector m_internalized_fmls; // formulas in internalized format - bool is_internalized() const { return m_preprocess.qhead() == m_fmls.size(); } + bool is_internalized() const { return m_preprocess_state.qhead() == m_fmls.size(); } public: sat_smt_solver(ast_manager& m, params_ref const& p): @@ -152,16 +151,12 @@ class sat_smt_solver : public solver { m_trail(m_preprocess_state.m_trail), m_dep(m, m_trail), m_solver(p, m.limit()), - m_assumptions(m), - m_core(m), - m_ors(m), - m_aux_fmls(m), + m_assumptions(m), m_core(m), m_ors(m), m_aux_fmls(m), m_internalized_fmls(m), m_map(m), - m_internalized_fmls(m) { + m_mc(alloc(generic_model_converter, m, "sat-smt-solver")) { updt_params(p); init_preprocess(); m_solver.set_incremental(true); - m_mc = alloc(generic_model_converter, m, "sat-smt-solver"); } solver* translate(ast_manager& dst_m, params_ref const& p) override { @@ -214,7 +209,7 @@ class sat_smt_solver : public solver { expr_ref_vector assumptions(m); for (unsigned i = 0; i < sz; ++i) assumptions.push_back(ensure_literal(_assumptions[i])); - TRACE("sat", tout << _assumptions << "\n";); + TRACE("sat", tout << assumptions << "\n";); lbool r = internalize_formulas(); if (r != l_true) return r; @@ -267,8 +262,7 @@ class sat_smt_solver : public solver { } void pop(unsigned n) override { - if (n > m_trail.get_num_scopes()) // allow inc_sat_solver to - n = m_trail.get_num_scopes(); // take over for another solver. + n = std::min(n, m_trail.get_num_scopes()); // allow sat_smt_solver to take over for another solver. m_preprocess.pop(n); m_preprocess_state.pop(n); @@ -357,7 +351,7 @@ class sat_smt_solver : public solver { m_solver.updt_params(m_params); m_solver.set_incremental(true); m_preprocess.updt_params(m_params); - if (sp.euf()) + if (sp.smt()) ensure_euf(); } @@ -537,7 +531,7 @@ class sat_smt_solver : public solver { void convert_internalized() { m_solver.pop_to_base_level(); - if (!is_internalized() && m_preprocess.qhead() > 0) + if (!is_internalized() && m_preprocess_state.qhead() > 0) internalize_formulas(); if (!is_internalized() || m_internalized_converted) return; @@ -623,15 +617,19 @@ class sat_smt_solver : public solver { if (is_internalized()) return l_true; - unsigned qhead = m_preprocess.qhead(); + unsigned qhead = m_preprocess_state.qhead(); m_trail.push(restore_vector(m_assumptions)); m_trail.push(restore_vector(m_fmls)); m_trail.push(value_trail(m_mc_size)); + TRACE("sat", tout << "qhead " << qhead << "\n"); m_internalized_converted = false; m_preprocess_state.replay(qhead); m_preprocess.reduce(); + if (!m.inc()) + return l_undef; + m_preprocess_state.advance_qhead(); m_preprocess_state.append(*m_mc); m_solver.pop_to_base_level(); m_aux_fmls.reset(); @@ -695,7 +693,7 @@ class sat_smt_solver : public solver { mdl = nullptr; if (!m_solver.model_is_current()) return; - if (m_fmls.size() > m_preprocess.qhead()) + if (m_fmls.size() > m_preprocess_state.qhead()) return; TRACE("sat", m_solver.display_model(tout);); CTRACE("sat", m_sat_mc, m_sat_mc->display(tout);); diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 2c9b87c13a5..6f2db37e1a2 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -629,7 +629,7 @@ namespace q { void mbqi::init_solver() { if (!m_solver) - m_solver = mk_smt2_solver(m, m_no_drat_params); + m_solver = mk_smt2_solver(m, m_no_drat_params, symbol::null); } void mbqi::init_search() { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 0f23527dd1a..865c5f15d72 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -99,7 +99,7 @@ struct goal2sat::imp : public sat::sat_internalizer { sat_params sp(p); m_ite_extra = p.get_bool("ite_extra", true); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); - m_euf = sp.euf(); + m_euf = sp.euf() || sp.smt(); } void throw_op_not_handled(std::string const& s) { diff --git a/src/solver/solver.h b/src/solver/solver.h index 4ffdd509269..957cb7c8edc 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -35,7 +35,7 @@ class solver_factory { solver_factory * mk_smt_strategic_solver_factory(symbol const & logic = symbol::null); -solver* mk_smt2_solver(ast_manager& m, params_ref const& p); +solver* mk_smt2_solver(ast_manager& m, params_ref const& p, symbol const& logic = symbol::null); /** \brief Abstract interface for making solvers available in the Z3 diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index 95282d93cce..63cb021d706 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -30,7 +30,7 @@ class card2bv_tactic_factory : public dependent_expr_simplifier_factory { }; inline tactic* mk_card2bv_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(card2bv_tactic_factory), "card2bv"); + return alloc(dependent_expr_state_tactic, m, p, alloc(card2bv_tactic_factory)); } /* diff --git a/src/tactic/bv/bv_slice_tactic.cpp b/src/tactic/bv/bv_slice_tactic.cpp index 040068e3941..17d69c7b147 100644 --- a/src/tactic/bv/bv_slice_tactic.cpp +++ b/src/tactic/bv/bv_slice_tactic.cpp @@ -29,5 +29,5 @@ class bv_slice_factory : public dependent_expr_simplifier_factory { }; 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"); + return alloc(dependent_expr_state_tactic, m, p, alloc(bv_slice_factory)); } diff --git a/src/tactic/bv/max_bv_sharing_tactic.h b/src/tactic/bv/max_bv_sharing_tactic.h index ebd050aa514..3521b4a041e 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.h +++ b/src/tactic/bv/max_bv_sharing_tactic.h @@ -32,7 +32,7 @@ class max_bv_sharing_tactic_factory : public dependent_expr_simplifier_factory { }; inline tactic* mk_max_bv_sharing_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(max_bv_sharing_tactic_factory), "max-bv-sharing"); + return alloc(dependent_expr_state_tactic, m, p, alloc(max_bv_sharing_tactic_factory)); } /* diff --git a/src/tactic/core/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h index d9f6196f2f7..e7226a8f0af 100644 --- a/src/tactic/core/elim_uncnstr2_tactic.h +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -29,7 +29,7 @@ class elim_uncnstr2_tactic_factory : public dependent_expr_simplifier_factory { }; inline tactic * mk_elim_uncnstr2_tactic(ast_manager & m, params_ref const & p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(elim_uncnstr2_tactic_factory), "elim-uncnstr2"); + return alloc(dependent_expr_state_tactic, m, p, alloc(elim_uncnstr2_tactic_factory)); } diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index 3daffb1f32d..de2291260b7 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -17,9 +17,9 @@ Module Name: #pragma once #include "util/params.h" +#include "ast/simplifiers/eliminate_predicates.h" #include "tactic/tactic.h" #include "tactic/dependent_expr_state_tactic.h" -#include "ast/simplifiers/eliminate_predicates.h" class eliminate_predicates_tactic_factory : public dependent_expr_simplifier_factory { @@ -30,7 +30,7 @@ class eliminate_predicates_tactic_factory : public dependent_expr_simplifier_fac }; inline tactic * mk_eliminate_predicates_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(eliminate_predicates_tactic_factory), "elim-predicates"); + return alloc(dependent_expr_state_tactic, m, p, alloc(eliminate_predicates_tactic_factory)); } /* diff --git a/src/tactic/core/euf_completion_tactic.cpp b/src/tactic/core/euf_completion_tactic.cpp index bdd940f175f..d229df62f74 100644 --- a/src/tactic/core/euf_completion_tactic.cpp +++ b/src/tactic/core/euf_completion_tactic.cpp @@ -28,5 +28,5 @@ class euf_completion_tactic_factory : public dependent_expr_simplifier_factory { }; 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"); + return alloc(dependent_expr_state_tactic, m, p, alloc(euf_completion_tactic_factory)); } diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h index 58e263e8021..834e8bebd48 100644 --- a/src/tactic/core/propagate_values2_tactic.h +++ b/src/tactic/core/propagate_values2_tactic.h @@ -31,7 +31,7 @@ class propagate_values2_tactic_factory : public dependent_expr_simplifier_factor }; inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(propagate_values2_tactic_factory), "propagate-values2"); + return alloc(dependent_expr_state_tactic, m, p, alloc(propagate_values2_tactic_factory)); } diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 5d6da2e9a1e..76a73866004 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -30,7 +30,7 @@ class solve_eqs_tactic_factory : public dependent_expr_simplifier_factory { }; inline tactic * mk_solve_eqs_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs_tactic_factory), "solve-eqs"); + return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs_tactic_factory)); } /* diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index f16bb0ff3d1..dda114c24dc 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -22,7 +22,6 @@ Module Name: class dependent_expr_state_tactic : public tactic, public dependent_expr_state { ast_manager& m; params_ref m_params; - std::string m_name; trail_stack m_trail; goal_ref m_goal; dependent_expr m_dep; @@ -42,10 +41,9 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { public: - dependent_expr_state_tactic(ast_manager& m, params_ref const& p, dependent_expr_simplifier_factory* f, char const* name): + dependent_expr_state_tactic(ast_manager& m, params_ref const& p, dependent_expr_simplifier_factory* f): m(m), m_params(p), - m_name(name), m_factory(f), m_simp(nullptr), m_dep(m, m.mk_true(), nullptr) @@ -79,7 +77,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { return *m_model_trail; } - char const* name() const override { return m_name.c_str(); } + char const* name() const override { return m_simp?m_simp->name():"null"; } void updt_params(params_ref const & p) override { m_params.append(p); @@ -93,7 +91,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { } tactic * translate(ast_manager & m) override { - return alloc(dependent_expr_state_tactic, m, m_params, m_factory.get(), name()); + return alloc(dependent_expr_state_tactic, m, m_params, m_factory.get()); } void operator()(goal_ref const & in, @@ -105,10 +103,12 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { try { if (!in->proofs_enabled()) m_simp->reduce(); + if (m.inc()) + advance_qhead(); } catch (rewriter_exception& ex) { throw tactic_exception(ex.msg()); - } + } m_goal->elim_true(); m_goal->elim_redundancies(); m_goal->inc_depth(); diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 1a9fc0f56ca..8acfbe40d63 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -47,6 +47,7 @@ Module Name: #include "solver/parallel_params.hpp" #include "params/tactic_params.hpp" #include "parsers/smt2/smt2parser.h" +#include "sat/sat_params.hpp" @@ -114,6 +115,15 @@ static solver* mk_special_solver_for_logic(ast_manager & m, params_ref const & p return nullptr; } +solver* mk_smt2_solver(ast_manager& m, params_ref const& p, symbol const& logic) { + sat_params sp(p); + if (sp.smt()) + return mk_sat_smt_solver(m, p); + if (sp.euf()) + return mk_inc_sat_solver(m, p); + return mk_smt_solver(m, p, logic); +} + static solver* mk_solver_for_logic(ast_manager & m, params_ref const & p, symbol const& logic) { bv_rewriter rw(m); solver* s = mk_special_solver_for_logic(m, p, logic); @@ -123,7 +133,7 @@ static solver* mk_solver_for_logic(ast_manager & m, params_ref const & p, symbol if (!s && tp.default_tactic() == "sat") s = mk_inc_sat_solver(m, p); if (!s) - s = mk_smt_solver(m, p, logic); + s = mk_smt2_solver(m, p, logic); return s; } @@ -170,6 +180,4 @@ solver_factory * mk_smt_strategic_solver_factory(symbol const & logic) { return alloc(smt_strategic_solver_factory, logic); } -solver* mk_smt2_solver(ast_manager& m, params_ref const& p) { - return mk_inc_sat_solver(m, p); -} + From 73a652cf4b60bc382b676da2b96a8b5bd359e342 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 29 Nov 2022 16:42:42 +0700 Subject: [PATCH 139/597] some fixes to backtracking restore points in new solver --- src/ast/simplifiers/dependent_expr_state.h | 2 +- src/sat/sat_solver/sat_smt_solver.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 54ee5626b99..a65492a5838 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -55,7 +55,7 @@ class dependent_expr_state { void push() { m_trail.push_scope(); } void pop(unsigned n) { m_trail.pop_scope(n); } unsigned qhead() const { return m_qhead; } - void advance_qhead() { m_qhead = size(); } + void advance_qhead() { if (m_trail.get_num_scopes() > 0) m_trail.push(value_trail(m_qhead)); m_qhead = size(); } unsigned num_exprs() { expr_fast_mark1 visited; unsigned r = 0; diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 2d95dab6922..91a2bfec26f 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -259,6 +259,9 @@ class sat_smt_solver : public solver { m_map.push(); m_preprocess_state.push(); m_preprocess.push(); + m_trail.push(restore_vector(m_assumptions)); + m_trail.push(restore_vector(m_fmls)); + m_trail.push(value_trail(m_mc_size)); } void pop(unsigned n) override { @@ -618,9 +621,6 @@ class sat_smt_solver : public solver { return l_true; unsigned qhead = m_preprocess_state.qhead(); - m_trail.push(restore_vector(m_assumptions)); - m_trail.push(restore_vector(m_fmls)); - m_trail.push(value_trail(m_mc_size)); TRACE("sat", tout << "qhead " << qhead << "\n"); m_internalized_converted = false; From bec3acd14618120a2606b35f606132f5063c5821 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 08:35:29 +0700 Subject: [PATCH 140/597] consolidate freeze functionality into dependent_expr_state rename size() to qtail() and introduce shortcuts ensure tactic goals are not updated if they are in inconsistent state (because indices could be invalidated) --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/bit_blaster.cpp | 2 +- src/ast/simplifiers/bv_slice.cpp | 4 +- src/ast/simplifiers/card2bv.cpp | 2 +- src/ast/simplifiers/dependent_expr_state.cpp | 109 ++++++++++++++++++ src/ast/simplifiers/dependent_expr_state.h | 52 +++++++-- src/ast/simplifiers/elim_unconstrained.cpp | 31 ++--- src/ast/simplifiers/elim_unconstrained.h | 1 - src/ast/simplifiers/eliminate_predicates.cpp | 37 ++---- src/ast/simplifiers/eliminate_predicates.h | 2 +- src/ast/simplifiers/euf_completion.cpp | 8 +- src/ast/simplifiers/extract_eqs.cpp | 2 +- src/ast/simplifiers/max_bv_sharing.cpp | 2 +- .../model_reconstruction_trail.cpp | 6 +- src/ast/simplifiers/propagate_values.cpp | 8 +- src/ast/simplifiers/rewriter_simplifier.h | 2 +- src/ast/simplifiers/seq_simplifier.h | 1 + src/ast/simplifiers/solve_context_eqs.cpp | 4 +- src/ast/simplifiers/solve_eqs.cpp | 4 +- src/sat/sat_solver/sat_smt_solver.cpp | 31 +++-- src/tactic/dependent_expr_state_tactic.h | 7 +- 21 files changed, 223 insertions(+), 93 deletions(-) create mode 100644 src/ast/simplifiers/dependent_expr_state.cpp diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index c488a626995..9d894627aa6 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(simplifiers bit_blaster.cpp bv_slice.cpp card2bv.cpp + dependent_expr_state.cpp elim_unconstrained.cpp eliminate_predicates.cpp euf_completion.cpp diff --git a/src/ast/simplifiers/bit_blaster.cpp b/src/ast/simplifiers/bit_blaster.cpp index dc086c62843..72ee19ecac2 100644 --- a/src/ast/simplifiers/bit_blaster.cpp +++ b/src/ast/simplifiers/bit_blaster.cpp @@ -37,7 +37,7 @@ void bit_blaster::reduce() { expr_ref new_curr(m); proof_ref new_pr(m); bool change = false; - for (unsigned idx = m_fmls.qhead(); idx < m_fmls.size(); idx++) { + for (unsigned idx = qhead(); idx < qtail(); idx++) { if (m_fmls.inconsistent()) break; auto [curr, d] = m_fmls[idx](); diff --git a/src/ast/simplifiers/bv_slice.cpp b/src/ast/simplifiers/bv_slice.cpp index 14d6edb99f0..cc8b4fd85ad 100644 --- a/src/ast/simplifiers/bv_slice.cpp +++ b/src/ast/simplifiers/bv_slice.cpp @@ -27,7 +27,7 @@ namespace bv { } void slice::process_eqs() { - for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { + for (unsigned i = qhead(); i < qtail(); ++i) { auto const [f, d] = m_fmls[i](); process_eq(f); } @@ -136,7 +136,7 @@ namespace bv { expr_ref_vector cache(m), pin(m); ptr_vector todo, args; expr* c; - for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { + for (unsigned i = qhead(); i < qtail(); ++i) { auto const [f, d] = m_fmls[i](); todo.push_back(f); pin.push_back(f); diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index 3feb8647410..d20b4a6c54f 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -29,7 +29,7 @@ void card2bv::reduce() { expr_ref new_f1(m), new_f2(m); proof_ref new_pr(m); - for (unsigned idx = m_fmls.qhead(); !m_fmls.inconsistent() && idx < m_fmls.size(); idx++) { + for (unsigned idx = qhead(); !m_fmls.inconsistent() && idx < qtail(); idx++) { auto [f, d] = m_fmls[idx](); rw1(f, new_f1); rw2(false, new_f1, new_f2, new_pr); diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp new file mode 100644 index 00000000000..5219d25da78 --- /dev/null +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -0,0 +1,109 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + dependent_expr_state.cpp + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-2. + +--*/ + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/recfun_decl_plugin.h" +#include "ast/for_each_ast.h" + +unsigned dependent_expr_state::num_exprs() { + expr_fast_mark1 visited; + unsigned r = 0; + for (unsigned i = 0; i < qtail(); i++) + r += get_num_exprs((*this)[i].fml(), visited); + return r; +} + +void dependent_expr_state::freeze(func_decl* f) { + if (m_frozen.is_marked(f)) + return; + m_frozen_trail.push_back(f); + m_frozen.mark(f, true); +} + +void dependent_expr_state::freeze(expr* term) { + if (is_app(term)) + freeze(to_app(term)->get_decl()); +} + +/** +* Freeze functions appearing as sub-expressions of 'e'. +* The only_as_array flag indicates whether to only freeze occurrences of as-array +* from elimination. +*/ +void dependent_expr_state::freeze_terms(expr* e, bool only_as_array, ast_mark& visited) { + auto& m = m_frozen_trail.get_manager(); + struct proc { + bool only_as_array; + array_util a; + dependent_expr_state& st; + proc(ast_manager& m, bool o, dependent_expr_state& d) : + only_as_array(o), a(m), st(d) {} + void operator()(func_decl* f) { + if (!only_as_array) + st.freeze(f); + if (a.is_as_array(f, f) && is_uninterp(f)) + st.freeze(f); + } + void operator()(ast* s) {} + }; + proc proc(m, only_as_array, *this); + for_each_ast(proc, visited, e); +} + +/** +* Freeze all functions used in recursive definitions +*/ + +void dependent_expr_state::freeze_recfun() { + if (m_recfun_frozen) + return; + m_recfun_frozen = true; + auto& m = m_frozen_trail.get_manager(); + recfun::util rec(m); + ast_mark visited; + for (func_decl* f : rec.get_rec_funs()) + freeze_terms(rec.get_def(f).get_rhs(), false, visited); +} + +/** +* The current qhead is to be updated to qtail. +* Before this update, freeze all functions appearing in formulas. +*/ +void dependent_expr_state::freeze_prefix() { + ast_mark visited; + for (unsigned i = qhead(); i < qtail(); ++i) + freeze_terms((*this)[i].fml(), false, visited); +} + +/** +* Freeze functions in the unprocessed suffix that appear in dependencies and in as-array. +*/ +void dependent_expr_state::freeze_suffix() { + if (m_suffix_frozen) + return; + m_suffix_frozen = true; + auto& m = m_frozen_trail.get_manager(); + freeze_recfun(); + ast_mark visited; + ptr_vector es; + for (unsigned i = qhead(); i < qtail(); ++i) { + auto d = (*this)[i]; + if (d.dep()) { + es.reset(); + m.linearize(d.dep(), es); + for (expr* e : es) + freeze_terms(e, false, visited); + } + freeze_terms(d.fml(), true, visited); + } +} diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index a65492a5838..4c9b21f0afb 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -42,27 +42,54 @@ Module Name: */ class dependent_expr_state { unsigned m_qhead = 0; + bool m_suffix_frozen = false; + bool m_recfun_frozen = false; + ast_mark m_frozen; + func_decl_ref_vector m_frozen_trail; + void freeze_prefix(); + void freeze_recfun(); + void freeze_terms(expr* term, bool only_as_array, ast_mark& visited); + void freeze(expr* term); + void freeze(func_decl* f); + struct thaw : public trail { + unsigned sz; + dependent_expr_state& st; + thaw(dependent_expr_state& st) : sz(st.m_frozen_trail.size()), st(st) {} + void undo() override { + for (unsigned i = st.m_frozen_trail.size(); i-- > sz; ) + st.m_frozen.mark(st.m_frozen_trail.get(i), false); + st.m_frozen_trail.shrink(sz); + } + }; public: + dependent_expr_state(ast_manager& m) : m_frozen_trail(m) {} virtual ~dependent_expr_state() {} - virtual unsigned size() const = 0; + unsigned qhead() const { return m_qhead; } + virtual unsigned qtail() const = 0; virtual dependent_expr const& operator[](unsigned i) = 0; virtual void update(unsigned i, dependent_expr const& j) = 0; virtual void add(dependent_expr const& j) = 0; virtual bool inconsistent() = 0; virtual model_reconstruction_trail& model_trail() = 0; + virtual void flatten_suffix() {} trail_stack m_trail; - void push() { m_trail.push_scope(); } - void pop(unsigned n) { m_trail.pop_scope(n); } - unsigned qhead() const { return m_qhead; } - void advance_qhead() { if (m_trail.get_num_scopes() > 0) m_trail.push(value_trail(m_qhead)); m_qhead = size(); } - unsigned num_exprs() { - expr_fast_mark1 visited; - unsigned r = 0; - for (unsigned i = 0; i < size(); i++) - r += get_num_exprs((*this)[i].fml(), visited); - return r; + void push() { + m_trail.push_scope(); + m_trail.push(value_trail(m_qhead)); + m_trail.push(thaw(*this)); } + void pop(unsigned n) { m_trail.pop_scope(n); } + + void advance_qhead() { freeze_prefix(); m_suffix_frozen = false; m_qhead = qtail(); } + unsigned num_exprs(); + + /** + * Freeze internal functions + */ + bool frozen(func_decl* f) const { return m_frozen.is_marked(f); } + bool frozen(expr* f) const { return is_app(f) && m_frozen.is_marked(to_app(f)->get_decl()); } + void freeze_suffix(); }; /** @@ -76,6 +103,9 @@ class dependent_expr_simplifier { unsigned num_scopes() const { return m_trail.get_num_scopes(); } + unsigned qhead() const { return m_fmls.qhead(); } + unsigned qtail() const { return m_fmls.qtail(); } + public: dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {} virtual ~dependent_expr_simplifier() {} diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 93a84dba644..41305b74520 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -48,7 +48,7 @@ Module Name: elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m) { std::function is_var = [&](expr* e) { - return is_uninterp_const(e) && !m_frozen.is_marked(e) && get_node(e).m_refcount <= 1; + return is_uninterp_const(e) && !m_fmls.frozen(e) && get_node(e).m_refcount <= 1; }; m_inverter.set_is_var(is_var); } @@ -121,12 +121,14 @@ expr* elim_unconstrained::get_parent(unsigned n) const { * initialize node structure */ void elim_unconstrained::init_nodes() { + + m_fmls.freeze_suffix(); + expr_ref_vector terms(m); - for (unsigned i = 0; i < m_fmls.size(); ++i) + for (unsigned i = qhead(); i < qtail(); ++i) terms.push_back(m_fmls[i].fml()); m_trail.append(terms); m_heap.reset(); - m_frozen.reset(); m_root.reset(); // initialize nodes for terms in the original goal @@ -135,23 +137,6 @@ void elim_unconstrained::init_nodes() { // top-level terms have reference count > 0 for (expr* e : terms) inc_ref(e); - - // freeze subterms before the already processed head - terms.reset(); - for (unsigned i = 0; i < m_fmls.qhead(); ++i) - terms.push_back(m_fmls[i].fml()); - for (expr* e : subterms::all(terms)) - m_frozen.mark(e, true); - - // freeze subterms that occur with recursive function definitions - recfun::util rec(m); - if (rec.has_rec_defs()) { - for (func_decl* f : rec.get_rec_funs()) { - expr* rhs = rec.get_def(f).get_rhs(); - for (expr* t : subterms::all(expr_ref(rhs, m))) - m_frozen.mark(t); - } - } } /** @@ -216,7 +201,7 @@ void elim_unconstrained::gc(expr* t) { */ void elim_unconstrained::reconstruct_terms() { expr_ref_vector terms(m); - for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) + for (unsigned i = qhead(); i < qtail(); ++i) terms.push_back(m_fmls[i].fml()); for (expr* e : subterms_postorder::all(terms)) { @@ -249,8 +234,8 @@ void elim_unconstrained::reconstruct_terms() { void elim_unconstrained::assert_normalized(vector& old_fmls) { - unsigned sz = m_fmls.size(); - for (unsigned i = m_fmls.qhead(); i < sz; ++i) { + unsigned sz = qtail(); + for (unsigned i = qhead(); i < sz; ++i) { auto [f, d] = m_fmls[i](); node& n = get_node(f); expr* g = n.m_term; diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index b66d0b72748..9da0f3fc205 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -45,7 +45,6 @@ class elim_unconstrained : public dependent_expr_simplifier { heap m_heap; expr_ref_vector m_trail; ptr_vector m_args; - expr_mark m_frozen; stats m_stats; unsigned_vector m_root; diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index fa615048f5b..d71d2180af6 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -109,7 +109,7 @@ bool eliminate_predicates::can_be_macro_head(expr* _head, unsigned num_bound) { return false; app* head = to_app(_head); func_decl* f = head->get_decl(); - if (m_disable_macro.is_marked(f)) + if (m_fmls.frozen(f)) return false; if (m_is_macro.is_marked(f)) return false; @@ -157,7 +157,7 @@ expr_ref eliminate_predicates::bind_free_variables_in_def(clause& cl, app* head, * (or (not (head x)) (def x)) */ bool eliminate_predicates::try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep) { - if (m_disable_macro.is_marked(p)) + if (m_fmls.frozen(p)) return false; expr_mark binary_pos, binary_neg; obj_map deps; @@ -501,7 +501,7 @@ void eliminate_predicates::reduce_definitions() { for (auto const& [k, v] : m_macros) macro_expander.insert(v->m_head, v->m_def, v->m_dep); - for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { + for (unsigned i = qhead(); i < qtail(); ++i) { auto [f, d] = m_fmls[i](); expr_ref fml(f, m), new_fml(m); expr_dependency_ref dep(m); @@ -524,7 +524,7 @@ void eliminate_predicates::reduce_definitions() { void eliminate_predicates::try_resolve(func_decl* p) { if (m_disable_elimination.is_marked(p)) return; - if (m_disable_macro.is_marked(p)) + if (m_fmls.frozen(p)) return; unsigned num_pos = 0, num_neg = 0; @@ -717,30 +717,20 @@ void eliminate_predicates::try_resolve() { /** * Process the terms m_to_exclude, walk all subterms. * Uninterpreted function declarations in these terms are added to 'exclude_set' -* Uninterpreted function declarations from as-array terms are added to 'm_disable_macro' */ void eliminate_predicates::process_to_exclude(ast_mark& exclude_set) { ast_mark visited; - array_util a(m); - struct proc { - array_util& a; ast_mark& to_exclude; - ast_mark& to_disable; - proc(array_util& a, ast_mark& f, ast_mark& d) : - a(a), to_exclude(f), to_disable(d) {} + proc(ast_mark& f) : + to_exclude(f) {} void operator()(func_decl* f) { if (is_uninterp(f)) to_exclude.mark(f, true); } - void operator()(app* e) { - func_decl* f; - if (a.is_as_array(e, f) && is_uninterp(f)) - to_disable.mark(f, true); - } void operator()(ast* s) {} }; - proc proc(a, exclude_set, m_disable_macro); + proc proc(exclude_set); for (expr* e : m_to_exclude) for_each_ast(proc, visited, e); @@ -779,16 +769,10 @@ eliminate_predicates::clause* eliminate_predicates::init_clause(expr* f, expr_de * eliminations. */ void eliminate_predicates::init_clauses() { - for (unsigned i = 0; i < m_fmls.qhead(); ++i) - m_to_exclude.push_back(m_fmls[i].fml()); - recfun::util rec(m); - if (rec.has_rec_defs()) - for (auto& d : rec.get_rec_funs()) - m_to_exclude.push_back(rec.get_def(d).get_rhs()); - - process_to_exclude(m_disable_macro); - for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) { + m_fmls.freeze_suffix(); + + for (unsigned i = qhead(); i < qtail(); ++i) { clause* cl = init_clause(i); add_use_list(*cl); m_clauses.push_back(cl); @@ -821,7 +805,6 @@ void eliminate_predicates::reset() { m_predicates.reset(); m_predicate_decls.reset(); m_to_exclude.reset(); - m_disable_macro.reset(); m_disable_elimination.reset(); m_is_macro.reset(); for (auto const& [k, v] : m_macros) diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index ca4110e99f2..a8d4fff489b 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -88,7 +88,7 @@ class eliminate_predicates : public dependent_expr_simplifier { }; scoped_ptr_vector m_clauses; - ast_mark m_disable_elimination, m_disable_macro, m_predicate_decls, m_is_macro; + ast_mark m_disable_elimination, m_predicate_decls, m_is_macro; ptr_vector m_predicates; ptr_vector m_to_exclude; stats m_stats; diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index f077f19182d..78be41c1adc 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -74,13 +74,13 @@ namespace euf { void completion::add_egraph() { m_nodes_to_canonize.reset(); - unsigned sz = m_fmls.size(); + unsigned sz = qtail(); auto add_children = [&](enode* n) { for (auto* ch : enode_args(n)) m_nodes_to_canonize.push_back(ch); }; - for (unsigned i = m_fmls.qhead(); i < sz; ++i) { + for (unsigned i = qhead(); i < sz; ++i) { expr* x, * y; auto [f, d] = m_fmls[i](); if (m.is_eq(f, x, y)) { @@ -113,8 +113,8 @@ namespace euf { return; } - unsigned sz = m_fmls.size(); - for (unsigned i = m_fmls.qhead(); i < sz; ++i) { + unsigned sz = qtail(); + for (unsigned i = qhead(); i < sz; ++i) { auto [f, d] = m_fmls[i](); expr_dependency_ref dep(d, m); diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index d65f9b16757..fdb889ee3ac 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -258,7 +258,7 @@ namespace euf { if (!m_enabled) return; m_nonzero.reset(); - for (unsigned i = 0; i < fmls.size(); ++i) + for (unsigned i = 0; i < fmls.qtail(); ++i) add_pos(fmls[i].fml()); } diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index 4744c473c5d..3ac27302a8d 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -255,7 +255,7 @@ class max_bv_sharing : public dependent_expr_simplifier { void reduce() override { expr_ref new_curr(m); proof_ref new_pr(m); - for (unsigned idx = m_fmls.qhead(); idx < m_fmls.size() && !m_fmls.inconsistent(); idx++) { + for (unsigned idx = qhead(); idx < qtail() && !m_fmls.inconsistent(); idx++) { auto [curr, d] = m_fmls[idx](); m_rw(curr, new_curr, new_pr); // Proof reconstruction: new_pr = m.mk_modus_ponens(old_pr, new_pr); diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 19c7d93811e..18af7449fd8 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -26,7 +26,7 @@ Module Name: void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st) { ast_mark free_vars; scoped_ptr rp = mk_default_expr_replacer(m, false); - for (unsigned i = qhead; i < st.size(); ++i) + for (unsigned i = qhead; i < st.qtail(); ++i) add_vars(st[i], free_vars); for (auto& t : m_trail) { @@ -64,7 +64,7 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st dependent_expr de(m, t->m_def, t->m_dep); add_vars(de, free_vars); - for (unsigned i = qhead; i < st.size(); ++i) { + for (unsigned i = qhead; i < st.qtail(); ++i) { auto [f, dep1] = st[i](); expr_ref g(m); expr_dependency_ref dep2(m); @@ -77,7 +77,7 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st rp->set_substitution(t->m_subst.get()); // rigid entries: // apply substitution to added in case of rigid model convertions - for (unsigned i = qhead; i < st.size(); ++i) { + for (unsigned i = qhead; i < st.qtail(); ++i) { auto [f, dep1] = st[i](); auto [g, dep2] = rp->replace_with_dep(f); dependent_expr d(m, g, m.mk_join(dep1, dep2)); diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 2572cc31002..928e8a7870a 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -41,7 +41,7 @@ void propagate_values::reduce() { auto add_shared = [&]() { shared_occs_mark visited; shared.reset(); - for (unsigned i = 0; i < m_fmls.size(); ++i) + for (unsigned i = 0; i < qtail(); ++i) shared(m_fmls[i].fml(), visited); }; @@ -78,7 +78,7 @@ void propagate_values::reduce() { subst.reset(); m_rewriter.reset(); m_rewriter.set_substitution(&subst); - for (unsigned i = 0; i < m_fmls.qhead(); ++i) + for (unsigned i = 0; i < qhead(); ++i) add_sub(m_fmls[i]); }; @@ -86,10 +86,10 @@ void propagate_values::reduce() { for (unsigned r = 0; r < m_max_rounds && rw != m_stats.m_num_rewrites; ++r) { rw = m_stats.m_num_rewrites; init_sub(); - for (unsigned i = m_fmls.qhead(); i < m_fmls.size() && !m_fmls.inconsistent(); ++i) + for (unsigned i = qhead(); i < qtail() && !m_fmls.inconsistent(); ++i) process_fml(i); init_sub(); - for (unsigned i = m_fmls.size(); i-- > m_fmls.qhead() && !m_fmls.inconsistent();) + for (unsigned i = qtail(); i-- > qhead() && !m_fmls.inconsistent();) process_fml(i); if (subst.empty()) break; diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h index f956ec80828..bb9fd0d22ae 100644 --- a/src/ast/simplifiers/rewriter_simplifier.h +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -40,7 +40,7 @@ class rewriter_simplifier : public dependent_expr_simplifier { m_num_steps = 0; expr_ref new_curr(m); proof_ref new_pr(m); - for (unsigned idx = m_fmls.qhead(); idx < m_fmls.size(); idx++) { + for (unsigned idx = qhead(); idx < qtail(); idx++) { if (m_fmls.inconsistent()) break; auto d = m_fmls[idx]; diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h index 0fa5990ddc4..326b3d09e09 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/seq_simplifier.h @@ -70,6 +70,7 @@ class seq_simplifier : public dependent_expr_simplifier { break; collect_stats _cs(*s); s->reduce(); + m_fmls.flatten_suffix(); } } diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index 7eaa8ad2fc9..e8738182507 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -42,7 +42,7 @@ namespace euf { 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) + for (unsigned i = 0; i < m_fmls.qtail(); ++i) if (!is_safe_eq(m_fmls[i].fml(), e)) return false; return true; @@ -147,7 +147,7 @@ namespace euf { void solve_context_eqs::collect_nested_equalities(dep_eq_vector& eqs) { expr_mark visited; - unsigned sz = m_fmls.size(); + unsigned sz = m_fmls.qtail(); for (unsigned i = m_fmls.qhead(); i < sz; ++i) collect_nested_equalities(m_fmls[i], visited, eqs); diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index f6055af50bb..e2fbc0c9a9a 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -34,7 +34,7 @@ namespace euf { void solve_eqs::get_eqs(dep_eq_vector& eqs) { for (extract_eq* ex : m_extract_plugins) - for (unsigned i = m_fmls.qhead(); i < m_fmls.size(); ++i) + for (unsigned i = qhead(); i < qtail(); ++i) ex->get_eqs(m_fmls[i], eqs); } @@ -187,7 +187,7 @@ namespace euf { scoped_ptr rp = mk_default_expr_replacer(m, false); rp->set_substitution(m_subst.get()); - for (unsigned i = m_fmls.qhead(); i < m_fmls.size() && !m_fmls.inconsistent(); ++i) { + for (unsigned i = qhead(); i < qtail() && !m_fmls.inconsistent(); ++i) { auto [f, d] = m_fmls[i](); auto [new_f, new_dep] = rp->replace_with_dep(f); m_rewriter(new_f); diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 91a2bfec26f..69cc7ed17ad 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -17,9 +17,6 @@ Module Name: Notes: - - proper handling of dependencies + pre-processing - - literals used in dependencies should not be eliminated by pre-processing routines - This has to be enforced. - add translation for preprocess state. - If the pre-processors are stateful, they need to be properly translated. - add back get_consequences, maybe or just have them handled by inc_sat_solver @@ -55,9 +52,9 @@ class sat_smt_solver : public solver { struct dep_expr_state : public dependent_expr_state { sat_smt_solver& s; model_reconstruction_trail m_reconstruction_trail; - dep_expr_state(sat_smt_solver& s):s(s), m_reconstruction_trail(s.m, m_trail) {} + dep_expr_state(sat_smt_solver& s):dependent_expr_state(s.m), s(s), m_reconstruction_trail(s.m, m_trail) {} ~dep_expr_state() override {} - virtual unsigned size() const override { return s.m_fmls.size(); } + virtual unsigned qtail() const override { return s.m_fmls.size(); } dependent_expr const& operator[](unsigned i) override { return s.m_fmls[i]; } void update(unsigned i, dependent_expr const& j) override { s.m_fmls[i] = j; } void add(dependent_expr const& j) override { s.m_fmls.push_back(j); } @@ -65,6 +62,28 @@ class sat_smt_solver : public solver { model_reconstruction_trail& model_trail() override { return m_reconstruction_trail; } void append(generic_model_converter& mc) { model_trail().append(mc); } void replay(unsigned qhead) { m_reconstruction_trail.replay(qhead, *this); } + void flatten_suffix() override { + expr_mark seen; + unsigned j = qhead(); + for (unsigned i = qhead(); i < qtail(); ++i) { + expr* f = s.m_fmls[i].fml(); + if (seen.is_marked(f)) + continue; + seen.mark(f, true); + if (s.m.is_true(f)) + continue; + if (s.m.is_and(f)) { + auto* d = s.m_fmls[i].dep(); + for (expr* arg : *to_app(f)) + s.m_fmls.push_back(dependent_expr(s.m, arg, d)); + continue; + } + if (i != j) + s.m_fmls[j] = s.m_fmls[i]; + ++j; + } + s.m_fmls.shrink(j); + } }; struct dependency2assumptions { @@ -253,7 +272,6 @@ class sat_smt_solver : public solver { } void push_internal() { - m_trail.push_scope(); m_solver.user_push(); m_goal2sat.user_push(); m_map.push(); @@ -272,7 +290,6 @@ class sat_smt_solver : public solver { m_map.pop(n); m_goal2sat.user_pop(n); m_solver.user_pop(n); - m_trail.pop_scope(n); m_mc->shrink(m_mc_size); } diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index dda114c24dc..3afc0fc9fe7 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -42,6 +42,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { public: dependent_expr_state_tactic(ast_manager& m, params_ref const& p, dependent_expr_simplifier_factory* f): + dependent_expr_state(m), m(m), m_params(p), m_factory(f), @@ -52,7 +53,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { /** * size(), [](), update() and inconsisent() implement the abstract interface of dependent_expr_state */ - unsigned size() const override { return m_goal->size(); } + unsigned qtail() 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)); @@ -60,11 +61,15 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { } void update(unsigned i, dependent_expr const& j) override { + if (inconsistent()) + return; auto [f, d] = j(); m_goal->update(i, f, nullptr, d); } void add(dependent_expr const& j) override { + if (inconsistent()) + return; auto [f, d] = j(); m_goal->assert_expr(f, nullptr, d); } From b084821a0cbf2dca5c14872be9686bc6e261ffe4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 13:41:40 +0700 Subject: [PATCH 141/597] wip - dependent expr simpliifer - simplify iterator over current indices - add more simplifiers used by asserted_formulas - improve diagnostics printing --- src/ast/rewriter/macro_replacer.cpp | 23 ++- src/ast/rewriter/macro_replacer.h | 5 +- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/bit2int.h | 44 ++++++ src/ast/simplifiers/bv_elim.h | 43 ++++++ src/ast/simplifiers/dependent_expr.h | 17 ++ src/ast/simplifiers/dependent_expr_state.cpp | 12 +- src/ast/simplifiers/dependent_expr_state.h | 31 +++- src/ast/simplifiers/distribute_forall.h | 45 ++++++ src/ast/simplifiers/elim_bounds.h | 45 ++++++ src/ast/simplifiers/eliminate_predicates.cpp | 57 +++++-- src/ast/simplifiers/eliminate_predicates.h | 3 +- .../model_reconstruction_trail.cpp | 39 ++++- .../simplifiers/model_reconstruction_trail.h | 2 + src/ast/simplifiers/propagate_values.cpp | 6 +- src/ast/simplifiers/pull_nested_quantifiers.h | 46 ++++++ src/ast/simplifiers/refine_inj_axiom.h | 44 ++++++ src/ast/simplifiers/rewriter_simplifier.h | 6 +- src/ast/simplifiers/seq_simplifier.h | 6 +- src/ast/simplifiers/solve_eqs.cpp | 7 +- src/ast/simplifiers/solve_eqs.h | 6 + src/qe/lite/qe_lite.cpp | 146 +++++------------- src/qe/lite/qe_lite.h | 3 + src/sat/sat_solver/sat_smt_preprocess.cpp | 38 ++++- src/sat/sat_solver/sat_smt_solver.cpp | 34 ++-- 25 files changed, 552 insertions(+), 157 deletions(-) create mode 100644 src/ast/simplifiers/bit2int.h create mode 100644 src/ast/simplifiers/bv_elim.h create mode 100644 src/ast/simplifiers/distribute_forall.h create mode 100644 src/ast/simplifiers/elim_bounds.h create mode 100644 src/ast/simplifiers/pull_nested_quantifiers.h create mode 100644 src/ast/simplifiers/refine_inj_axiom.h diff --git a/src/ast/rewriter/macro_replacer.cpp b/src/ast/rewriter/macro_replacer.cpp index da0131bf7d7..d8389ae6af3 100644 --- a/src/ast/rewriter/macro_replacer.cpp +++ b/src/ast/rewriter/macro_replacer.cpp @@ -125,9 +125,28 @@ void macro_replacer::insert(app* head, expr* def, expr_dependency* dep) { m_map.insert(f, std::tuple(head, def, dep)); } -void macro_replacer::operator()(expr* t, expr_ref& result, expr_dependency_ref& dep) { - macro_replacer_rw exp(m, *this, dep); +void macro_replacer::operator()(expr* t, expr_dependency* dep_in, expr_ref& result, expr_dependency_ref& dep_out) { + expr_dependency_ref _dep_in(dep_in, m); + macro_replacer_rw exp(m, *this, dep_out); exp(t, result); + if (!dep_in) + return; + // update dependencies if needed + m_dep_exprs.reset(); + m.linearize(dep_in, m_dep_exprs); + unsigned sz = m_trail.size(); + for (expr*& d : m_dep_exprs) { + exp(d, result); + if (result != d) { + d = result.get(); + m_trail.push_back(result); + } + } + if (sz != m_trail.size()) { + dep_in = m.mk_join(m_dep_exprs.size(), m_dep_exprs.data()); + m_trail.shrink(sz); + } + dep_out = m.mk_join(dep_in, dep_out); } bool macro_replacer::has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& dep) { diff --git a/src/ast/rewriter/macro_replacer.h b/src/ast/rewriter/macro_replacer.h index a0cc5242b07..8513a7549e9 100644 --- a/src/ast/rewriter/macro_replacer.h +++ b/src/ast/rewriter/macro_replacer.h @@ -26,6 +26,7 @@ class macro_replacer { ast_manager& m; ast_ref_vector m_trail; expr_dependency_ref_vector m_deps; + ptr_vector m_dep_exprs; obj_map> m_map; struct macro_replacer_cfg; struct macro_replacer_rw; @@ -35,8 +36,8 @@ class macro_replacer { macro_replacer(ast_manager& m): m(m), m_trail(m), m_deps(m) {} void insert(app* head, expr* def, expr_dependency* dep); - void operator()(expr* t, expr_ref& result, expr_dependency_ref& dep); - void operator()(expr* t, expr_ref & result) { expr_dependency_ref dep(m); (*this)(t, result, dep); } + void operator()(expr* t, expr_dependency* d, expr_ref& result, expr_dependency_ref& dep); + void operator()(expr* t, expr_ref & result) { expr_dependency_ref dep(m); (*this)(t, nullptr, result, dep); } void operator()(expr_ref & t) { expr_ref s(t, m); (*this)(s, t); } bool has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& d); diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 9d894627aa6..c6a8469ee99 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -17,4 +17,5 @@ z3_add_component(simplifiers euf rewriter bit_blaster + normal_forms ) diff --git a/src/ast/simplifiers/bit2int.h b/src/ast/simplifiers/bit2int.h new file mode 100644 index 00000000000..7d07029d225 --- /dev/null +++ b/src/ast/simplifiers/bit2int.h @@ -0,0 +1,44 @@ + +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bit2int.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/bit2int.h" + + +class bit2int_simplifier : public dependent_expr_simplifier { + bit2int m_rewriter; + +public: + bit2int_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_rewriter(m) { + } + + char const* name() const override { return "bit2int"; } + + void reduce() override { + expr_ref r(m); + proof_ref pr(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + if (!has_quantifiers(d.fml())) + continue; + m_rewriter(d.fml(), r, pr); + m_fmls.update(idx, dependent_expr(m, r, d.dep())); + } + } +}; + diff --git a/src/ast/simplifiers/bv_elim.h b/src/ast/simplifiers/bv_elim.h new file mode 100644 index 00000000000..6f045fc5420 --- /dev/null +++ b/src/ast/simplifiers/bv_elim.h @@ -0,0 +1,43 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bv_elim.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/bv_elim.h" + + +namespace bv { +class elim_simplifier : public dependent_expr_simplifier { + bv_elim_rw m_rewriter; + +public: + elim_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_rewriter(m) { + } + + char const* name() const override { return "bv-elim"; } + + void reduce() override { + expr_ref r(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + if (!has_quantifiers(d.fml())) + continue; + m_rewriter(d.fml(), r); + m_fmls.update(idx, dependent_expr(m, r, d.dep())); + } + } +}; +} diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h index 4d16bc2bb67..ddf119070e0 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -18,6 +18,7 @@ Module Name: #pragma once #include "ast/ast.h" +#include "ast/ast_pp.h" #include "ast/ast_translation.h" class dependent_expr { @@ -88,4 +89,20 @@ class dependent_expr { std::tuple operator()() const { return { m_fml, m_dep }; } + + std::ostream& display(std::ostream& out) const { + return out << mk_pp(m_fml, m); + if (m_dep) { + out << "\n <- "; + ptr_vector deps; + m.linearize(m_dep, deps); + for (expr* arg : deps) + out << mk_pp(arg, m) << " "; + } + return out; + } }; + +inline std::ostream& operator<<(std::ostream& out, dependent_expr const& d) { + return d.display(out); +} \ No newline at end of file diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp index 5219d25da78..40528fe99cf 100644 --- a/src/ast/simplifiers/dependent_expr_state.cpp +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -24,7 +24,7 @@ unsigned dependent_expr_state::num_exprs() { } void dependent_expr_state::freeze(func_decl* f) { - if (m_frozen.is_marked(f)) + if (m_frozen.is_marked(f) || !is_uninterp(f)) return; m_frozen_trail.push_back(f); m_frozen.mark(f, true); @@ -107,3 +107,13 @@ void dependent_expr_state::freeze_suffix() { freeze_terms(d.fml(), true, visited); } } + +bool dependent_expr_state::has_quantifiers() { + if (m_has_quantifiers != l_undef) + return m_has_quantifiers == l_true; + bool found = false; + for (unsigned i = qhead(); i < qtail(); ++i) + found |= ::has_quantifiers((*this)[i].fml()); + m_has_quantifiers = found ? l_true : l_false; + return m_has_quantifiers == l_true; +} diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 4c9b21f0afb..d8539d60485 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -44,6 +44,7 @@ class dependent_expr_state { unsigned m_qhead = 0; bool m_suffix_frozen = false; bool m_recfun_frozen = false; + lbool m_has_quantifiers = l_undef; ast_mark m_frozen; func_decl_ref_vector m_frozen_trail; void freeze_prefix(); @@ -81,7 +82,7 @@ class dependent_expr_state { } void pop(unsigned n) { m_trail.pop_scope(n); } - void advance_qhead() { freeze_prefix(); m_suffix_frozen = false; m_qhead = qtail(); } + void advance_qhead() { freeze_prefix(); m_suffix_frozen = false; m_has_quantifiers = l_undef; m_qhead = qtail(); } unsigned num_exprs(); /** @@ -90,8 +91,16 @@ class dependent_expr_state { bool frozen(func_decl* f) const { return m_frozen.is_marked(f); } bool frozen(expr* f) const { return is_app(f) && m_frozen.is_marked(to_app(f)->get_decl()); } void freeze_suffix(); + + virtual std::ostream& display(std::ostream& out) const { return out; } + + bool has_quantifiers(); }; +inline std::ostream& operator<<(std::ostream& out, dependent_expr_state& st) { + return st.display(out); +} + /** Shared interface of simplifiers. */ @@ -105,6 +114,26 @@ class dependent_expr_simplifier { unsigned qhead() const { return m_fmls.qhead(); } unsigned qtail() const { return m_fmls.qtail(); } + struct iterator { + dependent_expr_simplifier& s; + unsigned m_index = 0; + bool at_end = false; + unsigned index() const { return at_end ? s.qtail() : m_index; } + iterator(dependent_expr_simplifier& s, unsigned i) : s(s), m_index(i), at_end(i == s.qtail()) {} + bool operator==(iterator const& other) const { return index() == other.index(); } + bool operator!=(iterator const& other) const { return !(*this == other); } + iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent()) at_end = true; else ++m_index; return *this; } + unsigned operator*() const { return m_index; } + }; + + struct index_set { + dependent_expr_simplifier& s; + iterator begin() { return iterator(s, s.qhead()); } + iterator end() { return iterator(s, s.qtail()); } + index_set(dependent_expr_simplifier& s) : s(s) {} + }; + + index_set indices() { return index_set(*this); } public: dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {} diff --git a/src/ast/simplifiers/distribute_forall.h b/src/ast/simplifiers/distribute_forall.h new file mode 100644 index 00000000000..82709d29f72 --- /dev/null +++ b/src/ast/simplifiers/distribute_forall.h @@ -0,0 +1,45 @@ + +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + distribute_forall.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/distribute_forall.h" + + +class distribute_forall_simplifier : public dependent_expr_simplifier { + distribute_forall m_dist; + +public: + distribute_forall_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_dist(m) { + } + + char const* name() const override { return "distribute-forall"; } + + void reduce() override { + if (!m_fmls.has_quantifiers()) + return; + expr_ref r(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + if (!has_quantifiers(d.fml())) + continue; + m_dist(d.fml(), r); + m_fmls.update(idx, dependent_expr(m, r, d.dep())); + } + } +}; + diff --git a/src/ast/simplifiers/elim_bounds.h b/src/ast/simplifiers/elim_bounds.h new file mode 100644 index 00000000000..d6631adfdd4 --- /dev/null +++ b/src/ast/simplifiers/elim_bounds.h @@ -0,0 +1,45 @@ + +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + elim_bounds.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/elim_bounds.h" + + +class elim_bounds_simplifier : public dependent_expr_simplifier { + elim_bounds_rw m_rewriter; + +public: + elim_bounds_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_rewriter(m) { + } + + char const* name() const override { return "cheap-fourier-motzkin"; } + + void reduce() override { + if (!m_fmls.has_quantifiers()) + return; + expr_ref r(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + if (!has_quantifiers(d.fml())) + continue; + m_rewriter(d.fml(), r); + m_fmls.update(idx, dependent_expr(m, r, d.dep())); + } + } +}; + diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index d71d2180af6..eb7006da6c5 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -129,6 +129,38 @@ bool eliminate_predicates::can_be_macro_head(expr* _head, unsigned num_bound) { return true; } +/** + * a quasi macro head is of the form + * f(x,x) where x is the only bound variable + * f(x,y,x+y+3,1) where x, y are the only bound variables + */ + +bool eliminate_predicates::can_be_quasi_macro_head(expr* head, unsigned num_bound) { + if (!is_app(_head)) + return false; + app* head = to_app(_head); + func_decl* f = head->get_decl(); + if (m_fmls.frozen(f)) + return false; + if (m_is_macro.is_marked(f)) + return false; + if (f->is_associative()) + return false; + uint_set indices; + for (expr* arg : *head) { + if (!is_var(arg)) + return continue; + unsigned idx = to_var(arg)->get_idx(); + if (indices.contains(idx)) + return continue; + if (idx >= num_bound) + return false; + indices.insert(idx); + } + return indices.size() == num_bound; +} + + expr_ref eliminate_predicates::bind_free_variables_in_def(clause& cl, app* head, expr* def) { unsigned num_bound = cl.m_bound.size(); if (head->get_num_args() == num_bound) @@ -365,7 +397,6 @@ void eliminate_predicates::try_find_macro(clause& cl) { // (= (+ (f x) s) t) // becomes (= (f x) (- t s)) // - // TBD: // (= (+ (* -1 (f x)) x) t) // becomes (= (f x) (- (- t s))) @@ -473,10 +504,13 @@ void eliminate_predicates::try_find_macro(clause& cl) { // becomes (= (f x) (- t s (k x)) // add (>= (k x) 0) // why is this a real improvement? - // + // + + // // To review: quasi-macros - // (= (f x y (+ x y)) s), where x y are all bound variables. - // then ...? + // (= (f x y (+ x y)) s), where x y are all bound variables. + // then replace (f x y z) by (if (= z (+ x y)) s (f' x y)) + // } @@ -501,21 +535,18 @@ void eliminate_predicates::reduce_definitions() { for (auto const& [k, v] : m_macros) macro_expander.insert(v->m_head, v->m_def, v->m_dep); - for (unsigned i = qhead(); i < qtail(); ++i) { + for (unsigned i : indices()) { auto [f, d] = m_fmls[i](); expr_ref fml(f, m), new_fml(m); - expr_dependency_ref dep(m); + expr_dependency_ref dep(d, m); while (true) { - macro_expander(fml, new_fml, dep); + macro_expander(fml, dep, new_fml, dep); if (new_fml == fml) break; rewrite(new_fml); fml = new_fml; } - if (fml != f) { - dep = m.mk_join(d, dep); - m_fmls.update(i, dependent_expr(m, fml, dep)); - } + m_fmls.update(i, dependent_expr(m, fml, dep)); } reset(); init_clauses(); @@ -772,7 +803,7 @@ void eliminate_predicates::init_clauses() { m_fmls.freeze_suffix(); - for (unsigned i = qhead(); i < qtail(); ++i) { + for (unsigned i : indices()) { clause* cl = init_clause(i); add_use_list(*cl); m_clauses.push_back(cl); @@ -816,6 +847,8 @@ void eliminate_predicates::reset() { void eliminate_predicates::reduce() { + if (!m_fmls.has_quantifiers()) + return; reset(); init_clauses(); find_definitions(); diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index a8d4fff489b..d91922f9e5d 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -109,6 +109,7 @@ class eliminate_predicates : public dependent_expr_simplifier { void insert_macro(app* head, expr* def, expr_dependency* dep); expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); bool can_be_macro_head(expr* head, unsigned num_bound); + bool can_be_quasi_macro_head(expr* head, unsigned num_bound); bool is_macro_safe(expr* e); void try_find_macro(clause& cl); @@ -145,4 +146,4 @@ class eliminate_predicates : public dependent_expr_simplifier { inline std::ostream& operator<<(std::ostream& out, eliminate_predicates::clause const& c) { return c.display(out); -} \ No newline at end of file +} diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 18af7449fd8..0436822d136 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -68,8 +68,9 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st auto [f, dep1] = st[i](); expr_ref g(m); expr_dependency_ref dep2(m); - mrp(f, g, dep2); - st.update(i, dependent_expr(m, g, m.mk_join(dep1, dep2))); + mrp(f, dep1, g, dep2); + CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n"); + st.update(i, dependent_expr(m, g, dep2)); } continue; } @@ -77,10 +78,28 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st rp->set_substitution(t->m_subst.get()); // rigid entries: // apply substitution to added in case of rigid model convertions + ptr_vector dep_exprs; + expr_ref_vector trail(m); for (unsigned i = qhead; i < st.qtail(); ++i) { auto [f, dep1] = st[i](); auto [g, dep2] = rp->replace_with_dep(f); + if (dep1) { + dep_exprs.reset(); + trail.reset(); + m.linearize(dep1, dep_exprs); + for (auto*& d : dep_exprs) { + auto [h, dep3] = rp->replace_with_dep(d); + if (h != d) { + trail.push_back(h); + d = h; + dep2 = m.mk_join(dep2, dep3); + } + } + if (!trail.empty()) + dep1 = m.mk_join(dep_exprs.size(), dep_exprs.data()); + } dependent_expr d(m, g, m.mk_join(dep1, dep2)); + CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n"); add_vars(d, free_vars); st.update(i, d); } @@ -121,3 +140,19 @@ void model_reconstruction_trail::append(generic_model_converter& mc) { m_trail_stack.push(value_trail(m_trail_index)); append(mc, m_trail_index); } + +std::ostream& model_reconstruction_trail::display(std::ostream& out) const { + for (auto* t : m_trail) { + if (!t->m_active) + continue; + else if (t->is_hide()) + out << "hide " << t->m_decl->get_name() << "\n"; + else if (t->is_def()) + out << t->m_decl->get_name() << " <- " << mk_pp(t->m_def, m) << "\n"; + else { + for (auto const& [v, def] : t->m_subst->sub()) + out << mk_pp(v, m) << " <- " << mk_pp(def, m) << "\n"; + } + } + return out; +} \ No newline at end of file diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 5ad204bf7d4..4ef58f790d6 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -138,5 +138,7 @@ class model_reconstruction_trail { * Append new updates to model converter, update m_trail_index in the process. */ void append(generic_model_converter& mc); + + std::ostream& display(std::ostream& out) const; }; diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 928e8a7870a..0a3cfb5eedd 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -83,13 +83,13 @@ void propagate_values::reduce() { }; unsigned rw = m_stats.m_num_rewrites + 1; - for (unsigned r = 0; r < m_max_rounds && rw != m_stats.m_num_rewrites; ++r) { + for (unsigned r = 0; r < m_max_rounds && m.inc() && rw != m_stats.m_num_rewrites; ++r) { rw = m_stats.m_num_rewrites; init_sub(); - for (unsigned i = qhead(); i < qtail() && !m_fmls.inconsistent(); ++i) + for (unsigned i = qhead(); i < qtail() && m.inc() && !m_fmls.inconsistent(); ++i) process_fml(i); init_sub(); - for (unsigned i = qtail(); i-- > qhead() && !m_fmls.inconsistent();) + for (unsigned i = qtail(); i-- > qhead() && m.inc() && !m_fmls.inconsistent();) process_fml(i); if (subst.empty()) break; diff --git a/src/ast/simplifiers/pull_nested_quantifiers.h b/src/ast/simplifiers/pull_nested_quantifiers.h new file mode 100644 index 00000000000..a113c36c2f4 --- /dev/null +++ b/src/ast/simplifiers/pull_nested_quantifiers.h @@ -0,0 +1,46 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + pull_nested_quantifiers.h + +Abstract: + + pull nested quantifiers + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/normal_forms/pull_quant.h" + + +class pull_nested_quantifiers_simplifier : public dependent_expr_simplifier { + pull_nested_quant m_pull; + +public: + pull_nested_quantifiers_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_pull(m) { + } + + char const* name() const override { return "pull-nested-quantifiers"; } + + void reduce() override { + if (!m_fmls.has_quantifiers()) + return; + expr_ref new_curr(m); + proof_ref new_pr(m); + for (unsigned idx : indices()) { + auto d = m_fmls[idx]; + m_pull(d.fml(), new_curr, new_pr); + m_fmls.update(idx, dependent_expr(m, new_curr, d.dep())); + } + } +}; diff --git a/src/ast/simplifiers/refine_inj_axiom.h b/src/ast/simplifiers/refine_inj_axiom.h new file mode 100644 index 00000000000..2333ba690df --- /dev/null +++ b/src/ast/simplifiers/refine_inj_axiom.h @@ -0,0 +1,44 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + refine_inj_axiom.h + +Abstract: + + refine injectivity axiom + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/inj_axiom.h" + + + +class refine_inj_axiom_simplifier : public dependent_expr_simplifier { + +public: + refine_inj_axiom_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls) { + } + + char const* name() const override { return "refine-injectivity"; } + + void reduce() override { + if (!m_fmls.has_quantifiers()) + return; + expr_ref r(m); + for (unsigned idx : indices()) { + expr* f = m_fmls[idx].fml(); + if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), r)) + m_fmls.update(idx, dependent_expr(m, r, m_fmls[idx].dep())); + } + } +}; diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h index bb9fd0d22ae..be54ca005e4 100644 --- a/src/ast/simplifiers/rewriter_simplifier.h +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -40,16 +40,14 @@ class rewriter_simplifier : public dependent_expr_simplifier { m_num_steps = 0; expr_ref new_curr(m); proof_ref new_pr(m); - for (unsigned idx = qhead(); idx < qtail(); idx++) { - if (m_fmls.inconsistent()) - break; + for (unsigned idx : indices()) { auto d = m_fmls[idx]; m_rewriter(d.fml(), new_curr, new_pr); m_num_steps += m_rewriter.get_num_steps(); m_fmls.update(idx, dependent_expr(m, new_curr, d.dep())); } } - void collect_statistics(statistics& st) const override { st.update("simplifier", m_num_steps); } + void collect_statistics(statistics& st) const override { st.update("simplifier-steps", m_num_steps); } void reset_statistics() override { m_num_steps = 0; } void updt_params(params_ref const& p) override { m_params.append(p); m_rewriter.updt_params(m_params); } void collect_param_descrs(param_descrs& r) override { th_rewriter::get_param_descrs(r); } diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h index 326b3d09e09..0e93f62f924 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/seq_simplifier.h @@ -46,7 +46,8 @@ class seq_simplifier : public dependent_expr_simplifier { << " :after-memory " << std::fixed << std::setprecision(2) << end_memory << ")" << "\n"; s.collect_statistics(st); - verbose_stream() << st); + if (st.size() > 0) + st.display_smt2(verbose_stream())); } }; @@ -63,14 +64,17 @@ class seq_simplifier : public dependent_expr_simplifier { } void reduce() override { + TRACE("simplifier", tout << m_fmls << "\n"); for (auto* s : m_simplifiers) { if (m_fmls.inconsistent()) break; if (!m.inc()) break; + s->reset_statistics(); collect_stats _cs(*s); s->reduce(); m_fmls.flatten_suffix(); + TRACE("simplifier", tout << s->name() << "\n" << m_fmls << "\n"); } } diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index e2fbc0c9a9a..94b089c809f 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -34,7 +34,7 @@ namespace euf { void solve_eqs::get_eqs(dep_eq_vector& eqs) { for (extract_eq* ex : m_extract_plugins) - for (unsigned i = qhead(); i < qtail(); ++i) + for (unsigned i : indices()) ex->get_eqs(m_fmls[i], eqs); } @@ -99,6 +99,9 @@ namespace euf { auto const& [orig, v, t, d] = eq; SASSERT(j == var2id(v)); bool is_safe = true; + if (m_fmls.frozen(v)) + continue; + unsigned todo_sz = todo.size(); // determine if substitution is safe. @@ -187,7 +190,7 @@ namespace euf { scoped_ptr rp = mk_default_expr_replacer(m, false); rp->set_substitution(m_subst.get()); - for (unsigned i = qhead(); i < qtail() && !m_fmls.inconsistent(); ++i) { + for (unsigned i : indices()) { auto [f, d] = m_fmls[i](); auto [new_f, new_dep] = rp->replace_with_dep(f); m_rewriter(new_f); diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index 4b2905b2a1c..c8fbe3a40c0 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -32,6 +32,10 @@ namespace euf { struct stats { unsigned m_num_steps = 0; unsigned m_num_elim_vars = 0; + void reset() { + m_num_steps = 0; + m_num_elim_vars = 0; + } }; struct config { @@ -78,5 +82,7 @@ namespace euf { void collect_statistics(statistics& st) const override; + void reset_statistics() override { m_stats.reset(); } + }; } diff --git a/src/qe/lite/qe_lite.cpp b/src/qe/lite/qe_lite.cpp index 6d337c12fa9..2ea11b6b960 100644 --- a/src/qe/lite/qe_lite.cpp +++ b/src/qe/lite/qe_lite.cpp @@ -35,6 +35,8 @@ Revision History: #include "tactic/tactical.h" #include "qe/mbp/mbp_solve_plugin.h" #include "qe/lite/qe_lite.h" +#include "tactic/dependent_expr_state_tactic.h" + namespace qel { @@ -2407,122 +2409,50 @@ void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_re } namespace { -class qe_lite_tactic : public tactic { - ast_manager& m; - params_ref m_params; - qe_lite m_qe; - - void checkpoint() { - tactic::checkpoint(m); - } - -#if 0 - void debug_diff(expr* a, expr* b) { - ptr_vector as, bs; - as.push_back(a); - bs.push_back(b); - expr* a1, *a2, *b1, *b2; - while (!as.empty()) { - a = as.back(); - b = bs.back(); - as.pop_back(); - bs.pop_back(); - if (a == b) { - continue; - } - else if (is_forall(a) && is_forall(b)) { - as.push_back(to_quantifier(a)->get_expr()); - bs.push_back(to_quantifier(b)->get_expr()); - } - else if (m.is_and(a, a1, a2) && m.is_and(b, b1, b2)) { - as.push_back(a1); - as.push_back(a2); - bs.push_back(b1); - bs.push_back(b2); - } - else if (m.is_eq(a, a1, a2) && m.is_eq(b, b1, b2)) { - as.push_back(a1); - as.push_back(a2); - bs.push_back(b1); - bs.push_back(b2); - } - else { - TRACE("qe", tout << mk_pp(a, m) << " != " << mk_pp(b, m) << "\n";); - } + class qe_lite_simplifier : public dependent_expr_simplifier { + params_ref m_params; + qe_lite m_qe; + public: + qe_lite_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st) : + dependent_expr_simplifier(m, st), + m_qe(m, p, true) { + updt_params(p); } - } -#endif -public: - qe_lite_tactic(ast_manager & m, params_ref const & p): - m(m), - m_params(p), - m_qe(m, p, true) {} - - char const* name() const override { return "qe_lite"; } - - tactic * translate(ast_manager & m) override { - return alloc(qe_lite_tactic, m, m_params); - } + char const* name() const override { return "qe-lite"; } - void updt_params(params_ref const & p) override { - m_params.append(p); - // m_imp->updt_params(p); - } - - void collect_param_descrs(param_descrs & r) override { - // m_imp->collect_param_descrs(r); - } - - void operator()(goal_ref const & g, - goal_ref_buffer & result) override { - tactic_report report("qe-lite", *g); - proof_ref new_pr(m); - expr_ref new_f(m); + void updt_params(params_ref const& p) override { + m_params.append(p); + } - unsigned sz = g->size(); - for (unsigned i = 0; i < sz; i++) { - checkpoint(); - if (g->inconsistent()) - break; - expr * f = g->form(i); - if (!has_quantifiers(f)) - continue; - new_f = f; - m_qe(new_f, new_pr); - if (new_pr) { - expr* fact = m.get_fact(new_pr); - if (to_app(fact)->get_arg(0) != to_app(fact)->get_arg(1)) { - new_pr = m.mk_modus_ponens(g->pr(i), new_pr); - } - else { - new_pr = g->pr(i); - } - } - if (f != new_f) { - TRACE("qe", tout << mk_pp(f, m) << "\n" << new_f << "\n" << new_pr << "\n";); - g->update(i, new_f, new_pr, g->dep(i)); + void reduce() override { + if (!m_fmls.has_quantifiers()) + return; + proof_ref new_pr(m); + expr_ref new_f(m); + for (unsigned i : indices()) { + expr* f = m_fmls[i].fml(); + if (!has_quantifiers(f)) + continue; + new_f = f; + m_qe(new_f, new_pr); + m_fmls.update(i, dependent_expr(m, new_f, m_fmls[i].dep())); } } - g->inc_depth(); - result.push_back(g.get()); - } - - void collect_statistics(statistics & st) const override { - // m_imp->collect_statistics(st); - } - - void reset_statistics() override { - // m_imp->reset_statistics(); - } + }; - void cleanup() override { - m_qe.~qe_lite(); - new (&m_qe) qe_lite(m, m_params, true); - } -}; + class qe_lite_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(qe_lite_simplifier, m, p, s); + } + }; } tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { - return alloc(qe_lite_tactic, m, p); + return alloc(dependent_expr_state_tactic, m, p, alloc(qe_lite_tactic_factory)); +} + +dependent_expr_simplifier* mk_qe_lite_simplifer(ast_manager& m, params_ref const& p, dependent_expr_state& st) { + return alloc(qe_lite_simplifier, m, p, st); } diff --git a/src/qe/lite/qe_lite.h b/src/qe/lite/qe_lite.h index 47af8552a9e..9a4d4c0f621 100644 --- a/src/qe/lite/qe_lite.h +++ b/src/qe/lite/qe_lite.h @@ -23,6 +23,7 @@ Revision History: #include "ast/ast.h" #include "util/uint_set.h" #include "util/params.h" +#include "ast/simplifiers/dependent_expr_state.h" class tactic; @@ -70,3 +71,5 @@ tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p = params_ref()) /* ADD_TACTIC("qe-light", "apply light-weight quantifier elimination.", "mk_qe_lite_tactic(m, p)") */ + +dependent_expr_simplifier* mk_qe_lite_simplifer(ast_manager& m, params_ref const& p, dependent_expr_state& st); diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index f05fc098ff6..dfacf3fbaee 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -22,9 +22,19 @@ Module Name: #include "ast/simplifiers/propagate_values.h" #include "ast/simplifiers/rewriter_simplifier.h" #include "ast/simplifiers/solve_eqs.h" +#include "ast/simplifiers/bv_slice.h" #include "ast/simplifiers/eliminate_predicates.h" +#include "ast/simplifiers/elim_unconstrained.h" +#include "ast/simplifiers/pull_nested_quantifiers.h" +#include "ast/simplifiers/distribute_forall.h" +#include "ast/simplifiers/refine_inj_axiom.h" +#include "ast/simplifiers/elim_bounds.h" +#include "ast/simplifiers/bit2int.h" +#include "ast/simplifiers/bv_elim.h" #include "sat/sat_params.hpp" +#include "smt/params/smt_params.h" #include "sat/sat_solver/sat_smt_preprocess.h" +#include "qe/lite/qe_lite.h" void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { params_ref simp1_p = p; @@ -44,18 +54,38 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep simp2_p.set_bool("flat_and_or", false); sat_params sp(p); + smt_params smtp(p); if (sp.euf() || sp.smt()) { s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); s.add_simplifier(alloc(propagate_values, m, p, st)); + s.add_simplifier(alloc(euf::solve_eqs, m, st)); + s.add_simplifier(alloc(elim_unconstrained, m, st)); + if (smtp.m_macro_finder || smtp.m_quasi_macros) s.add_simplifier(alloc(eliminate_predicates, m, st)); + if (smtp.m_qe_lite) s.add_simplifier(mk_qe_lite_simplifer(m, p, st)); + if (smtp.m_pull_nested_quantifiers) s.add_simplifier(alloc(pull_nested_quantifiers_simplifier, m, p, st)); + if (smtp.m_max_bv_sharing) s.add_simplifier(mk_max_bv_sharing(m, p, st)); + if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); + if (smtp.m_bv_size_reduce) s.add_simplifier(alloc(bv::slice, m, st)); + if (smtp.m_distribute_forall) s.add_simplifier(alloc(distribute_forall_simplifier, m, p, st)); + if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); + if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); + if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); // // add: - // solve_eqs - // elim_predicates - // elim_uncnstr // euf_completion? // - // add: make it externally configurable + // add: make it externally programmable // +#if 0 + ?? if (!invoke(m_lift_ite)) return; + m_lift_ite.m_functor.set_conservative(m_smt_params.m_lift_ite == lift_ite_kind::LI_CONSERVATIVE); + m_ng_lift_ite.m_functor.set_conservative(m_smt_params.m_ng_lift_ite == lift_ite_kind::LI_CONSERVATIVE); + ?? if (!invoke(m_ng_lift_ite)) return; + if (!invoke(m_elim_term_ite)) return; + if (!invoke(m_apply_quasi_macros)) return; + if (!invoke(m_flatten_clauses)) return; +#endif + } else { s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 69cc7ed17ad..c544df6fa71 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -60,6 +60,17 @@ class sat_smt_solver : public solver { void add(dependent_expr const& j) override { s.m_fmls.push_back(j); } bool inconsistent() override { return s.m_solver.inconsistent(); } model_reconstruction_trail& model_trail() override { return m_reconstruction_trail; } + std::ostream& display(std::ostream& out) const override { + unsigned i = 0; + for (auto const& d : s.m_fmls) { + if (i == qhead()) + out << "---- head ---\n"; + out << d << "\n"; + ++i; + } + m_reconstruction_trail.display(out); + return out; + } void append(generic_model_converter& mc) { model_trail().append(mc); } void replay(unsigned qhead) { m_reconstruction_trail.replay(qhead, *this); } void flatten_suffix() override { @@ -421,12 +432,10 @@ class sat_smt_solver : public solver { } expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override { - if (!is_internalized()) { - lbool r = internalize_formulas(); - if (r != l_true) { - IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n"); - return expr_ref_vector(m); - } + lbool r = internalize_formulas(); + if (r != l_true) { + IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n"); + return expr_ref_vector(m); } convert_internalized(); if (m_solver.inconsistent()) @@ -551,8 +560,7 @@ class sat_smt_solver : public solver { void convert_internalized() { m_solver.pop_to_base_level(); - if (!is_internalized() && m_preprocess_state.qhead() > 0) - internalize_formulas(); + internalize_formulas(); if (!is_internalized() || m_internalized_converted) return; sat2goal s2g; @@ -723,9 +731,8 @@ class sat_smt_solver : public solver { for (unsigned v = 0; v < var2expr.size(); ++v) { expr * n = var2expr.get(v); - if (!n || !is_uninterp_const(n)) { - continue; - } + if (!n || !is_uninterp_const(n)) + continue; switch (sat::value_at(v, ll_m)) { case l_true: mdl->register_decl(to_app(n)->get_decl(), m.mk_true()); @@ -747,9 +754,8 @@ class sat_smt_solver : public solver { TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); - if (!gparams::get_ref().get_bool("model_validate", false)) { - return; - } + if (!gparams::get_ref().get_bool("model_validate", false)) + return; IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";); model_evaluator eval(*mdl); eval.set_model_completion(true); From 7b9dfb8e1e439ac8194246f23f4a8708c17a4562 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 13:43:40 +0700 Subject: [PATCH 142/597] update dependencies for python build --- scripts/mk_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 497aa350fb8..2da7b0f21a6 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -39,7 +39,7 @@ def init_project_def(): add_lib('macros', ['rewriter'], 'ast/macros') add_lib('model', ['macros']) add_lib('converters', ['model'], 'ast/converters') - add_lib('simplifiers', ['euf', 'rewriter', 'bit_blaster', 'converters'], 'ast/simplifiers') + add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'converters'], 'ast/simplifiers') add_lib('tactic', ['simplifiers']) add_lib('solver', ['params', 'model', 'tactic', 'proofs']) add_lib('cmd_context', ['solver', 'rewriter', 'params']) From c1ff3d31920d6b5474fc4faa008bddb40c05a381 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 13:46:00 +0700 Subject: [PATCH 143/597] wip - adding quasi macro detection --- src/ast/simplifiers/eliminate_predicates.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index eb7006da6c5..700f96ce3b7 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -135,7 +135,7 @@ bool eliminate_predicates::can_be_macro_head(expr* _head, unsigned num_bound) { * f(x,y,x+y+3,1) where x, y are the only bound variables */ -bool eliminate_predicates::can_be_quasi_macro_head(expr* head, unsigned num_bound) { +bool eliminate_predicates::can_be_quasi_macro_head(expr* _head, unsigned num_bound) { if (!is_app(_head)) return false; app* head = to_app(_head); @@ -149,15 +149,15 @@ bool eliminate_predicates::can_be_quasi_macro_head(expr* head, unsigned num_boun uint_set indices; for (expr* arg : *head) { if (!is_var(arg)) - return continue; + continue; unsigned idx = to_var(arg)->get_idx(); if (indices.contains(idx)) - return continue; + continue; if (idx >= num_bound) return false; indices.insert(idx); } - return indices.size() == num_bound; + return indices.num_elems() == num_bound; } From 23c53c6820b2d0c786dc416dab9a50473a7bbde3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 19:36:13 +0900 Subject: [PATCH 144/597] fix build --- src/ast/simplifiers/dependent_expr_state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index d8539d60485..a5f31b70c0b 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -118,7 +118,7 @@ class dependent_expr_simplifier { dependent_expr_simplifier& s; unsigned m_index = 0; bool at_end = false; - unsigned index() const { return at_end ? s.qtail() : m_index; } + unsigned index() const { return at_end ? s.qtail() : std::min(m_index, s.qtail()); } iterator(dependent_expr_simplifier& s, unsigned i) : s(s), m_index(i), at_end(i == s.qtail()) {} bool operator==(iterator const& other) const { return index() == other.index(); } bool operator!=(iterator const& other) const { return !(*this == other); } From edb0fc394b8578d35661a5308eec919ae180d20c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 23:15:32 +0900 Subject: [PATCH 145/597] rewrite some simplifiers --- src/ast/simplifiers/bit_blaster.cpp | 8 +- src/ast/simplifiers/bv_slice.cpp | 4 +- src/ast/simplifiers/card2bv.cpp | 2 +- src/ast/simplifiers/dependent_expr_state.h | 2 +- src/ast/simplifiers/elim_term_ite.h | 49 +++++ src/ast/simplifiers/elim_unconstrained.cpp | 7 +- src/ast/simplifiers/max_bv_sharing.cpp | 242 ++------------------- src/ast/simplifiers/propagate_values.cpp | 2 +- 8 files changed, 80 insertions(+), 236 deletions(-) create mode 100644 src/ast/simplifiers/elim_term_ite.h diff --git a/src/ast/simplifiers/bit_blaster.cpp b/src/ast/simplifiers/bit_blaster.cpp index 72ee19ecac2..a1988a9306e 100644 --- a/src/ast/simplifiers/bit_blaster.cpp +++ b/src/ast/simplifiers/bit_blaster.cpp @@ -37,13 +37,11 @@ void bit_blaster::reduce() { expr_ref new_curr(m); proof_ref new_pr(m); bool change = false; - for (unsigned idx = qhead(); idx < qtail(); idx++) { - if (m_fmls.inconsistent()) - break; + for (unsigned idx : indices()) { auto [curr, d] = m_fmls[idx](); - m_rewriter(curr, new_curr, new_pr); - m_num_steps += m_rewriter.get_num_steps(); + m_rewriter(curr, new_curr, new_pr); if (curr != new_curr) { + m_num_steps += m_rewriter.get_num_steps(); change = true; TRACE("bit_blaster", tout << mk_pp(curr, m) << " -> " << new_curr << "\n";); m_fmls.update(idx, dependent_expr(m, new_curr, d)); diff --git a/src/ast/simplifiers/bv_slice.cpp b/src/ast/simplifiers/bv_slice.cpp index cc8b4fd85ad..7ffa56a29f9 100644 --- a/src/ast/simplifiers/bv_slice.cpp +++ b/src/ast/simplifiers/bv_slice.cpp @@ -27,7 +27,7 @@ namespace bv { } void slice::process_eqs() { - for (unsigned i = qhead(); i < qtail(); ++i) { + for (unsigned i : indices()) { auto const [f, d] = m_fmls[i](); process_eq(f); } @@ -136,7 +136,7 @@ namespace bv { expr_ref_vector cache(m), pin(m); ptr_vector todo, args; expr* c; - for (unsigned i = qhead(); i < qtail(); ++i) { + for (unsigned i : indices()) { auto const [f, d] = m_fmls[i](); todo.push_back(f); pin.push_back(f); diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index d20b4a6c54f..58774e1c6bc 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -29,7 +29,7 @@ void card2bv::reduce() { expr_ref new_f1(m), new_f2(m); proof_ref new_pr(m); - for (unsigned idx = qhead(); !m_fmls.inconsistent() && idx < qtail(); idx++) { + for (unsigned idx : indices()) { auto [f, d] = m_fmls[idx](); rw1(f, new_f1); rw2(false, new_f1, new_f2, new_pr); diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index a5f31b70c0b..9f27a336c74 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -7,7 +7,7 @@ Module Name: Abstract: - abstraction for simplification of depenent expression states. + abstraction for simplification of dependent 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. diff --git a/src/ast/simplifiers/elim_term_ite.h b/src/ast/simplifiers/elim_term_ite.h new file mode 100644 index 00000000000..e34b0b45753 --- /dev/null +++ b/src/ast/simplifiers/elim_term_ite.h @@ -0,0 +1,49 @@ + +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + elim_term_ite.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/elim_term_ite.h" + + +class elim_term_ite_simplifier : public dependent_expr_simplifier { + elim_term_ite m_elim; + +public: + elim_term_ite_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_elim_term_ite(m) { + } + + char const* name() const override { return "distribute-forall"; } + + void reduce() override { + if (!m_fmls.has_quantifiers()) + return; + expr_ref r(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + if (!has_quantifiers(d.fml())) + continue; + m_rewriter(d.fml(), r); + m_fmls.update(idx, dependent_expr(m, r, d.dep())); + } + } + + void push() override { dependent_expr_simplifier::push(); m_rewriter.push(); } + + void pop(unsigned n) override { dependent_expr_simplifier::pop(n); m_rewriter.pop(n); } +}; + diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 41305b74520..9d40a5f2115 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -125,7 +125,7 @@ void elim_unconstrained::init_nodes() { m_fmls.freeze_suffix(); expr_ref_vector terms(m); - for (unsigned i = qhead(); i < qtail(); ++i) + for (unsigned i : indices()) terms.push_back(m_fmls[i].fml()); m_trail.append(terms); m_heap.reset(); @@ -201,7 +201,7 @@ void elim_unconstrained::gc(expr* t) { */ void elim_unconstrained::reconstruct_terms() { expr_ref_vector terms(m); - for (unsigned i = qhead(); i < qtail(); ++i) + for (unsigned i : indices()) terms.push_back(m_fmls[i].fml()); for (expr* e : subterms_postorder::all(terms)) { @@ -234,8 +234,7 @@ void elim_unconstrained::reconstruct_terms() { void elim_unconstrained::assert_normalized(vector& old_fmls) { - unsigned sz = qtail(); - for (unsigned i = qhead(); i < sz; ++i) { + for (unsigned i : indices()) { auto [f, d] = m_fmls[i](); node& n = get_node(f); expr* g = n.m_term; diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index 3ac27302a8d..c12fa4410d4 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -20,234 +20,26 @@ Revision History: --*/ -#include "ast/bv_decl_plugin.h" -#include "ast/rewriter/rewriter_def.h" -#include "util/obj_pair_hashtable.h" +#include "ast/rewriter/maximize_ac_sharing.h" #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/ast_lt.h" class max_bv_sharing : public dependent_expr_simplifier { - - struct rw_cfg : public default_rewriter_cfg { - typedef std::pair expr_pair; - typedef obj_pair_hashtable set; - bv_util m_util; - set m_add_apps; - set m_mul_apps; - set m_xor_apps; - set m_or_apps; - unsigned long long m_max_memory; - unsigned m_max_steps; - unsigned m_max_args; - - ast_manager & m() const { return m_util.get_manager(); } - - rw_cfg(ast_manager & m, params_ref const & p): - m_util(m) { - updt_params(p); - } - - void cleanup() { - m_add_apps.finalize(); - m_mul_apps.finalize(); - m_or_apps.finalize(); - m_xor_apps.finalize(); - } - - void updt_params(params_ref const & p) { - m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); - m_max_steps = p.get_uint("max_steps", UINT_MAX); - m_max_args = p.get_uint("max_args", 128); - } - - bool max_steps_exceeded(unsigned num_steps) const { - if (memory::get_allocation_size() > m_max_memory) - throw rewriter_exception(Z3_MAX_MEMORY_MSG); - return num_steps > m_max_steps; - } - - set & f2set(func_decl * f) { - switch (f->get_decl_kind()) { - case OP_BADD: return m_add_apps; - case OP_BMUL: return m_mul_apps; - case OP_BXOR: return m_xor_apps; - case OP_BOR: return m_or_apps; - default: - UNREACHABLE(); - return m_or_apps; // avoid compilation error - } - } - - expr * reuse(set & s, func_decl * f, expr * arg1, expr * arg2) { - if (s.contains(expr_pair(arg1, arg2))) - return m().mk_app(f, arg1, arg2); - if (s.contains(expr_pair(arg2, arg1))) - return m().mk_app(f, arg2, arg1); - return nullptr; - } - - struct ref_count_lt { - bool operator()(expr * t1, expr * t2) const { - if (t1->get_ref_count() < t2->get_ref_count()) - return true; - return (t1->get_ref_count() == t2->get_ref_count()) && lt(t1, t2); - } - }; - - br_status reduce_ac_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { - set & s = f2set(f); - - if (num_args == 2) { - if (!m_util.is_numeral(args[0]) && !m_util.is_numeral(args[1])) - s.insert(expr_pair(args[0], args[1])); - return BR_FAILED; - } - - ptr_buffer _args; - bool first = false; - expr * num = nullptr; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (num == nullptr && m_util.is_numeral(arg)) { - if (i == 0) first = true; - num = arg; - } - else { - _args.push_back(arg); - } - } - num_args = _args.size(); - - // std::sort(_args.begin(), _args.end(), ref_count_lt()); - // std::sort(_args.begin(), _args.end(), ast_to_lt()); - - try_to_reuse: - if (num_args > 1 && num_args < m_max_args) { - for (unsigned i = 0; i < num_args - 1; i++) { - for (unsigned j = i + 1; j < num_args; j++) { - expr * r = reuse(s, f, _args[i], _args[j]); - if (r != nullptr) { - TRACE("bv_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";); - _args[i] = r; - SASSERT(num_args > 1); - for (unsigned w = j; w < num_args - 1; w++) { - _args[w] = _args[w+1]; - } - num_args--; - goto try_to_reuse; - } - } - } - } - - // TODO: - // some benchmarks are more efficiently solved using a tree-like structure (better sharing) - // other benchmarks are more efficiently solved using a chain-like structure (better propagation for arguments "closer to the output"). - // - // One possible solution is to do a global analysis that finds a good order that increases sharing without affecting - // propagation. - // - // Another cheap trick is to create an option, and try both for a small amount of time. -#if 0 - SASSERT(num_args > 0); - if (num_args == 1) { - result = _args[0]; - } - else { - // ref_count_lt is not a total order on expr's - std::stable_sort(_args.c_ptr(), _args.c_ptr() + num_args, ref_count_lt()); - result = m().mk_app(f, _args[0], _args[1]); - for (unsigned i = 2; i < num_args; i++) { - result = m().mk_app(f, result.get(), _args[i]); - } - } - if (num != 0) { - if (first) - result = m().mk_app(f, num, result); - else - result = m().mk_app(f, result, num); - } - return BR_DONE; -#else - // Create "tree-like circuit" - while (true) { - TRACE("bv_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";); - unsigned j = 0; - for (unsigned i = 0; i < num_args; i += 2, j++) { - if (i == num_args - 1) { - _args[j] = _args[i]; - } - else { - s.insert(expr_pair(_args[i], _args[i+1])); - _args[j] = m().mk_app(f, _args[i], _args[i+1]); - } - } - num_args = j; - if (num_args == 1) { - if (num == nullptr) { - result = _args[0]; - } - else { - if (first) - result = m().mk_app(f, num, _args[0]); - else - result = m().mk_app(f, _args[0], num); - } - return BR_DONE; - } - } -#endif - } + maximize_bv_sharing_rw m_rewriter; + unsigned m_num_steps = 0; - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - if (f->get_family_id() != m_util.get_family_id()) - return BR_FAILED; - switch (f->get_decl_kind()) { - case OP_BADD: - case OP_BMUL: - case OP_BOR: - case OP_BXOR: - result_pr = nullptr; - return reduce_ac_app(f, num, args, result); - default: - return BR_FAILED; - } - } - }; - - struct rw : public rewriter_tpl { - rw_cfg m_cfg; - - rw(ast_manager & m, params_ref const & p): - rewriter_tpl(m, m.proofs_enabled(), m_cfg), - m_cfg(m, p) { - } - }; - - rw m_rw; - unsigned m_num_steps = 0; - - - params_ref m_params; - public: max_bv_sharing(ast_manager & m, params_ref const & p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls), - m_params(p), - m_rw(m, p) { + m_rewriter(m) { } - void updt_params(params_ref const & p) override { - m_params.append(p); - m_rw.cfg().updt_params(m_params); + void reset_statistics() override { + m_num_steps = 0; } - void collect_param_descrs(param_descrs & r) override { - insert_max_memory(r); - insert_max_steps(r); - r.insert("max_args", CPK_UINT, - "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); + void collect_statistics(statistics& st) const override { + st.update("max-sharing-steps", m_num_steps); } char const* name() const override { return "max-bv-sharing"; } @@ -255,15 +47,21 @@ class max_bv_sharing : public dependent_expr_simplifier { void reduce() override { expr_ref new_curr(m); proof_ref new_pr(m); - for (unsigned idx = qhead(); idx < qtail() && !m_fmls.inconsistent(); idx++) { + for (unsigned idx : indices()) { auto [curr, d] = m_fmls[idx](); - m_rw(curr, new_curr, new_pr); + m_rewriter(curr, new_curr, new_pr); // Proof reconstruction: new_pr = m.mk_modus_ponens(old_pr, new_pr); - m_num_steps += m_rw.get_num_steps(); - m_fmls.update(idx, dependent_expr(m, new_curr, d)); + if (new_curr != curr) { + m_num_steps += m_rewriter.get_num_steps(); + m_fmls.update(idx, dependent_expr(m, new_curr, d)); + } } - m_rw.cfg().cleanup(); - } + } + + void push() override { dependent_expr_simplifier::push(); m_rewriter.push_scope(); } + + void pop(unsigned n) override { dependent_expr_simplifier::pop(n); m_rewriter.pop_scope(n); } + }; dependent_expr_simplifier * mk_max_bv_sharing(ast_manager & m, params_ref const & p, dependent_expr_state& fmls) { diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 0a3cfb5eedd..f0d85af7279 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -86,7 +86,7 @@ void propagate_values::reduce() { for (unsigned r = 0; r < m_max_rounds && m.inc() && rw != m_stats.m_num_rewrites; ++r) { rw = m_stats.m_num_rewrites; init_sub(); - for (unsigned i = qhead(); i < qtail() && m.inc() && !m_fmls.inconsistent(); ++i) + for (unsigned i : indices()) process_fml(i); init_sub(); for (unsigned i = qtail(); i-- > qhead() && m.inc() && !m_fmls.inconsistent();) From cfc8e19baf676102214ed97c71bb721ddc930a34 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 1 Dec 2022 02:35:43 +0900 Subject: [PATCH 146/597] add more simplifiers, fix model reconstruction order for elim_unconstrained - enable sat.smt in smt_tactic that is invoked by default on first goals add flatten-clauses add push-ite have tptp5 front-end pretty print SMT2 formulas a little nicer. --- examples/tptp/tptp5.cpp | 21 +++- src/ast/simplifiers/elim_term_ite.h | 16 ++-- src/ast/simplifiers/elim_unconstrained.cpp | 20 ++-- src/ast/simplifiers/flatten_clauses.h | 95 +++++++++++++++++++ .../model_reconstruction_trail.cpp | 6 +- src/ast/simplifiers/push_ite.h | 66 +++++++++++++ src/ast/simplifiers/seq_simplifier.h | 4 +- src/math/simplex/model_based_opt.cpp | 45 +++++++-- src/sat/sat_solver/sat_smt_preprocess.cpp | 44 +++++---- src/tactic/smtlogics/smt_tactic.cpp | 8 +- 10 files changed, 271 insertions(+), 54 deletions(-) create mode 100644 src/ast/simplifiers/flatten_clauses.h create mode 100644 src/ast/simplifiers/push_ite.h diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index 4f1d25aa9ea..04b225caf90 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -2305,12 +2305,25 @@ static void display_smt2(std::ostream& out) { return; } + z3::expr_vector asms(ctx); size_t num_assumptions = fmls.m_formulas.size(); + for (size_t i = 0; i < num_assumptions; ++i) + asms.push_back(fmls.m_formulas[i]); - Z3_ast* assumptions = new Z3_ast[num_assumptions]; - for (size_t i = 0; i < num_assumptions; ++i) { - assumptions[i] = fmls.m_formulas[i]; + for (size_t i = 0; i < asms.size(); ++i) { + z3::expr fml = asms[i]; + if (fml.is_and()) { + asms.set(i, fml.arg(0)); + for (unsigned j = 1; j < fml.num_args(); ++j) + asms.push_back(fml.arg(j)); + --i; + } } + + Z3_ast* assumptions = new Z3_ast[asms.size()]; + for (size_t i = 0; i < asms.size(); ++i) + assumptions[i] = asms[i]; + Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB_FULL); Z3_string s = Z3_benchmark_to_smtlib_string( ctx, @@ -2318,7 +2331,7 @@ static void display_smt2(std::ostream& out) { 0, // no logic is set "unknown", // no status annotation "", // attributes - static_cast(num_assumptions), + static_cast(asms.size()), assumptions, ctx.bool_val(true)); diff --git a/src/ast/simplifiers/elim_term_ite.h b/src/ast/simplifiers/elim_term_ite.h index e34b0b45753..6455db321e8 100644 --- a/src/ast/simplifiers/elim_term_ite.h +++ b/src/ast/simplifiers/elim_term_ite.h @@ -15,19 +15,21 @@ Module Name: #pragma once #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/rewriter/elim_term_ite.h" +#include "ast/normal_forms/elim_term_ite.h"" class elim_term_ite_simplifier : public dependent_expr_simplifier { - elim_term_ite m_elim; - + defined_names m_df; + elim_term_ite_rw m_rewriter; + public: elim_term_ite_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls), - m_elim_term_ite(m) { + m_df(m), + m_rewriter(m, m_df) { } - char const* name() const override { return "distribute-forall"; } + char const* name() const override { return "elim-term-ite"; } void reduce() override { if (!m_fmls.has_quantifiers()) @@ -42,8 +44,8 @@ class elim_term_ite_simplifier : public dependent_expr_simplifier { } } - void push() override { dependent_expr_simplifier::push(); m_rewriter.push(); } + void push() override { dependent_expr_simplifier::push(); m_df.push(); m_rewriter.push(); } - void pop(unsigned n) override { dependent_expr_simplifier::pop(n); m_rewriter.pop(n); } + void pop(unsigned n) override { m_rewriter.pop(n); m_df.pop(n); dependent_expr_simplifier::pop(n); } }; diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 9d40a5f2115..d9fe5d48003 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -249,32 +249,32 @@ void elim_unconstrained::assert_normalized(vector& old_fmls) { void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector const& old_fmls) { auto& trail = m_fmls.model_trail(); - scoped_ptr rp = mk_default_expr_replacer(m, false); - scoped_ptr sub = alloc(expr_substitution, m, true, false); - rp->set_substitution(sub.get()); - expr_ref new_def(m); + for (auto const& entry : mc.entries()) { switch (entry.m_instruction) { case generic_model_converter::instruction::HIDE: + trail.hide(entry.m_f); break; case generic_model_converter::instruction::ADD: - new_def = entry.m_def; - (*rp)(new_def); - sub->insert(m.mk_const(entry.m_f), new_def, nullptr, nullptr); break; } } - trail.push(sub.detach(), old_fmls); - + scoped_ptr rp = mk_default_expr_replacer(m, false); + scoped_ptr sub = alloc(expr_substitution, m, true, false); + rp->set_substitution(sub.get()); + expr_ref new_def(m); for (auto const& entry : mc.entries()) { switch (entry.m_instruction) { case generic_model_converter::instruction::HIDE: - trail.hide(entry.m_f); break; case generic_model_converter::instruction::ADD: + new_def = entry.m_def; + (*rp)(new_def); + sub->insert(m.mk_const(entry.m_f), new_def, nullptr, nullptr); break; } } + trail.push(sub.detach(), old_fmls); } void elim_unconstrained::reduce() { diff --git a/src/ast/simplifiers/flatten_clauses.h b/src/ast/simplifiers/flatten_clauses.h new file mode 100644 index 00000000000..02444f0930c --- /dev/null +++ b/src/ast/simplifiers/flatten_clauses.h @@ -0,0 +1,95 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + flatten_clauses.h + +Abstract: + + flatten clauses + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/ast_util.h" + + +class flatten_clauses : public dependent_expr_simplifier { + + unsigned m_num_flat = 0; + + bool is_literal(expr* a) { + m.is_not(a, a); + return !is_app(a) || to_app(a)->get_num_args() == 0; + } + + bool is_reducible(expr* a, expr* b) { + return b->get_ref_count() == 1 || is_literal(a); + } + +public: + flatten_clauses(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls) { + } + + char const* name() const override { return "flatten-clauses"; } + + void reset_statistics() override { m_num_flat = 0; } + + void collect_statistics(statistics& st) const override { + st.update("flatten-clauses-rewrites", m_num_flat); + } + + void reduce() override { + bool change = true; + + while (change) { + change = false; + for (unsigned idx : indices()) { + auto de = m_fmls[idx]; + expr* f = de.fml(), *a, *b, *c; + bool decomposed = false; + if (m.is_or(f, a, b) && m.is_not(b, b) && m.is_or(b) && is_reducible(a, b)) + decomposed = true; + else if (m.is_or(f, b, a) && m.is_not(b, b) && m.is_or(b) && is_reducible(a, b)) + decomposed = true; + if (decomposed) { + for (expr* arg : *to_app(b)) + m_fmls.add(dependent_expr(m, m.mk_or(a, mk_not(m, arg)), de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + change = true; + ++m_num_flat; + continue; + } + if (m.is_or(f, a, b) && m.is_and(b) && is_reducible(a, b)) + decomposed = true; + else if (m.is_or(f, b, a) && m.is_and(b) && is_reducible(a, b)) + decomposed = true; + if (decomposed) { + for (expr * arg : *to_app(b)) + m_fmls.add(dependent_expr(m, m.mk_or(a, arg), de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + change = true; + ++m_num_flat; + continue; + } + if (m.is_ite(f, a, b, c)) { + m_fmls.add(dependent_expr(m, m.mk_or(mk_not(m, a), b), de.dep())); + m_fmls.add(dependent_expr(m, m.mk_or(a, c), de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + change = true; + ++m_num_flat; + continue; + } + } + } + } +}; diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 0436822d136..7e8d3e2f1c3 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -151,8 +151,10 @@ std::ostream& model_reconstruction_trail::display(std::ostream& out) const { out << t->m_decl->get_name() << " <- " << mk_pp(t->m_def, m) << "\n"; else { for (auto const& [v, def] : t->m_subst->sub()) - out << mk_pp(v, m) << " <- " << mk_pp(def, m) << "\n"; + out << mk_pp(v, m) << " <- " << mk_pp(def, m) << "\n"; } + for (auto const& d : t->m_removed) + out << "rm: " << d << "\n"; } return out; -} \ No newline at end of file +} diff --git a/src/ast/simplifiers/push_ite.h b/src/ast/simplifiers/push_ite.h new file mode 100644 index 00000000000..e2acdbdb063 --- /dev/null +++ b/src/ast/simplifiers/push_ite.h @@ -0,0 +1,66 @@ + +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + push_ite.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/push_app_ite.h" + + +class push_ite_simplifier : public dependent_expr_simplifier { + push_app_ite_rw m_push; + +public: + push_ite_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls, bool c): + dependent_expr_simplifier(m, fmls), + m_push(m) { + m_push.set_conservative(c); + } + + char const* name() const override { return "push-app-ite"; } + + void reduce() override { + expr_ref r(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + m_push(d.fml(), r); + m_fmls.update(idx, dependent_expr(m, r, d.dep())); + } + } + +}; + + +class ng_push_ite_simplifier : public dependent_expr_simplifier { + ng_push_app_ite_rw m_push; + +public: + ng_push_ite_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls, bool c): + dependent_expr_simplifier(m, fmls), + m_push(m) { + m_push.set_conservative(c); + } + + char const* name() const override { return "ng-push-app-ite"; } + + void reduce() override { + expr_ref r(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + m_push(d.fml(), r); + m_fmls.update(idx, dependent_expr(m, r, d.dep())); + } + } +}; + diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h index 0e93f62f924..a5ef91d7c8a 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/seq_simplifier.h @@ -64,7 +64,7 @@ class seq_simplifier : public dependent_expr_simplifier { } void reduce() override { - TRACE("simplifier", tout << m_fmls << "\n"); + TRACE("simplifier", tout << m_fmls); for (auto* s : m_simplifiers) { if (m_fmls.inconsistent()) break; @@ -74,7 +74,7 @@ class seq_simplifier : public dependent_expr_simplifier { collect_stats _cs(*s); s->reduce(); m_fmls.flatten_suffix(); - TRACE("simplifier", tout << s->name() << "\n" << m_fmls << "\n"); + TRACE("simplifier", tout << s->name() << "\n" << m_fmls); } } diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 9e7ac75ce72..919e07b163b 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -1014,12 +1014,14 @@ namespace opt { return dst; } + // -x + lo <= 0 void model_based_opt::add_lower_bound(unsigned x, rational const& lo) { vector coeffs; coeffs.push_back(var(x, rational::minus_one())); add_constraint(coeffs, lo, t_le); } + // x - hi <= 0 void model_based_opt::add_upper_bound(unsigned x, rational const& hi) { vector coeffs; coeffs.push_back(var(x, rational::one())); @@ -1442,8 +1444,9 @@ namespace opt { for (unsigned ri : mod_rows) { rational a = get_coefficient(ri, x); replace_var(ri, x, rational::zero()); + rational rMod = m_rows[ri].m_mod; - // add w = b mod K + // add w = b mod rMod vector coeffs = m_rows[ri].m_vars; rational coeff = m_rows[ri].m_coeff; unsigned v = m_rows[ri].m_id; @@ -1451,16 +1454,43 @@ namespace opt { unsigned w = UINT_MAX; rational offset(0); - if (coeffs.empty() || K == 1) - offset = mod(coeff, K); + if (coeffs.empty() || rMod == 1) + offset = mod(coeff, rMod); else - w = add_mod(coeffs, coeff, K); + w = add_mod(coeffs, coeff, rMod); rational w_value = w == UINT_MAX ? offset : m_var2value[w]; - // add v = a*z + w - V, for k = (a*z_value + w_value) div K - // claim: (= (mod x K) (- x (* K (div x K)))))) is a theorem for every x, K != 0 +#if 1 + // V := (a * z_value - w_value) div rMod + // V*rMod <= a*z + w < (V+1)*rMod + // v = a*z + w - V*rMod + SASSERT(a * z_value - w_value >= 0); + rational V = div(a * z_value + w_value, rMod); + vector mod_coeffs; + SASSERT(V >= 0); + SASSERT(a * z_value + w_value >= V*rMod); + SASSERT((V+1)*rMod > a*z_value + w_value); + // -a*z - w + V*rMod <= 0 + mod_coeffs.push_back(var(z, -a)); + if (w != UINT_MAX) mod_coeffs.push_back(var(w, -rational::one())); + add_constraint(mod_coeffs, V*rMod - offset, t_le); + mod_coeffs.reset(); + // a*z + w - (V+1)*rMod + 1 <= 0 + mod_coeffs.push_back(var(z, a)); + if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); + add_constraint(mod_coeffs, -(V+1)*rMod + offset + 1, t_le); + mod_coeffs.reset(); + // -v + a*z + w - V*rMod = 0 + mod_coeffs.push_back(var(v, rational::minus_one())); + mod_coeffs.push_back(var(z, a)); + if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); + add_constraint(mod_coeffs, offset - V*rMod, t_eq); + +#else + // add v = a*z + w - V, for V = v_value - a * z_value - w_value + // claim: (= (mod x rMod) (- x (* rMod (div x rMod)))))) is a theorem for every x, rMod != 0 rational V = v_value - a * z_value - w_value; vector mod_coeffs; mod_coeffs.push_back(var(v, rational::minus_one())); @@ -1468,7 +1498,8 @@ namespace opt { if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); add_constraint(mod_coeffs, V + offset, t_eq); add_lower_bound(v, rational::zero()); - add_upper_bound(v, K - 1); + add_upper_bound(v, rMod - 1); +#endif retire_row(ri); vs.push_back(v); diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index dfacf3fbaee..e3d6b3edba2 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -31,27 +31,15 @@ Module Name: #include "ast/simplifiers/elim_bounds.h" #include "ast/simplifiers/bit2int.h" #include "ast/simplifiers/bv_elim.h" +#include "ast/simplifiers/push_ite.h" +#include "ast/simplifiers/elim_term_ite.h" +#include "ast/simplifiers/flatten_clauses.h" #include "sat/sat_params.hpp" #include "smt/params/smt_params.h" #include "sat/sat_solver/sat_smt_preprocess.h" #include "qe/lite/qe_lite.h" void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { - params_ref simp1_p = p; - simp1_p.set_bool("som", true); - simp1_p.set_bool("pull_cheap_ite", true); - simp1_p.set_bool("push_ite_bv", false); - simp1_p.set_bool("local_ctx", true); - simp1_p.set_uint("local_ctx_limit", 10000000); - simp1_p.set_bool("flat", true); // required by som - simp1_p.set_bool("hoist_mul", false); // required by som - simp1_p.set_bool("elim_and", true); - simp1_p.set_bool("blast_distinct", true); - simp1_p.set_bool("flat_and_or", false); - - params_ref simp2_p = p; - simp2_p.set_bool("flat", false); - simp2_p.set_bool("flat_and_or", false); sat_params sp(p); smt_params smtp(p); @@ -70,6 +58,10 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); + if (smtp.m_eliminate_term_ite && smtp.m_lift_ite != lift_ite_kind::LI_FULL) s.add_simplifier(alloc(elim_term_ite_simplifier, m, p, st)); + if (smtp.m_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(push_ite_simplifier, m, p, st, smtp.m_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); + if (smtp.m_ng_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(ng_push_ite_simplifier, m, p, st, smtp.m_ng_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); + s.add_simplifier(alloc(flatten_clauses, m, p, st)); // // add: // euf_completion? @@ -77,17 +69,27 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep // add: make it externally programmable // #if 0 - ?? if (!invoke(m_lift_ite)) return; - m_lift_ite.m_functor.set_conservative(m_smt_params.m_lift_ite == lift_ite_kind::LI_CONSERVATIVE); - m_ng_lift_ite.m_functor.set_conservative(m_smt_params.m_ng_lift_ite == lift_ite_kind::LI_CONSERVATIVE); - ?? if (!invoke(m_ng_lift_ite)) return; - if (!invoke(m_elim_term_ite)) return; if (!invoke(m_apply_quasi_macros)) return; - if (!invoke(m_flatten_clauses)) return; #endif } else { + params_ref simp1_p = p; + simp1_p.set_bool("som", true); + simp1_p.set_bool("pull_cheap_ite", true); + simp1_p.set_bool("push_ite_bv", false); + simp1_p.set_bool("local_ctx", true); + simp1_p.set_uint("local_ctx_limit", 10000000); + simp1_p.set_bool("flat", true); // required by som + simp1_p.set_bool("hoist_mul", false); // required by som + simp1_p.set_bool("elim_and", true); + simp1_p.set_bool("blast_distinct", true); + simp1_p.set_bool("flat_and_or", false); + + params_ref simp2_p = p; + simp2_p.set_bool("flat", false); + simp2_p.set_bool("flat_and_or", false); + s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); s.add_simplifier(alloc(propagate_values, m, p, st)); s.add_simplifier(alloc(card2bv, m, p, st)); diff --git a/src/tactic/smtlogics/smt_tactic.cpp b/src/tactic/smtlogics/smt_tactic.cpp index 0b78761ca8d..d47650c3460 100644 --- a/src/tactic/smtlogics/smt_tactic.cpp +++ b/src/tactic/smtlogics/smt_tactic.cpp @@ -18,10 +18,16 @@ Module Name: #include "smt/tactic/smt_tactic_core.h" #include "sat/tactic/sat_tactic.h" #include "sat/sat_params.hpp" +#include "solver/solver2tactic.h" +#include "solver/solver.h" tactic * mk_smt_tactic(ast_manager & m, params_ref const & p) { sat_params sp(p); - return sp.euf() ? mk_sat_tactic(m, p) : mk_smt_tactic_core(m, p); + if (sp.smt()) + return mk_solver2tactic(mk_smt2_solver(m, p)); + if (sp.euf()) + return mk_sat_tactic(m, p); + return mk_smt_tactic_core(m, p); } tactic * mk_smt_tactic_using(ast_manager& m, bool auto_config, params_ref const& p) { From f24ecde35c29b46706f736f5f29982d4016fb74a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 1 Dec 2022 09:31:52 +0900 Subject: [PATCH 147/597] wip - fixes to simplifiers --- src/ast/simplifiers/elim_term_ite.h | 6 +--- src/ast/simplifiers/elim_unconstrained.cpp | 4 +++ src/ast/simplifiers/flatten_clauses.h | 36 +++++++++++++++++----- src/ast/simplifiers/push_ite.h | 1 - src/sat/sat_solver/sat_smt_solver.cpp | 2 +- 5 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/ast/simplifiers/elim_term_ite.h b/src/ast/simplifiers/elim_term_ite.h index 6455db321e8..5b6ef38fa49 100644 --- a/src/ast/simplifiers/elim_term_ite.h +++ b/src/ast/simplifiers/elim_term_ite.h @@ -15,7 +15,7 @@ Module Name: #pragma once #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/normal_forms/elim_term_ite.h"" +#include "ast/normal_forms/elim_term_ite.h" class elim_term_ite_simplifier : public dependent_expr_simplifier { @@ -32,13 +32,9 @@ class elim_term_ite_simplifier : public dependent_expr_simplifier { char const* name() const override { return "elim-term-ite"; } void reduce() override { - if (!m_fmls.has_quantifiers()) - return; expr_ref r(m); for (unsigned idx : indices()) { auto const& d = m_fmls[idx]; - if (!has_quantifiers(d.fml())) - continue; m_rewriter(d.fml(), r); m_fmls.update(idx, dependent_expr(m, r, d.dep())); } diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index d9fe5d48003..713d7941ce6 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -130,6 +130,7 @@ void elim_unconstrained::init_nodes() { m_trail.append(terms); m_heap.reset(); m_root.reset(); + m_nodes.reset(); // initialize nodes for terms in the original goal init_terms(terms); @@ -159,6 +160,7 @@ void elim_unconstrained::init_terms(expr_ref_vector const& terms) { n.m_orig = e; n.m_term = e; n.m_refcount = 0; + if (is_uninterp_const(e)) m_heap.insert(root(e)); if (is_quantifier(e)) { @@ -250,6 +252,8 @@ void elim_unconstrained::assert_normalized(vector& old_fmls) { void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector const& old_fmls) { auto& trail = m_fmls.model_trail(); + // fresh declarations are added first since + // model reconstruction proceeds in reverse order of stack. for (auto const& entry : mc.entries()) { switch (entry.m_instruction) { case generic_model_converter::instruction::HIDE: diff --git a/src/ast/simplifiers/flatten_clauses.h b/src/ast/simplifiers/flatten_clauses.h index 02444f0930c..aa81024effb 100644 --- a/src/ast/simplifiers/flatten_clauses.h +++ b/src/ast/simplifiers/flatten_clauses.h @@ -28,7 +28,11 @@ class flatten_clauses : public dependent_expr_simplifier { bool is_literal(expr* a) { m.is_not(a, a); - return !is_app(a) || to_app(a)->get_num_args() == 0; + if (m.is_eq(a) && !m.is_iff(a)) + return true; + if (!is_app(a)) + return true; + return to_app(a)->get_family_id() != m.get_basic_family_id(); } bool is_reducible(expr* a, expr* b) { @@ -49,14 +53,14 @@ class flatten_clauses : public dependent_expr_simplifier { } void reduce() override { - bool change = true; - - while (change) { - change = false; + unsigned nf = m_num_flat + 1; + while (nf != m_num_flat) { + nf = m_num_flat; for (unsigned idx : indices()) { auto de = m_fmls[idx]; expr* f = de.fml(), *a, *b, *c; bool decomposed = false; + // (or a (not (or b_i)) => and_i (or a (not b_i)) if (m.is_or(f, a, b) && m.is_not(b, b) && m.is_or(b) && is_reducible(a, b)) decomposed = true; else if (m.is_or(f, b, a) && m.is_not(b, b) && m.is_or(b) && is_reducible(a, b)) @@ -65,10 +69,10 @@ class flatten_clauses : public dependent_expr_simplifier { for (expr* arg : *to_app(b)) m_fmls.add(dependent_expr(m, m.mk_or(a, mk_not(m, arg)), de.dep())); m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); - change = true; ++m_num_flat; continue; } + // (or a (and b_i)) => and_i (or a b_i) if (m.is_or(f, a, b) && m.is_and(b) && is_reducible(a, b)) decomposed = true; else if (m.is_or(f, b, a) && m.is_and(b) && is_reducible(a, b)) @@ -77,7 +81,24 @@ class flatten_clauses : public dependent_expr_simplifier { for (expr * arg : *to_app(b)) m_fmls.add(dependent_expr(m, m.mk_or(a, arg), de.dep())); m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); - change = true; + ++m_num_flat; + continue; + } + // not (and a (or b_i)) => and_i (not a) or (not b_i) + if (m.is_not(f, c) && m.is_and(c, a, b) && m.is_or(b) && is_reducible(a, b)) + decomposed = true; + else if (m.is_not(f, c) && m.is_and(c, b, a) && m.is_or(b) && is_reducible(a, b)) + decomposed = true; + if (decomposed) { + expr* na = mk_not(m, a); + for (expr* arg : *to_app(b)) + m_fmls.add(dependent_expr(m, m.mk_or(na, arg), de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + ++m_num_flat; + continue; + } + if (m.is_implies(f, a, b)) { + m_fmls.update(idx, dependent_expr(m, m.mk_or(mk_not(m, a), b), de.dep())); ++m_num_flat; continue; } @@ -85,7 +106,6 @@ class flatten_clauses : public dependent_expr_simplifier { m_fmls.add(dependent_expr(m, m.mk_or(mk_not(m, a), b), de.dep())); m_fmls.add(dependent_expr(m, m.mk_or(a, c), de.dep())); m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); - change = true; ++m_num_flat; continue; } diff --git a/src/ast/simplifiers/push_ite.h b/src/ast/simplifiers/push_ite.h index e2acdbdb063..e26070a7eda 100644 --- a/src/ast/simplifiers/push_ite.h +++ b/src/ast/simplifiers/push_ite.h @@ -38,7 +38,6 @@ class push_ite_simplifier : public dependent_expr_simplifier { m_fmls.update(idx, dependent_expr(m, r, d.dep())); } } - }; diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index c544df6fa71..3fd3a008030 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -63,7 +63,7 @@ class sat_smt_solver : public solver { std::ostream& display(std::ostream& out) const override { unsigned i = 0; for (auto const& d : s.m_fmls) { - if (i == qhead()) + if (i > 0 && i == qhead()) out << "---- head ---\n"; out << d << "\n"; ++i; From 30c9cda61e49c13e602daf2ecc40c73496ca4f14 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 1 Dec 2022 10:04:33 +0900 Subject: [PATCH 148/597] increment generation for literals created during E-matching --- src/sat/smt/q_ematch.cpp | 5 +++-- src/sat/smt/q_ematch.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 9335c576b03..573003305f7 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -383,7 +383,7 @@ namespace q { sat::literal_vector lits; lits.push_back(~j.m_clause.m_literal); for (unsigned i = 0; i < j.m_clause.size(); ++i) - lits.push_back(instantiate(j.m_clause, j.m_binding, j.m_clause[i])); + lits.push_back(instantiate(j.m_clause, j.m_generation, j.m_binding, j.m_clause[i])); m_qs.log_instantiation(lits, &j); euf::th_proof_hint* ph = nullptr; if (ctx.use_drat()) @@ -418,11 +418,12 @@ namespace q { m_qs.log_instantiation(~c.m_literal, lit); } - sat::literal ematch::instantiate(clause& c, euf::enode* const* binding, lit const& l) { + sat::literal ematch::instantiate(clause& c, unsigned generation, euf::enode* const* binding, lit const& l) { expr_ref_vector _binding(m); for (unsigned i = 0; i < c.num_decls(); ++i) _binding.push_back(binding[i]->get_expr()); var_subst subst(m); + euf::solver::scoped_generation sg(ctx, generation + 1); auto sub = [&](expr* e) { expr_ref r = subst(e, _binding); //ctx.rewrite(r); diff --git a/src/sat/smt/q_ematch.h b/src/sat/smt/q_ematch.h index c04107b3b91..cbeb34679bf 100644 --- a/src/sat/smt/q_ematch.h +++ b/src/sat/smt/q_ematch.h @@ -103,7 +103,7 @@ namespace q { void ensure_ground_enodes(clause const& c); void instantiate(binding& b); - sat::literal instantiate(clause& c, euf::enode* const* binding, lit const& l); + sat::literal instantiate(clause& c, unsigned generation, euf::enode* const* binding, lit const& l); // register as callback into egraph. void on_merge(euf::enode* root, euf::enode* other); From 147fb0d9c16beb0a8c8dc31dc32d70c3216ccab2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 21:41:44 -0800 Subject: [PATCH 149/597] fix tptp5 build --- examples/tptp/tptp5.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index 04b225caf90..1355cffa8bb 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -2313,7 +2313,8 @@ static void display_smt2(std::ostream& out) { for (size_t i = 0; i < asms.size(); ++i) { z3::expr fml = asms[i]; if (fml.is_and()) { - asms.set(i, fml.arg(0)); + z3::expr arg0 = fml.arg(0); + asms.set(i, arg0); for (unsigned j = 1; j < fml.num_args(); ++j) asms.push_back(fml.arg(j)); --i; From 529f116be084470af275715d7691143ee04c038e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 22:29:59 -0800 Subject: [PATCH 150/597] disable new code until pre-condition gets fixed Signed-off-by: Nikolaj Bjorner --- src/ast/static_features.cpp | 6 + src/ast/static_features.h | 4 +- src/math/simplex/model_based_opt.cpp | 9 +- src/smt/params/smt_params.cpp | 228 +++++++++++++++++++++++++++ src/smt/params/smt_params.h | 44 ++++++ src/smt/smt_setup.cpp | 198 +++-------------------- 6 files changed, 311 insertions(+), 178 deletions(-) diff --git a/src/ast/static_features.cpp b/src/ast/static_features.cpp index ad289cf5e4b..c5dedb16b59 100644 --- a/src/ast/static_features.cpp +++ b/src/ast/static_features.cpp @@ -665,3 +665,9 @@ void static_features::display(std::ostream & out) const { void static_features::get_feature_vector(vector & result) { } + +bool static_features::is_dense() const { + return + (m_num_uninterpreted_constants < 1000) && + (m_num_arith_eqs + m_num_arith_ineqs) > m_num_uninterpreted_constants * 9; +} diff --git a/src/ast/static_features.h b/src/ast/static_features.h index 92e0331fb7e..59c6154ef2d 100644 --- a/src/ast/static_features.h +++ b/src/ast/static_features.h @@ -188,7 +188,9 @@ struct static_features { void get_feature_vector(vector & result); bool has_uf() const; unsigned num_theories() const; - unsigned num_non_uf_theories() const; + unsigned num_non_uf_theories() const; + + bool is_dense() const; }; diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 919e07b163b..8df04327dc7 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -1462,11 +1462,14 @@ namespace opt { rational w_value = w == UINT_MAX ? offset : m_var2value[w]; -#if 1 - // V := (a * z_value - w_value) div rMod +#if 0 + // V := (a * z_value + w_value) div rMod // V*rMod <= a*z + w < (V+1)*rMod // v = a*z + w - V*rMod - SASSERT(a * z_value - w_value >= 0); + SASSERT(a > 0); + SASSERT(z_value >= 0); + SASSERT(w_value >= 0); + SASSERT(a * z_value + w_value >= 0); rational V = div(a * z_value + w_value, rMod); vector mod_coeffs; SASSERT(V >= 0); diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 8143bc31b21..2636d29f38e 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -188,3 +188,231 @@ void smt_params::validate_string_solver(symbol const& s) const { return; throw default_exception("Invalid string solver value. Legal values are z3str3, seq, empty, auto, none"); } + +void smt_params::setup_QF_UF() { + m_relevancy_lvl = 0; + m_nnf_cnf = false; + m_restart_strategy = RS_LUBY; + m_phase_selection = PS_CACHING_CONSERVATIVE2; + m_random_initial_activity = IA_RANDOM; +} + +void smt_params::setup_QF_RDL() { + m_relevancy_lvl = 0; + m_arith_eq2ineq = true; + m_arith_reflect = false; + m_arith_propagate_eqs = false; + m_nnf_cnf = false; +} + +void smt_params::setup_QF_RDL(static_features & st) { + +} + +void smt_params::setup_QF_IDL() { + m_relevancy_lvl = 0; + m_arith_eq2ineq = true; + m_arith_reflect = false; + m_arith_propagate_eqs = false; + m_arith_small_lemma_size = 30; + m_nnf_cnf = false; +} + +void smt_params::setup_QF_IDL(static_features & st) { + +} + +void smt_params::setup_QF_LRA() { + m_relevancy_lvl = 0; + m_arith_eq2ineq = true; + m_arith_reflect = false; + m_arith_propagate_eqs = false; + m_eliminate_term_ite = true; + m_nnf_cnf = false; + m_phase_selection = PS_THEORY; +} + +void smt_params::setup_QF_LRA(static_features const& st) { + m_relevancy_lvl = 0; + m_arith_eq2ineq = true; + m_arith_reflect = false; + m_arith_propagate_eqs = false; + m_eliminate_term_ite = true; + m_nnf_cnf = false; + if (numerator(st.m_arith_k_sum) > rational(2000000) && denominator(st.m_arith_k_sum) > rational(500)) { + m_relevancy_lvl = 2; + m_relevancy_lemma = false; + } + m_phase_selection = PS_THEORY; + if (!st.m_cnf) { + m_restart_strategy = RS_GEOMETRIC; + m_arith_stronger_lemmas = false; + m_restart_adaptive = false; + } + m_arith_small_lemma_size = 32; +} + +void smt_params::setup_QF_LIA() { + m_relevancy_lvl = 0; + m_arith_eq2ineq = true; + m_arith_reflect = false; + m_arith_propagate_eqs = false; + m_nnf_cnf = false; +} + +void smt_params::setup_QF_LIA(static_features const& st) { + m_relevancy_lvl = 0; + m_arith_eq2ineq = true; + m_arith_reflect = false; + m_arith_propagate_eqs = false; + m_nnf_cnf = false; + if (st.m_max_ite_tree_depth > 50) { + m_arith_eq2ineq = false; + m_pull_cheap_ite = true; + m_arith_propagate_eqs = true; + m_relevancy_lvl = 2; + m_relevancy_lemma = false; + } + else if (st.m_num_clauses == st.m_num_units) { + m_arith_gcd_test = false; + m_arith_branch_cut_ratio = 4; + m_relevancy_lvl = 2; + m_arith_eq2ineq = true; + m_eliminate_term_ite = true; + } + else { + m_eliminate_term_ite = true; + m_restart_adaptive = false; + m_restart_strategy = RS_GEOMETRIC; + m_restart_factor = 1.5; + } + if (st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses && st.m_cnf && st.m_arith_k_sum > rational(100000)) { + m_arith_bound_prop = bound_prop_mode::BP_NONE; + m_arith_stronger_lemmas = false; + } +} + +void smt_params::setup_QF_UFLIA() { + m_relevancy_lvl = 0; + m_arith_reflect = false; + m_nnf_cnf = false; + m_arith_propagation_threshold = 1000; +} + + +void smt_params::setup_QF_UFLRA() { + m_relevancy_lvl = 0; + m_arith_reflect = false; + m_nnf_cnf = false; +} + +void smt_params::setup_QF_BV() { + m_relevancy_lvl = 0; + m_arith_reflect = false; + m_bv_cc = false; + m_bb_ext_gates = true; + m_nnf_cnf = false; +} + +void smt_params::setup_QF_AUFBV() { + m_array_mode = AR_SIMPLE; + m_relevancy_lvl = 0; + m_bv_cc = false; + m_bb_ext_gates = true; + m_nnf_cnf = false; +} + +void smt_params::setup_QF_AX() { + m_array_mode = AR_SIMPLE; + m_nnf_cnf = false; +} + +void smt_params::setup_QF_AX(static_features const& st) { + m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; + m_nnf_cnf = false; + if (st.m_num_clauses == st.m_num_units) { + m_relevancy_lvl = 0; + m_phase_selection = PS_ALWAYS_FALSE; + } + else + m_relevancy_lvl = 2; +} + +void smt_params::setup_QF_AUFLIA() { + m_array_mode = AR_SIMPLE; + m_nnf_cnf = false; + m_relevancy_lvl = 2; + m_restart_strategy = RS_GEOMETRIC; + m_restart_factor = 1.5; + m_phase_selection = PS_CACHING_CONSERVATIVE2; +} + +void smt_params::setup_QF_AUFLIA(static_features const& st) { + m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; + if (st.m_has_real) + throw default_exception("Benchmark has real variables but it is marked as QF_AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); + m_nnf_cnf = false; + if (st.m_num_clauses == st.m_num_units) { + TRACE("QF_AUFLIA", tout << "using relevancy: 0\n";); + m_relevancy_lvl = 0; + m_phase_selection = PS_ALWAYS_FALSE; + } + else { + m_relevancy_lvl = 0; // it was 2, for some reason 2 doesn't work anymore TODO: investigate + m_restart_strategy = RS_GEOMETRIC; + m_restart_factor = 1.5; + m_phase_selection = PS_CACHING_CONSERVATIVE2; + m_random_initial_activity = IA_ZERO; + } +} + +void smt_params::setup_AUFLIA(bool simple_array) { + m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; + m_pi_use_database = true; + m_phase_selection = PS_ALWAYS_FALSE; + m_restart_strategy = RS_GEOMETRIC; + m_restart_factor = 1.5; + m_eliminate_bounds = true; + m_qi_quick_checker = MC_UNSAT; + m_qi_lazy_threshold = 20; + m_mbqi = true; // enabling MBQI and MACRO_FINDER by default :-) + + // MACRO_FINDER is a horrible for AUFLIA and UFNIA benchmarks (boogie benchmarks in general) + // It destroys the existing patterns. + // m_macro_finder = true; + + if (m_ng_lift_ite == lift_ite_kind::LI_NONE) + m_ng_lift_ite = lift_ite_kind::LI_CONSERVATIVE; +} + +void smt_params::setup_AUFLIA(static_features const & st) { + m_qi_eager_threshold = st.m_num_quantifiers_with_patterns == 0 ? 5 : 7; +} + +void smt_params::setup_AUFLIRA(bool simple_array) { + m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; + m_phase_selection = PS_ALWAYS_FALSE; + m_eliminate_bounds = true; + m_qi_quick_checker = MC_UNSAT; + m_qi_eager_threshold = 5; + // Added for MBQI release + m_qi_lazy_threshold = 20; + // + m_macro_finder = true; + if (m_ng_lift_ite == lift_ite_kind::LI_NONE) + m_ng_lift_ite = lift_ite_kind::LI_CONSERVATIVE; + m_pi_max_multi_patterns = 10; //<< it was used for SMT-COMP + m_array_lazy_ieq = true; + m_array_lazy_ieq_delay = 4; + // + m_mbqi = true; // enabling MBQI by default :-) + // +} + +void smt_params::setup_LRA() { + m_relevancy_lvl = 0; + m_arith_reflect = false; + m_arith_propagate_eqs = false; + m_eliminate_term_ite = true; +} + diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 73f556eb889..96e8c9ceb79 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -18,6 +18,7 @@ Revision History: --*/ #pragma once +#include "ast/static_features.h" #include "smt/params/dyn_ack_params.h" #include "smt/params/qi_params.h" #include "smt/params/theory_arith_params.h" @@ -254,6 +255,49 @@ struct smt_params : public preprocessor_params, void display(std::ostream & out) const; void validate_string_solver(symbol const& s) const; + + void setup_QF_UF(); + + void setup_QF_RDL(); + + void setup_QF_RDL(static_features & st); + + void setup_QF_IDL(); + + void setup_QF_IDL(static_features & st); + + void setup_QF_LRA(); + + void setup_QF_LRA(static_features const& st); + + void setup_QF_LIA(); + + void setup_QF_LIA(static_features const& st); + + void setup_QF_UFLIA(); + + void setup_QF_UFLRA(); + + void setup_QF_BV(); + + void setup_QF_AUFBV(); + + void setup_QF_AX(); + + void setup_QF_AX(static_features const& st); + + void setup_QF_AUFLIA(); + + void setup_QF_AUFLIA(static_features const& st); + + void setup_AUFLIA(bool simple_array); + + void setup_AUFLIA(static_features const & st); + + void setup_AUFLIRA(bool simple_array); + + void setup_LRA(); + }; diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 2d131c6ab7d..ef5f5493cff 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -212,11 +212,7 @@ namespace smt { } void setup::setup_QF_UF() { - m_params.m_relevancy_lvl = 0; - m_params.m_nnf_cnf = false; - m_params.m_restart_strategy = RS_LUBY; - m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; - m_params.m_random_initial_activity = IA_RANDOM; + m_params.setup_QF_UF(); } void setup::setup_QF_DT() { @@ -240,20 +236,10 @@ namespace smt { } void setup::setup_QF_RDL() { - m_params.m_relevancy_lvl = 0; - m_params.m_arith_eq2ineq = true; - m_params.m_arith_reflect = false; - m_params.m_arith_propagate_eqs = false; - m_params.m_nnf_cnf = false; + m_params.setup_QF_RDL(); setup_mi_arith(); } - static bool is_dense(static_features const & st) { - return - st.m_num_uninterpreted_constants < 1000 && - (st.m_num_arith_eqs + st.m_num_arith_ineqs) > st.m_num_uninterpreted_constants * 9; - } - static bool is_in_diff_logic(static_features const & st) { return st.m_num_arith_eqs == st.m_num_diff_eqs && @@ -285,7 +271,7 @@ namespace smt { m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; - if (is_dense(st)) { + if (st.is_dense()) { m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_adaptive = false; m_params.m_phase_selection = PS_CACHING; @@ -327,12 +313,7 @@ namespace smt { void setup::setup_QF_IDL() { TRACE("setup", tout << "setup_QF_IDL()\n";); - m_params.m_relevancy_lvl = 0; - m_params.m_arith_eq2ineq = true; - m_params.m_arith_reflect = false; - m_params.m_arith_propagate_eqs = false; - m_params.m_arith_small_lemma_size = 30; - m_params.m_nnf_cnf = false; + m_params.setup_QF_IDL(); setup_lra_arith(); } @@ -353,11 +334,11 @@ namespace smt { m_params.m_nnf_cnf = false; if (st.m_num_uninterpreted_constants > 5000) m_params.m_relevancy_lvl = 2; - else if (st.m_cnf && !is_dense(st)) + else if (st.m_cnf && !st.is_dense()) m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; else m_params.m_phase_selection = PS_CACHING; - if (is_dense(st) && st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses) { + if (st.is_dense() && st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses) { m_params.m_restart_adaptive = false; m_params.m_restart_strategy = RS_GEOMETRIC; } @@ -373,7 +354,7 @@ namespace smt { if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_context)); } - else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) { + else if (!m_params.m_arith_auto_config_simplex && st.is_dense()) { TRACE("setup", tout << "using dense diff logic...\n";); m_params.m_phase_selection = PS_CACHING_CONSERVATIVE; if (st.arith_k_sum_is_small()) @@ -418,7 +399,7 @@ namespace smt { if (st.m_num_uninterpreted_functions == 0) { m_params.m_arith_eq2ineq = true; m_params.m_arith_propagate_eqs = false; - if (is_dense(st)) { + if (st.is_dense()) { m_params.m_arith_small_lemma_size = 128; m_params.m_lemma_gc_half = true; m_params.m_restart_strategy = RS_GEOMETRIC; @@ -449,35 +430,13 @@ namespace smt { void setup::setup_QF_LRA() { TRACE("setup", tout << "setup_QF_LRA()\n";); - m_params.m_relevancy_lvl = 0; - m_params.m_arith_eq2ineq = true; - m_params.m_arith_reflect = false; - m_params.m_arith_propagate_eqs = false; - m_params.m_eliminate_term_ite = true; - m_params.m_nnf_cnf = false; - m_params.m_phase_selection = PS_THEORY; + m_params.setup_QF_LRA(); setup_lra_arith(); } void setup::setup_QF_LRA(static_features const & st) { check_no_uninterpreted_functions(st, "QF_LRA"); - m_params.m_relevancy_lvl = 0; - m_params.m_arith_eq2ineq = true; - m_params.m_arith_reflect = false; - m_params.m_arith_propagate_eqs = false; - m_params.m_eliminate_term_ite = true; - m_params.m_nnf_cnf = false; - if (numerator(st.m_arith_k_sum) > rational(2000000) && denominator(st.m_arith_k_sum) > rational(500)) { - m_params.m_relevancy_lvl = 2; - m_params.m_relevancy_lemma = false; - } - m_params.m_phase_selection = PS_THEORY; - if (!st.m_cnf) { - m_params.m_restart_strategy = RS_GEOMETRIC; - m_params.m_arith_stronger_lemmas = false; - m_params.m_restart_adaptive = false; - } - m_params.m_arith_small_lemma_size = 32; + m_params.setup_QF_LRA(st); setup_lra_arith(); } @@ -487,56 +446,20 @@ namespace smt { void setup::setup_QF_LIA() { TRACE("setup", tout << "setup_QF_LIA(st)\n";); - m_params.m_relevancy_lvl = 0; - m_params.m_arith_eq2ineq = true; - m_params.m_arith_reflect = false; - m_params.m_arith_propagate_eqs = false; - m_params.m_nnf_cnf = false; + m_params.setup_QF_LIA(); setup_lra_arith(); } void setup::setup_QF_LIA(static_features const & st) { check_no_uninterpreted_functions(st, "QF_LIA"); TRACE("setup", tout << "QF_LIA setup\n";); - - m_params.m_relevancy_lvl = 0; - m_params.m_arith_eq2ineq = true; - m_params.m_arith_reflect = false; - m_params.m_arith_propagate_eqs = false; - m_params.m_nnf_cnf = false; - if (st.m_max_ite_tree_depth > 50) { - m_params.m_arith_eq2ineq = false; - m_params.m_pull_cheap_ite = true; - m_params.m_arith_propagate_eqs = true; - m_params.m_relevancy_lvl = 2; - m_params.m_relevancy_lemma = false; - } - else if (st.m_num_clauses == st.m_num_units) { - m_params.m_arith_gcd_test = false; - m_params.m_arith_branch_cut_ratio = 4; - m_params.m_relevancy_lvl = 2; - m_params.m_arith_eq2ineq = true; - m_params.m_eliminate_term_ite = true; - } - else { - m_params.m_eliminate_term_ite = true; - m_params.m_restart_adaptive = false; - m_params.m_restart_strategy = RS_GEOMETRIC; - m_params.m_restart_factor = 1.5; - } - if (st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses && st.m_cnf && st.m_arith_k_sum > rational(100000)) { - m_params.m_arith_bound_prop = bound_prop_mode::BP_NONE; - m_params.m_arith_stronger_lemmas = false; - } + m_params.setup_QF_LIA(st); setup_lra_arith(); } void setup::setup_QF_UFLIA() { - m_params.m_relevancy_lvl = 0; - m_params.m_arith_reflect = false; - m_params.m_nnf_cnf = false; - m_params.m_arith_propagation_threshold = 1000; setup_lra_arith(); + m_params.setup_QF_UFLIA(); } void setup::setup_QF_UFLIA(static_features & st) { @@ -548,103 +471,49 @@ namespace smt { } void setup::setup_QF_UFLRA() { - m_params.m_relevancy_lvl = 0; - m_params.m_arith_reflect = false; - m_params.m_nnf_cnf = false; + m_params.setup_QF_UFLRA(); setup_lra_arith(); } void setup::setup_QF_BV() { TRACE("setup", tout << "qf-bv\n";); - m_params.m_relevancy_lvl = 0; - m_params.m_arith_reflect = false; - m_params.m_bv_cc = false; - m_params.m_bb_ext_gates = true; - m_params.m_nnf_cnf = false; + m_params.setup_QF_BV(); m_context.register_plugin(alloc(smt::theory_bv, m_context)); } void setup::setup_QF_AUFBV() { - m_params.m_array_mode = AR_SIMPLE; - m_params.m_relevancy_lvl = 0; - m_params.m_bv_cc = false; - m_params.m_bb_ext_gates = true; - m_params.m_nnf_cnf = false; + m_params.setup_QF_AUFBV(); m_context.register_plugin(alloc(smt::theory_bv, m_context)); setup_arrays(); } void setup::setup_QF_AX() { TRACE("setup", tout << "QF_AX\n";); - m_params.m_array_mode = AR_SIMPLE; - m_params.m_nnf_cnf = false; + m_params.setup_QF_AX(); setup_arrays(); } void setup::setup_QF_AX(static_features const & st) { - m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; - m_params.m_nnf_cnf = false; - if (st.m_num_clauses == st.m_num_units) { - m_params.m_relevancy_lvl = 0; - m_params.m_phase_selection = PS_ALWAYS_FALSE; - } - else { - m_params.m_relevancy_lvl = 2; - } + m_params.setup_QF_AX(st); setup_arrays(); } void setup::setup_QF_AUFLIA() { TRACE("QF_AUFLIA", tout << "no static features\n";); - m_params.m_array_mode = AR_SIMPLE; - m_params.m_nnf_cnf = false; - m_params.m_relevancy_lvl = 2; - m_params.m_restart_strategy = RS_GEOMETRIC; - m_params.m_restart_factor = 1.5; - m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; + m_params.setup_QF_AUFLIA(); setup_i_arith(); setup_arrays(); } void setup::setup_QF_AUFLIA(static_features const & st) { - m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; - if (st.m_has_real) - throw default_exception("Benchmark has real variables but it is marked as QF_AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); - m_params.m_nnf_cnf = false; - if (st.m_num_clauses == st.m_num_units) { - TRACE("QF_AUFLIA", tout << "using relevancy: 0\n";); - m_params.m_relevancy_lvl = 0; - m_params.m_phase_selection = PS_ALWAYS_FALSE; - } - else { - m_params.m_relevancy_lvl = 0; // it was 2, for some reason 2 doesn't work anymore TODO: investigate - m_params.m_restart_strategy = RS_GEOMETRIC; - m_params.m_restart_factor = 1.5; - m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; - m_params.m_random_initial_activity = IA_ZERO; - } + m_params.setup_QF_AUFLIA(st); setup_i_arith(); setup_arrays(); } void setup::setup_AUFLIA(bool simple_array) { TRACE("setup", tout << "AUFLIA\n";); - m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; - m_params.m_pi_use_database = true; - m_params.m_phase_selection = PS_ALWAYS_FALSE; - m_params.m_restart_strategy = RS_GEOMETRIC; - m_params.m_restart_factor = 1.5; - m_params.m_eliminate_bounds = true; - m_params.m_qi_quick_checker = MC_UNSAT; - m_params.m_qi_lazy_threshold = 20; - m_params.m_mbqi = true; // enabling MBQI and MACRO_FINDER by default :-) - - // MACRO_FINDER is a horrible for AUFLIA and UFNIA benchmarks (boogie benchmarks in general) - // It destroys the existing patterns. - // m_params.m_macro_finder = true; - - if (m_params.m_ng_lift_ite == lift_ite_kind::LI_NONE) - m_params.m_ng_lift_ite = lift_ite_kind::LI_CONSERVATIVE; + m_params.setup_AUFLIA(simple_array); TRACE("setup", tout << "max_eager_multipatterns: " << m_params.m_qi_max_eager_multipatterns << "\n";); m_context.register_plugin(alloc(smt::theory_i_arith, m_context)); setup_arrays(); @@ -653,29 +522,13 @@ namespace smt { void setup::setup_AUFLIA(static_features const & st) { if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); - m_params.m_qi_eager_threshold = st.m_num_quantifiers_with_patterns == 0 ? 5 : 7; + m_params.setup_AUFLIA(st); setup_AUFLIA(); } void setup::setup_AUFLIRA(bool simple_array) { TRACE("setup", tout << "AUFLIRA\n";); - m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; - m_params.m_phase_selection = PS_ALWAYS_FALSE; - m_params.m_eliminate_bounds = true; - m_params.m_qi_quick_checker = MC_UNSAT; - m_params.m_qi_eager_threshold = 5; - // Added for MBQI release - m_params.m_qi_lazy_threshold = 20; - // - m_params.m_macro_finder = true; - if (m_params.m_ng_lift_ite == lift_ite_kind::LI_NONE) - m_params.m_ng_lift_ite = lift_ite_kind::LI_CONSERVATIVE; - m_params.m_pi_max_multi_patterns = 10; //<< it was used for SMT-COMP - m_params.m_array_lazy_ieq = true; - m_params.m_array_lazy_ieq_delay = 4; - // - m_params.m_mbqi = true; // enabling MBQI by default :-) - // + m_params.setup_AUFLIRA(simple_array); setup_mi_arith(); setup_arrays(); } @@ -697,10 +550,7 @@ namespace smt { } void setup::setup_LRA() { - m_params.m_relevancy_lvl = 0; - m_params.m_arith_reflect = false; - m_params.m_arith_propagate_eqs = false; - m_params.m_eliminate_term_ite = true; + m_params.setup_LRA(); setup_mi_arith(); } From 847aec1d300b44dd6c28ec39179a71b96bfcfe5f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 22:48:10 -0800 Subject: [PATCH 151/597] update dependencies --- scripts/mk_project.py | 2 +- src/smt/params/CMakeLists.txt | 1 + src/smt/params/smt_params.cpp | 12 ++++++++++++ src/smt/params/smt_params.h | 2 ++ src/smt/smt_setup.cpp | 12 +----------- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 2da7b0f21a6..a979359f303 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -21,10 +21,10 @@ def init_project_def(): add_lib('hilbert', ['util'], 'math/hilbert') add_lib('automata', ['util'], 'math/automata') add_lib('params', ['util']) - add_lib('smt_params', ['params'], 'smt/params') add_lib('realclosure', ['interval'], 'math/realclosure') add_lib('subpaving', ['interval'], 'math/subpaving') add_lib('ast', ['util', 'polynomial']) + add_lib('smt_params', ['ast', 'params'], 'smt/params') add_lib('parser_util', ['ast'], 'parsers/util') add_lib('euf', ['ast'], 'ast/euf') add_lib('grobner', ['ast', 'dd', 'simplex'], 'math/grobner') diff --git a/src/smt/params/CMakeLists.txt b/src/smt/params/CMakeLists.txt index 0be62f82013..d7ebb2be2d0 100644 --- a/src/smt/params/CMakeLists.txt +++ b/src/smt/params/CMakeLists.txt @@ -12,6 +12,7 @@ z3_add_component(smt_params theory_str_params.cpp COMPONENT_DEPENDENCIES params + ast PYG_FILES smt_params_helper.pyg ) diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 2636d29f38e..37249fdaca2 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -292,6 +292,18 @@ void smt_params::setup_QF_LIA(static_features const& st) { } } +void smt_params::setup_QF_UFIDL() { + m_relevancy_lvl = 0; + m_arith_reflect = false; + m_nnf_cnf = false; + m_arith_eq_bounds = true; + m_arith_eq2ineq = true; + // m_params.m_phase_selection = PS_THEORY; + m_restart_strategy = RS_GEOMETRIC; + m_restart_factor = 1.5; + m_restart_adaptive = false; +} + void smt_params::setup_QF_UFLIA() { m_relevancy_lvl = 0; m_arith_reflect = false; diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 96e8c9ceb79..07b6b609553 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -274,6 +274,8 @@ struct smt_params : public preprocessor_params, void setup_QF_LIA(static_features const& st); + void setup_QF_UFIDL(); + void setup_QF_UFLIA(); void setup_QF_UFLRA(); diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index ef5f5493cff..aed515ef066 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -377,15 +377,7 @@ namespace smt { void setup::setup_QF_UFIDL() { TRACE("setup", tout << "setup_QF_UFIDL()\n";); - m_params.m_relevancy_lvl = 0; - m_params.m_arith_reflect = false; - m_params.m_nnf_cnf = false; - m_params.m_arith_eq_bounds = true; - m_params.m_arith_eq2ineq = true; - // m_params.m_phase_selection = PS_THEORY; - m_params.m_restart_strategy = RS_GEOMETRIC; - m_params.m_restart_factor = 1.5; - m_params.m_restart_adaptive = false; + m_params.setup_QF_UFIDL(); setup_lra_arith(); } @@ -624,8 +616,6 @@ namespace smt { } } - - void setup::setup_arith() { static_features st(m_manager); IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); From e3e2c21632bcc15ba2973d0208bd47c11b3a73ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 22:53:14 -0800 Subject: [PATCH 152/597] Create cnf_nnf.h --- src/ast/simplifiers/cnf_nnf.h | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/ast/simplifiers/cnf_nnf.h diff --git a/src/ast/simplifiers/cnf_nnf.h b/src/ast/simplifiers/cnf_nnf.h new file mode 100644 index 00000000000..eb80387d6e5 --- /dev/null +++ b/src/ast/simplifiers/cnf_nnf.h @@ -0,0 +1,57 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + cnf_nnf.h + +Abstract: + + pull nested quantifiers + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/normal_forms/pull_quant.h" + + +class cnf_nnf_simplifier : public dependent_expr_simplifier { + + defined_names m_defined_names; + +public: + cnf_nnf_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + m_pull(m) { + } + + char const* name() const override { return "cnf-nnf"; } + + void reduce() override { + nnf apply_nnf(m, m_defined_names); + expr_ref_vector push_todo(m); + proof_ref_vector push_todo_prs(m); + proof_ref pr(m); + expr_ref r(m); + unsigned sz = qtail(); + for (unsigned i = qhead(); i < sz && m.inc(); ++i) { + auto d = m_fmls[idx]; + push_todo.reset(); + push_todo_prs.reset(); + apply_nnf(d.fml(), push_todo, push_todo_prs, r, pr); + m_fmls.update(i, dependent_expr(m, r, d.dep())); + for (expr* f : m_push_todo) { + if (!m.inc()) + break; + m_rewriter(f, r, pr); + m_fmls.add(i, depdendent_expr(m, r, d.dep())); + } + } + } +}; From e5984dd3979ece320b465e5ed65538ae644e737b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Nov 2022 23:04:38 -0800 Subject: [PATCH 153/597] add cnf/nnf simplifier --- src/ast/simplifiers/cnf_nnf.h | 17 ++++++++++++----- src/sat/sat_solver/sat_smt_preprocess.cpp | 3 +++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ast/simplifiers/cnf_nnf.h b/src/ast/simplifiers/cnf_nnf.h index eb80387d6e5..56cdc1367e1 100644 --- a/src/ast/simplifiers/cnf_nnf.h +++ b/src/ast/simplifiers/cnf_nnf.h @@ -18,17 +18,20 @@ Module Name: #pragma once #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/normal_forms/pull_quant.h" +#include "ast/normal_forms/nnf.h" +#include "ast/rewriter/th_rewriter.h" class cnf_nnf_simplifier : public dependent_expr_simplifier { defined_names m_defined_names; + th_rewriter m_rewriter; public: cnf_nnf_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls), - m_pull(m) { + m_defined_names(m), + m_rewriter(m, p){ } char const* name() const override { return "cnf-nnf"; } @@ -41,17 +44,21 @@ class cnf_nnf_simplifier : public dependent_expr_simplifier { expr_ref r(m); unsigned sz = qtail(); for (unsigned i = qhead(); i < sz && m.inc(); ++i) { - auto d = m_fmls[idx]; + auto d = m_fmls[i]; push_todo.reset(); push_todo_prs.reset(); apply_nnf(d.fml(), push_todo, push_todo_prs, r, pr); m_fmls.update(i, dependent_expr(m, r, d.dep())); - for (expr* f : m_push_todo) { + for (expr* f : push_todo) { if (!m.inc()) break; m_rewriter(f, r, pr); - m_fmls.add(i, depdendent_expr(m, r, d.dep())); + m_fmls.add(dependent_expr(m, r, d.dep())); } } } + + void push() override { dependent_expr_simplifier::push(); m_defined_names.push(); } + + void pop(unsigned n) override { dependent_expr_simplifier::pop(n); m_defined_names.pop(n); } }; diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index e3d6b3edba2..7c716c48160 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -34,6 +34,7 @@ Module Name: #include "ast/simplifiers/push_ite.h" #include "ast/simplifiers/elim_term_ite.h" #include "ast/simplifiers/flatten_clauses.h" +#include "ast/simplifiers/cnf_nnf.h" #include "sat/sat_params.hpp" #include "smt/params/smt_params.h" #include "sat/sat_solver/sat_smt_preprocess.h" @@ -48,6 +49,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep s.add_simplifier(alloc(propagate_values, m, p, st)); s.add_simplifier(alloc(euf::solve_eqs, m, st)); s.add_simplifier(alloc(elim_unconstrained, m, st)); + if (smtp.m_nnf_cnf) s.add_simplifier(alloc(cnf_nnf_simplifier, m, p, st)); if (smtp.m_macro_finder || smtp.m_quasi_macros) s.add_simplifier(alloc(eliminate_predicates, m, st)); if (smtp.m_qe_lite) s.add_simplifier(mk_qe_lite_simplifer(m, p, st)); if (smtp.m_pull_nested_quantifiers) s.add_simplifier(alloc(pull_nested_quantifiers_simplifier, m, p, st)); @@ -62,6 +64,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep if (smtp.m_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(push_ite_simplifier, m, p, st, smtp.m_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); if (smtp.m_ng_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(ng_push_ite_simplifier, m, p, st, smtp.m_ng_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); s.add_simplifier(alloc(flatten_clauses, m, p, st)); + // // add: // euf_completion? From a96b7d243afd01a130a804570cb6b193f546dd92 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 1 Dec 2022 00:04:08 -0800 Subject: [PATCH 154/597] remove incorrect check for quantifier --- src/ast/simplifiers/bit2int.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ast/simplifiers/bit2int.h b/src/ast/simplifiers/bit2int.h index 7d07029d225..6605c2e7ef4 100644 --- a/src/ast/simplifiers/bit2int.h +++ b/src/ast/simplifiers/bit2int.h @@ -34,8 +34,6 @@ class bit2int_simplifier : public dependent_expr_simplifier { proof_ref pr(m); for (unsigned idx : indices()) { auto const& d = m_fmls[idx]; - if (!has_quantifiers(d.fml())) - continue; m_rewriter(d.fml(), r, pr); m_fmls.update(idx, dependent_expr(m, r, d.dep())); } From 54a8d656179fc092cc62bf9652700e06821cc429 Mon Sep 17 00:00:00 2001 From: yizhou7 Date: Fri, 2 Dec 2022 16:56:53 -0500 Subject: [PATCH 155/597] move flushes in display_statistics (#6472) --- src/shell/smtlib_frontend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index d0d0b452dd0..220001b4044 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -44,12 +44,12 @@ static void display_statistics() { lock_guard lock(*display_stats_mux); clock_t end_time = clock(); if (g_cmd_context && g_display_statistics) { - std::cout.flush(); - std::cerr.flush(); if (g_cmd_context) { g_cmd_context->set_regular_stream("stdout"); g_cmd_context->display_statistics(true, ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC)); } + std::cout.flush(); + std::cerr.flush(); } } From 5073959ae02b1ff062ffd43a4d62af219db7ce34 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Dec 2022 07:52:17 -0800 Subject: [PATCH 156/597] add macro attribute --- src/ast/recfun_decl_plugin.cpp | 1 + src/ast/recfun_decl_plugin.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 5584885926e..86c3fcf3bd9 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -408,6 +408,7 @@ namespace recfun { void promise_def::set_definition(replace& r, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) { SASSERT(n_vars == d->get_arity()); + d->m_is_macro = is_macro; is_imm_pred is_i(*u); d->compute_cases(*u, r, is_i, is_macro, n_vars, vars, rhs); } diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index c369e282722..bb75a854cb2 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -117,6 +117,7 @@ namespace recfun { func_decl_ref m_decl; //!< generic declaration expr_ref m_rhs; //!< definition family_id m_fid; + bool m_is_macro; def(ast_manager &m, family_id fid, symbol const & s, unsigned arity, sort *const * domain, sort* range, bool is_generated); @@ -138,6 +139,7 @@ namespace recfun { bool is_fun_macro() const { return m_cases.size() == 1; } bool is_fun_defined() const { return !is_fun_macro(); } + bool is_macro() const { return m_is_macro; } def* copy(util& dst, ast_translation& tr); From cf7bba62880c3f9a215d20e05b757a16a5da0a23 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Dec 2022 07:53:32 -0800 Subject: [PATCH 157/597] use ast_manager as an attribute --- src/ast/rewriter/arith_rewriter.cpp | 226 +++++++++---------- src/ast/rewriter/arith_rewriter.h | 24 +- src/ast/rewriter/bv_rewriter.cpp | 321 +++++++++++++-------------- src/ast/rewriter/bv_rewriter.h | 13 +- src/ast/rewriter/poly_rewriter.h | 1 - src/ast/rewriter/poly_rewriter_def.h | 42 ++-- 6 files changed, 311 insertions(+), 316 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index cdf09d7f388..c2a9b4cf4cd 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -24,7 +24,7 @@ Module Name: seq_util& arith_rewriter_core::seq() { if (!m_seq) { - m_seq = alloc(seq_util, m()); + m_seq = alloc(seq_util, m); } return *m_seq; } @@ -93,9 +93,9 @@ br_status arith_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c case OP_TANH: SASSERT(num_args == 1); st = mk_tanh_core(args[0], result); break; default: st = BR_FAILED; break; } - CTRACE("arith_rewriter", st != BR_FAILED, tout << st << ": " << mk_pp(f, m()); - for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m()) << " "; - tout << "\n==>\n" << mk_pp(result.get(), m()) << "\n"; + CTRACE("arith_rewriter", st != BR_FAILED, tout << st << ": " << mk_pp(f, m); + for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m) << " "; + tout << "\n==>\n" << mk_pp(result.get(), m) << "\n"; if (is_app(result)) tout << "args: " << to_app(result)->get_num_args() << "\n"; ); return st; @@ -133,7 +133,7 @@ bool arith_rewriter::div_polynomial(expr * t, numeral const & g, const_treatment SASSERT(!g.is_one()); unsigned sz; expr * const * ms = get_monomials(t, sz); - expr_ref_buffer new_args(m()); + expr_ref_buffer new_args(m); numeral a; for (unsigned i = 0; i < sz; i++) { expr * arg = ms[i]; @@ -196,10 +196,10 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & switch (kind) { case LE: c = floor(c); break; case GE: c = ceil(c); break; - case EQ: result = m().mk_false(); return true; + case EQ: result = m.mk_false(); return true; } } - expr_ref k(m_util.mk_numeral(c, is_int), m()); + expr_ref k(m_util.mk_numeral(c, is_int), m); switch (kind) { case LE: result = m_util.mk_le(pp, k); return true; case GE: result = m_util.mk_ge(pp, k); return true; @@ -223,24 +223,24 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & if (c.is_neg()) { switch (kind) { case EQ: - case LE: result = m().mk_false(); return true; - case GE: result = m().mk_true(); return true; + case LE: result = m.mk_false(); return true; + case GE: result = m.mk_true(); return true; } } if (c.is_zero() && kind == GE) { - result = m().mk_true(); + result = m.mk_true(); return true; } if (c.is_pos() && c >= abs(b)) { switch (kind) { - case LE: result = m().mk_true(); return true; + case LE: result = m.mk_true(); return true; case EQ: - case GE: result = m().mk_false(); return true; + case GE: result = m.mk_false(); return true; } } // mod x b <= b - 1 if (c + rational::one() == abs(b) && kind == LE) { - result = m().mk_true(); + result = m.mk_true(); return true; } } @@ -304,7 +304,7 @@ br_status arith_rewriter::is_separated(expr* arg1, expr* arg2, op_kind kind, exp if (kind != LE && kind != GE) return BR_FAILED; rational bound(0), r1, r2; - expr_ref narg(m()); + expr_ref narg(m); bool has_bound = true; if (!m_util.is_numeral(arg2, r2)) return BR_FAILED; @@ -335,47 +335,47 @@ br_status arith_rewriter::is_separated(expr* arg1, expr* arg2, op_kind kind, exp if (kind == GE && r1 > r2) return BR_FAILED; if (kind == LE && r1 > r2) { - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } if (kind == GE && r1 < r2) { - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } SASSERT(r1 == r2); - expr_ref zero(m_util.mk_numeral(rational(0), arg1->get_sort()), m()); + expr_ref zero(m_util.mk_numeral(rational(0), arg1->get_sort()), m); if (r1.is_zero() && m_util.is_mul(arg1)) { - expr_ref_buffer eqs(m()); + expr_ref_buffer eqs(m); ptr_buffer args; flat_mul(arg1, args); for (expr* arg : args) { if (m_util.is_numeral(arg)) continue; - eqs.push_back(m().mk_eq(arg, zero)); + eqs.push_back(m.mk_eq(arg, zero)); } - result = m().mk_or(eqs); + result = m.mk_or(eqs); return BR_REWRITE2; } if (kind == LE && m_util.is_add(arg1)) { - expr_ref_buffer leqs(m()); + expr_ref_buffer leqs(m); for (expr* arg : *to_app(arg1)) { if (!m_util.is_numeral(arg)) leqs.push_back(m_util.mk_le(arg, zero)); } - result = m().mk_and(leqs); + result = m.mk_and(leqs); return BR_REWRITE2; } if (kind == GE && m_util.is_add(arg1)) { - expr_ref_buffer geqs(m()); + expr_ref_buffer geqs(m); for (expr* arg : *to_app(arg1)) { if (!m_util.is_numeral(arg)) geqs.push_back(m_util.mk_ge(arg, zero)); } - result = m().mk_and(geqs); + result = m.mk_and(geqs); return BR_REWRITE2; } @@ -399,8 +399,8 @@ bool arith_rewriter::elim_to_real_var(expr * var, expr_ref & new_var) { bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial) { if (m_util.is_mul(monomial)) { - expr_ref_buffer new_vars(m()); - expr_ref new_var(m()); + expr_ref_buffer new_vars(m); + expr_ref new_var(m); unsigned num = to_app(monomial)->get_num_args(); for (unsigned i = 0; i < num; i++) { if (!elim_to_real_var(to_app(monomial)->get_arg(i), new_var)) @@ -417,8 +417,8 @@ bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial) bool arith_rewriter::elim_to_real_pol(expr * p, expr_ref & new_p) { if (m_util.is_add(p)) { - expr_ref_buffer new_monomials(m()); - expr_ref new_monomial(m()); + expr_ref_buffer new_monomials(m); + expr_ref new_monomial(m); for (expr* arg : *to_app(p)) { if (!elim_to_real_mon(arg, new_monomial)) return false; @@ -507,14 +507,14 @@ br_status arith_rewriter::reduce_power(expr * arg1, expr * arg2, op_kind kind, e switch (kind) { case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_REWRITE1; case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_REWRITE1; - default: result = m().mk_eq(new_arg1, new_arg2); return BR_REWRITE1; + default: result = m.mk_eq(new_arg1, new_arg2); return BR_REWRITE1; } } br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { expr *orig_arg1 = arg1, *orig_arg2 = arg2; - expr_ref new_arg1(m()); - expr_ref new_arg2(m()); + expr_ref new_arg1(m); + expr_ref new_arg2(m); if ((is_zero(arg1) && is_reduce_power_target(arg2, kind == EQ)) || (is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ))) return reduce_power(arg1, arg2, kind, result); @@ -524,29 +524,29 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin arg1 = new_arg1; arg2 = new_arg2; } - expr_ref new_new_arg1(m()); - expr_ref new_new_arg2(m()); + expr_ref new_new_arg1(m); + expr_ref new_new_arg2(m); if (m_elim_to_real && elim_to_real(arg1, arg2, new_new_arg1, new_new_arg2)) { arg1 = new_new_arg1; arg2 = new_new_arg2; - CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); + CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n";); if (st == BR_FAILED) st = BR_DONE; } numeral a1, a2; if (is_numeral(arg1, a1) && is_numeral(arg2, a2)) { switch (kind) { - case LE: result = a1 <= a2 ? m().mk_true() : m().mk_false(); return BR_DONE; - case GE: result = a1 >= a2 ? m().mk_true() : m().mk_false(); return BR_DONE; - default: result = a1 == a2 ? m().mk_true() : m().mk_false(); return BR_DONE; + case LE: result = a1 <= a2 ? m.mk_true() : m.mk_false(); return BR_DONE; + case GE: result = a1 >= a2 ? m.mk_true() : m.mk_false(); return BR_DONE; + default: result = a1 == a2 ? m.mk_true() : m.mk_false(); return BR_DONE; } } #define ANUM_LE_GE_EQ() { \ switch (kind) { \ - case LE: result = am.le(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ - case GE: result = am.ge(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ - default: result = am.eq(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ + case LE: result = am.le(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \ + case GE: result = am.ge(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \ + default: result = am.eq(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \ } \ } @@ -593,12 +593,12 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin if (!first && !g.is_one() && num_consts <= 1) { bool is_sat = div_polynomial(arg1, g, (kind == LE ? CT_CEIL : (kind == GE ? CT_FLOOR : CT_FALSE)), new_arg1); if (!is_sat) { - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } is_sat = div_polynomial(arg2, g, (kind == LE ? CT_FLOOR : (kind == GE ? CT_CEIL : CT_FALSE)), new_arg2); if (!is_sat) { - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } arg1 = new_arg1.get(); @@ -607,25 +607,25 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin } } expr* c = nullptr, *t = nullptr, *e = nullptr; - if (m().is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) { + if (m.is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) { switch (kind) { - case LE: result = a1 <= a2 ? m().mk_or(c, m_util.mk_le(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2; - case GE: result = a1 >= a2 ? m().mk_or(c, m_util.mk_ge(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2; - case EQ: result = a1 == a2 ? m().mk_or(c, m().mk_eq(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2; + case LE: result = a1 <= a2 ? m.mk_or(c, m_util.mk_le(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2; + case GE: result = a1 >= a2 ? m.mk_or(c, m_util.mk_ge(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2; + case EQ: result = a1 == a2 ? m.mk_or(c, m.mk_eq(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2; } } - if (m().is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) { + if (m.is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) { switch (kind) { - case LE: result = a1 <= a2 ? m().mk_or(m().mk_not(c), m_util.mk_le(t, arg2)) : m().mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2; - case GE: result = a1 >= a2 ? m().mk_or(m().mk_not(c), m_util.mk_ge(t, arg2)) : m().mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2; - case EQ: result = a1 == a2 ? m().mk_or(m().mk_not(c), m().mk_eq(t, arg2)) : m().mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2; + case LE: result = a1 <= a2 ? m.mk_or(m.mk_not(c), m_util.mk_le(t, arg2)) : m.mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2; + case GE: result = a1 >= a2 ? m.mk_or(m.mk_not(c), m_util.mk_ge(t, arg2)) : m.mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2; + case EQ: result = a1 == a2 ? m.mk_or(m.mk_not(c), m.mk_eq(t, arg2)) : m.mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2; } } - if (m().is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) { + if (m.is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) { switch (kind) { - case LE: result = m().mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2; - case GE: result = m().mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2; - case EQ: result = m().mk_ite(c, m().mk_eq(t, arg2), m().mk_eq(e, arg2)); return BR_REWRITE2; + case LE: result = m.mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2; + case GE: result = m.mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2; + case EQ: result = m.mk_ite(c, m.mk_eq(t, arg2), m.mk_eq(e, arg2)); return BR_REWRITE2; } } if (m_util.is_to_int(arg2) && is_numeral(arg1)) { @@ -642,7 +642,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin return BR_REWRITE1; case EQ: result = m_util.mk_ge(t, m_util.mk_numeral(a2, false)); - result = m().mk_and(m_util.mk_lt(t, m_util.mk_numeral(a2+1, false)), result); + result = m.mk_and(m_util.mk_lt(t, m_util.mk_numeral(a2+1, false)), result); return BR_REWRITE3; } } @@ -663,7 +663,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin switch (kind) { case LE: result = m_util.mk_le(arg1, arg2); return BR_DONE; case GE: result = m_util.mk_ge(arg1, arg2); return BR_DONE; - default: result = m().mk_eq(arg1, arg2); return BR_DONE; + default: result = m.mk_eq(arg1, arg2); return BR_DONE; } } return BR_FAILED; @@ -674,7 +674,7 @@ br_status arith_rewriter::mk_le_core(expr * arg1, expr * arg2, expr_ref & result } br_status arith_rewriter::mk_lt_core(expr * arg1, expr * arg2, expr_ref & result) { - result = m().mk_not(m_util.mk_le(arg2, arg1)); + result = m.mk_not(m_util.mk_le(arg2, arg1)); return BR_REWRITE2; } @@ -683,7 +683,7 @@ br_status arith_rewriter::mk_ge_core(expr * arg1, expr * arg2, expr_ref & result } br_status arith_rewriter::mk_gt_core(expr * arg1, expr * arg2, expr_ref & result) { - result = m().mk_not(m_util.mk_le(arg1, arg2)); + result = m.mk_not(m_util.mk_le(arg1, arg2)); return BR_REWRITE2; } @@ -694,7 +694,7 @@ bool arith_rewriter::is_arith_term(expr * n) const { br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { br_status st = BR_FAILED; if (m_eq2ineq) { - result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2)); + result = m.mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2)); st = BR_REWRITE2; } else if (m_arith_lhs || is_arith_term(arg1) || is_arith_term(arg2)) { @@ -724,7 +724,7 @@ br_status arith_rewriter::mk_and_core(unsigned n, expr* const* args, expr_ref& r } if (rest.size() < n - 1) { rest.push_back(arg0); - result = m().mk_and(rest); + result = m.mk_and(rest); return BR_REWRITE1; } } @@ -742,8 +742,8 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) { rational a, b; rational g = gcd(p, k, a, b); if (g == 1) { - expr_ref nb(m_util.mk_numeral(b, true), m()); - result = m().mk_eq(m_util.mk_mod(u, y), + expr_ref nb(m_util.mk_numeral(b, true), m); + result = m.mk_eq(m_util.mk_mod(u, y), m_util.mk_mod(m_util.mk_mul(nb, arg2), y)); return true; } @@ -752,7 +752,7 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) { } expr_ref arith_rewriter::neg_monomial(expr* e) const { - expr_ref_vector args(m()); + expr_ref_vector args(m); rational a1; if (m_util.is_numeral(e, a1)) args.push_back(m_util.mk_numeral(-a1, e->get_sort())); @@ -773,10 +773,10 @@ expr_ref arith_rewriter::neg_monomial(expr* e) const { args.push_back(e); } if (args.size() == 1) { - return expr_ref(args.back(), m()); + return expr_ref(args.back(), m); } else { - return expr_ref(m_util.mk_mul(args.size(), args.data()), m()); + return expr_ref(m_util.mk_mul(args.size(), args.data()), m); } } @@ -793,7 +793,7 @@ bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) const { expr * t2 = to_app(t)->get_arg(0); if (m_util.is_mul(t2) && is_numeral(to_app(t2)->get_arg(0), r) && r.is_neg()) { - expr_ref_vector args1(m()); + expr_ref_vector args1(m); for (expr* e1 : *to_app(t)) { args1.push_back(neg_monomial(e1)); } @@ -826,7 +826,7 @@ bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args) br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) { if (is_anum_simp_target(num_args, args)) { - expr_ref_buffer new_args(m()); + expr_ref_buffer new_args(m); anum_manager & am = m_util.am(); scoped_anum r(am); scoped_anum arg(am); @@ -864,7 +864,7 @@ br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, ex new_args.push_back(m_util.mk_numeral(am, r, false)); br_status st = poly_rewriter::mk_add_core(new_args.size(), new_args.data(), result); if (st == BR_FAILED) { - result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data()); + result = m.mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data()); return BR_DONE; } return st; @@ -876,7 +876,7 @@ br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, ex br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { if (is_anum_simp_target(num_args, args)) { - expr_ref_buffer new_args(m()); + expr_ref_buffer new_args(m); anum_manager & am = m_util.am(); scoped_anum r(am); scoped_anum arg(am); @@ -913,7 +913,7 @@ br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, ex br_status st = poly_rewriter::mk_mul_core(new_args.size(), new_args.data(), result); if (st == BR_FAILED) { - result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data()); + result = m.mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data()); return BR_DONE; } return st; @@ -998,7 +998,7 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul else { numeral k(1); k /= v2; - result = m().mk_app(get_fid(), OP_MUL, + result = m.mk_app(get_fid(), OP_MUL, m_util.mk_numeral(k, false), arg1); return BR_REWRITE1; @@ -1028,8 +1028,8 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul v1 /= v2; result = m_util.mk_mul(m_util.mk_numeral(v1, false), m_util.mk_div(b, d)); - expr_ref z(m_util.mk_real(0), m()); - result = m().mk_ite(m().mk_eq(d, z), m_util.mk_div(arg1, z), result); + expr_ref z(m_util.mk_real(0), m); + result = m.mk_ite(m.mk_eq(d, z), m_util.mk_div(arg1, z), result); return BR_REWRITE2; } } @@ -1039,7 +1039,7 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul } br_status arith_rewriter::mk_idivides(unsigned k, expr * arg, expr_ref & result) { - result = m().mk_eq(m_util.mk_mod(arg, m_util.mk_int(k)), m_util.mk_int(0)); + result = m.mk_eq(m_util.mk_mod(arg, m_util.mk_int(k)), m_util.mk_int(0)); return BR_REWRITE2; } @@ -1063,12 +1063,12 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu return BR_FAILED; } if (arg1 == arg2) { - expr_ref zero(m_util.mk_int(0), m()); - result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); + expr_ref zero(m_util.mk_int(0), m); + result = m.mk_ite(m.mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); return BR_REWRITE3; } if (m_util.is_numeral(arg2, v2, is_int) && v2.is_pos() && m_util.is_add(arg1)) { - expr_ref_buffer args(m()); + expr_ref_buffer args(m); bool change = false; rational add(0); for (expr* arg : *to_app(arg1)) { @@ -1083,15 +1083,15 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu } } if (change) { - result = m_util.mk_idiv(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2); + result = m_util.mk_idiv(m.mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2); result = m_util.mk_add(m_util.mk_numeral(add, true), result); TRACE("div_bug", tout << "mk_div result: " << result << "\n";); return BR_REWRITE3; } } if (divides(arg1, arg2, result)) { - expr_ref zero(m_util.mk_int(0), m()); - result = m().mk_ite(m().mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result); + expr_ref zero(m_util.mk_int(0), m); + result = m.mk_ite(m.mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result); return BR_REWRITE_FULL; } return BR_FAILED; @@ -1150,17 +1150,17 @@ expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { flat_mul(den, args2); remove_divisor(arg, args1); remove_divisor(arg, args2); - expr_ref zero(m_util.mk_int(0), m()); + expr_ref zero(m_util.mk_int(0), m); num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.data()); den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.data()); - expr_ref d(m_util.mk_idiv(num, den), m()); - expr_ref nd(m_util.mk_idiv(m_util.mk_uminus(num), m_util.mk_uminus(den)), m()); - return expr_ref(m().mk_ite(m().mk_eq(zero, arg), + expr_ref d(m_util.mk_idiv(num, den), m); + expr_ref nd(m_util.mk_idiv(m_util.mk_uminus(num), m_util.mk_uminus(den)), m); + return expr_ref(m.mk_ite(m.mk_eq(zero, arg), m_util.mk_idiv(zero, zero), - m().mk_ite(m_util.mk_ge(arg, zero), + m.mk_ite(m_util.mk_ge(arg, zero), d, nd)), - m()); + m); } void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { @@ -1208,8 +1208,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul } if (arg1 == arg2 && !m_util.is_numeral(arg2)) { - expr_ref zero(m_util.mk_int(0), m()); - result = m().mk_ite(m().mk_eq(arg2, zero), m_util.mk_mod(zero, zero), zero); + expr_ref zero(m_util.mk_int(0), m); + result = m.mk_ite(m.mk_eq(arg2, zero), m_util.mk_mod(zero, zero), zero); return BR_DONE; } @@ -1222,8 +1222,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul // propagate mod inside only if there is something to reduce. if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) { - TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); - expr_ref_buffer args(m()); + TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n";); + expr_ref_buffer args(m); bool change = false; for (expr* arg : *to_app(arg1)) { rational arg_v; @@ -1246,8 +1246,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul if (!change) { return BR_FAILED; // did not find any target for applying simplification } - result = m_util.mk_mod(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2); - TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m()) << "\n";); + result = m_util.mk_mod(m.mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2); + TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m) << "\n";); return BR_REWRITE3; } @@ -1290,10 +1290,10 @@ br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & resul } else if (m_elim_rem) { expr * mod = m_util.mk_mod(arg1, arg2); - result = m().mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)), + result = m.mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)), mod, m_util.mk_uminus(mod)); - TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m()) << "\n";); + TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m) << "\n";); return BR_REWRITE3; } return BR_FAILED; @@ -1322,7 +1322,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res bool is_num_y = m_util.is_numeral(arg2, y); auto ensure_real = [&](expr* e) { return m_util.is_int(e) ? m_util.mk_to_real(e) : e; }; - TRACE("arith", tout << mk_pp(arg1, m()) << " " << mk_pp(arg2, m()) << "\n";); + TRACE("arith", tout << mk_pp(arg1, m) << " " << mk_pp(arg2, m) << "\n";); if (is_num_x && x.is_one()) { result = m_util.mk_numeral(x, false); return BR_DONE; @@ -1377,7 +1377,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res if (is_num_y && y.is_minus_one()) { result = m_util.mk_div(m_util.mk_real(1), ensure_real(arg1)); - result = m().mk_ite(m().mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))), + result = m.mk_ite(m.mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))), m_util.mk_real(0), result); return BR_REWRITE2; @@ -1387,7 +1387,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res // (^ t -k) --> (^ (/ 1 t) k) result = m_util.mk_power(m_util.mk_div(m_util.mk_numeral(rational(1), false), arg1), m_util.mk_numeral(-y, false)); - result = m().mk_ite(m().mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))), + result = m.mk_ite(m.mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))), m_util.mk_real(0), result); return BR_REWRITE3; @@ -1504,7 +1504,7 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { // Try to apply simplifications such as: // (to_int (+ 1.0 (to_real x)) y) --> (+ 1 x (to_int y)) - expr_ref_buffer int_args(m()), real_args(m()); + expr_ref_buffer int_args(m), real_args(m); for (expr* c : *to_app(arg)) { if (m_util.is_numeral(c, a) && a.is_int()) { int_args.push_back(m_util.mk_numeral(a, true)); @@ -1520,17 +1520,17 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { return BR_FAILED; if (real_args.empty()) { - result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.data()); + result = m.mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.data()); return BR_REWRITE1; } if (!int_args.empty() && m_util.is_add(arg)) { decl_kind k = to_app(arg)->get_decl()->get_decl_kind(); - expr_ref t1(m().mk_app(get_fid(), k, int_args.size(), int_args.data()), m()); - expr_ref t2(m().mk_app(get_fid(), k, real_args.size(), real_args.data()), m()); + expr_ref t1(m.mk_app(get_fid(), k, int_args.size(), int_args.data()), m); + expr_ref t2(m.mk_app(get_fid(), k, real_args.size(), real_args.data()), m); int_args.reset(); int_args.push_back(t1); int_args.push_back(m_util.mk_to_int(t2)); - result = m().mk_app(get_fid(), k, int_args.size(), int_args.data()); + result = m.mk_app(get_fid(), k, int_args.size(), int_args.data()); return BR_REWRITE3; } } @@ -1550,9 +1550,9 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { for (expr* e : *to_app(arg)) new_args.push_back(m_util.mk_to_real(e)); if (m_util.is_add(arg)) - result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data()); + result = m.mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data()); else - result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data()); + result = m.mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data()); return BR_REWRITE2; } } @@ -1562,23 +1562,23 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { br_status arith_rewriter::mk_is_int(expr * arg, expr_ref & result) { numeral a; if (m_util.is_numeral(arg, a)) { - result = a.is_int() ? m().mk_true() : m().mk_false(); + result = a.is_int() ? m.mk_true() : m.mk_false(); return BR_DONE; } else if (m_util.is_to_real(arg)) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } else { - result = m().mk_eq(m().mk_app(get_fid(), OP_TO_REAL, - m().mk_app(get_fid(), OP_TO_INT, arg)), + result = m.mk_eq(m.mk_app(get_fid(), OP_TO_REAL, + m.mk_app(get_fid(), OP_TO_INT, arg)), arg); return BR_REWRITE3; } } br_status arith_rewriter::mk_abs_core(expr * arg, expr_ref & result) { - result = m().mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg)); + result = m.mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg)); return BR_REWRITE2; } @@ -1647,9 +1647,9 @@ bool arith_rewriter::is_pi_integer(expr * t) { a = c; b = d; } - TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m()) << "\n"; - tout << "a: " << mk_ismt2_pp(a, m()) << "\n"; - tout << "b: " << mk_ismt2_pp(b, m()) << "\n";); + TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m) << "\n"; + tout << "a: " << mk_ismt2_pp(a, m) << "\n"; + tout << "b: " << mk_ismt2_pp(b, m) << "\n";); return (m_util.is_pi(a) && m_util.is_to_real(b)) || (m_util.is_to_real(a) && m_util.is_pi(b)); @@ -1861,7 +1861,7 @@ br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) { } if (is_pi_multiple(arg, k)) { - expr_ref n(m()), d(m()); + expr_ref n(m), d(m); n = mk_sin_value(k); if (n.get() == nullptr) goto end; diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index c80226d0cf7..3cd9d6165a4 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -25,13 +25,13 @@ Module Name: class arith_rewriter_core { protected: typedef rational numeral; + ast_manager& m; arith_util m_util; scoped_ptr m_seq; - bool m_expand_power{ false }; - bool m_mul2power{ false }; - bool m_expand_tan{ false }; + bool m_expand_power = false; + bool m_mul2power = false; + bool m_expand_tan = false; - ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } seq_util& seq(); @@ -47,7 +47,7 @@ class arith_rewriter_core { app* mk_power(expr* x, rational const& r, sort* s); expr* coerce(expr* x, sort* s); public: - arith_rewriter_core(ast_manager & m):m_util(m) {} + arith_rewriter_core(ast_manager & m):m(m), m_util(m) {} bool is_zero(expr * n) const { return m_util.is_zero(n); } }; @@ -120,7 +120,7 @@ class arith_rewriter : public poly_rewriter { br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) - result = m().mk_app(f, num_args, args); + result = m.mk_app(f, num_args, args); } br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result); @@ -159,30 +159,30 @@ class arith_rewriter : public poly_rewriter { br_status mk_power_core(expr* arg1, expr* arg2, expr_ref & result); void mk_div(expr * arg1, expr * arg2, expr_ref & result) { if (mk_div_core(arg1, arg2, result) == BR_FAILED) - result = m().mk_app(get_fid(), OP_DIV, arg1, arg2); + result = m.mk_app(get_fid(), OP_DIV, arg1, arg2); } void mk_idiv(expr * arg1, expr * arg2, expr_ref & result) { if (mk_idiv_core(arg1, arg2, result) == BR_FAILED) - result = m().mk_app(get_fid(), OP_IDIV, arg1, arg2); + result = m.mk_app(get_fid(), OP_IDIV, arg1, arg2); } void mk_mod(expr * arg1, expr * arg2, expr_ref & result) { if (mk_mod_core(arg1, arg2, result) == BR_FAILED) - result = m().mk_app(get_fid(), OP_MOD, arg1, arg2); + result = m.mk_app(get_fid(), OP_MOD, arg1, arg2); } void mk_rem(expr * arg1, expr * arg2, expr_ref & result) { if (mk_rem_core(arg1, arg2, result) == BR_FAILED) - result = m().mk_app(get_fid(), OP_REM, arg1, arg2); + result = m.mk_app(get_fid(), OP_REM, arg1, arg2); } br_status mk_to_int_core(expr * arg, expr_ref & result); br_status mk_to_real_core(expr * arg, expr_ref & result); void mk_to_int(expr * arg, expr_ref & result) { if (mk_to_int_core(arg, result) == BR_FAILED) - result = m().mk_app(get_fid(), OP_TO_INT, 1, &arg); + result = m.mk_app(get_fid(), OP_TO_INT, 1, &arg); } void mk_to_real(expr * arg, expr_ref & result) { if (mk_to_real_core(arg, result) == BR_FAILED) - result = m().mk_app(get_fid(), OP_TO_REAL, 1, &arg); + result = m.mk_app(get_fid(), OP_TO_REAL, 1, &arg); } br_status mk_is_int(expr * arg, expr_ref & result); diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 972259bd724..1557c9b3cd8 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -217,7 +217,7 @@ br_status bv_rewriter::mk_uge(expr * a, expr * b, expr_ref & result) { } br_status bv_rewriter::mk_ult(expr * a, expr * b, expr_ref & result) { - result = m().mk_not(m_util.mk_ule(b, a)); + result = m.mk_not(m_util.mk_ule(b, a)); return BR_REWRITE2; } @@ -234,7 +234,7 @@ br_status bv_rewriter::mk_sge(expr * a, expr * b, expr_ref & result) { } br_status bv_rewriter::mk_slt(expr * a, expr * b, expr_ref & result) { - result = m().mk_not(m_util.mk_sle(b, a)); + result = m.mk_not(m_util.mk_sle(b, a)); return BR_REWRITE2; } @@ -300,7 +300,7 @@ bool bv_rewriter::are_eq_upto_num(expr * _a, expr * _b, if (has_num_b) is_numeral(b->get_arg(0), b0_val, b0_sz); SASSERT(a0_sz == m_util.get_bv_size(a) && b0_sz == m_util.get_bv_size(a)); if (has_num_a && numa > 2) { - common = m().mk_app(m_util.get_fid(), add_decl_kind(), numa - 1, a->get_args() + 1); + common = m.mk_app(m_util.get_fid(), add_decl_kind(), numa - 1, a->get_args() + 1); } else { common = has_num_a ? a->get_arg(1) : a; @@ -311,13 +311,13 @@ bool bv_rewriter::are_eq_upto_num(expr * _a, expr * _b, // simplifies expressions as (bvuleq (X + c1) (X + c2)) for some common expression X and numerals c1, c2 br_status bv_rewriter::rw_leq_overflow(bool is_signed, expr * a, expr * b, expr_ref & result) { if (is_signed) return BR_FAILED; - expr_ref common(m()); + expr_ref common(m); numeral a0_val, b0_val; if (!are_eq_upto_num(a, b, common, a0_val, b0_val)) return BR_FAILED; SASSERT(a0_val.is_nonneg() && b0_val.is_nonneg()); const unsigned sz = m_util.get_bv_size(a); if (a0_val == b0_val) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } if (a0_val < b0_val) { @@ -329,14 +329,14 @@ br_status bv_rewriter::rw_leq_overflow(bool is_signed, expr * a, expr * b, expr_ const numeral lower = rational::power_of_two(sz) - a0_val; const numeral upper = rational::power_of_two(sz) - b0_val - numeral::one(); if (lower == upper) { - result = m().mk_eq(common, mk_numeral(lower, sz)); + result = m.mk_eq(common, mk_numeral(lower, sz)); } else if (b0_val.is_zero()) { result = m_util.mk_ule(mk_numeral(lower, sz), common); } else { SASSERT(lower.is_pos()); - result = m().mk_and(m_util.mk_ule(mk_numeral(lower, sz), common), + result = m.mk_and(m_util.mk_ule(mk_numeral(lower, sz), common), m_util.mk_ule(common, mk_numeral(upper, sz))); } return BR_REWRITE2; @@ -363,11 +363,11 @@ br_status bv_rewriter::rw_leq_concats(bool is_signed, expr * _a, expr * _b, expr const numeral hi_bf = m_util.norm(bf_sz > sz_min ? div(bf, rational::power_of_two(bf_sz - sz_min)) : bf, sz_min, is_signed); if (hi_af != hi_bf) { - result = hi_af < hi_bf ? m().mk_true() : m().mk_false(); + result = hi_af < hi_bf ? m.mk_true() : m.mk_false(); return BR_DONE; } - expr_ref new_a(m()); - expr_ref new_b(m()); + expr_ref new_a(m); + expr_ref new_b(m); if (af_sz > sz_min) { ptr_buffer new_args; new_args.push_back(mk_numeral(af, af_sz - sz_min)); @@ -391,11 +391,11 @@ br_status bv_rewriter::rw_leq_concats(bool is_signed, expr * _a, expr * _b, expr { // common prefix unsigned common = 0; - while (common < num_min && m().are_equal(a->get_arg(common), b->get_arg(common))) ++common; + while (common < num_min && m.are_equal(a->get_arg(common), b->get_arg(common))) ++common; SASSERT((common == numa) == (common == numb)); if (common == numa) { SASSERT(0); // shouldn't get here as both sides are equal - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } if (common > 0) { @@ -411,13 +411,13 @@ br_status bv_rewriter::rw_leq_concats(bool is_signed, expr * _a, expr * _b, expr while (new_numa && new_numb) { expr * const last_a = a->get_arg(new_numa - 1); expr * const last_b = b->get_arg(new_numb - 1); - if (!m().are_equal(last_a, last_b)) break; + if (!m.are_equal(last_a, last_b)) break; new_numa--; new_numb--; } if (new_numa == 0) { SASSERT(0); // shouldn't get here as both sides are equal - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } if (new_numa != numa) { @@ -438,7 +438,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref bool is_num2 = is_numeral(b, r2, sz); if (a == b) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } @@ -448,7 +448,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref r2 = m_util.norm(r2, sz, is_signed); if (is_num1 && is_num2) { - result = m().mk_bool_val(r1 <= r2); + result = m.mk_bool_val(r1 <= r2); return BR_DONE; } @@ -467,11 +467,11 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref if (is_num2) { if (r2 == lower) { - result = m().mk_eq(a, b); + result = m.mk_eq(a, b); return BR_REWRITE1; } if (r2 == upper) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } } @@ -479,13 +479,13 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref if (is_num1) { // 0 <= b is true if (r1 == lower) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } // 2^n-1 <= b is a = b if (r1 == upper) { - result = m().mk_eq(a, b); + result = m.mk_eq(a, b); return BR_REWRITE1; } } @@ -512,12 +512,10 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref // other cases r1 > r2, r1 < r2 are TBD if (!is_signed && is_num1 && m_util.is_bv_add(b, a1, a2) && is_numeral(a1, r2, sz)) { result = m_util.mk_ule(a2, m_util.mk_numeral(-r2 - 1, sz)); - if (r1 > r2) { - result = m().mk_and(result, m_util.mk_ule(m_util.mk_numeral(r1-r2, sz), a2)); - } - else if (r1 < r2) { - result = m().mk_or(result, m_util.mk_ule(m_util.mk_numeral(r1-r2, sz), a2)); - } + if (r1 > r2) + result = m.mk_and(result, m_util.mk_ule(m_util.mk_numeral(r1-r2, sz), a2)); + else if (r1 < r2) + result = m.mk_or(result, m_util.mk_ule(m_util.mk_numeral(r1-r2, sz), a2)); return BR_REWRITE2; } @@ -525,7 +523,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref const br_status cst = rw_leq_concats(is_signed, a, b, result); if (cst != BR_FAILED) { TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n") - << mk_ismt2_pp(a, m(), 2) << "\n" << mk_ismt2_pp(b, m(), 2) << "\n--->\n"<< mk_ismt2_pp(result, m(), 2) << "\n";); + << mk_ismt2_pp(a, m, 2) << "\n" << mk_ismt2_pp(b, m, 2) << "\n--->\n"<< mk_ismt2_pp(result, m, 2) << "\n";); return cst; } } @@ -534,7 +532,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref const br_status cst = rw_leq_overflow(is_signed, a, b, result); if (cst != BR_FAILED) { TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n") - << mk_ismt2_pp(a, m(), 2) << "\n" << mk_ismt2_pp(b, m(), 2) << "\n--->\n"<< mk_ismt2_pp(result, m(), 2) << "\n";); + << mk_ismt2_pp(a, m, 2) << "\n" << mk_ismt2_pp(b, m, 2) << "\n--->\n"<< mk_ismt2_pp(result, m, 2) << "\n";); return cst; } } @@ -548,7 +546,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref expr * b_2 = to_app(b)->get_arg(1); unsigned sz1 = get_bv_size(b_1); unsigned sz2 = get_bv_size(b_2); - result = m().mk_and(m().mk_eq(m_mk_extract(sz2+sz1-1, sz2, a), b_1), + result = m.mk_and(m.mk_eq(m_mk_extract(sz2+sz1-1, sz2, a), b_1), m_util.mk_ule(m_mk_extract(sz2-1, 0, a), b_2)); return BR_REWRITE3; } @@ -572,11 +570,11 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref if (first_non_zero == UINT_MAX) { // all bits are zero - result = m().mk_eq(a, m_util.mk_numeral(numeral(0), bv_sz)); + result = m.mk_eq(a, m_util.mk_numeral(numeral(0), bv_sz)); return BR_REWRITE1; } else if (first_non_zero < bv_sz - 1 && m_le2extract) { - result = m().mk_and(m().mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)), + result = m.mk_and(m.mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)), m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b))); return BR_REWRITE3; } @@ -673,7 +671,7 @@ unsigned bv_rewriter::propagate_extract(unsigned high, expr * arg, expr_ref & re } if (new_arg) new_args.push_back(new_arg); } - result = m().mk_app(get_fid(), a->get_decl()->get_decl_kind(), new_args.size(), new_args.data()); + result = m.mk_app(get_fid(), a->get_decl()->get_decl_kind(), new_args.size(), new_args.data()); SASSERT(m_util.is_bv(result)); return removable; } @@ -777,17 +775,17 @@ br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ expr * curr = to_app(arg)->get_arg(i); new_args.push_back(m_mk_extract(high, low, curr)); } - result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), new_args.size(), new_args.data()); + result = m.mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), new_args.size(), new_args.data()); return BR_REWRITE2; } if (m_extract_prop && (high >= low)) { - expr_ref ep_res(m()); + expr_ref ep_res(m); const unsigned ep_rm = propagate_extract(high, arg, ep_res); if (ep_rm != 0) { result = m_mk_extract(high, low, ep_res); - TRACE("extract_prop", tout << mk_ismt2_pp(arg, m()) << "\n[" << high <<"," << low << "]\n" << ep_rm << "---->\n" - << mk_ismt2_pp(result.get(), m()) << "\n";); + TRACE("extract_prop", tout << mk_ismt2_pp(arg, m) << "\n[" << high <<"," << low << "]\n" << ep_rm << "---->\n" + << mk_ismt2_pp(result.get(), m) << "\n";); return BR_REWRITE2; } } @@ -797,9 +795,9 @@ br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ // branch of ite to be expanded or if one of the expanded ite branches have a single // reference count. expr* c = nullptr, *t = nullptr, *e = nullptr; - if (m().is_ite(arg, c, t, e) && - (t->get_ref_count() == 1 || e->get_ref_count() == 1 || !m().is_ite(t) || !m().is_ite(e))) { - result = m().mk_ite(c, m_mk_extract(high, low, t), m_mk_extract(high, low, e)); + if (m.is_ite(arg, c, t, e) && + (t->get_ref_count() == 1 || e->get_ref_count() == 1 || !m.is_ite(t) || !m.is_ite(e))) { + result = m.mk_ite(c, m_mk_extract(high, low, t), m_mk_extract(high, low, e)); return BR_REWRITE2; } @@ -855,9 +853,9 @@ br_status bv_rewriter::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { expr* x = nullptr, *y = nullptr; if (m_util.is_bv_shl(arg1, x, y)) { - expr_ref sum(m_util.mk_bv_add(y, arg2), m()); - expr_ref cond(m_util.mk_ule(y, sum), m()); - result = m().mk_ite(cond, + expr_ref sum(m_util.mk_bv_add(y, arg2), m); + expr_ref cond(m_util.mk_ule(y, sum), m); + result = m.mk_ite(cond, m_util.mk_bv_shl(x, sum), mk_numeral(0, bv_size)); return BR_REWRITE3; @@ -989,7 +987,7 @@ br_status bv_rewriter::mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result) { r1 += r2; if (r1 > numeral(bv_size)) r1 = numeral(bv_size); - result = m().mk_app(get_fid(), OP_BASHR, + result = m.mk_app(get_fid(), OP_BASHR, to_app(arg1)->get_arg(0), mk_numeral(r1, bv_size)); return BR_REWRITE1; // not really needed at this time. @@ -1029,7 +1027,7 @@ br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, e } else { // The "hardware interpretation" for (bvsdiv x 0) is (ite (bvslt x #x0000) #x0001 #xffff) - result = m().mk_ite(m().mk_app(get_fid(), OP_SLT, arg1, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_app(get_fid(), OP_SLT, arg1, mk_numeral(0, bv_size)), mk_numeral(1, bv_size), mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size)); return BR_REWRITE2; @@ -1057,7 +1055,7 @@ br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), m_util.mk_bv_sdiv0(arg1), m_util.mk_bv_sdiv_i(arg1, arg2)); return BR_REWRITE2; @@ -1097,7 +1095,7 @@ br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, e unsigned shift; if (r2.is_power_of_two(shift)) { - result = m().mk_app(get_fid(), OP_BLSHR, arg1, mk_numeral(shift, bv_size)); + result = m.mk_app(get_fid(), OP_BLSHR, arg1, mk_numeral(shift, bv_size)); return BR_REWRITE1; } @@ -1112,11 +1110,11 @@ br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), m_util.mk_bv_udiv0(arg1), m_util.mk_bv_udiv_i(arg1, arg2)); - TRACE("bv_udiv", tout << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n---->\n" << mk_ismt2_pp(result, m()) << "\n";); + TRACE("bv_udiv", tout << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n---->\n" << mk_ismt2_pp(result, m) << "\n";); return BR_REWRITE2; } @@ -1128,7 +1126,7 @@ br_status bv_rewriter::mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, e r2 = m_util.norm(r2, bv_size, true); if (r2.is_zero()) { if (!hi_div0) { - result = m().mk_app(get_fid(), OP_BSREM0, arg1); + result = m.mk_app(get_fid(), OP_BSREM0, arg1); return BR_REWRITE1; } else { @@ -1149,19 +1147,19 @@ br_status bv_rewriter::mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, e return BR_DONE; } - result = m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2); + result = m.mk_app(get_fid(), OP_BSREM_I, arg1, arg2); return BR_DONE; } if (hi_div0) { - result = m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2); + result = m.mk_app(get_fid(), OP_BSREM_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); - result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), - m().mk_app(get_fid(), OP_BSREM0, arg1), - m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2)); + result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), + m.mk_app(get_fid(), OP_BSREM0, arg1), + m.mk_app(get_fid(), OP_BSREM_I, arg1, arg2)); return BR_REWRITE2; } @@ -1253,7 +1251,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e // urem(0, x) ==> ite(x = 0, urem0(x), 0) if (is_num1 && r1.is_zero()) { expr * zero = arg1; - result = m().mk_ite(m().mk_eq(arg2, zero), + result = m.mk_ite(m.mk_eq(arg2, zero), m_util.mk_bv_urem0(zero), zero); return BR_REWRITE2; @@ -1265,7 +1263,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e bv_size = get_bv_size(arg1); expr * x_minus_1 = arg1; expr * minus_one = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); - result = m().mk_ite(m().mk_eq(x, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(x, mk_numeral(0, bv_size)), m_util.mk_bv_urem0(minus_one), x_minus_1); return BR_REWRITE2; @@ -1295,7 +1293,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), m_util.mk_bv_urem0(arg1), m_util.mk_bv_urem_i(arg1, arg2)); return BR_REWRITE2; @@ -1357,8 +1355,8 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e !m_util.is_concat(a) && !m_util.is_concat(b)) { unsigned nb = r2.get_num_bits(); - expr_ref a1(m_util.mk_bv_smod(a, arg2), m()); - expr_ref a2(m_util.mk_bv_smod(b, arg2), m()); + expr_ref a1(m_util.mk_bv_smod(a, arg2), m); + expr_ref a2(m_util.mk_bv_smod(b, arg2), m); a1 = m_util.mk_concat( mk_numeral(0, bv_size - nb), m_mk_extract(nb-1,0,a1)); a2 = m_util.mk_concat( mk_numeral(0, bv_size - nb), m_mk_extract(nb-1,0,a2)); result = m_util.mk_bv_mul(a1, a2); @@ -1371,14 +1369,14 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e } if (hi_div0) { - result = m().mk_app(get_fid(), OP_BSMOD_I, arg1, arg2); + result = m.mk_app(get_fid(), OP_BSMOD_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); - result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), - m().mk_app(get_fid(), OP_BSMOD0, arg1), - m().mk_app(get_fid(), OP_BSMOD_I, arg1, arg2)); + result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), + m.mk_app(get_fid(), OP_BSMOD0, arg1), + m.mk_app(get_fid(), OP_BSMOD_I, arg1, arg2)); return BR_REWRITE2; } @@ -1413,7 +1411,7 @@ br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) { result = m_autil.mk_int(0); return BR_DONE; } - expr_ref_vector args(m()); + expr_ref_vector args(m); unsigned num_args = to_app(arg)->get_num_args(); for (expr* x : *to_app(arg)) { @@ -1421,7 +1419,7 @@ br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) { } unsigned sz = get_bv_size(to_app(arg)->get_arg(num_args-1)); for (unsigned i = num_args - 1; i > 0; ) { - expr_ref tmp(m()); + expr_ref tmp(m); --i; tmp = args[i].get(); tmp = m_autil.mk_mul(m_autil.mk_numeral(power(numeral(2), sz), true), tmp); @@ -1432,13 +1430,13 @@ br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) { return BR_REWRITE2; } if (is_mul_no_overflow(arg)) { - expr_ref_vector args(m()); + expr_ref_vector args(m); for (expr* x : *to_app(arg)) args.push_back(m_util.mk_bv2int(x)); result = m_autil.mk_mul(args.size(), args.data()); return BR_REWRITE2; } if (is_add_no_overflow(arg)) { - expr_ref_vector args(m()); + expr_ref_vector args(m); for (expr* x : *to_app(arg)) args.push_back(m_util.mk_bv2int(x)); result = m_autil.mk_add(args.size(), args.data()); return BR_REWRITE2; @@ -1507,7 +1505,7 @@ unsigned bv_rewriter::num_leading_zero_bits(expr* e) { br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) { - expr_ref_buffer new_args(m()); + expr_ref_buffer new_args(m); numeral v1; numeral v2; unsigned sz1, sz2; @@ -1554,11 +1552,11 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re if (!fused_numeral && !expanded && !fused_extract) { expr* x, *y, *z; if (eq_args) { - if (m().is_ite(new_args.back(), x, y, z)) { + 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)); + result = m.mk_ite(x, m_util.mk_concat(args1), m_util.mk_concat(args2)); return BR_REWRITE2; } } @@ -1776,8 +1774,8 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re std::reverse(exs.begin(), exs.end()); result = m_util.mk_concat(exs.size(), exs.data()); TRACE("mask_bug", - tout << "(assert (distinct (bvor (_ bv" << old_v1 << " " << sz << ")\n" << mk_ismt2_pp(t, m()) << ")\n"; - tout << mk_ismt2_pp(result, m()) << "))\n";); + tout << "(assert (distinct (bvor (_ bv" << old_v1 << " " << sz << ")\n" << mk_ismt2_pp(t, m) << ")\n"; + tout << mk_ismt2_pp(result, m) << "))\n";); return BR_REWRITE2; } @@ -1896,8 +1894,8 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r } SASSERT(t != 0); numeral two(2); - expr_ref_buffer exs(m()); - expr_ref not_t(m()); + expr_ref_buffer exs(m); + expr_ref not_t(m); not_t = m_util.mk_bv_not(t); unsigned low = 0; unsigned i = 0; @@ -1936,7 +1934,7 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r } ptr_buffer new_args; - expr_ref c(m()); // may not be used + expr_ref c(m); // may not be used if (!v1.is_zero()) { c = mk_numeral(v1, sz); new_args.push_back(c); @@ -1990,13 +1988,13 @@ bool bv_rewriter::distribute_concat(decl_kind k, unsigned n, expr* const* args, expr* e = to_app(arg)->get_arg(0); unsigned sz1 = get_bv_size(e); unsigned sz2 = get_bv_size(arg); - expr_ref_vector args1(m()), args2(m()); + expr_ref_vector args1(m), args2(m); for (unsigned j = 0; j < n; ++j) { args1.push_back(m_mk_extract(sz2 - 1, sz2 - sz1, args[j])); args2.push_back(m_mk_extract(sz2 - sz1 - 1, 0, args[j])); } - expr* arg1 = m().mk_app(get_fid(), k, args1.size(), args1.data()); - expr* arg2 = m().mk_app(get_fid(), k, args2.size(), args2.data()); + expr* arg1 = m.mk_app(get_fid(), k, args1.size(), args1.data()); + expr* arg2 = m.mk_app(get_fid(), k, args2.size(), args2.data()); result = m_util.mk_concat(arg1, arg2); return true; } @@ -2028,15 +2026,15 @@ br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { } expr* x, *y, *z; - if (m().is_ite(arg, x, y, z) && m_util.is_numeral(y, val, bv_size)) { + 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)); + 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)) { + 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)); + result = m.mk_ite(x, m_util.mk_bv_not(y), m_util.mk_numeral(val, bv_size)); return BR_REWRITE2; } @@ -2051,13 +2049,13 @@ br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { } } if (m_util.is_bv_add(arg, s, t)) { - expr_ref ns(m()); - expr_ref nt(m()); + expr_ref ns(m); + expr_ref nt(m); // ~(x + y) --> (~x + ~y + 1) when x and y are easy to negate if (is_negatable(t, nt) && is_negatable(s, ns)) { bv_size = m_util.get_bv_size(s); expr * nargs[3] = { m_util.mk_numeral(rational::one(), bv_size), ns.get(), nt.get() }; - result = m().mk_app(m_util.get_fid(), OP_BADD, 3, nargs); + result = m.mk_app(m_util.get_fid(), OP_BADD, 3, nargs); return BR_REWRITE1; } } @@ -2092,7 +2090,7 @@ br_status bv_rewriter::mk_bv_nor(unsigned num_args, expr * const * args, expr_re br_status bv_rewriter::mk_bv_xnor(unsigned num_args, expr * const * args, expr_ref & result) { switch (num_args) { - case 0: result = m().mk_true(); break; + case 0: result = m.mk_true(); break; case 1: result = m_util.mk_bv_not(args[0]); break; case 2: result = m_util.mk_bv_not(m_util.mk_bv_xor(num_args, args)); break; default: @@ -2176,7 +2174,7 @@ br_status bv_rewriter::mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result) { return BR_DONE; } - result = m().mk_ite(m().mk_eq(arg1, arg2), + result = m.mk_ite(m.mk_eq(arg1, arg2), mk_numeral(1, 1), mk_numeral(0, 1)); return BR_REWRITE2; @@ -2214,7 +2212,7 @@ br_status bv_rewriter::mk_bv_add(unsigned num_args, expr * const * args, expr_re return st; } - result = m().mk_app(get_fid(), OP_BOR, x, y); + result = m.mk_app(get_fid(), OP_BOR, x, y); return BR_REWRITE1; #else unsigned _num_args; @@ -2244,7 +2242,7 @@ br_status bv_rewriter::mk_bv_add(unsigned num_args, expr * const * args, expr_re } } } - result = m().mk_app(get_fid(), OP_BOR, _num_args, _args); + result = m.mk_app(get_fid(), OP_BOR, _num_args, _args); return BR_REWRITE1; #endif } @@ -2253,21 +2251,17 @@ bool bv_rewriter::is_zero_bit(expr * x, unsigned idx) { numeral val; unsigned bv_size; loop: - if (is_numeral(x, val, bv_size)) { - if (val.is_zero()) - return true; - div(val, rational::power_of_two(idx), val); - return (val % numeral(2)).is_zero(); - } + if (is_numeral(x, val, bv_size)) + return val.is_zero() || !val.get_bit(idx); + if (m_util.is_concat(x)) { unsigned i = to_app(x)->get_num_args(); while (i > 0) { --i; expr * y = to_app(x)->get_arg(i); bv_size = get_bv_size(y); - if (bv_size <= idx) { + if (bv_size <= idx) idx -= bv_size; - } else { x = y; goto loop; @@ -2363,7 +2357,7 @@ br_status bv_rewriter::mk_bit2bool(expr * n, int idx, expr_ref & result) { return BR_FAILED; div(v, rational::power_of_two(idx), bit); mod(bit, rational(2), bit); - result = m().mk_bool_val(bit.is_one()); + result = m.mk_bool_val(bit.is_one()); return BR_DONE; } @@ -2381,61 +2375,62 @@ br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { if (is_numeral(lhs)) { SASSERT(is_numeral(rhs)); - result = m().mk_bool_val(lhs == rhs); + result = m.mk_bool_val(lhs == rhs); return BR_DONE; } expr* a = nullptr, *b = nullptr, *c = nullptr; - if (m().is_ite(lhs, a, b, c)) { - bool_rewriter rw(m()); - expr_ref e1(rw.mk_eq(b, rhs), m()); - expr_ref e2(rw.mk_eq(c, rhs), m()); + if (m.is_ite(lhs, a, b, c)) { + bool_rewriter rw(m); + expr_ref e1(rw.mk_eq(b, rhs), m); + expr_ref e2(rw.mk_eq(c, rhs), m); result = rw.mk_ite(a, e1, e2); return BR_REWRITE2; } if (m_util.is_bv_not(lhs, a)) { SASSERT(v.is_one() || v.is_zero()); - result = m().mk_eq(a, mk_numeral(numeral(1) - v, 1)); + result = m.mk_eq(a, mk_numeral(numeral(1) - v, 1)); return BR_REWRITE1; } bool is_one = v.is_one(); - expr_ref bit1(m()); - bit1 = is_one ? rhs : mk_numeral(numeral(1), 1); - + if (m_util.is_bv_or(lhs)) { + if (!m_bit1) + m_bit1 = is_one ? rhs : mk_numeral(numeral(1), 1); ptr_buffer new_args; for (expr* arg : *to_app(lhs)) - new_args.push_back(m().mk_eq(arg, bit1)); - result = m().mk_or(new_args); + new_args.push_back(m.mk_eq(arg, m_bit1)); + result = m.mk_or(new_args); if (is_one) { return BR_REWRITE2; } else { - result = m().mk_not(result); + result = m.mk_not(result); return BR_REWRITE3; } } if (m_util.is_bv_xor(lhs)) { + if (!m_bit1) + m_bit1 = is_one ? rhs : mk_numeral(numeral(1), 1); ptr_buffer new_args; for (expr* arg : *to_app(lhs)) - new_args.push_back(m().mk_eq(arg, bit1)); + new_args.push_back(m.mk_eq(arg, m_bit1)); // TODO: bool xor is not flat_assoc... must fix that. - result = m().mk_xor(new_args); + result = m.mk_xor(new_args); if (is_one) { return BR_REWRITE2; } else { - result = m().mk_not(result); + result = m.mk_not(result); return BR_REWRITE3; } } - return BR_FAILED; } @@ -2443,7 +2438,7 @@ br_status bv_rewriter::mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & resu unsigned sz = get_bv_size(lhs); if (sz == 1) return BR_FAILED; - TRACE("blast_eq_value", tout << "sz: " << sz << "\n" << mk_ismt2_pp(lhs, m()) << "\n";); + TRACE("blast_eq_value", tout << "sz: " << sz << "\n" << mk_ismt2_pp(lhs, m) << "\n";); if (is_numeral(lhs)) std::swap(lhs, rhs); @@ -2458,11 +2453,11 @@ br_status bv_rewriter::mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & resu ptr_buffer new_args; for (unsigned i = 0; i < sz; i++) { bool bit0 = (v % two).is_zero(); - new_args.push_back(m().mk_eq(m_mk_extract(i,i, lhs), + new_args.push_back(m.mk_eq(m_mk_extract(i,i, lhs), mk_numeral(bit0 ? 0 : 1, 1))); div(v, two, v); } - result = m().mk_and(new_args); + result = m.mk_and(new_args); return BR_REWRITE3; } @@ -2503,7 +2498,7 @@ br_status bv_rewriter::mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result) { unsigned rsz1 = sz1 - low1; unsigned rsz2 = sz2 - low2; if (rsz1 == rsz2) { - new_eqs.push_back(m().mk_eq(m_mk_extract(sz1 - 1, low1, arg1), + new_eqs.push_back(m.mk_eq(m_mk_extract(sz1 - 1, low1, arg1), m_mk_extract(sz2 - 1, low2, arg2))); low1 = 0; low2 = 0; @@ -2512,14 +2507,14 @@ br_status bv_rewriter::mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result) { continue; } else if (rsz1 < rsz2) { - new_eqs.push_back(m().mk_eq(m_mk_extract(sz1 - 1, low1, arg1), + new_eqs.push_back(m.mk_eq(m_mk_extract(sz1 - 1, low1, arg1), m_mk_extract(rsz1 + low2 - 1, low2, arg2))); low1 = 0; low2 += rsz1; --i1; } else { - new_eqs.push_back(m().mk_eq(m_mk_extract(rsz2 + low1 - 1, low1, arg1), + new_eqs.push_back(m.mk_eq(m_mk_extract(rsz2 + low1 - 1, low1, arg1), m_mk_extract(sz2 - 1, low2, arg2))); low1 += rsz2; low2 = 0; @@ -2528,7 +2523,7 @@ br_status bv_rewriter::mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result) { } SASSERT(i1 == 0 && i2 == 0); SASSERT(new_eqs.size() >= 1); - result = m().mk_and(new_eqs); + result = m.mk_and(new_eqs); return BR_REWRITE3; } @@ -2548,9 +2543,9 @@ bool bv_rewriter::is_minus_one_times_t(expr * arg) { void bv_rewriter::mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result) { SASSERT(is_numeral(c)); if (is_minus_one_times_t(t1)) - result = m().mk_eq(t2, m_util.mk_bv_sub(c, t1)); + result = m.mk_eq(t2, m_util.mk_bv_sub(c, t1)); else - result = m().mk_eq(t1, m_util.mk_bv_sub(c, t2)); + result = m.mk_eq(t1, m_util.mk_bv_sub(c, t2)); } #include "ast/ast_pp.h" @@ -2564,9 +2559,9 @@ bool bv_rewriter::isolate_term(expr* lhs, expr* rhs, expr_ref& result) { } unsigned sz = to_app(rhs)->get_num_args(); expr * t1 = to_app(rhs)->get_arg(0); - expr_ref t2(m()); + expr_ref t2(m); if (sz > 2) { - t2 = m().mk_app(get_fid(), OP_BADD, sz-1, to_app(rhs)->get_args()+1); + t2 = m.mk_app(get_fid(), OP_BADD, sz-1, to_app(rhs)->get_args()+1); } else { SASSERT(sz == 2); @@ -2623,7 +2618,7 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { // c * x = a if (m_util.is_numeral(rhs, rhs_val, sz)) { // x = c_inv * a - result = m().mk_eq(x, m_util.mk_numeral(c_inv_val * rhs_val, sz)); + result = m.mk_eq(x, m_util.mk_numeral(c_inv_val * rhs_val, sz)); return BR_REWRITE1; } @@ -2634,9 +2629,9 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { // x = c_inv * c2 * x2 numeral new_c2 = m_util.norm(c_inv_val * c2_val, sz); if (new_c2.is_one()) - result = m().mk_eq(x, x2); + result = m.mk_eq(x, x2); else - result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val * c2_val, sz), x2)); + result = m.mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val * c2_val, sz), x2)); return BR_REWRITE1; } @@ -2644,7 +2639,7 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { // and t_i's have non-unary coefficients (this condition is used to make sure we are actually reducing the number of multipliers). if (is_add_mul_const(rhs)) { // Potential problem: this simplification may increase the number of adders by reducing the amount of sharing. - result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs)); + result = m.mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs)); return BR_REWRITE2; } } @@ -2662,7 +2657,7 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { } } if (found) { - result = m().mk_eq(m_util.mk_numeral(c2_inv_val*c_val, sz), + result = m.mk_eq(m_util.mk_numeral(c2_inv_val*c_val, sz), m_util.mk_bv_mul(m_util.mk_numeral(c2_inv_val, sz), rhs)); return BR_REWRITE3; } @@ -2682,12 +2677,12 @@ bool bv_rewriter::is_urem_any(expr * e, expr * & dividend, expr * & divisor) { br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (lhs == rhs) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } if (is_numeral(lhs) && is_numeral(rhs)) { - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } @@ -2699,7 +2694,7 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { #if 0 if (!gcd_test(lhs, rhs)) { - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } #endif @@ -2713,13 +2708,13 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { st = mk_mul_eq(lhs, rhs, result); if (st != BR_FAILED) { - TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m()) << "\n=\n" << mk_ismt2_pp(rhs, m()) << "\n----->\n" << mk_ismt2_pp(result,m()) << "\n";); + TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m) << "\n=\n" << mk_ismt2_pp(rhs, m) << "\n----->\n" << mk_ismt2_pp(result,m) << "\n";); return st; } st = mk_mul_eq(rhs, lhs, result); if (st != BR_FAILED) { - TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m()) << "\n=\n" << mk_ismt2_pp(rhs, m()) << "\n----->\n" << mk_ismt2_pp(result,m()) << "\n";); + TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m) << "\n=\n" << mk_ismt2_pp(rhs, m) << "\n----->\n" << mk_ismt2_pp(result,m) << "\n";); return st; } @@ -2738,24 +2733,24 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { && is_numeral(rhs, rhs_val, rhs_sz) && is_numeral(divisor, divisor_val, divisor_sz)) { if (!divisor_val.is_zero() && rhs_val >= divisor_val) {//(= (bvurem x c1) c2) where c2 >= c1 - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } if ((divisor_val + rhs_val) >= rational::power_of_two(divisor_sz)) {//(= (bvurem x c1) c2) where c1+c2 >= 2^width - result = m().mk_eq(dividend, rhs); + result = m.mk_eq(dividend, rhs); return BR_REWRITE2; } } } - expr_ref new_lhs(m()); - expr_ref new_rhs(m()); + expr_ref new_lhs(m); + expr_ref new_rhs(m); if (m_util.is_bv_add(lhs) || m_util.is_bv_mul(lhs) || m_util.is_bv_add(rhs) || m_util.is_bv_mul(rhs)) { st = cancel_monomials(lhs, rhs, false, new_lhs, new_rhs); if (st != BR_FAILED) { if (is_numeral(new_lhs) && is_numeral(new_rhs)) { - result = m().mk_bool_val(new_lhs == new_rhs); + result = m.mk_bool_val(new_lhs == new_rhs); return BR_DONE; } lhs = new_lhs; @@ -2772,7 +2767,7 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { } if (st != BR_FAILED) { - result = m().mk_eq(lhs, rhs); + result = m.mk_eq(lhs, rhs); return BR_DONE; } } @@ -2782,7 +2777,7 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { } if (swapped) { - result = m().mk_eq(lhs, rhs); + result = m.mk_eq(lhs, rhs); return BR_DONE; } @@ -2794,7 +2789,7 @@ br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & res if (m_mkbv2num) { unsigned i; for (i = 0; i < num; i++) - if (!m().is_true(args[i]) && !m().is_false(args[i])) + if (!m.is_true(args[i]) && !m.is_false(args[i])) return BR_FAILED; numeral val; numeral two(2); @@ -2802,7 +2797,7 @@ br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & res while (i > 0) { --i; val *= two; - if (m().is_true(args[i])) + if (m.is_true(args[i])) val++; } result = mk_numeral(val, num); @@ -2812,18 +2807,18 @@ br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & res } br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) { - TRACE("bv_ite", tout << "mk_ite_core:\n" << mk_ismt2_pp(c, m()) << "?\n" - << mk_ismt2_pp(t, m()) << "\n:" << mk_ismt2_pp(e, m()) << "\n";); - if (m().are_equal(t, e)) { + TRACE("bv_ite", tout << "mk_ite_core:\n" << mk_ismt2_pp(c, m) << "?\n" + << mk_ismt2_pp(t, m) << "\n:" << mk_ismt2_pp(e, m) << "\n";); + if (m.are_equal(t, e)) { result = e; return BR_REWRITE1; } - if (m().is_not(c)) { - result = m().mk_ite(to_app(c)->get_arg(0), e, t); + if (m.is_not(c)) { + result = m.mk_ite(to_app(c)->get_arg(0), e, t); return BR_REWRITE1; } - if (m_ite2id && m().is_eq(c) && is_bv(t) && is_bv(e)) { + if (m_ite2id && m.is_eq(c) && is_bv(t) && is_bv(e)) { // detect when ite is actually some simple function based on the pattern (lhs=rhs) ? t : e expr * lhs = to_app(c)->get_arg(0); expr * rhs = to_app(c)->get_arg(1); @@ -2832,8 +2827,8 @@ br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resu if (is_numeral(lhs)) std::swap(lhs, rhs); - if ( (m().are_equal(lhs, t) && m().are_equal(rhs, e)) - || (m().are_equal(lhs, e) && m().are_equal(rhs, t))) { + if ( (m.are_equal(lhs, t) && m.are_equal(rhs, e)) + || (m.are_equal(lhs, e) && m.are_equal(rhs, t))) { // (a = b ? a : b) is b. (a = b ? b : a) is a result = e; return BR_REWRITE1; @@ -2846,8 +2841,8 @@ br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resu && is_numeral(t, t_n, t_sz) && is_numeral(e, e_n, e_sz)) { if (t_sz == 1) { SASSERT(rhs_sz == sz && e_sz == sz && t_sz == sz); - SASSERT(!m().are_equal(t, e)); - result = m().are_equal(rhs, t) ? lhs : m_util.mk_bv_not(lhs); + SASSERT(!m.are_equal(t, e)); + result = m.are_equal(rhs, t) ? lhs : m_util.mk_bv_not(lhs); return BR_REWRITE1; } if (rhs_n.is_one() && t_n.is_one() && e_n.is_zero()) { @@ -2873,7 +2868,7 @@ br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resu br_status bv_rewriter::mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) { if (num_args <= 1) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } unsigned sz = get_bv_size(args[0]); @@ -2882,7 +2877,7 @@ br_status bv_rewriter::mk_distinct(unsigned num_args, expr * const * args, expr_ return BR_FAILED; if (num_args <= 1u << sz) return BR_FAILED; - result = m().mk_false(); + result = m.mk_false(); return BR_DONE; } @@ -2895,11 +2890,11 @@ br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_num2 = is_numeral(args[1], a1_val, bv_sz); if (is_num1 && (a0_val.is_zero() || (bv_sz != 1 && a0_val.is_one()))) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } if (is_num2 && (a1_val.is_zero() || (bv_sz != 1 && a1_val.is_one()))) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } @@ -2913,9 +2908,9 @@ br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, rational lim = rational::power_of_two(bv_sz-1); rational r = a0_val * a1_val; if (is_overflow) - result = m().mk_bool_val(sign0 != sign1 || r < lim); + result = m.mk_bool_val(sign0 != sign1 || r < lim); else - result = m().mk_bool_val(sign0 == sign1 || r <= lim); + result = m.mk_bool_val(sign0 == sign1 || r <= lim); return BR_DONE; } @@ -2927,18 +2922,18 @@ br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, bool is_num1 = is_numeral(args[0], a0_val, bv_sz); bool is_num2 = is_numeral(args[1], a1_val, bv_sz); if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) { - result = m().mk_true(); + result = m.mk_true(); return BR_DONE; } if (is_num1 && is_num2) { rational mr = a0_val * a1_val; rational lim = rational::power_of_two(bv_sz); - result = m().mk_bool_val(mr < lim); + result = m.mk_bool_val(mr < lim); return BR_DONE; } diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 703e668b679..b22a119474a 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -25,10 +25,11 @@ Module Name: class bv_rewriter_core { protected: + ast_manager& m; typedef rational numeral; bv_util m_util; - ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } + expr_ref m_bit1; bool is_numeral(expr * n) const { return m_util.is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { unsigned sz; return m_util.is_numeral(n, r, sz); } @@ -44,7 +45,7 @@ class bv_rewriter_core { decl_kind power_decl_kind() const { UNREACHABLE(); return static_cast(UINT_MAX); } public: - bv_rewriter_core(ast_manager & m):m_util(m) {} + bv_rewriter_core(ast_manager & m):m(m), m_util(m), m_bit1(m) {} }; class bv_rewriter : public poly_rewriter { @@ -176,7 +177,7 @@ class bv_rewriter : public poly_rewriter { br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) - result = m().mk_app(f, num_args, args); + result = m.mk_app(f, num_args, args); } bool is_urem_any(expr * e, expr * & dividend, expr * & divisor); @@ -190,14 +191,14 @@ class bv_rewriter : public poly_rewriter { #define MK_BV_BINARY(OP) \ expr_ref OP(expr* a, expr* b) { \ - expr_ref result(m()); \ + expr_ref result(m); \ if (BR_FAILED == OP(a, b, result)) \ result = m_util.OP(a, b); \ return result; \ } \ expr_ref mk_zero_extend(unsigned n, expr * arg) { - expr_ref result(m()); + expr_ref result(m); if (BR_FAILED == mk_zero_extend(n, arg, result)) result = m_util.mk_zero_extend(n, arg); return result; @@ -211,7 +212,7 @@ class bv_rewriter : public poly_rewriter { expr_ref mk_bv2int(expr* a) { - expr_ref result(m()); + expr_ref result(m); if (BR_FAILED == mk_bv2int(a, result)) result = m_util.mk_bv2int(a); return result; diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h index c505103bb20..c9253b0e403 100644 --- a/src/ast/rewriter/poly_rewriter.h +++ b/src/ast/rewriter/poly_rewriter.h @@ -106,7 +106,6 @@ class poly_rewriter : public Config { SASSERT(!m_som || !m_hoist_mul); // som is mutually exclusive with hoisting multiplication. } - ast_manager & m() const { return Config::m(); } family_id get_fid() const { return Config::get_fid(); } void updt_params(params_ref const & p); diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index 0a17fc37537..f9ae333b414 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -51,7 +51,7 @@ expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) switch (num_args) { case 0: return mk_numeral(numeral(0)); case 1: return args[0]; - default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); + default: return m.mk_app(get_fid(), add_decl_kind(), num_args, args); } } @@ -119,7 +119,7 @@ expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) if (new_args.size() > 2 && is_numeral(new_args.get(0), a)) { return mk_mul_app(a, mk_mul_app(new_args.size() - 1, new_args.data() + 1)); } - return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.data()); + return m.mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.data()); } } else { @@ -127,7 +127,7 @@ expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) if (num_args > 2 && is_numeral(args[0], a)) { return mk_mul_app(a, mk_mul_app(num_args - 1, args + 1)); } - return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); + return m.mk_app(get_fid(), mul_decl_kind(), num_args, args); } } } @@ -189,9 +189,9 @@ br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * cons br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.data(), result); TRACE("poly_rewriter", tout << "flat mul:\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m) << "\n"; tout << "---->\n"; - for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n"; + for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m) << "\n"; tout << st << "\n"; ); if (st == BR_FAILED) { @@ -292,7 +292,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); } result = mk_add_app(new_add_args.size(), new_add_args.data()); - TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); + TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m,5) << "\n";); return BR_REWRITE2; } } @@ -328,7 +328,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); + tout << mk_ismt2_pp(new_args[i], m); } tout << "\nordered: " << ordered << "\n";); if (ordered && num_coeffs == 0 && !use_power()) @@ -340,7 +340,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); + tout << mk_ismt2_pp(new_args[i], m); } tout << "\n";); } @@ -349,8 +349,8 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con result = mk_mul_app(c, result); TRACE("poly_rewriter", for (unsigned i = 0; i < num_args; ++i) - tout << mk_ismt2_pp(args[i], m()) << " "; - tout << "\nmk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); + tout << mk_ismt2_pp(args[i], m) << " "; + tout << "\nmk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m) << "\n";); return BR_DONE; } @@ -373,7 +373,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con } } unsigned orig_size = sums.size(); - expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception + expr_ref_buffer sum(m); // must be ref_buffer because we may throw an exception ptr_buffer m_args; TRACE("som", tout << "starting som...\n";); do { @@ -566,7 +566,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con SASSERT(m_sort_sums || ordered); TRACE("rewriter", tout << "ordered: " << ordered << " sort sums: " << m_sort_sums << "\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); + for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m) << "\n";); if (has_multiple) { // expensive case @@ -589,7 +589,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con coeffs.push_back(a); } } - expr_ref_buffer new_args(m()); + expr_ref_buffer new_args(m); if (!c.is_zero()) { new_args.push_back(mk_numeral(c)); } @@ -639,7 +639,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) return BR_FAILED; } - expr_ref_buffer new_args(m()); + expr_ref_buffer new_args(m); if (!c.is_zero()) new_args.push_back(mk_numeral(c)); for (unsigned i = 0; i < num_args; i++) { @@ -690,8 +690,8 @@ br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, return BR_DONE; } set_curr_sort(args[0]->get_sort()); - expr_ref minus_one(mk_numeral(numeral(-1)), m()); - expr_ref_buffer new_args(m()); + expr_ref minus_one(mk_numeral(numeral(-1)), m); + expr_ref_buffer new_args(m); new_args.push_back(args[0]); for (unsigned i = 1; i < num_args; i++) { if (is_zero(args[i])) continue; @@ -984,11 +984,11 @@ bool poly_rewriter::hoist_ite(expr_ref& e) { return false; obj_hashtable shared; ptr_buffer adds; - expr_ref_vector bs(m()), pinned(m()); + expr_ref_vector bs(m), pinned(m); TO_BUFFER(is_add, adds, e); unsigned i = 0; for (expr* a : adds) { - if (m().is_ite(a)) { + if (m.is_ite(a)) { shared.reset(); numeral g(0); if (hoist_ite(a, shared, g) && (is_nontrivial_gcd(g) || !shared.empty())) { @@ -1026,7 +1026,7 @@ bool poly_rewriter::hoist_ite(expr_ref& e) { template bool poly_rewriter::hoist_ite(expr* a, obj_hashtable& shared, numeral& g) { expr* c = nullptr, *t = nullptr, *e = nullptr; - if (m().is_ite(a, c, t, e)) { + if (m.is_ite(a, c, t, e)) { return hoist_ite(t, shared, g) && hoist_ite(e, shared, g); } rational k, g1; @@ -1064,8 +1064,8 @@ bool poly_rewriter::hoist_ite(expr* a, obj_hashtable& shared, nume template expr* poly_rewriter::apply_hoist(expr* a, numeral const& g, obj_hashtable const& shared) { expr* c = nullptr, *t = nullptr, *e = nullptr; - if (m().is_ite(a, c, t, e)) { - return m().mk_ite(c, apply_hoist(t, g, shared), apply_hoist(e, g, shared)); + if (m.is_ite(a, c, t, e)) { + return m.mk_ite(c, apply_hoist(t, g, shared), apply_hoist(e, g, shared)); } rational k; if (is_nontrivial_gcd(g) && is_int_numeral(a, k)) { From 758c3b2c3ba02d8c6fb0d26180018fb9007549e2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Dec 2022 07:53:53 -0800 Subject: [PATCH 158/597] fix filtering for recursive functions --- src/ast/simplifiers/dependent_expr_state.cpp | 18 +++++++++++++----- src/ast/simplifiers/dependent_expr_state.h | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp index 40528fe99cf..8e9ecf19040 100644 --- a/src/ast/simplifiers/dependent_expr_state.cpp +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -65,14 +65,22 @@ void dependent_expr_state::freeze_terms(expr* e, bool only_as_array, ast_mark& v */ void dependent_expr_state::freeze_recfun() { - if (m_recfun_frozen) - return; - m_recfun_frozen = true; auto& m = m_frozen_trail.get_manager(); recfun::util rec(m); + if (!rec.has_rec_defs()) + return; + unsigned sz = rec.get_rec_funs().size(); + if (m_num_recfun >= sz) + return; + ast_mark visited; - for (func_decl* f : rec.get_rec_funs()) - freeze_terms(rec.get_def(f).get_rhs(), false, visited); + for (func_decl* f : rec.get_rec_funs()) { + auto& d = rec.get_def(f); + if (!d.is_macro()) + freeze_terms(d.get_rhs(), false, visited); + } + m_trail.push(value_trail(m_num_recfun)); + m_num_recfun = sz; } /** diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 9f27a336c74..d7aa1d6ebd1 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -43,7 +43,7 @@ Module Name: class dependent_expr_state { unsigned m_qhead = 0; bool m_suffix_frozen = false; - bool m_recfun_frozen = false; + unsigned m_num_recfun = 0; lbool m_has_quantifiers = l_undef; ast_mark m_frozen; func_decl_ref_vector m_frozen_trail; From 3ebbb8472a4f57672528f69dc22850312dd9ba2d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Dec 2022 07:54:14 -0800 Subject: [PATCH 159/597] fix perf bugs in new value propagation --- src/ast/simplifiers/propagate_values.cpp | 74 +++++++++++++----------- src/ast/simplifiers/propagate_values.h | 6 ++ 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index f0d85af7279..7d698faa310 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -22,44 +22,19 @@ Module Name: #include "params/tactic_params.hpp" #include "ast/ast_pp.h" #include "ast/ast_util.h" -#include "ast/shared_occs.h" #include "ast/simplifiers/propagate_values.h" propagate_values::propagate_values(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls), - m_rewriter(m) { - m_rewriter.set_order_eq(true); + m_rewriter(m), + m_shared(m, true), + m_subst(m, true, false) { m_rewriter.set_flat_and_or(false); updt_params(p); } -void propagate_values::reduce() { - shared_occs shared(m, true); - expr_substitution subst(m, true, false); - expr* x, * y; - - auto add_shared = [&]() { - shared_occs_mark visited; - shared.reset(); - for (unsigned i = 0; i < qtail(); ++i) - shared(m_fmls[i].fml(), visited); - }; - - auto add_sub = [&](dependent_expr const& de) { - auto const& [f, dep] = de(); - if (m.is_not(f, x) && shared.is_shared(x)) - subst.insert(x, m.mk_false(), dep); - else if (shared.is_shared(f)) - subst.insert(f, m.mk_true(), dep); - if (m.is_eq(f, x, y)) { - if (m.is_value(x) && shared.is_shared(y)) - subst.insert(y, x, dep); - else if (m.is_value(y) && shared.is_shared(x)) - subst.insert(x, y, dep); - } - }; - - auto process_fml = [&](unsigned i) { +void propagate_values::process_fml(unsigned i) { + if (!m_subst.empty()) { auto [f, dep] = m_fmls[i](); expr_ref fml(m); proof_ref pr(m); @@ -70,14 +45,41 @@ void propagate_values::reduce() { ++m_stats.m_num_rewrites; } m_rewriter.reset_used_dependencies(); - add_sub(m_fmls[i]); - }; + } + add_sub(m_fmls[i]); +} + +void propagate_values::add_sub(dependent_expr const& de) { + expr* x, * y; + auto const& [f, dep] = de(); + if (m.is_not(f, x) && m_shared.is_shared(x)) + m_subst.insert(x, m.mk_false(), dep); + if (m_shared.is_shared(f)) + m_subst.insert(f, m.mk_true(), dep); + if (m.is_eq(f, x, y)) { + if (m.is_value(x) && m_shared.is_shared(y)) + m_subst.insert(y, x, dep); + else if (m.is_value(y) && m_shared.is_shared(x)) + m_subst.insert(x, y, dep); + } +}; +void propagate_values::reduce() { + m_shared.reset(); + m_subst.reset(); + + auto add_shared = [&]() { + shared_occs_mark visited; + m_shared.reset(); + for (unsigned i = 0; i < qtail(); ++i) + m_shared(m_fmls[i].fml(), visited); + }; + auto init_sub = [&]() { add_shared(); - subst.reset(); + m_subst.reset(); m_rewriter.reset(); - m_rewriter.set_substitution(&subst); + m_rewriter.set_substitution(&m_subst); for (unsigned i = 0; i < qhead(); ++i) add_sub(m_fmls[i]); }; @@ -91,12 +93,14 @@ void propagate_values::reduce() { init_sub(); for (unsigned i = qtail(); i-- > qhead() && m.inc() && !m_fmls.inconsistent();) process_fml(i); - if (subst.empty()) + if (m_subst.empty()) break; } m_rewriter.set_substitution(nullptr); m_rewriter.reset(); + m_subst.reset(); + m_shared.reset(); } void propagate_values::collect_statistics(statistics& st) const { diff --git a/src/ast/simplifiers/propagate_values.h b/src/ast/simplifiers/propagate_values.h index 9ad59d1b34d..8b4e9fdd09a 100644 --- a/src/ast/simplifiers/propagate_values.h +++ b/src/ast/simplifiers/propagate_values.h @@ -22,6 +22,7 @@ Module Name: #include "ast/simplifiers/dependent_expr_state.h" #include "ast/rewriter/th_rewriter.h" +#include "ast/shared_occs.h" class propagate_values : public dependent_expr_simplifier { @@ -34,6 +35,11 @@ class propagate_values : public dependent_expr_simplifier { th_rewriter m_rewriter; stats m_stats; unsigned m_max_rounds = 4; + shared_occs m_shared; + expr_substitution m_subst; + + void process_fml(unsigned i); + void add_sub(dependent_expr const& de); public: propagate_values(ast_manager& m, params_ref const& p, dependent_expr_state& fmls); From 59fa8964cad87215cc8f6e24661224cc8566e2c3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Dec 2022 07:54:32 -0800 Subject: [PATCH 160/597] minor code cleanup --- src/tactic/arith/bound_propagator.cpp | 5 ++--- src/tactic/arith/propagate_ineqs_tactic.cpp | 4 +--- src/tactic/smtlogics/qfauflia_tactic.cpp | 1 - 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tactic/arith/bound_propagator.cpp b/src/tactic/arith/bound_propagator.cpp index 3c4844462a0..ba58b61603c 100644 --- a/src/tactic/arith/bound_propagator.cpp +++ b/src/tactic/arith/bound_propagator.cpp @@ -840,9 +840,8 @@ void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector break; } } - unsigned sz = todo.size(); - for (unsigned i = 0; i < sz; i++) - todo[i].second->m_mark = false; + for (var_bound& vb : todo) + vb.second->m_mark = false; todo.reset(); } diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp index 0f62b45f436..871a9ac3bcc 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ b/src/tactic/arith/propagate_ineqs_tactic.cpp @@ -135,9 +135,7 @@ struct propagate_ineqs_tactic::imp { mpq c_mpq_val; if (m_util.is_add(t)) { rational c_val; - unsigned num = to_app(t)->get_num_args(); - for (unsigned i = 0; i < num; i++) { - expr * mon = to_app(t)->get_arg(i); + for (expr* mon : *to_app(t)) { expr * c, * x; if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) { nm.set(c_mpq_val, c_val.to_mpq()); diff --git a/src/tactic/smtlogics/qfauflia_tactic.cpp b/src/tactic/smtlogics/qfauflia_tactic.cpp index 2f1879d5878..5c2a92ef6eb 100644 --- a/src/tactic/smtlogics/qfauflia_tactic.cpp +++ b/src/tactic/smtlogics/qfauflia_tactic.cpp @@ -19,7 +19,6 @@ Module Name: #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" -#include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/smtlogics/smt_tactic.h" From 79e6d4e32dc329b4be35d413feaa299ea5da3c4a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Dec 2022 20:23:46 -0800 Subject: [PATCH 161/597] tune and debug elim-unconstrained (v2 - for simplifiers infrastructure) --- src/ast/bv_decl_plugin.h | 5 ++ src/ast/converters/expr_inverter.cpp | 54 ++++++++++--- src/ast/rewriter/bv_rewriter.cpp | 89 +++++++++++----------- src/ast/rewriter/bv_rewriter.h | 4 + src/ast/simplifiers/elim_unconstrained.cpp | 42 ++++++++-- src/ast/simplifiers/elim_unconstrained.h | 2 + 6 files changed, 132 insertions(+), 64 deletions(-) diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index ee618b42cbb..fc7e3524557 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -411,6 +411,11 @@ class bv_util : public bv_recognizers { app * mk_numeral(rational const & val, sort* s) const; app * mk_numeral(rational const & val, unsigned bv_size) const; app * mk_numeral(uint64_t u, unsigned bv_size) const { return mk_numeral(rational(u, rational::ui64()), bv_size); } + app * mk_zero(sort* s) const { return mk_numeral(rational::zero(), s); } + app * mk_zero(unsigned bv_size) const { return mk_numeral(rational::zero(), bv_size); } + app * mk_one(sort* s) const { return mk_numeral(rational::one(), s); } + app * mk_one(unsigned bv_size) const { return mk_numeral(rational::one(), bv_size); } + sort * mk_sort(unsigned bv_size); unsigned get_bv_size(sort const * s) const { diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 2874abd6881..5553420adcd 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -300,7 +300,7 @@ class bv_expr_inverter : public iexpr_inverter { sort* s = args[0]->get_sort(); mk_fresh_uncnstr_var_for(f, r); if (m_mc) - add_defs(num, args, r, bv.mk_numeral(rational(1), s)); + add_defs(num, args, r, bv.mk_one(s)); return true; } // c * v (c is odd) case @@ -335,7 +335,7 @@ class bv_expr_inverter : public iexpr_inverter { } mk_fresh_uncnstr_var_for(f, r); if (sh > 0) - r = bv.mk_concat(bv.mk_extract(sz - sh - 1, 0, r), bv.mk_numeral(0, sh)); + r = bv.mk_concat(bv.mk_extract(sz - sh - 1, 0, r), bv.mk_zero(sh)); if (m_mc) { rational inv_r; @@ -376,10 +376,10 @@ class bv_expr_inverter : public iexpr_inverter { else { ptr_buffer args; if (high < bv_size - 1) - args.push_back(bv.mk_numeral(rational(0), bv_size - high - 1)); + args.push_back(bv.mk_zero(bv_size - high - 1)); args.push_back(r); if (low > 0) - args.push_back(bv.mk_numeral(rational(0), low)); + args.push_back(bv.mk_zero(low)); add_def(arg, bv.mk_concat(args.size(), args.data())); } return true; @@ -391,7 +391,7 @@ class bv_expr_inverter : public iexpr_inverter { mk_fresh_uncnstr_var_for(f, r); if (m_mc) { add_def(arg1, r); - add_def(arg2, bv.mk_numeral(rational(1), s)); + add_def(arg2, bv.mk_one(s)); } return true; } @@ -419,13 +419,22 @@ class bv_expr_inverter : public iexpr_inverter { } bool process_bv_le(func_decl* f, expr* arg1, expr* arg2, bool is_signed, expr_ref& r) { + unsigned bv_sz = bv.get_bv_size(arg1); + if (uncnstr(arg1) && uncnstr(arg2)) { + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(arg1, m.mk_ite(r, bv.mk_zero(bv_sz), bv.mk_one(bv_sz))); + add_def(arg2, bv.mk_zero(bv_sz)); + } + return true; + } if (uncnstr(arg1)) { // v <= t expr* v = arg1; expr* t = arg2; // v <= t ---> (u or t == MAX) u is fresh // add definition v = ite(u or t == MAX, t, t+1) - unsigned bv_sz = bv.get_bv_size(arg1); + rational MAX; if (is_signed) MAX = rational::power_of_two(bv_sz - 1) - rational(1); @@ -434,7 +443,7 @@ class bv_expr_inverter : public iexpr_inverter { mk_fresh_uncnstr_var_for(f, r); r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MAX, bv_sz))); if (m_mc) - add_def(v, m.mk_ite(r, t, bv.mk_bv_add(t, bv.mk_numeral(rational(1), bv_sz)))); + add_def(v, m.mk_ite(r, t, bv.mk_bv_add(t, bv.mk_one(bv_sz)))); return true; } if (uncnstr(arg2)) { @@ -443,7 +452,6 @@ class bv_expr_inverter : public iexpr_inverter { expr* t = arg1; // v >= t ---> (u ot t == MIN) u is fresh // add definition v = ite(u or t == MIN, t, t-1) - unsigned bv_sz = bv.get_bv_size(arg1); rational MIN; if (is_signed) MIN = -rational::power_of_two(bv_sz - 1); @@ -452,7 +460,7 @@ class bv_expr_inverter : public iexpr_inverter { mk_fresh_uncnstr_var_for(f, r); r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MIN, bv_sz))); if (m_mc) - add_def(v, m.mk_ite(r, t, bv.mk_bv_sub(t, bv.mk_numeral(rational(1), bv_sz)))); + add_def(v, m.mk_ite(r, t, bv.mk_bv_sub(t, bv.mk_one(bv_sz)))); return true; } return false; @@ -467,6 +475,18 @@ class bv_expr_inverter : public iexpr_inverter { return true; } + bool process_shift(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) { + if (uncnstr(arg1) && uncnstr(arg2)) { + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(arg1, r); + add_def(arg2, bv.mk_zero(arg2->get_sort())); + } + return true; + } + return false; + } + public: bv_expr_inverter(ast_manager& m) : iexpr_inverter(m), bv(m) {} @@ -543,10 +563,23 @@ class bv_expr_inverter : public iexpr_inverter { sort* s = args[0]->get_sort(); mk_fresh_uncnstr_var_for(f, r); if (m_mc) - add_defs(num, args, r, bv.mk_numeral(rational(0), s)); + add_defs(num, args, r, bv.mk_zero(s)); + return true; + } + return false; + case OP_BAND: + if (num > 0 && uncnstr(num, args)) { + sort* s = args[0]->get_sort(); + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) + add_defs(num, args, r, bv.mk_numeral(rational::power_of_two(bv.get_bv_size(s)) - 1, s)); return true; } return false; + case OP_BSHL: + case OP_BASHR: + case OP_BLSHR: + return process_shift(f, args[0], args[1], r); default: return false; } @@ -599,6 +632,7 @@ class array_expr_inverter : public iexpr_inverter { } return true; } + return false; default: return false; } diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 1557c9b3cd8..34d2f650885 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -55,8 +55,8 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons SASSERT(f->get_family_id() == get_fid()); switch(f->get_decl_kind()) { - case OP_BIT0: SASSERT(num_args == 0); result = m_util.mk_numeral(0, 1); return BR_DONE; - case OP_BIT1: SASSERT(num_args == 0); result = m_util.mk_numeral(1, 1); return BR_DONE; + case OP_BIT0: SASSERT(num_args == 0); result = mk_zero(1); return BR_DONE; + case OP_BIT1: SASSERT(num_args == 0); result = mk_one(1); return BR_DONE; case OP_ULEQ: SASSERT(num_args == 2); return mk_ule(args[0], args[1], result); @@ -570,11 +570,11 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref if (first_non_zero == UINT_MAX) { // all bits are zero - result = m.mk_eq(a, m_util.mk_numeral(numeral(0), bv_sz)); + result = m.mk_eq(a, mk_zero(bv_sz)); return BR_REWRITE1; } else if (first_non_zero < bv_sz - 1 && m_le2extract) { - result = m.mk_and(m.mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)), + result = m.mk_and(m.mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), mk_zero(bv_sz - first_non_zero - 1)), m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b))); return BR_REWRITE3; } @@ -817,7 +817,7 @@ br_status bv_rewriter::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { } if (r2 >= numeral(bv_size)) { - result = mk_numeral(0, bv_size); + result = mk_zero(bv_size); return BR_DONE; } @@ -846,7 +846,7 @@ br_status bv_rewriter::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { // (bvshl x k) -> (concat (extract [n-1-k:0] x) bv0:k) unsigned k = r2.get_unsigned(); expr * new_args[2] = { m_mk_extract(bv_size - k - 1, 0, arg1), - mk_numeral(0, k) }; + mk_zero(k) }; result = m_util.mk_concat(2, new_args); return BR_REWRITE2; } @@ -857,7 +857,7 @@ br_status bv_rewriter::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { expr_ref cond(m_util.mk_ule(y, sum), m); result = m.mk_ite(cond, m_util.mk_bv_shl(x, sum), - mk_numeral(0, bv_size)); + mk_zero(bv_size)); return BR_REWRITE3; } @@ -877,7 +877,7 @@ br_status bv_rewriter::mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result) { } if (r2 >= numeral(bv_size)) { - result = mk_numeral(0, bv_size); + result = mk_zero(bv_size); return BR_DONE; } @@ -904,14 +904,14 @@ br_status bv_rewriter::mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result) { // (bvlshr x k) -> (concat bv0:k (extract [n-1:k] x)) SASSERT(r2.is_unsigned()); unsigned k = r2.get_unsigned(); - expr * new_args[2] = { mk_numeral(0, k), + expr * new_args[2] = { mk_zero(k), m_mk_extract(bv_size - 1, k, arg1) }; result = m_util.mk_concat(2, new_args); return BR_REWRITE2; } if (arg1 == arg2) { - result = mk_numeral(0, bv_size); + result = mk_zero(bv_size); return BR_DONE; } @@ -960,7 +960,7 @@ br_status bv_rewriter::mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.has_sign_bit(r1, bv_size)) result = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); else - result = mk_numeral(0, bv_size); + result = mk_zero(bv_size); return BR_DONE; } @@ -1027,8 +1027,8 @@ br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, e } else { // The "hardware interpretation" for (bvsdiv x 0) is (ite (bvslt x #x0000) #x0001 #xffff) - result = m.mk_ite(m.mk_app(get_fid(), OP_SLT, arg1, mk_numeral(0, bv_size)), - mk_numeral(1, bv_size), + result = m.mk_ite(m.mk_app(get_fid(), OP_SLT, arg1, mk_zero(bv_size)), + mk_one(bv_size), mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size)); return BR_REWRITE2; } @@ -1055,7 +1055,7 @@ br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_zero(bv_size)), m_util.mk_bv_sdiv0(arg1), m_util.mk_bv_sdiv_i(arg1, arg2)); return BR_REWRITE2; @@ -1110,7 +1110,7 @@ br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_zero(bv_size)), m_util.mk_bv_udiv0(arg1), m_util.mk_bv_udiv_i(arg1, arg2)); @@ -1137,7 +1137,7 @@ br_status bv_rewriter::mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, e } if (r2.is_one()) { - result = mk_numeral(0, bv_size); + result = mk_zero(bv_size); return BR_DONE; } @@ -1157,7 +1157,7 @@ br_status bv_rewriter::mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_zero(bv_size)), m.mk_app(get_fid(), OP_BSREM0, arg1), m.mk_app(get_fid(), OP_BSREM_I, arg1, arg2)); return BR_REWRITE2; @@ -1222,7 +1222,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e } if (r2.is_one()) { - result = mk_numeral(0, bv_size); + result = mk_zero(bv_size); return BR_DONE; } @@ -1236,7 +1236,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e unsigned shift; if (r2.is_power_of_two(shift)) { expr * args[2] = { - mk_numeral(0, bv_size - shift), + mk_zero(bv_size - shift), m_mk_extract(shift-1, 0, arg1) }; result = m_util.mk_concat(2, args); @@ -1263,7 +1263,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e bv_size = get_bv_size(arg1); expr * x_minus_1 = arg1; expr * minus_one = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); - result = m.mk_ite(m.mk_eq(x, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(x, mk_zero(bv_size)), m_util.mk_bv_urem0(minus_one), x_minus_1); return BR_REWRITE2; @@ -1293,7 +1293,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_zero(bv_size)), m_util.mk_bv_urem0(arg1), m_util.mk_bv_urem_i(arg1, arg2)); return BR_REWRITE2; @@ -1343,7 +1343,7 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e if (r2.is_one()) { // (bvsmod x 1) --> 0 - result = mk_numeral(0, bv_size); + result = mk_zero(bv_size); return BR_REWRITE2; } @@ -1357,8 +1357,8 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e unsigned nb = r2.get_num_bits(); expr_ref a1(m_util.mk_bv_smod(a, arg2), m); expr_ref a2(m_util.mk_bv_smod(b, arg2), m); - a1 = m_util.mk_concat( mk_numeral(0, bv_size - nb), m_mk_extract(nb-1,0,a1)); - a2 = m_util.mk_concat( mk_numeral(0, bv_size - nb), m_mk_extract(nb-1,0,a2)); + a1 = m_util.mk_concat( mk_zero(bv_size - nb), m_mk_extract(nb-1,0,a1)); + a2 = m_util.mk_concat( mk_zero(bv_size - nb), m_mk_extract(nb-1,0,a2)); result = m_util.mk_bv_mul(a1, a2); std::cout << result << "\n"; result = m_util.mk_bv_smod(result, arg2); @@ -1374,7 +1374,7 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e } bv_size = get_bv_size(arg2); - result = m.mk_ite(m.mk_eq(arg2, mk_numeral(0, bv_size)), + result = m.mk_ite(m.mk_eq(arg2, mk_zero(bv_size)), m.mk_app(get_fid(), OP_BSMOD0, arg1), m.mk_app(get_fid(), OP_BSMOD_I, arg1, arg2)); return BR_REWRITE2; @@ -1585,7 +1585,7 @@ br_status bv_rewriter::mk_zero_extend(unsigned n, expr * arg, expr_ref & result) return BR_DONE; } else { - expr * args[2] = { mk_numeral(0, n), arg }; + expr * args[2] = { mk_zero(n), arg }; result = m_util.mk_concat(2, args); return BR_REWRITE1; } @@ -1789,7 +1789,7 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re switch (new_args.size()) { case 0: - result = mk_numeral(0, sz); + result = mk_zero(sz); return BR_DONE; case 1: result = new_args[0]; @@ -1959,7 +1959,7 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r switch (new_args.size()) { case 0: - result = mk_numeral(0, sz); + result = mk_zero(sz); return BR_DONE; case 1: result = new_args[0]; @@ -2054,7 +2054,7 @@ br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { // ~(x + y) --> (~x + ~y + 1) when x and y are easy to negate if (is_negatable(t, nt) && is_negatable(s, ns)) { bv_size = m_util.get_bv_size(s); - expr * nargs[3] = { m_util.mk_numeral(rational::one(), bv_size), ns.get(), nt.get() }; + expr * nargs[3] = { mk_one(bv_size), ns.get(), nt.get() }; result = m.mk_app(m_util.get_fid(), OP_BADD, 3, nargs); return BR_REWRITE1; } @@ -2146,7 +2146,7 @@ br_status bv_rewriter::mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref br_status bv_rewriter::mk_bv_redor(expr * arg, expr_ref & result) { if (is_numeral(arg)) { - result = m_util.is_zero(arg) ? mk_numeral(0, 1) : mk_numeral(1, 1); + result = m_util.is_zero(arg) ? mk_zero(1) : mk_one(1); return BR_DONE; } return BR_FAILED; @@ -2156,7 +2156,7 @@ br_status bv_rewriter::mk_bv_redand(expr * arg, expr_ref & result) { numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { - result = (r == rational::power_of_two(bv_size) - numeral(1)) ? mk_numeral(1, 1) : mk_numeral(0, 1); + result = (r == rational::power_of_two(bv_size) - numeral(1)) ? mk_one(1) : mk_zero(1); return BR_DONE; } return BR_FAILED; @@ -2164,19 +2164,19 @@ br_status bv_rewriter::mk_bv_redand(expr * arg, expr_ref & result) { br_status bv_rewriter::mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result) { if (arg1 == arg2) { - result = mk_numeral(1,1); + result = mk_one(1); return BR_DONE; } if (is_numeral(arg1) && is_numeral(arg2)) { SASSERT(arg1 != arg2); - result = mk_numeral(0, 1); + result = mk_zero(1); return BR_DONE; } result = m.mk_ite(m.mk_eq(arg1, arg2), - mk_numeral(1, 1), - mk_numeral(0, 1)); + mk_one(1), + mk_zero(1)); return BR_REWRITE2; } @@ -2334,7 +2334,7 @@ br_status bv_rewriter::mk_bv_mul(unsigned num_args, expr * const * args, expr_re SASSERT(shift >= 1); expr * args[2] = { m_mk_extract(bv_size-shift-1, 0, y), - mk_numeral(0, shift) + mk_zero(shift) }; result = m_util.mk_concat(2, args); return BR_REWRITE2; @@ -2399,7 +2399,7 @@ br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { if (m_util.is_bv_or(lhs)) { if (!m_bit1) - m_bit1 = is_one ? rhs : mk_numeral(numeral(1), 1); + m_bit1 = is_one ? rhs : mk_one(1); ptr_buffer new_args; for (expr* arg : *to_app(lhs)) new_args.push_back(m.mk_eq(arg, m_bit1)); @@ -2416,7 +2416,7 @@ br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { if (m_util.is_bv_xor(lhs)) { if (!m_bit1) - m_bit1 = is_one ? rhs : mk_numeral(numeral(1), 1); + m_bit1 = is_one ? rhs : mk_one(1); ptr_buffer new_args; for (expr* arg : *to_app(lhs)) new_args.push_back(m.mk_eq(arg, m_bit1)); @@ -2499,7 +2499,7 @@ br_status bv_rewriter::mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result) { unsigned rsz2 = sz2 - low2; if (rsz1 == rsz2) { new_eqs.push_back(m.mk_eq(m_mk_extract(sz1 - 1, low1, arg1), - m_mk_extract(sz2 - 1, low2, arg2))); + m_mk_extract(sz2 - 1, low2, arg2))); low1 = 0; low2 = 0; --i1; @@ -2508,14 +2508,14 @@ br_status bv_rewriter::mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result) { } else if (rsz1 < rsz2) { new_eqs.push_back(m.mk_eq(m_mk_extract(sz1 - 1, low1, arg1), - m_mk_extract(rsz1 + low2 - 1, low2, arg2))); + m_mk_extract(rsz1 + low2 - 1, low2, arg2))); low1 = 0; low2 += rsz1; --i1; } else { new_eqs.push_back(m.mk_eq(m_mk_extract(rsz2 + low1 - 1, low1, arg1), - m_mk_extract(sz2 - 1, low2, arg2))); + m_mk_extract(sz2 - 1, low2, arg2))); low1 += rsz2; low2 = 0; --i2; @@ -2572,12 +2572,9 @@ bool bv_rewriter::isolate_term(expr* lhs, expr* rhs, expr_ref& result) { } bool bv_rewriter::is_add_mul_const(expr* e) const { - if (!m_util.is_bv_add(e)) { + if (!m_util.is_bv_add(e)) return false; - } - unsigned num = to_app(e)->get_num_args(); - for (unsigned i = 0; i < num; i++) { - expr * arg = to_app(e)->get_arg(i); + for (expr * arg : *to_app(e)) { expr * c2, * x2; if (m_util.is_numeral(arg)) continue; diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index b22a119474a..ca999c79300 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -173,6 +173,10 @@ class bv_rewriter : public poly_rewriter { bool is_bv(expr * t) const { return m_util.is_bv(t); } expr * mk_numeral(numeral const & v, unsigned sz) { return m_util.mk_numeral(v, sz); } expr * mk_numeral(unsigned v, unsigned sz) { return m_util.mk_numeral(numeral(v), sz); } + app * mk_zero(sort* s) { return m_util.mk_zero(s); } + app * mk_one(sort* s) { return m_util.mk_one(s); } + app * mk_zero(unsigned sz) { return m_util.mk_zero(sz); } + app * mk_one(unsigned sz) { return m_util.mk_one(sz); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 713d7941ce6..7404382a38c 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -96,14 +96,17 @@ void elim_unconstrained::eliminate() { m_trail.push_back(r); SASSERT(r); gc(e); + init_children(e, r); m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; get_node(e).m_refcount++; IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); SASSERT(!m_heap.contains(root(e))); - if (is_uninterp_const(r)) - m_heap.insert(root(e)); + if (is_uninterp_const(r)) + m_heap.insert(root(e)); + else + m_created_compound = true; IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(n.m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n";); @@ -177,6 +180,24 @@ void elim_unconstrained::init_terms(expr_ref_vector const& terms) { } } +void elim_unconstrained::init_children(expr* e, expr* r) { + expr_ref_vector children(m); + SASSERT(e != r); + if (is_quantifier(r)) + children.push_back(to_quantifier(r)->get_expr()); + else if (is_app(r)) + children.append(to_app(r)->get_num_args(), to_app(r)->get_args()); + else + return; + if (children.empty()) + return; + init_terms(children); + for (expr* arg : children) { + get_node(arg).m_parents.push_back(e); + inc_ref(arg); + } +} + void elim_unconstrained::gc(expr* t) { ptr_vector todo; todo.push_back(t); @@ -284,10 +305,15 @@ void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector< void elim_unconstrained::reduce() { generic_model_converter_ref mc = alloc(generic_model_converter, m, "elim-unconstrained"); m_inverter.set_model_converter(mc.get()); - init_nodes(); - eliminate(); - reconstruct_terms(); - vector old_fmls; - assert_normalized(old_fmls); - update_model_trail(*mc, old_fmls); + m_created_compound = true; + for (unsigned rounds = 0; m_created_compound && rounds < 3; ++rounds) { + m_created_compound = false; + init_nodes(); + eliminate(); + reconstruct_terms(); + vector old_fmls; + assert_normalized(old_fmls); + update_model_trail(*mc, old_fmls); + } + } diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 9da0f3fc205..1eb5d732b09 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -47,6 +47,7 @@ class elim_unconstrained : public dependent_expr_simplifier { ptr_vector m_args; stats m_stats; unsigned_vector m_root; + bool m_created_compound = false; bool is_var_lt(int v1, int v2) const; node& get_node(unsigned n) { return m_nodes[n]; } @@ -58,6 +59,7 @@ class elim_unconstrained : public dependent_expr_simplifier { void inc_ref(expr* t) { ++get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.increased(root(t)); } void dec_ref(expr* t) { --get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.decreased(root(t)); } void gc(expr* t); + void init_children(expr* e, expr* r); expr* get_parent(unsigned n) const; void init_terms(expr_ref_vector const& terms); void init_nodes(); From e455897178b9bd64dcd01316df998b91c2948af9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 04:36:06 -0800 Subject: [PATCH 162/597] fix #6476 --- src/tactic/ufbv/ufbv_rewriter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp index bfaf3598097..a6a4e195762 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/tactic/ufbv/ufbv_rewriter.cpp @@ -735,6 +735,8 @@ struct match_args_aux_proc { m_subst.insert(n, 0, expr_offset(n, 1)); } } + else + throw no_match(); } void operator()(quantifier * n) { throw no_match(); } void operator()(app * n) {} From 7fe67877489f2acda6f6fddbdcf0806877fba7e1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 04:44:02 -0800 Subject: [PATCH 163/597] ufbv-rewriter is really a demodulator rewriter and does not reference ufbv so moving first the rewriter into place of other rewriters --- src/ast/rewriter/CMakeLists.txt | 1 + .../rewriter/demodulator_rewriter.cpp} | 50 +++++++++---------- .../rewriter/demodulator_rewriter.h} | 9 ++-- src/tactic/ufbv/CMakeLists.txt | 1 - src/tactic/ufbv/ufbv_rewriter_tactic.cpp | 12 ++--- 5 files changed, 37 insertions(+), 36 deletions(-) rename src/{tactic/ufbv/ufbv_rewriter.cpp => ast/rewriter/demodulator_rewriter.cpp} (93%) rename src/{tactic/ufbv/ufbv_rewriter.h => ast/rewriter/demodulator_rewriter.h} (97%) diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index c785804c127..9eef651c84d 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -11,6 +11,7 @@ z3_add_component(rewriter cached_var_subst.cpp char_rewriter.cpp datatype_rewriter.cpp + demodulator_rewriter.cpp der.cpp distribute_forall.cpp dl_rewriter.cpp diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/ast/rewriter/demodulator_rewriter.cpp similarity index 93% rename from src/tactic/ufbv/ufbv_rewriter.cpp rename to src/ast/rewriter/demodulator_rewriter.cpp index a6a4e195762..8a691192506 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/ast/rewriter/demodulator_rewriter.cpp @@ -24,9 +24,9 @@ Revision History: #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/rewriter/var_subst.h" -#include "tactic/ufbv/ufbv_rewriter.h" +#include "ast/rewriter/demodulator_rewriter.h" -ufbv_rewriter::ufbv_rewriter(ast_manager & m): +demodulator_rewriter::demodulator_rewriter(ast_manager & m): m(m), m_match_subst(m), m_bsimp(m), @@ -41,7 +41,7 @@ ufbv_rewriter::ufbv_rewriter(ast_manager & m): m_bsimp.updt_params(p); } -ufbv_rewriter::~ufbv_rewriter() { +demodulator_rewriter::~demodulator_rewriter() { reset_dealloc_values(m_fwd_idx); reset_dealloc_values(m_back_idx); for (auto & kv : m_demodulator2lhs_rhs) { @@ -51,7 +51,7 @@ ufbv_rewriter::~ufbv_rewriter() { } } -bool ufbv_rewriter::is_demodulator(expr * e, app_ref & large, expr_ref & small) const { +bool demodulator_rewriter::is_demodulator(expr * e, app_ref & large, expr_ref & small) const { if (!is_forall(e)) { return false; } @@ -117,7 +117,7 @@ class var_set_proc { void operator()(app * n) {} }; -int ufbv_rewriter::is_subset(expr * e1, expr * e2) const { +int demodulator_rewriter::is_subset(expr * e1, expr * e2) const { uint_set ev1, ev2; if (m.is_value(e1)) @@ -134,7 +134,7 @@ int ufbv_rewriter::is_subset(expr * e1, expr * e2) const { 0 ; } -int ufbv_rewriter::is_smaller(expr * e1, expr * e2) const { +int demodulator_rewriter::is_smaller(expr * e1, expr * e2) const { unsigned sz1 = 0, sz2 = 0; // values are always smaller! @@ -186,14 +186,14 @@ class max_var_id_proc { unsigned get_max() { return m_max_var_id; } }; -unsigned ufbv_rewriter::max_var_id(expr * e) +unsigned demodulator_rewriter::max_var_id(expr * e) { max_var_id_proc proc; for_each_expr(proc, e); return proc.get_max(); } -void ufbv_rewriter::insert_fwd_idx(expr * large, expr * small, quantifier * demodulator) { +void demodulator_rewriter::insert_fwd_idx(expr * large, expr * small, quantifier * demodulator) { SASSERT(large->get_kind() == AST_APP); SASSERT(demodulator); SASSERT(large && small); @@ -217,7 +217,7 @@ void ufbv_rewriter::insert_fwd_idx(expr * large, expr * small, quantifier * demo m_demodulator2lhs_rhs.insert(demodulator, expr_pair(large, small)); } -void ufbv_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { +void demodulator_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { TRACE("demodulator_fwd", tout << "REMOVE: " << std::hex << (size_t)demodulator << std::endl; ); fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); @@ -234,7 +234,7 @@ void ufbv_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { } } -bool ufbv_rewriter::check_fwd_idx_consistency() { +bool demodulator_rewriter::check_fwd_idx_consistency() { for (auto & kv : m_fwd_idx) { quantifier_set * set = kv.m_value; SASSERT(set); @@ -247,7 +247,7 @@ bool ufbv_rewriter::check_fwd_idx_consistency() { return true; } -void ufbv_rewriter::show_fwd_idx(std::ostream & out) { +void demodulator_rewriter::show_fwd_idx(std::ostream & out) { for (auto & kv : m_fwd_idx) { quantifier_set * set = kv.m_value; SASSERT(!set); @@ -265,7 +265,7 @@ void ufbv_rewriter::show_fwd_idx(std::ostream & out) { } } -bool ufbv_rewriter::rewrite1(func_decl * f, expr_ref_vector & m_new_args, expr_ref & np) { +bool demodulator_rewriter::rewrite1(func_decl * f, expr_ref_vector & m_new_args, expr_ref & np) { fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); if (it != m_fwd_idx.end()) { TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n"; @@ -294,7 +294,7 @@ bool ufbv_rewriter::rewrite1(func_decl * f, expr_ref_vector & m_new_args, expr_r return false; } -bool ufbv_rewriter::rewrite_visit_children(app * a) { +bool demodulator_rewriter::rewrite_visit_children(app * a) { bool res=true; unsigned j = a->get_num_args(); while (j > 0) { @@ -327,11 +327,11 @@ bool ufbv_rewriter::rewrite_visit_children(app * a) { return res; } -void ufbv_rewriter::rewrite_cache(expr * e, expr * new_e, bool done) { +void demodulator_rewriter::rewrite_cache(expr * e, expr * new_e, bool done) { m_rewrite_cache.insert(e, expr_bool_pair(new_e, done)); } -expr * ufbv_rewriter::rewrite(expr * n) { +expr * demodulator_rewriter::rewrite(expr * n) { if (m_fwd_idx.empty()) return n; @@ -441,7 +441,7 @@ expr * ufbv_rewriter::rewrite(expr * n) { return r; } -class ufbv_rewriter::add_back_idx_proc { +class demodulator_rewriter::add_back_idx_proc { back_idx_map & m_back_idx; expr * m_expr; public: @@ -467,7 +467,7 @@ class ufbv_rewriter::add_back_idx_proc { } }; -class ufbv_rewriter::remove_back_idx_proc { +class demodulator_rewriter::remove_back_idx_proc { back_idx_map & m_back_idx; expr * m_expr; public: @@ -488,7 +488,7 @@ class ufbv_rewriter::remove_back_idx_proc { } }; -void ufbv_rewriter::reschedule_processed(func_decl * f) { +void demodulator_rewriter::reschedule_processed(func_decl * f) { //use m_back_idx to find all formulas p in m_processed that contains f { back_idx_map::iterator it = m_back_idx.find_iterator(f); if (it != m_back_idx.end()) { @@ -511,7 +511,7 @@ void ufbv_rewriter::reschedule_processed(func_decl * f) { } } -bool ufbv_rewriter::can_rewrite(expr * n, expr * lhs) { +bool demodulator_rewriter::can_rewrite(expr * n, expr * lhs) { // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. // we cannot use the trick used for m_processed, since the main loop would not terminate. @@ -568,7 +568,7 @@ bool ufbv_rewriter::can_rewrite(expr * n, expr * lhs) { return false; } -void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { +void demodulator_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // use m_back_idx to find all demodulators d in m_fwd_idx that contains f { //ptr_vector to_remove; @@ -616,7 +616,7 @@ void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { } } -void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * prs, +void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { if (m.proofs_enabled()) { TRACE("tactic", tout << "PRE_DEMODULATOR=true is not supported when proofs are enabled.";); @@ -710,7 +710,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * } -ufbv_rewriter::match_subst::match_subst(ast_manager & m): +demodulator_rewriter::match_subst::match_subst(ast_manager & m): m(m), m_subst(m) { } @@ -742,7 +742,7 @@ struct match_args_aux_proc { void operator()(app * n) {} }; -bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { +bool demodulator_rewriter::match_subst::match_args(app * lhs, expr * const * args) { m_cache.reset(); m_todo.reset(); @@ -858,7 +858,7 @@ bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { } -bool ufbv_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) { +bool demodulator_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) { if (match_args(lhs, args)) { if (m_all_args_eq) { @@ -873,7 +873,7 @@ bool ufbv_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * const return false; } -bool ufbv_rewriter::match_subst::operator()(expr * t, expr * i) { +bool demodulator_rewriter::match_subst::operator()(expr * t, expr * i) { m_cache.reset(); m_todo.reset(); if (is_var(t)) diff --git a/src/tactic/ufbv/ufbv_rewriter.h b/src/ast/rewriter/demodulator_rewriter.h similarity index 97% rename from src/tactic/ufbv/ufbv_rewriter.h rename to src/ast/rewriter/demodulator_rewriter.h index d855047a0ed..a60debabf69 100644 --- a/src/tactic/ufbv/ufbv_rewriter.h +++ b/src/ast/rewriter/demodulator_rewriter.h @@ -3,7 +3,7 @@ Copyright (c) 2006 Microsoft Corporation Module Name: - demodulator.h + demodulator_rewriter.h Abstract: @@ -16,6 +16,7 @@ Module Name: Revision History: Christoph M. Wintersteiger (cwinter) 2012-10-24: Moved from demodulator.h to ufbv_rewriter.h + Nikolaj Bjorner (nbjorner) 2022-12-4: Moved to rewriter and renamed to demodulator_rewriter.h --*/ #pragma once @@ -91,7 +92,7 @@ The code in spc_rewriter.* does something like that. We cannot reuse this code d for the superposion engine in Z3, but we can adapt it for our needs in the preprocessor. */ -class ufbv_rewriter final { +class demodulator_rewriter final { class rewrite_proc; class add_back_idx_proc; class remove_back_idx_proc; @@ -193,8 +194,8 @@ class ufbv_rewriter final { int is_subset(expr * e1, expr * e2) const; public: - ufbv_rewriter(ast_manager & m); - ~ufbv_rewriter(); + demodulator_rewriter(ast_manager & m); + ~demodulator_rewriter(); void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); diff --git a/src/tactic/ufbv/CMakeLists.txt b/src/tactic/ufbv/CMakeLists.txt index 511dc2b2d0d..2c2567b91f5 100644 --- a/src/tactic/ufbv/CMakeLists.txt +++ b/src/tactic/ufbv/CMakeLists.txt @@ -2,7 +2,6 @@ z3_add_component(ufbv_tactic SOURCES macro_finder_tactic.cpp quasi_macros_tactic.cpp - ufbv_rewriter.cpp ufbv_rewriter_tactic.cpp ufbv_tactic.cpp COMPONENT_DEPENDENCIES diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp index e254523c04f..46958982abc 100644 --- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp +++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp @@ -17,21 +17,21 @@ Module Name: --*/ #include "tactic/tactical.h" -#include "tactic/ufbv/ufbv_rewriter.h" +#include "ast/rewriter/demodulator_rewriter.h" #include "tactic/ufbv/ufbv_rewriter_tactic.h" -class ufbv_rewriter_tactic : public tactic { +class demodulator_rewriter_tactic : public tactic { ast_manager & m_manager; params_ref m_params; public: - ufbv_rewriter_tactic(ast_manager & m, params_ref const & p): + demodulator_rewriter_tactic(ast_manager & m, params_ref const & p): m_manager(m), m_params(p) {} char const* name() const override { return "ufbv"; } tactic * translate(ast_manager & m) override { - return alloc(ufbv_rewriter_tactic, m, m_params); + return alloc(demodulator_rewriter_tactic, m, m_params); } void updt_params(params_ref const & p) override { @@ -50,7 +50,7 @@ class ufbv_rewriter_tactic : public tactic { bool produce_proofs = g->proofs_enabled(); - ufbv_rewriter dem(m_manager); + demodulator_rewriter dem(m_manager); expr_ref_vector forms(m_manager), new_forms(m_manager); proof_ref_vector proofs(m_manager), new_proofs(m_manager); @@ -79,5 +79,5 @@ class ufbv_rewriter_tactic : public tactic { }; tactic * mk_ufbv_rewriter_tactic(ast_manager & m, params_ref const & p) { - return alloc(ufbv_rewriter_tactic, m, p); + return alloc(demodulator_rewriter_tactic, m, p); } From d218083145db789b709c57d583b8b48f17267ab6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 04:48:48 -0800 Subject: [PATCH 164/597] The demodulator doesn't produce proofs so remove code path that depends it does. --- src/ast/rewriter/demodulator_rewriter.cpp | 11 ++--------- src/ast/rewriter/demodulator_rewriter.h | 2 +- src/tactic/ufbv/ufbv_rewriter_tactic.cpp | 10 ++++++---- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/ast/rewriter/demodulator_rewriter.cpp b/src/ast/rewriter/demodulator_rewriter.cpp index 8a691192506..56be9952903 100644 --- a/src/ast/rewriter/demodulator_rewriter.cpp +++ b/src/ast/rewriter/demodulator_rewriter.cpp @@ -616,15 +616,8 @@ void demodulator_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { } } -void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * prs, - expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { - if (m.proofs_enabled()) { - TRACE("tactic", tout << "PRE_DEMODULATOR=true is not supported when proofs are enabled.";); - // Let us not waste time with proof production - new_exprs.append(n, exprs); - new_prs.append(n, prs); - return; - } +void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, + expr_ref_vector & new_exprs) { TRACE("demodulator", tout << "before demodulator:\n"; for ( unsigned i = 0 ; i < n ; i++ ) diff --git a/src/ast/rewriter/demodulator_rewriter.h b/src/ast/rewriter/demodulator_rewriter.h index a60debabf69..90d15bcec7d 100644 --- a/src/ast/rewriter/demodulator_rewriter.h +++ b/src/ast/rewriter/demodulator_rewriter.h @@ -197,7 +197,7 @@ class demodulator_rewriter final { demodulator_rewriter(ast_manager & m); ~demodulator_rewriter(); - void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); + void operator()(unsigned n, expr * const * exprs, expr_ref_vector & new_exprs); /** Given a demodulator (aka rewrite rule) of the form diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp index 46958982abc..f8f3153c527 100644 --- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp +++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp @@ -41,14 +41,16 @@ class demodulator_rewriter_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); - insert_produce_proofs(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("ufbv-rewriter", *g); fail_if_unsat_core_generation("ufbv-rewriter", g); - bool produce_proofs = g->proofs_enabled(); + if (g->proofs_enabled()) { + result.push_back(g.get()); + return; + } demodulator_rewriter dem(m_manager); @@ -61,11 +63,11 @@ class demodulator_rewriter_tactic : public tactic { proofs.push_back(g->pr(i)); } - dem(forms.size(), forms.data(), proofs.data(), new_forms, new_proofs); + dem(forms.size(), forms.data(), new_forms); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) - g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : nullptr, nullptr); + g->assert_expr(new_forms.get(i), nullptr, nullptr); // CMW: Remark: The demodulator could potentially // remove all references to a variable. From 3d7bd40a87cee19d8b1135b3352a86b91f006a98 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 06:07:45 -0800 Subject: [PATCH 165/597] a round of cleanup --- src/ast/rewriter/demodulator_rewriter.cpp | 321 ++++++++++------------ src/ast/rewriter/demodulator_rewriter.h | 13 +- src/tactic/ufbv/ufbv_rewriter_tactic.cpp | 7 +- 3 files changed, 151 insertions(+), 190 deletions(-) diff --git a/src/ast/rewriter/demodulator_rewriter.cpp b/src/ast/rewriter/demodulator_rewriter.cpp index 56be9952903..f1fc6f969e9 100644 --- a/src/ast/rewriter/demodulator_rewriter.cpp +++ b/src/ast/rewriter/demodulator_rewriter.cpp @@ -3,7 +3,7 @@ Copyright (c) 2006 Microsoft Corporation Module Name: - demodulator.cpp + demodulator_rewriter.cpp Abstract: @@ -17,6 +17,7 @@ Revision History: Christoph M. Wintersteiger (cwinter) 2010-04-21: Implementation Christoph M. Wintersteiger (cwinter) 2012-10-24: Moved from demodulator.h to ufbv_rewriter.h + Nikolaj Bjorner (nbjorner) 2022-12-4: Moved to demodulator_rewriter.h --*/ @@ -186,48 +187,45 @@ class max_var_id_proc { unsigned get_max() { return m_max_var_id; } }; -unsigned demodulator_rewriter::max_var_id(expr * e) -{ +unsigned demodulator_rewriter::max_var_id(expr_ref_vector const& es) { max_var_id_proc proc; - for_each_expr(proc, e); + for (expr* e : es) + for_each_expr(proc, e); return proc.get_max(); } -void demodulator_rewriter::insert_fwd_idx(expr * large, expr * small, quantifier * demodulator) { - SASSERT(large->get_kind() == AST_APP); +void demodulator_rewriter::insert_fwd_idx(app * large, expr * small, quantifier * demodulator) { SASSERT(demodulator); SASSERT(large && small); TRACE("demodulator_fwd", tout << "INSERT: " << mk_pp(demodulator, m) << std::endl; ); func_decl * fd = to_app(large)->get_decl(); - fwd_idx_map::iterator it = m_fwd_idx.find_iterator(fd); - if (it == m_fwd_idx.end()) { - quantifier_set * qs = alloc(quantifier_set, 1); + quantifier_set * qs; + if (!m_fwd_idx.find(fd, qs)) { + qs = alloc(quantifier_set, 1); m_fwd_idx.insert(fd, qs); - it = m_fwd_idx.find_iterator(fd); } - SASSERT(it->m_value); - it->m_value->insert(demodulator); + SASSERT(qs); + qs->insert(demodulator); m.inc_ref(demodulator); m.inc_ref(large); m.inc_ref(small); - m_demodulator2lhs_rhs.insert(demodulator, expr_pair(large, small)); + m_demodulator2lhs_rhs.insert(demodulator, app_expr_pair(large, small)); } void demodulator_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { TRACE("demodulator_fwd", tout << "REMOVE: " << std::hex << (size_t)demodulator << std::endl; ); - fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); - if (it != m_fwd_idx.end()) { - demodulator2lhs_rhs::iterator fit = m_demodulator2lhs_rhs.find_iterator(demodulator); - expr_pair p = fit->m_value; + quantifier_set* qs; + if (m_fwd_idx.find(f, qs)) { + auto [lhs, rhs] = m_demodulator2lhs_rhs[demodulator]; m_demodulator2lhs_rhs.erase(demodulator); - it->m_value->erase(demodulator); - m.dec_ref(p.first); - m.dec_ref(p.second); + qs->erase(demodulator); + m.dec_ref(lhs); + m.dec_ref(rhs); m.dec_ref(demodulator); } else { SASSERT(m_demodulator2lhs_rhs.contains(demodulator)); @@ -235,59 +233,50 @@ void demodulator_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulato } bool demodulator_rewriter::check_fwd_idx_consistency() { - for (auto & kv : m_fwd_idx) { - quantifier_set * set = kv.m_value; + for (auto & [k, set] : m_fwd_idx) { SASSERT(set); - for (auto e : *set) { + for (auto e : *set) if (!m_demodulator2lhs_rhs.contains(e)) return false; - } } - return true; } void demodulator_rewriter::show_fwd_idx(std::ostream & out) { - for (auto & kv : m_fwd_idx) { - quantifier_set * set = kv.m_value; - SASSERT(!set); - - out << kv.m_key->get_name() << ": " << std::endl; - - for (auto e : *set) { - out << std::hex << (size_t)e << std::endl; - } + for (auto & [k, set] : m_fwd_idx) { + out << k->get_name() << ": " << std::endl; + if (set) + for (auto e : *set) + out << std::hex << (size_t)e << std::endl; } out << "D2LR: " << std::endl; - for (auto & kv : m_demodulator2lhs_rhs) { - out << (size_t) kv.m_key << std::endl; + for (auto & [k, v] : m_demodulator2lhs_rhs) { + out << (size_t) k << std::endl; } } -bool demodulator_rewriter::rewrite1(func_decl * f, expr_ref_vector & m_new_args, expr_ref & np) { - fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); - if (it != m_fwd_idx.end()) { - TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n"; - tout << m_new_args << "\n";); - for (quantifier* d : *it->m_value) { - - SASSERT(m_demodulator2lhs_rhs.contains(d)); - expr_pair l_s; - m_demodulator2lhs_rhs.find(d, l_s); - app * large = to_app(l_s.first); - - if (large->get_num_args() != m_new_args.size()) - continue; - - TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m) << std::endl; ); +bool demodulator_rewriter::rewrite1(func_decl * f, expr_ref_vector const & args, expr_ref & np) { + quantifier_set* set; + if (!m_fwd_idx.find(f, set)) + return false; + TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n"; + tout << m_new_args << "\n";); - SASSERT(large->get_decl() == f); + for (quantifier* d : *set) { - if (m_match_subst(large, l_s.second, m_new_args.data(), np)) { - TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(l_s.second, m) << "\n===>\n" << mk_pp(np, m) << "\n";); - return true; - } + auto const& [large, rhs] = m_demodulator2lhs_rhs[d]; + + if (large->get_num_args() != args.size()) + continue; + + TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m) << std::endl; ); + + SASSERT(large->get_decl() == f); + + if (m_match_subst(large, rhs, args.data(), np)) { + TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(rhs, m) << "\n===>\n" << mk_pp(np, m) << "\n";); + return true; } } @@ -449,21 +438,19 @@ class demodulator_rewriter::add_back_idx_proc { void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { - // We track only uninterpreted and constant functions. - if (n->get_num_args()==0) return; + // We track only uninterpreted functions. + if (n->get_num_args() == 0) + return; SASSERT(m_expr && m_expr != (expr*) 0x00000003); - func_decl * d=n->get_decl(); - if (d->get_family_id() == null_family_id) { - back_idx_map::iterator it = m_back_idx.find_iterator(d); - if (it != m_back_idx.end()) { - SASSERT(it->m_value); - it->m_value->insert(m_expr); - } else { - expr_set * e = alloc(expr_set); - e->insert(m_expr); - m_back_idx.insert(d, e); - } + func_decl * d = n->get_decl(); + if (d->get_family_id() != null_family_id) + return; + expr_set* set = nullptr; + if (!m_back_idx.find(d, set)) { + set = alloc(expr_set); + m_back_idx.insert(d, set); } + set->insert(m_expr); } }; @@ -475,39 +462,48 @@ class demodulator_rewriter::remove_back_idx_proc { void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { - // We track only uninterpreted and constant functions. - if (n->get_num_args()==0) return; - func_decl * d=n->get_decl(); - if (d->get_family_id() == null_family_id) { - back_idx_map::iterator it = m_back_idx.find_iterator(d); - if (it != m_back_idx.end()) { - SASSERT(it->m_value); - it->m_value->remove(m_expr); - } - } + // We track only uninterpreted functions. + if (n->get_num_args() == 0) + return; + func_decl * d = n->get_decl(); + if (d->get_family_id() != null_family_id) + return; + expr_set* set = nullptr; + if (m_back_idx.find(d, set)) + set->remove(m_expr); } }; + +void demodulator_rewriter::insert_bwd_idx(expr* e) { + add_back_idx_proc proc(m_back_idx, e); + for_each_expr(proc, e); +} + +void demodulator_rewriter::remove_bwd_idx(expr* e) { + remove_back_idx_proc proc(m_back_idx, e); + for_each_expr(proc, e); +} + void demodulator_rewriter::reschedule_processed(func_decl * f) { //use m_back_idx to find all formulas p in m_processed that contains f { - back_idx_map::iterator it = m_back_idx.find_iterator(f); - if (it != m_back_idx.end()) { - SASSERT(it->m_value); - expr_set temp; - - for (expr* p : *it->m_value) { - if (m_processed.contains(p)) + expr_set* set = nullptr; + if (!m_back_idx.find(f, set)) + return; + SASSERT(set); + expr_set temp; + + for (expr* p : *set) + if (m_processed.contains(p)) temp.insert(p); - } - for (expr * p : temp) { - // remove p from m_processed and m_back_idx - m_processed.remove(p); - remove_back_idx_proc proc(m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. - for_each_expr(proc, p); - // insert p into m_todo - m_todo.push_back(p); - } + for (expr * p : temp) { + // remove p from m_processed and m_back_idx + m_processed.remove(p); + // this could change `set', thus we need the `temp' set. + remove_bwd_idx(p); + // insert p into m_todo + m_todo.push_back(p); } } @@ -545,20 +541,10 @@ bool demodulator_rewriter::can_rewrite(expr * n, expr * lhs) { break; case AST_QUANTIFIER: - if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_patterns(), - to_quantifier(curr)->get_patterns())) { - break; - } - if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(), - to_quantifier(curr)->get_no_patterns())) { - break; - } - if (!visited.is_marked(to_quantifier(curr)->get_expr())) { + if (visited.is_marked(to_quantifier(curr)->get_expr())) + stack.pop_back(); + else stack.push_back(to_quantifier(curr)->get_expr()); - break; - } - - stack.pop_back(); break; default: UNREACHABLE(); @@ -571,66 +557,55 @@ bool demodulator_rewriter::can_rewrite(expr * n, expr * lhs) { void demodulator_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // use m_back_idx to find all demodulators d in m_fwd_idx that contains f { - //ptr_vector to_remove; - back_idx_map::iterator it = m_back_idx.find_iterator(f); - if (it != m_back_idx.end()) { - SASSERT(it->m_value); - expr_set all_occurrences; - expr_ref l(m); + expr_set* set = nullptr; + if (!m_back_idx.find(f, set)) + return; + SASSERT(set); + expr_set all_occurrences; + app_ref l(m); - for (auto s : *it->m_value) - all_occurrences.insert(s); + for (auto s : *set) + all_occurrences.insert(s); + + // Run over all f-demodulators + for (expr* occ : all_occurrences) { + + if (!is_quantifier(occ)) + continue; + quantifier* qe = to_quantifier(occ); + + // Use the fwd idx to find out whether this is a demodulator. + app_expr_pair p; + if (!m_demodulator2lhs_rhs.find(qe, p)) + continue; - // Run over all f-demodulators - for (expr* occ : all_occurrences) { + l = p.first; + quantifier_ref d(qe, m); + func_decl_ref df(l->get_decl(), m); + + // Now we know there is an occurrence of f in d + if (!can_rewrite(d, lhs)) + continue; - if (!is_quantifier(occ)) - continue; + TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m) << std::endl); - // Use the fwd idx to find out whether this is a demodulator. - demodulator2lhs_rhs::iterator d2lr_it = m_demodulator2lhs_rhs.find_iterator(to_quantifier(occ)); - if (d2lr_it != m_demodulator2lhs_rhs.end()) { - l = d2lr_it->m_value.first; - quantifier_ref d(m); - func_decl_ref df(m); - d = to_quantifier(occ); - df = to_app(l)->get_decl(); - - // Now we know there is an occurrence of f in d - // if n' can rewrite d { - if (can_rewrite(d, lhs)) { - TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m) << std::endl; ); - // remove d from m_fwd_idx - remove_fwd_idx(df, d); - // remove d from m_back_idx - // just remember it here, because otherwise it and/or esit might become invalid? - // to_remove.insert(d); - remove_back_idx_proc proc(m_back_idx, d); - for_each_expr(proc, d); - // insert d into m_todo - m_todo.push_back(d); - } - } - } + remove_fwd_idx(df, d); + remove_bwd_idx(d); + m_todo.push_back(d); } } -void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, - expr_ref_vector & new_exprs) { +void demodulator_rewriter::operator()(expr_ref_vector const& exprs, + expr_ref_vector & new_exprs) { - TRACE("demodulator", tout << "before demodulator:\n"; - for ( unsigned i = 0 ; i < n ; i++ ) - tout << mk_pp(exprs[i], m) << std::endl; ); + TRACE("demodulator", tout << "before demodulator:\n" << exprs); // Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty. - unsigned max_vid = 0; - for ( unsigned i = 0 ; i < n ; i++ ) { - m_todo.push_back(exprs[i]); - max_vid = std::max(max_vid, max_var_id(exprs[i])); - } + for (expr* e : exprs) + m_todo.push_back(e); - m_match_subst.reserve(max_vid); + m_match_subst.reserve(max_var_id(exprs)); while (!m_todo.empty()) { // let n be the next formula in m_todo. @@ -644,7 +619,6 @@ void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, // unless there is a demodulator cycle // SASSERT(rewrite(np)==np); - // if (n' is not a demodulator) { app_ref large(m); expr_ref small(m); if (!is_demodulator(np, large, small)) { @@ -652,22 +626,11 @@ void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, m_processed.insert(np); m_in_processed.push_back(np); // update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) - add_back_idx_proc proc(m_back_idx, np); - for_each_expr(proc, np); - } else { + insert_bwd_idx(np); + } + else { // np is a demodulator that allows us to replace 'large' with 'small'. - TRACE("demodulator", tout << "Found demodulator: " << std::endl; - tout << mk_pp(large.get(), m) << std::endl << " ---> " << - std::endl << mk_pp(small.get(), m) << std::endl; ); - - TRACE("demodulator_s", tout << "Found demodulator: " << std::endl; - tout << to_app(large)->get_decl()->get_name() << - "[" << to_app(large)->get_depth() << "]" << " ---> "; - if (is_app(small)) - tout << to_app(small)->get_decl()->get_name() << - "[" << to_app(small)->get_depth() << "]" << std::endl; - else - tout << mk_pp(small.get(), m) << std::endl; ); + TRACE("demodulator", tout << "Found demodulator:\n" << large << "\n ---> " << small << "\n"); // let f be the top symbol of n' func_decl * f = large->get_decl(); @@ -679,8 +642,7 @@ void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, insert_fwd_idx(large, small, to_quantifier(np)); // update m_back_idx - add_back_idx_proc proc(m_back_idx, np); - for_each_expr(proc, np); + insert_bwd_idx(np); } } @@ -690,12 +652,11 @@ void demodulator_rewriter::operator()(unsigned n, expr * const * exprs, TRACE("demodulator", tout << mk_pp(e, m) << std::endl; ); } - for (auto const& kv : m_fwd_idx) { - if (kv.m_value) { - for (expr* e : *kv.m_value) { + for (auto const& [k, set] : m_fwd_idx) { + if (set) { + for (expr* e : *set) new_exprs.push_back(e); - TRACE("demodulator", tout << mk_pp(e, m) << std::endl; ); - } + TRACE("demodulator", for (expr* e : *set) tout << mk_pp(e, m) << std::endl; ); } } diff --git a/src/ast/rewriter/demodulator_rewriter.h b/src/ast/rewriter/demodulator_rewriter.h index 90d15bcec7d..2152520ce58 100644 --- a/src/ast/rewriter/demodulator_rewriter.h +++ b/src/ast/rewriter/demodulator_rewriter.h @@ -111,11 +111,12 @@ class demodulator_rewriter final { typedef array_map expr_map; typedef std::pair expr_pair; + typedef std::pair app_expr_pair; typedef obj_hashtable expr_set; typedef obj_map back_idx_map; typedef obj_hashtable quantifier_set; typedef obj_map fwd_idx_map; - typedef obj_map demodulator2lhs_rhs; + typedef obj_map demodulator2lhs_rhs; typedef expr_map rewrite_cache_map; /** @@ -172,20 +173,22 @@ class demodulator_rewriter final { rewrite_cache_map m_rewrite_cache; expr_ref_buffer m_new_exprs; - void insert_fwd_idx(expr * large, expr * small, quantifier * demodulator); + void insert_fwd_idx(app * large, expr * small, quantifier * demodulator); void remove_fwd_idx(func_decl * f, quantifier * demodulator); + void insert_bwd_idx(expr* q); + void remove_bwd_idx(expr* q); bool check_fwd_idx_consistency(); void show_fwd_idx(std::ostream & out); bool is_demodulator(expr * e, app_ref & large, expr_ref & small) const; bool can_rewrite(expr * n, expr * lhs); expr * rewrite(expr * n); - bool rewrite1(func_decl * f, expr_ref_vector & m_new_args, expr_ref & np); + bool rewrite1(func_decl * f, expr_ref_vector const & args, expr_ref & np); bool rewrite_visit_children(app * a); void rewrite_cache(expr * e, expr * new_e, bool done); void reschedule_processed(func_decl * f); void reschedule_demodulators(func_decl * f, expr * np); - unsigned max_var_id(expr * e); + unsigned max_var_id(expr_ref_vector const& es); // is_smaller returns -1 for e1e2. int is_smaller(expr * e1, expr * e2) const; @@ -197,7 +200,7 @@ class demodulator_rewriter final { demodulator_rewriter(ast_manager & m); ~demodulator_rewriter(); - void operator()(unsigned n, expr * const * exprs, expr_ref_vector & new_exprs); + void operator()(expr_ref_vector const& exprs, expr_ref_vector & new_exprs); /** Given a demodulator (aka rewrite rule) of the form diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp index f8f3153c527..f374ea11426 100644 --- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp +++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp @@ -55,15 +55,12 @@ class demodulator_rewriter_tactic : public tactic { demodulator_rewriter dem(m_manager); expr_ref_vector forms(m_manager), new_forms(m_manager); - proof_ref_vector proofs(m_manager), new_proofs(m_manager); unsigned size = g->size(); - for (unsigned i = 0; i < size; i++) { + for (unsigned i = 0; i < size; i++) forms.push_back(g->form(i)); - proofs.push_back(g->pr(i)); - } - dem(forms.size(), forms.data(), new_forms); + dem(forms, new_forms); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) From 9acbfa392361e785365498cfe18e3276d8fa10fc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 06:23:32 -0800 Subject: [PATCH 166/597] move it into substitution to handle dependencies --- src/ast/rewriter/CMakeLists.txt | 1 - src/ast/substitution/CMakeLists.txt | 1 + .../demodulator_rewriter.cpp | 51 +++++++++---------- .../demodulator_rewriter.h | 0 src/tactic/ufbv/ufbv_rewriter_tactic.cpp | 8 +-- 5 files changed, 29 insertions(+), 32 deletions(-) rename src/ast/{rewriter => substitution}/demodulator_rewriter.cpp (95%) rename src/ast/{rewriter => substitution}/demodulator_rewriter.h (100%) diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index 9eef651c84d..c785804c127 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -11,7 +11,6 @@ z3_add_component(rewriter cached_var_subst.cpp char_rewriter.cpp datatype_rewriter.cpp - demodulator_rewriter.cpp der.cpp distribute_forall.cpp dl_rewriter.cpp diff --git a/src/ast/substitution/CMakeLists.txt b/src/ast/substitution/CMakeLists.txt index 80e12c9953b..8dbf2c9e3de 100644 --- a/src/ast/substitution/CMakeLists.txt +++ b/src/ast/substitution/CMakeLists.txt @@ -1,5 +1,6 @@ z3_add_component(substitution SOURCES + demodulator_rewriter.cpp matcher.cpp substitution.cpp substitution_tree.cpp diff --git a/src/ast/rewriter/demodulator_rewriter.cpp b/src/ast/substitution/demodulator_rewriter.cpp similarity index 95% rename from src/ast/rewriter/demodulator_rewriter.cpp rename to src/ast/substitution/demodulator_rewriter.cpp index f1fc6f969e9..6e79242e0a5 100644 --- a/src/ast/rewriter/demodulator_rewriter.cpp +++ b/src/ast/substitution/demodulator_rewriter.cpp @@ -25,7 +25,7 @@ Revision History: #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/rewriter/var_subst.h" -#include "ast/rewriter/demodulator_rewriter.h" +#include "ast/substitution/demodulator_rewriter.h" demodulator_rewriter::demodulator_rewriter(ast_manager & m): m(m), @@ -284,34 +284,31 @@ bool demodulator_rewriter::rewrite1(func_decl * f, expr_ref_vector const & args, } bool demodulator_rewriter::rewrite_visit_children(app * a) { - bool res=true; - unsigned j = a->get_num_args(); - while (j > 0) { - expr * e = a->get_arg(--j); - if (!m_rewrite_cache.contains(e) || !m_rewrite_cache.get(e).second) { - bool recursive = false; - unsigned sz = m_rewrite_todo.size(); - expr * v = e; - if (m_rewrite_cache.contains(e)) { - expr_bool_pair const & ebp = m_rewrite_cache.get(e); - if (ebp.second) { - v = ebp.first; - } - } - for (unsigned i = sz; i-- > 0;) { - if (m_rewrite_todo[i] == v) { - recursive = true; - TRACE("demodulator", tout << "Detected demodulator cycle: " << - mk_pp(a, m) << " --> " << mk_pp(v, m) << std::endl;); - rewrite_cache(e, v, true); - break; - } - } - if (!recursive) { - m_rewrite_todo.push_back(e); - res = false; + bool res = true; + for (expr* e : *a) { + if (m_rewrite_cache.contains(e) && m_rewrite_cache.get(e).second) + continue; + bool recursive = false; + unsigned sz = m_rewrite_todo.size(); + expr * v = e; + if (m_rewrite_cache.contains(e)) { + expr_bool_pair const & ebp = m_rewrite_cache.get(e); + if (ebp.second) + v = ebp.first; + } + for (unsigned i = sz; i-- > 0;) { + if (m_rewrite_todo[i] == v) { + recursive = true; + TRACE("demodulator", tout << "Detected demodulator cycle: " << + mk_pp(a, m) << " --> " << mk_pp(v, m) << std::endl;); + rewrite_cache(e, v, true); + break; } } + if (!recursive) { + m_rewrite_todo.push_back(e); + res = false; + } } return res; } diff --git a/src/ast/rewriter/demodulator_rewriter.h b/src/ast/substitution/demodulator_rewriter.h similarity index 100% rename from src/ast/rewriter/demodulator_rewriter.h rename to src/ast/substitution/demodulator_rewriter.h diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp index f374ea11426..66d37749106 100644 --- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp +++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp @@ -17,7 +17,7 @@ Module Name: --*/ #include "tactic/tactical.h" -#include "ast/rewriter/demodulator_rewriter.h" +#include "ast/substitution/demodulator_rewriter.h" #include "tactic/ufbv/ufbv_rewriter_tactic.h" class demodulator_rewriter_tactic : public tactic { @@ -28,7 +28,7 @@ class demodulator_rewriter_tactic : public tactic { demodulator_rewriter_tactic(ast_manager & m, params_ref const & p): m_manager(m), m_params(p) {} - char const* name() const override { return "ufbv"; } + char const* name() const override { return "ufbv-rewriter"; } tactic * translate(ast_manager & m) override { return alloc(demodulator_rewriter_tactic, m, m_params); @@ -63,8 +63,8 @@ class demodulator_rewriter_tactic : public tactic { dem(forms, new_forms); g->reset(); - for (unsigned i = 0; i < new_forms.size(); i++) - g->assert_expr(new_forms.get(i), nullptr, nullptr); + for (expr* fml : new_forms) + g->assert_expr(fml, nullptr, nullptr); // CMW: Remark: The demodulator could potentially // remove all references to a variable. From 1974c224ab74fb7957afba04fc3f13649a8929e8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 09:39:28 -0800 Subject: [PATCH 167/597] add demodulator simplifier refactor demodulator-rewriter a bit to separate reusable features. --- scripts/mk_project.py | 2 +- src/CMakeLists.txt | 2 +- src/ast/rewriter/th_rewriter.h | 1 + src/ast/simplifiers/CMakeLists.txt | 2 + .../simplifiers/demodulator_simplifier.cpp | 199 ++++++++++ src/ast/simplifiers/demodulator_simplifier.h | 60 +++ src/ast/substitution/demodulator_rewriter.cpp | 371 +++++++++++++----- src/ast/substitution/demodulator_rewriter.h | 136 ++++--- 8 files changed, 621 insertions(+), 152 deletions(-) create mode 100644 src/ast/simplifiers/demodulator_simplifier.cpp create mode 100644 src/ast/simplifiers/demodulator_simplifier.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index a979359f303..a16913317f3 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -39,7 +39,7 @@ def init_project_def(): add_lib('macros', ['rewriter'], 'ast/macros') add_lib('model', ['macros']) add_lib('converters', ['model'], 'ast/converters') - add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'converters'], 'ast/simplifiers') + add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'converters', 'substitution'], 'ast/simplifiers') add_lib('tactic', ['simplifiers']) add_lib('solver', ['params', 'model', 'tactic', 'proofs']) add_lib('cmd_context', ['solver', 'rewriter', 'params']) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dadd70bba8e..652aef4ac51 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,9 +52,9 @@ add_subdirectory(ast/macros) add_subdirectory(model) add_subdirectory(ast/euf) add_subdirectory(ast/converters) +add_subdirectory(ast/substitution) add_subdirectory(ast/simplifiers) add_subdirectory(tactic) -add_subdirectory(ast/substitution) add_subdirectory(smt/params) add_subdirectory(parsers/util) add_subdirectory(math/grobner) diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index a3f0037992e..71c39b18e14 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -52,6 +52,7 @@ class th_rewriter { 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()); } + expr_ref mk_app(func_decl* f, expr_ref_vector const& args) { return mk_app(f, args.size(), args.data()); } expr_ref mk_eq(expr* a, expr* b); bool reduce_quantifier(quantifier * old_q, diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index c6a8469ee99..df44427cfa9 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(simplifiers bit_blaster.cpp bv_slice.cpp card2bv.cpp + demodulator_simplifier.cpp dependent_expr_state.cpp elim_unconstrained.cpp eliminate_predicates.cpp @@ -18,4 +19,5 @@ z3_add_component(simplifiers rewriter bit_blaster normal_forms + substitution ) diff --git a/src/ast/simplifiers/demodulator_simplifier.cpp b/src/ast/simplifiers/demodulator_simplifier.cpp new file mode 100644 index 00000000000..eb358560644 --- /dev/null +++ b/src/ast/simplifiers/demodulator_simplifier.cpp @@ -0,0 +1,199 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + demodulator_simplifier.cpp + +Author: + + Nikolaj Bjorner (nbjorner) 2022-12-4 + +--*/ + +#include "ast/simplifiers/demodulator_simplifier.h" + +demodulator_index::~demodulator_index() { + reset(); +} + +void demodulator_index::reset() { + for (auto& [k, v] : m_fwd_index) + dealloc(v); + for (auto& [k, v] : m_bwd_index) + dealloc(v); + m_fwd_index.reset(); + m_bwd_index.reset(); +} + +void demodulator_index::add(func_decl* f, unsigned i, obj_map& map) { + uint_set* s; + if (!map.find(f, s)) { + s = alloc(uint_set); + map.insert(f, s); + } + s->insert(i); +} + +void demodulator_index::del(func_decl* f, unsigned i, obj_map& map) { + uint_set* s; + if (map.find(f, s)) + s->remove(i); +} + +void demodulator_index::insert_bwd(expr* e, unsigned i) { + struct proc { + unsigned i; + demodulator_index& idx; + proc(unsigned i, demodulator_index& idx) :i(i), idx(idx) {} + void operator()(app* a) { + if (a->get_num_args() > 0 && is_uninterp(a)) + idx.add(a->get_decl(), i, idx.m_bwd_index); + } + void operator()(expr* e) {} + }; + proc p(i, *this); + for_each_expr(p, e); +} + +void demodulator_index::remove_bwd(expr* e, unsigned i) { + struct proc { + unsigned i; + demodulator_index& idx; + proc(unsigned i, demodulator_index& idx) :i(i), idx(idx) {} + void operator()(app* a) { + if (a->get_num_args() > 0 && is_uninterp(a)) + idx.del(a->get_decl(), i, idx.m_bwd_index); + } + void operator()(expr* e) {} + }; + proc p(i, *this); + for_each_expr(p, e); +} + +demodulator_simplifier::demodulator_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st): + dependent_expr_simplifier(m, st), + m_util(m), + m_match_subst(m), + m_rewriter(m), + m_pinned(m) +{ + std::function rw = [&](func_decl* f, expr_ref_vector const& args, expr_ref& r) { + return rewrite1(f, args, r); + }; + m_rewriter.set_rewrite1(rw); +} + +void demodulator_simplifier::rewrite(unsigned i) { + if (m_index.empty()) + return; + + m_dependencies.reset(); + expr* f = fml(i); + expr_ref r = m_rewriter.rewrite(f); + if (r == f) + return; + expr_dependency_ref d(dep(i), m); + for (unsigned j : m_dependencies) + d = m.mk_join(d, dep(j)); + m_fmls.update(i, dependent_expr(m, r, d)); +} + +bool demodulator_simplifier::rewrite1(func_decl* f, expr_ref_vector const& args, expr_ref& np) { + uint_set* set; + if (!m_index.find_fwd(f, set)) + return false; + + TRACE("demodulator", tout << "trying to rewrite: " << f->get_name() << " args:\n" << m_new_args << "\n";); + + for (unsigned i : *set) { + + auto const& [lhs, rhs] = m_rewrites[i]; + + if (lhs->get_num_args() != args.size()) + continue; + + SASSERT(lhs->get_decl() == f); + + TRACE("demodulator", tout << "Matching with demodulator: " << mk_pp(d, m) << std::endl; ); + + if (m_match_subst(lhs, rhs, args.data(), np)) { + TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(rhs, m) << "\n===>\n" << np << "\n";); + m_dependencies.insert(i); + return true; + } + } + + return false; +} + +void demodulator_simplifier::reschedule_processed(func_decl* f) { + uint_set* set = nullptr; + if (!m_index.find_bwd(f, set)) + return; + uint_set tmp; + for (auto i : *set) + if (m_processed.contains(i)) + tmp.insert(i); + for (auto i : tmp) { + m_processed.remove(i); + m_index.remove_fwd(f, i); + m_index.remove_bwd(fml(i), i); + m_todo.push_back(i); + } +} + +void demodulator_simplifier::reschedule_demodulators(func_decl* f, expr* lhs) { + uint_set* set; + if (!m_index.find_bwd(f, set)) + return; + uint_set all_occurrences(*set); + for (unsigned i : all_occurrences) { + app_expr_pair p; + if (!m_rewrites.find(i, p)) + continue; + if (!m_match_subst.can_rewrite(fml(i), lhs)) + continue; + func_decl* f = p.first->get_decl(); + m_index.remove_fwd(f, i); + m_index.remove_bwd(fml(i), i); + m_todo.push_back(i); + } +} + +void demodulator_simplifier::reset() { + m_pinned.reset(); + m_index.reset(); + m_processed.reset(); + m_todo.reset(); + unsigned max_vid = 1; + for (unsigned i : indices()) + max_vid = std::max(max_vid, m_util.max_var_id(fml(i))); + m_match_subst.reserve(max_vid); +} + +void demodulator_simplifier::reduce() { + reset(); + for (unsigned i : indices()) + m_todo.push_back(i); + + app_ref large(m); + expr_ref small(m); + while (!m_todo.empty()) { + unsigned i = m_todo.back(); + m_todo.pop_back(); + rewrite(i); + if (m_util.is_demodulator(fml(i), large, small)) { + func_decl* f = large->get_decl(); + reschedule_processed(f); + reschedule_demodulators(f, large); + m_index.insert_fwd(f, i); + m_rewrites.insert(i, app_expr_pair(large, small)); + m_pinned.push_back(large); + m_pinned.push_back(small); + } + else + m_processed.insert(i); + m_index.insert_bwd(fml(i), i); + } +} diff --git a/src/ast/simplifiers/demodulator_simplifier.h b/src/ast/simplifiers/demodulator_simplifier.h new file mode 100644 index 00000000000..e104c056b5f --- /dev/null +++ b/src/ast/simplifiers/demodulator_simplifier.h @@ -0,0 +1,60 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + demodulator_simplifier.h + +Author: + + Nikolaj Bjorner (nbjorner) 2022-12-4 + +--*/ + +#pragma once + +#include "ast/substitution/demodulator_rewriter.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "util/uint_set.h" + +class demodulator_index { + obj_map m_fwd_index, m_bwd_index; + void add(func_decl* f, unsigned i, obj_map& map); + void del(func_decl* f, unsigned i, obj_map& map); + public: + ~demodulator_index(); + void reset(); + void insert_fwd(func_decl* f, unsigned i) { add(f, i, m_fwd_index); } + void remove_fwd(func_decl* f, unsigned i) { del(f, i, m_fwd_index); } + void insert_bwd(expr* e, unsigned i); + void remove_bwd(expr* e, unsigned i); + bool find_fwd(func_decl* f, uint_set*& s) { return m_bwd_index.find(f, s); } + bool find_bwd(func_decl* f, uint_set*& s) { return m_fwd_index.find(f, s); } + bool empty() const { return m_fwd_index.empty(); } +}; + +class demodulator_simplifier : public dependent_expr_simplifier { + typedef std::pair app_expr_pair; + demodulator_index m_index; + demodulator_util m_util; + demodulator_match_subst m_match_subst; + demodulator_rewriter_util m_rewriter; + u_map m_rewrites; + uint_set m_processed, m_dependencies; + unsigned_vector m_todo; + expr_ref_vector m_pinned; + + void rewrite(unsigned i); + bool rewrite1(func_decl* f, expr_ref_vector const& args, expr_ref& np); + expr* fml(unsigned i) { return m_fmls[i].fml(); } + expr_dependency* dep(unsigned i) { return m_fmls[i].dep(); } + void reschedule_processed(func_decl* f); + void reschedule_demodulators(func_decl* f, expr* lhs); + void reset(); + + public: + demodulator_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st); + void reduce() override; + + +}; diff --git a/src/ast/substitution/demodulator_rewriter.cpp b/src/ast/substitution/demodulator_rewriter.cpp index 6e79242e0a5..0b37308c9a2 100644 --- a/src/ast/substitution/demodulator_rewriter.cpp +++ b/src/ast/substitution/demodulator_rewriter.cpp @@ -27,87 +27,6 @@ Revision History: #include "ast/rewriter/var_subst.h" #include "ast/substitution/demodulator_rewriter.h" -demodulator_rewriter::demodulator_rewriter(ast_manager & m): - m(m), - m_match_subst(m), - m_bsimp(m), - m_todo(m), - m_in_processed(m), - m_new_args(m), - m_rewrite_todo(m), - m_rewrite_cache(m), - m_new_exprs(m) { - params_ref p; - p.set_bool("elim_and", true); - m_bsimp.updt_params(p); -} - -demodulator_rewriter::~demodulator_rewriter() { - reset_dealloc_values(m_fwd_idx); - reset_dealloc_values(m_back_idx); - for (auto & kv : m_demodulator2lhs_rhs) { - m.dec_ref(kv.m_key); - m.dec_ref(kv.m_value.first); - m.dec_ref(kv.m_value.second); - } -} - -bool demodulator_rewriter::is_demodulator(expr * e, app_ref & large, expr_ref & small) const { - if (!is_forall(e)) { - return false; - } - expr * qe = to_quantifier(e)->get_expr(); - expr * lhs = nullptr, *rhs = nullptr, *n; - if (m.is_eq(qe, lhs, rhs)) { - int subset = is_subset(lhs, rhs); - int smaller = is_smaller(lhs, rhs); - TRACE("demodulator", tout << "testing is_demodulator:\n" - << mk_pp(lhs, m) << "\n" - << mk_pp(rhs, m) << "\n" - << "subset: " << subset << ", smaller: " << smaller << "\n";); - // We only track uninterpreted functions, everything else is likely too expensive. - if ((subset == +1 || subset == +2) && smaller == +1) { - if (is_uninterp(rhs)) { - large = to_app(rhs); - small = lhs; - return true; - } - // lhs = (not rhs) --> (not lhs) = rhs - if (m.is_not(rhs, n) && is_uninterp(n)) { - large = to_app(n); - small = m.mk_not(lhs); - return true; - } - } - - if ((subset == -1 || subset == +2) && smaller == -1) { - if (is_uninterp(lhs)) { - large = to_app(lhs); - small = rhs; - return true; - } - // (not lhs) = rhs --> lhs = (not rhs) - if (m.is_not(lhs, n) && is_uninterp(n)) { - large = to_app(n); - small = m.mk_not(rhs); - return true; - } - } - } - else if (m.is_not(qe, n) && is_app(n)) { - // this is like (not (f ... )) --> (= (f ...) false) - large = to_app(n); - small = m.mk_false(); - return true; - } - else if (is_uninterp(qe)) { - // this is like (f ... ) --> (= (f ...) true) - large = to_app(qe); - small = m.mk_true(); - return true; - } - return false; -} class var_set_proc { uint_set & m_set; @@ -118,7 +37,7 @@ class var_set_proc { void operator()(app * n) {} }; -int demodulator_rewriter::is_subset(expr * e1, expr * e2) const { +int demodulator_util::is_subset(expr * e1, expr * e2) const { uint_set ev1, ev2; if (m.is_value(e1)) @@ -135,7 +54,7 @@ int demodulator_rewriter::is_subset(expr * e1, expr * e2) const { 0 ; } -int demodulator_rewriter::is_smaller(expr * e1, expr * e2) const { +int demodulator_util::is_smaller(expr * e1, expr * e2) const { unsigned sz1 = 0, sz2 = 0; // values are always smaller! @@ -174,6 +93,63 @@ int demodulator_rewriter::is_smaller(expr * e1, expr * e2) const { -1 ; } +bool demodulator_util::is_demodulator(expr * e, app_ref & large, expr_ref & small) const { + if (!is_forall(e)) { + return false; + } + expr * qe = to_quantifier(e)->get_expr(); + expr * lhs = nullptr, *rhs = nullptr, *n; + if (m.is_eq(qe, lhs, rhs)) { + int subset = is_subset(lhs, rhs); + int smaller = is_smaller(lhs, rhs); + TRACE("demodulator", tout << "testing is_demodulator:\n" + << mk_pp(lhs, m) << "\n" + << mk_pp(rhs, m) << "\n" + << "subset: " << subset << ", smaller: " << smaller << "\n";); + // We only track uninterpreted functions, everything else is likely too expensive. + if ((subset == +1 || subset == +2) && smaller == +1) { + if (is_uninterp(rhs)) { + large = to_app(rhs); + small = lhs; + return true; + } + // lhs = (not rhs) --> (not lhs) = rhs + if (m.is_not(rhs, n) && is_uninterp(n)) { + large = to_app(n); + small = m.mk_not(lhs); + return true; + } + } + + if ((subset == -1 || subset == +2) && smaller == -1) { + if (is_uninterp(lhs)) { + large = to_app(lhs); + small = rhs; + return true; + } + // (not lhs) = rhs --> lhs = (not rhs) + if (m.is_not(lhs, n) && is_uninterp(n)) { + large = to_app(n); + small = m.mk_not(rhs); + return true; + } + } + } + else if (m.is_not(qe, n) && is_app(n)) { + // this is like (not (f ... )) --> (= (f ...) false) + large = to_app(n); + small = m.mk_false(); + return true; + } + else if (is_uninterp(qe)) { + // this is like (f ... ) --> (= (f ...) true) + large = to_app(qe); + small = m.mk_true(); + return true; + } + return false; +} + class max_var_id_proc { unsigned m_max_var_id; public: @@ -187,13 +163,202 @@ class max_var_id_proc { unsigned get_max() { return m_max_var_id; } }; -unsigned demodulator_rewriter::max_var_id(expr_ref_vector const& es) { +unsigned demodulator_util::max_var_id(expr* e) { + max_var_id_proc proc; + for_each_expr(proc, e); + return proc.get_max(); +} + +unsigned demodulator_util::max_var_id(expr_ref_vector const& es) { max_var_id_proc proc; for (expr* e : es) for_each_expr(proc, e); return proc.get_max(); } + +// ------------------ + +demodulator_rewriter_util::demodulator_rewriter_util(ast_manager& m): + m(m), + m_th_rewriter(m), + m_rewrite_todo(m), + m_rewrite_cache(m), + m_new_exprs(m), + m_new_args(m) +{} + +expr_ref demodulator_rewriter_util::rewrite(expr * n) { + + TRACE("demodulator", tout << "rewrite: " << mk_pp(n, m) << std::endl; ); + app * a; + + SASSERT(m_rewrite_todo.empty()); + m_new_exprs.reset(); + m_rewrite_cache.reset(); + + m_rewrite_todo.push_back(n); + while (!m_rewrite_todo.empty()) { + TRACE("demodulator_stack", tout << "STACK: " << std::endl; + for (unsigned i = 0; i < m_rewrite_todo.size(); i++) + tout << std::dec << i << ": " << std::hex << (size_t)m_rewrite_todo[i] << + " = " << mk_pp(m_rewrite_todo[i], m) << std::endl; + ); + + expr * e = m_rewrite_todo.back(); + expr_ref actual(e, m); + + if (m_rewrite_cache.contains(e)) { + const expr_bool_pair &ebp = m_rewrite_cache.get(e); + if (ebp.second) { + m_rewrite_todo.pop_back(); + continue; + } + else { + actual = ebp.first; + } + } + + switch (actual->get_kind()) { + case AST_VAR: + rewrite_cache(e, actual, true); + m_rewrite_todo.pop_back(); + break; + case AST_APP: + a = to_app(actual); + if (rewrite_visit_children(a)) { + func_decl * f = a->get_decl(); + m_new_args.reset(); + bool all_untouched = true; + for (expr* o_child : *a) { + expr * n_child; + SASSERT(m_rewrite_cache.contains(o_child) && m_rewrite_cache.get(o_child).second); + expr_bool_pair const & ebp = m_rewrite_cache.get(o_child); + n_child = ebp.first; + if (n_child != o_child) + all_untouched = false; + m_new_args.push_back(n_child); + } + expr_ref np(m); + if (m_rewrite1(f, m_new_args, np)) { + rewrite_cache(e, np, false); + // No pop. + } + else { + if (all_untouched) { + rewrite_cache(e, actual, true); + } + else { + expr_ref na(m); + na = m_th_rewriter.mk_app(f, m_new_args); + TRACE("demodulator_bug", tout << "e:\n" << mk_pp(e, m) << "\nnew_args: \n"; + tout << m_new_args << "\n"; + tout << "=====>\n"; + tout << "na:\n " << na << "\n";); + rewrite_cache(e, na, true); + } + m_rewrite_todo.pop_back(); + } + } + break; + case AST_QUANTIFIER: { + expr * body = to_quantifier(actual)->get_expr(); + if (m_rewrite_cache.contains(body)) { + const expr_bool_pair ebp = m_rewrite_cache.get(body); + SASSERT(ebp.second); + expr * new_body = ebp.first; + quantifier_ref q(m); + q = m.update_quantifier(to_quantifier(actual), new_body); + m_new_exprs.push_back(q); + expr_ref new_q = elim_unused_vars(m, q, params_ref()); + m_new_exprs.push_back(new_q); + rewrite_cache(e, new_q, true); + m_rewrite_todo.pop_back(); + } else { + m_rewrite_todo.push_back(body); + } + break; + } + default: + UNREACHABLE(); + } + } + + SASSERT(m_rewrite_cache.contains(n)); + const expr_bool_pair & ebp = m_rewrite_cache.get(n); + SASSERT(ebp.second); + expr * r = ebp.first; + + TRACE("demodulator", tout << "rewrite result: " << mk_pp(r, m) << std::endl; ); + + return expr_ref(r, m); +} + +bool demodulator_rewriter_util::rewrite_visit_children(app * a) { + bool res = true; + for (expr* e : *a) { + if (m_rewrite_cache.contains(e) && m_rewrite_cache.get(e).second) + continue; + bool recursive = false; + expr * v = e; + if (m_rewrite_cache.contains(e)) { + auto const & [t, marked] = m_rewrite_cache.get(e); + if (marked) + v = t; + } + for (expr* t : m_rewrite_todo) { + if (t == v) { + recursive = true; + TRACE("demodulator", tout << "Detected demodulator cycle: " << + mk_pp(a, m) << " --> " << mk_pp(v, m) << std::endl;); + rewrite_cache(e, v, true); + break; + } + } + if (!recursive) { + m_rewrite_todo.push_back(e); + res = false; + } + } + return res; +} + +void demodulator_rewriter_util::rewrite_cache(expr * e, expr * new_e, bool done) { + m_rewrite_cache.insert(e, expr_bool_pair(new_e, done)); +} + + + +// ------------------ + +demodulator_rewriter::demodulator_rewriter(ast_manager & m): + m(m), + m_match_subst(m), + m_util(m), + m_bsimp(m), + m_todo(m), + m_in_processed(m), + m_new_args(m), + m_rewrite_todo(m), + m_rewrite_cache(m), + m_new_exprs(m) { + params_ref p; + p.set_bool("elim_and", true); + m_bsimp.updt_params(p); +} + +demodulator_rewriter::~demodulator_rewriter() { + reset_dealloc_values(m_fwd_idx); + reset_dealloc_values(m_back_idx); + for (auto & kv : m_demodulator2lhs_rhs) { + m.dec_ref(kv.m_key); + m.dec_ref(kv.m_value.first); + m.dec_ref(kv.m_value.second); + } +} + + + void demodulator_rewriter::insert_fwd_idx(app * large, expr * small, quantifier * demodulator) { SASSERT(demodulator); SASSERT(large && small); @@ -265,17 +430,18 @@ bool demodulator_rewriter::rewrite1(func_decl * f, expr_ref_vector const & args, for (quantifier* d : *set) { - auto const& [large, rhs] = m_demodulator2lhs_rhs[d]; + auto const& [lhs, rhs] = m_demodulator2lhs_rhs[d]; - if (large->get_num_args() != args.size()) + if (lhs->get_num_args() != args.size()) continue; TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m) << std::endl; ); - SASSERT(large->get_decl() == f); + SASSERT(lhs->get_decl() == f); - if (m_match_subst(large, rhs, args.data(), np)) { + if (m_match_subst(lhs, rhs, args.data(), np)) { TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(rhs, m) << "\n===>\n" << mk_pp(np, m) << "\n";); + m_new_exprs.push_back(np); return true; } } @@ -289,15 +455,14 @@ bool demodulator_rewriter::rewrite_visit_children(app * a) { if (m_rewrite_cache.contains(e) && m_rewrite_cache.get(e).second) continue; bool recursive = false; - unsigned sz = m_rewrite_todo.size(); expr * v = e; if (m_rewrite_cache.contains(e)) { - expr_bool_pair const & ebp = m_rewrite_cache.get(e); - if (ebp.second) - v = ebp.first; + auto const & [t, marked] = m_rewrite_cache.get(e); + if (marked) + v = t; } - for (unsigned i = sz; i-- > 0;) { - if (m_rewrite_todo[i] == v) { + for (expr* t : m_rewrite_todo) { + if (t == v) { recursive = true; TRACE("demodulator", tout << "Detected demodulator cycle: " << mk_pp(a, m) << " --> " << mk_pp(v, m) << std::endl;); @@ -504,7 +669,7 @@ void demodulator_rewriter::reschedule_processed(func_decl * f) { } } -bool demodulator_rewriter::can_rewrite(expr * n, expr * lhs) { +bool demodulator_match_subst::can_rewrite(expr * n, expr * lhs) { // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. // we cannot use the trick used for m_processed, since the main loop would not terminate. @@ -530,7 +695,7 @@ bool demodulator_rewriter::can_rewrite(expr * n, expr * lhs) { case AST_APP: if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { - if (m_match_subst(lhs, curr)) + if ((*this)(lhs, curr)) return true; visited.mark(curr, true); stack.pop_back(); @@ -582,7 +747,7 @@ void demodulator_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { func_decl_ref df(l->get_decl(), m); // Now we know there is an occurrence of f in d - if (!can_rewrite(d, lhs)) + if (!m_match_subst.can_rewrite(d, lhs)) continue; TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m) << std::endl); @@ -602,7 +767,7 @@ void demodulator_rewriter::operator()(expr_ref_vector const& exprs, for (expr* e : exprs) m_todo.push_back(e); - m_match_subst.reserve(max_var_id(exprs)); + m_match_subst.reserve(m_util.max_var_id(exprs)); while (!m_todo.empty()) { // let n be the next formula in m_todo. @@ -618,7 +783,7 @@ void demodulator_rewriter::operator()(expr_ref_vector const& exprs, app_ref large(m); expr_ref small(m); - if (!is_demodulator(np, large, small)) { + if (!m_util.is_demodulator(np, large, small)) { // insert n' into m_processed m_processed.insert(np); m_in_processed.push_back(np); @@ -661,7 +826,7 @@ void demodulator_rewriter::operator()(expr_ref_vector const& exprs, } -demodulator_rewriter::match_subst::match_subst(ast_manager & m): +demodulator_match_subst::demodulator_match_subst(ast_manager & m): m(m), m_subst(m) { } @@ -693,7 +858,7 @@ struct match_args_aux_proc { void operator()(app * n) {} }; -bool demodulator_rewriter::match_subst::match_args(app * lhs, expr * const * args) { +bool demodulator_match_subst::match_args(app * lhs, expr * const * args) { m_cache.reset(); m_todo.reset(); @@ -809,7 +974,7 @@ bool demodulator_rewriter::match_subst::match_args(app * lhs, expr * const * arg } -bool demodulator_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) { +bool demodulator_match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) { if (match_args(lhs, args)) { if (m_all_args_eq) { @@ -824,7 +989,7 @@ bool demodulator_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * return false; } -bool demodulator_rewriter::match_subst::operator()(expr * t, expr * i) { +bool demodulator_match_subst::operator()(expr * t, expr * i) { m_cache.reset(); m_todo.reset(); if (is_var(t)) diff --git a/src/ast/substitution/demodulator_rewriter.h b/src/ast/substitution/demodulator_rewriter.h index 2152520ce58..18befc1980a 100644 --- a/src/ast/substitution/demodulator_rewriter.h +++ b/src/ast/substitution/demodulator_rewriter.h @@ -24,6 +24,7 @@ Revision History: #include "ast/ast.h" #include "ast/substitution/substitution.h" #include "ast/rewriter/bool_rewriter.h" +#include "ast/rewriter/th_rewriter.h" #include "util/obj_hashtable.h" #include "util/obj_pair_hashtable.h" #include "util/array_map.h" @@ -92,6 +93,92 @@ The code in spc_rewriter.* does something like that. We cannot reuse this code d for the superposion engine in Z3, but we can adapt it for our needs in the preprocessor. */ +class demodulator_util { + ast_manager& m; + int is_subset(expr*, expr*) const; + int is_smaller(expr*, expr*) const; + public: + demodulator_util(ast_manager& m):m(m) {} + bool is_demodulator(expr* e, app_ref& large, expr_ref & small) const; + unsigned max_var_id(expr* e); + unsigned max_var_id(expr_ref_vector const& e); +}; + +/** + \brief Custom matcher & substitution application +*/ +class demodulator_match_subst { + typedef std::pair expr_pair; + typedef obj_pair_hashtable cache; + + void reset(); + + ast_manager & m; + substitution m_subst; + cache m_cache; + svector m_todo; + bool m_all_args_eq; + + bool match_args(app * t, expr * const * args); + +public: + demodulator_match_subst(ast_manager & m); + + void reserve(unsigned max_vid) { m_subst.reserve(2, max_vid+1); } + /** + \brief Let f be the top symbol of lhs. If (f args) is an + instance of lhs, that is, there is a substitution s + s.t. s[lhs] = (f args), then return true and store s[rhs] + into new_rhs. Where s[t] represents the application of the + substitution s into t. + + Assumptions, the variables in lhs and (f args) are assumed to be distinct. + So, (f x y) matches (f y x). + Moreover, the result should be in terms of the variables in (f args). + */ + bool operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs); + + /** + \brief Return true if \c i is an instance of \c t. + */ + bool operator()(expr * t, expr * i); + + bool can_rewrite(expr* n, expr* lhs); +}; + +class demodulator_rewriter_util { + ast_manager& m; + std::function m_rewrite1; + + typedef std::pair expr_bool_pair; + + class plugin { + ast_manager& m; + public: + plugin(ast_manager& m): m(m) { } + void ins_eh(expr* k, expr_bool_pair v) { m.inc_ref(k); m.inc_ref(v.first); } + void del_eh(expr* k, expr_bool_pair v) { m.dec_ref(k); m.dec_ref(v.first); } + static unsigned to_int(expr const * k) { return k->get_id(); } + }; + typedef array_map expr_map; + + typedef expr_map rewrite_cache_map; + + th_rewriter m_th_rewriter; + expr_ref_buffer m_rewrite_todo; + rewrite_cache_map m_rewrite_cache; + expr_ref_buffer m_new_exprs; + expr_ref_vector m_new_args; + + bool rewrite_visit_children(app * a); + void rewrite_cache(expr * e, expr * new_e, bool done); + +public: + demodulator_rewriter_util(ast_manager& m); + void set_rewrite1(std::function& fn) { m_rewrite1 = fn; } + expr_ref rewrite(expr * n); +}; + class demodulator_rewriter final { class rewrite_proc; class add_back_idx_proc; @@ -119,47 +206,10 @@ class demodulator_rewriter final { typedef obj_map demodulator2lhs_rhs; typedef expr_map rewrite_cache_map; - /** - \brief Custom matcher & substitution application - */ - class match_subst { - typedef std::pair expr_pair; - typedef obj_pair_hashtable cache; - - void reset(); - - ast_manager & m; - substitution m_subst; - cache m_cache; - svector m_todo; - bool m_all_args_eq; - - bool match_args(app * t, expr * const * args); - - public: - match_subst(ast_manager & m); - void reserve(unsigned max_vid) { m_subst.reserve(2, max_vid+1); } - /** - \brief Let f be the top symbol of lhs. If (f args) is an - instance of lhs, that is, there is a substitution s - s.t. s[lhs] = (f args), then return true and store s[rhs] - into new_rhs. Where s[t] represents the application of the - substitution s into t. - - Assumptions, the variables in lhs and (f args) are assumed to be distinct. - So, (f x y) matches (f y x). - Moreover, the result should be in terms of the variables in (f args). - */ - bool operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs); - - /** - \brief Return true if \c i is an instance of \c t. - */ - bool operator()(expr * t, expr * i); - }; ast_manager & m; - match_subst m_match_subst; + demodulator_match_subst m_match_subst; + demodulator_util m_util; bool_rewriter m_bsimp; fwd_idx_map m_fwd_idx; back_idx_map m_back_idx; @@ -179,7 +229,6 @@ class demodulator_rewriter final { void remove_bwd_idx(expr* q); bool check_fwd_idx_consistency(); void show_fwd_idx(std::ostream & out); - bool is_demodulator(expr * e, app_ref & large, expr_ref & small) const; bool can_rewrite(expr * n, expr * lhs); expr * rewrite(expr * n); @@ -188,13 +237,6 @@ class demodulator_rewriter final { void rewrite_cache(expr * e, expr * new_e, bool done); void reschedule_processed(func_decl * f); void reschedule_demodulators(func_decl * f, expr * np); - unsigned max_var_id(expr_ref_vector const& es); - - // is_smaller returns -1 for e1e2. - int is_smaller(expr * e1, expr * e2) const; - - // is_subset returns -1 for e1 subset e2, +1 for e2 subset e1, 0 else. - int is_subset(expr * e1, expr * e2) const; public: demodulator_rewriter(ast_manager & m); From 0f7bebcbed8fe33c6d5c3f9c653be57eb7e9adb8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 09:49:32 -0800 Subject: [PATCH 168/597] try big M for linux build --- src/ast/rewriter/poly_rewriter.h | 1 + src/ast/rewriter/poly_rewriter_def.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h index c9253b0e403..6880f7e237b 100644 --- a/src/ast/rewriter/poly_rewriter.h +++ b/src/ast/rewriter/poly_rewriter.h @@ -36,6 +36,7 @@ class poly_rewriter : public Config { bool m_hoist_mul; bool m_ast_order; bool m_hoist_ite; + ast_manager& M() { return Config::m; } bool is_numeral(expr * n) const { return Config::is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); } diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index f9ae333b414..33f1b510aae 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -51,7 +51,7 @@ expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) switch (num_args) { case 0: return mk_numeral(numeral(0)); case 1: return args[0]; - default: return m.mk_app(get_fid(), add_decl_kind(), num_args, args); + default: return M().mk_app(get_fid(), add_decl_kind(), num_args, args); } } From 9b58135876826b569e7967aedade05e9430f9b52 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 09:55:31 -0800 Subject: [PATCH 169/597] try to fix linux builds --- src/ast/rewriter/poly_rewriter_def.h | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index 33f1b510aae..6550089c941 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -119,7 +119,7 @@ expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) if (new_args.size() > 2 && is_numeral(new_args.get(0), a)) { return mk_mul_app(a, mk_mul_app(new_args.size() - 1, new_args.data() + 1)); } - return m.mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.data()); + return M().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.data()); } } else { @@ -127,7 +127,7 @@ expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) if (num_args > 2 && is_numeral(args[0], a)) { return mk_mul_app(a, mk_mul_app(num_args - 1, args + 1)); } - return m.mk_app(get_fid(), mul_decl_kind(), num_args, args); + return M().mk_app(get_fid(), mul_decl_kind(), num_args, args); } } } @@ -189,9 +189,9 @@ br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * cons br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.data(), result); TRACE("poly_rewriter", tout << "flat mul:\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m) << "\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], M()) << "\n"; tout << "---->\n"; - for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m) << "\n"; + for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], M()) << "\n"; tout << st << "\n"; ); if (st == BR_FAILED) { @@ -328,7 +328,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m); + tout << mk_ismt2_pp(new_args[i], M()); } tout << "\nordered: " << ordered << "\n";); if (ordered && num_coeffs == 0 && !use_power()) @@ -340,7 +340,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m); + tout << mk_ismt2_pp(new_args[i], M()); } tout << "\n";); } @@ -349,8 +349,8 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con result = mk_mul_app(c, result); TRACE("poly_rewriter", for (unsigned i = 0; i < num_args; ++i) - tout << mk_ismt2_pp(args[i], m) << " "; - tout << "\nmk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m) << "\n";); + tout << mk_ismt2_pp(args[i], M()) << " "; + tout << "\nmk_nflat_mul_core result:\n" << mk_ismt2_pp(result, M()) << "\n";); return BR_DONE; } @@ -373,9 +373,9 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con } } unsigned orig_size = sums.size(); - expr_ref_buffer sum(m); // must be ref_buffer because we may throw an exception + expr_ref_buffer sum(M()); // must be ref_buffer because we may throw an exception ptr_buffer m_args; - TRACE("som", tout << "starting som...\n";); + TRACE("som", tout << "starting soM()...\n";); do { TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; tout << "\n";); @@ -566,7 +566,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con SASSERT(m_sort_sums || ordered); TRACE("rewriter", tout << "ordered: " << ordered << " sort sums: " << m_sort_sums << "\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m) << "\n";); + for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], M()) << "\n";); if (has_multiple) { // expensive case @@ -589,7 +589,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con coeffs.push_back(a); } } - expr_ref_buffer new_args(m); + expr_ref_buffer new_args(M()); if (!c.is_zero()) { new_args.push_back(mk_numeral(c)); } @@ -639,7 +639,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) return BR_FAILED; } - expr_ref_buffer new_args(m); + expr_ref_buffer new_args(M()); if (!c.is_zero()) new_args.push_back(mk_numeral(c)); for (unsigned i = 0; i < num_args; i++) { @@ -690,8 +690,8 @@ br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, return BR_DONE; } set_curr_sort(args[0]->get_sort()); - expr_ref minus_one(mk_numeral(numeral(-1)), m); - expr_ref_buffer new_args(m); + expr_ref minus_one(mk_numeral(numeral(-1)), M()); + expr_ref_buffer new_args(M()); new_args.push_back(args[0]); for (unsigned i = 1; i < num_args; i++) { if (is_zero(args[i])) continue; @@ -984,11 +984,11 @@ bool poly_rewriter::hoist_ite(expr_ref& e) { return false; obj_hashtable shared; ptr_buffer adds; - expr_ref_vector bs(m), pinned(m); + expr_ref_vector bs(M()), pinned(M()); TO_BUFFER(is_add, adds, e); unsigned i = 0; for (expr* a : adds) { - if (m.is_ite(a)) { + if (M().is_ite(a)) { shared.reset(); numeral g(0); if (hoist_ite(a, shared, g) && (is_nontrivial_gcd(g) || !shared.empty())) { @@ -1026,7 +1026,7 @@ bool poly_rewriter::hoist_ite(expr_ref& e) { template bool poly_rewriter::hoist_ite(expr* a, obj_hashtable& shared, numeral& g) { expr* c = nullptr, *t = nullptr, *e = nullptr; - if (m.is_ite(a, c, t, e)) { + if (M().is_ite(a, c, t, e)) { return hoist_ite(t, shared, g) && hoist_ite(e, shared, g); } rational k, g1; @@ -1064,8 +1064,8 @@ bool poly_rewriter::hoist_ite(expr* a, obj_hashtable& shared, nume template expr* poly_rewriter::apply_hoist(expr* a, numeral const& g, obj_hashtable const& shared) { expr* c = nullptr, *t = nullptr, *e = nullptr; - if (m.is_ite(a, c, t, e)) { - return m.mk_ite(c, apply_hoist(t, g, shared), apply_hoist(e, g, shared)); + if (M().is_ite(a, c, t, e)) { + return M().mk_ite(c, apply_hoist(t, g, shared), apply_hoist(e, g, shared)); } rational k; if (is_nontrivial_gcd(g) && is_int_numeral(a, k)) { From b76ed6a47fc00d184fbceb518eeb5b18ea8ae93d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 10:19:39 -0800 Subject: [PATCH 170/597] proper fix to #6476 --- src/ast/simplifiers/demodulator_simplifier.cpp | 6 +++--- src/ast/substitution/demodulator_rewriter.cpp | 7 ++----- src/ast/substitution/demodulator_rewriter.h | 3 --- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/ast/simplifiers/demodulator_simplifier.cpp b/src/ast/simplifiers/demodulator_simplifier.cpp index eb358560644..d7650717e14 100644 --- a/src/ast/simplifiers/demodulator_simplifier.cpp +++ b/src/ast/simplifiers/demodulator_simplifier.cpp @@ -104,7 +104,7 @@ bool demodulator_simplifier::rewrite1(func_decl* f, expr_ref_vector const& args, if (!m_index.find_fwd(f, set)) return false; - TRACE("demodulator", tout << "trying to rewrite: " << f->get_name() << " args:\n" << m_new_args << "\n";); + TRACE("demodulator", tout << "trying to rewrite: " << f->get_name() << " args:\n" << args << "\n";); for (unsigned i : *set) { @@ -115,7 +115,7 @@ bool demodulator_simplifier::rewrite1(func_decl* f, expr_ref_vector const& args, SASSERT(lhs->get_decl() == f); - TRACE("demodulator", tout << "Matching with demodulator: " << mk_pp(d, m) << std::endl; ); + TRACE("demodulator", tout << "Matching with demodulator: " << mk_pp(lhs, m) << std::endl; ); if (m_match_subst(lhs, rhs, args.data(), np)) { TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(rhs, m) << "\n===>\n" << np << "\n";); @@ -154,7 +154,7 @@ void demodulator_simplifier::reschedule_demodulators(func_decl* f, expr* lhs) { continue; if (!m_match_subst.can_rewrite(fml(i), lhs)) continue; - func_decl* f = p.first->get_decl(); + SASSERT(f == p.first->get_decl()); m_index.remove_fwd(f, i); m_index.remove_bwd(fml(i), i); m_todo.push_back(i); diff --git a/src/ast/substitution/demodulator_rewriter.cpp b/src/ast/substitution/demodulator_rewriter.cpp index 0b37308c9a2..7b06234dc78 100644 --- a/src/ast/substitution/demodulator_rewriter.cpp +++ b/src/ast/substitution/demodulator_rewriter.cpp @@ -847,12 +847,9 @@ struct match_args_aux_proc { SASSERT(r.get_offset() == 1); throw no_match(); } - else { - m_subst.insert(n, 0, expr_offset(n, 1)); - } } - else - throw no_match(); + else + m_subst.insert(n, 0, expr_offset(n, 1)); } void operator()(quantifier * n) { throw no_match(); } void operator()(app * n) {} diff --git a/src/ast/substitution/demodulator_rewriter.h b/src/ast/substitution/demodulator_rewriter.h index 18befc1980a..3a1e442d5e2 100644 --- a/src/ast/substitution/demodulator_rewriter.h +++ b/src/ast/substitution/demodulator_rewriter.h @@ -111,8 +111,6 @@ class demodulator_match_subst { typedef std::pair expr_pair; typedef obj_pair_hashtable cache; - void reset(); - ast_manager & m; substitution m_subst; cache m_cache; @@ -229,7 +227,6 @@ class demodulator_rewriter final { void remove_bwd_idx(expr* q); bool check_fwd_idx_consistency(); void show_fwd_idx(std::ostream & out); - bool can_rewrite(expr * n, expr * lhs); expr * rewrite(expr * n); bool rewrite1(func_decl * f, expr_ref_vector const & args, expr_ref & np); From ead2a46a88972caa03e7013b43dd5f9e1dbb4a23 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 10:38:24 -0800 Subject: [PATCH 171/597] build --- src/ast/rewriter/poly_rewriter_def.h | 2 +- .../simplifiers/demodulator_simplifier.cpp | 7 +- src/ast/simplifiers/demodulator_simplifier.h | 3 +- src/ast/substitution/demodulator_rewriter.cpp | 87 +++++++++---------- 4 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index 6550089c941..f739579e6f9 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -292,7 +292,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); } result = mk_add_app(new_add_args.size(), new_add_args.data()); - TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m,5) << "\n";); + TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, M(), 5) << "\n";); return BR_REWRITE2; } } diff --git a/src/ast/simplifiers/demodulator_simplifier.cpp b/src/ast/simplifiers/demodulator_simplifier.cpp index d7650717e14..7c402c7b5d5 100644 --- a/src/ast/simplifiers/demodulator_simplifier.cpp +++ b/src/ast/simplifiers/demodulator_simplifier.cpp @@ -115,11 +115,12 @@ bool demodulator_simplifier::rewrite1(func_decl* f, expr_ref_vector const& args, SASSERT(lhs->get_decl() == f); - TRACE("demodulator", tout << "Matching with demodulator: " << mk_pp(lhs, m) << std::endl; ); + TRACE("demodulator", tout << "Matching with demodulator: " << mk_pp(lhs, m) << "\n"); if (m_match_subst(lhs, rhs, args.data(), np)) { - TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(rhs, m) << "\n===>\n" << np << "\n";); - m_dependencies.insert(i); + TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(rhs, m) << "\n===>\n" << np << "\n"); + if (dep(i)) + m_dependencies.insert(i); return true; } } diff --git a/src/ast/simplifiers/demodulator_simplifier.h b/src/ast/simplifiers/demodulator_simplifier.h index e104c056b5f..afb5d1a18aa 100644 --- a/src/ast/simplifiers/demodulator_simplifier.h +++ b/src/ast/simplifiers/demodulator_simplifier.h @@ -54,7 +54,6 @@ class demodulator_simplifier : public dependent_expr_simplifier { public: demodulator_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st); - void reduce() override; - + void reduce() override; }; diff --git a/src/ast/substitution/demodulator_rewriter.cpp b/src/ast/substitution/demodulator_rewriter.cpp index 7b06234dc78..18d6ff9254d 100644 --- a/src/ast/substitution/demodulator_rewriter.cpp +++ b/src/ast/substitution/demodulator_rewriter.cpp @@ -669,53 +669,6 @@ void demodulator_rewriter::reschedule_processed(func_decl * f) { } } -bool demodulator_match_subst::can_rewrite(expr * n, expr * lhs) { - // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. - // we cannot use the trick used for m_processed, since the main loop would not terminate. - - ptr_vector stack; - expr * curr; - expr_mark visited; - - stack.push_back(n); - - while (!stack.empty()) { - curr = stack.back(); - - if (visited.is_marked(curr)) { - stack.pop_back(); - continue; - } - - switch(curr->get_kind()) { - case AST_VAR: - visited.mark(curr, true); - stack.pop_back(); - break; - - case AST_APP: - if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { - if ((*this)(lhs, curr)) - return true; - visited.mark(curr, true); - stack.pop_back(); - } - break; - - case AST_QUANTIFIER: - if (visited.is_marked(to_quantifier(curr)->get_expr())) - stack.pop_back(); - else - stack.push_back(to_quantifier(curr)->get_expr()); - break; - default: - UNREACHABLE(); - } - } - - return false; -} - void demodulator_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // use m_back_idx to find all demodulators d in m_fwd_idx that contains f { @@ -831,6 +784,46 @@ demodulator_match_subst::demodulator_match_subst(ast_manager & m): m_subst(m) { } +bool demodulator_match_subst::can_rewrite(expr* n, expr* lhs) { + // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. + // we cannot use the trick used for m_processed, since the main loop would not terminate. + ptr_vector stack; + expr* curr; + expr_mark visited; + + stack.push_back(n); + while (!stack.empty()) { + curr = stack.back(); + if (visited.is_marked(curr)) { + stack.pop_back(); + continue; + } + switch (curr->get_kind()) { + case AST_VAR: + visited.mark(curr, true); + stack.pop_back(); + break; + case AST_APP: + if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { + if ((*this)(lhs, curr)) + return true; + visited.mark(curr, true); + stack.pop_back(); + } + break; + case AST_QUANTIFIER: + if (visited.is_marked(to_quantifier(curr)->get_expr())) + stack.pop_back(); + else + stack.push_back(to_quantifier(curr)->get_expr()); + break; + default: + UNREACHABLE(); + } + } + return false; +} + /** \brief Auxiliary functor used to implement optimization in match_args. See comment there. */ From 87095950cb7ac8a5f5653b44d0a1bd8ac50f5636 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 4 Dec 2022 13:02:45 -0800 Subject: [PATCH 172/597] fix #6477 --- src/ast/rewriter/bv_rewriter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 34d2f650885..179113241da 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -1988,6 +1988,10 @@ bool bv_rewriter::distribute_concat(decl_kind k, unsigned n, expr* const* args, expr* e = to_app(arg)->get_arg(0); unsigned sz1 = get_bv_size(e); unsigned sz2 = get_bv_size(arg); + if (sz1 == sz2) { + result = m.mk_app(get_fid(), k, n, args); + return true; + } expr_ref_vector args1(m), args2(m); for (unsigned j = 0; j < n; ++j) { args1.push_back(m_mk_extract(sz2 - 1, sz2 - sz1, args[j])); From de916f50d62b18b85194949fc213f7f7bb86ece5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 5 Dec 2022 03:20:46 -0800 Subject: [PATCH 173/597] add demodulator tactic based on demodulator-simplifier - some handling for commutative operators - fix bug in demodulator_index where fwd and bwd are swapped --- .../simplifiers/demodulator_simplifier.cpp | 18 +++- src/ast/simplifiers/demodulator_simplifier.h | 9 +- src/ast/substitution/demodulator_rewriter.cpp | 92 ++++++++++++------- src/tactic/core/CMakeLists.txt | 1 + src/tactic/core/demodulator_tactic.h | 40 ++++++++ 5 files changed, 123 insertions(+), 37 deletions(-) create mode 100644 src/tactic/core/demodulator_tactic.h diff --git a/src/ast/simplifiers/demodulator_simplifier.cpp b/src/ast/simplifiers/demodulator_simplifier.cpp index 7c402c7b5d5..afd57b425ef 100644 --- a/src/ast/simplifiers/demodulator_simplifier.cpp +++ b/src/ast/simplifiers/demodulator_simplifier.cpp @@ -71,8 +71,20 @@ void demodulator_index::remove_bwd(expr* e, unsigned i) { for_each_expr(p, e); } +std::ostream& demodulator_index::display(std::ostream& out) const { + out << "forward\n"; + for (auto& [k, v] : m_fwd_index) + out << mk_pp(k, m) << " : " << *v << "\n"; + out << "backward\n"; + for (auto& [k, v] : m_bwd_index) + out << mk_pp(k, m) << " : " << *v << "\n"; + return out; +} + + demodulator_simplifier::demodulator_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st): dependent_expr_simplifier(m, st), + m_index(m), m_util(m), m_match_subst(m), m_rewriter(m), @@ -104,18 +116,19 @@ bool demodulator_simplifier::rewrite1(func_decl* f, expr_ref_vector const& args, if (!m_index.find_fwd(f, set)) return false; - TRACE("demodulator", tout << "trying to rewrite: " << f->get_name() << " args:\n" << args << "\n";); + TRACE("demodulator", tout << "trying to rewrite: " << f->get_name() << " args:" << args << "\n"; m_index.display(tout)); for (unsigned i : *set) { auto const& [lhs, rhs] = m_rewrites[i]; + TRACE("demodulator", tout << "Matching with demodulator: " << i << " " << mk_pp(lhs, m) << "\n"); + if (lhs->get_num_args() != args.size()) continue; SASSERT(lhs->get_decl() == f); - TRACE("demodulator", tout << "Matching with demodulator: " << mk_pp(lhs, m) << "\n"); if (m_match_subst(lhs, rhs, args.data(), np)) { TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(rhs, m) << "\n===>\n" << np << "\n"); @@ -186,6 +199,7 @@ void demodulator_simplifier::reduce() { rewrite(i); if (m_util.is_demodulator(fml(i), large, small)) { func_decl* f = large->get_decl(); + TRACE("demodulator", tout << i << " " << mk_pp(fml(i), m) << ": " << large << " ==> " << small << "\n"); reschedule_processed(f); reschedule_demodulators(f, large); m_index.insert_fwd(f, i); diff --git a/src/ast/simplifiers/demodulator_simplifier.h b/src/ast/simplifiers/demodulator_simplifier.h index afb5d1a18aa..9dff42f19fb 100644 --- a/src/ast/simplifiers/demodulator_simplifier.h +++ b/src/ast/simplifiers/demodulator_simplifier.h @@ -18,19 +18,22 @@ Module Name: #include "util/uint_set.h" class demodulator_index { + ast_manager& m; obj_map m_fwd_index, m_bwd_index; void add(func_decl* f, unsigned i, obj_map& map); void del(func_decl* f, unsigned i, obj_map& map); public: + demodulator_index(ast_manager& m): m(m) {} ~demodulator_index(); void reset(); void insert_fwd(func_decl* f, unsigned i) { add(f, i, m_fwd_index); } void remove_fwd(func_decl* f, unsigned i) { del(f, i, m_fwd_index); } void insert_bwd(expr* e, unsigned i); void remove_bwd(expr* e, unsigned i); - bool find_fwd(func_decl* f, uint_set*& s) { return m_bwd_index.find(f, s); } - bool find_bwd(func_decl* f, uint_set*& s) { return m_fwd_index.find(f, s); } + bool find_fwd(func_decl* f, uint_set*& s) { return m_fwd_index.find(f, s); } + bool find_bwd(func_decl* f, uint_set*& s) { return m_bwd_index.find(f, s); } bool empty() const { return m_fwd_index.empty(); } + std::ostream& display(std::ostream& out) const; }; class demodulator_simplifier : public dependent_expr_simplifier { @@ -56,4 +59,6 @@ class demodulator_simplifier : public dependent_expr_simplifier { demodulator_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st); void reduce() override; + + char const* name() const override { return "demodulator"; } }; diff --git a/src/ast/substitution/demodulator_rewriter.cpp b/src/ast/substitution/demodulator_rewriter.cpp index 18d6ff9254d..c25647c01e6 100644 --- a/src/ast/substitution/demodulator_rewriter.cpp +++ b/src/ast/substitution/demodulator_rewriter.cpp @@ -852,19 +852,45 @@ bool demodulator_match_subst::match_args(app * lhs, expr * const * args) { m_cache.reset(); m_todo.reset(); + auto fill_commutative = [&](app* lhs, expr * const* args) { + if (!lhs->get_decl()->is_commutative()) + return false; + if (lhs->get_num_args() != 2) + return false; + expr* l1 = lhs->get_arg(0); + expr* l2 = lhs->get_arg(1); + expr* r1 = args[0]; + expr* r2 = args[1]; + + if (is_app(l1) && is_app(r1) && to_app(l1)->get_decl() != to_app(r1)->get_decl()) { + m_all_args_eq = false; + m_todo.push_back(expr_pair(l1, r2)); + m_todo.push_back(expr_pair(l2, r1)); + return true; + } + if (is_app(l2) && is_app(r2) && to_app(l2)->get_decl() != to_app(r2)->get_decl()) { + m_all_args_eq = false; + m_todo.push_back(expr_pair(l1, r2)); + m_todo.push_back(expr_pair(l2, r1)); + return true; + } + return false; + }; // fill todo-list, and perform quick success/failure tests m_all_args_eq = true; unsigned num_args = lhs->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * t_arg = lhs->get_arg(i); - expr * i_arg = args[i]; - if (t_arg != i_arg) - m_all_args_eq = false; - if (is_app(t_arg) && is_app(i_arg) && to_app(t_arg)->get_decl() != to_app(i_arg)->get_decl()) { - // quick failure... - return false; + if (!fill_commutative(lhs, args)) { + for (unsigned i = 0; i < num_args; i++) { + expr * t_arg = lhs->get_arg(i); + expr * i_arg = args[i]; + if (t_arg != i_arg) + m_all_args_eq = false; + if (is_app(t_arg) && is_app(i_arg) && to_app(t_arg)->get_decl() != to_app(i_arg)->get_decl()) { + // quick failure... + return false; + } + m_todo.push_back(expr_pair(t_arg, i_arg)); } - m_todo.push_back(expr_pair(t_arg, i_arg)); } if (m_all_args_eq) { @@ -875,48 +901,47 @@ bool demodulator_match_subst::match_args(app * lhs, expr * const * args) { m_subst.reset(); while (!m_todo.empty()) { - expr_pair const & p = m_todo.back(); + auto const & [a, b] = m_todo.back(); - if (is_var(p.first)) { + if (is_var(a)) { expr_offset r; - if (m_subst.find(to_var(p.first), 0, r)) { - if (r.get_expr() != p.second) + if (m_subst.find(to_var(a), 0, r)) { + if (r.get_expr() != b) return false; } else { - m_subst.insert(to_var(p.first), 0, expr_offset(p.second, 1)); + m_subst.insert(to_var(a), 0, expr_offset(b, 1)); } m_todo.pop_back(); continue; } - if (is_var(p.second)) + if (is_var(b)) return false; // we may have nested quantifiers. - if (is_quantifier(p.first) || is_quantifier(p.second)) + if (is_quantifier(a) || is_quantifier(b)) return false; - SASSERT(is_app(p.first) && is_app(p.second)); + SASSERT(is_app(a) && is_app(b)); - if (to_app(p.first)->is_ground() && !to_app(p.second)->is_ground()) + if (to_app(a)->is_ground() && !to_app(b)->is_ground()) return false; - if (p.first == p.second && to_app(p.first)->is_ground()) { - SASSERT(to_app(p.second)->is_ground()); + if (a == b && to_app(a)->is_ground()) { m_todo.pop_back(); continue; } - if (m_cache.contains(p)) { + if (m_cache.contains(expr_pair(a, b))) { m_todo.pop_back(); continue; } - if (p.first == p.second) { - // p.first and p.second is not ground... + if (a == b) { + // a and b is not ground... - // Traverse p.first and check whether every variable X:0 in p.first + // Traverse a and check whether every variable X:0 in a // 1) is unbounded (then we bind X:0 -> X:1) // 2) or, is already bounded to X:1 // If that is, the case, we execute: @@ -927,10 +952,10 @@ bool demodulator_match_subst::match_args(app * lhs, expr * const * args) { // return false; match_args_aux_proc proc(m_subst); try { - for_each_expr(proc, p.first); + for_each_expr(proc, a); // succeeded m_todo.pop_back(); - m_cache.insert(p); + m_cache.insert(expr_pair(a, b)); continue; } catch (const match_args_aux_proc::no_match &) { @@ -938,8 +963,8 @@ bool demodulator_match_subst::match_args(app * lhs, expr * const * args) { } } - app * n1 = to_app(p.first); - app * n2 = to_app(p.second); + app * n1 = to_app(a); + app * n2 = to_app(b); if (n1->get_decl() != n2->get_decl()) return false; @@ -953,12 +978,13 @@ bool demodulator_match_subst::match_args(app * lhs, expr * const * args) { if (num_args1 == 0) continue; - m_cache.insert(p); - unsigned j = num_args1; - while (j > 0) { - --j; + m_cache.insert(expr_pair(a, b)); + + if (fill_commutative(n1, n2->get_args())) + continue; + + for (unsigned j = num_args1; j-- > 0; ) m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); - } } return true; } diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index bc8c4ab719c..8a86e9a0b38 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -32,6 +32,7 @@ z3_add_component(core_tactics cofactor_term_ite_tactic.h collect_statistics_tactic.h ctx_simplify_tactic.h + demodulator_tactic.h der_tactic.h distribute_forall_tactic.h dom_simplify_tactic.h diff --git a/src/tactic/core/demodulator_tactic.h b/src/tactic/core/demodulator_tactic.h new file mode 100644 index 00000000000..2583e902eb0 --- /dev/null +++ b/src/tactic/core/demodulator_tactic.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + demodulator_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/demodulator_simplifier.h" + + +class demodulator_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(demodulator_simplifier, m, p, s); + } +}; + +inline tactic * mk_demodulator_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, alloc(demodulator_tactic_factory)); +} + +/* + ADD_TACTIC("demodulator", "solve for variables.", "mk_demodulator_tactic(m, p)") +*/ + + From eb8c53c16431ddb6f3f07b5f102eab2a1a50769b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 5 Dec 2022 14:07:57 +0000 Subject: [PATCH 174/597] simplify factory of dependent_expr_state_tactic And as a side-effect, remove heap allocations for factories --- src/ast/simplifiers/dependent_expr_state.h | 18 ------------------ src/qe/lite/qe_lite.cpp | 10 ++-------- src/tactic/arith/card2bv_tactic.h | 13 ++----------- src/tactic/bv/bv_slice_tactic.cpp | 11 ++--------- src/tactic/bv/max_bv_sharing_tactic.h | 9 +-------- src/tactic/core/demodulator_tactic.h | 13 ++----------- src/tactic/core/elim_uncnstr2_tactic.h | 13 ++----------- src/tactic/core/eliminate_predicates_tactic.h | 13 ++----------- src/tactic/core/euf_completion_tactic.cpp | 10 ++-------- src/tactic/core/propagate_values2_tactic.h | 14 ++------------ src/tactic/core/solve_eqs_tactic.h | 13 ++----------- src/tactic/dependent_expr_state_tactic.h | 12 +++++++----- 12 files changed, 26 insertions(+), 123 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index d7aa1d6ebd1..212bc99a4a3 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -149,21 +149,3 @@ class dependent_expr_simplifier { ast_manager& get_manager() { return m; } dependent_expr_state& get_fmls() { return m_fmls; } }; - -/** - 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/qe/lite/qe_lite.cpp b/src/qe/lite/qe_lite.cpp index 2ea11b6b960..d3d45bd4b1e 100644 --- a/src/qe/lite/qe_lite.cpp +++ b/src/qe/lite/qe_lite.cpp @@ -2440,17 +2440,11 @@ namespace { } } }; - - class qe_lite_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(qe_lite_simplifier, m, p, s); - } - }; } tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { - return alloc(dependent_expr_state_tactic, m, p, alloc(qe_lite_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(qe_lite_simplifier, m, p, s); }); } dependent_expr_simplifier* mk_qe_lite_simplifer(ast_manager& m, params_ref const& p, dependent_expr_state& st) { diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index 63cb021d706..d7ad76e7d30 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -16,25 +16,16 @@ Module Name: --*/ #pragma once - #include "util/params.h" #include "tactic/tactic.h" #include "tactic/dependent_expr_state_tactic.h" #include "ast/simplifiers/card2bv.h" -class card2bv_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(card2bv, m, p, s); - } -}; - inline tactic* mk_card2bv_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(card2bv_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(card2bv, m, p, s); }); } /* ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)") */ - - diff --git a/src/tactic/bv/bv_slice_tactic.cpp b/src/tactic/bv/bv_slice_tactic.cpp index 17d69c7b147..d470f6e7275 100644 --- a/src/tactic/bv/bv_slice_tactic.cpp +++ b/src/tactic/bv/bv_slice_tactic.cpp @@ -20,14 +20,7 @@ Module Name: #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)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bv::slice, m, s); }); } diff --git a/src/tactic/bv/max_bv_sharing_tactic.h b/src/tactic/bv/max_bv_sharing_tactic.h index 3521b4a041e..bf14e9f14e7 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.h +++ b/src/tactic/bv/max_bv_sharing_tactic.h @@ -24,15 +24,8 @@ Revision History: #include "ast/simplifiers/max_bv_sharing.h" #include "tactic/dependent_expr_state_tactic.h" -class max_bv_sharing_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 mk_max_bv_sharing(m, p, s); - } -}; - inline tactic* mk_max_bv_sharing_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(max_bv_sharing_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, mk_max_bv_sharing); } /* diff --git a/src/tactic/core/demodulator_tactic.h b/src/tactic/core/demodulator_tactic.h index 2583e902eb0..b278392c929 100644 --- a/src/tactic/core/demodulator_tactic.h +++ b/src/tactic/core/demodulator_tactic.h @@ -21,20 +21,11 @@ Module Name: #include "tactic/dependent_expr_state_tactic.h" #include "ast/simplifiers/demodulator_simplifier.h" - -class demodulator_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(demodulator_simplifier, m, p, s); - } -}; - inline tactic * mk_demodulator_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(demodulator_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(demodulator_simplifier, m, p, s); }); } /* ADD_TACTIC("demodulator", "solve for variables.", "mk_demodulator_tactic(m, p)") */ - - diff --git a/src/tactic/core/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h index e7226a8f0af..61e3bbb5a75 100644 --- a/src/tactic/core/elim_uncnstr2_tactic.h +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -21,20 +21,11 @@ Module Name: #include "tactic/dependent_expr_state_tactic.h" #include "ast/simplifiers/elim_unconstrained.h" -class elim_uncnstr2_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(elim_unconstrained, m, s); - } -}; - inline tactic * mk_elim_uncnstr2_tactic(ast_manager & m, params_ref const & p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(elim_uncnstr2_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(elim_unconstrained, m, s); }); } - /* ADD_TACTIC("elim-uncnstr2", "eliminate unconstrained variables.", "mk_elim_uncnstr2_tactic(m, p)") */ - - diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index de2291260b7..d89f369dc00 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -21,20 +21,11 @@ Module Name: #include "tactic/tactic.h" #include "tactic/dependent_expr_state_tactic.h" - -class eliminate_predicates_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(eliminate_predicates, m, s); - } -}; - inline tactic * mk_eliminate_predicates_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(eliminate_predicates_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(eliminate_predicates, m, s); }); } /* ADD_TACTIC("elim-predicates", "eliminate predicates.", "mk_eliminate_predicates_tactic(m, p)") */ - - diff --git a/src/tactic/core/euf_completion_tactic.cpp b/src/tactic/core/euf_completion_tactic.cpp index d229df62f74..ec440878299 100644 --- a/src/tactic/core/euf_completion_tactic.cpp +++ b/src/tactic/core/euf_completion_tactic.cpp @@ -20,13 +20,7 @@ Module Name: #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)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(euf::completion, m, s); }); } diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h index 834e8bebd48..4c89fae9766 100644 --- a/src/tactic/core/propagate_values2_tactic.h +++ b/src/tactic/core/propagate_values2_tactic.h @@ -14,8 +14,6 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-11-24 --*/ - -#include "util/params.h" #pragma once #include "util/params.h" @@ -23,19 +21,11 @@ Module Name: #include "tactic/dependent_expr_state_tactic.h" #include "ast/simplifiers/propagate_values.h" -class propagate_values2_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(propagate_values, m, p, s); - } -}; - inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(propagate_values2_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(propagate_values, m, p, s); }); } - /* ADD_TACTIC("propagate-values2", "propagate constants.", "mk_propagate_values2_tactic(m, p)") */ - diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 76a73866004..a0fd3b63bef 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -21,20 +21,11 @@ Module Name: #include "tactic/dependent_expr_state_tactic.h" #include "ast/simplifiers/solve_eqs.h" - -class solve_eqs_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_eqs_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, alloc(solve_eqs_tactic_factory)); + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(euf::solve_eqs, m, s); }); } /* ADD_TACTIC("solve-eqs", "solve for variables.", "mk_solve_eqs_tactic(m, p)") */ - - diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 3afc0fc9fe7..f2d53236840 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -20,19 +20,22 @@ Module Name: #include "ast/simplifiers/dependent_expr_state.h" class dependent_expr_state_tactic : public tactic, public dependent_expr_state { +public: + using factoryTy = dependent_expr_simplifier(*(*)(ast_manager& m, params_ref const& p, dependent_expr_state& s)); +private: ast_manager& m; params_ref m_params; trail_stack m_trail; goal_ref m_goal; dependent_expr m_dep; statistics m_st; - ref m_factory; + factoryTy m_factory; scoped_ptr m_simp; scoped_ptr m_model_trail; void init() { if (!m_simp) { - m_simp = m_factory->mk(m, m_params, *this); + m_simp = m_factory(m, m_params, *this); m_st.reset(); } if (!m_model_trail) @@ -41,12 +44,11 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { public: - dependent_expr_state_tactic(ast_manager& m, params_ref const& p, dependent_expr_simplifier_factory* f): + dependent_expr_state_tactic(ast_manager& m, params_ref const& p, factoryTy f): dependent_expr_state(m), m(m), m_params(p), m_factory(f), - m_simp(nullptr), m_dep(m, m.mk_true(), nullptr) {} @@ -96,7 +98,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { } tactic * translate(ast_manager & m) override { - return alloc(dependent_expr_state_tactic, m, m_params, m_factory.get()); + return alloc(dependent_expr_state_tactic, m, m_params, m_factory); } void operator()(goal_ref const & in, From a2f5a5b50b47f64ee02db964058da05ce00a4656 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 5 Dec 2022 14:29:14 +0000 Subject: [PATCH 175/597] remove memory alloc from statistics_report --- src/tactic/core/elim_uncnstr_tactic.cpp | 3 +-- src/tactic/tactic.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index ed54b67680d..b8b4334f411 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -867,8 +867,7 @@ class elim_uncnstr_tactic : public tactic { void run(goal_ref const & g, goal_ref_buffer & result) { bool produce_proofs = g->proofs_enabled(); TRACE("goal", g->display(tout);); - std::function coll = [&](statistics& st) { collect_statistics(st); }; - statistics_report sreport(coll); + statistics_report sreport([&](statistics& st) { collect_statistics(st); }); tactic_report report("elim-uncnstr", *g); m_vars.reset(); collect_occs p; diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index c43120943dd..ddd18733716 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -120,7 +120,7 @@ class statistics_report { std::function m_collector; public: statistics_report(tactic& t):m_tactic(&t) {} - statistics_report(std::function& coll): m_collector(coll) {} + statistics_report(std::function&& coll): m_collector(std::move(coll)) {} ~statistics_report(); }; From f1a65d964224cff059ac1aabfa8a47a90c594107 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 5 Dec 2022 03:34:14 -0800 Subject: [PATCH 176/597] add documentation notes --- src/tactic/core/demodulator_tactic.h | 67 +++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/tactic/core/demodulator_tactic.h b/src/tactic/core/demodulator_tactic.h index b278392c929..c591f90898f 100644 --- a/src/tactic/core/demodulator_tactic.h +++ b/src/tactic/core/demodulator_tactic.h @@ -7,12 +7,75 @@ Module Name: Abstract: - Tactic for solving variables + Tactic for rewriting goals using quantified equalities Author: Nikolaj Bjorner (nbjorner) 2022-10-30 +Documentation Notes: + +Name: demodulator + +Short Description: + extracts equalities from quantifiers and applies them for simplification + +Long Description: + In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form: + + Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn] + Where L[X1, ..., Xn] contains all variables in R[X1, ..., Xn], and + L[X1, ..., Xn] is "bigger" than R[X1, ...,Xn]. + + The idea is to replace something big L[X1, ..., Xn] with something smaller R[X1, ..., Xn]. + + After selecting the demodulators, we traverse the rest of the formula looking for instances of L[X1, ..., Xn]. + Whenever we find an instance, we replace it with the associated instance of R[X1, ..., Xn]. + + For example, suppose we have + + ``` + Forall x, y. f(x+y, y) = y + and + f(g(b) + h(c), h(c)) <= 0 + ``` + + The term `f(g(b) + h(c), h(c))` is an instance of `f(x+y, y)` if we replace `x <- g(b)` and `y <- h(c)`. + So, we can replace it with `y` which is bound to `h(c)` in this example. So, the result of the transformation is: + + ``` + Forall x, y. f(x+y, y) = y + and + h(c) <= 0 + ``` + + +Usage: + (declare-sort S 0) + (declare-sort S1 0) + (declare-sort S2 0) + (declare-fun f () S) + (declare-fun f1 () S) + (declare-fun f2 (S1 S) S) + (declare-fun f3 (S2 S) S1) + (declare-fun f4 () S) + (declare-fun f5 () S2) + (assert (not (= f1 (f2 (f3 f5 f4) f)))) + (assert (forall ((q S) (v S)) (or (= q v) (= f1 (f2 (f3 f5 q) v)) (= (f2 (f3 f5 v) q) f1)))) + (assert (forall ((q S) (x S)) (not (= (f2 (f3 f5 q) x) f1)))) + (apply demodulator) + + (goals + (goal + (forall ((q S) (v S)) (= q v)) + (forall ((q S) (x S)) (not (= (f2 (f3 f5 q) x) f1))) + :precision precise :depth 1) + ) + +Supports: unsat cores + +Does not support: proofs + --*/ #pragma once @@ -27,5 +90,5 @@ inline tactic * mk_demodulator_tactic(ast_manager& m, params_ref const& p = para } /* - ADD_TACTIC("demodulator", "solve for variables.", "mk_demodulator_tactic(m, p)") + ADD_TACTIC("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "mk_demodulator_tactic(m, p)") */ From 5a5758baaa54eef5a65b58414d4cb082515a37fb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 5 Dec 2022 20:04:58 -0800 Subject: [PATCH 177/597] add documentation to initial selection of tactics --- doc/mk_tactic_doc.py | 47 ++++++++++++ src/ast/simplifiers/solve_context_eqs.cpp | 2 +- src/tactic/core/demodulator_tactic.h | 67 +++++++++-------- src/tactic/core/elim_uncnstr2_tactic.h | 90 +++++++++++++++++++++++ src/tactic/core/solve_eqs_tactic.h | 53 ++++++++++++- 5 files changed, 228 insertions(+), 31 deletions(-) create mode 100644 doc/mk_tactic_doc.py diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py new file mode 100644 index 00000000000..49d2a177a37 --- /dev/null +++ b/doc/mk_tactic_doc.py @@ -0,0 +1,47 @@ +# Copyright (c) Microsoft Corporation 2015 +""" +Tactic documentation generator script +""" + +import os +import re + +SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) +OUTPUT_DIRECTORY = os.path.join(os.getcwd(), 'api') + +def doc_path(path): + return os.path.join(SCRIPT_DIR, path) + +is_doc = re.compile("Tactic Documentation") +is_doc_end = re.compile("\-\-\*\/") + +def generate_tactic_doc(ous, f, ins): + for line in ins: + if is_doc_end.search(line): + break + ous.write(line) + +def extract_tactic_doc(ous, f): + with open(f) as ins: + for line in ins: + if is_doc.search(line): + generate_tactic_doc(ous, f, ins) + +def help(ous): + ous.write("---\n") + ous.write("title: Tactics Summary\n") + ous.write("sidebar_position: 5\n") + ous.write("---\n") + for root, dirs, files in os.walk(doc_path("../src")): + for f in files: + if f.endswith("tactic.h"): + extract_tactic_doc(ous, os.path.join(root, f)) + +def mk_dir(d): + if not os.path.exists(d): + os.makedirs(d) + +mk_dir(os.path.join(OUTPUT_DIRECTORY, 'md')) + +with open(OUTPUT_DIRECTORY + "/md/tactics-summary.md",'w') as ous: + help(ous) diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index e8738182507..6cb38e4a686 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -279,7 +279,7 @@ namespace euf { } else if (m.is_not(f, f)) todo.push_back({ !s, depth, f }); - else if (!s && 1 == depth % 2) { + else if (!s && 1 <= depth) { 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); diff --git a/src/tactic/core/demodulator_tactic.h b/src/tactic/core/demodulator_tactic.h index c591f90898f..9ffaa6ca90e 100644 --- a/src/tactic/core/demodulator_tactic.h +++ b/src/tactic/core/demodulator_tactic.h @@ -13,44 +13,47 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-10-30 -Documentation Notes: +Tactic Documentation: -Name: demodulator +## Tactic demodulator -Short Description: - extracts equalities from quantifiers and applies them for simplification +### Short Description: -Long Description: - In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form: +Extracts equalities from quantifiers and applies them for simplification - Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn] - Where L[X1, ..., Xn] contains all variables in R[X1, ..., Xn], and - L[X1, ..., Xn] is "bigger" than R[X1, ...,Xn]. +### Long Description - The idea is to replace something big L[X1, ..., Xn] with something smaller R[X1, ..., Xn]. +In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form: - After selecting the demodulators, we traverse the rest of the formula looking for instances of L[X1, ..., Xn]. - Whenever we find an instance, we replace it with the associated instance of R[X1, ..., Xn]. +`Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn]` +Where `L[X1, ..., Xn]` contains all variables in `R[X1, ..., Xn]`, and +`L[X1, ..., Xn]` is "bigger" than `R[X1, ...,Xn]`. - For example, suppose we have +The idea is to replace something big `L[X1, ..., Xn]` with something smaller `R[X1, ..., Xn]`. - ``` - Forall x, y. f(x+y, y) = y - and - f(g(b) + h(c), h(c)) <= 0 - ``` +After selecting the demodulators, we traverse the rest of the formula looking for instances of `L[X1, ..., Xn]`. +Whenever we find an instance, we replace it with the associated instance of `R[X1, ..., Xn]`. - The term `f(g(b) + h(c), h(c))` is an instance of `f(x+y, y)` if we replace `x <- g(b)` and `y <- h(c)`. - So, we can replace it with `y` which is bound to `h(c)` in this example. So, the result of the transformation is: +For example, suppose we have - ``` - Forall x, y. f(x+y, y) = y - and - h(c) <= 0 - ``` +``` +Forall x, y. f(x+y, y) = y +and +f(g(b) + h(c), h(c)) <= 0 +``` +The term `f(g(b) + h(c), h(c))` is an instance of `f(x+y, y)` if we replace `x <- g(b)` and `y <- h(c)`. +So, we can replace it with `y` which is bound to `h(c)` in this example. So, the result of the transformation is: -Usage: +``` +Forall x, y. f(x+y, y) = y +and +h(c) <= 0 +``` + +### Example + +``` (declare-sort S 0) (declare-sort S1 0) (declare-sort S2 0) @@ -64,17 +67,23 @@ Long Description: (assert (forall ((q S) (v S)) (or (= q v) (= f1 (f2 (f3 f5 q) v)) (= (f2 (f3 f5 v) q) f1)))) (assert (forall ((q S) (x S)) (not (= (f2 (f3 f5 q) x) f1)))) (apply demodulator) - +``` + +It generates + +``` (goals (goal (forall ((q S) (v S)) (= q v)) (forall ((q S) (x S)) (not (= (f2 (f3 f5 q) x) f1))) :precision precise :depth 1) ) +``` -Supports: unsat cores +### Notes -Does not support: proofs +* supports unsat cores +* does not support fine-grained proofs --*/ #pragma once diff --git a/src/tactic/core/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h index 61e3bbb5a75..f1c9b9bd7b2 100644 --- a/src/tactic/core/elim_uncnstr2_tactic.h +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -13,6 +13,96 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-10-30 +Tactic Documentation: + +## Tactic elim-uncnstr + +### Short Description + +Eliminate Unconstrained uninterpreted constants + +### Long Description + +The tactic eliminates uninterpreted constants that occur only once in a goal and such that the immediate context +where they occur can be replaced by a fresh constant. We call these occurrences invertible. +It relies on a series of theory specific invertibility transformations. +In the following assume `x` and `x'` occur in a unique subterm and `y` is a fresh uninterpreted constant. + +#### Boolean Connectives + +| Original Context | New Term | Updated solution | +|------------------|----------|------------------------ | +`(if c x x')` | `y` | `x = x' = y` | +`(if x x' e)` | `y` | `x = true, x' = y` | +`(if x t x')` | `y` | `x = false, x' = y` | +`(not x)` | `y` | `x = (not y)` | +`(and x x')` | `y` | `x = y, x' = true` | +`(or x x')` | `y` | `x = y, x' = false` | +`(= x t)` | `y` | `x = (if y t (diff t))` | + +where diff is a diagnonalization function available in domains of size `>` 1. + +#### Arithmetic + +| Original Context | New Term | Updated solution | +|------------------|----------|------------------------ | +`(+ x t)` | `y` | `x = y - t` | +`(* x x')` | `y` | `x = y, x' = 1` | +`(* -1 x)` | `y` | `x = -y` | +`(<= x t)` | `y` | `x = (if y t (+ t 1))` | +`(<= t x)` | `y` | `x = (if y t (- t 1))` | + +#### Bit-vectors + +| Original Context | New Term | Updated solution | +|------------------|----------|--------------------------| +`(bvadd x t)` | `y` | `x = y - t` | +`(bvmul x x')` | `y` | `x = y, x' = 1` | +`(bvmul odd x)` | `y` | `x = inv(odd)*y` | +`((extract sz-1 0) x)` | `y` | `x = y` | +`((extract hi lo) x)` | `y` | `x = (concat y1 y y2)` | +`(udiv x x')` | `y` | `x = y, x' = 1` | +`(concat x x')` | `y` | `x = (extract hi1 lo1 y)` | +`(bvule x t)` | `(or y (= t MAX))` | `x = (if y t (bvadd t 1))` | +`(bvule t x)` | `(or y (= t MIN))` | `x = (if y t (bvsub t 1))` | +`(bvnot x)` | `y` | `x = (bvnot y)` | +`(bvand x x')` | `y` | `x = y, x' = MAX` | + +In addition there are conversions for shift and bit-wise or and signed comparison. + +#### Arrays + +| Original Context | New Term | Updated solution | +|------------------|----------|--------------------------| +`(select x t)` | `y` | `x = (const y)` | +`(store x x1 x2)` | `y` | `x2 = (select x x1), x = y, x1 = arb` | + +#### Algebraic Datatypes + +| Original Context | New Term | Updated solution | +|------------------|----------|--------------------------| +`(head x)` | `y` | `x = (cons y arb)` | + + + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-fun p (Int) Bool) +(assert (>= (+ y (+ x y)) y)) +(assert (p y)) +(apply elim-uncnstr) +(assert (p (+ x y))) +(apply elim-uncnstr) +``` + +### Notes + +* supports unsat cores +* does not support fine-grained proofs + --*/ #pragma once diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index a0fd3b63bef..1207f3de9ce 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -13,9 +13,60 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-10-30 +Tactic Documentation: + +## Tactic solve-eqs + +### Short Description + +Solve for variables + +### Long Description + +The tactic eliminates variables that can be brought into solved form. +For example, the assertion `x = f(y + z)` can be solved for `x`, replacing `x` +everywhere by `f(x + y)`. It depends on a set of theory specific equality solvers + +* Basic equations + * equations between uninterpreted constants and terms. + * equations written as `(if p (= x t) (= x s))` are solved as `(= x (if p t s))`. + * asserting `p` or `(not p)` where `p` is uninterpreted, causes `p` to be replaced by `true` (or `false`). + +* Arithmetic equations + * It solves `x mod k = s` to `x = k * m' + s`, where m'` is a fresh constant. + * It finds variables with unit coefficients in integer linear equations. + * It solves for `x * Y = Z$ under the side-condition `Y != 0` as `x = Z/Y`. + +It also allows solving for uninterpreted constants that only appear in a single disjuction. For example, +`(or (= x (+ 5 y)) (= y (+ u z)))` allows solving for `x`. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(declare-const u Int) +(assert (or (and (= x (+ 5 y)) (> u z)) (= y (+ u z)))) +(apply solve-eqs) +``` + +It produces the goal +``` +(goal + (or (not (<= u z)) (= y (+ u z))) + :precision precise :depth 1) +``` +where `x` was solved as `(+ 5 y)`. + +### Notes + +* supports unsat cores +* does not support fine-grained proofs + --*/ -#pragma once +#pragma once #include "util/params.h" #include "tactic/tactic.h" #include "tactic/dependent_expr_state_tactic.h" From 90ba225ae3bd82aa9be438303f02d6a9e650a993 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 05:39:05 -0800 Subject: [PATCH 178/597] add more doc Signed-off-by: Nikolaj Bjorner --- src/tactic/core/propagate_values2_tactic.h | 35 +++++++++++++++++++--- src/tactic/core/simplify_tactic.h | 32 +++++++++++++++++++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h index 4c89fae9766..e380ce7d382 100644 --- a/src/tactic/core/propagate_values2_tactic.h +++ b/src/tactic/core/propagate_values2_tactic.h @@ -5,14 +5,41 @@ Module Name: propagate_values2_tactic.h -Abstract: - - Tactic for propagating equalities (= t v) where v is a value - Author: Nikolaj Bjorner (nbjorner) 2022-11-24 +Tactic Documentation: + +## Tactic propagate-values + +### Short Description: + +Tactic for propagating equalities `(= t v)` where `v` is a value + +### Long Description + +In a context where terms are equated to constants it is invariably beneficial to +replace terms, that can be compound, with the constants and then simplify the resulting formulas. +The propagate-values tactic accomplishes the task of replacing such terms. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-fun f (Int) Int) +(assert (= 1 (f (+ x y)))) +(assert (= 2 x)) +(assert (> (f (+ 2 y)) y)) +(apply propagate-values) +``` + +### Notes + +* supports unsat cores + + --*/ #pragma once diff --git a/src/tactic/core/simplify_tactic.h b/src/tactic/core/simplify_tactic.h index fc262f99859..39e3e9bdeec 100644 --- a/src/tactic/core/simplify_tactic.h +++ b/src/tactic/core/simplify_tactic.h @@ -13,7 +13,37 @@ Module Name: Leonardo (leonardo) 2011-11-20 -Notes: +Tactic Documentation: + +## Tactic simplify + +### Short Description: + +The tactic performs algebraic simplifcations on formulas + +### Long Description + +The simplify tactic invokes z3's main rewriting engine. +The rewriting engine contains support for theory specific simplifications. +The set of simplifications invoked is open ended. Useful algebraic simplifications +are added to the rewrite engine as they are discovered to be useful. + +Note that the simplifier does not ensure that equivalent formulas are simplified to the same form. +In other words it does not guarantee canonicity. This contrasts with BDD packages where BDDs constructed +from two equivalent formulas are guaranteed to be equal. + +### Example + +```z3 + (declare-const x Int) + (declare-const y Int) + (assert (> x (+ x y))) + (apply simplify) +``` + +### Notes + +* supports unsat cores, proof terms --*/ #pragma once From 7df4e04a2c881479d661a2c9d3edea2913548118 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 05:46:52 -0800 Subject: [PATCH 179/597] add der description Signed-off-by: Nikolaj Bjorner --- src/tactic/core/der_tactic.h | 33 ++++++++++++++++++++++++++++++ src/tactic/core/solve_eqs_tactic.h | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/tactic/core/der_tactic.h b/src/tactic/core/der_tactic.h index 01417c08d15..555d3108d3a 100644 --- a/src/tactic/core/der_tactic.h +++ b/src/tactic/core/der_tactic.h @@ -13,6 +13,39 @@ Module Name: Leonardo de Moura (leonardo) 2012-10-20 +Tactic Documentation: + +## Tactic der + +### Short Description: + +The tactic performs _destructive equality resolution_. + +### Long Description + +Destructive equality resolution replaces bound variables that are +_solved_ by their solutions in formulas. In short, the destructive +equality resolution rule takes the form: + +``` + (forall (X Y) (or X /= s C[X])) --> (forall (Y) C[Y]) +``` + + +### Example + +```z3 + (declare-fun f (Int) Int) + (declare-fun p (Int Int) Bool) + (assert (forall ((x Int) (y Int)) (or (not (= x (f y))) (p x y)))) + (apply der) +``` + +### Notes + +* supports unsat cores, proof terms + + --*/ #pragma once diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 1207f3de9ce..604bcf4264a 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -42,7 +42,7 @@ It also allows solving for uninterpreted constants that only appear in a single ### Example -```z3 +``` (declare-const x Int) (declare-const y Int) (declare-const z Int) From 1e06c7414acf11bd4ad85f777d4ed1f6d49e1c3e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 15:44:21 -0800 Subject: [PATCH 180/597] add doc --- doc/mk_tactic_doc.py | 23 ++++++++++ src/tactic/core/nnf_tactic.h | 42 ++++++++++++++++- src/tactic/core/reduce_args_tactic.h | 67 +++++++++++++++++++++++++++- 3 files changed, 130 insertions(+), 2 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index 49d2a177a37..b49811fb9ea 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -5,7 +5,10 @@ import os import re +import sys +import subprocess +BUILD_DIR='../build' SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) OUTPUT_DIRECTORY = os.path.join(os.getcwd(), 'api') @@ -14,10 +17,30 @@ def doc_path(path): is_doc = re.compile("Tactic Documentation") is_doc_end = re.compile("\-\-\*\/") +is_tac_name = re.compile("## Tactic (.*)") +def extract_params(ous, tac): + z3_exe = BUILD_DIR + "/z3" + out = subprocess.Popen([z3_exe, f"-tactics:{tac}"], stdout=subprocess.PIPE).communicate()[0] + if not out: + return + out = out.decode(sys.stdout.encoding) + ous.write("#### Parameters\n") + ous.write("```\n") + for line in out: + ous.write(line.replace("\r","")) + ous.write("\n") + ous.write("```\n") + def generate_tactic_doc(ous, f, ins): + tac_name = None for line in ins: + m = is_tac_name.search(line) + if m: + tac_name = m.group(1) if is_doc_end.search(line): + if tac_name: + extract_params(ous, tac_name) break ous.write(line) diff --git a/src/tactic/core/nnf_tactic.h b/src/tactic/core/nnf_tactic.h index a821f56d0ec..fa724f2b877 100644 --- a/src/tactic/core/nnf_tactic.h +++ b/src/tactic/core/nnf_tactic.h @@ -13,7 +13,47 @@ Module Name: Leonardo de Moura (leonardo) 2011-12-28. -Revision History: +Note: + + tactic documentation below co-created using gptchat (with some corrections) :-) + +Tactic Documentation: + +## Tactic nnf + +### Short Description: + +The tactic converts formulas to negation normal form (NNF) + +### Long Description + +In NNF, negations only appear in front of atomic formulas. + +Standard rules for conversion into negation normal form are: +- `(not (and p q))` is converted to `(or (not p) (not q))` +- `(not (or p q))` is converted to `(and (not p) (not q))` +- `(not (not p))` is converted to `p` +- `(not (exists x. p))` is converted to `(forall x. (not p))` +- `(not (forall x. p))` is converted to `(exists x. (not p))` + + +Once all negations are pushed inside, the resulting formula is in NNF. + +### Example + +```z3 + (declare-const x Int) + (assert (not (or (> x 0) (< x 0)))) + (apply nnf) +``` + +This would convert the formula (not (or (> x 0) (< x 0))) to (and (<= x 0) (>= x 0)), which is in NNF. + +### Notes + +* supports unsat cores, proof terms + + --*/ #pragma once diff --git a/src/tactic/core/reduce_args_tactic.h b/src/tactic/core/reduce_args_tactic.h index ed4dc3fb3f6..5fa1f74cda8 100644 --- a/src/tactic/core/reduce_args_tactic.h +++ b/src/tactic/core/reduce_args_tactic.h @@ -13,7 +13,72 @@ Module Name: Leonardo (leonardo) 2012-02-19 -Notes: +Tactic Documentation: + +## Tactic reduce-args + +### Short Description: + +Reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value. + +### Long Description + +Example, suppose we have a function `f` with `2` arguments. +There are 1000 applications of this function, but the first argument is always "a", "b" or "c". +Thus, we replace the `f(t1, t2)` with + +* `f_a(t2)` if `t1 = a` +* `f_b(t2)` if `t2 = b` +* `f_c(t2)` if `t2 = c` + +Since `f_a`, `f_b`, `f_c` are new symbols, satisfiability is preserved. + +This transformation is very similar in spirit to the Ackermman's reduction. + +This transformation should work in the following way: + +``` + 1- Create a mapping decl2arg_map from declarations to tuples of booleans, an entry [f -> (true, false, true)] + means that f is a declaration with 3 arguments where the first and third arguments are always values. + 2- Traverse the formula and populate the mapping. + For each function application f(t1, ..., tn) do + a) Create a boolean tuple (is_value(t1), ..., is_value(tn)) and do + the logical-and with the tuple that is already in the mapping. If there is no such tuple + in the mapping, we just add a new entry. + + If all entries are false-tuples, then there is nothing to be done. The transformation is not applicable. + + Now, we create a mapping decl2new_decl from (decl, val_1, ..., val_n) to decls. Note that, n may be different for each entry, + but it is the same for the same declaration. + For example, suppose we have [f -> (true, false, true)] in decl2arg_map, + and applications f(1, a, 2), f(1, b, 2), f(1, b, 3), f(2, b, 3), f(2, c, 3) in the formula. + Then, decl2arg_map would contain + (f, 1, 2) -> f_1_2 + (f, 1, 3) -> f_1_3 + (f, 2, 3) -> f_2_3 + where f_1_2, f_1_3 and f_2_3 are new function symbols. + Using the new map, we can replace the occurrences of f. +``` + +### Example + +```z3 +(declare-fun f (Int Int) Bool) +(declare-const x Int) +(assert (f 1 2)) +(assert (f 1 3)) +(assert (f 2 4)) +(assert (f 2 5)) +(assert (f 1 6)) +(assert (f 1 7)) +(assert (f 1 x)) +(apply reduce-args) +``` + +### Notes + +* supports unsat cores +* does not support proof terms --*/ #pragma once From d125d87aed403edaf2e971f4038e573eaba708c6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 15:51:38 -0800 Subject: [PATCH 181/597] typo Signed-off-by: Nikolaj Bjorner --- src/tactic/core/solve_eqs_tactic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 604bcf4264a..fec46bf045c 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -35,7 +35,7 @@ everywhere by `f(x + y)`. It depends on a set of theory specific equality solver * Arithmetic equations * It solves `x mod k = s` to `x = k * m' + s`, where m'` is a fresh constant. * It finds variables with unit coefficients in integer linear equations. - * It solves for `x * Y = Z$ under the side-condition `Y != 0` as `x = Z/Y`. + * It solves for `x * Y = Z` under the side-condition `Y != 0` as `x = Z/Y`. It also allows solving for uninterpreted constants that only appear in a single disjuction. For example, `(or (= x (+ 5 y)) (= y (+ u z)))` allows solving for `x`. From aaabbfb594fcfed4240c580e31db51cc3483e6ed Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 15:53:55 -0800 Subject: [PATCH 182/597] remove comment that does not align with result Signed-off-by: Nikolaj Bjorner --- src/tactic/core/nnf_tactic.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tactic/core/nnf_tactic.h b/src/tactic/core/nnf_tactic.h index fa724f2b877..083380be858 100644 --- a/src/tactic/core/nnf_tactic.h +++ b/src/tactic/core/nnf_tactic.h @@ -47,7 +47,6 @@ Once all negations are pushed inside, the resulting formula is in NNF. (apply nnf) ``` -This would convert the formula (not (or (> x 0) (< x 0))) to (and (<= x 0) (>= x 0)), which is in NNF. ### Notes From 80033e874499d18f9b650235dbd4e75c568aa447 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 17:02:04 -0800 Subject: [PATCH 183/597] cave in to supporting proofs (partially) in simplifiers, updated doc --- doc/mk_tactic_doc.py | 6 ++-- src/ast/simplifiers/bit2int.h | 4 ++- src/ast/simplifiers/bit_blaster.cpp | 4 +-- src/ast/simplifiers/bv_elim.h | 2 +- src/ast/simplifiers/bv_slice.cpp | 6 ++-- src/ast/simplifiers/card2bv.cpp | 6 ++-- src/ast/simplifiers/cnf_nnf.h | 5 ++-- .../simplifiers/demodulator_simplifier.cpp | 2 +- src/ast/simplifiers/dependent_expr.h | 24 ++++++++++++++-- src/ast/simplifiers/dependent_expr_state.h | 3 ++ src/ast/simplifiers/distribute_forall.h | 2 +- src/ast/simplifiers/elim_bounds.h | 2 +- src/ast/simplifiers/elim_term_ite.h | 8 ++++-- src/ast/simplifiers/elim_unconstrained.cpp | 4 +-- src/ast/simplifiers/eliminate_predicates.cpp | 10 +++---- src/ast/simplifiers/euf_completion.cpp | 8 +++--- src/ast/simplifiers/extract_eqs.cpp | 4 +-- src/ast/simplifiers/flatten_clauses.h | 20 ++++++------- src/ast/simplifiers/max_bv_sharing.cpp | 5 ++-- .../model_reconstruction_trail.cpp | 11 ++++---- src/ast/simplifiers/propagate_values.cpp | 6 ++-- src/ast/simplifiers/pull_nested_quantifiers.h | 4 ++- src/ast/simplifiers/push_ite.h | 5 ++-- src/ast/simplifiers/refine_inj_axiom.h | 2 +- src/ast/simplifiers/rewriter_simplifier.h | 3 +- src/ast/simplifiers/solve_context_eqs.cpp | 2 +- src/ast/simplifiers/solve_eqs.cpp | 7 +++-- src/qe/lite/qe_lite.cpp | 5 ++-- src/sat/sat_solver/sat_smt_solver.cpp | 8 +++--- src/shell/main.cpp | 7 +++-- src/shell/smtlib_frontend.cpp | 7 +++-- src/shell/smtlib_frontend.h | 2 +- src/tactic/core/injectivity_tactic.cpp | 15 +--------- src/tactic/core/injectivity_tactic.h | 28 ++++++++++++++++++- src/tactic/core/reduce_args_tactic.h | 14 +++++----- src/tactic/dependent_expr_state_tactic.h | 14 +++++----- 36 files changed, 157 insertions(+), 108 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index b49811fb9ea..85dfcb4c35d 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -21,16 +21,14 @@ def doc_path(path): def extract_params(ous, tac): z3_exe = BUILD_DIR + "/z3" - out = subprocess.Popen([z3_exe, f"-tactics:{tac}"], stdout=subprocess.PIPE).communicate()[0] + out = subprocess.Popen([z3_exe, f"-tacticsmd:{tac}"], stdout=subprocess.PIPE).communicate()[0] if not out: return out = out.decode(sys.stdout.encoding) - ous.write("#### Parameters\n") - ous.write("```\n") + ous.write("### Parameters\n\n") for line in out: ous.write(line.replace("\r","")) ous.write("\n") - ous.write("```\n") def generate_tactic_doc(ous, f, ins): tac_name = None diff --git a/src/ast/simplifiers/bit2int.h b/src/ast/simplifiers/bit2int.h index 6605c2e7ef4..64c7f3ecf6d 100644 --- a/src/ast/simplifiers/bit2int.h +++ b/src/ast/simplifiers/bit2int.h @@ -35,8 +35,10 @@ class bit2int_simplifier : public dependent_expr_simplifier { for (unsigned idx : indices()) { auto const& d = m_fmls[idx]; m_rewriter(d.fml(), r, pr); - m_fmls.update(idx, dependent_expr(m, r, d.dep())); + m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep())); } } + + bool supports_proofs() const override { return true; } }; diff --git a/src/ast/simplifiers/bit_blaster.cpp b/src/ast/simplifiers/bit_blaster.cpp index a1988a9306e..c66cef826da 100644 --- a/src/ast/simplifiers/bit_blaster.cpp +++ b/src/ast/simplifiers/bit_blaster.cpp @@ -38,13 +38,13 @@ void bit_blaster::reduce() { proof_ref new_pr(m); bool change = false; for (unsigned idx : indices()) { - auto [curr, d] = m_fmls[idx](); + auto [curr, p, d] = m_fmls[idx](); m_rewriter(curr, new_curr, new_pr); if (curr != new_curr) { m_num_steps += m_rewriter.get_num_steps(); change = true; TRACE("bit_blaster", tout << mk_pp(curr, m) << " -> " << new_curr << "\n";); - m_fmls.update(idx, dependent_expr(m, new_curr, d)); + m_fmls.update(idx, dependent_expr(m, new_curr, mp(p, new_pr), d)); } } diff --git a/src/ast/simplifiers/bv_elim.h b/src/ast/simplifiers/bv_elim.h index 6f045fc5420..344a9df82e7 100644 --- a/src/ast/simplifiers/bv_elim.h +++ b/src/ast/simplifiers/bv_elim.h @@ -36,7 +36,7 @@ class elim_simplifier : public dependent_expr_simplifier { if (!has_quantifiers(d.fml())) continue; m_rewriter(d.fml(), r); - m_fmls.update(idx, dependent_expr(m, r, d.dep())); + m_fmls.update(idx, dependent_expr(m, r, nullptr, d.dep())); } } }; diff --git a/src/ast/simplifiers/bv_slice.cpp b/src/ast/simplifiers/bv_slice.cpp index 7ffa56a29f9..45db268b3e7 100644 --- a/src/ast/simplifiers/bv_slice.cpp +++ b/src/ast/simplifiers/bv_slice.cpp @@ -28,7 +28,7 @@ namespace bv { void slice::process_eqs() { for (unsigned i : indices()) { - auto const [f, d] = m_fmls[i](); + auto const [f, p, d] = m_fmls[i](); process_eq(f); } } @@ -137,7 +137,7 @@ namespace bv { ptr_vector todo, args; expr* c; for (unsigned i : indices()) { - auto const [f, d] = m_fmls[i](); + auto const [f, p, d] = m_fmls[i](); todo.push_back(f); pin.push_back(f); while (!todo.empty()) { @@ -191,7 +191,7 @@ namespace bv { } c = cache.get(f->get_id()); if (c != f) - m_fmls.update(i, dependent_expr(m, c, d)); + m_fmls.update(i, dependent_expr(m, c, nullptr, d)); } } diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index 58774e1c6bc..b2fece21e3f 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -30,12 +30,12 @@ void card2bv::reduce() { expr_ref new_f1(m), new_f2(m); proof_ref new_pr(m); for (unsigned idx : indices()) { - auto [f, d] = m_fmls[idx](); + auto [f, p, d] = m_fmls[idx](); rw1(f, new_f1); rw2(false, new_f1, new_f2, new_pr); if (new_f2 != f) { TRACE("card2bv", tout << "Rewriting " << new_f1 << "\n" << new_f2 << "\n"); - m_fmls.update(idx, dependent_expr(m, new_f2, d)); + m_fmls.update(idx, dependent_expr(m, new_f2, mp(p, new_pr), d)); ++m_stats.m_num_rewrites; } } @@ -43,7 +43,7 @@ void card2bv::reduce() { expr_ref_vector fmls(m); rw2.flush_side_constraints(fmls); for (expr* e : fmls) - m_fmls.add(dependent_expr(m, e, nullptr)); + m_fmls.add(dependent_expr(m, e, nullptr, nullptr)); func_decl_ref_vector const& fns = rw2.fresh_constants(); for (func_decl* f : fns) diff --git a/src/ast/simplifiers/cnf_nnf.h b/src/ast/simplifiers/cnf_nnf.h index 56cdc1367e1..6cb1c346e78 100644 --- a/src/ast/simplifiers/cnf_nnf.h +++ b/src/ast/simplifiers/cnf_nnf.h @@ -48,12 +48,13 @@ class cnf_nnf_simplifier : public dependent_expr_simplifier { push_todo.reset(); push_todo_prs.reset(); apply_nnf(d.fml(), push_todo, push_todo_prs, r, pr); - m_fmls.update(i, dependent_expr(m, r, d.dep())); + m_fmls.update(i, dependent_expr(m, r, mp(d.pr(), pr), d.dep())); for (expr* f : push_todo) { if (!m.inc()) break; m_rewriter(f, r, pr); - m_fmls.add(dependent_expr(m, r, d.dep())); + if (f != r) + m_fmls.add(dependent_expr(m, r, pr, d.dep())); } } } diff --git a/src/ast/simplifiers/demodulator_simplifier.cpp b/src/ast/simplifiers/demodulator_simplifier.cpp index afd57b425ef..0e42e34818b 100644 --- a/src/ast/simplifiers/demodulator_simplifier.cpp +++ b/src/ast/simplifiers/demodulator_simplifier.cpp @@ -108,7 +108,7 @@ void demodulator_simplifier::rewrite(unsigned i) { expr_dependency_ref d(dep(i), m); for (unsigned j : m_dependencies) d = m.mk_join(d, dep(j)); - m_fmls.update(i, dependent_expr(m, r, d)); + m_fmls.update(i, dependent_expr(m, r, nullptr, d)); } bool demodulator_simplifier::rewrite1(func_decl* f, expr_ref_vector const& args, expr_ref& np) { diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h index ddf119070e0..55f8d8f4695 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -24,21 +24,26 @@ Module Name: class dependent_expr { ast_manager& m; expr* m_fml; + proof* m_proof; expr_dependency* m_dep; public: - dependent_expr(ast_manager& m, expr* fml, expr_dependency* d): + dependent_expr(ast_manager& m, expr* fml, proof* p, expr_dependency* d): m(m), m_fml(fml), + m_proof(p), m_dep(d) { SASSERT(fml); m.inc_ref(fml); m.inc_ref(d); + m.inc_ref(p); } dependent_expr(ast_translation& tr, dependent_expr const& src) : m(tr.to()) { m_fml = tr(src.fml()); m.inc_ref(m_fml); + m_proof = tr(src.pr()); + m.inc_ref(m_proof); expr_dependency_translation dtr(tr); m_dep = dtr(src.dep()); m.inc_ref(m_dep); @@ -49,10 +54,13 @@ class dependent_expr { if (this != &other) { m.inc_ref(other.m_fml); m.inc_ref(other.m_dep); + m.inc_ref(other.m_proof); m.dec_ref(m_fml); m.dec_ref(m_dep); + m.dec_ref(m_proof); m_fml = other.m_fml; m_dep = other.m_dep; + m_proof = other.m_proof; } return *this; } @@ -60,24 +68,30 @@ class dependent_expr { dependent_expr(dependent_expr const& other): m(other.m), m_fml(other.m_fml), + m_proof(other.m_proof), m_dep(other.m_dep) { m.inc_ref(m_fml); + m.inc_ref(m_proof); m.inc_ref(m_dep); } dependent_expr(dependent_expr && other) noexcept : m(other.m), m_fml(nullptr), + m_proof(nullptr), m_dep(nullptr) { std::swap(m_fml, other.m_fml); + std::swap(m_proof, other.m_proof); std::swap(m_dep, other.m_dep); } ~dependent_expr() { m.dec_ref(m_fml); m.dec_ref(m_dep); + m.dec_ref(m_proof); m_fml = nullptr; m_dep = nullptr; + m_proof = nullptr; } ast_manager& get_manager() const { return m; } @@ -85,9 +99,11 @@ class dependent_expr { expr* fml() const { return m_fml; } expr_dependency* dep() const { return m_dep; } + + proof* pr() const { return m_proof; } - std::tuple operator()() const { - return { m_fml, m_dep }; + std::tuple operator()() const { + return { m_fml, m_proof, m_dep }; } std::ostream& display(std::ostream& out) const { @@ -99,6 +115,8 @@ class dependent_expr { for (expr* arg : deps) out << mk_pp(arg, m) << " "; } + if (m_proof) + out << "\n:- " << mk_pp(m_proof, m); return out; } }; diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 212bc99a4a3..85b6352ad25 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -135,6 +135,8 @@ class dependent_expr_simplifier { index_set indices() { return index_set(*this); } + proof* mp(proof* a, proof* b) { return (a && b) ? m.mk_modus_ponens(a, b) : nullptr; } + public: dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {} virtual ~dependent_expr_simplifier() {} @@ -146,6 +148,7 @@ class dependent_expr_simplifier { virtual void reset_statistics() {} virtual void updt_params(params_ref const& p) {} virtual void collect_param_descrs(param_descrs& r) {} + virtual bool supports_proofs() const { return false; } ast_manager& get_manager() { return m; } dependent_expr_state& get_fmls() { return m_fmls; } }; diff --git a/src/ast/simplifiers/distribute_forall.h b/src/ast/simplifiers/distribute_forall.h index 82709d29f72..3127a4ec87a 100644 --- a/src/ast/simplifiers/distribute_forall.h +++ b/src/ast/simplifiers/distribute_forall.h @@ -38,7 +38,7 @@ class distribute_forall_simplifier : public dependent_expr_simplifier { if (!has_quantifiers(d.fml())) continue; m_dist(d.fml(), r); - m_fmls.update(idx, dependent_expr(m, r, d.dep())); + m_fmls.update(idx, dependent_expr(m, r, nullptr, d.dep())); } } }; diff --git a/src/ast/simplifiers/elim_bounds.h b/src/ast/simplifiers/elim_bounds.h index d6631adfdd4..ed93faeba26 100644 --- a/src/ast/simplifiers/elim_bounds.h +++ b/src/ast/simplifiers/elim_bounds.h @@ -38,7 +38,7 @@ class elim_bounds_simplifier : public dependent_expr_simplifier { if (!has_quantifiers(d.fml())) continue; m_rewriter(d.fml(), r); - m_fmls.update(idx, dependent_expr(m, r, d.dep())); + m_fmls.update(idx, dependent_expr(m, r, nullptr, d.dep())); } } }; diff --git a/src/ast/simplifiers/elim_term_ite.h b/src/ast/simplifiers/elim_term_ite.h index 5b6ef38fa49..1b8f58f9e5d 100644 --- a/src/ast/simplifiers/elim_term_ite.h +++ b/src/ast/simplifiers/elim_term_ite.h @@ -33,13 +33,17 @@ class elim_term_ite_simplifier : public dependent_expr_simplifier { void reduce() override { expr_ref r(m); + proof_ref pr(m); for (unsigned idx : indices()) { auto const& d = m_fmls[idx]; - m_rewriter(d.fml(), r); - m_fmls.update(idx, dependent_expr(m, r, d.dep())); + m_rewriter(d.fml(), r, pr); + if (d.fml() != r) + m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep())); } } + bool supports_proofs() const override { return true; } + void push() override { dependent_expr_simplifier::push(); m_df.push(); m_rewriter.push(); } void pop(unsigned n) override { m_rewriter.pop(n); m_df.pop(n); dependent_expr_simplifier::pop(n); } diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 7404382a38c..032c46a92f3 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -258,13 +258,13 @@ void elim_unconstrained::reconstruct_terms() { void elim_unconstrained::assert_normalized(vector& old_fmls) { for (unsigned i : indices()) { - auto [f, d] = m_fmls[i](); + auto [f, p, d] = m_fmls[i](); node& n = get_node(f); expr* g = n.m_term; if (f == g) continue; old_fmls.push_back(m_fmls[i]); - m_fmls.update(i, dependent_expr(m, g, d)); + m_fmls.update(i, dependent_expr(m, g, nullptr, d)); IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); TRACE("elim_unconstrained", tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); } diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 700f96ce3b7..118edf04d24 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -536,7 +536,7 @@ void eliminate_predicates::reduce_definitions() { macro_expander.insert(v->m_head, v->m_def, v->m_dep); for (unsigned i : indices()) { - auto [f, d] = m_fmls[i](); + auto [f, p, d] = m_fmls[i](); expr_ref fml(f, m), new_fml(m); expr_dependency_ref dep(d, m); while (true) { @@ -546,7 +546,7 @@ void eliminate_predicates::reduce_definitions() { rewrite(new_fml); fml = new_fml; } - m_fmls.update(i, dependent_expr(m, fml, dep)); + m_fmls.update(i, dependent_expr(m, fml, nullptr, dep)); } reset(); init_clauses(); @@ -770,7 +770,7 @@ void eliminate_predicates::process_to_exclude(ast_mark& exclude_set) { eliminate_predicates::clause* eliminate_predicates::init_clause(unsigned i) { - auto [f, d] = m_fmls[i](); + auto [f, p, d] = m_fmls[i](); return init_clause(f, d, i); } @@ -821,12 +821,12 @@ void eliminate_predicates::decompile() { if (cl->m_fml_index != UINT_MAX) { if (cl->m_alive) continue; - dependent_expr de(m, m.mk_true(), nullptr); + dependent_expr de(m, m.mk_true(), nullptr, nullptr); m_fmls.update(cl->m_fml_index, de); } else if (cl->m_alive) { expr_ref new_cl = cl->m_fml; - dependent_expr de(m, new_cl, cl->m_dep); + dependent_expr de(m, new_cl, nullptr, cl->m_dep); m_fmls.add(de); } } diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 78be41c1adc..3ede7024ee3 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -82,7 +82,7 @@ namespace euf { for (unsigned i = qhead(); i < sz; ++i) { expr* x, * y; - auto [f, d] = m_fmls[i](); + auto [f, p, d] = m_fmls[i](); if (m.is_eq(f, x, y)) { enode* a = mk_enode(x); enode* b = mk_enode(y); @@ -108,19 +108,19 @@ namespace euf { if (m_egraph.inconsistent()) { auto* d = explain_conflict(); - dependent_expr de(m, m.mk_false(), d); + dependent_expr de(m, m.mk_false(), nullptr, d); m_fmls.update(0, de); return; } unsigned sz = qtail(); for (unsigned i = qhead(); i < sz; ++i) { - auto [f, d] = m_fmls[i](); + auto [f, p, d] = m_fmls[i](); 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_fmls.update(i, dependent_expr(m, g, nullptr, dep)); m_stats.m_num_rewrites++; IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); update_has_new_eq(g); diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index fdb889ee3ac..b2e264b1fb4 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -39,7 +39,7 @@ namespace euf { } void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { - auto [f, d] = e(); + auto [f, p, d] = e(); expr* x, * y; if (m.is_eq(f, x, y)) { if (x == y) @@ -246,7 +246,7 @@ namespace euf { void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { if (!m_enabled) return; - auto [f, d] = e(); + auto [f, p, d] = e(); expr* x, * y; if (m.is_eq(f, x, y) && a.is_int_real(x)) { solve_eq(f, x, y, d, eqs); diff --git a/src/ast/simplifiers/flatten_clauses.h b/src/ast/simplifiers/flatten_clauses.h index aa81024effb..e2da2d182aa 100644 --- a/src/ast/simplifiers/flatten_clauses.h +++ b/src/ast/simplifiers/flatten_clauses.h @@ -67,8 +67,8 @@ class flatten_clauses : public dependent_expr_simplifier { decomposed = true; if (decomposed) { for (expr* arg : *to_app(b)) - m_fmls.add(dependent_expr(m, m.mk_or(a, mk_not(m, arg)), de.dep())); - m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + m_fmls.add(dependent_expr(m, nullptr, m.mk_or(a, mk_not(m, arg)), de.dep())); + m_fmls.update(idx, dependent_expr(m, nullptr, m.mk_true(), nullptr)); ++m_num_flat; continue; } @@ -79,8 +79,8 @@ class flatten_clauses : public dependent_expr_simplifier { decomposed = true; if (decomposed) { for (expr * arg : *to_app(b)) - m_fmls.add(dependent_expr(m, m.mk_or(a, arg), de.dep())); - m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + m_fmls.add(dependent_expr(m, nullptr, m.mk_or(a, arg), de.dep())); + m_fmls.update(idx, dependent_expr(m, nullptr, m.mk_true(), nullptr)); ++m_num_flat; continue; } @@ -92,20 +92,20 @@ class flatten_clauses : public dependent_expr_simplifier { if (decomposed) { expr* na = mk_not(m, a); for (expr* arg : *to_app(b)) - m_fmls.add(dependent_expr(m, m.mk_or(na, arg), de.dep())); - m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + m_fmls.add(dependent_expr(m, m.mk_or(na, arg), nullptr, de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr, nullptr)); ++m_num_flat; continue; } if (m.is_implies(f, a, b)) { - m_fmls.update(idx, dependent_expr(m, m.mk_or(mk_not(m, a), b), de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_or(mk_not(m, a), b), nullptr, de.dep())); ++m_num_flat; continue; } if (m.is_ite(f, a, b, c)) { - m_fmls.add(dependent_expr(m, m.mk_or(mk_not(m, a), b), de.dep())); - m_fmls.add(dependent_expr(m, m.mk_or(a, c), de.dep())); - m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr)); + m_fmls.add(dependent_expr(m, m.mk_or(mk_not(m, a), b), nullptr, de.dep())); + m_fmls.add(dependent_expr(m, m.mk_or(a, c), nullptr, de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr, nullptr)); ++m_num_flat; continue; } diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index c12fa4410d4..f708f9dce75 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -48,12 +48,11 @@ class max_bv_sharing : public dependent_expr_simplifier { expr_ref new_curr(m); proof_ref new_pr(m); for (unsigned idx : indices()) { - auto [curr, d] = m_fmls[idx](); + auto [curr, p, d] = m_fmls[idx](); m_rewriter(curr, new_curr, new_pr); - // Proof reconstruction: new_pr = m.mk_modus_ponens(old_pr, new_pr); if (new_curr != curr) { m_num_steps += m_rewriter.get_num_steps(); - m_fmls.update(idx, dependent_expr(m, new_curr, d)); + m_fmls.update(idx, dependent_expr(m, new_curr, mp(p, new_pr), d)); } } } diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 7e8d3e2f1c3..567546e67b1 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -61,16 +61,17 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st args.push_back(m.mk_var(i, d->get_domain(i))); head = m.mk_app(d, args); mrp.insert(head, t->m_def, t->m_dep); - dependent_expr de(m, t->m_def, t->m_dep); + dependent_expr de(m, t->m_def, nullptr, t->m_dep); add_vars(de, free_vars); for (unsigned i = qhead; i < st.qtail(); ++i) { - auto [f, dep1] = st[i](); + auto [f, p, dep1] = st[i](); expr_ref g(m); expr_dependency_ref dep2(m); mrp(f, dep1, g, dep2); CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n"); - st.update(i, dependent_expr(m, g, dep2)); + if (f != g) + st.update(i, dependent_expr(m, g, nullptr, dep2)); } continue; } @@ -81,7 +82,7 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st ptr_vector dep_exprs; expr_ref_vector trail(m); for (unsigned i = qhead; i < st.qtail(); ++i) { - auto [f, dep1] = st[i](); + auto [f, p, dep1] = st[i](); auto [g, dep2] = rp->replace_with_dep(f); if (dep1) { dep_exprs.reset(); @@ -98,7 +99,7 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st if (!trail.empty()) dep1 = m.mk_join(dep_exprs.size(), dep_exprs.data()); } - dependent_expr d(m, g, m.mk_join(dep1, dep2)); + dependent_expr d(m, g, nullptr, m.mk_join(dep1, dep2)); CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n"); add_vars(d, free_vars); st.update(i, d); diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 7d698faa310..1083cc36d73 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -35,13 +35,13 @@ propagate_values::propagate_values(ast_manager& m, params_ref const& p, dependen void propagate_values::process_fml(unsigned i) { if (!m_subst.empty()) { - auto [f, dep] = m_fmls[i](); + auto [f, p, dep] = m_fmls[i](); expr_ref fml(m); proof_ref pr(m); m_rewriter(f, fml, pr); if (fml != f) { dep = m.mk_join(dep, m_rewriter.get_used_dependencies()); - m_fmls.update(i, dependent_expr(m, fml, dep)); + m_fmls.update(i, dependent_expr(m, fml, mp(p, pr), dep)); ++m_stats.m_num_rewrites; } m_rewriter.reset_used_dependencies(); @@ -51,7 +51,7 @@ void propagate_values::process_fml(unsigned i) { void propagate_values::add_sub(dependent_expr const& de) { expr* x, * y; - auto const& [f, dep] = de(); + auto const& [f, p, dep] = de(); if (m.is_not(f, x) && m_shared.is_shared(x)) m_subst.insert(x, m.mk_false(), dep); if (m_shared.is_shared(f)) diff --git a/src/ast/simplifiers/pull_nested_quantifiers.h b/src/ast/simplifiers/pull_nested_quantifiers.h index a113c36c2f4..fac605d4971 100644 --- a/src/ast/simplifiers/pull_nested_quantifiers.h +++ b/src/ast/simplifiers/pull_nested_quantifiers.h @@ -40,7 +40,9 @@ class pull_nested_quantifiers_simplifier : public dependent_expr_simplifier { for (unsigned idx : indices()) { auto d = m_fmls[idx]; m_pull(d.fml(), new_curr, new_pr); - m_fmls.update(idx, dependent_expr(m, new_curr, d.dep())); + m_fmls.update(idx, dependent_expr(m, new_curr, mp(d.pr(), new_pr), d.dep())); } } + + bool supports_proofs() const override { return true; } }; diff --git a/src/ast/simplifiers/push_ite.h b/src/ast/simplifiers/push_ite.h index e26070a7eda..8e508bf5344 100644 --- a/src/ast/simplifiers/push_ite.h +++ b/src/ast/simplifiers/push_ite.h @@ -35,7 +35,8 @@ class push_ite_simplifier : public dependent_expr_simplifier { for (unsigned idx : indices()) { auto const& d = m_fmls[idx]; m_push(d.fml(), r); - m_fmls.update(idx, dependent_expr(m, r, d.dep())); + if (r != d.fml()) + m_fmls.update(idx, dependent_expr(m, r, nullptr, d.dep())); } } }; @@ -58,7 +59,7 @@ class ng_push_ite_simplifier : public dependent_expr_simplifier { for (unsigned idx : indices()) { auto const& d = m_fmls[idx]; m_push(d.fml(), r); - m_fmls.update(idx, dependent_expr(m, r, d.dep())); + m_fmls.update(idx, dependent_expr(m, r, nullptr, d.dep())); } } }; diff --git a/src/ast/simplifiers/refine_inj_axiom.h b/src/ast/simplifiers/refine_inj_axiom.h index 2333ba690df..53c96081270 100644 --- a/src/ast/simplifiers/refine_inj_axiom.h +++ b/src/ast/simplifiers/refine_inj_axiom.h @@ -38,7 +38,7 @@ class refine_inj_axiom_simplifier : public dependent_expr_simplifier { for (unsigned idx : indices()) { expr* f = m_fmls[idx].fml(); if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), r)) - m_fmls.update(idx, dependent_expr(m, r, m_fmls[idx].dep())); + m_fmls.update(idx, dependent_expr(m, r, nullptr, m_fmls[idx].dep())); } } }; diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h index be54ca005e4..f3dc91a51f3 100644 --- a/src/ast/simplifiers/rewriter_simplifier.h +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -44,9 +44,10 @@ class rewriter_simplifier : public dependent_expr_simplifier { auto d = m_fmls[idx]; m_rewriter(d.fml(), new_curr, new_pr); m_num_steps += m_rewriter.get_num_steps(); - m_fmls.update(idx, dependent_expr(m, new_curr, d.dep())); + m_fmls.update(idx, dependent_expr(m, new_curr, mp(d.pr(), new_pr), d.dep())); } } + bool supports_proofs() const override { return true; } void collect_statistics(statistics& st) const override { st.update("simplifier-steps", m_num_steps); } void reset_statistics() override { m_num_steps = 0; } void updt_params(params_ref const& p) override { m_params.append(p); m_rewriter.updt_params(m_params); } diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index 6cb38e4a686..b56802caf8d 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -282,7 +282,7 @@ namespace euf { else if (!s && 1 <= depth) { 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->get_eqs(dependent_expr(m, f, nullptr, df.dep()), eqs); ex->set_allow_booleans(true); } } diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 94b089c809f..e481dad8dee 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -191,14 +191,15 @@ namespace euf { rp->set_substitution(m_subst.get()); for (unsigned i : indices()) { - auto [f, d] = m_fmls[i](); + auto [f, p, d] = m_fmls[i](); auto [new_f, new_dep] = rp->replace_with_dep(f); - m_rewriter(new_f); + proof_ref new_pr(m); + m_rewriter(new_f, new_f, new_pr); 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)); + m_fmls.update(i, dependent_expr(m, new_f, mp(p, new_pr), new_dep)); } } diff --git a/src/qe/lite/qe_lite.cpp b/src/qe/lite/qe_lite.cpp index d3d45bd4b1e..a052ae94435 100644 --- a/src/qe/lite/qe_lite.cpp +++ b/src/qe/lite/qe_lite.cpp @@ -2431,12 +2431,13 @@ namespace { proof_ref new_pr(m); expr_ref new_f(m); for (unsigned i : indices()) { - expr* f = m_fmls[i].fml(); + auto [f, p, d] = m_fmls[i](); if (!has_quantifiers(f)) continue; new_f = f; m_qe(new_f, new_pr); - m_fmls.update(i, dependent_expr(m, new_f, m_fmls[i].dep())); + if (f != new_f) + m_fmls.update(i, dependent_expr(m, new_f, mp(p, new_pr), d)); } } }; diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 3fd3a008030..e37d513a02e 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -86,7 +86,7 @@ class sat_smt_solver : public solver { if (s.m.is_and(f)) { auto* d = s.m_fmls[i].dep(); for (expr* arg : *to_app(f)) - s.m_fmls.push_back(dependent_expr(s.m, arg, d)); + s.m_fmls.push_back(dependent_expr(s.m, arg, nullptr, d)); continue; } if (i != j) @@ -349,18 +349,18 @@ class sat_smt_solver : public solver { return a; expr* new_dep = m.mk_fresh_const("dep", m.mk_bool_sort()); expr* fml = m.mk_iff(new_dep, a); - m_fmls.push_back(dependent_expr(m, fml, nullptr)); + m_fmls.push_back(dependent_expr(m, fml, nullptr, nullptr)); m_dep.insert(a, new_dep); return new_dep; } void assert_expr_core2(expr * t, expr * a) override { a = ensure_literal(a); - m_fmls.push_back(dependent_expr(m, t, m.mk_leaf(a))); + m_fmls.push_back(dependent_expr(m, t, nullptr, m.mk_leaf(a))); } void assert_expr_core(expr * t) override { - m_fmls.push_back(dependent_expr(m, t, nullptr)); + m_fmls.push_back(dependent_expr(m, t, nullptr, nullptr)); } ast_manager& get_manager() const override { return m; } diff --git a/src/shell/main.cpp b/src/shell/main.cpp index af3b22db06e..2fdd95d79e8 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -293,11 +293,12 @@ static void parse_cmd_line_args(std::string& input_file, int argc, char ** argv) if (!opt_arg) help_tactics(); else - help_tactic(opt_arg); + help_tactic(opt_arg, false); } - else if (strcmp(opt_name, "probes") == 0) { + else if (strcmp(opt_name, "tacticsmd") == 0 && opt_arg) + help_tactic(opt_arg, true); + else if (strcmp(opt_name, "probes") == 0) help_probes(); - } else { std::cerr << "Error: invalid command line option: " << arg << "\n"; std::cerr << "For usage information: z3 -h\n"; diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 220001b4044..698f0f23962 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -88,14 +88,17 @@ void help_tactics() { std::cout << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n"; } -void help_tactic(char const* name) { +void help_tactic(char const* name, bool markdown) { cmd_context ctx; for (auto cmd : ctx.tactics()) { if (cmd->get_name() == name) { tactic_ref t = cmd->mk(ctx.m()); param_descrs descrs; t->collect_param_descrs(descrs); - descrs.display(std::cout, 4); + if (markdown) + descrs.display_markdown(std::cout); + else + descrs.display(std::cout, 4); } } } diff --git a/src/shell/smtlib_frontend.h b/src/shell/smtlib_frontend.h index 04f35c5c847..9769472f263 100644 --- a/src/shell/smtlib_frontend.h +++ b/src/shell/smtlib_frontend.h @@ -22,6 +22,6 @@ unsigned read_smtlib_file(char const * benchmark_file); unsigned read_smtlib2_commands(char const * command_file); void help_tactics(); void help_probes(); -void help_tactic(char const* name); +void help_tactic(char const* name, bool markdown); diff --git a/src/tactic/core/injectivity_tactic.cpp b/src/tactic/core/injectivity_tactic.cpp index dfcb152a254..e4071628cdf 100644 --- a/src/tactic/core/injectivity_tactic.cpp +++ b/src/tactic/core/injectivity_tactic.cpp @@ -5,19 +5,11 @@ Module Name: injectivity_tactic.cpp -Abstract: - - Injectivity tactics - - Discover axioms of the form `forall x. (= (g (f x)) x` - Mark `f` as injective - - Rewrite (sub)terms of the form `(= (f x) (f y))` to `(= x y)` whenever `f` is injective. Author: Nicolas Braud-Santoni (t-nibrau) 2017-08-10 -Notes: - --*/ #include #include @@ -164,8 +156,6 @@ class injectivity_tactic : public tactic { struct rewriter_eq_cfg : public default_rewriter_cfg { ast_manager & m_manager; InjHelper & inj_map; -// expr_ref_vector m_out; -// sort_ref_vector m_bindings; ast_manager & m() const { return m_manager; } @@ -176,14 +166,13 @@ class injectivity_tactic : public tactic { } void cleanup_buffers() { -// m_out.finalize(); } void reset() { } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - if(num != 2) + if (num != 2) return BR_FAILED; if (!m().is_eq(f)) @@ -230,8 +219,6 @@ class injectivity_tactic : public tactic { finder * m_finder; rewriter_eq * m_eq; InjHelper * m_map; -// rewriter_inverse * m_inverse; - params_ref m_params; ast_manager & m_manager; diff --git a/src/tactic/core/injectivity_tactic.h b/src/tactic/core/injectivity_tactic.h index e23f8216309..78310909a8b 100644 --- a/src/tactic/core/injectivity_tactic.h +++ b/src/tactic/core/injectivity_tactic.h @@ -13,7 +13,33 @@ Module Name: Nicolas Braud-Santoni (t-nibrau) 2017-08-10 -Notes: + +Tactic Documentation: + +## Tactic injectivity + +### Short Description: + +- Discover axioms of the form `forall x. (= (g (f x)) x` + Mark `f` as injective + +- Rewrite (sub)terms of the form `(= (f x) (f y))` to `(= x y)` whenever `f` is injective. + +### Example + +```z3 + (declare-fun f (Int) Int) + (declare-fun g (Int) Int) + (declare-const x Int) + (declare-const y Int) + (assert (forall ((x Int)) (= (g (f x)) x))) + (assert (not (= (f x) (f (f y))))) + (apply injectivity) +``` + +### Notes + +* does not support cores nor proofs --*/ #pragma once diff --git a/src/tactic/core/reduce_args_tactic.h b/src/tactic/core/reduce_args_tactic.h index 5fa1f74cda8..615b4d70fc8 100644 --- a/src/tactic/core/reduce_args_tactic.h +++ b/src/tactic/core/reduce_args_tactic.h @@ -23,15 +23,15 @@ Reduce the number of arguments of function applications, when for all occurrence ### Long Description -Example, suppose we have a function `f` with `2` arguments. -There are 1000 applications of this function, but the first argument is always "a", "b" or "c". -Thus, we replace the `f(t1, t2)` with +Example, suppose we have a function $f$ with 2 arguments. +There are 1000 applications of this function, but the first argument is always $a$, $b$ or $c$. +Thus, we replace the $f(t_1, t_2)$ with -* `f_a(t2)` if `t1 = a` -* `f_b(t2)` if `t2 = b` -* `f_c(t2)` if `t2 = c` +* $f_a(t_2)$ if $t_1 = a$ +* $f_b(t_2)$ if $t_2 = b$ +* $f_c(t_2)$ if $t_2 = c$ -Since `f_a`, `f_b`, `f_c` are new symbols, satisfiability is preserved. +Since $f_a$, $f_b$, $f_c$ are new symbols, satisfiability is preserved. This transformation is very similar in spirit to the Ackermman's reduction. diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index f2d53236840..9da9c1965e0 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -49,7 +49,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { m(m), m_params(p), m_factory(f), - m_dep(m, m.mk_true(), nullptr) + m_dep(m, m.mk_true(), nullptr, nullptr) {} /** @@ -58,22 +58,22 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { unsigned qtail() 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)); + m_dep = dependent_expr(m, m_goal->form(i), m_goal->pr(i), m_goal->dep(i)); return m_dep; } void update(unsigned i, dependent_expr const& j) override { if (inconsistent()) return; - auto [f, d] = j(); - m_goal->update(i, f, nullptr, d); + auto [f, p, d] = j(); + m_goal->update(i, f, p, d); } void add(dependent_expr const& j) override { if (inconsistent()) return; - auto [f, d] = j(); - m_goal->assert_expr(f, nullptr, d); + auto [f, p, d] = j(); + m_goal->assert_expr(f, p, d); } bool inconsistent() override { @@ -108,7 +108,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { tactic_report report(name(), *in); m_goal = in.get(); try { - if (!in->proofs_enabled()) + if (!in->proofs_enabled() || m_simp->supports_proofs()) m_simp->reduce(); if (m.inc()) advance_qhead(); From c33e58ee1a4e482319de25a06c651dde75ed3ac6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 17:59:33 -0800 Subject: [PATCH 184/597] update distribute forall Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/distribute_forall.h | 26 ++-- src/tactic/core/CMakeLists.txt | 1 - src/tactic/core/distribute_forall_tactic.cpp | 141 ------------------- src/tactic/core/distribute_forall_tactic.h | 32 ++++- 5 files changed, 39 insertions(+), 162 deletions(-) delete mode 100644 src/tactic/core/distribute_forall_tactic.cpp diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index df44427cfa9..b807b00e384 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -5,6 +5,7 @@ z3_add_component(simplifiers card2bv.cpp demodulator_simplifier.cpp dependent_expr_state.cpp + distribute_forall.cpp elim_unconstrained.cpp eliminate_predicates.cpp euf_completion.cpp diff --git a/src/ast/simplifiers/distribute_forall.h b/src/ast/simplifiers/distribute_forall.h index 3127a4ec87a..d5c511ab1e7 100644 --- a/src/ast/simplifiers/distribute_forall.h +++ b/src/ast/simplifiers/distribute_forall.h @@ -15,31 +15,23 @@ Module Name: #pragma once #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/rewriter/distribute_forall.h" class distribute_forall_simplifier : public dependent_expr_simplifier { - distribute_forall m_dist; + + struct rw_cfg; + struct rw; public: + distribute_forall_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): - dependent_expr_simplifier(m, fmls), - m_dist(m) { + dependent_expr_simplifier(m, fmls) { } char const* name() const override { return "distribute-forall"; } - - void reduce() override { - if (!m_fmls.has_quantifiers()) - return; - expr_ref r(m); - for (unsigned idx : indices()) { - auto const& d = m_fmls[idx]; - if (!has_quantifiers(d.fml())) - continue; - m_dist(d.fml(), r); - m_fmls.update(idx, dependent_expr(m, r, nullptr, d.dep())); - } - } + + bool supports_proofs() const override { return true; } + + void reduce() override; }; diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index 8a86e9a0b38..d4a115dcc24 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -6,7 +6,6 @@ z3_add_component(core_tactics collect_statistics_tactic.cpp ctx_simplify_tactic.cpp der_tactic.cpp - distribute_forall_tactic.cpp dom_simplify_tactic.cpp elim_term_ite_tactic.cpp elim_uncnstr_tactic.cpp diff --git a/src/tactic/core/distribute_forall_tactic.cpp b/src/tactic/core/distribute_forall_tactic.cpp deleted file mode 100644 index 1d171aae3fa..00000000000 --- a/src/tactic/core/distribute_forall_tactic.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - distribute_forall_tactic.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2012-02-18. - ---*/ -#include "tactic/tactical.h" -#include "ast/ast_util.h" -#include "ast/rewriter/rewriter_def.h" -#include "ast/rewriter/var_subst.h" - -class distribute_forall_tactic : public tactic { - - struct rw_cfg : public default_rewriter_cfg { - ast_manager & m; - - rw_cfg(ast_manager & _m):m(_m) {} - bool reduce_quantifier(quantifier * old_q, - expr * new_body, - expr * const * new_patterns, - expr * const * new_no_patterns, - expr_ref & result, - proof_ref & result_pr) { - - if (!is_forall(old_q)) { - return false; - } - - if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) { - // (forall X (not (or F1 ... Fn))) - // --> - // (and (forall X (not F1)) - // ... - // (forall X (not Fn))) - app * or_e = to_app(to_app(new_body)->get_arg(0)); - unsigned num_args = or_e->get_num_args(); - expr_ref_buffer new_args(m); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = or_e->get_arg(i); - expr * not_arg = mk_not(m, arg); - quantifier_ref tmp_q(m); - tmp_q = m.update_quantifier(old_q, not_arg); - new_args.push_back(elim_unused_vars(m, tmp_q, params_ref())); - } - result = m.mk_and(new_args.size(), new_args.data()); - if (m.proofs_enabled()) { - result_pr = m.mk_push_quant(old_q, result); - } - return true; - } - - if (m.is_and(new_body)) { - // (forall X (and F1 ... Fn)) - // --> - // (and (forall X F1) - // ... - // (forall X Fn) - unsigned num_args = to_app(new_body)->get_num_args(); - expr_ref_buffer new_args(m); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(new_body)->get_arg(i); - quantifier_ref tmp_q(m); - tmp_q = m.update_quantifier(old_q, arg); - new_args.push_back(elim_unused_vars(m, tmp_q, params_ref())); - } - result = m.mk_and(new_args.size(), new_args.data()); - if (m.proofs_enabled()) { - result_pr = m.mk_push_quant(old_q, result); - } - return true; - } - - return false; - } - }; - - struct rw : public rewriter_tpl { - rw_cfg m_cfg; - - rw(ast_manager & m, bool proofs_enabled): - rewriter_tpl(m, proofs_enabled, m_cfg), - m_cfg(m) { - } - }; - - rw * m_rw; - -public: - distribute_forall_tactic():m_rw(nullptr) {} - - tactic * translate(ast_manager & m) override { - return alloc(distribute_forall_tactic); - } - - char const* name() const override { return "distribute_forall"; } - - void operator()(goal_ref const & g, - goal_ref_buffer & result) override { - ast_manager & m = g->m(); - bool produce_proofs = g->proofs_enabled(); - rw r(m, produce_proofs); - m_rw = &r; - result.reset(); - tactic_report report("distribute-forall", *g); - - expr_ref new_curr(m); - proof_ref new_pr(m); - unsigned size = g->size(); - for (unsigned idx = 0; idx < size; idx++) { - if (g->inconsistent()) - break; - expr * curr = g->form(idx); - r(curr, new_curr, new_pr); - if (g->proofs_enabled()) { - proof * pr = g->pr(idx); - new_pr = m.mk_modus_ponens(pr, new_pr); - } - g->update(idx, new_curr, new_pr, g->dep(idx)); - } - - g->inc_depth(); - result.push_back(g.get()); - m_rw = nullptr; - } - - void cleanup() override {} -}; - -tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) { - return alloc(distribute_forall_tactic); -} diff --git a/src/tactic/core/distribute_forall_tactic.h b/src/tactic/core/distribute_forall_tactic.h index d7a030500aa..328a62b2f01 100644 --- a/src/tactic/core/distribute_forall_tactic.h +++ b/src/tactic/core/distribute_forall_tactic.h @@ -13,14 +13,40 @@ Module Name: Leonardo de Moura (leonardo) 2012-02-18. +Tactic Documentation: + +## Tactic distribute-forall + +### Short Description: + +Distribute $\forall$ over conjunctions (and distribute $\exists$ over disjunctions) + +### Example + +```z3 + (declare-const x Int) + (declare-fun p (Int) Bool) + (declare-fun q (Int) Bool) + (assert (forall ((x Int)) (and (p x) (q x)))) + (apply distribute-forall) +``` + +### Notes + +* supports unsat cores, proof terms + + --*/ #pragma once #include "util/params.h" -class ast_manager; -class tactic; +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/distribute_forall.h" -tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p); +inline tactic * mk_distribute_forall_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(distribute_forall_simplifier, m, p, s); }); +} /* ADD_TACTIC("distribute-forall", "distribute forall over conjunctions.", "mk_distribute_forall_tactic(m, p)") From 7e69dab8f69747680d7af1d825bfc68a634afbd8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Dec 2022 18:15:18 -0800 Subject: [PATCH 185/597] distribute forall cpp code --- doc/mk_tactic_doc.py | 5 ++ src/ast/simplifiers/distribute_forall.cpp | 105 ++++++++++++++++++++++ src/util/params.cpp | 2 + 3 files changed, 112 insertions(+) create mode 100644 src/ast/simplifiers/distribute_forall.cpp diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index 85dfcb4c35d..37b77ef5283 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -19,12 +19,17 @@ def doc_path(path): is_doc_end = re.compile("\-\-\*\/") is_tac_name = re.compile("## Tactic (.*)") +def is_ws(s): + return all([0 for ch in s if ch != ' ' and ch != '\n']) + def extract_params(ous, tac): z3_exe = BUILD_DIR + "/z3" out = subprocess.Popen([z3_exe, f"-tacticsmd:{tac}"], stdout=subprocess.PIPE).communicate()[0] if not out: return out = out.decode(sys.stdout.encoding) + if is_ws(out): + return ous.write("### Parameters\n\n") for line in out: ous.write(line.replace("\r","")) diff --git a/src/ast/simplifiers/distribute_forall.cpp b/src/ast/simplifiers/distribute_forall.cpp new file mode 100644 index 00000000000..c7cc6659aad --- /dev/null +++ b/src/ast/simplifiers/distribute_forall.cpp @@ -0,0 +1,105 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + distribute_forall.cpp + +Author: + + Leonardo de Moura (leonardo) 2012-02-18. + Nikolaj Bjorner (nbjorner) 2022-11-24 + +--*/ + +#include "ast/ast_util.h" +#include "ast/rewriter/rewriter.h" +#include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/var_subst.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/simplifiers/distribute_forall.h" + +struct distribute_forall_simplifier::rw_cfg : public default_rewriter_cfg { + ast_manager & m; + + rw_cfg(ast_manager & m):m(m) {} + + bool reduce_quantifier(quantifier * old_q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + + quantifier_ref tmp_q(m); + expr_ref_vector es(m); + expr* f; + if (is_forall(old_q)) { + // (forall X (and F1 ... Fn)) + // --> + // (and (forall X F1) + // ... + // (forall X Fn) + + if (!m.is_and(new_body) && !(m.is_not(new_body, f) && (m.is_implies(f) || m.is_or(f)))) + return false; + flatten_and(new_body, es); + unsigned i = 0; + for (expr* arg : es) { + tmp_q = m.update_quantifier(old_q, arg); + es[i++] = elim_unused_vars(m, tmp_q, params_ref()); + } + result = mk_and(es); + if (m.proofs_enabled()) + result_pr = m.mk_push_quant(old_q, result); + return true; + } + if (is_exists(old_q)) { + // (exists X (or F1 ... Fn)) + // --> + // (or (exists X F1) + // ... + // (exists X Fn) + + if (!m.is_or(new_body) && !m.is_implies(new_body) && !(m.is_not(new_body, f) && m.is_and(f))) + return false; + flatten_or(new_body, es); + unsigned i = 0; + for (expr* arg : es) { + tmp_q = m.update_quantifier(old_q, arg); + es[i++] = elim_unused_vars(m, tmp_q, params_ref()); + } + result = mk_or(es); + if (m.proofs_enabled()) + result_pr = m.mk_push_quant(old_q, result); + return true; + } + return false; + } +}; + +struct distribute_forall_simplifier::rw : public rewriter_tpl { + rw_cfg m_cfg; + + rw(ast_manager & m, bool proofs_enabled): + rewriter_tpl(m, proofs_enabled, m_cfg), + m_cfg(m) { + } +}; + +void distribute_forall_simplifier::reduce() { + if (!m_fmls.has_quantifiers()) + return; + rw rw(m, m.proofs_enabled()); + expr_ref r(m); + proof_ref pr(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + if (!has_quantifiers(d.fml())) + continue; + rw(d.fml(), r, pr); + if (r != d.fml()) + m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep())); + } +}; + diff --git a/src/util/params.cpp b/src/util/params.cpp index ee61bf47f46..c801b342146 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -167,6 +167,8 @@ struct param_descrs::imp { names.push_back(kv.m_key); } std::sort(names.begin(), names.end(), symlt()); + if (names.empty()) + return; if (markdown) { out << " Parameter | Type | Description | Default\n"; out << " ----------|------|-------------|--------\n"; From c45c40e782d6c8c95771cc9ec8944600c78de760 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 7 Dec 2022 08:51:18 -0800 Subject: [PATCH 186/597] doc Signed-off-by: Nikolaj Bjorner --- src/tactic/core/blast_term_ite_tactic.cpp | 2 - src/tactic/core/blast_term_ite_tactic.h | 38 +++++++++++++++---- src/tactic/core/ctx_simplify_tactic.h | 25 +++++++++++- src/tactic/core/elim_term_ite_tactic.h | 35 ++++++++++++++--- src/tactic/core/pb_preprocess_tactic.cpp | 32 ++++------------ src/tactic/core/pb_preprocess_tactic.h | 46 ++++++++++++++++++++++- src/tactic/core/tseitin_cnf_tactic.cpp | 39 +------------------ src/tactic/core/tseitin_cnf_tactic.h | 32 +++++++++++++++- 8 files changed, 167 insertions(+), 82 deletions(-) diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp index 49e43e6335d..987ab9f9075 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -13,8 +13,6 @@ Module Name: Nikolaj Bjorner (nbjorner) 2013-11-4 -Notes: - --*/ #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" diff --git a/src/tactic/core/blast_term_ite_tactic.h b/src/tactic/core/blast_term_ite_tactic.h index b5f643a1eca..a322b8e11df 100644 --- a/src/tactic/core/blast_term_ite_tactic.h +++ b/src/tactic/core/blast_term_ite_tactic.h @@ -4,20 +4,42 @@ Copyright (c) 2013 Microsoft Corporation Module Name: blast_term_ite_tactic.h - -Abstract: - - Blast term if-then-else by hoisting them up. - This is expensive but useful in some cases, such as - for enforcing constraints being in difference logic. - Use elim-term-ite elsewhere when possible. Author: Nikolaj Bjorner (nbjorner) 2013-11-4 -Notes: +Tactic Documentation: + +## Tactic blast-term-ite + +### Short Description: + +Blast term if-then-else by hoisting them up. +This is expensive but useful in some cases, such as +for enforcing constraints being in difference logic. +Use `elim-term-ite` elsewhere when possible. + +### Example + +```z3 +(declare-fun f (Int) Int) +(declare-fun p (Int) Bool) +(declare-const c1 Bool) +(declare-const c2 Bool) +(declare-const c3 Bool) +(declare-const e1 Int) +(declare-const e2 Int) +(declare-const e3 Int) +(declare-const e4 Int) +(assert (p (f (if c1 (if c2 e1 (if c3 e2 e3)) e4)))) +(apply blast-term-ite) +``` + +### Notes + + --*/ #pragma once diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index c8e34f33d08..213f01f6234 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -13,7 +13,30 @@ Module Name: Leonardo (leonardo) 2011-10-26 -Notes: +Tactic Documentation: + +## Tactic ctx-simplify + +### Short Description: + +The tactic performs simplifies sub-formulas using context built up by walking assertions and sub-formulas. + +### Example + +```z3 + (declare-const p Bool) + (declare-const q Bool) + (declare-const r Bool) + (declare-fun f (Bool) Bool) + (assert p) + (assert (or (f p) (and r (or (not r) q)))) + (apply ctx-simplify) +``` + +### Notes + +* supports proof terms with limited features + --*/ #pragma once diff --git a/src/tactic/core/elim_term_ite_tactic.h b/src/tactic/core/elim_term_ite_tactic.h index 8fa9f90319c..ca8d3d43e38 100644 --- a/src/tactic/core/elim_term_ite_tactic.h +++ b/src/tactic/core/elim_term_ite_tactic.h @@ -5,16 +5,39 @@ Module Name: elim_term_ite_tactic.h -Abstract: - - Eliminate term if-then-else by adding - new fresh auxiliary variables. - Author: Leonardo (leonardo) 2011-12-29 -Notes: +Tactic Documentation: + +## Tactic elim-term-ite + +### Short Description: + +Eliminate term if-then-else by adding +new fresh auxiliary variables. + + +### Example + +```z3 +(declare-fun f (Int) Int) +(declare-fun p (Int) Bool) +(declare-const c1 Bool) +(declare-const c2 Bool) +(declare-const c3 Bool) +(declare-const e1 Int) +(declare-const e2 Int) +(declare-const e3 Int) +(declare-const e4 Int) +(assert (p (f (if c1 (if c2 e1 (if c3 e2 e3)) e4)))) +(apply elim-term-ite) +``` + +### Notes + +* supports proof terms and unsat cores --*/ #pragma once diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index 9f071713504..05ed6eee903 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -14,22 +14,6 @@ Module Name: Nikolaj Bjorner (nbjorner) 2013-12-23 -Notes: - - Resolution for PB constraints require the implicit - inequalities that each variable ranges over [0,1] - so not all resolvents produce smaller sets of clauses. - - We here implement subsumption resolution. - - x + y >= 1 - A~x + B~y + Cz >= k - --------------------- - Cz >= k - B - - where A <= B, x, y do not occur elsewhere. - - --*/ #include "tactic/core/pb_preprocess_tactic.h" #include "tactic/tactical.h" @@ -106,22 +90,20 @@ class pb_preprocess_tactic : public tactic { return alloc(pb_preprocess_tactic, m); } - char const* name() const override { return "pb_preprocess"; } + char const* name() const override { return "pb-preprocess"; } void operator()( goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("pb-preprocess", *g); - if (g->proofs_enabled()) { - throw tactic_exception("pb-preprocess does not support proofs"); - } - - generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess"); - g->inc_depth(); result.push_back(g.get()); - while (simplify(g, *pp)); - g->add(pp); + + if (!g->proofs_enabled()) { + generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess"); + while (simplify(g, *pp)); + g->add(pp); + } // decompose(g); } diff --git a/src/tactic/core/pb_preprocess_tactic.h b/src/tactic/core/pb_preprocess_tactic.h index ec387e6e0fa..83e8723f631 100644 --- a/src/tactic/core/pb_preprocess_tactic.h +++ b/src/tactic/core/pb_preprocess_tactic.h @@ -14,7 +14,51 @@ Module Name: Nikolaj Bjorner (nbjorner) 2013-12-23 -Notes: +Documentation: + +## Tactic pb-preprocess + +### Short Description: + +The tactic eliminates variables from pseudo-Boolean inequalities and performs algebraic simplifcations on formulas + +### Long Description + +Resolution for PB constraints require the implicit +inequalities that each variable ranges over [0,1] +so not all resolvents produce smaller sets of clauses. + +We here implement subsumption resolution. + +``` + x + y >= 1 + A~x + B~y + Cz >= k + --------------------- + Cz >= k - B +``` + +where `A <= B` and `x, y` do not occur elsewhere. + + +### Example + +```z3 + (declare-const x Bool) + (declare-const y Bool) + (declare-const z Bool) + (declare-const u Bool) + (declare-const v Bool) + (assert ((_ pbge 1 1 1 2) (not x) (not y) (not z))) + (assert ((_ pbge 1 1 1 2) x u v)) + (assert (not (and y v))) + (assert (not (and z u))) + (apply pb-preprocess) +``` + +### Notes + +* supports unsat cores +* does not support proof terms --*/ #pragma once diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index e4476548a0c..667d53626b9 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -5,49 +5,14 @@ Module Name: tseitin_cnf_tactic.cpp -Abstract: - - Puts an assertion set in CNF. - Auxiliary variables are used to avoid blowup. - - Features: - - - Efficient encoding is used for commonly used patterns such as: - (iff a (iff b c)) - (or (not (or a b)) (not (or a c)) (not (or b c))) - - - Efficient encoding is used for chains of if-then-elses - - - Distributivity is applied to non-shared nodes if the blowup is acceptable. - - - The features above can be disabled/enabled using parameters. - - - The assertion-set is only modified if the resultant set of clauses - is "acceptable". - - Notes: - - - Term-if-then-else expressions are not handled by this strategy. - This kind of expression should be processed by other strategies. - - - Quantifiers are treated as "theory" atoms. They are viewed - as propositional variables by this strategy. - - - The assertion set may contain free variables. - - - This strategy assumes the assertion_set_rewriter was - used before invoking it. - In particular, it is more effective when "and" operators - were eliminated. - - TODO: add proof production - Author: Leonardo (leonardo) 2011-12-29 Notes: + TODO: add proof production + --*/ #include "ast/ast_pp.h" #include "tactic/tactical.h" diff --git a/src/tactic/core/tseitin_cnf_tactic.h b/src/tactic/core/tseitin_cnf_tactic.h index 363a1fafc23..a5e116825d9 100644 --- a/src/tactic/core/tseitin_cnf_tactic.h +++ b/src/tactic/core/tseitin_cnf_tactic.h @@ -10,12 +10,40 @@ Module Name: Puts an assertion set in CNF. Auxiliary variables are used to avoid blowup. + Features: + + - Efficient encoding is used for commonly used patterns such as: + (iff a (iff b c)) + (or (not (or a b)) (not (or a c)) (not (or b c))) + + - Efficient encoding is used for chains of if-then-elses + + - Distributivity is applied to non-shared nodes if the blowup is acceptable. + + - The features above can be disabled/enabled using parameters. + + - The assertion-set is only modified if the resultant set of clauses + is "acceptable". + + Notes: + + - Term-if-then-else expressions are not handled by this strategy. + This kind of expression should be processed by other strategies. + + - Quantifiers are treated as "theory" atoms. They are viewed + as propositional variables by this strategy. + + - The assertion set may contain free variables. + + - This strategy assumes the assertion_set_rewriter was + used before invoking it. + In particular, it is more effective when "and" operators + were eliminated. + Author: Leonardo (leonardo) 2011-12-29 -Notes: - --*/ #pragma once From 4a451b10d8ba9f8186789048b08379e19776a4d0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 7 Dec 2022 09:07:13 -0800 Subject: [PATCH 187/597] add custom coercion for floats. fix #6482 Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 05df8186eed..72a3cd05f13 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1194,7 +1194,7 @@ def _coerce_expr_merge(s, a): else: if z3_debug(): _z3_assert(s1.ctx == s.ctx, "context mismatch") - _z3_assert(False, "sort mismatch") + _z3_assert(False, "sort mismatch") else: return s @@ -1207,6 +1207,11 @@ def _coerce_exprs(a, b, ctx=None): a = StringVal(a, b.ctx) if isinstance(b, str) and isinstance(a, SeqRef): b = StringVal(b, a.ctx) + if isinstance(a, float) and isinstance(b, ArithRef): + a = RealVal(a, b.ctx) + if isinstance(b, float) and isinstance(a, ArithRef): + b = RealVal(b, a.ctx) + s = None s = _coerce_expr_merge(s, a) s = _coerce_expr_merge(s, b) From 8981d32caf488cd4df4ed6ec7f14c390eba8ddb3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 8 Dec 2022 07:06:27 -0800 Subject: [PATCH 188/597] #6481 --- src/math/lp/stacked_vector.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/math/lp/stacked_vector.h b/src/math/lp/stacked_vector.h index 61131955ada..ecd61eb10fb 100644 --- a/src/math/lp/stacked_vector.h +++ b/src/math/lp/stacked_vector.h @@ -55,6 +55,9 @@ template < typename B> class stacked_vector { bool operator==(B const& other) const { return m_vec.m_vector[m_i] == other; } + bool operator!=(B const& other) const { + return m_vec.m_vector[m_i] != other; + } B& operator+=(B const &delta) { // not tracking the change here! return m_vec.m_vector[m_i] += delta; From ca6fed8b25331cd6a472d3296b4b28b73c0c993b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 8 Dec 2022 18:20:46 +0000 Subject: [PATCH 189/597] minor code simplification --- src/qe/lite/qe_lite.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qe/lite/qe_lite.cpp b/src/qe/lite/qe_lite.cpp index a052ae94435..75c90ee4d41 100644 --- a/src/qe/lite/qe_lite.cpp +++ b/src/qe/lite/qe_lite.cpp @@ -2444,8 +2444,7 @@ namespace { } tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { - return alloc(dependent_expr_state_tactic, m, p, - [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(qe_lite_simplifier, m, p, s); }); + return alloc(dependent_expr_state_tactic, m, p, mk_qe_lite_simplifer); } dependent_expr_simplifier* mk_qe_lite_simplifer(ast_manager& m, params_ref const& p, dependent_expr_state& st) { From c6f9c09d70b7202ecb2b1492493e6d7bec392253 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 9 Dec 2022 11:34:53 +0000 Subject: [PATCH 190/597] cleanup more in dependent_expr_state_tactic to reduce mem consumption --- src/ast/simplifiers/dependent_expr.h | 1 - src/tactic/dependent_expr_state_tactic.h | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h index 55f8d8f4695..c1ba9dd2dac 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -32,7 +32,6 @@ class dependent_expr { m_fml(fml), m_proof(p), m_dep(d) { - SASSERT(fml); m.inc_ref(fml); m.inc_ref(d); m.inc_ref(p); diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 9da9c1965e0..f635acca1ae 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -49,7 +49,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { m(m), m_params(p), m_factory(f), - m_dep(m, m.mk_true(), nullptr, nullptr) + m_dep(m, nullptr, nullptr, nullptr) {} /** @@ -115,13 +115,13 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { } catch (rewriter_exception& ex) { throw tactic_exception(ex.msg()); - } + } m_goal->elim_true(); m_goal->elim_redundancies(); 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(); } @@ -130,6 +130,8 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { m_simp->collect_statistics(m_st); m_simp = nullptr; m_model_trail = nullptr; + m_goal = nullptr; + m_dep = dependent_expr(m, nullptr, nullptr, nullptr); } void collect_statistics(statistics & st) const override { From a96f5a9b425b6f5ba7e8ce1c1a75db6683c4bdc9 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 9 Dec 2022 11:59:39 +0000 Subject: [PATCH 191/597] fix overflow in mpz::bitwise_not --- src/util/mpz.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 9b0da70fcfb..c56ab166794 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1460,9 +1460,11 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { SASSERT(is_nonneg(a)); - if (is_small(a) && sz <= 63) { - int64_t mask = (static_cast(1) << sz) - static_cast(1); - set_i64(c, (~ i64(a)) & mask); + if (is_small(a) && sz <= 64) { + uint64_t v = ~get_uint64(a); + unsigned zero_out = 64 - sz; + v = (v >> zero_out) << zero_out; + set(c, v); } else { mpz a1, a2, m, tmp; From 96a2c0402657d2b836e23428a4c95c3ff8637070 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 9 Dec 2022 07:56:51 -0800 Subject: [PATCH 192/597] fix bug reported by Nuno qhead should not be changed after tactic execution. It should remain 0 so the same tactic can be applied repeatedly on the entire state --- src/tactic/dependent_expr_state_tactic.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index f635acca1ae..58507a850a8 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -110,8 +110,6 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { try { if (!in->proofs_enabled() || m_simp->supports_proofs()) m_simp->reduce(); - if (m.inc()) - advance_qhead(); } catch (rewriter_exception& ex) { throw tactic_exception(ex.msg()); From 9ebacd87e2ee8a79adfe128021fbfd444db7857a Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 9 Dec 2022 16:16:52 +0000 Subject: [PATCH 193/597] fix buggy mask (typo in my last commit..) --- src/util/mpz.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index c56ab166794..c3ba3016160 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1463,7 +1463,7 @@ void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { if (is_small(a) && sz <= 64) { uint64_t v = ~get_uint64(a); unsigned zero_out = 64 - sz; - v = (v >> zero_out) << zero_out; + v = (v << zero_out) >> zero_out; set(c, v); } else { From 1434c7d394d36ab53627b42d85dde50f8921a1cf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 9 Dec 2022 08:50:32 -0800 Subject: [PATCH 194/597] #6059 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 21 +++++++++++++++++++++ src/api/python/z3/z3.py | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 553e7128748..caf7ce07ec8 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1566,6 +1566,11 @@ namespace z3 { */ expr substitute(expr_vector const& dst); + /** + \brief Apply function substitution by macro definitions. + */ + expr substitute(func_decl_vector const& funs, expr_vector const& bodies); + class iterator { expr& e; @@ -4059,6 +4064,22 @@ namespace z3 { return expr(ctx(), r); } + inline expr expr::substitute(func_decl_vector const& funs, expr_vector const& dst) { + array _dst(dst.size()); + array _funs(funs.size()); + if (dst.size() != funs.size()) { + Z3_THROW(exception("length of argument lists don't align")); + return expr(ctx(), nullptr); + } + for (unsigned i = 0; i < dst.size(); ++i) { + _dst[i] = dst[i]; + _funs[i] = funs[i]; + } + Z3_ast r = Z3_substitute_funs(ctx(), m_ast, dst.size(), _funs.ptr(), _dst.ptr()); + check_error(); + return expr(ctx(), r); + } + typedef std::function on_clause_eh_t; class on_clause { diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 72a3cd05f13..c97cd2124c3 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -8837,7 +8837,7 @@ def substitute_vars(t, *m): return _to_expr_ref(Z3_substitute_vars(t.ctx.ref(), t.as_ast(), num, _to), t.ctx) def substitute_funs(t, *m): - """Apply subistitution m on t, m is a list of pairs of a function and expression (from, to) + """Apply substitution m on t, m is a list of pairs of a function and expression (from, to) Every occurrence in to of the function from is replaced with the expression to. The expression to can have free variables, that refer to the arguments of from. For examples, see From a302c2f15ee0e4d24ce9036050fffd8e827fe053 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 11 Dec 2022 15:21:23 +0000 Subject: [PATCH 195/597] fix crashes in elim-uncnstr2 This would crash before: (declare-fun x () (_ BitVec 4)) (assert (not (bvule x #x1))) (apply elim-uncnstr2) That's because the index_set iterator was querying qtail to compute the end of the iteration But the problem is that elim-uncnstr2 may add new fmls to the goal, as in this case. The bvule is replaced with an 'or', but since it's negated, it turns into 2 goals Solve the issue by freezing the qtail for the iteration loop. This is the right behavior for elim-uncnstr2, as it can't rewrite exprs that haven't been analyzed before @NikolajBjorner please check if this the right behavior for the other simplifiers. Thank you --- src/ast/simplifiers/dependent_expr_state.h | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 85b6352ad25..f9faee5494a 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -116,20 +116,17 @@ class dependent_expr_simplifier { unsigned qtail() const { return m_fmls.qtail(); } struct iterator { dependent_expr_simplifier& s; - unsigned m_index = 0; - bool at_end = false; - unsigned index() const { return at_end ? s.qtail() : std::min(m_index, s.qtail()); } - iterator(dependent_expr_simplifier& s, unsigned i) : s(s), m_index(i), at_end(i == s.qtail()) {} - bool operator==(iterator const& other) const { return index() == other.index(); } - bool operator!=(iterator const& other) const { return !(*this == other); } - iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent()) at_end = true; else ++m_index; return *this; } + unsigned m_index, m_end; + iterator(dependent_expr_simplifier& s, unsigned i, unsigned end) : s(s), m_index(i), m_end(end) {} + bool operator!=(iterator const& other) const { return m_index != other.m_index; } + iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent()) m_index = m_end; else ++m_index; return *this; } unsigned operator*() const { return m_index; } }; struct index_set { dependent_expr_simplifier& s; - iterator begin() { return iterator(s, s.qhead()); } - iterator end() { return iterator(s, s.qtail()); } + iterator begin() { return iterator(s, s.qhead(), s.qtail()); } + iterator end() { return iterator(s, s.qtail(), s.qtail()); } index_set(dependent_expr_simplifier& s) : s(s) {} }; From 527fb18366f5b028b6144d0f84df43767cc45985 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Dec 2022 09:51:30 -0800 Subject: [PATCH 196/597] add doc for card2bv --- src/ast/rewriter/pb2bv_rewriter.cpp | 8 ++-- src/tactic/arith/arith_bounds_tactic.cpp | 2 +- src/tactic/arith/bound_manager.cpp | 11 +++--- src/tactic/arith/card2bv_tactic.h | 49 ++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp index 5e4e2aa2891..d3dd8ae7677 100644 --- a/src/ast/rewriter/pb2bv_rewriter.cpp +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -200,7 +200,7 @@ struct pb2bv_rewriter::imp { } if (m_pb_solver == "segmented") { - throw default_exception("segmented encoding is disabled, use a different value for pb.solver"); + throw default_exception("segmented encoding is disabled, use a different value for pb.solver"); switch (is_le) { case l_true: return mk_seg_le(k); case l_false: return mk_seg_ge(k); @@ -1077,9 +1077,9 @@ struct pb2bv_rewriter::imp { } void collect_param_descrs(param_descrs& r) const { - r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: false) retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver"); - r.insert("pb.solver", CPK_SYMBOL, "(default: solver) retain pb constraints (don't bit-blast them) and use built-in pb solver"); - r.insert("cardinality.encoding", CPK_SYMBOL, "(default: none) grouped, bimander, ordered, unate, circuit"); + r.insert("keep_cardinality_constraints", CPK_BOOL, "retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver", "false"); + r.insert("pb.solver", CPK_SYMBOL, "encoding used for Pseudo-Boolean constraints: totalizer, sorting, binary_merge, bv, solver. PB constraints are retained if set to 'solver'", "solver"); + r.insert("cardinality.encoding", CPK_SYMBOL, "encoding used for cardinality constraints: grouped, bimander, ordered, unate, circuit", "none"); } unsigned get_num_steps() const { return m_rw.get_num_steps(); } diff --git a/src/tactic/arith/arith_bounds_tactic.cpp b/src/tactic/arith/arith_bounds_tactic.cpp index 87308078a6d..9a57b9eca92 100644 --- a/src/tactic/arith/arith_bounds_tactic.cpp +++ b/src/tactic/arith/arith_bounds_tactic.cpp @@ -61,7 +61,7 @@ struct arith_bounds_tactic : public tactic { return true; } if ((!is_negated && (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1))) || - (is_negated && (a.is_le(e, e2, e1) || a.is_ge(e, e1, e2)))) { + (is_negated && (a.is_le(e, e2, e1) || a.is_ge(e, e1, e2)))) { is_strict = true; return true; } diff --git a/src/tactic/arith/bound_manager.cpp b/src/tactic/arith/bound_manager.cpp index ef8ca3fcbfc..d6422abff69 100644 --- a/src/tactic/arith/bound_manager.cpp +++ b/src/tactic/arith/bound_manager.cpp @@ -262,13 +262,12 @@ void bound_manager::reset() { } bool bound_manager::inconsistent() const { - for (auto const& kv : m_lowers) { - limit const& lim1 = kv.m_value; + for (auto const& [k,v] : m_lowers) { + limit const& lim1 = v; limit lim2; - if (m_uppers.find(kv.m_key, lim2)) { - if (lim1.first > lim2.first) { - return true; - } + if (m_uppers.find(k, lim2)) { + if (lim1.first > lim2.first) + return true; if (lim1.first == lim2.first && !lim1.second && lim2.second) { return true; diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index d7ad76e7d30..d2832651493 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -5,14 +5,55 @@ Module Name: card2bv_tactic.cpp -Abstract: - - Tactic for converting Pseudo-Boolean constraints to BV - Author: Nikolaj Bjorner (nbjorner) 2014-03-20 +Tactic Documentation: + +## Tactic car2bv + +### Short Description + +Tactic for converting Pseudo-Boolean constraints to bit-vectors. + +### Long Description + +The tactic implements a set of standard methods for converting cardinality and Pseudo-Boolean constraints into bit-vector or propositional formulas +(using basic logical connectives, conjunction, disjunction, negation). The conversions from cardinality constraints are controlled +separately from the conversions from Pseudo-Boolean constraints using different parameters. + +### Example + +```z3 +(declare-const a1 Bool) +(declare-const a2 Bool) +(declare-const a3 Bool) +(declare-const a4 Bool) +(declare-const a5 Bool) +(declare-const a6 Bool) +(push) +(assert ((_ at-most 1) a1 a2 a3 a4 a5 a6)) +(assert ((_ at-most 2) a1 a2 a3 a4 a5 a6)) +(apply (with card2bv :cardinality.encoding unate)) +(apply (with card2bv :cardinality.encoding circuit)) +(apply (with card2bv :cardinality.encoding ordered)) +(apply (with card2bv :cardinality.encoding grouped)) +(apply (with card2bv :cardinality.encoding bimander)) +(pop) +(assert ((_ pbge 5 2 3 4 4 3 5) a1 a2 a3 a4 a5 a6)) +(apply (with card2bv :pb.solver totalizer)) +(apply (with card2bv :pb.solver sorting)) +(apply (with card2bv :pb.solver binary_merge)) +(apply (with card2bv :pb.solver bv)) +(apply (with card2bv :pb.solver solver)) +``` + +### Notes + +* supports cores +* does not support proofs + --*/ #pragma once From a9f52b0069bf31d316f3485731b13897551aee43 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Dec 2022 10:04:01 -0800 Subject: [PATCH 197/597] doc fixes --- doc/mk_tactic_doc.py | 17 +++++++++++++---- src/ast/simplifiers/card2bv.cpp | 2 +- src/tactic/arith/card2bv_tactic.h | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index 37b77ef5283..e4c71341a88 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -53,15 +53,24 @@ def extract_tactic_doc(ous, f): if is_doc.search(line): generate_tactic_doc(ous, f, ins) +def presort_files(): + tac_files = [] + for root, dirs, files in os.walk(doc_path("../src")): + for f in files: + if f.endswith("tactic.h"): + tac_files += [(f, os.path.join(root, f))] + tac_files = sorted(tac_files, key = lambda x: x[0]) + return tac_files + def help(ous): + presort_files() ous.write("---\n") ous.write("title: Tactics Summary\n") ous.write("sidebar_position: 5\n") ous.write("---\n") - for root, dirs, files in os.walk(doc_path("../src")): - for f in files: - if f.endswith("tactic.h"): - extract_tactic_doc(ous, os.path.join(root, f)) + tac_files = presort_files() + for file, path in tac_files: + extract_tactic_doc(ous, path) def mk_dir(d): if not os.path.exists(d): diff --git a/src/ast/simplifiers/card2bv.cpp b/src/ast/simplifiers/card2bv.cpp index b2fece21e3f..d2d482aa3a8 100644 --- a/src/ast/simplifiers/card2bv.cpp +++ b/src/ast/simplifiers/card2bv.cpp @@ -55,7 +55,7 @@ void card2bv::collect_statistics(statistics& st) const { } void card2bv::collect_param_descrs(param_descrs& r) { - r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints for solver"); + r.insert("keep_cardinality_constraints", CPK_BOOL, "retain cardinality constraints for solver", "true"); pb2bv_rewriter rw(m, m_params); rw.collect_param_descrs(r); } diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index d2832651493..f3cd4e2c8dc 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -11,7 +11,7 @@ Module Name: Tactic Documentation: -## Tactic car2bv +## Tactic card2bv ### Short Description From f7269bb60a3165d6a670f22aaa45b164b23906a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Dec 2022 10:16:17 -0800 Subject: [PATCH 198/597] update doc Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/propagate_values.cpp | 2 +- src/ast/simplifiers/solve_eqs.cpp | 8 ++++---- src/tactic/core/blast_term_ite_tactic.cpp | 2 +- src/tactic/core/ctx_simplify_tactic.cpp | 4 ++-- src/tactic/core/propagate_values_tactic.cpp | 2 +- src/util/params.cpp | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ast/simplifiers/propagate_values.cpp b/src/ast/simplifiers/propagate_values.cpp index 1083cc36d73..efaf7f244ac 100644 --- a/src/ast/simplifiers/propagate_values.cpp +++ b/src/ast/simplifiers/propagate_values.cpp @@ -115,5 +115,5 @@ void propagate_values::updt_params(params_ref const& p) { void propagate_values::collect_param_descrs(param_descrs& r) { th_rewriter::get_param_descrs(r); - r.insert("max_rounds", CPK_UINT, "(default: 4) maximum number of rounds."); + r.insert("max_rounds", CPK_UINT, "maximum number of rounds.", "4"); } diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index e481dad8dee..1117ad0bcc5 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -270,10 +270,10 @@ namespace euf { } void solve_eqs::collect_param_descrs(param_descrs& r) { - r.insert("solve_eqs_max_occs", CPK_UINT, "(default: infty) maximum number of occurrences for considering a variable for gaussian eliminations."); - r.insert("theory_solver", CPK_BOOL, "(default: true) use theory solvers."); - r.insert("ite_solver", CPK_BOOL, "(default: true) use if-then-else solver."); - r.insert("context_solve", CPK_BOOL, "(default: false) solve equalities under disjunctions."); + r.insert("solve_eqs_max_occs", CPK_UINT, "(default: infty) maximum number of occurrences for considering a variable for gaussian eliminations.", "4294967295"); + r.insert("theory_solver", CPK_BOOL, "theory solvers.", "true"); + r.insert("ite_solver", CPK_BOOL, "use if-then-else solver.", "true"); + r.insert("context_solve", CPK_BOOL, "solve equalities under disjunctions.", "false"); } void solve_eqs::collect_statistics(statistics& st) const { diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp index 987ab9f9075..b38f08e5496 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -179,7 +179,7 @@ class blast_term_ite_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); - r.insert("max_inflation", CPK_UINT, "(default: infinity) multiplicative factor of initial term size."); + r.insert("max_inflation", CPK_UINT, "(default: infinity) multiplicative factor of initial term size.", "4294967295"); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 9ef1cf224b9..aa4358e9cb1 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -611,8 +611,8 @@ void ctx_simplify_tactic::updt_params(params_ref const & p) { void ctx_simplify_tactic::get_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); - r.insert("max_depth", CPK_UINT, "(default: 1024) maximum term depth."); - r.insert("propagate_eq", CPK_BOOL, "(default: false) enable equality propagation from bounds."); + r.insert("max_depth", CPK_UINT, "maximum term depth.", "1024"); + r.insert("propagate_eq", CPK_BOOL, "enable equality propagation from bounds.", "false"); } void ctx_simplify_tactic::operator()(goal_ref const & in, diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 6b8395fd860..5d5bc09454b 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -230,7 +230,7 @@ class propagate_values_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); - r.insert("max_rounds", CPK_UINT, "(default: 4) maximum number of rounds."); + r.insert("max_rounds", CPK_UINT, "maximum number of rounds.", "4"); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { diff --git a/src/util/params.cpp b/src/util/params.cpp index c801b342146..d8902615245 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -314,19 +314,19 @@ void param_descrs::display_markdown(std::ostream & out, bool smt2_style, bool in } void insert_max_memory(param_descrs & r) { - r.insert("max_memory", CPK_UINT, "(default: infty) maximum amount of memory in megabytes."); + r.insert("max_memory", CPK_UINT, "(default: infty) maximum amount of memory in megabytes.", "4294967295"); } void insert_max_steps(param_descrs & r) { - r.insert("max_steps", CPK_UINT, "(default: infty) maximum number of steps."); + r.insert("max_steps", CPK_UINT, "(default: infty) maximum number of steps.", "4294967295"); } void insert_produce_models(param_descrs & r) { - r.insert("produce_models", CPK_BOOL, "(default: false) model generation."); + r.insert("produce_models", CPK_BOOL, "model generation.", "false"); } void insert_produce_proofs(param_descrs & r) { - r.insert("produce_proofs", CPK_BOOL, "(default: false) proof generation."); + r.insert("produce_proofs", CPK_BOOL, "proof generation.", "false"); } void insert_timeout(param_descrs & r) { From 6a1b3f73446bc1c33bad3bd8a063c772bdac8f72 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Dec 2022 12:51:46 -0800 Subject: [PATCH 199/597] move debug output to before state update --- src/ast/simplifiers/elim_unconstrained.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 032c46a92f3..fc3928a2c4d 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -108,7 +108,7 @@ void elim_unconstrained::eliminate() { else m_created_compound = true; - IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(n.m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n";); + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(get_node(v).m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n";); SASSERT(!side_cond && "not implemented to add side conditions\n"); } @@ -264,9 +264,9 @@ void elim_unconstrained::assert_normalized(vector& old_fmls) { if (f == g) continue; old_fmls.push_back(m_fmls[i]); - m_fmls.update(i, dependent_expr(m, g, nullptr, d)); IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(f, m, 3) << " -> " << mk_bounded_pp(g, m, 3) << "\n"); TRACE("elim_unconstrained", tout << mk_bounded_pp(f, m) << " -> " << mk_bounded_pp(g, m) << "\n"); + m_fmls.update(i, dependent_expr(m, g, nullptr, d)); } } From 2d43ccc4c6a4f63c2c6da58302a02c5610f98a30 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 11 Dec 2022 21:37:25 +0000 Subject: [PATCH 200/597] Revert "fix crashes in elim-uncnstr2" This reverts commit a302c2f15ee0e4d24ce9036050fffd8e827fe053. --- src/ast/simplifiers/dependent_expr_state.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index f9faee5494a..85b6352ad25 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -116,17 +116,20 @@ class dependent_expr_simplifier { unsigned qtail() const { return m_fmls.qtail(); } struct iterator { dependent_expr_simplifier& s; - unsigned m_index, m_end; - iterator(dependent_expr_simplifier& s, unsigned i, unsigned end) : s(s), m_index(i), m_end(end) {} - bool operator!=(iterator const& other) const { return m_index != other.m_index; } - iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent()) m_index = m_end; else ++m_index; return *this; } + unsigned m_index = 0; + bool at_end = false; + unsigned index() const { return at_end ? s.qtail() : std::min(m_index, s.qtail()); } + iterator(dependent_expr_simplifier& s, unsigned i) : s(s), m_index(i), at_end(i == s.qtail()) {} + bool operator==(iterator const& other) const { return index() == other.index(); } + bool operator!=(iterator const& other) const { return !(*this == other); } + iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent()) at_end = true; else ++m_index; return *this; } unsigned operator*() const { return m_index; } }; struct index_set { dependent_expr_simplifier& s; - iterator begin() { return iterator(s, s.qhead(), s.qtail()); } - iterator end() { return iterator(s, s.qtail(), s.qtail()); } + iterator begin() { return iterator(s, s.qhead()); } + iterator end() { return iterator(s, s.qtail()); } index_set(dependent_expr_simplifier& s) : s(s) {} }; From 2520dcb04ba2661cb7ed8e433833338e25559f7f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Dec 2022 14:03:22 -0800 Subject: [PATCH 201/597] merge Signed-off-by: Nikolaj Bjorner --- .github/workflows/coverage.yml | 2 +- src/ast/simplifiers/dependent_expr_state.h | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 707db63d66b..e8caa9bfba0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -58,7 +58,7 @@ jobs: - name: Run examples run: | - ${{github.workspace}}/build/examples/c_example_build_dir/c_example +# Disabled: ${{github.workspace}}/build/examples/c_example_build_dir/c_example ${{github.workspace}}/build/examples/cpp_example_build_dir/cpp_example ${{github.workspace}}/build/examples/tptp_build_dir/z3_tptp5 --help ${{github.workspace}}/build/examples/c_maxsat_example_build_dir/c_maxsat_example ${{github.workspace}}/examples/maxsat/ex.smt diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 85b6352ad25..6d1f1aa773e 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -116,13 +116,10 @@ class dependent_expr_simplifier { unsigned qtail() const { return m_fmls.qtail(); } struct iterator { dependent_expr_simplifier& s; - unsigned m_index = 0; - bool at_end = false; - unsigned index() const { return at_end ? s.qtail() : std::min(m_index, s.qtail()); } - iterator(dependent_expr_simplifier& s, unsigned i) : s(s), m_index(i), at_end(i == s.qtail()) {} - bool operator==(iterator const& other) const { return index() == other.index(); } - bool operator!=(iterator const& other) const { return !(*this == other); } - iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent()) at_end = true; else ++m_index; return *this; } + unsigned m_index, m_end; + iterator(dependent_expr_simplifier& s, unsigned i, unsigned end) : s(s), m_index(i), m_end(end) {} + bool operator!=(iterator const& other) const { return m_index != other.m_index; } + iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent() || m_index > s.qtail()) m_index = m_end; else ++m_index; return *this; } unsigned operator*() const { return m_index; } }; From 6b60a3dbed4f7ed492afb4244eb59cd2edd4cf2a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Dec 2022 14:06:08 -0800 Subject: [PATCH 202/597] fix syntax Signed-off-by: Nikolaj Bjorner --- .github/workflows/coverage.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e8caa9bfba0..366a2224e39 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -56,9 +56,10 @@ jobs: ./test-z3 -a cd - +# Disabled: ${{github.workspace}}/build/examples/c_example_build_dir/c_example + - name: Run examples run: | -# Disabled: ${{github.workspace}}/build/examples/c_example_build_dir/c_example ${{github.workspace}}/build/examples/cpp_example_build_dir/cpp_example ${{github.workspace}}/build/examples/tptp_build_dir/z3_tptp5 --help ${{github.workspace}}/build/examples/c_maxsat_example_build_dir/c_maxsat_example ${{github.workspace}}/examples/maxsat/ex.smt From d308b8f5557d49da1f69290b68ec0ddc3bb19524 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 11 Dec 2022 22:09:41 +0000 Subject: [PATCH 203/597] simplify code + remove unused file --- .../converters/generic_model_converter.cpp | 5 -- src/ast/converters/generic_model_converter.h | 2 - src/smt/tactic/smt_tactic_core.h | 1 - src/tactic/filter_model_converter.h | 50 ------------------- src/tactic/goal.cpp | 4 +- 5 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 src/tactic/filter_model_converter.h diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index 50c3b071a79..9adb9ee4b01 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -28,11 +28,6 @@ Module Name: #include "model/model_v2_pp.h" #include "model/model_evaluator.h" - -generic_model_converter::~generic_model_converter() { -} - - void generic_model_converter::add(func_decl * d, expr* e) { VERIFY(e); VERIFY(d->get_range() == e->get_sort()); diff --git a/src/ast/converters/generic_model_converter.h b/src/ast/converters/generic_model_converter.h index 0706b181f1e..cf551c92a48 100644 --- a/src/ast/converters/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -41,8 +41,6 @@ class generic_model_converter : public model_converter { public: generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {} - ~generic_model_converter() override; - void hide(expr* e) { SASSERT(is_app(e) && to_app(e)->get_num_args() == 0); hide(to_app(e)->get_decl()); } void hide(func_decl * f) { m_entries.push_back(entry(f, nullptr, m, HIDE)); } diff --git a/src/smt/tactic/smt_tactic_core.h b/src/smt/tactic/smt_tactic_core.h index f89b3c6494d..7c6fa97f77f 100644 --- a/src/smt/tactic/smt_tactic_core.h +++ b/src/smt/tactic/smt_tactic_core.h @@ -24,7 +24,6 @@ Module Name: #include "tactic/goal.h" class tactic; -class filter_model_converter; tactic * mk_smt_tactic_core(ast_manager& m, params_ref const & p = params_ref(), symbol const& logic = symbol::null); // syntax sugar for using_params(mk_smt_tactic(), p) where p = (:auto_config, auto_config) diff --git a/src/tactic/filter_model_converter.h b/src/tactic/filter_model_converter.h deleted file mode 100644 index 56dda91db6b..00000000000 --- a/src/tactic/filter_model_converter.h +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - filter_model_converter.h - -Abstract: - - Filter decls from a model - -Author: - - Leonardo (leonardo) 2011-05-06 - -Notes: - ---*/ -#pragma once - -#include "tactic/model_converter.h" - -class filter_model_converter : public model_converter { - func_decl_ref_vector m_decls; -public: - filter_model_converter(ast_manager & m):m_decls(m) {} - - ~filter_model_converter() override; - - ast_manager & m() const { return m_decls.get_manager(); } - - void operator()(model_ref & md, unsigned goal_idx) override; - - virtual void operator()(svector & labels, unsigned goal_idx); - - void operator()(model_ref & md) override { operator()(md, 0); } // TODO: delete - - void cancel() override {} - - void display(std::ostream & out) override; - - void insert(func_decl * d) { - m_decls.push_back(d); - } - - model_converter * translate(ast_translation & translator) override; -}; - -typedef ref filter_model_converter_ref; - diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index e489a1100c6..b6fe76f6a88 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -159,9 +159,7 @@ void goal::quick_process(bool save_first, expr_ref& f, expr_dependency * d) { while (!todo.empty()) { if (m_inconsistent) return; - expr_pol p = todo.back(); - expr * curr = p.first; - bool pol = p.second; + auto [curr, pol] = todo.back(); todo.pop_back(); if (pol && m().is_and(curr)) { app * t = to_app(curr); From cb8603177e7624b98ae23f5c0c56a8c0c964df4b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 11 Dec 2022 22:17:11 +0000 Subject: [PATCH 204/597] fix build --- src/ast/simplifiers/dependent_expr_state.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 6d1f1aa773e..e8f137e35bb 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -119,14 +119,14 @@ class dependent_expr_simplifier { unsigned m_index, m_end; iterator(dependent_expr_simplifier& s, unsigned i, unsigned end) : s(s), m_index(i), m_end(end) {} bool operator!=(iterator const& other) const { return m_index != other.m_index; } - iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent() || m_index > s.qtail()) m_index = m_end; else ++m_index; return *this; } + iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent() || m_index >= s.qtail()) m_index = m_end; else ++m_index; return *this; } unsigned operator*() const { return m_index; } }; struct index_set { dependent_expr_simplifier& s; - iterator begin() { return iterator(s, s.qhead()); } - iterator end() { return iterator(s, s.qtail()); } + iterator begin() { return iterator(s, s.qhead(), s.qtail()); } + iterator end() { return iterator(s, s.qtail(), s.qtail()); } index_set(dependent_expr_simplifier& s) : s(s) {} }; From 039de6a2c80f6640cdfb6bb3db3add1f8c8e68a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Dec 2022 15:05:03 -0800 Subject: [PATCH 205/597] build issues Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/extract_eqs.cpp | 6 +++--- src/ast/simplifiers/model_reconstruction_trail.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index b2e264b1fb4..5c2851e913e 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -34,7 +34,7 @@ namespace euf { public: basic_extract_eq(ast_manager& m) : m(m) {} - virtual void set_allow_booleans(bool f) { + void set_allow_booleans(bool f) override { m_allow_bool = f; } @@ -74,7 +74,7 @@ namespace euf { eqs.push_back(dependent_eq(e.fml(), to_app(x), expr_ref(m.mk_false(), m), d)); } - void updt_params(params_ref const& p) { + void updt_params(params_ref const& p) override { tactic_params tp(p); m_ite_solver = p.get_bool("ite_solver", tp.solve_eqs_ite_solver()); } @@ -263,7 +263,7 @@ namespace euf { } - void updt_params(params_ref const& p) { + void updt_params(params_ref const& p) override { tactic_params tp(p); m_enabled = p.get_bool("theory_solver", tp.solve_eqs_ite_solver()); } diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 4ef58f790d6..7f8c7dc8f09 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -49,7 +49,7 @@ class model_reconstruction_trail { entry(ast_manager& m, func_decl* h) : m_decl(h, m), m_def(m), m_dep(m) {} entry(ast_manager& m, func_decl* f, expr* def, expr_dependency* dep, vector const& rem) : - m_decl(f, m), m_def(def, m), m_removed(rem), m_dep(dep, m) {} + m_removed(rem), m_decl(f, m), m_def(def, m), m_dep(dep, m) {} bool is_loose() const { return !m_removed.empty(); } From a3e68856802b4d4a440b6bc178277521917f0d0f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 12 Dec 2022 09:50:44 -0800 Subject: [PATCH 206/597] fix #6488 --- src/ast/simplifiers/elim_unconstrained.cpp | 24 ++++++++++++++-------- src/ast/simplifiers/elim_unconstrained.h | 3 ++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index fc3928a2c4d..a6acd726014 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -96,7 +96,7 @@ void elim_unconstrained::eliminate() { m_trail.push_back(r); SASSERT(r); gc(e); - init_children(e, r); + freeze_rec(r); m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; @@ -180,9 +180,8 @@ void elim_unconstrained::init_terms(expr_ref_vector const& terms) { } } -void elim_unconstrained::init_children(expr* e, expr* r) { +void elim_unconstrained::freeze_rec(expr* r) { expr_ref_vector children(m); - SASSERT(e != r); if (is_quantifier(r)) children.push_back(to_quantifier(r)->get_expr()); else if (is_app(r)) @@ -191,11 +190,20 @@ void elim_unconstrained::init_children(expr* e, expr* r) { return; if (children.empty()) return; - init_terms(children); - for (expr* arg : children) { - get_node(arg).m_parents.push_back(e); - inc_ref(arg); - } + for (expr* t : subterms::all(children)) + freeze(t); +} + +void elim_unconstrained::freeze(expr* t) { + if (!is_uninterp_const(t)) + return; + if (m_nodes.size() <= t->get_id()) + return; + node& n = get_node(t); + if (!n.m_term) + return; + n.m_refcount = UINT_MAX / 2; + m_heap.increased(root(t)); } void elim_unconstrained::gc(expr* t) { diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 1eb5d732b09..68ca30777c0 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -58,8 +58,9 @@ class elim_unconstrained : public dependent_expr_simplifier { unsigned get_refcount(expr* t) const { return get_node(t).m_refcount; } void inc_ref(expr* t) { ++get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.increased(root(t)); } void dec_ref(expr* t) { --get_node(t).m_refcount; if (is_uninterp_const(t)) m_heap.decreased(root(t)); } + void freeze(expr* t); + void freeze_rec(expr* r); void gc(expr* t); - void init_children(expr* e, expr* r); expr* get_parent(unsigned n) const; void init_terms(expr_ref_vector const& terms); void init_nodes(); From 4598af70c823f741ed12186aa1273bf6febc7a45 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 12 Dec 2022 11:04:46 -0800 Subject: [PATCH 207/597] fix #6488 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/elim_unconstrained.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index a6acd726014..b3f833a785c 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -199,6 +199,8 @@ void elim_unconstrained::freeze(expr* t) { return; if (m_nodes.size() <= t->get_id()) return; + if (m_nodes.size() <= root(t)) + return; node& n = get_node(t); if (!n.m_term) return; From aded8e5bf475b6a645488b67bc49d33fd5a7a2c4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 12 Dec 2022 11:40:59 -0800 Subject: [PATCH 208/597] fix #6488 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/elim_unconstrained.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index b3f833a785c..bb7a4fa49d8 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -205,7 +205,8 @@ void elim_unconstrained::freeze(expr* t) { if (!n.m_term) return; n.m_refcount = UINT_MAX / 2; - m_heap.increased(root(t)); + if (m_heap.contains(root(t))) + m_heap.increased(root(t)); } void elim_unconstrained::gc(expr* t) { From e82c8e78ae7f9825d40ffaf9935a6364b793c2db Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Mon, 12 Dec 2022 23:12:31 +0100 Subject: [PATCH 209/597] Fix a compilation error with clang-cl (VS2022) (#6489) --- src/math/polynomial/upolynomial_factorization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/polynomial/upolynomial_factorization.cpp b/src/math/polynomial/upolynomial_factorization.cpp index fad8a85e4d8..310a11fd164 100644 --- a/src/math/polynomial/upolynomial_factorization.cpp +++ b/src/math/polynomial/upolynomial_factorization.cpp @@ -27,7 +27,7 @@ Module Name: #include "math/polynomial/upolynomial_factorization_int.h" #include "util/prime_generator.h" -using namespace std; +using std::endl; namespace upolynomial { From e648e68d3608b39077dadfd252908e76c314f775 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 12 Dec 2022 17:29:58 -0800 Subject: [PATCH 210/597] add doc Signed-off-by: Nikolaj Bjorner --- src/tactic/arith/degree_shift_tactic.h | 35 +++++++++++++++----- src/tactic/arith/diff_neq_tactic.cpp | 2 +- src/tactic/arith/diff_neq_tactic.h | 44 ++++++++++++++++++++------ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/tactic/arith/degree_shift_tactic.h b/src/tactic/arith/degree_shift_tactic.h index 9f3f9f09de4..cdc4823e288 100644 --- a/src/tactic/arith/degree_shift_tactic.h +++ b/src/tactic/arith/degree_shift_tactic.h @@ -5,18 +5,37 @@ Module Name: degree_shift_tactic.h -Abstract: - - Simple degree shift procedure. - Basic idea: if goal G contains a real variable x, x occurs with degrees - d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1. - Then, replace x^n with a new fresh variable y. - Author: Leonardo de Moura (leonardo) 2011-12-30. -Revision History: +Tactic Documentation: + +## Tactic degree-shift + +### Short Description + +The procedure reduces the degrees of variables. + +### Long Description + +Basic idea: if goal $G$ contains a real variable $x$, $x$ occurs with degrees +$d_1, ..., d_k$ in $G$, and $n = \gcd(d_1, ..., d_k) > 1$. +Then, replace $x^n$ with a new fresh variable $y$. + +### Example + +```z3 +(declare-const x Real) +(declare-const y Real) +(assert (> (+ (* x x x 4) (* x x 3) 0))) +(assert (= (* x x) (* y y))) +(apply degree-shift) +``` + +### Notes + +* supports proofs and cores --*/ #pragma once diff --git a/src/tactic/arith/diff_neq_tactic.cpp b/src/tactic/arith/diff_neq_tactic.cpp index 4269aff857a..59baace1062 100644 --- a/src/tactic/arith/diff_neq_tactic.cpp +++ b/src/tactic/arith/diff_neq_tactic.cpp @@ -365,7 +365,7 @@ class diff_neq_tactic : public tactic { } void collect_param_descrs(param_descrs & r) override { - r.insert("diff_neq_max_k", CPK_UINT, "(default: 1024) maximum variable upper bound for diff neq solver."); + r.insert("diff_neq_max_k", CPK_UINT, "maximum variable upper bound for diff neq solver.", "1024"); } void collect_statistics(statistics & st) const override { diff --git a/src/tactic/arith/diff_neq_tactic.h b/src/tactic/arith/diff_neq_tactic.h index 2280a5d7791..02028c385c7 100644 --- a/src/tactic/arith/diff_neq_tactic.h +++ b/src/tactic/arith/diff_neq_tactic.h @@ -5,19 +5,45 @@ Module Name: diff_neq_tactic.h -Abstract: - - Solver for integer problems that contains literals of the form - k <= x - x <= k - x - y != k - And all variables are bounded. - Author: Leonardo de Moura (leonardo) 2012-02-07. -Revision History: +Tactic Documentation: + +## Tactic diff-neq + +### Short Description + +A specialized solver for integer problems using only constant bounds and differences to constants. + +### Long Description + +Solver for integer problems that contains literals of the form +``` + k <= x + x <= k + x - y != k +``` + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(assert (<= 0 x)) +(assert (<= x 1)) +(assert (<= 0 y)) +(assert (<= y 1)) +(assert (not (= (+ x (* -1 y)) -1))) +(assert (not (= (+ x (* -1 y)) 1))) +(assert (not (= (+ x (* -1 y)) 0))) +(apply diff-neq) +``` + +### Notes + +* The tactic works only when the lower bounds are 0 and disequalities use multiplication with -1. Use normalize-bounds to ensure all lower bounds are 0. --*/ #pragma once From 7afcaa5364370dc20ba73b05304c138eb9dd7aed Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 12 Dec 2022 18:56:21 -0800 Subject: [PATCH 211/597] update doc Signed-off-by: Nikolaj Bjorner --- src/tactic/arith/eq2bv_tactic.h | 28 ++++++++++++++---- src/tactic/arith/factor_tactic.cpp | 2 +- src/tactic/arith/factor_tactic.h | 16 ++++++++++- src/tactic/arith/fix_dl_var_tactic.h | 29 +++++++++++++++---- src/tactic/arith/fm_tactic.cpp | 18 ++++++------ src/tactic/arith/fm_tactic.h | 43 +++++++++++++++++++++------- 6 files changed, 102 insertions(+), 34 deletions(-) diff --git a/src/tactic/arith/eq2bv_tactic.h b/src/tactic/arith/eq2bv_tactic.h index e8c29715cc0..81d2718a6be 100644 --- a/src/tactic/arith/eq2bv_tactic.h +++ b/src/tactic/arith/eq2bv_tactic.h @@ -5,16 +5,32 @@ Module Name: eq2bv_tactic.h -Abstract: - - Extract integer variables that are used as finite domain indicators. - The integer variables can only occur in equalities. - Author: Nikolaj Bjorner (nbjorner) 2015-8-19 -Notes: +Tactic Documentation: + +## Tactic eq2bv + +### Short Description + +Extract integer variables that are used as finite domain indicators. +The integer variables can only occur in equalities. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(assert (or (= x 5) (> y 3))) +(assert (or (= x 4) (= y 2))) +(apply eq2bv) +``` + +### Notes + +* does not support proofs --*/ #pragma once diff --git a/src/tactic/arith/factor_tactic.cpp b/src/tactic/arith/factor_tactic.cpp index 6b9b226b3c7..565f43af9c6 100644 --- a/src/tactic/arith/factor_tactic.cpp +++ b/src/tactic/arith/factor_tactic.cpp @@ -303,7 +303,7 @@ class factor_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { r.insert("split_factors", CPK_BOOL, - "(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0))."); + "apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0)).", "true"); polynomial::factor_params::get_param_descrs(r); } diff --git a/src/tactic/arith/factor_tactic.h b/src/tactic/arith/factor_tactic.h index b02f674484f..7be5c5df66b 100644 --- a/src/tactic/arith/factor_tactic.h +++ b/src/tactic/arith/factor_tactic.h @@ -13,7 +13,21 @@ Module Name: Leonardo de Moura (leonardo) 2012-02-03 -Revision History: +Tactic Documentation: + +## Tactic factor + +### Short Description + +Factor polynomials in equalities and inequalities. + +### Example +```z3 +(declare-const x Real) +(declare-const y Real) +(assert (> (* x x) (* x y))) +(apply factor) +``` --*/ #pragma once diff --git a/src/tactic/arith/fix_dl_var_tactic.h b/src/tactic/arith/fix_dl_var_tactic.h index d7a79bf4b0d..f8b03557f9a 100644 --- a/src/tactic/arith/fix_dl_var_tactic.h +++ b/src/tactic/arith/fix_dl_var_tactic.h @@ -7,18 +7,35 @@ Module Name: Abstract: - Fix a difference logic variable to 0. - If the problem is in the difference logic fragment, that is, all arithmetic terms - are of the form (x + k), and the arithmetic atoms are of the - form x - y <= k or x - y = k. Then, we can set one variable to 0. - This is useful because, many bounds can be exposed after this operation is performed. Author: Leonardo (leonardo) 2011-12-29 -Notes: +Tactic Documentation: + +## Tactic fix-dl-var + +### Short Description + +Fix a difference logic variable to `0`. +If the problem is in the difference logic fragment, that is, all arithmetic terms +are of the form `(x + k)`, and the arithmetic atoms are of the +form `x - y <= k` or `x - y = k`. Then, we can set one variable to `0`. + +This is useful because, many bounds can be exposed after this operation is performed. + +### Example + +```z3 +(declare-const x Real) +(declare-const y Real) +(declare-const z Real) +(assert (<= (+ x (* -1.0 y)) 3.0)) +(assert (<= (+ x (* -1.0 z)) 5.0)) +(apply fix-dl-var) +``` --*/ #pragma once diff --git a/src/tactic/arith/fm_tactic.cpp b/src/tactic/arith/fm_tactic.cpp index d0564139ad0..1d3bc277058 100644 --- a/src/tactic/arith/fm_tactic.cpp +++ b/src/tactic/arith/fm_tactic.cpp @@ -467,10 +467,8 @@ class fm_tactic : public tactic { x = t; return true; } - else if (m_util.is_to_real(t) && is_uninterp_const(to_app(t)->get_arg(0))) { - x = to_app(t)->get_arg(0); - return true; - } + else if (m_util.is_to_real(t, x) && is_uninterp_const(x)) + return true; return false; } @@ -1675,12 +1673,12 @@ class fm_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); insert_max_memory(r); - r.insert("fm_real_only", CPK_BOOL, "(default: true) consider only real variables for fourier-motzkin elimination."); - r.insert("fm_occ", CPK_BOOL, "(default: false) consider inequalities occurring in clauses for FM."); - r.insert("fm_limit", CPK_UINT, "(default: 5000000) maximum number of constraints, monomials, clauses visited during FM."); - r.insert("fm_cutoff1", CPK_UINT, "(default: 8) first cutoff for FM based on maximum number of lower/upper occurrences."); - r.insert("fm_cutoff2", CPK_UINT, "(default: 256) second cutoff for FM based on num_lower * num_upper occurrences."); - r.insert("fm_extra", CPK_UINT, "(default: 0) max. increase on the number of inequalities for each FM variable elimination step."); + r.insert("fm_real_only", CPK_BOOL, "consider only real variables for fourier-motzkin elimination.", "true"); + r.insert("fm_occ", CPK_BOOL, "consider inequalities occurring in clauses for FM.", "false"); + r.insert("fm_limit", CPK_UINT, "maximum number of constraints, monomials, clauses visited during FM.", "5000000"); + r.insert("fm_cutoff1", CPK_UINT, "first cutoff for FM based on maximum number of lower/upper occurrences.", "8"); + r.insert("fm_cutoff2", CPK_UINT, "second cutoff for FM based on num_lower * num_upper occurrences.", "256"); + r.insert("fm_extra", CPK_UINT, "max. increase on the number of inequalities for each FM variable elimination step.", "0"); } diff --git a/src/tactic/arith/fm_tactic.h b/src/tactic/arith/fm_tactic.h index 622007703a1..7021dc27304 100644 --- a/src/tactic/arith/fm_tactic.h +++ b/src/tactic/arith/fm_tactic.h @@ -5,20 +5,43 @@ Module Name: fm_tactic.h -Abstract: - - Use Fourier-Motzkin to eliminate variables. - This strategy can handle conditional bounds - (i.e., clauses with at most one constraint). - - The strategy mk_occf can be used to put the - formula in OCC form. - Author: Leonardo de Moura (leonardo) 2012-02-04. -Revision History: +Tactic Documentation: + +## Tactic fm + +### Short Description + +Use Fourier-Motzkin to eliminate variables. +This strategy can handle conditional bounds +(i.e., clauses with at most one constraint). + +The strategy mk_occf can be used to put the +formula in OCC form. + +### Example + +```z3 +(declare-const x Real) +(declare-const y Real) +(declare-const z Real) +(declare-const u Real) +(declare-const v Real) +(declare-const w Real) +(declare-fun P (Real) Bool) +(assert (<= x (+ y (* 2.0 z)))) +(assert (>= x (- y z))) +(assert (>= x (- y 3 (* 3 z)))) +(assert (>= x 5)) +(assert (<= x u)) +(assert (>= x v)) +(assert (P u)) +(assert (P v)) +(apply fm) +``` --*/ #pragma once From 2d7a38e95e2c29ed32b8e75954ea6eac6535e5de Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 13 Dec 2022 16:07:41 -0800 Subject: [PATCH 212/597] fix #6488 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/elim_unconstrained.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index bb7a4fa49d8..2e950868a5e 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -65,7 +65,6 @@ void elim_unconstrained::eliminate() { expr_ref r(m), side_cond(m); int v = m_heap.erase_min(); node& n = get_node(v); - IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(n.m_orig, m) << " @ " << n.m_refcount << "\n"); if (n.m_refcount == 0) continue; if (n.m_refcount > 1) @@ -203,10 +202,11 @@ void elim_unconstrained::freeze(expr* t) { return; node& n = get_node(t); if (!n.m_term) - return; - n.m_refcount = UINT_MAX / 2; - if (m_heap.contains(root(t))) + return; + if (m_heap.contains(root(t))) { + n.m_refcount = UINT_MAX / 2; m_heap.increased(root(t)); + } } void elim_unconstrained::gc(expr* t) { From cd3d38caf79ccc0ecbf18f28044451b135f69cb2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 13 Dec 2022 16:17:38 -0800 Subject: [PATCH 213/597] sort out terminology/add explanations, add shortcut to C++, fix #6491 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 17 +++++++++++++++++ src/api/python/z3/z3.py | 4 +++- src/api/z3_api.h | 8 ++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index caf7ce07ec8..838a34be6d1 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -366,8 +366,14 @@ namespace z3 { void recdef(func_decl, expr_vector const& args, expr const& body); func_decl user_propagate_function(symbol const& name, sort_vector const& domain, sort const& range); + /** + \brief create an uninterpreted constant. + */ expr constant(symbol const & name, sort const & s); expr constant(char const * name, sort const & s); + /** + \brief create uninterpreted constants of a given sort. + */ expr bool_const(char const * name); expr int_const(char const * name); expr real_const(char const * name); @@ -378,6 +384,12 @@ namespace z3 { template expr fpa_const(char const * name); + /** + \brief create a de-Bruijn variable. + */ + expr variable(unsigned index, sort const& s); + + expr fpa_rounding_mode(); expr bool_val(bool b); @@ -3580,6 +3592,11 @@ namespace z3 { return expr(*this, r); } inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } + inline expr context::variable(unsigned idx, sort const& s) { + Z3_ast r = Z3_mk_bound(m_ctx, idx, s); + check_error(); + return expr(*this, r); + } inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } inline expr context::int_const(char const * name) { return constant(name, int_sort()); } inline expr context::real_const(char const * name) { return constant(name, real_sort()); } diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index c97cd2124c3..e829fdafb70 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1469,7 +1469,9 @@ def FreshConst(sort, prefix="c"): def Var(idx, s): """Create a Z3 free variable. Free variables are used to create quantified formulas. - + A free variable with index n is bound when it occurs within the scope of n+1 quantified + declarations. + >>> Var(0, IntSort()) Var(0) >>> eq(Var(0, IntSort()), Var(0, BoolSort())) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 1100d60dd60..467f3079224 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -4051,7 +4051,10 @@ extern "C" { Z3_pattern Z3_API Z3_mk_pattern(Z3_context c, unsigned num_patterns, Z3_ast const terms[]); /** - \brief Create a bound variable. + \brief Create a variable. + + Variables are intended to be bound by a scope created by a quantifier. So we call them bound variables + even if they appear as free variables in the expression produced by \c Z3_mk_bound. Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain the meaning of de-Bruijn indices by indicating the compilation process from @@ -5318,8 +5321,9 @@ extern "C" { Z3_ast const to[]); /** - \brief Substitute the free variables in \c a with the expressions in \c to. + \brief Substitute the variables in \c a with the expressions in \c to. For every \c i smaller than \c num_exprs, the variable with de-Bruijn index \c i is replaced with term \ccode{to[i]}. + Note that a variable is created using the function \ref Z3_mk_bound. def_API('Z3_substitute_vars', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ From 9054e729203931be474a9cdb15d6bc16e000cf01 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 13 Dec 2022 19:35:20 -0800 Subject: [PATCH 214/597] fix #6467 --- src/muz/base/dl_rule.cpp | 25 ++++++++------- src/muz/base/dl_rule.h | 2 +- src/muz/base/dl_util.h | 23 +++++++------- src/muz/rel/dl_base.h | 2 +- src/muz/rel/dl_finite_product_relation.cpp | 2 +- src/muz/rel/dl_mk_explanations.cpp | 37 ++++++++++++---------- src/muz/rel/dl_relation_manager.cpp | 2 +- src/muz/rel/dl_sieve_relation.cpp | 4 +-- 8 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index a1864d3b903..4ef02c03cad 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -1013,28 +1013,31 @@ namespace datalog { } } - void rule::display(context & ctx, std::ostream & out) const { + void rule::display(context & ctx, std::ostream & out, bool compact) const { ast_manager & m = ctx.get_manager(); - out << m_name.str () << ":\n"; + if (!compact) + out << m_name.str () << ":\n"; output_predicate(ctx, m_head, out); if (m_tail_size == 0) { - out << ".\n"; + out << "."; + if (!compact) + out << "\n"; return; } out << " :- "; for (unsigned i = 0; i < m_tail_size; i++) { if (i > 0) out << ","; - out << "\n "; + if (!compact) + out << "\n"; + out << " "; if (is_neg_tail(i)) out << "not "; app * t = get_tail(i); - if (ctx.is_predicate(t)) { + if (ctx.is_predicate(t)) output_predicate(ctx, t, out); - } - else { + else out << mk_pp(t, m); - } } out << '.'; if (ctx.output_profile()) { @@ -1042,10 +1045,10 @@ namespace datalog { output_profile(out); out << '}'; } - out << '\n'; - if (m_proof) { + if (!compact) + out << '\n'; + if (m_proof) out << mk_pp(m_proof, m) << '\n'; - } } diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 6a21a2621e8..60b2bbe8fb4 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -365,7 +365,7 @@ namespace datalog { void get_vars(ast_manager& m, ptr_vector& sorts) const; - void display(context & ctx, std::ostream & out) const; + void display(context & ctx, std::ostream & out, bool compact = false) const; /** \brief Return the name(s) associated with this rule. Plural for preprocessed (e.g. obtained by inlining) rules. diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 4688a67fd1d..623f287f795 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -320,32 +320,31 @@ namespace datalog { unsigned_vector & res, bool & identity); template - void permutate_by_cycle(T & container, unsigned cycle_len, const unsigned * permutation_cycle) { - if (cycle_len<2) { + void permute_by_cycle(T& container, unsigned cycle_len, const unsigned * permutation_cycle) { + if (cycle_len < 2) return; - } auto aux = container[permutation_cycle[0]]; - for (unsigned i=1; i - void permutate_by_cycle(ref_vector & container, unsigned cycle_len, const unsigned * permutation_cycle) { + void permute_by_cycle(ref_vector & container, unsigned cycle_len, const unsigned * permutation_cycle) { if (cycle_len<2) { return; } + verbose_stream() << "ptr\n"; T * aux = container.get(permutation_cycle[0]); - for (unsigned i=1; i - void permutate_by_cycle(T & container, const unsigned_vector & permutation_cycle) { - permutate_by_cycle(container, permutation_cycle.size(), permutation_cycle.data()); + void permute_by_cycle(T & container, const unsigned_vector & permutation_cycle) { + permute_by_cycle(container, permutation_cycle.size(), permutation_cycle.data()); } diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index ea317ca4554..f6d03162423 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -160,7 +160,7 @@ namespace datalog { SASSERT(cycle_len>=2); result=src; - permutate_by_cycle(result, cycle_len, permutation_cycle); + permute_by_cycle(result, cycle_len, permutation_cycle); } /** diff --git a/src/muz/rel/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp index a991c9e6ca5..b1cdf055356 100644 --- a/src/muz/rel/dl_finite_product_relation.cpp +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -674,7 +674,7 @@ namespace datalog { unsigned sig_sz = r.get_signature().size(); unsigned_vector permutation; add_sequence(0, sig_sz, permutation); - permutate_by_cycle(permutation, cycle_len, permutation_cycle); + permute_by_cycle(permutation, cycle_len, permutation_cycle); unsigned_vector table_permutation; diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp index 7c5691c0aa3..37c67e0c8ef 100644 --- a/src/muz/rel/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -70,20 +70,16 @@ namespace datalog { m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {} ~explanation_relation_plugin() override { - for (unsigned i = 0; i < m_pool.size(); ++i) { - for (unsigned j = 0; j < m_pool[i].size(); ++j) { + for (unsigned i = 0; i < m_pool.size(); ++i) + for (unsigned j = 0; j < m_pool[i].size(); ++j) dealloc(m_pool[i][j]); - } - } } bool can_handle_signature(const relation_signature & s) override { unsigned n=s.size(); - for (unsigned i=0; iget_sort()), m_data[0]); + verbose_stream() << "FORMULA " << fml << "\n"; } bool is_undefined(unsigned col_idx) const { @@ -339,6 +340,7 @@ namespace datalog { if (!r.empty()) { relation_fact proj_data = r.m_data; project_out_vector_columns(proj_data, m_removed_cols); + verbose_stream() << "project\n"; res->assign_data(proj_data); } return res; @@ -365,9 +367,9 @@ namespace datalog { explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); if (!r.empty()) { - relation_fact permutated_data = r.m_data; - permutate_by_cycle(permutated_data, m_cycle); - res->assign_data(permutated_data); + relation_fact permuted_data = r.m_data; + permute_by_cycle(dynamic_cast(permuted_data), m_cycle); + res->assign_data(permuted_data); } return res; } @@ -406,6 +408,7 @@ namespace datalog { } else { if (tgt.empty()) { + verbose_stream() << "union\n"; tgt.assign_data(src.m_data); if (delta && delta->empty()) { delta->assign_data(src.m_data); @@ -704,7 +707,7 @@ namespace datalog { symbol mk_explanations::get_rule_symbol(rule * r) { if (r->name() == symbol::null) { std::stringstream sstm; - r->display(m_context, sstm); + r->display(m_context, sstm, true); std::string res = sstm.str(); res = res.substr(0, res.find_last_not_of('\n')+1); return symbol(res.c_str()); diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index 9410e2ab023..8de25c8f38e 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -1149,7 +1149,7 @@ namespace datalog { } void modify_fact(table_fact & f) const override { - permutate_by_cycle(f, m_cycle); + permute_by_cycle(f, m_cycle); } table_base * operator()(const table_base & t) override { diff --git a/src/muz/rel/dl_sieve_relation.cpp b/src/muz/rel/dl_sieve_relation.cpp index c7eab954963..80110899945 100644 --- a/src/muz/rel/dl_sieve_relation.cpp +++ b/src/muz/rel/dl_sieve_relation.cpp @@ -413,14 +413,14 @@ namespace datalog { unsigned sig_sz = r.get_signature().size(); unsigned_vector permutation; add_sequence(0, sig_sz, permutation); - permutate_by_cycle(permutation, cycle_len, permutation_cycle); + permute_by_cycle(permutation, cycle_len, permutation_cycle); bool inner_identity; unsigned_vector inner_permutation; collect_sub_permutation(permutation, r.m_sig2inner, inner_permutation, inner_identity); bool_vector result_inner_cols = r.m_inner_cols; - permutate_by_cycle(result_inner_cols, cycle_len, permutation_cycle); + permute_by_cycle(result_inner_cols, cycle_len, permutation_cycle); relation_signature result_sig; relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, result_sig); From dbb4bbe7dc8b494c7a3aff206672a7fcab69317c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 13 Dec 2022 19:36:55 -0800 Subject: [PATCH 215/597] remove debug out --- src/muz/rel/dl_mk_explanations.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp index 37c67e0c8ef..a4b7045974a 100644 --- a/src/muz/rel/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -147,7 +147,6 @@ namespace datalog { void assign_data(const relation_fact & f) { m_empty = false; - verbose_stream() << "assign data " << f << "\n"; unsigned n = get_signature().size(); SASSERT(f.size()==n); m_data.reset(); @@ -159,7 +158,6 @@ namespace datalog { m_data.resize(get_signature().size()); } void unite_with_data(const relation_fact & f) { - verbose_stream() << "unite data " << f << "\n"; if (empty()) { assign_data(f); @@ -186,7 +184,6 @@ namespace datalog { void to_formula(expr_ref& fml) const override { ast_manager& m = fml.get_manager(); fml = m.mk_eq(m.mk_var(0, m_data[0]->get_sort()), m_data[0]); - verbose_stream() << "FORMULA " << fml << "\n"; } bool is_undefined(unsigned col_idx) const { @@ -340,7 +337,6 @@ namespace datalog { if (!r.empty()) { relation_fact proj_data = r.m_data; project_out_vector_columns(proj_data, m_removed_cols); - verbose_stream() << "project\n"; res->assign_data(proj_data); } return res; @@ -408,7 +404,6 @@ namespace datalog { } else { if (tgt.empty()) { - verbose_stream() << "union\n"; tgt.assign_data(src.m_data); if (delta && delta->empty()) { delta->assign_data(src.m_data); From c4b2acac24c1e660df99bbf4364f531826186b11 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Dec 2022 09:27:43 -0800 Subject: [PATCH 216/597] add missing error checking #6492 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 838a34be6d1..ec7b451fa9e 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1919,21 +1919,21 @@ namespace z3 { inline expr operator>(expr const & a, int b) { return a > a.ctx().num_val(b, a.get_sort()); } inline expr operator>(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) > b; } - inline expr operator&(expr const & a, expr const & b) { if (a.is_bool()) return a && b; check_context(a, b); Z3_ast r = Z3_mk_bvand(a.ctx(), a, b); return expr(a.ctx(), r); } + inline expr operator&(expr const & a, expr const & b) { if (a.is_bool()) return a && b; check_context(a, b); Z3_ast r = Z3_mk_bvand(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr operator&(expr const & a, int b) { return a & a.ctx().num_val(b, a.get_sort()); } inline expr operator&(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) & b; } - inline expr operator^(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = a.is_bool() ? Z3_mk_xor(a.ctx(), a, b) : Z3_mk_bvxor(a.ctx(), a, b); return expr(a.ctx(), r); } + inline expr operator^(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = a.is_bool() ? Z3_mk_xor(a.ctx(), a, b) : Z3_mk_bvxor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr operator^(expr const & a, int b) { return a ^ a.ctx().num_val(b, a.get_sort()); } inline expr operator^(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) ^ b; } - inline expr operator|(expr const & a, expr const & b) { if (a.is_bool()) return a || b; check_context(a, b); Z3_ast r = Z3_mk_bvor(a.ctx(), a, b); return expr(a.ctx(), r); } + inline expr operator|(expr const & a, expr const & b) { if (a.is_bool()) return a || b; check_context(a, b); Z3_ast r = Z3_mk_bvor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr operator|(expr const & a, int b) { return a | a.ctx().num_val(b, a.get_sort()); } inline expr operator|(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) | b; } - inline expr nand(expr const& a, expr const& b) { if (a.is_bool()) return !(a && b); check_context(a, b); Z3_ast r = Z3_mk_bvnand(a.ctx(), a, b); return expr(a.ctx(), r); } - inline expr nor(expr const& a, expr const& b) { if (a.is_bool()) return !(a || b); check_context(a, b); Z3_ast r = Z3_mk_bvnor(a.ctx(), a, b); return expr(a.ctx(), r); } - inline expr xnor(expr const& a, expr const& b) { if (a.is_bool()) return !(a ^ b); check_context(a, b); Z3_ast r = Z3_mk_bvxnor(a.ctx(), a, b); return expr(a.ctx(), r); } + inline expr nand(expr const& a, expr const& b) { if (a.is_bool()) return !(a && b); check_context(a, b); Z3_ast r = Z3_mk_bvnand(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } + inline expr nor(expr const& a, expr const& b) { if (a.is_bool()) return !(a || b); check_context(a, b); Z3_ast r = Z3_mk_bvnor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } + inline expr xnor(expr const& a, expr const& b) { if (a.is_bool()) return !(a ^ b); check_context(a, b); Z3_ast r = Z3_mk_bvxnor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr min(expr const& a, expr const& b) { check_context(a, b); Z3_ast r; @@ -1947,6 +1947,7 @@ namespace z3 { assert(a.is_fpa()); r = Z3_mk_fpa_min(a.ctx(), a, b); } + a.check_error(); return expr(a.ctx(), r); } inline expr max(expr const& a, expr const& b) { @@ -1962,6 +1963,7 @@ namespace z3 { assert(a.is_fpa()); r = Z3_mk_fpa_max(a.ctx(), a, b); } + a.check_error(); return expr(a.ctx(), r); } inline expr bvredor(expr const & a) { From d47dd159d7019c103e5a78d9ec1c291919418c9f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Dec 2022 09:43:29 -0800 Subject: [PATCH 217/597] set encoding into gparams because this is the only entry point in zstring #6490 --- src/params/context_params.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/params/context_params.cpp b/src/params/context_params.cpp index 294c5cbbead..fbdd90b8ce4 100644 --- a/src/params/context_params.cpp +++ b/src/params/context_params.cpp @@ -109,6 +109,7 @@ void context_params::set(char const * param, char const * value) { else if (p == "encoding") { if (strcmp(value, "unicode") == 0 || strcmp(value, "bmp") == 0 || strcmp(value, "ascii") == 0) { m_encoding = value; + gparams::set("encoding", value); } else { std::stringstream strm; From aed3d76a886ea1ef0a7c2db9504df893e388e591 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Dec 2022 16:45:58 -0800 Subject: [PATCH 218/597] add doc Signed-off-by: Nikolaj Bjorner --- src/tactic/arith/lia2card_tactic.h | 34 ++++++++++++--- src/tactic/arith/nla2bv_tactic.h | 24 +++++++++- src/tactic/arith/normalize_bounds_tactic.h | 31 ++++++++++--- src/tactic/arith/recover_01_tactic.h | 51 +++++++++++++++++----- 4 files changed, 114 insertions(+), 26 deletions(-) diff --git a/src/tactic/arith/lia2card_tactic.h b/src/tactic/arith/lia2card_tactic.h index ff076aa42cd..5186b419b23 100644 --- a/src/tactic/arith/lia2card_tactic.h +++ b/src/tactic/arith/lia2card_tactic.h @@ -5,16 +5,38 @@ Module Name: lia2card_tactic.h -Abstract: - - Extract 0-1 integer variables used in - cardinality constraints and replace them by Booleans. - Author: Nikolaj Bjorner (nbjorner) 2013-11-5 -Notes: +Tactic Documentation: + +## Tactic lia2card + +### Short Description + +Extract 0-1 integer variables used in +cardinality and pseudo-Boolean constraints and replace them by Booleans. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(assert (<= 0 x)) +(assert (<= 0 y)) +(assert (<= 0 z)) +(assert (>= 1 x)) +(assert (>= 1 y)) +(assert (>= 1 z)) +(assert (>= (+ (* 5 x) (* -2 z) (* 3 y) 1) 4)) +(apply lia2card) +``` + +### Notes + +* The tactic does not (properly) support proofs or cores. --*/ #pragma once diff --git a/src/tactic/arith/nla2bv_tactic.h b/src/tactic/arith/nla2bv_tactic.h index 80a60b30fd2..d1acc861aa6 100644 --- a/src/tactic/arith/nla2bv_tactic.h +++ b/src/tactic/arith/nla2bv_tactic.h @@ -7,7 +7,6 @@ Module Name: Abstract: - Convert quantified NIA problems to bounded bit-vector arithmetic problems. Author: @@ -16,6 +15,29 @@ Module Name: Notes: Ported to tactic framework on 2012-02-28 +Tactic Documentation: + +## Tactic nla2bv + +### Short Description + +Convert quantified NIA problems to bounded bit-vector arithmetic problems. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(assert (= (* x x y) (* 2 z y y))) +(apply nla2bv) +``` + +### Notes + +* The tactic creates an under-approximation (a stronger set of formulas) + + --*/ #pragma once diff --git a/src/tactic/arith/normalize_bounds_tactic.h b/src/tactic/arith/normalize_bounds_tactic.h index dec4d486eed..4d456c38a5c 100644 --- a/src/tactic/arith/normalize_bounds_tactic.h +++ b/src/tactic/arith/normalize_bounds_tactic.h @@ -5,17 +5,34 @@ Module Name: normalize_bounds_tactic.h -Abstract: - - Replace x with x' + l, when l <= x - where x' is a fresh variable. - Note that, after the transformation 0 <= x'. - Author: Leonardo de Moura (leonardo) 2011-10-21. -Revision History: +Tactic Documentation: + +## Tactic normalize-bounds + +### Short Description + +Replace $x$ with $x' + l$, when $l \leq x$ +where $x'$ is a fresh variable. +Note that, after the transformation $0 \leq x'$. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(assert (<= 3 x)) +(assert (<= (+ x y) z)) +(apply normalize-bounds) +``` + +### Notes + +* supports proofs and cores --*/ #pragma once diff --git a/src/tactic/arith/recover_01_tactic.h b/src/tactic/arith/recover_01_tactic.h index 4e16dbf4a0d..cd7e47ea049 100644 --- a/src/tactic/arith/recover_01_tactic.h +++ b/src/tactic/arith/recover_01_tactic.h @@ -5,29 +5,56 @@ Module Name: recover_01_tactic.h -Abstract: +Author: + + Leonardo de Moura (leonardo) 2012-02-17. + +Tactic Documentation: + +## Tactic recover-01 + +### Short Description + +Recover 01 variables from propositional constants. - Recover 01 variables +### Long Description - Search for clauses of the form +Search for clauses of the form + +``` p or q or x = 0 ~p or q or x = k1 p or ~q or x = k2 ~p or ~q or x = k1+k2 +``` - Then, replaces - x with k1*y1 + k2*y2 - p with y1=1 - q with y2=1 - where y1 and y2 are fresh 01 variables +Then, replaces - The clauses are also removed. -Author: +* `x` with `k1*y1 + k2*y2` +* `p` with `y1 = 1` +* `q` with `y2 = 1` - Leonardo de Moura (leonardo) 2012-02-17. +where `y1` and `y2` are fresh 01 variables. + +The clauses are also removed. + +### Example + +```z3 +(declare-const p Bool) +(declare-const q Bool) +(declare-const x Int) +(assert (or p q (= x 0))) +(assert (or (not p) q (= x 3))) +(assert (or p (not q) (= x 6))) +(assert (or (not p) (not q) (= x 9))) +(apply recover-01) +``` + +### Notes -Revision History: +* does not support proofs, does not support cores --*/ #pragma once From d5316e017e9c0c9f44e4aa2453f908fde7ff4324 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Dec 2022 20:38:28 -0800 Subject: [PATCH 219/597] add tactic descriptions --- src/muz/spacer/spacer_sym_mux.cpp | 4 +- src/tactic/arith/pb2bv_tactic.h | 27 ++++++++++- src/tactic/arith/propagate_ineqs_tactic.h | 55 +++++++++++++++-------- src/tactic/arith/purify_arith_tactic.h | 23 +++++++++- 4 files changed, 88 insertions(+), 21 deletions(-) diff --git a/src/muz/spacer/spacer_sym_mux.cpp b/src/muz/spacer/spacer_sym_mux.cpp index cfe0908d698..451a2b3dcf8 100644 --- a/src/muz/spacer/spacer_sym_mux.cpp +++ b/src/muz/spacer/spacer_sym_mux.cpp @@ -144,10 +144,12 @@ struct conv_rewriter_cfg : public default_rewriter_cfg { bool get_subst(expr * s, expr * & t, proof * & t_pr) { - if (!is_app(s)) { return false; } + if (!is_app(s)) + return false; app * a = to_app(s); func_decl * sym = a->get_decl(); if (!m_parent.has_index(sym, m_from_idx)) { + CTRACE("spacer", m_homogenous && m_parent.is_muxed(sym), tout << "not found " << mk_pp(a, m) << "\n"); SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); return false; } diff --git a/src/tactic/arith/pb2bv_tactic.h b/src/tactic/arith/pb2bv_tactic.h index e23c54d8350..b1c94a6ba98 100644 --- a/src/tactic/arith/pb2bv_tactic.h +++ b/src/tactic/arith/pb2bv_tactic.h @@ -13,7 +13,32 @@ Module Name: Christoph (cwinter) 2012-02-15 -Notes: +Tactic Documentation: + +## Tactic pb2bv + +### Short Description + +Convert pseudo-boolean constraints to bit-vectors + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(declare-const u Int) +(assert (<= 0 x)) +(assert (<= 0 y)) +(assert (<= 0 z)) +(assert (<= 0 u)) +(assert (<= x 1)) +(assert (<= y 1)) +(assert (<= z 1)) +(assert (<= u 1)) +(assert (>= (+ (* 3 x) (* 2 y) (* 2 z) (* 2 u)) 4)) +(apply pb2bv) +``` --*/ #pragma once diff --git a/src/tactic/arith/propagate_ineqs_tactic.h b/src/tactic/arith/propagate_ineqs_tactic.h index 47806a34192..6341bf28165 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.h +++ b/src/tactic/arith/propagate_ineqs_tactic.h @@ -4,30 +4,49 @@ Copyright (c) 2012 Microsoft Corporation Module Name: propagate_ineqs_tactic.h + +Author: -Abstract: + Leonardo (leonardo) 2012-02-19 - This tactic performs the following tasks: +Tactic Documentation: - - Propagate bounds using the bound_propagator. - - Eliminate subsumed inequalities. - For example: - x - y >= 3 - can be replaced with true if we know that - x >= 3 and y <= 0 - - - Convert inequalities of the form p <= k and p >= k into p = k, - where p is a polynomial and k is a constant. +## Tactic propagate-ineqs - This strategy assumes the input is in arith LHS mode. - This can be achieved by using option :arith-lhs true in the - simplifier. - -Author: +### Short Description - Leonardo (leonardo) 2012-02-19 +Propagate ineqs/bounds, remove subsumed inequalities + +### Long Description + +This tactic performs the following tasks: + +- Propagate bounds using the bound_propagator. +- Eliminate subsumed inequalities. + - For example: + `x - y >= 3` can be replaced with true if we know that `x >= 3` and `y <= 0` + + - Convert inequalities of the form `p <= k` and `p >= k` into `p = k`, + where `p` is a polynomial and `k` is a constant. + +This strategy assumes the input is in arith LHS mode. +This can be achieved by using option :arith-lhs true in the simplifier. -Notes: +### Example +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(declare-const u Int) +(declare-const v Int) +(declare-const w Int) +(assert (>= x 3)) +(assert (<= y 0)) +(assert (>= (- x y) 3)) +(assert (>= (* u v w) 2)) +(assert (<= (* v u w) 2)) +(apply (and-then simplify propagate-ineqs)) +``` --*/ #pragma once diff --git a/src/tactic/arith/purify_arith_tactic.h b/src/tactic/arith/purify_arith_tactic.h index ef5f08b6180..4f3aa847a51 100644 --- a/src/tactic/arith/purify_arith_tactic.h +++ b/src/tactic/arith/purify_arith_tactic.h @@ -42,7 +42,28 @@ Module Name: Leonardo de Moura (leonardo) 2011-12-30. -Revision History: +Tactic Documentation: + +## Tactic purify-arith + +### Short Description + +Eliminate unnecessary operators: -, /, div, mod, rem, is-int, to-int, ^, root-objects. +These operators can be replaced by introcing fresh variables and using multiplication and addition. + +### Example +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(declare-const u Int) +(declare-const v Int) +(declare-const w Int) +(assert (= (div x 3) y)) +(assert (= (mod z 4) u)) +(assert (> (mod v w) u)) +(apply purify-arith) +``` --*/ #pragma once From 13920c4772683d179932507230755a7a19dc8b24 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 15 Dec 2022 11:42:02 -0800 Subject: [PATCH 220/597] more doc Signed-off-by: Nikolaj Bjorner --- src/tactic/arith/nla2bv_tactic.cpp | 6 ++-- src/tactic/arith/normalize_bounds_tactic.cpp | 2 +- src/tactic/arith/purify_arith_tactic.cpp | 6 ++-- src/tactic/arith/recover_01_tactic.cpp | 2 +- src/tactic/bv/bit_blaster_tactic.cpp | 8 +++--- src/tactic/bv/bit_blaster_tactic.h | 30 ++++++++++++++------ 6 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index bdcf5795380..cd37db40292 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -442,9 +442,9 @@ class nla2bv_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { r.insert("nla2bv_max_bv_size", CPK_UINT, "(default: inf) maximum bit-vector size used by nla2bv tactic"); - r.insert("nla2bv_bv_size", CPK_UINT, "(default: 4) default bit-vector size used by nla2bv tactic."); - r.insert("nla2bv_root", CPK_UINT, "(default: 2) nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding."); - r.insert("nla2bv_divisor", CPK_UINT, "(default: 2) nla2bv tactic parameter."); + r.insert("nla2bv_bv_size", CPK_UINT, "default bit-vector size used by nla2bv tactic.", "4"); + r.insert("nla2bv_root", CPK_UINT, "nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding.", "2"); + r.insert("nla2bv_divisor", CPK_UINT, "nla2bv tactic parameter.", "2"); } /** diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index cba791ee16b..b20eaf53a3d 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -161,7 +161,7 @@ class normalize_bounds_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); - r.insert("norm_int_only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants."); + r.insert("norm_int_only", CPK_BOOL, "normalize only the bounds of integer constants.", "true"); } void operator()(goal_ref const & in, diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 7c69ef12e82..db19863987a 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -911,11 +911,11 @@ class purify_arith_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { r.insert("complete", CPK_BOOL, - "(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root"); + "add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root", "true"); r.insert("elim_root_objects", CPK_BOOL, - "(default: true) eliminate root objects."); + "eliminate root objects.", "true"); r.insert("elim_inverses", CPK_BOOL, - "(default: true) eliminate inverse trigonometric functions (asin, acos, atan)."); + "eliminate inverse trigonometric functions (asin, acos, atan).", "true"); th_rewriter::get_param_descrs(r); } diff --git a/src/tactic/arith/recover_01_tactic.cpp b/src/tactic/arith/recover_01_tactic.cpp index d97f9b80fc6..623f82cf9d6 100644 --- a/src/tactic/arith/recover_01_tactic.cpp +++ b/src/tactic/arith/recover_01_tactic.cpp @@ -407,7 +407,7 @@ class recover_01_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); - r.insert("recover_01_max_bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause."); + r.insert("recover_01_max_bits", CPK_UINT, "maximum number of bits to consider in a clause.", "10"); } void operator()(goal_ref const & g, diff --git a/src/tactic/bv/bit_blaster_tactic.cpp b/src/tactic/bv/bit_blaster_tactic.cpp index 978a0a9a6f6..5e35d7d9ad0 100644 --- a/src/tactic/bv/bit_blaster_tactic.cpp +++ b/src/tactic/bv/bit_blaster_tactic.cpp @@ -129,10 +129,10 @@ class bit_blaster_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); - r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders)."); - r.insert("blast_add", CPK_BOOL, "(default: true) bit-blast adders."); - r.insert("blast_quant", CPK_BOOL, "(default: false) bit-blast quantified variables."); - r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms."); + r.insert("blast_mul", CPK_BOOL, "bit-blast multipliers (and dividers, remainders).", "true"); + r.insert("blast_add", CPK_BOOL, "bit-blast adders.", "true"); + r.insert("blast_quant", CPK_BOOL, "bit-blast quantified variables.", "false"); + r.insert("blast_full", CPK_BOOL, "bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms.", "false"); } void operator()(goal_ref const & g, diff --git a/src/tactic/bv/bit_blaster_tactic.h b/src/tactic/bv/bit_blaster_tactic.h index e90a675aa24..07d85d9c6a0 100644 --- a/src/tactic/bv/bit_blaster_tactic.h +++ b/src/tactic/bv/bit_blaster_tactic.h @@ -1,21 +1,33 @@ /*++ Copyright (c) 2011 Microsoft Corporation - Module Name: +Module Name: bit_blaster_tactic.h - Abstract: +Author: - Apply bit-blasting to a given goal. - - Author: - - Leonardo (leonardo) 2011-10-25 - - Notes: + Leonardo (leonardo) 2011-10-25 +Tactic Documentation: + +## Tactic bit-blast + +### Short Description + +Apply bit-blasting to a given goal. + +### Example + +```z3 +(declare-const x (_ BitVec 8)) +(declare-const y (_ BitVec 8)) +(assert (bvule x y)) +(apply bit-blast) +``` + --*/ + #pragma once #include "util/params.h" From ecf25a4fe267cc7151a0f42ae0c7bbd8b6664b4b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 15 Dec 2022 14:57:52 -0800 Subject: [PATCH 221/597] outline scheme Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_eqs.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 1117ad0bcc5..ad58239c3ad 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -13,6 +13,24 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-11-2. +Notes: + +extract_subst is inefficient. +It traverses the same sub-terms many times. + +Outline of a presumably better scheme: + +1. maintain map FV: term -> bit-set where bitset reprsents set of free variables. Assume the number of variables is bounded. + FV is built from initial terms. +2. maintain parent: term -> term-list of parent occurrences. +3. repeat + pick x = t, such that x not in FV(t) + orient x -> t + for p in parent*(x): + FV(p) := FV(p) u FV(t) + if y = s is processed and x in FV(s) order y < x + if y = s is processed and x in FV(t) order x < y + --*/ From 0768a2ead15d162e0e39c5791441cd16ccd73dd0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 15 Dec 2022 19:23:32 -0800 Subject: [PATCH 222/597] updated doc --- src/tactic/bv/bv1_blaster_tactic.h | 36 +++++++++++++++------ src/tactic/bv/bv_bound_chk_tactic.cpp | 1 - src/tactic/bv/bv_bound_chk_tactic.h | 14 ++++---- src/tactic/bv/bv_size_reduction_tactic.h | 41 ++++++++++++++++++++---- 4 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/tactic/bv/bv1_blaster_tactic.h b/src/tactic/bv/bv1_blaster_tactic.h index c150778fd8a..9cc7f90d530 100644 --- a/src/tactic/bv/bv1_blaster_tactic.h +++ b/src/tactic/bv/bv1_blaster_tactic.h @@ -5,21 +5,37 @@ Module Name: bv1_blaster_tactic.h -Abstract: +Author: - Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1. - This rewriter only supports concat and extract operators. - This transformation is useful for handling benchmarks that contain - many BV equalities. + Leonardo (leonardo) 2011-10-25 - Remark: other operators can be mapped into concat/extract by using - the simplifiers. +Tactic Documentation: -Author: +## Tactic bv1-blast - Leonardo (leonardo) 2011-10-25 +### Short Description + +Reduce bit-vector expressions into bit-vectors of size 1 (notes: only equality, extract and concat are supported). + +### Long Description + +Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1. +This rewriter only supports concat and extract operators. +This transformation is useful for handling benchmarks that contain +many BV equalities. + +_Remark_: other operators can be mapped into concat/extract by using +the simplifiers. + +### Example -Notes: +```z3 +(declare-const x (_ BitVec 8)) +(declare-const y (_ BitVec 4)) +(declare-const z (_ BitVec 4)) +(assert (= (concat y z) x)) + (apply bv1-blast) +``` --*/ #pragma once diff --git a/src/tactic/bv/bv_bound_chk_tactic.cpp b/src/tactic/bv/bv_bound_chk_tactic.cpp index 3a2f8583125..f6db3c30e46 100644 --- a/src/tactic/bv/bv_bound_chk_tactic.cpp +++ b/src/tactic/bv/bv_bound_chk_tactic.cpp @@ -48,7 +48,6 @@ struct bv_bound_chk_rewriter_cfg : public default_rewriter_cfg { m_bv_ineq_consistency_test_max = p.bv_ineq_consistency_test_max(); m_max_memory = p.max_memory(); m_max_steps = p.max_steps(); - } ast_manager & m() const { return m_m; } diff --git a/src/tactic/bv/bv_bound_chk_tactic.h b/src/tactic/bv/bv_bound_chk_tactic.h index 60411e69353..bafd2ec5131 100644 --- a/src/tactic/bv/bv_bound_chk_tactic.h +++ b/src/tactic/bv/bv_bound_chk_tactic.h @@ -1,18 +1,18 @@ /*++ - Copyright (c) 2016 Microsoft Corporation +Copyright (c) 2016 Microsoft Corporation - Module Name: +Module Name: - bv_bound_chk_tactic.h + bv_bound_chk_tactic.h - Abstract: +Author: + Mikolas Janota - Author: +### Notes - Mikolas Janota +* does not support proofs, does not support cores - Revision History: --*/ #pragma once diff --git a/src/tactic/bv/bv_size_reduction_tactic.h b/src/tactic/bv/bv_size_reduction_tactic.h index 1bb512f3fc2..a55c66e73a0 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.h +++ b/src/tactic/bv/bv_size_reduction_tactic.h @@ -7,12 +7,6 @@ Module Name: Abstract: - Reduce the number of bits used to encode constants, by using signed bounds. - Example: suppose x is a bit-vector of size 8, and we have - signed bounds for x such that: - -2 <= x <= 2 - Then, x can be replaced by ((sign-extend 5) k) - where k is a fresh bit-vector constant of size 3. Author: @@ -20,6 +14,41 @@ Module Name: Notes: +Tactic Documentation: + +## Tactic reduce-bv-size + +### Short Description + +Rry to reduce bit-vector sizes using inequalities. + +### Long Description + +Reduce the number of bits used to encode constants, by using signed bounds. +Example: suppose $x$ is a bit-vector of size 8, and we have +signed bounds for $x$ such that: + +``` + -2 <= x <= 2 +``` + +Then, $x$ can be replaced by `((sign-extend 5) k)` +where `k` is a fresh bit-vector constant of size 3. + +### Example + +```z3 +(declare-const x (_ BitVec 32)) +(assert (bvsle (bvneg (_ bv2 32)) x)) +(assert (bvsle x (_ bv2 32))) +(assert (= (bvmul x x) (_ bv9 32))) +(apply (and-then simplify reduce-bv-size)) +``` + +### Notes + +* does not support proofs, nor unsat cores + --*/ #pragma once From e423fabf6a8808c36d906cdd62dda9c90773992d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 15 Dec 2022 20:35:36 -0800 Subject: [PATCH 223/597] tactic Signed-off-by: Nikolaj Bjorner --- doc/mk_tactic_doc.py | 10 +- src/tactic/bv/bv_bounds_tactic.cpp | 165 +++++++++++--------------- src/tactic/bv/bv_bounds_tactic.h | 27 ++++- src/tactic/bv/bvarray2uf_tactic.h | 26 ++-- src/tactic/bv/dt2bv_tactic.h | 23 +++- src/tactic/bv/elim_small_bv_tactic.h | 16 +++ src/tactic/bv/max_bv_sharing_tactic.h | 20 +++- 7 files changed, 170 insertions(+), 117 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index e4c71341a88..6f4837cddb6 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -53,13 +53,21 @@ def extract_tactic_doc(ous, f): if is_doc.search(line): generate_tactic_doc(ous, f, ins) +def find_tactic_name(path): + with open(path) as ins: + for line in ins: + m = is_tac_name.search(line) + if m: + return m.group(1) + return "" + def presort_files(): tac_files = [] for root, dirs, files in os.walk(doc_path("../src")): for f in files: if f.endswith("tactic.h"): tac_files += [(f, os.path.join(root, f))] - tac_files = sorted(tac_files, key = lambda x: x[0]) + tac_files = sorted(tac_files, key = lambda x: find_tactic_name(x[1])) return tac_files def help(ous): diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 72f0266c1cd..58183287d5c 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -35,11 +35,12 @@ namespace { struct interval { // l < h: [l, h] // l > h: [0, h] U [l, UMAX_INT] - uint64_t l, h; - unsigned sz; - bool tight; + uint64_t l = 0, h = 0; + unsigned sz = 0; + bool tight = true; interval() {} + interval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { // canonicalize full set if (is_wrapped() && l == h + 1) { @@ -50,8 +51,7 @@ namespace { } bool invariant() const { - return l <= uMaxInt(sz) && h <= uMaxInt(sz) && - (!is_wrapped() || l != h+1); + return l <= uMaxInt(sz) && h <= uMaxInt(sz) && (!is_wrapped() || l != h+1); } bool is_full() const { return l == 0 && h == uMaxInt(sz); } @@ -67,22 +67,17 @@ namespace { bool implies(const interval& b) const { if (b.is_full()) return true; - if (is_full()) + else if (is_full()) return false; - - if (is_wrapped()) { + else if (is_wrapped()) // l >= b.l >= b.h >= h - return b.is_wrapped() && h <= b.h && l >= b.l; - } - else if (b.is_wrapped()) { + return b.is_wrapped() && h <= b.h && l >= b.l; + else if (b.is_wrapped()) // b.l > b.h >= h >= l // h >= l >= b.l > b.h - return h <= b.h || l >= b.l; - } - else { - // - return l >= b.l && h <= b.h; - } + return h <= b.h || l >= b.l; + else + return l >= b.l && h <= b.h; } /// return false if intersection is unsat @@ -98,28 +93,26 @@ namespace { if (is_wrapped()) { if (b.is_wrapped()) { - if (h >= b.l) { + if (h >= b.l) result = b; - } else if (b.h >= l) { + else if (b.h >= l) result = *this; - } else { + else result = interval(std::max(l, b.l), std::min(h, b.h), sz); - } - } else { - return b.intersect(*this, result); } + else + return b.intersect(*this, result); } else if (b.is_wrapped()) { // ... b.h ... l ... h ... b.l .. - if (h < b.l && l > b.h) { - return false; - } + if (h < b.l && l > b.h) + return false; // ... l ... b.l ... h ... - if (h >= b.l && l <= b.h) { + if (h >= b.l && l <= b.h) result = b; - } else if (h >= b.l) { + else if (h >= b.l) result = interval(b.l, h, sz); - } else { + else { // ... l .. b.h .. h .. b.l ... SASSERT(l <= b.h); result = interval(l, std::min(h, b.h), sz); @@ -136,20 +129,16 @@ namespace { /// return false if negation is empty bool negate(interval& result) const { - if (!tight) { + if (!tight) result = interval(0, uMaxInt(sz), true); - return true; - } - - if (is_full()) + else if (is_full()) return false; - if (l == 0) { + else if (l == 0) result = interval(h + 1, uMaxInt(sz), sz); - } else if (uMaxInt(sz) == h) { + else if (uMaxInt(sz) == h) result = interval(0, l - 1, sz); - } else { - result = interval(h + 1, l - 1, sz); - } + else + result = interval(h + 1, l - 1, sz); return true; } }; @@ -163,9 +152,9 @@ namespace { struct undo_bound { - expr* e { nullptr }; + expr* e = nullptr; interval b; - bool fresh { false }; + bool fresh = false; undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} }; @@ -176,7 +165,7 @@ namespace { ast_manager& m; params_ref m_params; - bool m_propagate_eq; + bool m_propagate_eq = false; bv_util m_bv; vector m_scopes; map m_bound; @@ -224,7 +213,8 @@ namespace { v = lhs; return true; } - } else if (m.is_eq(e, lhs, rhs)) { + } + else if (m.is_eq(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { if (m_bv.is_numeral(rhs)) return false; @@ -252,7 +242,7 @@ namespace { } static void get_param_descrs(param_descrs& r) { - r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); + r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); } ~bv_bounds_simplifier() override { @@ -546,22 +536,19 @@ namespace { } static void get_param_descrs(param_descrs& r) { - r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); + r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); } ~dom_bv_bounds_simplifier() override { - for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { - dealloc(m_expr_vars[i]); - } - for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) { - dealloc(m_bound_exprs[i]); - } + for (auto* e : m_expr_vars) + dealloc(e); + for (auto* b : m_bound_exprs) + dealloc(b); } bool assert_expr(expr * t, bool sign) override { - while (m.is_not(t, t)) { - sign = !sign; - } + while (m.is_not(t, t)) + sign = !sign; interval b; expr* t1; @@ -581,7 +568,8 @@ namespace { return true; m_scopes.push_back(undo_bound(t1, old, false)); old = intr; - } else { + } + else { m_bound.insert(t1, b); m_scopes.push_back(undo_bound(t1, interval(), true)); } @@ -602,9 +590,8 @@ namespace { return; bool sign = false; - while (m.is_not(t, t)) { - sign = !sign; - } + while (m.is_not(t, t)) + sign = !sign; if (!is_bound(t, t1, b)) return; @@ -619,27 +606,21 @@ namespace { interval ctx, intr; bool was_updated = true; - if (b.is_full() && b.tight) { - r = m.mk_true(); - } + if (b.is_full() && b.tight) + r = m.mk_true(); else if (m_bound.find(t1, ctx)) { - if (ctx.implies(b)) { - r = m.mk_true(); - } - else if (!b.intersect(ctx, intr)) { - r = m.mk_false(); - } - else if (m_propagate_eq && intr.is_singleton()) { + if (ctx.implies(b)) + r = m.mk_true(); + else if (!b.intersect(ctx, intr)) + r = m.mk_false(); + else if (m_propagate_eq && intr.is_singleton()) r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), - t1->get_sort())); - } - else { - was_updated = false; - } - } - else { - was_updated = false; + t1->get_sort())); + else + was_updated = false; } + else + was_updated = false; TRACE("bv", tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); if (sign && was_updated) @@ -654,18 +635,16 @@ namespace { while (!todo.empty()) { t = todo.back(); todo.pop_back(); - if (mark.is_marked(t)) { - continue; - } + if (mark.is_marked(t)) + continue; if (t == v) { todo.reset(); return true; } mark.mark(t); - if (!is_app(t)) { - continue; - } + if (!is_app(t)) + continue; app* a = to_app(t); todo.append(a->get_num_args(), a->get_args()); } @@ -680,14 +659,11 @@ namespace { while (!todo.empty()) { t = todo.back(); todo.pop_back(); - if (mark1.is_marked(t)) { - continue; - } - mark1.mark(t); - - if (!is_app(t)) { - continue; - } + if (mark1.is_marked(t)) + continue; + mark1.mark(t); + if (!is_app(t)) + continue; interval b; expr* e; if (is_bound(t, e, b)) { @@ -718,14 +694,13 @@ namespace { m_scopes.reset(); return; } - for (unsigned i = m_scopes.size()-1; i >= target; --i) { + for (unsigned i = m_scopes.size(); i-- > target; ) { undo_bound& undo = m_scopes[i]; SASSERT(m_bound.contains(undo.e)); - if (undo.fresh) { + if (undo.fresh) m_bound.erase(undo.e); - } else { - m_bound.insert(undo.e, undo.b); - } + else + m_bound.insert(undo.e, undo.b); } m_scopes.shrink(target); } diff --git a/src/tactic/bv/bv_bounds_tactic.h b/src/tactic/bv/bv_bounds_tactic.h index 58de42199bf..325cca89e2b 100644 --- a/src/tactic/bv/bv_bounds_tactic.h +++ b/src/tactic/bv/bv_bounds_tactic.h @@ -5,15 +5,34 @@ Module Name: bv_bounds_tactic.h -Abstract: - - Contextual bounds simplification tactic. - Author: Nuno Lopes (nlopes) 2016-2-12 Nikolaj Bjorner (nbjorner) +Tactic Documentation: + +## Tactic propagate-bv-bounds + +### Short Description + +Contextual bounds simplification tactic. + +### Example + +```z3 +(declare-const x (_ BitVec 32)) +(declare-const y (_ BitVec 32)) +(declare-const z (_ BitVec 32)) +(assert (bvule (_ bv4 32) x)) +(assert (bvule x (_ bv24 32))) +(assert (or (bvule x (_ bv100 32)) (bvule (_ bv32 32) x))) +(apply propagate-bv-bounds) +``` + +### Notes + +* assumes that bit-vector inequalities have been simplified to use bvule/bvsle --*/ #pragma once diff --git a/src/tactic/bv/bvarray2uf_tactic.h b/src/tactic/bv/bvarray2uf_tactic.h index a22a78f86aa..393ab164c5d 100644 --- a/src/tactic/bv/bvarray2uf_tactic.h +++ b/src/tactic/bv/bvarray2uf_tactic.h @@ -3,18 +3,30 @@ Copyright (c) 2015 Microsoft Corporation Module Name: - bvarray2ufbvarray2uf_tactic.h - -Abstract: - - Tactic that rewrites bit-vector arrays into bit-vector - (uninterpreted) functions. + bvarray2uf_tactic.h Author: Christoph (cwinter) 2015-11-04 -Notes: +Tactic Documentation: + +## Tactic bvarray2uf + +### Short Description + +Tactic that rewrites bit-vector arrays into bit-vector +(uninterpreted) functions. + +### Example + +```z3 +(declare-const a (Array (_ BitVec 32) (_ BitVec 32))) +(declare-const b (_ BitVec 32)) +(declare-const c (_ BitVec 32)) +(assert (= (select a b) c)) +(apply bvarray2uf) +``` --*/ #pragma once diff --git a/src/tactic/bv/dt2bv_tactic.h b/src/tactic/bv/dt2bv_tactic.h index 906386ed4ed..05713dfd6b0 100644 --- a/src/tactic/bv/dt2bv_tactic.h +++ b/src/tactic/bv/dt2bv_tactic.h @@ -5,15 +5,28 @@ Module Name: dt2bv_tactic.h -Abstract: - - Tactic that eliminates finite domain data-types. - Author: nbjorner 2016-07-22 -Revision History: +Tactic Documentation + +## Tactic dt2bv + +### Short Description + +Tactic that eliminates finite domain data-types. + +### Example + +```z3 +(declare-datatypes ((Color 0)) (((Red) (Blue) (Green) (DarkBlue) (MetallicBlack) (MetallicSilver) (Silver) (Black)))) +(declare-const x Color) +(declare-const y Color) +(assert (not (= x y))) +(assert (not (= x Red))) +(apply dt2bv) +``` --*/ #pragma once diff --git a/src/tactic/bv/elim_small_bv_tactic.h b/src/tactic/bv/elim_small_bv_tactic.h index e4a91f70fe4..46e6dca3938 100644 --- a/src/tactic/bv/elim_small_bv_tactic.h +++ b/src/tactic/bv/elim_small_bv_tactic.h @@ -15,6 +15,22 @@ Module Name: Revision History: +Tactic Documentation + +## Tactic elim-small-bv + +### Short Description + +Eliminate small, quantified bit-vectors by expansion + +### Example + +```z3 +(declare-fun p ((_ BitVec 2)) Bool) +(assert (forall ((x (_ BitVec 2))) (p x))) +(apply elim-small-bv) +``` + --*/ #pragma once diff --git a/src/tactic/bv/max_bv_sharing_tactic.h b/src/tactic/bv/max_bv_sharing_tactic.h index bf14e9f14e7..2f21ee4b9e8 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.h +++ b/src/tactic/bv/max_bv_sharing_tactic.h @@ -7,16 +7,26 @@ Module Name: Abstract: - Rewriter for "maximing" the number of shared terms. - The idea is to rewrite AC terms to maximize sharing. - This rewriter is particularly useful for reducing - the number of Adders and Multipliers before "bit-blasting". + Author: Leonardo de Moura (leonardo) 2011-12-29. -Revision History: +Tactic Documentation + +## Tactic max-bv-sharing + +### Short Description + +Use heuristics to maximize the sharing of bit-vector expressions such as adders and multipliers + +### Long Description + +Rewriter for "maximing" the number of shared terms. +The idea is to rewrite AC terms to maximize sharing. +This rewriter is particularly useful for reducing +the number of Adders and Multipliers before "bit-blasting". --*/ #pragma once From 603597a22ebdfc90a1cdddf5ed0f41ef89bc00a8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 19 Dec 2022 12:40:39 -0800 Subject: [PATCH 224/597] deal with cancellation in qe for #6500 Signed-off-by: Nikolaj Bjorner --- src/qe/qe.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index d859880d811..f4ebce594ad 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1437,13 +1437,12 @@ namespace qe { res = m_solver.check(); if (res == l_true && has_uninterpreted(m, m_fml)) res = l_undef; - if (res == l_true) { + if (res == l_true) + res = final_check(); + if (res == l_true) is_sat = true; - final_check(); - } - else { + else break; - } } if (res == l_undef) { free_vars.append(num_vars, vars); @@ -1501,30 +1500,33 @@ namespace qe { private: - void final_check() { - model_ref model; + lbool final_check() { + model_ref model; m_solver.get_model(model); + if (!model) + return l_undef; scoped_ptr model_eval = alloc(model_evaluator, *model); - while (true) { + while (m.inc()) { TRACE("qe", model_v2_pp(tout, *model);); - while (can_propagate_assignment(*model_eval)) { + while (can_propagate_assignment(*model_eval)) propagate_assignment(*model_eval); - } VERIFY(CHOOSE_VAR == update_current(*model_eval, true)); SASSERT(m_current->fml()); if (l_true != m_solver.check()) { - return; + return l_true; } m_solver.get_model(model); model_eval = alloc(model_evaluator, *model); search_tree* st = m_current; update_current(*model_eval, false); - if (st == m_current) { + if (st == m_current) break; - } - } - pop(*model_eval); + } + if (!m.inc()) + return l_undef; + pop(*model_eval); + return l_true; } ast_manager& get_manager() override { return m; } From fe8034731d3f43fa9d0f8ebe5d72d085779752fe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 19 Dec 2022 21:02:23 -0800 Subject: [PATCH 225/597] fix #6501 --- src/ast/decl_collector.cpp | 29 +++++++++++++++-------------- src/smt/smt_context.cpp | 4 ++++ src/smt/smt_context.h | 2 ++ src/smt/theory_recfun.cpp | 2 +- src/tactic/core/simplify_tactic.h | 17 +++++++++++++++++ 5 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index 5b634abbdb1..7786a79d42c 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -32,9 +32,8 @@ void decl_collector::visit_sort(sort * n) { m_todo.push_back(cnstr); ptr_vector const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr); unsigned num_cas = cnstr_acc.size(); - for (unsigned j = 0; j < num_cas; j++) { - m_todo.push_back(cnstr_acc.get(j)); - } + for (unsigned j = 0; j < num_cas; j++) + m_todo.push_back(cnstr_acc.get(j)); } } for (unsigned i = n->get_num_parameters(); i-- > 0; ) { @@ -49,14 +48,19 @@ bool decl_collector::is_bool(sort * s) { void decl_collector::visit_func(func_decl * n) { func_decl* g; + if (!m_visited.is_marked(n)) { family_id fid = n->get_family_id(); if (fid == null_family_id) m_decls.push_back(n); else if (fid == m_rec_fid) { - m_rec_decls.push_back(n); recfun::util u(m()); - m_todo.push_back(u.get_def(n).get_rhs()); + if (u.has_def(n)) { + m_rec_decls.push_back(n); + m_todo.push_back(u.get_def(n).get_rhs()); + } + else + m_decls.push_back(n); } else if (m_ar_util.is_as_array(n, g)) m_todo.push_back(g); @@ -97,13 +101,11 @@ void decl_collector::visit(ast* n) { case AST_QUANTIFIER: { quantifier * q = to_quantifier(n); unsigned num_decls = q->get_num_decls(); - for (unsigned i = 0; i < num_decls; ++i) { - m_todo.push_back(q->get_decl_sort(i)); - } + for (unsigned i = 0; i < num_decls; ++i) + m_todo.push_back(q->get_decl_sort(i)); m_todo.push_back(q->get_expr()); - for (unsigned i = 0; i < q->get_num_patterns(); ++i) { - m_todo.push_back(q->get_pattern(i)); - } + for (unsigned i = 0; i < q->get_num_patterns(); ++i) + m_todo.push_back(q->get_pattern(i)); break; } case AST_SORT: @@ -111,9 +113,8 @@ void decl_collector::visit(ast* n) { break; case AST_FUNC_DECL: { func_decl * d = to_func_decl(n); - for (sort* srt : *d) { - m_todo.push_back(srt); - } + for (sort* srt : *d) + m_todo.push_back(srt); m_todo.push_back(d->get_range()); visit_func(d); break; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 45c469bde50..d1cf2d87521 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3027,6 +3027,10 @@ namespace smt { TRACE("end_assert_expr_ll", ast_mark m; m_asserted_formulas.display_ll(tout, m);); } + void context::add_asserted(expr* e) { + m_asserted_formulas.assert_expr(e); + } + void context::assert_expr(expr * e) { assert_expr(e, nullptr); } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 2b3a343f7a3..b6bf04a20e2 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1618,6 +1618,8 @@ namespace smt { void register_plugin(theory * th); + void add_asserted(expr* e); + void assert_expr(expr * e); void assert_expr(expr * e, proof * pr); diff --git a/src/smt/theory_recfun.cpp b/src/smt/theory_recfun.cpp index 515a51a17d1..6a8f2ab60e2 100644 --- a/src/smt/theory_recfun.cpp +++ b/src/smt/theory_recfun.cpp @@ -249,7 +249,7 @@ namespace smt { expr_ref eq1(m.mk_eq(l, r), m); expr_ref fn(m.mk_fresh_const("rec-eq", m.mk_bool_sort()), m); expr_ref eq(m.mk_eq(fn, eq1), m); - ctx.assert_expr(eq); + ctx.add_asserted(eq); ctx.internalize_assertions(); lit = mk_literal(fn); } diff --git a/src/tactic/core/simplify_tactic.h b/src/tactic/core/simplify_tactic.h index 39e3e9bdeec..1594b3d3795 100644 --- a/src/tactic/core/simplify_tactic.h +++ b/src/tactic/core/simplify_tactic.h @@ -41,6 +41,23 @@ from two equivalent formulas are guaranteed to be equal. (apply simplify) ``` +The simplifier is also exposed as a stand-alone command. +There are several options to control its behavior. + +```z3 +(declare-const x Int) +(declare-const y Int) +(declare-const z Int) +(declare-const u Int) +(declare-fun p (Int) Bool) +(assert (p (* (+ x y) (+ z u)))) +(apply simplify) +(apply (with simplify :som true)) + +(simplify (* (+ x y) (+ z u)) :som false) +(simplify (* (+ x y) (+ z u)) :som true) +``` + ### Notes * supports unsat cores, proof terms From 8efaaaf24982ce810b8ea85fdf74eedc3dea29ad Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 25 Dec 2022 17:28:57 -0800 Subject: [PATCH 226/597] Fix #6503 Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index e829fdafb70..ca3486ac570 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1559,13 +1559,15 @@ def __rmul__(self, other): def __mul__(self, other): """Create the Z3 expression `self * other`. """ - if other == 1: + if isinstance(other, int) and other == 1: return self - if other == 0: - return 0 + if isinstance(other, int) and other == 0: + return + if isinstance(other, BoolRef): + other = If(other, 1, 0) return If(self, other, 0) - + def is_bool(a): """Return `True` if `a` is a Z3 Boolean expression. From b9c4f5d4fafcace3968eb3a5c5489a4c881628a0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 25 Dec 2022 18:33:01 -0800 Subject: [PATCH 227/597] #6506 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/elim_unconstrained.cpp | 74 ++++++++++++++++++++-- src/ast/simplifiers/elim_unconstrained.h | 4 ++ 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 2e950868a5e..df6f92251c8 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -81,20 +81,24 @@ void elim_unconstrained::eliminate() { continue; } app* t = to_app(e); - m_args.reset(); + unsigned sz = m_args.size(); for (expr* arg : *to_app(t)) - m_args.push_back(get_node(arg).m_term); - if (!m_inverter(t->get_decl(), m_args.size(), m_args.data(), r, side_cond)) { + m_args.push_back(reconstruct_term(get_node(arg))); + bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r, side_cond); + n.m_refcount = 0; + m_args.shrink(sz); + if (!inverted) { IF_VERBOSE(11, verbose_stream() << "not inverted " << mk_bounded_pp(e, m) << "\n"); - n.m_refcount = 0; continue; } + + TRACE("elim_unconstrained", tout << mk_pp(t, m) << " -> " << r << "\n"); SASSERT(r->get_sort() == t->get_sort()); m_stats.m_num_eliminated++; - n.m_refcount = 0; m_trail.push_back(r); SASSERT(r); gc(e); + invalidate_parents(e); freeze_rec(r); m_root.setx(r->get_id(), e->get_id(), UINT_MAX); @@ -119,6 +123,26 @@ expr* elim_unconstrained::get_parent(unsigned n) const { return p; return nullptr; } + +void elim_unconstrained::invalidate_parents(expr* e) { + ptr_vector todo; + do { + node& n = get_node(e); + if (!n.m_dirty) { + n.m_dirty = true; + for (expr* e : n.m_parents) + todo.push_back(e); + } + e = nullptr; + if (!todo.empty()) { + e = todo.back(); + todo.pop_back(); + } + } + while (e); +} + + /** * initialize node structure */ @@ -230,6 +254,45 @@ void elim_unconstrained::gc(expr* t) { } } +expr_ref elim_unconstrained::reconstruct_term(node& n0) { + expr* t = n0.m_term; + if (!n0.m_dirty) + return expr_ref(t, m); + ptr_vector todo; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + node& n = get_node(t); + unsigned sz0 = todo.size(); + if (is_app(t)) { + for (expr* arg : *to_app(t)) + if (get_node(arg).m_dirty) + todo.push_back(arg); + if (todo.size() != sz0) + continue; + + unsigned sz = m_args.size(); + for (expr* arg : *to_app(t)) + m_args.push_back(get_node(arg).m_term); + n.m_term = m.mk_app(to_app(t)->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + m_args.shrink(sz); + } + else if (is_quantifier(t)) { + expr* body = to_quantifier(t)->get_expr(); + node& n2 = get_node(body); + if (n2.m_dirty) { + todo.push_back(body); + continue; + } + n.m_term = m.update_quantifier(to_quantifier(t), n2.m_term); + } + m_trail.push_back(n.m_term); + todo.pop_back(); + n.m_dirty = false; + } + return expr_ref(n0.m_term, m); +} + /** * walk nodes starting from lowest depth and reconstruct their normalized forms. */ @@ -266,6 +329,7 @@ void elim_unconstrained::reconstruct_terms() { } } + void elim_unconstrained::assert_normalized(vector& old_fmls) { for (unsigned i : indices()) { diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 68ca30777c0..aae14bdcb8c 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -26,6 +26,7 @@ class elim_unconstrained : public dependent_expr_simplifier { unsigned m_refcount = 0; expr* m_term = nullptr; expr* m_orig = nullptr; + bool m_dirty = false; ptr_vector m_parents; }; struct var_lt { @@ -66,8 +67,11 @@ class elim_unconstrained : public dependent_expr_simplifier { void init_nodes(); void eliminate(); void reconstruct_terms(); + expr_ref reconstruct_term(node& n); void assert_normalized(vector& old_fmls); void update_model_trail(generic_model_converter& mc, vector const& old_fmls); + void invalidate_parents(expr* e); + public: From 6fab4fec230820c6c73221a3e85164d6faedcfbd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Dec 2022 15:36:58 -0800 Subject: [PATCH 228/597] #6508 --- src/api/z3_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 467f3079224..0cd5e119f91 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3763,7 +3763,7 @@ extern "C" { If \c s does not contain \c substr, then the value is -1, def_API('Z3_mk_seq_last_index', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_seq_last_index(Z3_context c, Z3_ast, Z3_ast substr); + Z3_ast Z3_API Z3_mk_seq_last_index(Z3_context c, Z3_ast s, Z3_ast substr); /** \brief Convert string to integer. From 8d332cc3a17ccdd6cf39490020ea7d7eeedf56a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Dec 2022 15:42:04 -0800 Subject: [PATCH 229/597] #6508 (#6509) --- src/api/z3_api.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 0cd5e119f91..cdcab36d501 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3893,7 +3893,7 @@ extern "C" { def_API('Z3_mk_re_power', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_ast Z3_API Z3_mk_re_power(Z3_context c, Z3_ast, unsigned n); + Z3_ast Z3_API Z3_mk_re_power(Z3_context c, Z3_ast re, unsigned n); /** \brief Create the intersection of the regular languages. @@ -5881,7 +5881,7 @@ extern "C" { def_API('Z3_eval_smtlib2_string', STRING, (_in(CONTEXT), _in(STRING),)) */ - Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str); + Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context c Z3_string str); /** @@ -7029,7 +7029,7 @@ extern "C" { def_API('Z3_solver_propagate_consequence', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, AST), _in(UINT), _in_array(4, AST), _in_array(4, AST), _in(AST))) */ - void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq); + void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback cb, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq); /** \brief Check whether the assertions in a given solver are consistent or not. From bc19992543c4793ae90f286d4834c782e9b23548 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 27 Dec 2022 12:02:08 -0800 Subject: [PATCH 230/597] add doc for ackermannize Signed-off-by: Nikolaj Bjorner --- src/ackermannization/ackermannize_bv_tactic.h | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/ackermannization/ackermannize_bv_tactic.h b/src/ackermannization/ackermannize_bv_tactic.h index 073a680bff0..b99ae00e47f 100644 --- a/src/ackermannization/ackermannize_bv_tactic.h +++ b/src/ackermannization/ackermannize_bv_tactic.h @@ -11,7 +11,37 @@ ackermannize_bv_tactic.h Mikolas Janota -Revision History: +Tactic Documentation: + +## Tactic ackernannize_bv + +### Short Description + +A tactic for performing Ackermann reduction for bit-vector formulas + +### Long Description + +The Ackermann reduction replaces uninterpreted functions $f(t_1), f(t_2)$ +by fresh variables $f_1, f_2$ and addes axioms $t_1 \simeq t_2 \implies f_1 \simeq f_2$. +The reduction has the effect of eliminating uninterpreted functions. When the reduction +produces a pure bit-vector benchmark, it allows Z3 to use a specialized SAT solver. + +### Example + +```z3 +(declare-const x (_ BitVec 32)) +(declare-const y (_ BitVec 32)) +(declare-fun f ((_ BitVec 32)) (_ BitVec 8)) + +(assert (not (= (f x) (f y)))) +(apply ackermannize_bv) +``` + +### Notes + +* does not support proofs, does not support unsatisfiable cores + + --*/ #pragma once From 3e8cbb66112aaa58710e8e23719ab864b51ad0fe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 27 Dec 2022 18:07:57 -0800 Subject: [PATCH 231/597] #5884 Signed-off-by: Nikolaj Bjorner --- src/api/z3_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index cdcab36d501..7a0b47da061 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -5881,7 +5881,7 @@ extern "C" { def_API('Z3_eval_smtlib2_string', STRING, (_in(CONTEXT), _in(STRING),)) */ - Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context c Z3_string str); + Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context c, Z3_string str); /** From ec74a874232b6b23b17c741a91fff75f8d2fa6c7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 27 Dec 2022 20:19:26 -0800 Subject: [PATCH 232/597] fix #6510 Signed-off-by: Nikolaj Bjorner --- Parameters.md | 605 ---------------------------------------- src/api/python/z3/z3.py | 4 +- 2 files changed, 2 insertions(+), 607 deletions(-) delete mode 100644 Parameters.md diff --git a/Parameters.md b/Parameters.md deleted file mode 100644 index ea69becade2..00000000000 --- a/Parameters.md +++ /dev/null @@ -1,605 +0,0 @@ -## Module pi - -Description: pattern inference (heuristics) for universal formulas (without annotation) - Parameter | Type | Description | Default - ----------|------|-------------|-------- -arith | unsigned int | 0 - do not infer patterns with arithmetic terms, 1 - use patterns with arithmetic terms if there is no other pattern, 2 - always use patterns with arithmetic terms | 1 -arith_weight | unsigned int | default weight for quantifiers where the only available pattern has nested arithmetic terms | 5 -block_loop_patterns | bool | block looping patterns during pattern inference | true -max_multi_patterns | unsigned int | when patterns are not provided, the prover uses a heuristic to infer them, this option sets the threshold on the number of extra multi-patterns that can be created; by default, the prover creates at most one multi-pattern when there is no unary pattern | 0 -non_nested_arith_weight | unsigned int | default weight for quantifiers where the only available pattern has non nested arithmetic terms | 10 -pull_quantifiers | bool | pull nested quantifiers, if no pattern was found | true -use_database | bool | use pattern database | false -warnings | bool | enable/disable warning messages in the pattern inference module | false - -## Module tactic - -Description: tactic parameters - Parameter | Type | Description | Default - ----------|------|-------------|-------- -blast_term_ite.max_inflation | unsigned int | multiplicative factor of initial term size. | 4294967295 -blast_term_ite.max_steps | unsigned int | maximal number of steps allowed for tactic. | 4294967295 -default_tactic | symbol | overwrite default tactic in strategic solver | -propagate_values.max_rounds | unsigned int | maximal number of rounds to propagate values. | 4 -solve_eqs.context_solve | bool | solve equalities within disjunctions. | true -solve_eqs.ite_solver | bool | use if-then-else solvers. | true -solve_eqs.max_occs | unsigned int | maximum number of occurrences for considering a variable for gaussian eliminations. | 4294967295 -solve_eqs.theory_solver | bool | use theory solvers. | true - -## Module pp - -Description: pretty printer - Parameter | Type | Description | Default - ----------|------|-------------|-------- -bounded | bool | ignore characters exceeding max width | false -bv_literals | bool | use Bit-Vector literals (e.g, #x0F and #b0101) during pretty printing | true -bv_neg | bool | use bvneg when displaying Bit-Vector literals where the most significant bit is 1 | false -decimal | bool | pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise | false -decimal_precision | unsigned int | maximum number of decimal places to be used when pp.decimal=true | 10 -fixed_indent | bool | use a fixed indentation for applications | false -flat_assoc | bool | flat associative operators (when pretty printing SMT2 terms/formulas) | true -fp_real_literals | bool | use real-numbered floating point literals (e.g, +1.0p-1) during pretty printing | false -max_depth | unsigned int | max. term depth (when pretty printing SMT2 terms/formulas) | 5 -max_indent | unsigned int | max. indentation in pretty printer | 4294967295 -max_num_lines | unsigned int | max. number of lines to be displayed in pretty printer | 4294967295 -max_ribbon | unsigned int | max. ribbon (width - indentation) in pretty printer | 80 -max_width | unsigned int | max. width in pretty printer | 80 -min_alias_size | unsigned int | min. size for creating an alias for a shared term (when pretty printing SMT2 terms/formulas) | 10 -pretty_proof | bool | use slower, but prettier, printer for proofs | false -simplify_implies | bool | simplify nested implications for pretty printing | true -single_line | bool | ignore line breaks when true | false - -## Module sat - -Description: propositional SAT solver - Parameter | Type | Description | Default - ----------|------|-------------|-------- -abce | bool | eliminate blocked clauses using asymmetric literals | false -acce | bool | eliminate covered clauses using asymmetric added literals | false -anf | bool | enable ANF based simplification in-processing | false -anf.delay | unsigned int | delay ANF simplification by in-processing round | 2 -anf.exlin | bool | enable extended linear simplification | false -asymm_branch | bool | asymmetric branching | true -asymm_branch.all | bool | asymmetric branching on all literals per clause | false -asymm_branch.delay | unsigned int | number of simplification rounds to wait until invoking asymmetric branch simplification | 1 -asymm_branch.limit | unsigned int | approx. maximum number of literals visited during asymmetric branching | 100000000 -asymm_branch.rounds | unsigned int | maximal number of rounds to run asymmetric branch simplifications if progress is made | 2 -asymm_branch.sampled | bool | use sampling based asymmetric branching based on binary implication graph | true -ate | bool | asymmetric tautology elimination | true -backtrack.conflicts | unsigned int | number of conflicts before enabling chronological backtracking | 4000 -backtrack.scopes | unsigned int | number of scopes to enable chronological backtracking | 100 -bca | bool | blocked clause addition - add blocked binary clauses | false -bce | bool | eliminate blocked clauses | false -bce_at | unsigned int | eliminate blocked clauses only once at the given simplification round | 2 -bce_delay | unsigned int | delay eliminate blocked clauses until simplification round | 2 -binspr | bool | enable SPR inferences of binary propagation redundant clauses. This inprocessing step eliminates models | false -blocked_clause_limit | unsigned int | maximum number of literals visited during blocked clause elimination | 100000000 -branching.anti_exploration | bool | apply anti-exploration heuristic for branch selection | false -branching.heuristic | symbol | branching heuristic vsids, chb | vsids -burst_search | unsigned int | number of conflicts before first global simplification | 100 -cardinality.encoding | symbol | encoding used for at-most-k constraints: grouped, bimander, ordered, unate, circuit | grouped -cardinality.solver | bool | use cardinality solver | true -cce | bool | eliminate covered clauses | false -core.minimize | bool | minimize computed core | false -core.minimize_partial | bool | apply partial (cheap) core minimization | false -cut | bool | enable AIG based simplification in-processing | false -cut.aig | bool | extract aigs (and ites) from cluases for cut simplification | false -cut.delay | unsigned int | delay cut simplification by in-processing round | 2 -cut.dont_cares | bool | integrate dont cares with cuts | true -cut.force | bool | force redoing cut-enumeration until a fixed-point | false -cut.lut | bool | extract luts from clauses for cut simplification | false -cut.npn3 | bool | extract 3 input functions from clauses for cut simplification | false -cut.redundancies | bool | integrate redundancy checking of cuts | true -cut.xor | bool | extract xors from clauses for cut simplification | false -ddfw.init_clause_weight | unsigned int | initial clause weight for DDFW local search | 8 -ddfw.reinit_base | unsigned int | increment basis for geometric backoff scheme of re-initialization of weights | 10000 -ddfw.restart_base | unsigned int | number of flips used a starting point for hessitant restart backoff | 100000 -ddfw.threads | unsigned int | number of ddfw threads to run in parallel with sat solver | 0 -ddfw.use_reward_pct | unsigned int | percentage to pick highest reward variable when it has reward 0 | 15 -ddfw_search | bool | use ddfw local search instead of CDCL | false -dimacs.core | bool | extract core from DIMACS benchmarks | false -drat.activity | bool | dump variable activities | false -drat.binary | bool | use Binary DRAT output format | false -drat.check_sat | bool | build up internal trace, check satisfying model | false -drat.check_unsat | bool | build up internal proof and check | false -drat.file | symbol | file to dump DRAT proofs | -drup.trim | bool | build and trim drup proof | false -dyn_sub_res | bool | dynamic subsumption resolution for minimizing learned clauses | true -elim_vars | bool | enable variable elimination using resolution during simplification | true -elim_vars_bdd | bool | enable variable elimination using BDD recompilation during simplification | true -elim_vars_bdd_delay | unsigned int | delay elimination of variables using BDDs until after simplification round | 3 -enable_pre_simplify | bool | enable pre simplifications before the bounded search | false -euf | bool | enable euf solver (this feature is preliminary and not ready for general consumption) | false -force_cleanup | bool | force cleanup to remove tautologies and simplify clauses | false -gc | symbol | garbage collection strategy: psm, glue, glue_psm, dyn_psm | glue_psm -gc.burst | bool | perform eager garbage collection during initialization | false -gc.defrag | bool | defragment clauses when garbage collecting | true -gc.increment | unsigned int | increment to the garbage collection threshold | 500 -gc.initial | unsigned int | learned clauses garbage collection frequency | 20000 -gc.k | unsigned int | learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm) | 7 -gc.small_lbd | unsigned int | learned clauses with small LBD are never deleted (only used in dyn_psm) | 3 -inprocess.max | unsigned int | maximal number of inprocessing passes | 4294967295 -inprocess.out | symbol | file to dump result of the first inprocessing step and exit | -local_search | bool | use local search instead of CDCL | false -local_search_dbg_flips | bool | write debug information for number of flips | false -local_search_mode | symbol | local search algorithm, either default wsat or qsat | wsat -local_search_threads | unsigned int | number of local search threads to find satisfiable solution | 0 -lookahead.cube.cutoff | symbol | cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat | depth -lookahead.cube.depth | unsigned int | cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth. | 1 -lookahead.cube.fraction | double | adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat | 0.4 -lookahead.cube.freevars | double | cube free variable fraction. Used when lookahead.cube.cutoff is freevars | 0.8 -lookahead.cube.psat.clause_base | double | clause base for PSAT cutoff | 2 -lookahead.cube.psat.trigger | double | trigger value to create lookahead cubes for PSAT cutoff. Used when lookahead.cube.cutoff is psat | 5 -lookahead.cube.psat.var_exp | double | free variable exponent for PSAT cutoff | 1 -lookahead.delta_fraction | double | number between 0 and 1, the smaller the more literals are selected for double lookahead | 1.0 -lookahead.double | bool | enable doubld lookahead | true -lookahead.global_autarky | bool | prefer to branch on variables that occur in clauses that are reduced | false -lookahead.preselect | bool | use pre-selection of subset of variables for branching | false -lookahead.reward | symbol | select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu | march_cu -lookahead.use_learned | bool | use learned clauses when selecting lookahead literal | false -lookahead_scores | bool | extract lookahead scores. A utility that can only be used from the DIMACS front-end | false -lookahead_simplify | bool | use lookahead solver during simplification | false -lookahead_simplify.bca | bool | add learned binary clauses as part of lookahead simplification | true -max_conflicts | unsigned int | maximum number of conflicts | 4294967295 -max_memory | unsigned int | maximum amount of memory in megabytes | 4294967295 -minimize_lemmas | bool | minimize learned clauses | true -override_incremental | bool | override incremental safety gaps. Enable elimination of blocked clauses and variables even if solver is reused | false -pb.lemma_format | symbol | generate either cardinality or pb lemmas | cardinality -pb.min_arity | unsigned int | minimal arity to compile pb/cardinality constraints to CNF | 9 -pb.resolve | symbol | resolution strategy for boolean algebra solver: cardinality, rounding | cardinality -pb.solver | symbol | method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), binary_merge, segmented, solver (use native solver) | solver -phase | symbol | phase selection strategy: always_false, always_true, basic_caching, random, caching | caching -phase.sticky | bool | use sticky phase caching | true -prob_search | bool | use probsat local search instead of CDCL | false -probing | bool | apply failed literal detection during simplification | true -probing_binary | bool | probe binary clauses | true -probing_cache | bool | add binary literals as lemmas | true -probing_cache_limit | unsigned int | cache binaries unless overall memory usage exceeds cache limit | 1024 -probing_limit | unsigned int | limit to the number of probe calls | 5000000 -propagate.prefetch | bool | prefetch watch lists for assigned literals | true -random_freq | double | frequency of random case splits | 0.01 -random_seed | unsigned int | random seed | 0 -reorder.activity_scale | unsigned int | scaling factor for activity update | 100 -reorder.base | unsigned int | number of conflicts per random reorder | 4294967295 -reorder.itau | double | inverse temperature for softmax | 4.0 -rephase.base | unsigned int | number of conflicts per rephase | 1000 -resolution.cls_cutoff1 | unsigned int | limit1 - total number of problems clauses for the second cutoff of Boolean variable elimination | 100000000 -resolution.cls_cutoff2 | unsigned int | limit2 - total number of problems clauses for the second cutoff of Boolean variable elimination | 700000000 -resolution.limit | unsigned int | approx. maximum number of literals visited during variable elimination | 500000000 -resolution.lit_cutoff_range1 | unsigned int | second cutoff (total number of literals) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses | 700 -resolution.lit_cutoff_range2 | unsigned int | second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff1 and less than res_cls_cutoff2 | 400 -resolution.lit_cutoff_range3 | unsigned int | second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff2 | 300 -resolution.occ_cutoff | unsigned int | first cutoff (on number of positive/negative occurrences) for Boolean variable elimination | 10 -resolution.occ_cutoff_range1 | unsigned int | second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses | 8 -resolution.occ_cutoff_range2 | unsigned int | second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing more than res_cls_cutoff1 and less than res_cls_cutoff2 | 5 -resolution.occ_cutoff_range3 | unsigned int | second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing more than res_cls_cutoff2 | 3 -restart | symbol | restart strategy: static, luby, ema or geometric | ema -restart.emafastglue | double | ema alpha factor for fast moving average | 0.03 -restart.emaslowglue | double | ema alpha factor for slow moving average | 1e-05 -restart.factor | double | restart increment factor for geometric strategy | 1.5 -restart.fast | bool | use fast restart approach only removing less active literals. | true -restart.initial | unsigned int | initial restart (number of conflicts) | 2 -restart.margin | double | margin between fast and slow restart factors. For ema | 1.1 -restart.max | unsigned int | maximal number of restarts. | 4294967295 -retain_blocked_clauses | bool | retain blocked clauses as lemmas | true -scc | bool | eliminate Boolean variables by computing strongly connected components | true -scc.tr | bool | apply transitive reduction, eliminate redundant binary clauses | true -search.sat.conflicts | unsigned int | period for solving for sat (in number of conflicts) | 400 -search.unsat.conflicts | unsigned int | period for solving for unsat (in number of conflicts) | 400 -simplify.delay | unsigned int | set initial delay of simplification by a conflict count | 0 -subsumption | bool | eliminate subsumed clauses | true -subsumption.limit | unsigned int | approx. maximum number of literals visited during subsumption (and subsumption resolution) | 100000000 -threads | unsigned int | number of parallel threads to use | 1 -variable_decay | unsigned int | multiplier (divided by 100) for the VSIDS activity increment | 110 - -## Module solver - -Description: solver parameters - Parameter | Type | Description | Default - ----------|------|-------------|-------- -axioms2files | bool | print negated theory axioms to separate files during search | false -cancel_backup_file | symbol | file to save partial search state if search is canceled | -lemmas2console | bool | print lemmas during search | false -smtlib2_log | symbol | file to save solver interaction | -timeout | unsigned int | timeout on the solver object; overwrites a global timeout | 4294967295 - -## Module opt - -Description: optimization parameters - Parameter | Type | Description | Default - ----------|------|-------------|-------- -dump_benchmarks | bool | dump benchmarks for profiling | false -dump_models | bool | display intermediary models to stdout | false -elim_01 | bool | eliminate 01 variables | true -enable_core_rotate | bool | enable core rotation to both sample cores and correction sets | false -enable_lns | bool | enable LNS during weighted maxsat | false -enable_sat | bool | enable the new SAT core for propositional constraints | true -enable_sls | bool | enable SLS tuning during weighted maxsat | false -incremental | bool | set incremental mode. It disables pre-processing and enables adding constraints in model event handler | false -lns_conflicts | unsigned int | initial conflict count for LNS search | 1000 -maxlex.enable | bool | enable maxlex heuristic for lexicographic MaxSAT problems | true -maxres.add_upper_bound_block | bool | restict upper bound with constraint | false -maxres.hill_climb | bool | give preference for large weight cores | true -maxres.max_core_size | unsigned int | break batch of generated cores if size reaches this number | 3 -maxres.max_correction_set_size | unsigned int | allow generating correction set constraints up to maximal size | 3 -maxres.max_num_cores | unsigned int | maximal number of cores per round | 200 -maxres.maximize_assignment | bool | find an MSS/MCS to improve current assignment | false -maxres.pivot_on_correction_set | bool | reduce soft constraints if the current correction set is smaller than current core | true -maxres.wmax | bool | use weighted theory solver to constrain upper bounds | false -maxsat_engine | symbol | select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres', 'maxres-bin', 'rc2' | maxres -optsmt_engine | symbol | select optimization engine: 'basic', 'symba' | basic -pb.compile_equality | bool | compile arithmetical equalities into pseudo-Boolean equality (instead of two inequalites) | false -pp.neat | bool | use neat (as opposed to less readable, but faster) pretty printer when displaying context | true -pp.wcnf | bool | print maxsat benchmark into wcnf format | false -priority | symbol | select how to priortize objectives: 'lex' (lexicographic), 'pareto', 'box' | lex -rc2.totalizer | bool | use totalizer for rc2 encoding | true -rlimit | unsigned int | resource limit (0 means no limit) | 0 -solution_prefix | symbol | path prefix to dump intermediary, but non-optimal, solutions | -timeout | unsigned int | timeout (in milliseconds) (UINT_MAX and 0 mean no timeout) | 4294967295 - -## Module parallel - -Description: parameters for parallel solver - Parameter | Type | Description | Default - ----------|------|-------------|-------- -conquer.backtrack_frequency | unsigned int | frequency to apply core minimization during conquer | 10 -conquer.batch_size | unsigned int | number of cubes to batch together for fast conquer | 100 -conquer.delay | unsigned int | delay of cubes until applying conquer | 10 -conquer.restart.max | unsigned int | maximal number of restarts during conquer phase | 5 -enable | bool | enable parallel solver by default on selected tactics (for QF_BV) | false -simplify.exp | double | restart and inprocess max is multiplied by simplify.exp ^ depth | 1 -simplify.inprocess.max | unsigned int | maximal number of inprocessing steps during simplification | 2 -simplify.max_conflicts | unsigned int | maximal number of conflicts during simplifcation phase | 4294967295 -simplify.restart.max | unsigned int | maximal number of restarts during simplification phase | 5000 -threads.max | unsigned int | caps maximal number of threads below the number of processors | 10000 - -## Module nnf - -Description: negation normal form - Parameter | Type | Description | Default - ----------|------|-------------|-------- -ignore_labels | bool | remove/ignore labels in the input formula, this option is ignored if proofs are enabled | false -max_memory | unsigned int | maximum amount of memory in megabytes | 4294967295 -mode | symbol | NNF translation mode: skolem (skolem normal form), quantifiers (skolem normal form + quantifiers in NNF), full | skolem -sk_hack | bool | hack for VCC | false - -## Module algebraic - -Description: real algebraic number package. Non-default parameter settings are not supported - Parameter | Type | Description | Default - ----------|------|-------------|-------- -factor | bool | use polynomial factorization to simplify polynomials representing algebraic numbers | true -factor_max_prime | unsigned int | parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter limits the maximum prime number p to be used in the first step | 31 -factor_num_primes | unsigned int | parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. The search space may be reduced by factoring the polynomial in different GF(p)'s. This parameter specify the maximum number of finite factorizations to be considered, before lifiting and searching | 1 -factor_search_size | unsigned int | parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter can be used to limit the search space | 5000 -min_mag | unsigned int | Z3 represents algebraic numbers using a (square-free) polynomial p and an isolating interval (which contains one and only one root of p). This interval may be refined during the computations. This parameter specifies whether to cache the value of a refined interval or not. It says the minimal size of an interval for caching purposes is 1/2^16 | 16 -zero_accuracy | unsigned int | one of the most time-consuming operations in the real algebraic number module is determining the sign of a polynomial evaluated at a sample point with non-rational algebraic number values. Let k be the value of this option. If k is 0, Z3 uses precise computation. Otherwise, the result of a polynomial evaluation is considered to be 0 if Z3 can show it is inside the interval (-1/2^k, 1/2^k) | 0 - -## Module combined_solver - -Description: combines two solvers: non-incremental (solver1) and incremental (solver2) - Parameter | Type | Description | Default - ----------|------|-------------|-------- -ignore_solver1 | bool | if true, solver 2 is always used | false -solver2_timeout | unsigned int | fallback to solver 1 after timeout even when in incremental model | 4294967295 -solver2_unknown | unsigned int | what should be done when solver 2 returns unknown: 0 - just return unknown, 1 - execute solver 1 if quantifier free problem, 2 - execute solver 1 | 1 - -## Module rcf - -Description: real closed fields - Parameter | Type | Description | Default - ----------|------|-------------|-------- -clean_denominators | bool | clean denominators before root isolation | true -inf_precision | unsigned int | a value k that is the initial interval size (i.e., (0, 1/2^l)) used as an approximation for infinitesimal values | 24 -initial_precision | unsigned int | a value k that is the initial interval size (as 1/2^k) when creating transcendentals and approximated division | 24 -lazy_algebraic_normalization | bool | during sturm-seq and square-free polynomial computations, only normalize algebraic polynomial expressions when the defining polynomial is monic | true -max_precision | unsigned int | during sign determination we switch from interval arithmetic to complete methods when the interval size is less than 1/2^k, where k is the max_precision | 128 -use_prem | bool | use pseudo-remainder instead of remainder when computing GCDs and Sturm-Tarski sequences | true -ERROR: unknown module 'rewriter, description: new formula simplification module used in the tactic framework' - -## Module ackermannization - -Description: solving UF via ackermannization - Parameter | Type | Description | Default - ----------|------|-------------|-------- -eager | bool | eagerly instantiate all congruence rules | true -inc_sat_backend | bool | use incremental SAT | false -sat_backend | bool | use SAT rather than SMT in qfufbv_ackr_tactic | false - -## Module nlsat - -Description: nonlinear solver - Parameter | Type | Description | Default - ----------|------|-------------|-------- -check_lemmas | bool | check lemmas on the fly using an independent nlsat solver | false -factor | bool | factor polynomials produced during conflict resolution. | true -inline_vars | bool | inline variables that can be isolated from equations (not supported in incremental mode) | false -lazy | unsigned int | how lazy the solver is. | 0 -log_lemmas | bool | display lemmas as self-contained SMT formulas | false -max_conflicts | unsigned int | maximum number of conflicts. | 4294967295 -max_memory | unsigned int | maximum amount of memory in megabytes | 4294967295 -minimize_conflicts | bool | minimize conflicts | false -randomize | bool | randomize selection of a witness in nlsat. | true -reorder | bool | reorder variables. | true -seed | unsigned int | random seed. | 0 -shuffle_vars | bool | use a random variable order. | false -simplify_conflicts | bool | simplify conflicts using equalities before resolving them in nlsat solver. | true - - -## Module fp - -Description: fixedpoint parameters - Parameter | Type | Description | Default - ----------|------|-------------|-------- -bmc.linear_unrolling_depth | unsigned int | Maximal level to explore | 4294967295 -datalog.all_or_nothing_deltas | bool | compile rules so that it is enough for the delta relation in union and widening operations to determine only whether the updated relation was modified or not | false -datalog.check_relation | symbol | name of default relation to check. operations on the default relation will be verified using SMT solving | null -datalog.compile_with_widening | bool | widening will be used to compile recursive rules | false -datalog.dbg_fpr_nonempty_relation_signature | bool | if true, finite_product_relation will attempt to avoid creating inner relation with empty signature by putting in half of the table columns, if it would have been empty otherwise | false -datalog.default_relation | symbol | default relation implementation: external_relation, pentagon | pentagon -datalog.default_table | symbol | default table implementation: sparse, hashtable, bitvector, interval | sparse -datalog.default_table_checked | bool | if true, the default table will be default_table inside a wrapper that checks that its results are the same as of default_table_checker table | false -datalog.default_table_checker | symbol | see default_table_checked | null -datalog.explanations_on_relation_level | bool | if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect) | false -datalog.generate_explanations | bool | produce explanations for produced facts when using the datalog engine | false -datalog.initial_restart_timeout | unsigned int | length of saturation run before the first restart (in ms), zero means no restarts | 0 -datalog.magic_sets_for_queries | bool | magic set transformation will be used for queries | false -datalog.output_profile | bool | determines whether profile information should be output when outputting Datalog rules or instructions | false -datalog.print.tuples | bool | determines whether tuples for output predicates should be output | true -datalog.profile_timeout_milliseconds | unsigned int | instructions and rules that took less than the threshold will not be printed when printed the instruction/rule list | 0 -datalog.similarity_compressor | bool | rules that differ only in values of constants will be merged into a single rule | true -datalog.similarity_compressor_threshold | unsigned int | if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged | 11 -datalog.subsumption | bool | if true, removes/filters predicates with total transitions | true -datalog.timeout | unsigned int | Time limit used for saturation | 0 -datalog.unbound_compressor | bool | auxiliary relations will be introduced to avoid unbound variables in rule heads | true -datalog.use_map_names | bool | use names from map files when displaying tuples | true -engine | symbol | Select: auto-config, datalog, bmc, spacer | auto-config -generate_proof_trace | bool | trace for 'sat' answer as proof object | false -print_aig | symbol | Dump clauses in AIG text format (AAG) to the given file name | -print_answer | bool | print answer instance(s) to query | false -print_boogie_certificate | bool | print certificate for reachability or non-reachability using a format understood by Boogie | false -print_certificate | bool | print certificate for reachability or non-reachability | false -print_fixedpoint_extensions | bool | use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, when printing rules | true -print_low_level_smt2 | bool | use (faster) low-level SMT2 printer (the printer is scalable but the result may not be as readable) | false -print_statistics | bool | print statistics | false -print_with_variable_declarations | bool | use variable declarations when displaying rules (instead of attempting to use original names) | true -spacer.arith.solver | unsigned int | arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver | 2 -spacer.blast_term_ite_inflation | unsigned int | Maximum inflation for non-Boolean ite-terms expansion: 0 (none), k (multiplicative) | 3 -spacer.ctp | bool | Enable counterexample-to-pushing | true -spacer.dump_benchmarks | bool | Dump SMT queries as benchmarks | false -spacer.dump_threshold | double | Threshold in seconds on dumping benchmarks | 5.0 -spacer.elim_aux | bool | Eliminate auxiliary variables in reachability facts | true -spacer.eq_prop | bool | Enable equality and bound propagation in arithmetic | true -spacer.gpdr | bool | Use GPDR solving strategy for non-linear CHC | false -spacer.gpdr.bfs | bool | Use BFS exploration strategy for expanding model search | true -spacer.ground_pobs | bool | Ground pobs by using values from a model | true -spacer.iuc | unsigned int | 0 = use old implementation of unsat-core-generation, 1 = use new implementation of IUC generation, 2 = use new implementation of IUC + min-cut optimization | 1 -spacer.iuc.arith | unsigned int | 0 = use simple Farkas plugin, 1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation),2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin | 1 -spacer.iuc.debug_proof | bool | prints proof used by unsat-core-learner for debugging purposes (debugging) | false -spacer.iuc.old_hyp_reducer | bool | use old hyp reducer instead of new implementation, for debugging only | false -spacer.iuc.print_farkas_stats | bool | prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut (for debugging) | false -spacer.iuc.split_farkas_literals | bool | Split Farkas literals | false -spacer.keep_proxy | bool | keep proxy variables (internal parameter) | true -spacer.logic | symbol | SMT-LIB logic to configure internal SMT solvers | -spacer.max_level | unsigned int | Maximum level to explore | 4294967295 -spacer.max_num_contexts | unsigned int | maximal number of contexts to create | 500 -spacer.mbqi | bool | Enable mbqi | true -spacer.min_level | unsigned int | Minimal level to explore | 0 -spacer.native_mbp | bool | Use native mbp of Z3 | true -spacer.order_children | unsigned int | SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse), 2 (random) | 0 -spacer.p3.share_invariants | bool | Share invariants lemmas | false -spacer.p3.share_lemmas | bool | Share frame lemmas | false -spacer.print_json | symbol | Print pobs tree in JSON format to a given file | -spacer.propagate | bool | Enable propagate/pushing phase | true -spacer.push_pob | bool | push blocked pobs to higher level | false -spacer.push_pob_max_depth | unsigned int | Maximum depth at which push_pob is enabled | 4294967295 -spacer.q3 | bool | Allow quantified lemmas in frames | true -spacer.q3.instantiate | bool | Instantiate quantified lemmas | true -spacer.q3.qgen.normalize | bool | normalize cube before quantified generalization | true -spacer.q3.use_qgen | bool | use quantified lemma generalizer | false -spacer.random_seed | unsigned int | Random seed to be used by SMT solver | 0 -spacer.reach_dnf | bool | Restrict reachability facts to DNF | true -spacer.reset_pob_queue | bool | SPACER: reset pob obligation queue when entering a new level | true -spacer.restart_initial_threshold | unsigned int | Initial threshold for restarts | 10 -spacer.restarts | bool | Enable resetting obligation queue | false -spacer.simplify_lemmas_post | bool | simplify derived lemmas after inductive propagation | false -spacer.simplify_lemmas_pre | bool | simplify derived lemmas before inductive propagation | false -spacer.simplify_pob | bool | simplify pobs by removing redundant constraints | false -spacer.trace_file | symbol | Log file for progress events | -spacer.use_array_eq_generalizer | bool | SPACER: attempt to generalize lemmas with array equalities | true -spacer.use_bg_invs | bool | Enable external background invariants | false -spacer.use_derivations | bool | SPACER: using derivation mechanism to cache intermediate results for non-linear rules | true -spacer.use_euf_gen | bool | Generalize lemmas and pobs using implied equalities | false -spacer.use_inc_clause | bool | Use incremental clause to represent trans | true -spacer.use_inductive_generalizer | bool | generalize lemmas using induction strengthening | true -spacer.use_lemma_as_cti | bool | SPACER: use a lemma instead of a CTI in flexible_trace | false -spacer.use_lim_num_gen | bool | Enable limit numbers generalizer to get smaller numbers | false -spacer.validate_lemmas | bool | Validate each lemma after generalization | false -spacer.weak_abs | bool | Weak abstraction | true -tab.selection | symbol | selection method for tabular strategy: weight (default), first, var-use | weight -validate | bool | validate result (by proof checking or model checking) | false -xform.array_blast | bool | try to eliminate local array terms using Ackermannization -- some array terms may remain | false -xform.array_blast_full | bool | eliminate all local array variables by QE | false -xform.bit_blast | bool | bit-blast bit-vectors | false -xform.coalesce_rules | bool | coalesce rules | false -xform.coi | bool | use cone of influence simplification | true -xform.compress_unbound | bool | compress tails with unbound variables | true -xform.elim_term_ite | bool | Eliminate term-ite expressions | false -xform.elim_term_ite.inflation | unsigned int | Maximum inflation for non-Boolean ite-terms blasting: 0 (none), k (multiplicative) | 3 -xform.fix_unbound_vars | bool | fix unbound variables in tail | false -xform.inline_eager | bool | try eager inlining of rules | true -xform.inline_linear | bool | try linear inlining method | true -xform.inline_linear_branch | bool | try linear inlining method with potential expansion | false -xform.instantiate_arrays | bool | Transforms P(a) into P(i, a[i] a) | false -xform.instantiate_arrays.enforce | bool | Transforms P(a) into P(i, a[i]), discards a from predicate | false -xform.instantiate_arrays.nb_quantifier | unsigned int | Gives the number of quantifiers per array | 1 -xform.instantiate_arrays.slice_technique | symbol | => GetId(i) = i, => GetId(i) = true | no-slicing -xform.instantiate_quantifiers | bool | instantiate quantified Horn clauses using E-matching heuristic | false -xform.magic | bool | perform symbolic magic set transformation | false -xform.quantify_arrays | bool | create quantified Horn clauses from clauses with arrays | false -xform.scale | bool | add scaling variable to linear real arithmetic clauses | false -xform.slice | bool | simplify clause set using slicing | true -xform.subsumption_checker | bool | Enable subsumption checker (no support for model conversion) | true -xform.tail_simplifier_pve | bool | propagate_variable_equivalences | true -xform.transform_arrays | bool | Rewrites arrays equalities and applies select over store | false -xform.unfold_rules | unsigned int | unfold rules statically using iterative squaring | 0 - -## Module smt - -Description: smt solver based on lazy smt - Parameter | Type | Description | Default - ----------|------|-------------|-------- -arith.auto_config_simplex | bool | force simplex solver in auto_config | false -arith.bprop_on_pivoted_rows | bool | propagate bounds on rows changed by the pivot operation | true -arith.branch_cut_ratio | unsigned int | branch/cut ratio for linear integer arithmetic | 2 -arith.dump_lemmas | bool | dump arithmetic theory lemmas to files | false -arith.eager_eq_axioms | bool | eager equality axioms | true -arith.enable_hnf | bool | enable hnf (Hermite Normal Form) cuts | true -arith.greatest_error_pivot | bool | Pivoting strategy | false -arith.ignore_int | bool | treat integer variables as real | false -arith.int_eq_branch | bool | branching using derived integer equations | false -arith.min | bool | minimize cost | false -arith.nl | bool | (incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation, relevant only if smt.arith.solver=2 | true -arith.nl.branching | bool | branching on integer variables in non linear clusters, relevant only if smt.arith.solver=2 | true -arith.nl.delay | unsigned int | number of calls to final check before invoking bounded nlsat check | 500 -arith.nl.expp | bool | expensive patching | false -arith.nl.gr_q | unsigned int | grobner's quota | 10 -arith.nl.grobner | bool | run grobner's basis heuristic | true -arith.nl.grobner_cnfl_to_report | unsigned int | grobner's maximum number of conflicts to report | 1 -arith.nl.grobner_eqs_growth | unsigned int | grobner's number of equalities growth | 10 -arith.nl.grobner_expr_degree_growth | unsigned int | grobner's maximum expr degree growth | 2 -arith.nl.grobner_expr_size_growth | unsigned int | grobner's maximum expr size growth | 2 -arith.nl.grobner_frequency | unsigned int | grobner's call frequency | 4 -arith.nl.grobner_max_simplified | unsigned int | grobner's maximum number of simplifications | 10000 -arith.nl.grobner_subs_fixed | unsigned int | 0 - no subs, 1 - substitute, 2 - substitute fixed zeros only | 1 -arith.nl.horner | bool | run horner's heuristic | true -arith.nl.horner_frequency | unsigned int | horner's call frequency | 4 -arith.nl.horner_row_length_limit | unsigned int | row is disregarded by the heuristic if its length is longer than the value | 10 -arith.nl.horner_subs_fixed | unsigned int | 0 - no subs, 1 - substitute, 2 - substitute fixed zeros only | 2 -arith.nl.nra | bool | call nra_solver when incremental linearization does not produce a lemma, this option is ignored when arith.nl=false, relevant only if smt.arith.solver=6 | true -arith.nl.order | bool | run order lemmas | true -arith.nl.rounds | unsigned int | threshold for number of (nested) final checks for non linear arithmetic, relevant only if smt.arith.solver=2 | 1024 -arith.nl.tangents | bool | run tangent lemmas | true -arith.print_ext_var_names | bool | print external variable names | false -arith.print_stats | bool | print statistic | false -arith.propagate_eqs | bool | propagate (cheap) equalities | true -arith.propagation_mode | unsigned int | 0 - no propagation, 1 - propagate existing literals, 2 - refine finite bounds | 1 -arith.random_initial_value | bool | use random initial values in the simplex-based procedure for linear arithmetic | false -arith.rep_freq | unsigned int | the report frequency, in how many iterations print the cost and other info | 0 -arith.simplex_strategy | unsigned int | simplex strategy for the solver | 0 -arith.solver | unsigned int | arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver | 6 -array.extensional | bool | extensional array theory | true -array.weak | bool | weak array theory | false -auto_config | bool | automatically configure solver | true -bv.delay | bool | delay internalize expensive bit-vector operations | true -bv.enable_int2bv | bool | enable support for int2bv and bv2int operators | true -bv.eq_axioms | bool | enable redundant equality axioms for bit-vectors | true -bv.reflect | bool | create enode for every bit-vector term | true -bv.watch_diseq | bool | use watch lists instead of eager axioms for bit-vectors | false -candidate_models | bool | create candidate models even when quantifier or theory reasoning is incomplete | false -case_split | unsigned int | 0 - case split based on variable activity, 1 - similar to 0, but delay case splits created during the search, 2 - similar to 0, but cache the relevancy, 3 - case split based on relevancy (structural splitting), 4 - case split on relevancy and activity, 5 - case split on relevancy and current goal, 6 - activity-based case split with theory-aware branching activity | 1 -clause_proof | bool | record a clausal proof | false -core.extend_nonlocal_patterns | bool | extend unsat cores with literals that have quantifiers with patterns that contain symbols which are not in the quantifier's body | false -core.extend_patterns | bool | extend unsat core with literals that trigger (potential) quantifier instances | false -core.extend_patterns.max_distance | unsigned int | limits the distance of a pattern-extended unsat core | 4294967295 -core.minimize | bool | minimize unsat core produced by SMT context | false -core.validate | bool | [internal] validate unsat core produced by SMT context. This option is intended for debugging | false -cube_depth | unsigned int | cube depth. | 1 -dack | unsigned int | 0 - disable dynamic ackermannization, 1 - expand Leibniz's axiom if a congruence is the root of a conflict, 2 - expand Leibniz's axiom if a congruence is used during conflict resolution | 1 -dack.eq | bool | enable dynamic ackermannization for transtivity of equalities | false -dack.factor | double | number of instance per conflict | 0.1 -dack.gc | unsigned int | Dynamic ackermannization garbage collection frequency (per conflict) | 2000 -dack.gc_inv_decay | double | Dynamic ackermannization garbage collection decay | 0.8 -dack.threshold | unsigned int | number of times the congruence rule must be used before Leibniz's axiom is expanded | 10 -delay_units | bool | if true then z3 will not restart when a unit clause is learned | false -delay_units_threshold | unsigned int | maximum number of learned unit clauses before restarting, ignored if delay_units is false | 32 -dt_lazy_splits | unsigned int | How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy | 1 -ematching | bool | E-Matching based quantifier instantiation | true -induction | bool | enable generation of induction lemmas | false -lemma_gc_strategy | unsigned int | lemma garbage collection strategy: 0 - fixed, 1 - geometric, 2 - at restart, 3 - none | 0 -logic | symbol | logic used to setup the SMT solver | -macro_finder | bool | try to find universally quantified formulas that can be viewed as macros | false -max_conflicts | unsigned int | maximum number of conflicts before giving up. | 4294967295 -mbqi | bool | model based quantifier instantiation (MBQI) | true -mbqi.force_template | unsigned int | some quantifiers can be used as templates for building interpretations for functions. Z3 uses heuristics to decide whether a quantifier will be used as a template or not. Quantifiers with weight >= mbqi.force_template are forced to be used as a template | 10 -mbqi.id | string | Only use model-based instantiation for quantifiers with id's beginning with string | -mbqi.max_cexs | unsigned int | initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation | 1 -mbqi.max_cexs_incr | unsigned int | increment for MBQI_MAX_CEXS, the increment is performed after each round of MBQI | 0 -mbqi.max_iterations | unsigned int | maximum number of rounds of MBQI | 1000 -mbqi.trace | bool | generate tracing messages for Model Based Quantifier Instantiation (MBQI). It will display a message before every round of MBQI, and the quantifiers that were not satisfied | false -pb.conflict_frequency | unsigned int | conflict frequency for Pseudo-Boolean theory | 1000 -pb.learn_complements | bool | learn complement literals for Pseudo-Boolean theory | true -phase_caching_off | unsigned int | number of conflicts while phase caching is off | 100 -phase_caching_on | unsigned int | number of conflicts while phase caching is on | 400 -phase_selection | unsigned int | phase selection heuristic: 0 - always false, 1 - always true, 2 - phase caching, 3 - phase caching conservative, 4 - phase caching conservative 2, 5 - random, 6 - number of occurrences, 7 - theory | 3 -pull_nested_quantifiers | bool | pull nested quantifiers | false -q.lift_ite | unsigned int | 0 - don not lift non-ground if-then-else, 1 - use conservative ite lifting, 2 - use full lifting of if-then-else under quantifiers | 0 -q.lite | bool | Use cheap quantifier elimination during pre-processing | false -qi.cost | string | expression specifying what is the cost of a given quantifier instantiation | (+ weight generation) -qi.eager_threshold | double | threshold for eager quantifier instantiation | 10.0 -qi.lazy_threshold | double | threshold for lazy quantifier instantiation | 20.0 -qi.max_instances | unsigned int | maximum number of quantifier instantiations | 4294967295 -qi.max_multi_patterns | unsigned int | specify the number of extra multi patterns | 0 -qi.profile | bool | profile quantifier instantiation | false -qi.profile_freq | unsigned int | how frequent results are reported by qi.profile | 4294967295 -qi.quick_checker | unsigned int | specify quick checker mode, 0 - no quick checker, 1 - using unsat instances, 2 - using both unsat and no-sat instances | 0 -quasi_macros | bool | try to find universally quantified formulas that are quasi-macros | false -random_seed | unsigned int | random seed for the smt solver | 0 -refine_inj_axioms | bool | refine injectivity axioms | true -relevancy | unsigned int | relevancy propagation heuristic: 0 - disabled, 1 - relevancy is tracked by only affects quantifier instantiation, 2 - relevancy is tracked, and an atom is only asserted if it is relevant | 2 -restart.max | unsigned int | maximal number of restarts. | 4294967295 -restart_factor | double | when using geometric (or inner-outer-geometric) progression of restarts, it specifies the constant used to multiply the current restart threshold | 1.1 -restart_strategy | unsigned int | 0 - geometric, 1 - inner-outer-geometric, 2 - luby, 3 - fixed, 4 - arithmetic | 1 -restricted_quasi_macros | bool | try to find universally quantified formulas that are restricted quasi-macros | false -seq.max_unfolding | unsigned int | maximal unfolding depth for checking string equations and regular expressions | 1000000000 -seq.split_w_len | bool | enable splitting guided by length constraints | true -seq.validate | bool | enable self-validation of theory axioms created by seq theory | false -str.aggressive_length_testing | bool | prioritize testing concrete length values over generating more options | false -str.aggressive_unroll_testing | bool | prioritize testing concrete regex unroll counts over generating more options | true -str.aggressive_value_testing | bool | prioritize testing concrete string constant values over generating more options | false -str.fast_length_tester_cache | bool | cache length tester constants instead of regenerating them | false -str.fast_value_tester_cache | bool | cache value tester constants instead of regenerating them | true -str.fixed_length_naive_cex | bool | construct naive counterexamples when fixed-length model construction fails for a given length assignment (Z3str3 only) | true -str.fixed_length_refinement | bool | use abstraction refinement in fixed-length equation solver (Z3str3 only) | false -str.overlap_priority | double | theory-aware priority for overlapping variable cases; use smt.theory_aware_branching=true | -0.1 -str.regex_automata_difficulty_threshold | unsigned int | difficulty threshold for regex automata heuristics | 1000 -str.regex_automata_failed_automaton_threshold | unsigned int | number of failed automaton construction attempts after which a full automaton is automatically built | 10 -str.regex_automata_failed_intersection_threshold | unsigned int | number of failed automaton intersection attempts after which intersection is always computed | 10 -str.regex_automata_intersection_difficulty_threshold | unsigned int | difficulty threshold for regex intersection heuristics | 1000 -str.regex_automata_length_attempt_threshold | unsigned int | number of length/path constraint attempts before checking unsatisfiability of regex terms | 10 -str.string_constant_cache | bool | cache all generated string constants generated from anywhere in theory_str | true -str.strong_arrangements | bool | assert equivalences instead of implications when generating string arrangement axioms | true -string_solver | symbol | solver for string/sequence theories. options are: 'z3str3' (specialized string solver), 'seq' (sequence solver), 'auto' (use static features to choose best solver), 'empty' (a no-op solver that forces an answer unknown if strings were used), 'none' (no solver) | seq -theory_aware_branching | bool | Allow the context to use extra information from theory solvers regarding literal branching prioritization. | false -theory_case_split | bool | Allow the context to use heuristics involving theory case splits, which are a set of literals of which exactly one can be assigned True. If this option is false, the context will generate extra axioms to enforce this instead. | false -threads | unsigned int | maximal number of parallel threads. | 1 -threads.cube_frequency | unsigned int | frequency for using cubing | 2 -threads.max_conflicts | unsigned int | maximal number of conflicts between rounds of cubing for parallel SMT | 400 - -## Module sls - -Description: Experimental Stochastic Local Search Solver (for QFBV only). - Parameter | Type | Description | Default - ----------|------|-------------|-------- -early_prune | bool | use early pruning for score prediction | true -max_memory | unsigned int | maximum amount of memory in megabytes | 4294967295 -max_restarts | unsigned int | maximum number of restarts | 4294967295 -paws_init | unsigned int | initial/minimum assertion weights | 40 -paws_sp | unsigned int | smooth assertion weights with probability paws_sp / 1024 | 52 -random_offset | bool | use random offset for candidate evaluation | true -random_seed | unsigned int | random seed | 0 -rescore | bool | rescore/normalize top-level score every base restart interval | true -restart_base | unsigned int | base restart interval given by moves per run | 100 -restart_init | bool | initialize to 0 or random value (= 1) after restart | false -scale_unsat | double | scale score of unsat expressions by this factor | 0.5 -track_unsat | bool | keep a list of unsat assertions as done in SAT - currently disabled internally | false -vns_mc | unsigned int | in local minima, try Monte Carlo sampling vns_mc many 2-bit-flips per bit | 0 -vns_repick | bool | in local minima, try picking a different assertion (only for walksat) | false -walksat | bool | use walksat assertion selection (instead of gsat) | true -walksat_repick | bool | repick assertion if randomizing in local minima | true -walksat_ucb | bool | use bandit heuristic for walksat assertion selection (instead of random) | true -walksat_ucb_constant | double | the ucb constant c in the term score + c * f(touched) | 20.0 -walksat_ucb_forget | double | scale touched by this factor every base restart interval | 1.0 -walksat_ucb_init | bool | initialize total ucb touched to formula size | false -walksat_ucb_noise | double | add noise 0 <= 256 * ucb_noise to ucb score for assertion selection | 0.0002 -wp | unsigned int | random walk with probability wp / 1024 | 100 diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index ca3486ac570..1f24222adbe 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1560,9 +1560,9 @@ def __mul__(self, other): """Create the Z3 expression `self * other`. """ if isinstance(other, int) and other == 1: - return self + return If(self, 1, 0) if isinstance(other, int) and other == 0: - return + return IntVal(0, self.ctx) if isinstance(other, BoolRef): other = If(other, 1, 0) return If(self, other, 0) From 47324af210500af2d840595e8d1b493c5f7b1c90 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 29 Dec 2022 11:08:57 +0000 Subject: [PATCH 233/597] be nicer when memout is reached in SMT internalize: return undef rather than crashing --- src/smt/smt_context.cpp | 41 ++++++++++++++++++++++++++---------- src/smt/smt_context.h | 2 ++ src/smt/smt_internalizer.cpp | 2 +- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index d1cf2d87521..01e0aba7c81 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -2961,7 +2961,11 @@ namespace smt { pop_to_base_lvl(); setup_context(false); bool was_consistent = !inconsistent(); - internalize_assertions(); // internalize assertions before invoking m_asserted_formulas.push_scope + try { + internalize_assertions(); // internalize assertions before invoking m_asserted_formulas.push_scope + } catch (cancel_exception&) { + throw default_exception("Resource limits hit in push"); + } if (!m.inc()) throw default_exception("push canceled"); scoped_suspend_rlimit _suspend_cancel(m.limit()); @@ -3556,7 +3560,12 @@ namespace smt { return p(asms); } - internalize_assertions(); + try { + internalize_assertions(); + } catch (cancel_exception&) { + VERIFY(resource_limits_exceeded()); + return l_undef; + } expr_ref_vector theory_assumptions(m); add_theory_assumptions(theory_assumptions); if (!theory_assumptions.empty()) { @@ -3620,10 +3629,15 @@ namespace smt { do { pop_to_base_lvl(); expr_ref_vector asms(m, num_assumptions, assumptions); - internalize_assertions(); - add_theory_assumptions(asms); - TRACE("unsat_core_bug", tout << asms << "\n";); - init_assumptions(asms); + try { + internalize_assertions(); + add_theory_assumptions(asms); + TRACE("unsat_core_bug", tout << asms << '\n';); + init_assumptions(asms); + } catch (cancel_exception&) { + VERIFY(resource_limits_exceeded()); + return l_undef; + } TRACE("before_search", display(tout);); r = search(); r = mk_unsat_core(r); @@ -3641,11 +3655,16 @@ namespace smt { do { pop_to_base_lvl(); expr_ref_vector asms(cube); - internalize_assertions(); - add_theory_assumptions(asms); - // introducing proxies: if (!validate_assumptions(asms)) return l_undef; - for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; - init_assumptions(asms); + try { + internalize_assertions(); + add_theory_assumptions(asms); + // introducing proxies: if (!validate_assumptions(asms)) return l_undef; + for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; + init_assumptions(asms); + } catch (cancel_exception&) { + VERIFY(resource_limits_exceeded()); + return l_undef; + } for (auto const& clause : clauses) init_clause(clause); r = search(); r = mk_unsat_core(r); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index b6bf04a20e2..7a267fdeca1 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -62,6 +62,8 @@ namespace smt { class model_generator; + struct cancel_exception {}; + class context { friend class model_generator; friend class lookahead; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 26e23d92d93..3a7b95e2c49 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -353,7 +353,7 @@ namespace smt { */ void context::internalize(expr * n, bool gate_ctx) { if (memory::above_high_watermark()) - throw default_exception("resource limit exceeded during internalization"); + throw cancel_exception(); internalize_deep(n); internalize_rec(n, gate_ctx); } From 07ab4d38b659d525b4363d660c4526a725fbc182 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Dec 2022 09:55:10 -0800 Subject: [PATCH 234/597] fix #6513 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/extract_eqs.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 5c2851e913e..583f7620639 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -83,7 +83,7 @@ namespace euf { class arith_extract_eq : public extract_eq { ast_manager& m; arith_util a; - expr_ref_vector m_args; + expr_ref_vector m_args, m_trail; expr_sparse_mark m_nonzero; bool m_enabled = true; @@ -216,20 +216,25 @@ namespace euf { } } + void mark_nonzero(expr* e) { + m_trail.push_back(e); + m_nonzero(e); + } + void add_pos(expr* f) { - expr* lhs = nullptr, * rhs = nullptr; + 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); + mark_nonzero(lhs); else if (a.is_ge(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_pos()) - m_nonzero.mark(lhs); + mark_nonzero(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); + mark_nonzero(lhs); else if (a.is_ge(f, lhs, rhs) && a.is_numeral(rhs, val) && !val.is_pos()) - m_nonzero.mark(lhs); + mark_nonzero(lhs); else if (m.is_eq(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_zero()) - m_nonzero.mark(lhs); + mark_nonzero(lhs); } } @@ -241,7 +246,7 @@ namespace euf { public: - arith_extract_eq(ast_manager& m) : m(m), a(m), m_args(m) {} + arith_extract_eq(ast_manager& m) : m(m), a(m), m_args(m), m_trail(m) {} void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { if (!m_enabled) From 293627c889b4adef608d3204efcee1bb39826a7a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Dec 2022 09:55:33 -0800 Subject: [PATCH 235/597] fix #6513 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/extract_eqs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 583f7620639..0c24a0b154a 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -263,6 +263,7 @@ namespace euf { if (!m_enabled) return; m_nonzero.reset(); + m_trail.reset(); for (unsigned i = 0; i < fmls.qtail(); ++i) add_pos(fmls[i].fml()); } From 8002a51b82f703ce6dba5a429612e30bedb8b575 Mon Sep 17 00:00:00 2001 From: nikswamy Date: Fri, 30 Dec 2022 15:25:01 -0800 Subject: [PATCH 236/597] tiny fix to qprofdiff (#6497) --- contrib/qprofdiff/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/qprofdiff/main.cpp b/contrib/qprofdiff/main.cpp index 17b76081bbf..7833faa27bd 100644 --- a/contrib/qprofdiff/main.cpp +++ b/contrib/qprofdiff/main.cpp @@ -65,7 +65,7 @@ int parse(string const & filename, map & data) { inx != string::npos; inx = line.find(" : ", from)) { tokens[ti] = trim(line.substr(from, inx-from)); - from = inx+1; + from = inx+3; //3 is the length of " : " ti++; } if (from != line.length() && ti < 4) From 2c3ecceb03a2b14722d7436d977e6566c4ba122f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Dec 2022 15:47:24 -0800 Subject: [PATCH 237/597] fix build Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/extract_eqs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 0c24a0b154a..66ebef85c2d 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -218,7 +218,7 @@ namespace euf { void mark_nonzero(expr* e) { m_trail.push_back(e); - m_nonzero(e); + m_nonzero.mark(e); } void add_pos(expr* f) { From 0d05e0649bb7e45eee1924ba158c0ba2a966b8d4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Dec 2022 18:16:24 -0800 Subject: [PATCH 238/597] initialization order Signed-off-by: Nikolaj Bjorner --- src/tactic/dependent_expr_state_tactic.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 58507a850a8..bf33d7fd264 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -48,8 +48,8 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { dependent_expr_state(m), m(m), m_params(p), - m_factory(f), - m_dep(m, nullptr, nullptr, nullptr) + m_dep(m, nullptr, nullptr, nullptr), + m_factory(f) {} /** From 5f6f2fc758bd68836ab2db1c9a3f580a25b9f75c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Dec 2022 18:39:02 -0800 Subject: [PATCH 239/597] rename bit_blaster class to bit_blaster_simplifier to avoid name clash --- src/ast/simplifiers/bit_blaster.cpp | 12 ++++++------ src/ast/simplifiers/bit_blaster.h | 4 ++-- src/sat/sat_solver/sat_smt_preprocess.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ast/simplifiers/bit_blaster.cpp b/src/ast/simplifiers/bit_blaster.cpp index c66cef826da..eed751f3958 100644 --- a/src/ast/simplifiers/bit_blaster.cpp +++ b/src/ast/simplifiers/bit_blaster.cpp @@ -18,12 +18,12 @@ Module Name: #include "ast/simplifiers/bit_blaster.h" -void bit_blaster::updt_params(params_ref const & p) { +void bit_blaster_simplifier::updt_params(params_ref const & p) { m_params.append(p); m_rewriter.updt_params(m_params); } -void bit_blaster::collect_param_descrs(param_descrs & r) { +void bit_blaster_simplifier::collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders)."); @@ -32,7 +32,7 @@ void bit_blaster::collect_param_descrs(param_descrs & r) { r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms."); } -void bit_blaster::reduce() { +void bit_blaster_simplifier::reduce() { m_rewriter.start_rewrite(); expr_ref new_curr(m); proof_ref new_pr(m); @@ -61,16 +61,16 @@ void bit_blaster::reduce() { } -void bit_blaster::collect_statistics(statistics& st) const { +void bit_blaster_simplifier::collect_statistics(statistics& st) const { st.update("bit-blaster-num-steps", m_num_steps); } -void bit_blaster::push() { +void bit_blaster_simplifier::push() { m_rewriter.push(); dependent_expr_simplifier::push(); } -void bit_blaster::pop(unsigned n) { +void bit_blaster_simplifier::pop(unsigned n) { dependent_expr_simplifier::pop(n); m_rewriter.pop(n); } diff --git a/src/ast/simplifiers/bit_blaster.h b/src/ast/simplifiers/bit_blaster.h index 231a1ca68ed..5724cc07599 100644 --- a/src/ast/simplifiers/bit_blaster.h +++ b/src/ast/simplifiers/bit_blaster.h @@ -21,14 +21,14 @@ Module Name: #include "ast/simplifiers/dependent_expr_state.h" -class bit_blaster : public dependent_expr_simplifier { +class bit_blaster_simplifier : public dependent_expr_simplifier { bit_blaster_rewriter m_rewriter; unsigned m_num_steps = 0; params_ref m_params; public: - bit_blaster(ast_manager & m, params_ref const & p, dependent_expr_state& s): + bit_blaster_simplifier(ast_manager & m, params_ref const & p, dependent_expr_state& s): dependent_expr_simplifier(m, s), m_rewriter(m, p) { updt_params(p); diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index 7c716c48160..31888251c89 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -98,7 +98,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep s.add_simplifier(alloc(card2bv, m, p, st)); s.add_simplifier(alloc(rewriter_simplifier, m, simp1_p, st)); s.add_simplifier(mk_max_bv_sharing(m, p, st)); - s.add_simplifier(alloc(bit_blaster, m, p, st)); + s.add_simplifier(alloc(bit_blaster_simplifier, m, p, st)); s.add_simplifier(alloc(rewriter_simplifier, m, simp2_p, st)); } } From c0f1f338985898c059da924878715e5709f2eaa8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Dec 2022 18:47:32 -0800 Subject: [PATCH 240/597] dampen second setup of theory_bv --- src/smt/smt_setup.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index aed515ef066..854ddb9e0ec 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -689,9 +689,12 @@ namespace smt { } void setup::setup_bv() { + family_id bv_fid = m_manager.mk_family_id("bv"); + if (m_context.get_theory(bv_fid)) + return; switch(m_params.m_bv_mode) { case BS_NO_BV: - m_context.register_plugin(alloc(smt::theory_dummy, m_context, m_manager.mk_family_id("bv"), "no bit-vector")); + m_context.register_plugin(alloc(smt::theory_dummy, m_context, bv_fid, "no bit-vector")); break; case BS_BLASTER: m_context.register_plugin(alloc(smt::theory_bv, m_context)); From f6d411d54bbe80d5b305e9c2ae30b40c0ed358ad Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Dec 2022 21:41:27 -0800 Subject: [PATCH 241/597] experimental feature to access congruence closure of SimpleSolver This update includes an experimental feature to access a congruence closure data-structure after search. It comes with several caveats as pre-processing is free to eliminate terms. It is therefore necessary to use a solver that does not eliminate the terms you want to track for congruence of. This is partially addressed by using SimpleSolver or incremental mode solving. ```python from z3 import * s = SimpleSolver() x, y, z = Ints('x y z') s.add(x == y) s.add(y == z) s.check() print(s.root(x), s.root(y), s.root(z)) print(s.next(x), s.next(y), s.next(z)) ``` --- src/api/api_solver.cpp | 20 ++++++++++++++++++ src/api/python/z3/z3.py | 16 ++++++++++++++ src/api/z3_api.h | 20 ++++++++++++++++++ src/ast/converters/expr_inverter.cpp | 21 ++++++++++++------- src/ast/converters/expr_inverter.h | 7 +++++-- src/ast/simplifiers/elim_unconstrained.cpp | 7 ++++--- src/muz/spacer/spacer_iuc_solver.h | 2 ++ src/opt/opt_solver.h | 2 ++ src/sat/sat_solver/inc_sat_solver.cpp | 4 ++++ src/sat/sat_solver/sat_smt_solver.cpp | 3 +++ src/smt/smt_kernel.cpp | 14 +++++++++++++ src/smt/smt_kernel.h | 7 +++++++ src/smt/smt_solver.cpp | 4 ++++ src/solver/combined_solver.cpp | 4 ++++ src/solver/solver.h | 9 ++++++++ src/solver/solver_pool.cpp | 3 +++ src/solver/tactic2solver.cpp | 3 +++ .../fd_solver/bounded_int2bv_solver.cpp | 2 ++ src/tactic/fd_solver/enum2bv_solver.cpp | 3 +++ src/tactic/fd_solver/pb2bv_solver.cpp | 2 ++ src/tactic/fd_solver/smtfd_solver.cpp | 4 ++++ 21 files changed, 145 insertions(+), 12 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index b93fb42afdb..2ca54a5996e 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -903,6 +903,26 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_ast Z3_API Z3_solver_congruence_root(Z3_context c, Z3_solver s, Z3_ast a) { + Z3_TRY; + LOG_Z3_solver_congruence_root(c, s, a); + RESET_ERROR_CODE(); + init_solver(c, s); + expr* r = to_solver_ref(s)->congruence_root(to_expr(a)); + RETURN_Z3(of_expr(r)); + Z3_CATCH_RETURN(nullptr); + } + + Z3_ast Z3_API Z3_solver_congruence_next(Z3_context c, Z3_solver s, Z3_ast a) { + Z3_TRY; + LOG_Z3_solver_congruence_next(c, s, a); + RESET_ERROR_CODE(); + init_solver(c, s); + expr* sib = to_solver_ref(s)->congruence_next(to_expr(a)); + RETURN_Z3(of_expr(sib)); + Z3_CATCH_RETURN(nullptr); + } + class api_context_obj : public user_propagator::context_obj { api::context* c; public: diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 1f24222adbe..695bb939f0d 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -7241,6 +7241,22 @@ def cube_vars(self): cube are likely more useful to cube on.""" return self.cube_vs + def root(self, t): + t = _py2expr(t, self.ctx) + """Retrieve congruence closure root of the term t relative to the current search state + The function primarily works for SimpleSolver. Terms and variables that are + eliminated during pre-processing are not visible to the congruence closure. + """ + return _to_expr_ref(Z3_solver_congruence_root(self.ctx.ref(), self.solver, t.ast), self.ctx) + + def next(self, t): + t = _py2expr(t, self.ctx) + """Retrieve congruence closure sibling of the term t relative to the current search state + The function primarily works for SimpleSolver. Terms and variables that are + eliminated during pre-processing are not visible to the congruence closure. + """ + return _to_expr_ref(Z3_solver_congruence_next(self.ctx.ref(), self.solver, t.ast), self.ctx) + def proof(self): """Return a proof for the last `check()`. Proof construction must be enabled.""" return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 7a0b47da061..ffa0d866510 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -6882,6 +6882,26 @@ extern "C" { */ void Z3_API Z3_solver_get_levels(Z3_context c, Z3_solver s, Z3_ast_vector literals, unsigned sz, unsigned levels[]); + /** + \brief retrieve the congruence closure root of an expression. + The root is retrieved relative to the state where the solver was in when it completed. + If it completed during a set of case splits, the congruence roots are relative to these case splits. + That is, the congruences are not consequences but they are true under the current state. + + def_API('Z3_solver_congruence_root', AST, (_in(CONTEXT), _in(SOLVER), _in(AST))) + */ + Z3_ast Z3_API Z3_solver_congruence_root(Z3_context c, Z3_solver s, Z3_ast a); + + + /** + \brief retrieve the next expression in the congruence class. The set of congruent siblings form a cyclic list. + Repeated calls on the siblings will result in returning to the original expression. + + def_API('Z3_solver_congruence_next', AST, (_in(CONTEXT), _in(SOLVER), _in(AST))) + */ + Z3_ast Z3_API Z3_solver_congruence_next(Z3_context c, Z3_solver s, Z3_ast a); + + /** \brief register a callback to that retrieves assumed, inferred and deleted clauses during search. diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 5553420adcd..41a60ccd562 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -81,7 +81,7 @@ class basic_expr_inverter : public iexpr_inverter { * */ - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { SASSERT(f->get_family_id() == m.get_basic_family_id()); switch (f->get_decl_kind()) { case OP_ITE: @@ -233,7 +233,7 @@ class arith_expr_inverter : public iexpr_inverter { } - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { SASSERT(f->get_family_id() == a.get_family_id()); switch (f->get_decl_kind()) { case OP_ADD: @@ -531,7 +531,7 @@ class bv_expr_inverter : public iexpr_inverter { * y := 0 * */ - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { SASSERT(f->get_family_id() == bv.get_family_id()); switch (f->get_decl_kind()) { case OP_BADD: @@ -611,7 +611,7 @@ class array_expr_inverter : public iexpr_inverter { family_id get_fid() const override { return a.get_family_id(); } - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { SASSERT(f->get_family_id() == a.get_family_id()); switch (f->get_decl_kind()) { case OP_SELECT: @@ -679,7 +679,7 @@ class dt_expr_inverter : public iexpr_inverter { * head(x) -> fresh * x := cons(fresh, arb) */ - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, expr_ref& side_cond) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { if (dt.is_accessor(f)) { SASSERT(num == 1); if (uncnstr(args[0])) { @@ -799,7 +799,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { } -bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) { +bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr, proof_ref& pr) { if (num == 0) return false; @@ -812,7 +812,7 @@ bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, ex return false; auto* p = m_inverters.get(fid, nullptr); - return p && (*p)(f, num, args, new_expr, side_cond); + return p && (*p)(f, num, args, new_expr, pr); } bool expr_inverter::mk_diff(expr* t, expr_ref& r) { @@ -849,3 +849,10 @@ void expr_inverter::set_model_converter(generic_model_converter* mc) { if (p) p->set_model_converter(mc); } + +void expr_inverter::set_produce_proofs(bool pr) { + m_produce_proofs = pr; + for (auto* p : m_inverters) + if (p) + p->set_produce_proofs(pr); +} diff --git a/src/ast/converters/expr_inverter.h b/src/ast/converters/expr_inverter.h index 5b796547891..60540aff38a 100644 --- a/src/ast/converters/expr_inverter.h +++ b/src/ast/converters/expr_inverter.h @@ -24,6 +24,7 @@ class iexpr_inverter { ast_manager& m; std::function m_is_var; generic_model_converter_ref m_mc; + bool m_produce_proofs = false; bool uncnstr(expr* e) const { return m_is_var(e); } bool uncnstr(unsigned num, expr * const * args) const; @@ -37,8 +38,9 @@ class iexpr_inverter { virtual ~iexpr_inverter() {} virtual void set_is_var(std::function& is_var) { m_is_var = is_var; } virtual void set_model_converter(generic_model_converter* mc) { m_mc = mc; } + virtual void set_produce_proofs(bool p) { m_produce_proofs = true; } - virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) = 0; + virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, proof_ref& pr) = 0; virtual bool mk_diff(expr* t, expr_ref& r) = 0; virtual family_id get_fid() const = 0; }; @@ -49,9 +51,10 @@ class expr_inverter : public iexpr_inverter { public: expr_inverter(ast_manager& m); ~expr_inverter() override; - bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, expr_ref& side_cond) override; + bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, proof_ref& pr) override; bool mk_diff(expr* t, expr_ref& r) override; void set_is_var(std::function& is_var) override; void set_model_converter(generic_model_converter* mc) override; + void set_produce_proofs(bool p) override; family_id get_fid() const override { return null_family_id; } }; diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index df6f92251c8..41a905dea18 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -62,7 +62,8 @@ bool elim_unconstrained::is_var_lt(int v1, int v2) const { void elim_unconstrained::eliminate() { while (!m_heap.empty()) { - expr_ref r(m), side_cond(m); + expr_ref r(m); + proof_ref pr(m); int v = m_heap.erase_min(); node& n = get_node(v); if (n.m_refcount == 0) @@ -84,7 +85,7 @@ void elim_unconstrained::eliminate() { unsigned sz = m_args.size(); for (expr* arg : *to_app(t)) m_args.push_back(reconstruct_term(get_node(arg))); - bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r, side_cond); + bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r, pr); n.m_refcount = 0; m_args.shrink(sz); if (!inverted) { @@ -113,7 +114,7 @@ void elim_unconstrained::eliminate() { IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(get_node(v).m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n";); - SASSERT(!side_cond && "not implemented to add side conditions\n"); + SASSERT(!pr && "not implemented to add proofs\n"); } } diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index 0d471221509..e201a1fe131 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -122,6 +122,8 @@ class iuc_solver : public solver { void set_phase(phase* p) override { m_solver.set_phase(p); } void move_to_front(expr* e) override { m_solver.move_to_front(e); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } + expr* congruence_root(expr* e) override { return e; } + expr* congruence_next(expr* e) override { return e; } void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { m_solver.get_levels(vars, depth); } expr_ref_vector get_trail(unsigned max_level) override { return m_solver.get_trail(max_level); } diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index 84d31ed0f41..2682fca0951 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -110,6 +110,8 @@ namespace opt { void get_levels(ptr_vector const& vars, unsigned_vector& depth) override; expr_ref_vector get_trail(unsigned max_level) override { return m_context.get_trail(max_level); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } + expr* congruence_root(expr* e) override { return e; } + expr* congruence_next(expr* e) override { return e; } void set_phase(expr* e) override { m_context.set_phase(e); } phase* get_phase() override { return m_context.get_phase(); } void set_phase(phase* p) override { m_context.set_phase(p); } diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 41b8e609c9c..0362d8d3eff 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -463,6 +463,10 @@ class inc_sat_solver : public solver { } return fmls; } + + expr* congruence_next(expr* e) override { return e; } + expr* congruence_root(expr* e) override { return e; } + lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) override { init_preprocess(); diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index e37d513a02e..f5872b05a09 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -476,6 +476,9 @@ class sat_smt_solver : public solver { set_reason_unknown(m_solver.get_reason_unknown()); return fmls; } + + expr* congruence_next(expr* e) override { return e; } + expr* congruence_root(expr* e) override { return e; } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index ae3338f524b..c4ecf6787b1 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -213,6 +213,20 @@ namespace smt { return out; } + expr* kernel::congruence_root(expr * e) { + smt::enode* n = m_imp->m_kernel.find_enode(e); + if (!n) + return e; + return n->get_root()->get_expr(); + } + + expr* kernel::congruence_next(expr * e) { + smt::enode* n = m_imp->m_kernel.find_enode(e); + if (!n) + return e; + return n->get_next()->get_expr(); + } + void kernel::collect_statistics(::statistics & st) const { m_imp->m_kernel.collect_statistics(st); } diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index fa4a48406c1..ccea5caf8f8 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -239,6 +239,13 @@ namespace smt { */ expr_ref_vector cubes(unsigned depth); + /** + \brief access congruence closure + */ + expr* congruence_next(expr* e); + + expr* congruence_root(expr* e); + /** \brief retrieve depth of variables from decision stack. diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 61c7fdda7eb..4be78b20a75 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -330,6 +330,10 @@ namespace { m_context.get_units(units); } + expr* congruence_next(expr* e) override { return m_context.congruence_next(e); } + expr* congruence_root(expr* e) override { return m_context.congruence_root(e); } + + expr_ref_vector cube(expr_ref_vector& vars, unsigned cutoff) override { ast_manager& m = get_manager(); if (!m_cuber) { diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index 7b1449637a0..53aa5675300 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -275,6 +275,10 @@ class combined_solver : public solver { return m_solver2->cube(vars, backtrack_level); } + expr* congruence_next(expr* e) override { switch_inc_mode(); return m_solver2->congruence_next(e); } + expr* congruence_root(expr* e) override { switch_inc_mode(); return m_solver2->congruence_root(e); } + + expr * get_assumption(unsigned idx) const override { unsigned c1 = m_solver1->get_num_assumptions(); if (idx < c1) return m_solver1->get_assumption(idx); diff --git a/src/solver/solver.h b/src/solver/solver.h index 957cb7c8edc..7d7a3eec28a 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -238,6 +238,15 @@ class solver : public check_sat_result, public user_propagator::core { virtual expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) = 0; + /** + \brief retrieve congruence closure root. + */ + virtual expr* congruence_root(expr* e) = 0; + + /** + \brief retrieve congruence closure sibling + */ + virtual expr* congruence_next(expr* e) = 0; /** \brief Display the content of this solver. diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index f5760bde353..411634162ce 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -262,6 +262,9 @@ class pool_solver : public solver_na2as { expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { return expr_ref_vector(m); } + expr* congruence_next(expr* e) override { return e; } + expr* congruence_root(expr* e) override { return e; } + ast_manager& get_manager() const override { return m_base->get_manager(); } void refresh(solver* new_base) { diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index b65ffde5747..cc3ac9336db 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -136,6 +136,9 @@ class tactic2solver : public solver_na2as { return expr_ref_vector(get_manager()); } + expr* congruence_next(expr* e) override { return e; } + expr* congruence_root(expr* e) override { return e; } + model_converter_ref get_model_converter() const override { return m_mc; } void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { diff --git a/src/tactic/fd_solver/bounded_int2bv_solver.cpp b/src/tactic/fd_solver/bounded_int2bv_solver.cpp index 7b7ca630e5a..4ac82c0c2a3 100644 --- a/src/tactic/fd_solver/bounded_int2bv_solver.cpp +++ b/src/tactic/fd_solver/bounded_int2bv_solver.cpp @@ -210,6 +210,8 @@ class bounded_int2bv_solver : public solver_na2as { void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } + expr* congruence_next(expr* e) override { return m_solver->congruence_next(e); } + expr* congruence_root(expr* e) override { return m_solver->congruence_root(e); } expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { diff --git a/src/tactic/fd_solver/enum2bv_solver.cpp b/src/tactic/fd_solver/enum2bv_solver.cpp index 7ec5243e71a..2690e7033e2 100644 --- a/src/tactic/fd_solver/enum2bv_solver.cpp +++ b/src/tactic/fd_solver/enum2bv_solver.cpp @@ -131,6 +131,9 @@ class enum2bv_solver : public solver_na2as { expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { return m_solver->cube(vars, backtrack_level); } + expr* congruence_next(expr* e) override { return m_solver->congruence_next(e); } + expr* congruence_root(expr* e) override { return m_solver->congruence_root(e); } + lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { datatype_util dt(m); diff --git a/src/tactic/fd_solver/pb2bv_solver.cpp b/src/tactic/fd_solver/pb2bv_solver.cpp index 1a5f7d16a50..19f2630f2fa 100644 --- a/src/tactic/fd_solver/pb2bv_solver.cpp +++ b/src/tactic/fd_solver/pb2bv_solver.cpp @@ -122,6 +122,8 @@ class pb2bv_solver : public solver_na2as { void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } + expr* congruence_next(expr* e) override { return m_solver->congruence_next(e); } + expr* congruence_root(expr* e) override { return m_solver->congruence_root(e); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { flush_assertions(); diff --git a/src/tactic/fd_solver/smtfd_solver.cpp b/src/tactic/fd_solver/smtfd_solver.cpp index 3729a2ad17e..5676c6ee80c 100644 --- a/src/tactic/fd_solver/smtfd_solver.cpp +++ b/src/tactic/fd_solver/smtfd_solver.cpp @@ -2086,6 +2086,10 @@ namespace smtfd { expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { return expr_ref_vector(m); } + + expr* congruence_root(expr* e) override { return e; } + + expr* congruence_next(expr* e) override { return e; } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { return l_undef; From dbf93c5fbdf458396c38993fdcac54f4e0803824 Mon Sep 17 00:00:00 2001 From: Walden Yan Date: Sun, 1 Jan 2023 18:27:54 -0500 Subject: [PATCH 242/597] Fixing array select for lambda expressions in Python API (#6516) * fix: making array select work for lambda expressions * more elegant solution --- src/api/python/z3/z3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 695bb939f0d..074570b035d 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -4597,10 +4597,10 @@ def default(self): def _array_select(ar, arg): if isinstance(arg, tuple): - args = [ar.domain_n(i).cast(arg[i]) for i in range(len(arg))] + args = [ar.sort().domain_n(i).cast(arg[i]) for i in range(len(arg))] _args, sz = _to_ast_array(args) return _to_expr_ref(Z3_mk_select_n(ar.ctx_ref(), ar.as_ast(), sz, _args), ar.ctx) - arg = ar.domain().cast(arg) + arg = ar.sort().domain().cast(arg) return _to_expr_ref(Z3_mk_select(ar.ctx_ref(), ar.as_ast(), arg.as_ast()), ar.ctx) From ea0d09b6c89c455271ecf16a1f7337fcaa395c9f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 2 Jan 2023 16:49:31 -0800 Subject: [PATCH 243/597] add pointer to build parameters to README #6518 Signed-off-by: Nikolaj Bjorner --- src/api/js/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/js/README.md b/src/api/js/README.md index 42c29518ef9..8c446b91020 100644 --- a/src/api/js/README.md +++ b/src/api/js/README.md @@ -11,6 +11,9 @@ You'll need to have emscripten set up, along with all of its dependencies. The e Then run `npm i` to install dependencies, `npm run build:ts` to build the TypeScript wrapper, and `npm run build:wasm` to build the wasm artifact. +### Build on your own + +Consult the file [build-wasm.ts](https://github.com/Z3Prover/z3/blob/master/src/api/js/scripts/build-wasm.ts) for configurations used for building wasm. ## Tests From d30cb55bae24113468b3ae6f07d39536402b4891 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 3 Jan 2023 09:35:17 +0000 Subject: [PATCH 244/597] don't flush stream when printing param vals --- src/params/bit_blaster_params.h | 4 ++-- src/params/pattern_inference_params.cpp | 2 +- src/smt/params/dyn_ack_params.cpp | 2 +- src/smt/params/preprocessor_params.cpp | 2 +- src/smt/params/qi_params.cpp | 2 +- src/smt/params/smt_params.cpp | 2 +- src/smt/params/theory_arith_params.cpp | 2 +- src/smt/params/theory_array_params.cpp | 2 +- src/smt/params/theory_bv_params.cpp | 2 +- src/smt/params/theory_datatype_params.h | 2 +- src/smt/params/theory_pb_params.cpp | 2 +- src/smt/params/theory_str_params.cpp | 2 +- src/tactic/smtlogics/smt_tactic.cpp | 2 +- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/params/bit_blaster_params.h b/src/params/bit_blaster_params.h index 527835d2abf..9e405f187b4 100644 --- a/src/params/bit_blaster_params.h +++ b/src/params/bit_blaster_params.h @@ -33,8 +33,8 @@ struct bit_blaster_params { #endif void display(std::ostream & out) const { - out << "m_bb_ext_gates=" << m_bb_ext_gates << std::endl; - out << "m_bb_quantifiers=" << m_bb_quantifiers << std::endl; + out << "m_bb_ext_gates=" << m_bb_ext_gates << '\n'; + out << "m_bb_quantifiers=" << m_bb_quantifiers << '\n'; } }; diff --git a/src/params/pattern_inference_params.cpp b/src/params/pattern_inference_params.cpp index bb9b481ca23..26f606b635e 100644 --- a/src/params/pattern_inference_params.cpp +++ b/src/params/pattern_inference_params.cpp @@ -31,7 +31,7 @@ void pattern_inference_params::updt_params(params_ref const & _p) { m_pi_warnings = p.warnings(); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void pattern_inference_params::display(std::ostream & out) const { DISPLAY_PARAM(m_pi_max_multi_patterns); diff --git a/src/smt/params/dyn_ack_params.cpp b/src/smt/params/dyn_ack_params.cpp index b1e99cf21d7..57645903d80 100644 --- a/src/smt/params/dyn_ack_params.cpp +++ b/src/smt/params/dyn_ack_params.cpp @@ -29,7 +29,7 @@ void dyn_ack_params::updt_params(params_ref const & _p) { m_dack_gc_inv_decay = p.dack_gc_inv_decay(); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void dyn_ack_params::display(std::ostream & out) const { DISPLAY_PARAM((unsigned)m_dack); diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 9fcb09843b9..f3c46f95c98 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -34,7 +34,7 @@ void preprocessor_params::updt_params(params_ref const & p) { updt_local_params(p); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void preprocessor_params::display(std::ostream & out) const { pattern_inference_params::display(out); diff --git a/src/smt/params/qi_params.cpp b/src/smt/params/qi_params.cpp index 387df4dd599..d6b22d9f1fd 100644 --- a/src/smt/params/qi_params.cpp +++ b/src/smt/params/qi_params.cpp @@ -39,7 +39,7 @@ void qi_params::updt_params(params_ref const & _p) { m_qi_quick_checker = static_cast(p.qi_quick_checker()); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void qi_params::display(std::ostream & out) const { DISPLAY_PARAM(m_qi_cost); diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 37249fdaca2..3c63e2fffee 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -85,7 +85,7 @@ void smt_params::updt_params(context_params const & p) { m_model = p.m_model; } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void smt_params::display(std::ostream & out) const { preprocessor_params::display(out); diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 565000ebe91..7f3f1ca23ce 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -42,7 +42,7 @@ void theory_arith_params::updt_params(params_ref const & _p) { } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void theory_arith_params::display(std::ostream & out) const { DISPLAY_PARAM(m_arith_eq2ineq); diff --git a/src/smt/params/theory_array_params.cpp b/src/smt/params/theory_array_params.cpp index 892edb4ad15..2283be256af 100644 --- a/src/smt/params/theory_array_params.cpp +++ b/src/smt/params/theory_array_params.cpp @@ -25,7 +25,7 @@ void theory_array_params::updt_params(params_ref const & _p) { m_array_extensional = p.array_extensional(); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void theory_array_params::display(std::ostream & out) const { DISPLAY_PARAM(m_array_mode); diff --git a/src/smt/params/theory_bv_params.cpp b/src/smt/params/theory_bv_params.cpp index 35a62e7fc94..09fa4513fe2 100644 --- a/src/smt/params/theory_bv_params.cpp +++ b/src/smt/params/theory_bv_params.cpp @@ -31,7 +31,7 @@ void theory_bv_params::updt_params(params_ref const & _p) { m_bv_size_reduce = p.bv_size_reduce(); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void theory_bv_params::display(std::ostream & out) const { DISPLAY_PARAM(m_bv_mode); diff --git a/src/smt/params/theory_datatype_params.h b/src/smt/params/theory_datatype_params.h index 05957bfe9b3..b16f4254aef 100644 --- a/src/smt/params/theory_datatype_params.h +++ b/src/smt/params/theory_datatype_params.h @@ -32,7 +32,7 @@ struct theory_datatype_params { m_dt_lazy_splits = p.dt_lazy_splits(); } - void display(std::ostream & out) const { out << "m_dt_lazy_splits=" << m_dt_lazy_splits << std::endl; } + void display(std::ostream & out) const { out << "m_dt_lazy_splits=" << m_dt_lazy_splits << '\n'; } }; diff --git a/src/smt/params/theory_pb_params.cpp b/src/smt/params/theory_pb_params.cpp index 45a6ede1086..2df8d6fee98 100644 --- a/src/smt/params/theory_pb_params.cpp +++ b/src/smt/params/theory_pb_params.cpp @@ -25,7 +25,7 @@ void theory_pb_params::updt_params(params_ref const & _p) { m_pb_learn_complements = p.pb_learn_complements(); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void theory_pb_params::display(std::ostream & out) const { DISPLAY_PARAM(m_pb_conflict_frequency); diff --git a/src/smt/params/theory_str_params.cpp b/src/smt/params/theory_str_params.cpp index e0802b5d77f..7f84a6cbeed 100644 --- a/src/smt/params/theory_str_params.cpp +++ b/src/smt/params/theory_str_params.cpp @@ -37,7 +37,7 @@ void theory_str_params::updt_params(params_ref const & _p) { m_FixedLengthNaiveCounterexamples = p.str_fixed_length_naive_cex(); } -#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; +#define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void theory_str_params::display(std::ostream & out) const { DISPLAY_PARAM(m_StrongArrangements); diff --git a/src/tactic/smtlogics/smt_tactic.cpp b/src/tactic/smtlogics/smt_tactic.cpp index d47650c3460..aefe7ccadee 100644 --- a/src/tactic/smtlogics/smt_tactic.cpp +++ b/src/tactic/smtlogics/smt_tactic.cpp @@ -23,7 +23,7 @@ Module Name: tactic * mk_smt_tactic(ast_manager & m, params_ref const & p) { sat_params sp(p); - if (sp.smt()) + if (sp.smt()) return mk_solver2tactic(mk_smt2_solver(m, p)); if (sp.euf()) return mk_sat_tactic(m, p); From a2cc504d4a85755f23df2f1d1b18dbff3fcaf0dc Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 3 Jan 2023 09:49:58 +0000 Subject: [PATCH 245/597] remove a couple more std::endl --- src/math/lp/mps_reader.h | 4 ++-- src/tactic/core/collect_statistics_tactic.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h index f2cf2d32047..8093954b11e 100644 --- a/src/math/lp/mps_reader.h +++ b/src/math/lp/mps_reader.h @@ -277,8 +277,8 @@ class mps_reader { } else { fail: set_m_ok_to_false(); - *m_message_stream << "cannot understand this line" << std::endl; - *m_message_stream << "line = " << m_line << ", line number is " << m_line_number << std::endl; + *m_message_stream << "cannot understand this line\n" + "line = " << m_line << ", line number is " << m_line_number << std::endl; return; } } diff --git a/src/tactic/core/collect_statistics_tactic.cpp b/src/tactic/core/collect_statistics_tactic.cpp index b02adad6e03..b2c46cae6f2 100644 --- a/src/tactic/core/collect_statistics_tactic.cpp +++ b/src/tactic/core/collect_statistics_tactic.cpp @@ -73,10 +73,10 @@ class collect_statistics_tactic : public tactic { for (unsigned i = 0; i < sz; i++) for_each_expr(cp, visited, g->form(i)); - std::cout << "(" << std::endl; + std::cout << "(\n"; for (auto const& kv : m_stats) - std::cout << " :" << kv.first << " " << kv.second << std::endl; - std::cout << ")" << std::endl; + std::cout << " :" << kv.first << " " << kv.second << '\n'; + std::cout << ")\n"; g->inc_depth(); result.push_back(g.get()); From e508ef17f6d6d716f88906d7f7acb35bece9bc92 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 3 Jan 2023 10:39:28 +0000 Subject: [PATCH 246/597] fix Alive bug #875: bit blaster not respecting soft memory limit --- src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index dc1df22a362..7a3ee0ea619 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -27,7 +27,7 @@ Revision History: template void bit_blaster_tpl::checkpoint() { - if (memory::get_allocation_size() > m_max_memory) + if (memory::get_allocation_size() > m_max_memory || memory::above_high_watermark()) throw rewriter_exception(Z3_MAX_MEMORY_MSG); if (!m().inc()) throw rewriter_exception(m().limit().get_cancel_msg()); From e448191212c319f9388e0ddf6845b55de1348465 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 3 Jan 2023 11:08:57 +0000 Subject: [PATCH 247/597] array rewriter: expand select of store with const array into an ite This: (simplify (select (store ((as const (Array (_ BitVec 4) (_ BitVec 4))) #x0) x #x1) y)) => (ite (= x y) #x1 #x0) --- src/ast/rewriter/array_rewriter.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index dd0e7e86930..08173d430be 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -228,14 +228,17 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, } return true; }; + expr *array = to_app(args[0])->get_arg(0); + bool is_leaf = m_util.is_const(array); bool should_expand = m_blast_select_store || + is_leaf || are_values() || - (m_expand_select_store && to_app(args[0])->get_arg(0)->get_ref_count() == 1); + (m_expand_select_store && array->get_ref_count() == 1); if (should_expand) { // select(store(a, I, v), J) --> ite(I=J, v, select(a, J)) ptr_buffer new_args; - new_args.push_back(to_app(args[0])->get_arg(0)); + new_args.push_back(array); new_args.append(num_args-1, args+1); expr * sel_a_j = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.data()); expr * v = to_app(args[0])->get_arg(num_args); From 6f95c77023ea3793b3a631798eff02e04901938d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 11:56:28 -0800 Subject: [PATCH 248/597] fix bugs in flatten_clauses simplifier, switch proof/fml Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/dependent_expr.h | 3 ++- src/ast/simplifiers/flatten_clauses.h | 8 ++++---- src/sat/sat_solver/sat_smt_solver.cpp | 2 +- src/tactic/dependent_expr_state_tactic.h | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr.h b/src/ast/simplifiers/dependent_expr.h index c1ba9dd2dac..3b7515fe9e1 100644 --- a/src/ast/simplifiers/dependent_expr.h +++ b/src/ast/simplifiers/dependent_expr.h @@ -32,6 +32,7 @@ class dependent_expr { m_fml(fml), m_proof(p), m_dep(d) { + SASSERT(fml); m.inc_ref(fml); m.inc_ref(d); m.inc_ref(p); @@ -122,4 +123,4 @@ class dependent_expr { inline std::ostream& operator<<(std::ostream& out, dependent_expr const& d) { return d.display(out); -} \ No newline at end of file +} diff --git a/src/ast/simplifiers/flatten_clauses.h b/src/ast/simplifiers/flatten_clauses.h index e2da2d182aa..ab02faca1ea 100644 --- a/src/ast/simplifiers/flatten_clauses.h +++ b/src/ast/simplifiers/flatten_clauses.h @@ -67,8 +67,8 @@ class flatten_clauses : public dependent_expr_simplifier { decomposed = true; if (decomposed) { for (expr* arg : *to_app(b)) - m_fmls.add(dependent_expr(m, nullptr, m.mk_or(a, mk_not(m, arg)), de.dep())); - m_fmls.update(idx, dependent_expr(m, nullptr, m.mk_true(), nullptr)); + m_fmls.add(dependent_expr(m, m.mk_or(a, mk_not(m, arg)), nullptr, de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr, nullptr)); ++m_num_flat; continue; } @@ -79,8 +79,8 @@ class flatten_clauses : public dependent_expr_simplifier { decomposed = true; if (decomposed) { for (expr * arg : *to_app(b)) - m_fmls.add(dependent_expr(m, nullptr, m.mk_or(a, arg), de.dep())); - m_fmls.update(idx, dependent_expr(m, nullptr, m.mk_true(), nullptr)); + m_fmls.add(dependent_expr(m, m.mk_or(a, arg), nullptr, de.dep())); + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr, nullptr)); ++m_num_flat; continue; } diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index f5872b05a09..cc138be9b8d 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -56,7 +56,7 @@ class sat_smt_solver : public solver { ~dep_expr_state() override {} virtual unsigned qtail() const override { return s.m_fmls.size(); } dependent_expr const& operator[](unsigned i) override { return s.m_fmls[i]; } - void update(unsigned i, dependent_expr const& j) override { s.m_fmls[i] = j; } + void update(unsigned i, dependent_expr const& j) override { SASSERT(j.fml()); s.m_fmls[i] = j; } void add(dependent_expr const& j) override { s.m_fmls.push_back(j); } bool inconsistent() override { return s.m_solver.inconsistent(); } model_reconstruction_trail& model_trail() override { return m_reconstruction_trail; } diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index bf33d7fd264..931fc7c26c4 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -48,7 +48,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { dependent_expr_state(m), m(m), m_params(p), - m_dep(m, nullptr, nullptr, nullptr), + m_dep(m, m.mk_true(), nullptr, nullptr), m_factory(f) {} @@ -129,7 +129,7 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { m_simp = nullptr; m_model_trail = nullptr; m_goal = nullptr; - m_dep = dependent_expr(m, nullptr, nullptr, nullptr); + m_dep = dependent_expr(m, m.mk_true(), nullptr, nullptr); } void collect_statistics(statistics & st) const override { From aa080a6b19cfcc10cd03c2a281ed0a98a27c09e2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 12:22:38 -0800 Subject: [PATCH 249/597] update ignore-int handling #6429 Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_solver.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index a69f3604ea7..c7318dbd7df 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -971,6 +971,7 @@ namespace arith { } auto st = sat::check_result::CR_DONE; + bool int_undef = false; TRACE("arith", ctx.display(tout);); @@ -984,9 +985,7 @@ namespace arith { return sat::check_result::CR_CONTINUE; case l_undef: TRACE("arith", tout << "check-lia giveup\n";); - if (ctx.get_config().m_arith_ignore_int) - return sat::check_result::CR_GIVEUP; - + int_undef = true; st = sat::check_result::CR_CONTINUE; break; } @@ -1012,6 +1011,8 @@ namespace arith { } if (!check_delayed_eqs()) return sat::check_result::CR_CONTINUE; + if (ctx.get_config().m_arith_ignore_int && int_undef) + return sat::check_result::CR_GIVEUP; if (m_not_handled != nullptr) { TRACE("arith", tout << "unhandled operator " << mk_pp(m_not_handled, m) << "\n";); return sat::check_result::CR_GIVEUP; From ef101190056d5d856cf34135645916f01be6c20e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 13:05:45 -0800 Subject: [PATCH 250/597] #6429 fixes --- src/math/simplex/model_based_opt.cpp | 4 ++-- src/sat/sat_solver/sat_smt_solver.cpp | 2 +- src/sat/smt/euf_solver.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 8df04327dc7..f53ab6f8f45 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -1510,7 +1510,7 @@ namespace opt { for (unsigned v : vs) { - def v_def = project(v, false); + def v_def = project(v, compute_def); if (compute_def) eliminate(v, v_def); } @@ -1739,7 +1739,7 @@ namespace opt { for (unsigned i = 0; i < num_vars; ++i) { m_result.push_back(project(vars[i], compute_def)); eliminate(vars[i], m_result.back()); - TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); + TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n" << m_result << "\n");); } return m_result; } diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index cc138be9b8d..923594501b5 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -179,8 +179,8 @@ class sat_smt_solver : public solver { m_preprocess_state(*this), m_preprocess(m, p, m_preprocess_state), m_trail(m_preprocess_state.m_trail), - m_dep(m, m_trail), m_solver(p, m.limit()), + m_dep(m, m_trail), m_assumptions(m), m_core(m), m_ors(m), m_aux_fmls(m), m_internalized_fmls(m), m_map(m), m_mc(alloc(generic_model_converter, m, "sat-smt-solver")) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 8656083392d..e34a590f805 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -48,11 +48,11 @@ namespace euf { m_unhandled_functions(m), m_to_m(&m), m_to_si(&si), - m_values(m), m_clause_visitor(m), m_smt_proof_checker(m, p), - m_clause(m), - m_expr_args(m) + m_clause(m), + m_expr_args(m), + m_values(m) { updt_params(p); m_relevancy.set_enabled(get_config().m_relevancy_lvl > 2); From 21362c0b989456d7f842a639e08b63dcc3c7b63e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 15:00:25 -0800 Subject: [PATCH 251/597] make case-def and recfun-num-rounds re-parsable for logging --- src/ast/recfun_decl_plugin.cpp | 38 +++++++++++++------ src/ast/recfun_decl_plugin.h | 5 ++- src/cmd_context/cmd_context.cpp | 17 +++++---- src/tactic/bv/bit_blaster_model_converter.cpp | 1 + 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 86c3fcf3bd9..84d68d7823f 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -36,7 +36,6 @@ namespace recfun { ast_manager &m, family_id fid, def * d, - std::string & name, unsigned case_index, sort_ref_vector const & arg_sorts, expr_ref_vector const& guards, @@ -44,10 +43,10 @@ namespace recfun { : m_pred(m), m_guards(guards), m_rhs(expr_ref(rhs,m)), - m_def(d) { - parameter p(case_index); - func_decl_info info(fid, OP_FUN_CASE_PRED, 1, &p); - m_pred = m.mk_func_decl(symbol(name.c_str()), arg_sorts.size(), arg_sorts.data(), m.mk_bool_sort(), info); + m_def(d) { + parameter ps[2] = { parameter(case_index), parameter(d->get_decl()) }; + func_decl_info info(fid, OP_FUN_CASE_PRED, 2, ps); + m_pred = m.mk_func_decl(symbol("case-def"), arg_sorts.size(), arg_sorts.data(), m.mk_bool_sort(), info); } def::def(ast_manager &m, family_id fid, symbol const & s, @@ -220,11 +219,10 @@ namespace recfun { } - void def::add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) { - case_def c(m, m_fid, this, name, case_index, get_domain(), conditions, rhs); + void def::add_case(unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) { + case_def c(m, m_fid, this, case_index, get_domain(), conditions, rhs); c.set_is_immediate(is_imm); - TRACEFN("add_case " << name - << "\n" << mk_pp(rhs, m) + TRACEFN("add_case " << case_index << " " << mk_pp(rhs, m) << "\n:is_imm " << is_imm << "\n:guards " << conditions); m_cases.push_back(c); @@ -261,7 +259,7 @@ namespace recfun { // is the function a macro (unconditional body)? if (is_macro || n_vars == 0 || !contains_ite(u, rhs)) { // constant function or trivial control flow, only one (dummy) case - add_case(name, 0, conditions, rhs); + add_case(0, conditions, rhs); return; } @@ -347,7 +345,7 @@ namespace recfun { // yield new case bool is_imm = is_i(case_rhs); - add_case(name, case_idx++, conditions, case_rhs, is_imm); + add_case(case_idx++, conditions, case_rhs, is_imm); } } @@ -436,6 +434,12 @@ namespace recfun { return *(m_util.get()); } + void plugin::get_op_names(svector & op_names, symbol const & logic) { + op_names.push_back(builtin_name("case-def", OP_FUN_CASE_PRED)); + op_names.push_back(builtin_name("recfun-num-rounds", OP_NUM_ROUNDS)); + } + + promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) { def* d = u().decl_fun(name, n, params, range, is_generated); SASSERT(!m_defs.contains(d->get_decl())); @@ -498,6 +502,18 @@ namespace recfun { func_decl * plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { + func_decl_info info(get_family_id(), k, num_parameters, parameters); + switch (k) { + case OP_FUN_CASE_PRED: + SASSERT(num_parameters == 2); + return m().mk_func_decl(symbol("case-def"), arity, domain, m().mk_bool_sort(), info); + case OP_NUM_ROUNDS: + SASSERT(num_parameters == 1); + SASSERT(arity == 0); + return m().mk_const_decl(symbol("recfun-num-rounds"), m().mk_bool_sort(), info); + default: + break; + } UNREACHABLE(); return nullptr; } diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index bb75a854cb2..ae9c060e99f 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -72,7 +72,6 @@ namespace recfun { case_def(ast_manager & m, family_id fid, def * d, - std::string & name, unsigned case_index, sort_ref_vector const & arg_sorts, expr_ref_vector const& guards, @@ -124,7 +123,7 @@ namespace recfun { // compute cases for a function, given its RHS (possibly containing `ite`). void compute_cases(util& u, replace& subst, is_immediate_pred &, bool is_macro, unsigned n_vars, var *const * vars, expr* rhs); - void add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false); + void add_case(unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false); bool contains_ite(util& u, expr* e); // expression contains a test over a def? bool contains_def(util& u, expr* e); // expression contains a def public: @@ -190,6 +189,8 @@ namespace recfun { func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; + + void get_op_names(svector & op_names, symbol const & logic) override; promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 2d90b70c35e..5602f53a37b 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1085,7 +1085,12 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s); return f; } - throw cmd_exception("invalid function declaration reference, unknown function ", s); + if (num_indices > 0 && m_func_decls.find(s, fs)) + f = fs.find(m(), arity, domain, range); + if (f) + return f; + + throw cmd_exception("invalid function declaration reference, unknown indexed function ", s); } psort_decl * cmd_context::find_psort_decl(symbol const & s) const { @@ -1134,12 +1139,10 @@ bool cmd_context::try_mk_builtin_app(symbol const & s, unsigned num_args, expr * fid = d2.m_fid; k = d2.m_decl; } - if (num_indices == 0) { - result = m().mk_app(fid, k, 0, nullptr, num_args, args, range); - } - else { - result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); - } + if (num_indices == 0) + result = m().mk_app(fid, k, 0, nullptr, num_args, args, range); + else + result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); CHECK_SORT(result.get()); return nullptr != result.get(); } diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index 88ff1168312..9a423452f2f 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -148,6 +148,7 @@ struct bit_blaster_model_converter : public model_converter { for (expr* bit : *to_app(bs)) { func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); + CTRACE("bv", !bit_val, tout << mk_pp(bit, m()) << " " << *old_model << "\n"); SASSERT(bit_val); vals.push_back(bit_val); } From 380c701cbe1a16380bf462e5d6ad5d97ebde28c7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 15:01:40 -0800 Subject: [PATCH 252/597] restore debug clang/gcc build --- src/math/simplex/model_based_opt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index f53ab6f8f45..3c38cfb0e7c 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -1739,7 +1739,7 @@ namespace opt { for (unsigned i = 0; i < num_vars; ++i) { m_result.push_back(project(vars[i], compute_def)); eliminate(vars[i], m_result.back()); - TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n" << m_result << "\n");); + TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); } return m_result; } From e0099150ca63f16cdb98ada543492985baf62c10 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 15:28:57 -0800 Subject: [PATCH 253/597] #6429 --- src/tactic/bv/bit_blaster_model_converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index 9a423452f2f..5958a9d3867 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -148,8 +148,8 @@ struct bit_blaster_model_converter : public model_converter { for (expr* bit : *to_app(bs)) { func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); - CTRACE("bv", !bit_val, tout << mk_pp(bit, m()) << " " << *old_model << "\n"); - SASSERT(bit_val); + if (!bit_val) + bit_val = m().mk_false(); vals.push_back(bit_val); } if (TO_BOOL) From 81ce57b5a84d8903d51b0a3b639da28d36448e15 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 15:38:13 -0800 Subject: [PATCH 254/597] #6429 --- src/sat/smt/euf_solver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index e34a590f805..b56093eb0ff 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -574,7 +574,7 @@ namespace euf { return sat::check_result::CR_CONTINUE; if (cont) return sat::check_result::CR_CONTINUE; - if (m_qsolver) + if (m_qsolver && !m_config.m_arith_ignore_int) apply_solver(m_qsolver); if (num_nodes < m_egraph.num_nodes()) return sat::check_result::CR_CONTINUE; @@ -582,7 +582,9 @@ namespace euf { return sat::check_result::CR_CONTINUE; TRACE("after_search", s().display(tout);); if (give_up) - return sat::check_result::CR_GIVEUP; + return sat::check_result::CR_GIVEUP; + if (m_qsolver && m_config.m_arith_ignore_int) + return sat::check_result::CR_GIVEUP; return sat::check_result::CR_DONE; } From 0d8a472aacd6541200466d83cf8b06f538751b07 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Jan 2023 16:55:44 -0800 Subject: [PATCH 255/597] pass sign into literal definition for pbge --- src/sat/smt/pb_internalize.cpp | 12 ++++++------ src/sat/smt/pb_solver.cpp | 4 ++-- src/sat/smt/pb_solver.h | 2 +- src/tactic/tactic.cpp | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index af25e7a9252..1a83dbc87ca 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -113,13 +113,13 @@ namespace pb { k1 += wl.first; } } - add_pb_ge(sat::null_bool_var, wlits, k1); + add_pb_ge(sat::null_bool_var, sign, wlits, k1); return sat::null_literal; } else { bool_var v = s().add_var(true); literal lit(v, sign); - add_pb_ge(v, wlits, k.get_unsigned()); + add_pb_ge(v, sign, wlits, k.get_unsigned()); TRACE("ba", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } @@ -140,13 +140,13 @@ namespace pb { k1 += wl.first; } } - add_pb_ge(sat::null_bool_var, wlits, k1); + add_pb_ge(sat::null_bool_var, sign, wlits, k1); return sat::null_literal; } else { sat::bool_var v = s().add_var(true); sat::literal lit(v, sign); - add_pb_ge(v, wlits, k.get_unsigned()); + add_pb_ge(v, sign, wlits, k.get_unsigned()); TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } @@ -160,14 +160,14 @@ namespace pb { bool base_assert = (root && !sign && s().num_user_scopes() == 0); bool_var v1 = base_assert ? sat::null_bool_var : s().add_var(true); bool_var v2 = base_assert ? sat::null_bool_var : s().add_var(true); - add_pb_ge(v1, wlits, k.get_unsigned()); + add_pb_ge(v1, false, wlits, k.get_unsigned()); k.neg(); for (wliteral& wl : wlits) { wl.second.neg(); k += rational(wl.first); } check_unsigned(k); - add_pb_ge(v2, wlits, k.get_unsigned()); + add_pb_ge(v2, false, wlits, k.get_unsigned()); if (base_assert) { return sat::null_literal; } diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index 424b20d4e2c..ade48c42bfd 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -1472,8 +1472,8 @@ namespace pb { return p; } - void solver::add_pb_ge(bool_var v, svector const& wlits, unsigned k) { - literal lit = v == sat::null_bool_var ? sat::null_literal : literal(v, false); + void solver::add_pb_ge(bool_var v, bool sign, svector const& wlits, unsigned k) { + literal lit = v == sat::null_bool_var ? sat::null_literal : literal(v, sign); add_pb_ge(lit, wlits, k, m_is_redundant); } diff --git a/src/sat/smt/pb_solver.h b/src/sat/smt/pb_solver.h index f6a0c049e1f..99ea459839e 100644 --- a/src/sat/smt/pb_solver.h +++ b/src/sat/smt/pb_solver.h @@ -371,7 +371,7 @@ namespace pb { ~solver() override; void set_lookahead(sat::lookahead* l) override { m_lookahead = l; } void add_at_least(bool_var v, literal_vector const& lits, unsigned k); - void add_pb_ge(bool_var v, svector const& wlits, unsigned k); + void add_pb_ge(bool_var v, bool sign, svector const& wlits, unsigned k); bool is_external(bool_var v) override; bool propagated(literal l, sat::ext_constraint_idx idx) override; diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 8bf6efac5ee..179a42ab857 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -51,7 +51,7 @@ struct tactic_report::imp { << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory << " :after-memory " << std::fixed << std::setprecision(2) << end_memory - << ")" << std::endl); + << ")\n"); IF_VERBOSE(20, m_goal.display(verbose_stream() << m_id << "\n")); SASSERT(m_goal.is_well_formed()); } @@ -71,7 +71,7 @@ tactic_report::~tactic_report() { void report_tactic_progress(char const * id, unsigned val) { if (val > 0) { - IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << id << " " << val << ")" << std::endl;); + IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << id << " " << val << ")\n"); } } @@ -166,7 +166,7 @@ void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result) { t.cleanup(); } catch (tactic_exception & ex) { - IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(tactic-exception \"" << escaped(ex.msg()) << "\")" << std::endl;); + IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(tactic-exception \"" << escaped(ex.msg()) << "\")\n"); t.cleanup(); throw ex; } From c07b6ab38faa03e5439a3b7c65f489bae1d53081 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 5 Jan 2023 20:23:01 -0800 Subject: [PATCH 256/597] more tactic descriptions --- doc/mk_tactic_doc.py | 1 + src/tactic/bv/bv_bound_chk_tactic.h | 8 +++ src/tactic/bv/bv_slice_tactic.h | 28 ++++++++++ src/tactic/core/dom_simplify_tactic.h | 24 ++++++++- src/tactic/core/occf_tactic.h | 42 +++++++++++---- src/tactic/core/symmetry_reduce_tactic.h | 15 +++++- src/tactic/core/tseitin_cnf_tactic.h | 66 +++++++++++++++--------- src/tactic/ufbv/macro_finder_tactic.h | 40 +++++++++++++- src/tactic/ufbv/quasi_macros_tactic.cpp | 8 +++ src/tactic/ufbv/quasi_macros_tactic.h | 29 ++++++++++- 10 files changed, 224 insertions(+), 37 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index 6f4837cddb6..fe1a495c146 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -59,6 +59,7 @@ def find_tactic_name(path): m = is_tac_name.search(line) if m: return m.group(1) + print(f"no tactic in {path}") return "" def presort_files(): diff --git a/src/tactic/bv/bv_bound_chk_tactic.h b/src/tactic/bv/bv_bound_chk_tactic.h index bafd2ec5131..bbf4783533a 100644 --- a/src/tactic/bv/bv_bound_chk_tactic.h +++ b/src/tactic/bv/bv_bound_chk_tactic.h @@ -9,6 +9,14 @@ Module Name: Mikolas Janota +Tactic Documentation + +## Tactic bv_bound_chk + +### Short Description + +Attempts to detect inconsistencies of bounds on bv expressions. + ### Notes * does not support proofs, does not support cores diff --git a/src/tactic/bv/bv_slice_tactic.h b/src/tactic/bv/bv_slice_tactic.h index 23ed16680ec..77ff081d831 100644 --- a/src/tactic/bv/bv_slice_tactic.h +++ b/src/tactic/bv/bv_slice_tactic.h @@ -13,6 +13,34 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-10-30 +Tactic Documentation + +## Tactic bv-slice + +### Short Description + +Slices bit-vectors into sub-ranges to allow simplifying sub-ranges. + +### Long Description + +It rewrites a state using bit-vector slices. +Slices are extracted from bit-vector equality assertions. +An equality assertion may equate a sub-range of a bit-vector +with a constant. The tactic ensures that all occurrences of the +subrange are replaced by the constants to allow additional +simplification + +### Example + +```z3 ignore-errors +(declare-const x (_ BitVec 32)) +(declare-const y (_ BitVec 32)) + (assert (= ((_ extract 31 16) x) (_ bv123 16))) +(assert (= ((_ extract 15 0) x) ((_ extract 16 1) y))) +(assert (= (bvadd x x) y)) +(apply bv-slice) +``` + --*/ #pragma once diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 43e13d9610c..b4c83d04eeb 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -14,7 +14,29 @@ Module Name: Nikolaj and Nuno -Notes: +Tactic Documentation: + +## Tactic dom-simplify + +### Short Description + +Apply dominator simplification rules + +### Long Description + +Dominator-based simplification is a context dependent simplification function that uses a dominator tree to control the number of paths it +visits during simplification. The expression DAG may have an exponential number of paths, but only paths corresponding to a dominator +tree are visited. Since the paths selected by the dominator trees are limited, the simplifier may easily fail to simplify within a context. + +### Example + +```z3 +(declare-const a Bool) +(declare-const b Bool) +(assert (and a (or a b))) +(apply dom-simplify) +``` + --*/ diff --git a/src/tactic/core/occf_tactic.h b/src/tactic/core/occf_tactic.h index 2e211c9d77e..efc9a769b73 100644 --- a/src/tactic/core/occf_tactic.h +++ b/src/tactic/core/occf_tactic.h @@ -5,20 +5,42 @@ Module Name: occf_tactic.h -Abstract: - - Put clauses in the assertion set in - OOC (one constraint per clause) form. - Constraints occurring in formulas that - are not clauses are ignored. - The formula can be put into CNF by - using mk_sat_preprocessor strategy. - Author: Leonardo de Moura (leonardo) 2011-12-28. -Revision History: +Tactic Documentation: + +## Tactic occf + +### Short Description + +Put goal in one constraint per clause normal form + +### Long Description + +Put clauses in the assertion set in +OOC (one constraint per clause) form. +Constraints occurring in formulas that +are not clauses are ignored. +The formula can be put into CNF by +using `mk_sat_preprocessor` strategy. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) + +(assert (or (= x y) (> x (- y)))) +(assert (or (= x y) (< x (- y)))) +(apply occf) +``` + +### Notes + +* Does not support proofs +* only clauses are considered --*/ #pragma once diff --git a/src/tactic/core/symmetry_reduce_tactic.h b/src/tactic/core/symmetry_reduce_tactic.h index 90c032323a0..2544bb108ab 100644 --- a/src/tactic/core/symmetry_reduce_tactic.h +++ b/src/tactic/core/symmetry_reduce_tactic.h @@ -13,7 +13,20 @@ Module Name: Nikolaj (nbjorner) 2011-05-31 -Notes: + +Tactic Documentation: + +## Tactic symmetry-reduce + +### Short Description + +Apply symmetry reduction + +### Long Description + +The tactic applies symmetry reduction for uninterpreted functions and equalities. +It applies a straight-forward adaption of an algorithm proposed for veriT. + --*/ #pragma once diff --git a/src/tactic/core/tseitin_cnf_tactic.h b/src/tactic/core/tseitin_cnf_tactic.h index a5e116825d9..6942d1559ec 100644 --- a/src/tactic/core/tseitin_cnf_tactic.h +++ b/src/tactic/core/tseitin_cnf_tactic.h @@ -7,42 +7,62 @@ Module Name: Abstract: - Puts an assertion set in CNF. - Auxiliary variables are used to avoid blowup. - Features: +Author: + + Leonardo (leonardo) 2011-12-29 + +Tactic Documentation: + +## Tactic tseitin-cnf + +### Short Description + +Convert goal into CNF using tseitin-like encoding (note: quantifiers are ignored). + +### Long Description + +Puts an assertion set in CNF. +Auxiliary variables are used to avoid blowup. + +Features: - - Efficient encoding is used for commonly used patterns such as: - (iff a (iff b c)) - (or (not (or a b)) (not (or a c)) (not (or b c))) +- Efficient encoding is used for commonly used patterns such as: + `(iff a (iff b c))` + `(or (not (or a b)) (not (or a c)) (not (or b c)))` - - Efficient encoding is used for chains of if-then-elses +- Efficient encoding is used for chains of if-then-elses - - Distributivity is applied to non-shared nodes if the blowup is acceptable. +- Distributivity is applied to non-shared nodes if the blowup is acceptable. - - The features above can be disabled/enabled using parameters. +- The features above can be disabled/enabled using parameters. - - The assertion-set is only modified if the resultant set of clauses - is "acceptable". +- The assertion-set is only modified if the resultant set of clauses is "acceptable". - Notes: +Notes: - - Term-if-then-else expressions are not handled by this strategy. - This kind of expression should be processed by other strategies. +- Term-if-then-else expressions are not handled by this strategy. +This kind of expression should be processed by other strategies. - - Quantifiers are treated as "theory" atoms. They are viewed - as propositional variables by this strategy. +- Quantifiers are treated as "theory" atoms. They are viewed +as propositional variables by this strategy. - - The assertion set may contain free variables. +- The assertion set may contain free variables. - - This strategy assumes the assertion_set_rewriter was - used before invoking it. - In particular, it is more effective when "and" operators - were eliminated. +- This strategy assumes the assertion_set_rewriter was used before invoking it. +In particular, it is more effective when "and" operators +were eliminated. -Author: +### Example - Leonardo (leonardo) 2011-12-29 +```z3 +(declare-const a Bool) +(declare-const b Bool) +(declare-const c Bool) + +(assert (= a (= b c))) +(apply tseitin-cnf) +``` --*/ #pragma once diff --git a/src/tactic/ufbv/macro_finder_tactic.h b/src/tactic/ufbv/macro_finder_tactic.h index 03b5adc17d3..487cf1f0dc7 100644 --- a/src/tactic/ufbv/macro_finder_tactic.h +++ b/src/tactic/ufbv/macro_finder_tactic.h @@ -13,7 +13,45 @@ Module Name: Christoph (cwinter) 2012-10-26 -Notes: +Tactic Description + +## Tactic macro-finder + +### Short Description + +Identifies and applies macros. + +### Long Description + +It finds implicit macro definitions in quantifiers. +A main instance of a macro an equality that defines a function `f` using some term `t` that does not contain `f`. +Other instances of macros are also recognized by the macro finder. + +* `(forall (x) (= (f x) t))` + +* `not (= (p x) t)` is recognized as `(p x) = (not t)` + +* `(iff (= (f x) t) cond)` rewrites to `(f x) = (if cond t else (k x))` + * add clause `(not (= (k x) t))` + +* `(= (+ (f x) s) t)` becomes `(= (f x) (- t s))` + +* `(= (+ (* -1 (f x)) x) t)` becomes `(= (f x) (- (- t s)))` + +### Example + +```z3 +(declare-fun f (Int) Int) +(declare-fun p (Int) Bool) + +(assert (forall ((x Int)) (= (+ (f x) x) 3))) +(assert (p (f 8))) +(apply macro-finder) +``` + +### Notes + +* Supports proofs, unsat cores, but not goals with recursive function definitions. --*/ #pragma once diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index 051ee4fed78..12092cdc7ae 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -21,8 +21,10 @@ Module Name: #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" #include "ast/macros/quasi_macros.h" +#include "ast/recfun_decl_plugin.h" #include "tactic/ufbv/quasi_macros_tactic.h" + class quasi_macros_tactic : public tactic { struct imp { @@ -41,6 +43,12 @@ class quasi_macros_tactic : public tactic { bool produce_proofs = g->proofs_enabled(); bool produce_unsat_cores = g->unsat_core_enabled(); + + recfun::util rec(m()); + if (!rec.get_rec_funs().empty()) { + result.push_back(g.get()); + return; + } macro_manager mm(m_manager); quasi_macros qm(m_manager, mm); diff --git a/src/tactic/ufbv/quasi_macros_tactic.h b/src/tactic/ufbv/quasi_macros_tactic.h index a33466e9b51..872296dd9a4 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.h +++ b/src/tactic/ufbv/quasi_macros_tactic.h @@ -13,7 +13,34 @@ Module Name: Christoph (cwinter) 2012-10-26 -Notes: +Tactic Description + +## Tactic quasi-macro-finder + +### Short Description +dentifies and applies quasi-macros. + +### Long Description + +A quasi macro defines a function symbol that contains more arguments than the number of bound variables it defines. +The additional arguments are functions of the bound variables. + +### Example + +```z3 +(declare-fun f (Int Int Int) Int) +(declare-fun p (Int) Bool) +(declare-const a Int) + +(assert (forall ((x Int) (y Int)) (= (f x y 1) (* 2 x y)))) +(assert (p (f 8 a (+ a 8)))) +(apply quasi-macros) +``` + +### Notes + +* Supports proofs and cores + --*/ #pragma once From 25112e47b46691ef0007068858cae034b9d72055 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 5 Jan 2023 20:59:28 -0800 Subject: [PATCH 257/597] bugfix to flatten-clases simplifier Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/flatten_clauses.h | 2 +- src/tactic/arith/add_bounds_tactic.h | 24 ++++++++++++++++++++++-- src/tactic/arith/lia2pb_tactic.h | 24 ++++++++++++++++++++---- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/ast/simplifiers/flatten_clauses.h b/src/ast/simplifiers/flatten_clauses.h index ab02faca1ea..2d65fd76d79 100644 --- a/src/ast/simplifiers/flatten_clauses.h +++ b/src/ast/simplifiers/flatten_clauses.h @@ -92,7 +92,7 @@ class flatten_clauses : public dependent_expr_simplifier { if (decomposed) { expr* na = mk_not(m, a); for (expr* arg : *to_app(b)) - m_fmls.add(dependent_expr(m, m.mk_or(na, arg), nullptr, de.dep())); + m_fmls.add(dependent_expr(m, m.mk_or(na, mk_not(m, arg)), nullptr, de.dep())); m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr, nullptr)); ++m_num_flat; continue; diff --git a/src/tactic/arith/add_bounds_tactic.h b/src/tactic/arith/add_bounds_tactic.h index 0d42d8e6113..b69128c3eb1 100644 --- a/src/tactic/arith/add_bounds_tactic.h +++ b/src/tactic/arith/add_bounds_tactic.h @@ -7,13 +7,33 @@ Module Name: Abstract: - Tactic for bounding unbounded variables. + Author: Leonardo de Moura (leonardo) 2011-06-30. -Revision History: +Tactic Documentation: + +## Tactic add-bounds + +### Short Description + +Tactic for bounding unbounded variables. + +### Long Description + +The tactic creates a stronger sub-goal by adding bounds to variables. +The new goal may not be satisfiable even if the original goal is. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(assert (> (+ x y) 10)) +(apply add-bounds) +``` --*/ #pragma once diff --git a/src/tactic/arith/lia2pb_tactic.h b/src/tactic/arith/lia2pb_tactic.h index 860b04d1cd3..cd9c40634ca 100644 --- a/src/tactic/arith/lia2pb_tactic.h +++ b/src/tactic/arith/lia2pb_tactic.h @@ -5,15 +5,31 @@ Module Name: lia2pb_tactic.h -Abstract: - - Reduce bounded LIA benchmark into 0-1 LIA benchmark. Author: Leonardo de Moura (leonardo) 2012-02-07. -Revision History: +Tactic Documentation: + +## Tactic lia2pb + +### Short Description + +Reduce bounded LIA benchmark into 0-1 LIA benchmark. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(assert (<= 0 x)) +(assert (<= x 5)) +(assert (<= 0 y)) +(assert (<= y 5)) +(assert (>= (+ (* 2 x) y) 5)) +(apply lia2pb) +``` --*/ #pragma once From 95cb06d8cff3c70388dfdf295e2f2347e1945ffa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Jan 2023 19:53:55 -0800 Subject: [PATCH 258/597] add quasi macro detection --- src/ast/simplifiers/eliminate_predicates.cpp | 168 ++++++++++++++++++- src/ast/simplifiers/eliminate_predicates.h | 5 + src/tactic/arith/degree_shift_tactic.h | 2 +- 3 files changed, 170 insertions(+), 5 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 118edf04d24..2166913da04 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -115,6 +115,8 @@ bool eliminate_predicates::can_be_macro_head(expr* _head, unsigned num_bound) { return false; if (f->is_associative()) return false; + if (!is_uninterp(f)) + return false; uint_set indices; for (expr* arg : *head) { if (!is_var(arg)) @@ -146,8 +148,14 @@ bool eliminate_predicates::can_be_quasi_macro_head(expr* _head, unsigned num_bou return false; if (f->is_associative()) return false; + if (!is_uninterp(f)) + return false; uint_set indices; for (expr* arg : *head) { + if (occurs(f, arg)) + return false; + if (!is_macro_safe(arg)) + return false; if (!is_var(arg)) continue; unsigned idx = to_var(arg)->get_idx(); @@ -161,6 +169,49 @@ bool eliminate_predicates::can_be_quasi_macro_head(expr* _head, unsigned num_bou } +// +// (= (f x y (+ x y)) s), where x y are all bound variables. +// then replace (f x y z) by (if (= z (+ x y)) s (f' x y z)) +// + +void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause const& cl) { + expr_ref _body(body, m); + uint_set indices; + expr_ref_vector args(m), eqs(m); + var_ref new_var(m); + app_ref lhs(m), rhs(m); + func_decl_ref f1(m); + sort_ref_vector sorts(m); + svector names; + + unsigned num_decls = cl.m_bound.size(); + func_decl* f = head->get_decl(); + + for (expr* arg : *head) { + sorts.push_back(arg->get_sort()); + names.push_back(symbol(std::string("x") + std::to_string(args.size()))); + if (is_var(arg)) { + unsigned idx = to_var(arg)->get_idx(); + if (!indices.contains(idx)) { + indices.insert(idx); + args.push_back(arg); + continue; + } + } + new_var = m.mk_var(eqs.size() + num_decls, arg->get_sort()); + args.push_back(new_var); + eqs.push_back(m.mk_eq(arg, new_var)); + } + + // forall vars . f(args) = if eqs then body else f'(args) + f1 = m.mk_fresh_func_decl(f->get_name(), symbol::null, sorts.size(), sorts.data(), f->get_range()); + lhs = m.mk_app(f, args); + rhs = m.mk_ite(mk_and(eqs), body, m.mk_app(f1, args)); + insert_macro(lhs, rhs, cl.m_dep); +} + + + expr_ref eliminate_predicates::bind_free_variables_in_def(clause& cl, app* head, expr* def) { unsigned num_bound = cl.m_bound.size(); if (head->get_num_args() == num_bound) @@ -208,7 +259,7 @@ bool eliminate_predicates::try_find_binary_definition(func_decl* p, app_ref& hea }; for (auto* cl : m_use_list.get(p, false)) { - if (cl->m_alive && cl->m_literals.size() == 2) { + if (cl->m_alive && cl->size() == 2) { auto const& [atom1, sign1] = cl->m_literals[0]; auto const& [atom2, sign2] = cl->m_literals[1]; add_def(*cl, atom1, sign1, atom2, sign2); @@ -242,7 +293,7 @@ bool eliminate_predicates::try_find_binary_definition(func_decl* p, app_ref& hea }; for (auto* cl : m_use_list.get(p, true)) { - if (cl->m_alive && cl->m_literals.size() == 2) { + if (cl->m_alive && cl->size() == 2) { if (is_def(0, 1, *cl)) return true; if (is_def(1, 0, *cl)) @@ -507,10 +558,45 @@ void eliminate_predicates::try_find_macro(clause& cl) { // // - // To review: quasi-macros + // quasi-macros // (= (f x y (+ x y)) s), where x y are all bound variables. - // then replace (f x y z) by (if (= z (+ x y)) s (f' x y)) + // then replace (f x y z) by (if (= z (+ x y)) s (f' x y z)) // + auto can_be_qdef = [&](expr* _x, expr* y) { + if (!is_app(_x)) + return false; + app* x = to_app(_x); + return + can_be_quasi_macro_head(x, cl.m_bound.size()) && + is_macro_safe(y) && + !occurs(x->get_decl(), y); + }; + + if (cl.is_unit() && m.is_eq(cl.atom(0), x, y)) { + if (!cl.sign(0) && can_be_qdef(x, y)) { + insert_quasi_macro(to_app(x), y, cl); + return; + } + else if (!cl.sign(0) && can_be_qdef(y, x)) { + insert_quasi_macro(to_app(y), x, cl); + return; + } + else if (cl.sign(0) && m.is_bool(y) && can_be_qdef(x, y)) { + insert_quasi_macro(to_app(x), m.mk_not(y), cl); + return; + } + else if (cl.sign(0) && m.is_bool(y) && can_be_qdef(y, x)) { + insert_quasi_macro(to_app(y), m.mk_not(x), cl); + return; + } + } + if (cl.is_unit()) { + expr* body = cl.sign(0) ? m.mk_false() : m.mk_true(); + if (can_be_qdef(cl.atom(0), body)) { + insert_quasi_macro(to_app(x), body, cl); + return; + } + } } @@ -791,6 +877,11 @@ eliminate_predicates::clause* eliminate_predicates::init_clause(expr* f, expr_de bool sign = m.is_not(lit, lit); cl->m_literals.push_back({ expr_ref(lit, m), sign }); } + + // extend macro detection to exploit bijective functions? + // f(+ x 1) = g(x) -> f(x) = g(- x 1) + // init_injective(*cl); + // init_surjective(*cl); return cl; } @@ -811,6 +902,73 @@ void eliminate_predicates::init_clauses() { process_to_exclude(m_disable_elimination); } +/** + * Ad hoc recognize surjectivity axioms + * - exists y . f(y) = x + */ +void eliminate_predicates::init_surjective(clause const& cl) { + if (!cl.is_unit()) + return; + if (cl.sign(0)) + return; + if (!is_exists(cl.atom(0))) + return; +} + +/** + * Ad hoc recognize injectivity axioms + * - f(x) = f(y) => x = y + */ +void eliminate_predicates::init_injective(clause const& cl) { + if (cl.size() != 2) + return; + if (cl.m_bound.size() != 2) + return; + if (cl.sign(0) == cl.sign(1)) + return; + expr* a = cl.atom(0), *b = cl.atom(1); + if (!cl.sign(0) && cl.sign(1)) + std::swap(a, b); + expr* x, *y, *fx, *fy; + if (!m.is_eq(a, fx, fy)) + return; + if (!m.is_eq(b, x, y)) + return; + + auto is_fx = [&](expr* _fx, func_decl*& f, unsigned& idx) { + if (!is_app(_fx)) + return false; + app* fx = to_app(_fx); + if (fx->get_num_args() != 1) + return false; + if (!is_var(fx->get_arg(0))) + return false; + f = fx->get_decl(); + idx = to_var(fx->get_arg(0))->get_idx(); + return true; + }; + func_decl* f1, *f2; + unsigned idx1, idx2; + if (!is_fx(fx, f1, idx1)) + return; + if (!is_fx(fy, f2, idx2)) + return; + if (idx1 == idx2 || f1 != f2) + return; + + auto check_var = [&](expr* x, unsigned& idx) { + if (!is_var(x)) + return false; + idx = to_var(x)->get_idx(); + return true; + }; + if (!check_var(x, idx1) || !check_var(y, idx2)) + return; + if (idx1 == idx2) + return; + m_is_injective.mark(f1, true); +} + /** * Convert clauses to m_fmls */ @@ -838,6 +996,8 @@ void eliminate_predicates::reset() { m_to_exclude.reset(); m_disable_elimination.reset(); m_is_macro.reset(); + m_is_injective.reset(); + m_is_surjective.reset(); for (auto const& [k, v] : m_macros) dealloc(v); m_macros.reset(); diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index d91922f9e5d..af0ede11938 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -55,6 +55,7 @@ class eliminate_predicates : public dependent_expr_simplifier { std::ostream& display(std::ostream& out) const; + unsigned size() const { return m_literals.size(); } expr* atom(unsigned i) const { return m_literals[i].first; } bool sign(unsigned i) const { return m_literals[i].second; } bool is_unit() const { return m_literals.size() == 1; } @@ -91,6 +92,7 @@ class eliminate_predicates : public dependent_expr_simplifier { ast_mark m_disable_elimination, m_predicate_decls, m_is_macro; ptr_vector m_predicates; ptr_vector m_to_exclude; + ast_mark m_is_injective, m_is_surjective; stats m_stats; use_list m_use_list; der_rewriter m_der; @@ -101,6 +103,8 @@ class eliminate_predicates : public dependent_expr_simplifier { clause* init_clause(unsigned i); clause* init_clause(expr* f, expr_dependency* d, unsigned i); + void init_injective(clause const& cl); + void init_surjective(clause const& cl); clause* resolve(func_decl* p, clause& pos, clause& neg); void add_use_list(clause& cl); @@ -109,6 +113,7 @@ class eliminate_predicates : public dependent_expr_simplifier { void insert_macro(app* head, expr* def, expr_dependency* dep); expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); bool can_be_macro_head(expr* head, unsigned num_bound); + void insert_quasi_macro(app* head, expr* body, clause const& cl); bool can_be_quasi_macro_head(expr* head, unsigned num_bound); bool is_macro_safe(expr* e); void try_find_macro(clause& cl); diff --git a/src/tactic/arith/degree_shift_tactic.h b/src/tactic/arith/degree_shift_tactic.h index cdc4823e288..01317ade50c 100644 --- a/src/tactic/arith/degree_shift_tactic.h +++ b/src/tactic/arith/degree_shift_tactic.h @@ -28,7 +28,7 @@ Then, replace $x^n$ with a new fresh variable $y$. ```z3 (declare-const x Real) (declare-const y Real) -(assert (> (+ (* x x x 4) (* x x 3) 0))) +(assert (> (+ (* x x x 4) (* x x 3)) 0)) (assert (= (* x x) (* y y))) (apply degree-shift) ``` From fcea32344eafb4cbb4ca66161c1151f8f7c532aa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Jan 2023 13:32:26 -0800 Subject: [PATCH 259/597] add missing tactic descriptions, add rewrite for tamagochi Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bool_rewriter.cpp | 4 ++- src/ast/rewriter/bv_rewriter.cpp | 36 +++++++++++++++++++ src/ast/rewriter/bv_rewriter.h | 10 ++++++ src/ast/rewriter/th_rewriter.cpp | 34 ++---------------- src/cmd_context/echo_tactic.h | 2 +- src/math/subpaving/tactic/subpaving_tactic.h | 2 ++ src/muz/fp/horn_tactic.h | 24 ++++++++++++- src/sat/sat_solver/inc_sat_solver.cpp | 2 +- src/sat/sat_solver/sat_smt_solver.cpp | 2 -- src/smt/tactic/smt_tactic_core.cpp | 2 +- src/solver/CMakeLists.txt | 2 +- ...allel_tactic.cpp => parallel_tactical.cpp} | 4 +-- ...{parallel_tactic.h => parallel_tactical.h} | 2 -- src/tactic/aig/aig_tactic.h | 26 +++++++++++++- src/tactic/core/eliminate_predicates_tactic.h | 36 +++++++++++++++++++ src/tactic/fd_solver/fd_solver.cpp | 2 +- src/tactic/portfolio/smt_strategic_solver.cpp | 2 +- src/tactic/ufbv/macro_finder_tactic.h | 2 +- src/tactic/ufbv/quasi_macros_tactic.h | 2 +- 19 files changed, 147 insertions(+), 49 deletions(-) rename src/solver/{parallel_tactic.cpp => parallel_tactical.cpp} (99%) rename src/solver/{parallel_tactic.h => parallel_tactical.h} (97%) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 5abfe01a62f..392b2e681c7 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -857,6 +857,7 @@ br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & re s = true; } + // (ite c (ite c t1 t2) t3) ==> (ite c t1 t3 if (m().is_ite(t) && to_app(t)->get_arg(0) == c) { // Remark: (ite c (ite (not c) t1 t2) t3) ==> (ite c t2 t3) does not happen if applying rewrites bottom up @@ -943,7 +944,6 @@ br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & re } #if 0 - expr* t1, *t2; // (ite c (not (= t1 t2)) t1) ==> (not (= t1 (and c t2))) if (m().is_not(t, t1) && m().is_eq(t1, t1, t2) && e == t1) { expr_ref a(m()); @@ -960,6 +960,8 @@ br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & re #endif + + if (m().is_ite(t) && m_ite_extra_rules && m_elim_ite) { // (ite c1 (ite c2 t1 t2) t1) ==> (ite (and c1 (not c2)) t2 t1) if (e == to_app(t)->get_arg(1)) { diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 179113241da..a308cffc438 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -2807,6 +2807,28 @@ br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & res return BR_FAILED; } +bool bv_rewriter::is_bit(expr* t, unsigned& val) { + rational v; + unsigned sz; + return is_bv(t) && is_numeral(t, v, sz) && sz == 1 && (val = v.get_unsigned(), true); +} + +bool bv_rewriter::is_eq_bit(expr * t, expr * & x, unsigned & val) { + expr* lhs, *rhs; + if (!m.is_eq(t, lhs, rhs)) + return false; + if (is_bit(lhs, val)) { + x = rhs; + return true; + } + if (is_bit(rhs, val)) { + x = lhs; + return true; + } + return false; +} + + br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) { TRACE("bv_ite", tout << "mk_ite_core:\n" << mk_ismt2_pp(c, m) << "?\n" << mk_ismt2_pp(t, m) << "\n:" << mk_ismt2_pp(e, m) << "\n";); @@ -2819,6 +2841,20 @@ br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resu return BR_REWRITE1; } + // if x = 0 then 0 else 1 + expr* t1; + unsigned bit1, bit2, bit3; + if (is_bv(t) && is_eq_bit(c, t1, bit1) && is_bit(t, bit2) && is_bit(e, bit3)) { + if (bit1 == bit2 && bit3 != bit2) { + result = t1; + return BR_DONE; + } + if (bit1 == bit3 && bit3 != bit2) { + result = m_util.mk_bv_not(t1); + return BR_REWRITE1; + } + } + if (m_ite2id && m.is_eq(c) && is_bv(t) && is_bv(e)) { // detect when ite is actually some simple function based on the pattern (lhs=rhs) ? t : e expr * lhs = to_app(c)->get_arg(0); diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index ca999c79300..25bd65b6130 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -193,6 +193,16 @@ class bv_rewriter : public poly_rewriter { bv_util & get_util() { return m_util; } + // Return true if t is of the form + // (= t #b0) + // (= t #b1) + // (= #b0 t) + // (= #b1 t) + bool is_eq_bit(expr* t, expr*& x, unsigned& val); + + // return true if t is #b0 or #b1 + bool is_bit(expr* t, unsigned& val); + #define MK_BV_BINARY(OP) \ expr_ref OP(expr* a, expr* b) { \ expr_ref result(m); \ diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 456a7afba13..c6ab22636d4 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -125,36 +125,6 @@ struct th_rewriter_cfg : public default_rewriter_cfg { return num_steps > m_max_steps; } - // Return true if t is of the form - // (= t #b0) - // (= t #b1) - // (= #b0 t) - // (= #b1 t) - bool is_eq_bit(expr * t, expr * & x, unsigned & val) { - if (!m().is_eq(t)) - return false; - expr * lhs = to_app(t)->get_arg(0); - if (!m_bv_rw.is_bv(lhs)) - return false; - if (m_bv_rw.get_bv_size(lhs) != 1) - return false; - expr * rhs = to_app(t)->get_arg(1); - rational v; - unsigned sz; - if (m_bv_rw.is_numeral(lhs, v, sz)) { - x = rhs; - val = v.get_unsigned(); - SASSERT(val == 0 || val == 1); - return true; - } - if (m_bv_rw.is_numeral(rhs, v, sz)) { - x = lhs; - val = v.get_unsigned(); - SASSERT(val == 0 || val == 1); - return true; - } - return false; - } // (iff (= x bit1) A) // ---> @@ -162,11 +132,11 @@ struct th_rewriter_cfg : public default_rewriter_cfg { br_status apply_tamagotchi(expr * lhs, expr * rhs, expr_ref & result) { expr * x; unsigned val; - if (is_eq_bit(lhs, x, val)) { + if (m_bv_rw.is_eq_bit(lhs, x, val)) { result = m().mk_eq(x, m().mk_ite(rhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1))); return BR_REWRITE2; } - if (is_eq_bit(rhs, x, val)) { + if (m_bv_rw.is_eq_bit(rhs, x, val)) { result = m().mk_eq(x, m().mk_ite(lhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1))); return BR_REWRITE2; } diff --git a/src/cmd_context/echo_tactic.h b/src/cmd_context/echo_tactic.h index 050b8910b9f..ef4d1737b5a 100644 --- a/src/cmd_context/echo_tactic.h +++ b/src/cmd_context/echo_tactic.h @@ -13,7 +13,7 @@ Module Name: Leonardo (leonardo) 2012-10-20 -Notes: +## Tactic echo --*/ #pragma once diff --git a/src/math/subpaving/tactic/subpaving_tactic.h b/src/math/subpaving/tactic/subpaving_tactic.h index 9ddddbe6d2c..2bcb426bf33 100644 --- a/src/math/subpaving/tactic/subpaving_tactic.h +++ b/src/math/subpaving/tactic/subpaving_tactic.h @@ -15,6 +15,8 @@ Module Name: Revision History: +## Tactic subpaving + --*/ #pragma once diff --git a/src/muz/fp/horn_tactic.h b/src/muz/fp/horn_tactic.h index e4b21ffb7a0..8ceff96210c 100644 --- a/src/muz/fp/horn_tactic.h +++ b/src/muz/fp/horn_tactic.h @@ -13,7 +13,29 @@ Module Name: Nikolaj Bjorner (nbjorner) 2012-11-16. -Revision History: +Tactic Documentation: + +## Tactic horn + +### Short Description + +Solve a set of Horn clauses using the SPACER engine. + +### Long Description + +The SPACER engine is specialized to solving Constrained Horn Clauses. +This tactic instructs + +## Tactic horn-simplify + +### Short Description + +Apply pre-processing simplification rules to a set of Horn clauses + +### Long Description +This tactic exposes pre-processing simplification rules for Constrained Horn Clauses. +They include a repertoire of simplification options that can be controlled by toggling +the `fp` parameters. --*/ #pragma once diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 0362d8d3eff..75351f05361 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -26,7 +26,7 @@ Module Name: #include "solver/solver.h" #include "solver/tactic2solver.h" #include "solver/parallel_params.hpp" -#include "solver/parallel_tactic.h" +#include "solver/parallel_tactical.h" #include "tactic/tactical.h" #include "tactic/aig/aig_tactic.h" #include "tactic/core/propagate_values_tactic.h" diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 923594501b5..fd970a44ac5 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -33,8 +33,6 @@ Module Name: #include "ast/ast_translation.h" #include "ast/ast_util.h" #include "solver/solver.h" -#include "solver/parallel_params.hpp" -#include "solver/parallel_tactic.h" #include "model/model_smt2_pp.h" #include "model/model_evaluator.h" #include "sat/sat_solver.h" diff --git a/src/smt/tactic/smt_tactic_core.cpp b/src/smt/tactic/smt_tactic_core.cpp index 5ef52a54cc4..2be2ace5841 100644 --- a/src/smt/tactic/smt_tactic_core.cpp +++ b/src/smt/tactic/smt_tactic_core.cpp @@ -30,7 +30,7 @@ Module Name: #include "solver/solver2tactic.h" #include "solver/solver.h" #include "solver/mus.h" -#include "solver/parallel_tactic.h" +#include "solver/parallel_tactical.h" #include "solver/parallel_params.hpp" typedef obj_map expr2expr_map; diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index e259adc3efd..3bea76dd00c 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -4,7 +4,7 @@ z3_add_component(solver check_logic.cpp combined_solver.cpp mus.cpp - parallel_tactic.cpp + parallel_tactical.cpp smt_logics.cpp solver.cpp solver_na2as.cpp diff --git a/src/solver/parallel_tactic.cpp b/src/solver/parallel_tactical.cpp similarity index 99% rename from src/solver/parallel_tactic.cpp rename to src/solver/parallel_tactical.cpp index 1d24ed1e605..6d68df8a89d 100644 --- a/src/solver/parallel_tactic.cpp +++ b/src/solver/parallel_tactical.cpp @@ -3,7 +3,7 @@ Copyright (c) 2017 Microsoft Corporation Module Name: - parallel_tactic.cpp + parallel_tactical.cpp Abstract: @@ -36,7 +36,7 @@ Module Name: #include "solver/solver2tactic.h" #include "tactic/tactic.h" #include "tactic/tactical.h" -#include "solver/parallel_tactic.h" +#include "solver/parallel_tactical.h" #include "solver/parallel_params.hpp" diff --git a/src/solver/parallel_tactic.h b/src/solver/parallel_tactical.h similarity index 97% rename from src/solver/parallel_tactic.h rename to src/solver/parallel_tactical.h index 18843077bc1..5d21ad18d48 100644 --- a/src/solver/parallel_tactic.h +++ b/src/solver/parallel_tactical.h @@ -12,8 +12,6 @@ Module Name: Author: Nikolaj Bjorner (nbjorner) 2017-10-9 - -Notes: --*/ #pragma once diff --git a/src/tactic/aig/aig_tactic.h b/src/tactic/aig/aig_tactic.h index 33c00d69293..ca2f82d8b6e 100644 --- a/src/tactic/aig/aig_tactic.h +++ b/src/tactic/aig/aig_tactic.h @@ -13,7 +13,31 @@ Module Name: Leonardo (leonardo) 2011-10-24 -Notes: +Tactic Documentation: + +## Tactic aig + +### Short Description + +Simplify Boolean structure using AIGs (And-inverter graphs). + +### Long Description + +And-inverter graphs (AIGs) uses just the Boolean connectives `and` and `not` to encode Boolean +formulas. The circuit representation using AIGs first converts formulas using other connectives to this normal form, +then performs local simplification steps to minimize the circuit representation. +Note that the simplification steps used by this tactic are heuristic, trading speed for power, +and do not represent a high-quality circuit minimization approach. + +### Example + +```z3 +(declare-const a Bool) +(declare-const b Bool) +(declare-const c Bool) +(assert (or (and a b) (and b a c))) +(apply aig) +``` --*/ #pragma once diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index d89f369dc00..4d90b1acfdb 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -13,6 +13,42 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-10-30 +Tactic Documentation: + +## Tactic elim-predicates + +### Short Description +Eliminates predicates and macros from a formula. + +### Long Description +The tactic subsumes the functionality of `macro-finder` and `quasi-macros`. +Besides finding macros, it eliminates predicates using Davis-Putnam +resolution. + +### Example + +the predicate `p` occurs once positively. All negative occurrences of `p` are resolved against this positive occurrence. +The result of resolution is a set of equalities between arguments to `p`. The function `f` is replaced by a partial solution. + +``` +(declare-fun f (Int Int Int) Int) +(declare-fun p (Int) Bool) +(declare-const a Int) +(declare-const b Int) + +(assert (forall ((x Int) (y Int)) (= (f x y (+ x y)) (* 2 x y)))) +(assert (p (f 8 a (+ a 8)))) +(assert (not (p (f 0 a (+ a 8))))) +(assert (not (p (f 2 a (+ a 8))))) +(assert (not (p (f 1 a (+ a b))))) +(apply elim-predicates) +``` + +### Notes + +* support unsat cores +* does not support proofs + --*/ #pragma once diff --git a/src/tactic/fd_solver/fd_solver.cpp b/src/tactic/fd_solver/fd_solver.cpp index 8e32b74d0ea..4f1f6b6250e 100644 --- a/src/tactic/fd_solver/fd_solver.cpp +++ b/src/tactic/fd_solver/fd_solver.cpp @@ -24,7 +24,7 @@ Module Name: #include "tactic/fd_solver/pb2bv_solver.h" #include "tactic/fd_solver/bounded_int2bv_solver.h" #include "solver/solver2tactic.h" -#include "solver/parallel_tactic.h" +#include "solver/parallel_tactical.h" #include "solver/parallel_params.hpp" solver * mk_fd_solver(ast_manager & m, params_ref const & p, bool incremental_mode) { diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 8acfbe40d63..138c6e6465a 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -43,7 +43,7 @@ Module Name: #include "sat/sat_solver/sat_smt_solver.h" #include "ast/rewriter/bv_rewriter.h" #include "solver/solver2tactic.h" -#include "solver/parallel_tactic.h" +#include "solver/parallel_tactical.h" #include "solver/parallel_params.hpp" #include "params/tactic_params.hpp" #include "parsers/smt2/smt2parser.h" diff --git a/src/tactic/ufbv/macro_finder_tactic.h b/src/tactic/ufbv/macro_finder_tactic.h index 487cf1f0dc7..f1cf7080ea6 100644 --- a/src/tactic/ufbv/macro_finder_tactic.h +++ b/src/tactic/ufbv/macro_finder_tactic.h @@ -13,7 +13,7 @@ Module Name: Christoph (cwinter) 2012-10-26 -Tactic Description +Tactic Documentation ## Tactic macro-finder diff --git a/src/tactic/ufbv/quasi_macros_tactic.h b/src/tactic/ufbv/quasi_macros_tactic.h index 872296dd9a4..faa939954fb 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.h +++ b/src/tactic/ufbv/quasi_macros_tactic.h @@ -13,7 +13,7 @@ Module Name: Christoph (cwinter) 2012-10-26 -Tactic Description +Tactic Documentation ## Tactic quasi-macro-finder From 61b90e64b25629d14811921d0198079d96bf6b63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Jan 2023 14:17:49 -0800 Subject: [PATCH 260/597] disable new simplifcation for multiplier until really understood Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bv_rewriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index a308cffc438..894286a0262 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -2282,7 +2282,7 @@ br_status bv_rewriter::mk_mul_hoist(unsigned num_args, expr * const * args, expr expr* z = nullptr, *u = nullptr; for (unsigned i = 0; i < num_args; ++i) { // ~x = -1 - x - if (m_util.is_bv_not(args[i], z)) { + if (false && m_util.is_bv_not(args[i], z)) { unsigned sz = m_util.get_bv_size(z); ptr_vector new_args(num_args, args); rational p = rational(2).expt(sz) - 1; From 1ddef117a21de30909fd1166095caaa85c39da82 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Jan 2023 16:11:31 -0800 Subject: [PATCH 261/597] several fixes to proof logging in legacy solver Signed-off-by: Nikolaj Bjorner --- src/smt/smt_clause_proof.cpp | 29 +++++++++++++++++++++++------ src/smt/smt_clause_proof.h | 3 +++ src/smt/smt_model_checker.cpp | 4 +++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index 096a0994ffe..d533ae1612f 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -16,15 +16,27 @@ Revision History: #include "smt/smt_context.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" +#include namespace smt { clause_proof::clause_proof(context& ctx): ctx(ctx), m(ctx.get_manager()), m_lits(m), m_pp(m) { + auto proof_log = ctx.get_fparams().m_proof_log; - m_enabled = ctx.get_fparams().m_clause_proof || proof_log.is_non_empty_string(); - if (proof_log.is_non_empty_string()) { - m_pp_out = alloc(std::ofstream, proof_log.str()); + m_has_log = proof_log.is_non_empty_string(); + m_enabled = ctx.get_fparams().m_clause_proof || m_has_log; + } + + void clause_proof::init_pp_out() { + if (m_has_log && !m_pp_out) { + static unsigned id = 0; + auto proof_log = ctx.get_fparams().m_proof_log; + std::string log_name = proof_log.str(); + if (id > 0) + log_name = std::to_string(id) + log_name; + ++id; + m_pp_out = alloc(std::ofstream, log_name); if (!*m_pp_out) throw default_exception(std::string("Could not open file ") + proof_log.str()); } @@ -170,14 +182,18 @@ namespace smt { m_trail.push_back(info(st, v, p)); if (m_on_clause_eh) m_on_clause_eh(m_on_clause_ctx, p, v.size(), v.data()); - if (m_pp_out) { + if (m_has_log) { + init_pp_out(); auto& out = *m_pp_out; for (auto* e : v) declare(out, e); switch (st) { case clause_proof::status::assumption: - display_literals(out << "(assume", v) << ")\n"; - break; + if (!p || p->get_decl()->get_name() == "assumption") { + display_literals(out << "(assume", v) << ")\n"; + break; + } + Z3_fallthrough; case clause_proof::status::lemma: case clause_proof::status::th_lemma: case clause_proof::status::th_assumption: @@ -191,6 +207,7 @@ namespace smt { default: UNREACHABLE(); } + out.flush(); } } diff --git a/src/smt/smt_clause_proof.h b/src/smt/smt_clause_proof.h index 42f283f1dd7..b247a0454a3 100644 --- a/src/smt/smt_clause_proof.h +++ b/src/smt/smt_clause_proof.h @@ -58,10 +58,13 @@ namespace smt { expr_ref_vector m_lits; vector m_trail; bool m_enabled = false; + bool m_has_log = false; user_propagator::on_clause_eh_t m_on_clause_eh; void* m_on_clause_ctx = nullptr; ast_pp_util m_pp; scoped_ptr m_pp_out; + + void init_pp_out(); void update(status st, expr_ref_vector& v, proof* p); void update(clause& c, status st, proof* p); diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 72094f78aac..61173907b11 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -405,13 +405,15 @@ namespace smt { m_fparams->m_relevancy_lvl = 0; // no relevancy since the model checking problems are quantifier free m_fparams->m_case_split_strategy = CS_ACTIVITY; // avoid warning messages about smt.case_split >= 3. m_fparams->m_axioms2files = false; - m_fparams->m_lemmas2console = false; + m_fparams->m_lemmas2console = false; + m_fparams->m_proof_log = symbol::null; } if (!m_aux_context) { symbol logic; params_ref p; p.set_bool("solver.axioms2files", false); p.set_bool("solver.lemmas2console", false); + p.set_sym("solver.proof.log", symbol::null); m_aux_context = m_context->mk_fresh(&logic, m_fparams.get(), p); } } From 5899fe3cea8042d21203089f01d700fd22a1b7fb Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 9 Jan 2023 03:09:01 +0000 Subject: [PATCH 262/597] Add rewrite for array selects of chain of stores of a same value (#6526) * Add rewrite for array selects of chain of stores of a same value Example: ```smt (declare-fun mem () (Array (_ BitVec 4) (_ BitVec 4))) (declare-const x (_ BitVec 4)) (declare-const y (_ BitVec 4)) ; simplifies to #x1 (simplify (select (store (store (store mem #x1 #x1) y #x1) x #x1) #x1)) ``` * Update array_rewriter.cpp * Update array_rewriter.cpp --- src/ast/rewriter/array_rewriter.cpp | 134 ++++++++++++++++++---------- 1 file changed, 86 insertions(+), 48 deletions(-) diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 08173d430be..85cdeb50a39 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -198,13 +198,96 @@ bool array_rewriter::squash_store(unsigned n, expr* const* args, expr_ref& resul br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); + expr *arg0 = args[0]; + expr_ref tmp(m()); + bool first = true; + +#define RET(x, status) \ + tmp = x; \ + if (first || tmp == result) { \ + result = std::move(tmp); \ + return status; \ + } \ + goto exit + + while (true) { + if (m_util.is_store(arg0)) { + SASSERT(to_app(arg0)->get_num_args() == num_args+1); + switch (compare_args(num_args - 1, args+1, to_app(arg0)->get_args()+1)) { + case l_true: + // select(store(a, I, v), I) --> v + RET(to_app(arg0)->get_arg(num_args), BR_DONE); + + case l_false: + // select(store(a, I, v), J) --> select(a, J) if I != J + arg0 = to_app(arg0)->get_arg(0); + continue; + + case l_undef: + // check if loading from subsequent arrays yields the same value + if (first) { + result = to_app(arg0)->get_arg(num_args); + first = false; + } else if (result != to_app(arg0)->get_arg(num_args)) { + goto exit; + } + arg0 = to_app(arg0)->get_arg(0); + continue; + } + } + + if (m_util.is_const(arg0)) { + // select(const(v), I) --> v + RET(to_app(arg0)->get_arg(0), BR_DONE); + } + + if (is_lambda(arg0)) { + // anywhere lambda reduction as opposed to whnf + // select(lambda(X) M, N) -> M[N/X] + quantifier* q = to_quantifier(arg0); + SASSERT(q->get_num_decls() == num_args - 1); + var_subst subst(m()); + expr_ref_vector _args(m()); + var_shifter sh(m()); + for (unsigned i = 1; i < num_args; ++i) { + sh(args[i], num_args-1, result); + _args.push_back(result); + } + expr_ref tmp2 = subst(q->get_expr(), _args.size(), _args.data()); + inv_var_shifter invsh(m()); + invsh(tmp2, _args.size(), tmp2); + RET(std::move(tmp2), BR_REWRITE_FULL); + } + + if (m_util.is_map(arg0)) { + app* a = to_app(arg0); + func_decl* f0 = m_util.get_map_func_decl(a); + expr_ref_vector args0(m()); + for (expr* arg : *a) { + ptr_vector args1; + args1.push_back(arg); + args1.append(num_args-1, args + 1); + args0.push_back(m_util.mk_select(args1.size(), args1.data())); + } + RET(m().mk_app(f0, args0.size(), args0.data()), BR_REWRITE2); + } + + if (m_util.is_as_array(arg0)) { + // select(as-array[f], I) --> f(I) + func_decl * f = m_util.get_as_array_func_decl(to_app(arg0)); + RET(m().mk_app(f, num_args - 1, args + 1), BR_REWRITE1); + } + break; + } + +exit: + result.reset(); + if (m_util.is_store(args[0])) { SASSERT(to_app(args[0])->get_num_args() == num_args+1); switch (compare_args(num_args - 1, args+1, to_app(args[0])->get_args()+1)) { case l_true: - // select(store(a, I, v), I) --> v - result = to_app(args[0])->get_arg(num_args); - return BR_DONE; + UNREACHABLE(); case l_false: { expr* arg0 = to_app(args[0])->get_arg(0); while (m_util.is_store(arg0) && compare_args(num_args-1, args + 1, to_app(arg0)->get_args() + 1) == l_false) { @@ -261,51 +344,6 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, } } - if (m_util.is_const(args[0])) { - // select(const(v), I) --> v - result = to_app(args[0])->get_arg(0); - return BR_DONE; - } - - if (is_lambda(args[0])) { - // anywhere lambda reduction as opposed to whnf - // select(lambda(X) M, N) -> M[N/X] - quantifier* q = to_quantifier(args[0]); - SASSERT(q->get_num_decls() == num_args - 1); - var_subst subst(m()); - expr_ref_vector _args(m()); - var_shifter sh(m()); - for (unsigned i = 1; i < num_args; ++i) { - sh(args[i], num_args-1, result); - _args.push_back(result); - } - result = subst(q->get_expr(), _args.size(), _args.data()); - inv_var_shifter invsh(m()); - invsh(result, _args.size(), result); - return BR_REWRITE_FULL; - } - - if (m_util.is_map(args[0])) { - app* a = to_app(args[0]); - func_decl* f0 = m_util.get_map_func_decl(a); - expr_ref_vector args0(m()); - for (expr* arg : *a) { - ptr_vector args1; - args1.push_back(arg); - args1.append(num_args-1, args + 1); - args0.push_back(m_util.mk_select(args1.size(), args1.data())); - } - result = m().mk_app(f0, args0.size(), args0.data()); - return BR_REWRITE2; - } - - if (m_util.is_as_array(args[0])) { - // select(as-array[f], I) --> f(I) - func_decl * f = m_util.get_as_array_func_decl(to_app(args[0])); - result = m().mk_app(f, num_args - 1, args + 1); - return BR_REWRITE1; - } - expr* c, *th, *el; if (m().is_ite(args[0], c, th, el) && (m_expand_select_ite || (th->get_ref_count() == 1 || el->get_ref_count() == 1))) { ptr_vector args1, args2; From 49ee570b09047cbea94f7d26e3df10c86c9ce596 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Jan 2023 19:16:46 -0800 Subject: [PATCH 263/597] split into separate function Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/array_rewriter.cpp | 17 ++++++++++++----- src/ast/rewriter/array_rewriter.h | 9 +++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 85cdeb50a39..58e807fd7a7 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -196,10 +196,9 @@ bool array_rewriter::squash_store(unsigned n, expr* const* args, expr_ref& resul } -br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - expr *arg0 = args[0]; +br_status array_rewriter::mk_select_same_store(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref tmp(m()); + expr *arg0 = args[0]; bool first = true; #define RET(x, status) \ @@ -228,9 +227,9 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, if (first) { result = to_app(arg0)->get_arg(num_args); first = false; - } else if (result != to_app(arg0)->get_arg(num_args)) { - goto exit; } + else if (result != to_app(arg0)->get_arg(num_args)) + goto exit; arg0 = to_app(arg0)->get_arg(0); continue; } @@ -281,6 +280,14 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, } exit: + return BR_FAILED; +} + +br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + br_status st = mk_select_same_store(num_args, args, result); + if (st != BR_FAILED) + return st; result.reset(); if (m_util.is_store(args[0])) { diff --git a/src/ast/rewriter/array_rewriter.h b/src/ast/rewriter/array_rewriter.h index 4e52b237ef5..689aea1f90b 100644 --- a/src/ast/rewriter/array_rewriter.h +++ b/src/ast/rewriter/array_rewriter.h @@ -46,6 +46,11 @@ class array_rewriter { expr_ref expand_store(expr* s); bool squash_store(unsigned n, expr* const* args, expr_ref& result); + + br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result); + br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result); + br_status mk_select_same_store(unsigned num_args, expr * const * args, expr_ref & result); + br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); public: array_rewriter(ast_manager & m, params_ref const & p = params_ref()): @@ -63,10 +68,6 @@ class array_rewriter { br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); - br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result); - br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result); - br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); - void mk_store(unsigned num_args, expr * const * args, expr_ref & result); void mk_select(unsigned num_args, expr * const * args, expr_ref & result); void mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); From a4f2a1bb2e4ee9bcc2484f0e1088c8597ced6ad8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 09:16:55 +0000 Subject: [PATCH 264/597] Bump json5 from 2.2.1 to 2.2.3 in /src/api/js (#6527) Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/api/js/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json index c0ece3ae48a..f6969a933c8 100644 --- a/src/api/js/package-lock.json +++ b/src/api/js/package-lock.json @@ -3925,9 +3925,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonc-parser": { From 30e0f78c16c23b4baf945f2872aff541cc2fc503 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Jan 2023 10:00:33 -0800 Subject: [PATCH 265/597] remove exit Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/array_rewriter.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 58e807fd7a7..e580eb82d0d 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -207,7 +207,7 @@ br_status array_rewriter::mk_select_same_store(unsigned num_args, expr * const * result = std::move(tmp); \ return status; \ } \ - goto exit + return BR_FAILED; while (true) { if (m_util.is_store(arg0)) { @@ -228,8 +228,8 @@ br_status array_rewriter::mk_select_same_store(unsigned num_args, expr * const * result = to_app(arg0)->get_arg(num_args); first = false; } - else if (result != to_app(arg0)->get_arg(num_args)) - goto exit; + else if (result != to_app(arg0)->get_arg(num_args)) + return BR_FAILED; arg0 = to_app(arg0)->get_arg(0); continue; } @@ -278,8 +278,6 @@ br_status array_rewriter::mk_select_same_store(unsigned num_args, expr * const * } break; } - -exit: return BR_FAILED; } From 64ec8acd306ea6adb8e883860cce611665522f18 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Jan 2023 15:18:19 -0800 Subject: [PATCH 266/597] fix model reconstruction ordering for elim_unconstrained --- src/ast/simplifiers/elim_unconstrained.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 41a905dea18..982a3c7dc7e 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -357,6 +357,7 @@ void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector< trail.hide(entry.m_f); break; case generic_model_converter::instruction::ADD: + // trail.push(entry.m_f, entry.m_def, nullptr, old_fmls); break; } } @@ -364,7 +365,8 @@ void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector< scoped_ptr sub = alloc(expr_substitution, m, true, false); rp->set_substitution(sub.get()); expr_ref new_def(m); - for (auto const& entry : mc.entries()) { + for (unsigned i = mc.entries().size(); i-- > 0; ) { + auto const& entry = mc.entries()[i]; switch (entry.m_instruction) { case generic_model_converter::instruction::HIDE: break; From a4d4e2e48300369a2670d7feb26780defc85faa9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Jan 2023 15:18:33 -0800 Subject: [PATCH 267/597] track assertions --- src/sat/smt/q_mbi.cpp | 17 ++++++++++++----- src/sat/smt/q_mbi.h | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 6f2db37e1a2..6702cbf994a 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -107,7 +107,7 @@ namespace q { if (ctx.values2root().find(e, n) && n->class_generation() <= generation_min) eqs.push_back(m.mk_eq(sk, e)); } - m_solver->assert_expr(mk_or(eqs)); + assert_expr(mk_or(eqs)); } expr_ref mbqi::replace_model_value(expr* e) { @@ -168,7 +168,7 @@ namespace q { while (true) { ::solver::scoped_push _sp(*m_solver); add_universe_restriction(*qb); - m_solver->assert_expr(qb->mbody); + assert_expr(qb->mbody); ++m_stats.m_num_checks; lbool r = m_solver->check_sat(0, nullptr); if (r == l_undef) @@ -219,7 +219,7 @@ namespace q { if (!proj) break; add_instantiation(q, proj); - m_solver->assert_expr(m.mk_not(mk_and(eqs))); + assert_expr(m.mk_not(mk_and(eqs))); } return i > 0; } @@ -392,7 +392,7 @@ namespace q { if (!m_model->eval_expr(bounds, mbounds, true)) return; mbounds = subst(mbounds, qb.vars); - m_solver->assert_expr(mbounds); + assert_expr(mbounds); qb.domain_eqs.push_back(vbounds); } @@ -425,11 +425,18 @@ namespace q { continue; expr_ref meq = mk_or(meqs); expr_ref veq = mk_or(veqs); - m_solver->assert_expr(meq); + assert_expr(meq); qb.domain_eqs.push_back(veq); } } + void mbqi::assert_expr(expr* e) { + expr_ref _e(e, m); + TRACE("q", tout << _e << "\n"); + m_solver->assert_expr(e); + } + + /* * Add bounds to sub-terms under uninterpreted functions for projection. */ diff --git a/src/sat/smt/q_mbi.h b/src/sat/smt/q_mbi.h index 2cc5655bf3b..96e3ba56f97 100644 --- a/src/sat/smt/q_mbi.h +++ b/src/sat/smt/q_mbi.h @@ -93,6 +93,7 @@ namespace q { void extract_free_vars(quantifier* q, q_body& qb); void init_model(); void init_solver(); + void assert_expr(expr* e); mbp::project_plugin* get_plugin(app* var); void add_plugin(mbp::project_plugin* p); void add_instantiation(quantifier* q, expr_ref& proj); From c3e31149a5614e813909ef1041e007dad4f6abfd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Jan 2023 13:43:17 -0800 Subject: [PATCH 268/597] fix #6530 Signed-off-by: Nikolaj Bjorner --- src/ast/format.cpp | 10 +++------- src/ast/proofs/proof_checker.cpp | 8 ++------ src/sat/smt/q_mbi.cpp | 7 ++++++- src/smt/theory_arith_pp.h | 7 +------ src/util/debug.cpp | 6 +++--- 5 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/ast/format.cpp b/src/ast/format.cpp index a14d4b758ff..6583e9893d7 100644 --- a/src/ast/format.cpp +++ b/src/ast/format.cpp @@ -147,17 +147,13 @@ namespace format_ns { parameter p(s); return fm(m).mk_app(fid(m), OP_STRING, 1, &p, 0, nullptr); } - + format * mk_int(ast_manager & m, int i) { - char buffer[128]; - SPRINTF_D(buffer, i); - return mk_string(m, buffer); + return mk_string(m, std::to_string(i)); } format * mk_unsigned(ast_manager & m, unsigned u) { - char buffer[128]; - SPRINTF_U(buffer, u); - return mk_string(m, buffer); + return mk_string(m, std::to_string(u)); } format * mk_indent(ast_manager & m, unsigned i, format * f) { diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index 86ef1793c34..dd7cdc851dc 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -1245,12 +1245,8 @@ void proof_checker::dump_proof(proof const* pr) { } void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) { - char buffer[128]; -#ifdef _WINDOWS - sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id); -#else - sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id); -#endif + std::string buffer; + buffer = "proof_lemma_" + std::to_string(m_proof_lemma_id) + ".smt2"; std::ofstream out(buffer); ast_smt_pp pp(m); pp.set_benchmark_name("lemma"); diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 6702cbf994a..21830c16200 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -170,7 +170,9 @@ namespace q { add_universe_restriction(*qb); assert_expr(qb->mbody); ++m_stats.m_num_checks; + IF_VERBOSE(2, verbose_stream() << "(mbqi.check)\n"); lbool r = m_solver->check_sat(0, nullptr); + IF_VERBOSE(2, verbose_stream() << "(mbqi.check " << r << ")\n"); if (r == l_undef) return r; if (r == l_true) { @@ -212,7 +214,10 @@ namespace q { add_domain_eqs(mdl0, qb); for (; i < m_max_cex; ++i) { ++m_stats.m_num_checks; - if (l_true != m_solver->check_sat(0, nullptr)) + IF_VERBOSE(2, verbose_stream() << "(mbqi.check)\n"); + lbool r = m_solver->check_sat(0, nullptr); + IF_VERBOSE(2, verbose_stream() << "(mbqi.check " << r << ")\n"); + if (l_true != r) break; m_solver->get_model(mdl1); auto proj = solver_project(*mdl1, qb, eqs, true); diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index 049528f7901..d10bfca55a2 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -516,13 +516,8 @@ namespace smt { template void theory_arith::display_bounds_in_smtlib() const { - char buffer[128]; static int id = 0; -#ifdef _WINDOWS - sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "arith_%d.smt", id); -#else - sprintf(buffer, "arith_%d.smt", id); -#endif + std::string buffer = "arith_" + std::to_string(id) + ".smt2"; std::ofstream out(buffer); display_bounds_in_smtlib(out); out.close(); diff --git a/src/util/debug.cpp b/src/util/debug.cpp index 4380a0548ef..f97a2b57bb2 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -77,7 +77,7 @@ bool is_debug_enabled(const char * tag) { #if !defined(_WINDOWS) && !defined(NO_Z3_DEBUGGER) void invoke_gdb() { - char buffer[1024]; + std::string buffer; int * x = nullptr; for (;;) { std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n"; @@ -101,9 +101,9 @@ void invoke_gdb() { throw default_exception("assertion violation"); case 'G': case 'g': - sprintf(buffer, "gdb -nw /proc/%d/exe %d", getpid(), getpid()); + buffer = "gdb -nw /proc/" + std::to_string(getpid()) + "/exe " + std::to_string(getpid()); std::cerr << "invoking GDB...\n"; - if (system(buffer) == 0) { + if (system(buffer.c_str()) == 0) { std::cerr << "continuing the execution...\n"; } else { From 2bd933d87f9fec351fdff9c2a85698d42d32529d Mon Sep 17 00:00:00 2001 From: Brecht Sanders Date: Tue, 10 Jan 2023 22:44:11 +0100 Subject: [PATCH 269/597] Fix hwf.cpp for MinGW-w64 32-bit clang (#6529) Fix src/util/hwf.cpp for building with MinGW-w64 clang targetting Windows 32-bit. Without this fix there is an arror about `__control87_2` not being defined. --- src/util/hwf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index b1f0c3cbe86..8c20a4cdac8 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -48,7 +48,7 @@ Revision History: // clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects // the x87 FPU, even when /arch:SSE2 is on. // Luckily, these are kind of standardized, at least for Windows/Linux/macOS. -#if defined(__clang__) || defined(_M_ARM) && defined(_M_ARM64) +#if (defined(__clang__) && !defined(__MINGW32__)) || defined(_M_ARM) && defined(_M_ARM64) #undef USE_INTRINSICS #endif From b700dbffce68682d53530208918a8f1bb0a450b6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Jan 2023 14:42:19 -0800 Subject: [PATCH 270/597] fix #6528 --- src/solver/parallel_tactical.cpp | 44 +++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/solver/parallel_tactical.cpp b/src/solver/parallel_tactical.cpp index 6d68df8a89d..f748379b1c9 100644 --- a/src/solver/parallel_tactical.cpp +++ b/src/solver/parallel_tactical.cpp @@ -377,9 +377,10 @@ class parallel_tactic : public tactic { solver_ref m_solver; ast_manager& m_manager; + scoped_ptr m_serialize_manager; params_ref m_params; sref_vector m_models; - expr_ref_vector m_core; + scoped_ptr m_core; unsigned m_num_threads; statistics m_stats; task_queue m_queue; @@ -409,7 +410,7 @@ class parallel_tactic : public tactic { m_conquer_delay = pp.conquer_delay(); m_exn_code = 0; m_params.set_bool("override_incremental", true); - m_core.reset(); + m_core = nullptr; } void log_branches(lbool status) { @@ -436,10 +437,15 @@ class parallel_tactic : public tactic { void collect_core(expr_ref_vector const& core) { std::lock_guard lock(m_mutex); - ast_translation tr(core.get_manager(), m_manager); + if (!m_serialize_manager) + m_serialize_manager = alloc(ast_manager, core.get_manager(), true); + m_core = nullptr; + m_core = alloc(expr_ref_vector, *m_serialize_manager); + ast_translation tr(core.get_manager(), *m_serialize_manager); expr_ref_vector core1(tr(core)); for (expr* c : core1) { - if (!m_core.contains(c)) m_core.push_back(c); + if (!m_core->contains(c)) + m_core->push_back(c); } } @@ -463,11 +469,12 @@ class parallel_tactic : public tactic { s.get_solver().get_model(mdl); } if (mdl) { + // serialize access to m_serialize_manager std::lock_guard lock(m_mutex); - if (&s.m() != &m_manager) { - ast_translation tr(s.m(), m_manager); - mdl = mdl->translate(tr); - } + if (!m_serialize_manager) + m_serialize_manager = alloc(ast_manager, s.m(), true); + ast_translation tr(s.m(), *m_serialize_manager); + mdl = mdl->translate(tr); m_models.push_back(mdl.get()); } else if (m_models.empty()) { @@ -738,9 +745,14 @@ class parallel_tactic : public tactic { if (m_exn_code == -1) throw default_exception(std::move(m_exn_msg)); if (m_exn_code != 0) - throw z3_error(m_exn_code); + throw z3_error(m_exn_code); + + // retrieve model. The ast manager of the model is m_serialize_manager. + // the asts have to be translated into m_manager. if (!m_models.empty()) { - mdl = m_models.back(); + mdl = m_models.back(); + ast_translation tr(mdl->get_manager(), m_manager); + mdl = mdl->translate(tr); return l_true; } if (m_has_undef) @@ -770,8 +782,7 @@ class parallel_tactic : public tactic { parallel_tactic(solver* s, params_ref const& p) : m_solver(s), m_manager(s->get_manager()), - m_params(p), - m_core(m_manager) { + m_params(p) { init(); } @@ -806,10 +817,13 @@ class parallel_tactic : public tactic { g->add(concat(fmc.get(), model2model_converter(mdl.get()))); } break; - case l_false: + case l_false: SASSERT(!g->proofs_enabled()); - for (expr * c : m_core) { - lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); + if (m_core) { + ast_translation tr(m_core->get_manager(), m); + expr_ref_vector core(tr(*m_core)); + for (expr * c : core) + lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); } g->assert_expr(m.mk_false(), pr, lcore); break; From d415f073866b4dc729587d3ce8fe3e2166f7ac02 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Jan 2023 17:22:21 -0800 Subject: [PATCH 271/597] memory leak on proof justifications Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bv_rewriter.cpp | 7 ++++-- src/smt/smt_clause_proof.cpp | 37 ++++++++++++++++++++------------ src/smt/smt_clause_proof.h | 3 ++- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 894286a0262..1c374dcb84b 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -2286,8 +2286,11 @@ br_status bv_rewriter::mk_mul_hoist(unsigned num_args, expr * const * args, expr unsigned sz = m_util.get_bv_size(z); ptr_vector new_args(num_args, args); rational p = rational(2).expt(sz) - 1; - new_args[i] = m_util.mk_bv_sub(mk_numeral(p, sz), z); - result = m_util.mk_bv_mul(num_args, new_args.data()); + new_args[i] = mk_numeral(p, sz); + expr_ref a(m_util.mk_bv_mul(num_args, new_args.data()), m); + new_args[i] = z; + expr_ref b(m_util.mk_bv_mul(num_args, new_args.data()), m); + result = m_util.mk_bv_sub(a, b); return BR_REWRITE3; } // shl(z, u) * x = shl(x * z, u) diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index d533ae1612f..521510b596c 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -21,7 +21,8 @@ Revision History: namespace smt { clause_proof::clause_proof(context& ctx): - ctx(ctx), m(ctx.get_manager()), m_lits(m), m_pp(m) { + ctx(ctx), m(ctx.get_manager()), m_lits(m), m_pp(m), + m_assumption(m), m_rup(m), m_del(m), m_smt(m) { auto proof_log = ctx.get_fparams().m_proof_log; m_has_log = proof_log.is_non_empty_string(); @@ -58,27 +59,35 @@ namespace smt { } } - proof* clause_proof::justification2proof(status st, justification* j) { + proof_ref clause_proof::justification2proof(status st, justification* j) { proof* r = nullptr; if (j) r = j->mk_proof(ctx.get_cr()); if (r) - return r; + return proof_ref(r, m); if (!is_enabled()) - return nullptr; + return proof_ref(m); switch (st) { case status::assumption: - return m.mk_const("assumption", m.mk_proof_sort()); + if (!m_assumption) + m_assumption = m.mk_const("assumption", m.mk_proof_sort()); + return m_assumption; case status::lemma: - return m.mk_const("rup", m.mk_proof_sort()); + if (!m_rup) + m_rup = m.mk_const("rup", m.mk_proof_sort()); + return m_rup; case status::th_lemma: case status::th_assumption: - return m.mk_const("smt", m.mk_proof_sort()); + if (!m_smt) + m_smt = m.mk_const("smt", m.mk_proof_sort()); + return m_smt; case status::deleted: - return m.mk_const("del", m.mk_proof_sort()); + if (!m_del) + m_del = m.mk_const("del", m.mk_proof_sort()); + return m_del; } UNREACHABLE(); - return nullptr; + return proof_ref(m); } void clause_proof::add(clause& c) { @@ -86,7 +95,7 @@ namespace smt { return; justification* j = c.get_justification(); auto st = kind2st(c.get_kind()); - proof_ref pr(justification2proof(st, j), m); + auto pr = justification2proof(st, j); CTRACE("mk_clause", pr.get(), tout << mk_bounded_pp(pr, m, 4) << "\n";); update(c, st, pr); } @@ -95,7 +104,7 @@ namespace smt { if (!is_enabled()) return; auto st = kind2st(k); - proof_ref pr(justification2proof(st, j), m); + auto pr = justification2proof(st, j); CTRACE("mk_clause", pr.get(), tout << mk_bounded_pp(pr, m, 4) << "\n";); m_lits.reset(); for (unsigned i = 0; i < n; ++i) @@ -110,7 +119,7 @@ namespace smt { m_lits.reset(); for (unsigned i = 0; i < new_size; ++i) m_lits.push_back(ctx.literal2expr(c[i])); - proof* p = justification2proof(status::lemma, nullptr); + auto p = justification2proof(status::lemma, nullptr); update(status::lemma, m_lits, p); for (unsigned i = new_size; i < c.get_num_literals(); ++i) m_lits.push_back(ctx.literal2expr(c[i])); @@ -124,7 +133,7 @@ namespace smt { m_lits.reset(); m_lits.push_back(ctx.literal2expr(lit)); auto st = kind2st(k); - proof* pr = justification2proof(st, j); + auto pr = justification2proof(st, j); update(st, m_lits, pr); } @@ -135,7 +144,7 @@ namespace smt { m_lits.push_back(ctx.literal2expr(lit1)); m_lits.push_back(ctx.literal2expr(lit2)); auto st = kind2st(k); - proof* pr = justification2proof(st, j); + auto pr = justification2proof(st, j); update(st, m_lits, pr); } diff --git a/src/smt/smt_clause_proof.h b/src/smt/smt_clause_proof.h index b247a0454a3..1c593113669 100644 --- a/src/smt/smt_clause_proof.h +++ b/src/smt/smt_clause_proof.h @@ -63,13 +63,14 @@ namespace smt { void* m_on_clause_ctx = nullptr; ast_pp_util m_pp; scoped_ptr m_pp_out; + proof_ref m_assumption, m_rup, m_del, m_smt; void init_pp_out(); void update(status st, expr_ref_vector& v, proof* p); void update(clause& c, status st, proof* p); status kind2st(clause_kind k); - proof* justification2proof(status st, justification* j); + proof_ref justification2proof(status st, justification* j); void log(status st, proof* p); void declare(std::ostream& out, expr* e); std::ostream& display_literals(std::ostream& out, expr_ref_vector const& v); From 1c7ff72ae21c3da0f39c4d3c96e90764d6aeadce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Jan 2023 18:58:20 -0800 Subject: [PATCH 272/597] add tactic doc Signed-off-by: Nikolaj Bjorner --- src/tactic/core/split_clause_tactic.h | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/tactic/core/split_clause_tactic.h b/src/tactic/core/split_clause_tactic.h index 7573f075ed5..ef9c36a387c 100644 --- a/src/tactic/core/split_clause_tactic.h +++ b/src/tactic/core/split_clause_tactic.h @@ -5,16 +5,28 @@ Module Name: split_clause_tactic.h -Abstract: - - Tactic that creates a subgoal for each literal in a clause (l_1 or ... or l_n). - The tactic fails if the main goal does not contain any clause. - Author: Leonardo (leonardo) 2011-11-21 -Notes: +Tactic Documentation: + +## Tactic split-clause + +### Short Description + +Tactic that creates a subgoal for each literal in a clause `(l_1 or ... or l_n)`. +The tactic fails if the main goal does not contain any clause. + +### Example + +```z3 +(declare-const p Bool) +(declare-const q Bool) +(assert (or p q)) +(apply split-clause) +``` + --*/ #pragma once From 8970a54eaa5241d76a5b3699d0c644110b42c7f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Jan 2023 22:06:19 -0800 Subject: [PATCH 273/597] expose parameters to control behavior for #5660 --- src/sat/sat_solver/sat_smt_preprocess.cpp | 6 +-- src/sat/smt/euf_solver.cpp | 1 + src/sat/smt/q_ematch.cpp | 9 ++-- src/smt/params/preprocessor_params.cpp | 5 +++ src/smt/params/preprocessor_params.h | 52 +++++++++-------------- src/smt/params/smt_params_helper.pyg | 9 ++-- 6 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index 31888251c89..c1a4acc9e95 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -46,9 +46,9 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep smt_params smtp(p); if (sp.euf() || sp.smt()) { s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); - s.add_simplifier(alloc(propagate_values, m, p, st)); - s.add_simplifier(alloc(euf::solve_eqs, m, st)); - s.add_simplifier(alloc(elim_unconstrained, m, st)); + if (smtp.m_propagate_values) s.add_simplifier(alloc(propagate_values, m, p, st)); + if (smtp.m_solve_eqs) s.add_simplifier(alloc(euf::solve_eqs, m, st)); + if (smtp.m_elim_unconstrained) s.add_simplifier(alloc(elim_unconstrained, m, st)); if (smtp.m_nnf_cnf) s.add_simplifier(alloc(cnf_nnf_simplifier, m, p, st)); if (smtp.m_macro_finder || smtp.m_quasi_macros) s.add_simplifier(alloc(eliminate_predicates, m, st)); if (smtp.m_qe_lite) s.add_simplifier(mk_qe_lite_simplifer(m, p, st)); diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index b56093eb0ff..c9b0430b584 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -540,6 +540,7 @@ namespace euf { sat::check_result solver::check() { ++m_stats.m_final_checks; TRACE("euf", s().display(tout);); + TRACE("final_check", s().display(tout);); bool give_up = false; bool cont = false; diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 573003305f7..df832a675f7 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -69,9 +69,12 @@ namespace q { [&](euf::enode* n) { m_mam->add_node(n, false); }; - ctx.get_egraph().set_on_merge(_on_merge); - if (!ctx.relevancy_enabled()) - ctx.get_egraph().set_on_make(_on_make); + + if (ctx.get_config().m_ematching) { + ctx.get_egraph().set_on_merge(_on_merge); + if (!ctx.relevancy_enabled()) + ctx.get_egraph().set_on_make(_on_make); + } m_mam = mam::mk(ctx, *this); } diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index f3c46f95c98..180242f858a 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -26,6 +26,9 @@ void preprocessor_params::updt_local_params(params_ref const & _p) { m_restricted_quasi_macros = p.restricted_quasi_macros(); m_pull_nested_quantifiers = p.pull_nested_quantifiers(); m_refine_inj_axiom = p.refine_inj_axioms(); + m_propagate_values = p.propagate_values(); + m_elim_unconstrained = p.elim_unconstrained(); + m_solve_eqs = p.solve_eqs(); m_ng_lift_ite = static_cast(p.q_lift_ite()); } @@ -47,6 +50,8 @@ void preprocessor_params::display(std::ostream & out) const { DISPLAY_PARAM(m_eliminate_term_ite); DISPLAY_PARAM(m_macro_finder); DISPLAY_PARAM(m_propagate_values); + DISPLAY_PARAM(m_solve_eqs); + DISPLAY_PARAM(m_elim_unconstrained); DISPLAY_PARAM(m_refine_inj_axiom); DISPLAY_PARAM(m_eliminate_bounds); DISPLAY_PARAM(m_simplify_bit2int); diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h index 53568c36684..55a55980b2b 100644 --- a/src/smt/params/preprocessor_params.h +++ b/src/smt/params/preprocessor_params.h @@ -31,43 +31,29 @@ struct preprocessor_params : public pattern_inference_params, public bit_blaster_params { lift_ite_kind m_lift_ite; lift_ite_kind m_ng_lift_ite; // lift ite for non ground terms - bool m_pull_cheap_ite; - bool m_pull_nested_quantifiers; - bool m_eliminate_term_ite; - bool m_macro_finder; - bool m_propagate_values; - bool m_refine_inj_axiom; - bool m_eliminate_bounds; - bool m_simplify_bit2int; - bool m_nnf_cnf; - bool m_distribute_forall; - bool m_reduce_args; - bool m_quasi_macros; - bool m_restricted_quasi_macros; - bool m_max_bv_sharing; - bool m_pre_simplifier; - bool m_nlquant_elim; + bool m_pull_cheap_ite = false; + bool m_pull_nested_quantifiers = false; + bool m_eliminate_term_ite = false; + bool m_macro_finder = false; + bool m_propagate_values = true; + bool m_elim_unconstrained = true; + bool m_solve_eqs = true; + bool m_refine_inj_axiom = true; + bool m_eliminate_bounds = false; + bool m_simplify_bit2int = false; + bool m_nnf_cnf = true; + bool m_distribute_forall = false; + bool m_reduce_args = false; + bool m_quasi_macros = false; + bool m_restricted_quasi_macros = false; + bool m_max_bv_sharing = true; + bool m_pre_simplifier = true; + bool m_nlquant_elim = false; public: preprocessor_params(params_ref const & p = params_ref()): m_lift_ite(lift_ite_kind::LI_NONE), - m_ng_lift_ite(lift_ite_kind::LI_NONE), - m_pull_cheap_ite(false), - m_pull_nested_quantifiers(false), - m_eliminate_term_ite(false), - m_macro_finder(false), - m_propagate_values(true), - m_refine_inj_axiom(true), - m_eliminate_bounds(false), - m_simplify_bit2int(false), - m_nnf_cnf(true), - m_distribute_forall(false), - m_reduce_args(false), - m_quasi_macros(false), - m_restricted_quasi_macros(false), - m_max_bv_sharing(true), - m_pre_simplifier(true), - m_nlquant_elim(false) { + m_ng_lift_ite(lift_ite_kind::LI_NONE) { updt_local_params(p); } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 8dfcf5d3b6e..86a0371f24e 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -18,8 +18,11 @@ def_module_params(module_name='smt', ('case_split', UINT, 1, '0 - case split based on variable activity, 1 - similar to 0, but delay case splits created during the search, 2 - similar to 0, but cache the relevancy, 3 - case split based on relevancy (structural splitting), 4 - case split on relevancy and activity, 5 - case split on relevancy and current goal, 6 - activity-based case split with theory-aware branching activity'), ('delay_units', BOOL, False, 'if true then z3 will not restart when a unit clause is learned'), ('delay_units_threshold', UINT, 32, 'maximum number of learned unit clauses before restarting, ignored if delay_units is false'), - ('pull_nested_quantifiers', BOOL, False, 'pull nested quantifiers'), - ('refine_inj_axioms', BOOL, True, 'refine injectivity axioms'), + ('elim_unconstrained', BOOL, True, 'pre-processing: eliminate unconstrained subterms'), + ('solve_eqs', BOOL, True, 'pre-processing: solve equalities'), + ('propagate_values', BOOL, True, 'pre-processing: propagate values'), + ('pull_nested_quantifiers', BOOL, False, 'pre-processing: pull nested quantifiers'), + ('refine_inj_axioms', BOOL, True, 'pre-processing: refine injectivity axioms'), ('candidate_models', BOOL, False, 'create candidate models even when quantifier or theory reasoning is incomplete'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts before giving up.'), ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), @@ -50,7 +53,7 @@ def_module_params(module_name='smt', ('bv.watch_diseq', BOOL, False, 'use watch lists instead of eager axioms for bit-vectors'), ('bv.delay', BOOL, False, 'delay internalize expensive bit-vector operations'), ('bv.eq_axioms', BOOL, True, 'enable redundant equality axioms for bit-vectors'), - ('bv.size_reduce', BOOL, False, 'turn assertions that set the upper bits of a bit-vector to constants into a substitution that replaces the bit-vector with constant bits. Useful for minimizing circuits as many input bits to circuits are constant'), + ('bv.size_reduce', BOOL, False, 'pre-processing; turn assertions that set the upper bits of a bit-vector to constants into a substitution that replaces the bit-vector with constant bits. Useful for minimizing circuits as many input bits to circuits are constant'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), ('arith.solver', UINT, 6, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation, relevant only if smt.arith.solver=2'), From e5e16268cc4ed46f7f98cd3e96d72115af59993c Mon Sep 17 00:00:00 2001 From: Jerry James Date: Thu, 12 Jan 2023 12:20:28 -0700 Subject: [PATCH 274/597] Initialize m_istamp_id in lookahead::init (#6533) --- src/sat/sat_lookahead.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index 83e5b9ca584..2fa7ed040dd 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -1001,6 +1001,7 @@ namespace sat { m_inconsistent = false; m_qhead = 0; m_bstamp_id = 0; + m_istamp_id = 0; for (unsigned i = 0; i < m_num_vars; ++i) { init_var(i); From 25b0b1430c40d24f151756f84198b61d3091e272 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Jan 2023 12:42:20 -0800 Subject: [PATCH 275/597] move bound_manager to simplifiers, add bound manager to extract_eqs for solve-eqs #6532 --- src/ast/rewriter/arith_rewriter.cpp | 5 +- src/ast/simplifiers/CMakeLists.txt | 1 + .../simplifiers}/bound_manager.cpp | 19 +++---- .../arith => ast/simplifiers}/bound_manager.h | 4 +- src/ast/simplifiers/extract_eqs.cpp | 49 +++++++++++++++---- src/cmd_context/extra_cmds/dbg_cmds.cpp | 2 +- src/tactic/arith/CMakeLists.txt | 1 - src/tactic/arith/add_bounds_tactic.cpp | 5 +- src/tactic/arith/eq2bv_tactic.cpp | 5 +- src/tactic/arith/lia2card_tactic.cpp | 5 +- src/tactic/arith/lia2pb_tactic.cpp | 5 +- src/tactic/arith/nla2bv_tactic.cpp | 5 +- src/tactic/arith/normalize_bounds_tactic.cpp | 15 +++--- src/tactic/arith/pb2bv_model_converter.h | 2 +- src/tactic/arith/pb2bv_tactic.cpp | 10 ++-- .../fd_solver/bounded_int2bv_solver.cpp | 7 ++- src/tactic/smtlogics/qflia_tactic.cpp | 5 +- 17 files changed, 86 insertions(+), 59 deletions(-) rename src/{tactic/arith => ast/simplifiers}/bound_manager.cpp (95%) rename src/{tactic/arith => ast/simplifiers}/bound_manager.h (95%) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index c2a9b4cf4cd..15456d41ef2 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -23,9 +23,8 @@ Module Name: #include "ast/ast_pp.h" seq_util& arith_rewriter_core::seq() { - if (!m_seq) { - m_seq = alloc(seq_util, m); - } + if (!m_seq) + m_seq = alloc(seq_util, m); return *m_seq; } diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index b807b00e384..bd39395c043 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -1,6 +1,7 @@ z3_add_component(simplifiers SOURCES bit_blaster.cpp + bound_manager.cpp bv_slice.cpp card2bv.cpp demodulator_simplifier.cpp diff --git a/src/tactic/arith/bound_manager.cpp b/src/ast/simplifiers/bound_manager.cpp similarity index 95% rename from src/tactic/arith/bound_manager.cpp rename to src/ast/simplifiers/bound_manager.cpp index d6422abff69..0770bf4212a 100644 --- a/src/tactic/arith/bound_manager.cpp +++ b/src/ast/simplifiers/bound_manager.cpp @@ -16,10 +16,11 @@ Module Name: Notes: --*/ -#include "tactic/arith/bound_manager.h" + #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" -#include "tactic/goal.h" +#include "ast/ast_translation.h" +#include "ast/simplifiers/bound_manager.h" bound_manager::bound_manager(ast_manager & m): m_util(m), @@ -103,7 +104,9 @@ bool bound_manager::is_numeral(expr* v, numeral& n, bool& is_int) { return m_util.is_numeral(v, n, is_int); } -void bound_manager::operator()(expr * f, expr_dependency * d) { +void bound_manager::operator()(expr * f, expr_dependency * d, proof* p) { + if (p) + return; TRACE("bound_manager", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); expr * v; numeral n; @@ -243,16 +246,6 @@ bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { return true; } -void bound_manager::operator()(goal const & g) { - if (g.proofs_enabled()) - return; - unsigned sz = g.size(); - for (unsigned i = 0; i < sz; i++) { - operator()(g.form(i), g.dep(i)); - } -} - - void bound_manager::reset() { m_bounded_vars.finalize(); m_lowers.finalize(); diff --git a/src/tactic/arith/bound_manager.h b/src/ast/simplifiers/bound_manager.h similarity index 95% rename from src/tactic/arith/bound_manager.h rename to src/ast/simplifiers/bound_manager.h index 6047dd36df4..967ac2e0435 100644 --- a/src/tactic/arith/bound_manager.h +++ b/src/ast/simplifiers/bound_manager.h @@ -21,7 +21,6 @@ Module Name: #include "ast/ast.h" #include "ast/arith_decl_plugin.h" -class goal; class bound_manager { public: @@ -50,8 +49,7 @@ class bound_manager { ast_manager & m() const { return m_util.get_manager(); } - void operator()(goal const & g); - void operator()(expr * n, expr_dependency * d = nullptr); + void operator()(expr * n, expr_dependency * d, proof* p); bool has_lower(expr * c, numeral & v, bool & strict) const { limit l; diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 66ebef85c2d..37d92bcbbf7 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -21,6 +21,7 @@ Module Name: #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/simplifiers/extract_eqs.h" +#include "ast/simplifiers/bound_manager.h" #include "params/tactic_params.hpp" @@ -83,6 +84,7 @@ namespace euf { class arith_extract_eq : public extract_eq { ast_manager& m; arith_util a; + bound_manager m_bm; expr_ref_vector m_args, m_trail; expr_sparse_mark m_nonzero; bool m_enabled = true; @@ -107,6 +109,17 @@ namespace euf { solve_eq(orig, u, term, d, eqs); } + void solve_to_real(expr* orig, expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + expr* z, *u; + rational r; + if (!a.is_to_real(x, z) || !is_uninterp_const(z)) + return; + if (a.is_to_real(y, u)) + eqs.push_back(dependent_eq(orig, to_app(z), expr_ref(u, m), d)); + else if (a.is_numeral(y, r) && r.is_int()) + eqs.push_back(dependent_eq(orig, to_app(z), expr_ref(a.mk_int(r), m), d)); + } + /*** * Solve * x + Y = Z -> x = Z - Y @@ -157,8 +170,8 @@ namespace euf { 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) +break; } if (!nonzero) continue; @@ -222,12 +235,12 @@ namespace euf { } void add_pos(expr* f) { - expr* lhs = nullptr, *rhs = nullptr; + expr* lhs = nullptr, * rhs = nullptr; rational val; - if (a.is_le(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_neg()) - mark_nonzero(lhs); + if (a.is_le(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_neg()) + mark_nonzero(lhs); else if (a.is_ge(f, lhs, rhs) && a.is_numeral(rhs, val) && val.is_pos()) - mark_nonzero(lhs); + mark_nonzero(lhs); else if (m.is_not(f, f)) { if (a.is_le(f, lhs, rhs) && a.is_numeral(rhs, val) && !val.is_neg()) mark_nonzero(lhs); @@ -242,11 +255,12 @@ namespace euf { solve_add(orig, x, y, d, eqs); solve_mod(orig, x, y, d, eqs); solve_mul(orig, x, y, d, eqs); + solve_to_real(orig, x, y, d, eqs); } public: - arith_extract_eq(ast_manager& m) : m(m), a(m), m_args(m), m_trail(m) {} + arith_extract_eq(ast_manager& m) : m(m), a(m), m_bm(m), m_args(m), m_trail(m) {} void get_eqs(dependent_expr const& e, dep_eq_vector& eqs) override { if (!m_enabled) @@ -257,6 +271,18 @@ namespace euf { solve_eq(f, x, y, d, eqs); solve_eq(f, y, x, d, eqs); } + bool str; + rational lo, hi; + if (a.is_le(f, x, y) && a.is_numeral(y, hi) && m_bm.has_lower(x, lo, str) && !str && lo == hi) { + expr_dependency_ref d2(m); + d2 = m.mk_join(d, m_bm.lower_dep(x)); + if (is_uninterp_const(x)) + eqs.push_back(dependent_eq(f, to_app(x), expr_ref(y, m), d2)); + else { + solve_eq(f, x, y, d2, eqs); + solve_eq(f, y, x, d2, eqs); + } + } } void pre_process(dependent_expr_state& fmls) override { @@ -264,8 +290,13 @@ namespace euf { return; m_nonzero.reset(); m_trail.reset(); - for (unsigned i = 0; i < fmls.qtail(); ++i) - add_pos(fmls[i].fml()); + m_bm.reset(); + for (unsigned i = 0; i < fmls.qtail(); ++i) { + auto [f, p, d] = fmls[i](); + add_pos(f); + m_bm(f, d, p); + } + } diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 278c0a2056f..b18d435285d 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -26,7 +26,7 @@ Module Name: #include "ast/ast_lt.h" #include "cmd_context/simplify_cmd.h" #include "ast/ast_smt2_pp.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" diff --git a/src/tactic/arith/CMakeLists.txt b/src/tactic/arith/CMakeLists.txt index d5c7557e6c9..11aa8724238 100644 --- a/src/tactic/arith/CMakeLists.txt +++ b/src/tactic/arith/CMakeLists.txt @@ -2,7 +2,6 @@ z3_add_component(arith_tactics SOURCES add_bounds_tactic.cpp arith_bounds_tactic.cpp - bound_manager.cpp bound_propagator.cpp bv2int_rewriter.cpp bv2real_rewriter.cpp diff --git a/src/tactic/arith/add_bounds_tactic.cpp b/src/tactic/arith/add_bounds_tactic.cpp index a544c68100e..3d9f0bd250b 100644 --- a/src/tactic/arith/add_bounds_tactic.cpp +++ b/src/tactic/arith/add_bounds_tactic.cpp @@ -19,7 +19,7 @@ Revision History: #include "tactic/tactical.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt2_pp.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" struct is_unbounded_proc { struct found {}; @@ -41,7 +41,8 @@ struct is_unbounded_proc { bool is_unbounded(goal const & g) { ast_manager & m = g.m(); bound_manager bm(m); - bm(g); + for (unsigned i = 0; i < g.size(); ++i) + bm(g.form(i), g.dep(i), g.pr(i)); is_unbounded_proc proc(bm); return test(g, proc); } diff --git a/src/tactic/arith/eq2bv_tactic.cpp b/src/tactic/arith/eq2bv_tactic.cpp index 1711a34acb5..0b2630236cb 100644 --- a/src/tactic/arith/eq2bv_tactic.cpp +++ b/src/tactic/arith/eq2bv_tactic.cpp @@ -18,7 +18,7 @@ Module Name: --*/ #include "tactic/tactical.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" @@ -179,7 +179,8 @@ class eq2bv_tactic : public tactic { tactic_report report("eq2bv", *g); - m_bounds(*g); + for (unsigned i = 0; i < g->size(); ++i) + m_bounds(g->form(i), g->dep(i), g->pr(i)); if (m_bounds.inconsistent() || g->proofs_enabled()) { g->inc_depth(); diff --git a/src/tactic/arith/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp index cb6f6c228dd..f8cd3674aea 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -24,7 +24,7 @@ Module Name: #include "ast/ast_util.h" #include "ast/ast_pp_util.h" #include "tactic/tactical.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "ast/converters/generic_model_converter.h" class lia2card_tactic : public tactic { @@ -180,7 +180,8 @@ class lia2card_tactic : public tactic { tactic_report report("lia2card", *g); bound_manager bounds(m); - bounds(*g); + for (unsigned i = 0; i < g->size(); ++i) + bounds(g->form(i), g->dep(i), g->pr(i)); for (expr* x : bounds) { checkpoint(); diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index f78e8562168..450a7d25a93 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -17,7 +17,7 @@ Revision History: --*/ #include "tactic/tactical.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" #include "ast/converters/generic_model_converter.h" @@ -197,7 +197,8 @@ class lia2pb_tactic : public tactic { return; } - m_bm(*g); + for (unsigned i = 0; i < g->size(); ++i) + m_bm(g->form(i), g->dep(i), g->pr(i)); TRACE("lia2pb", m_bm.display(tout);); diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index cd37db40292..c791841ec2f 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -28,7 +28,7 @@ Module Name: #include "tactic/arith/bv2int_rewriter.h" #include "tactic/arith/bv2real_rewriter.h" #include "ast/converters/generic_model_converter.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_smt2_pp.h" @@ -89,7 +89,8 @@ class nla2bv_tactic : public tactic { ); tactic_report report("nla->bv", g); m_fmc = alloc(generic_model_converter, m_manager, "nla2bv"); - m_bounds(g); + for (unsigned i = 0; i < g.size(); ++i) + m_bounds(g.form(i), g.dep(i), g.pr(i)); collect_power2(g); switch (collect_vars(g)) { case has_num: diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index b20eaf53a3d..7c09703eba3 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include "tactic/tactical.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "ast/rewriter/th_rewriter.h" #include "ast/converters/generic_model_converter.h" #include "ast/arith_decl_plugin.h" @@ -67,13 +67,11 @@ class normalize_bounds_tactic : public tactic { } bool has_lowers() { - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { + for (auto* e : m_bm) { TRACE("normalize_bounds_tactic", rational val; bool strict; - tout << mk_ismt2_pp(*it, m) << " has_lower: " << m_bm.has_lower(*it, val, strict) << " val: " << val << "\n";); - if (is_target(*it)) + tout << mk_ismt2_pp(e, m) << " has_lower: " << m_bm.has_lower(e, val, strict) << " val: " << val << "\n";); + if (is_target(e)) return true; } return false; @@ -83,8 +81,9 @@ class normalize_bounds_tactic : public tactic { bool produce_models = in->models_enabled(); bool produce_proofs = in->proofs_enabled(); tactic_report report("normalize-bounds", *in); - - m_bm(*in); + + for (unsigned i = 0; i < in->size(); ++i) + m_bm(in->form(i), in->dep(i), in->pr(i)); if (!has_lowers()) { result.push_back(in.get()); diff --git a/src/tactic/arith/pb2bv_model_converter.h b/src/tactic/arith/pb2bv_model_converter.h index 2de873ba2ae..3477a2081ac 100644 --- a/src/tactic/arith/pb2bv_model_converter.h +++ b/src/tactic/arith/pb2bv_model_converter.h @@ -19,7 +19,7 @@ Module Name: #pragma once #include "ast/converters/model_converter.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" class pb2bv_model_converter : public model_converter { typedef std::pair func_decl_pair; diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 5ff7425ba72..507a54b6a13 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -28,7 +28,7 @@ Module Name: #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/pb2bv_rewriter.h" #include "tactic/tactical.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "ast/converters/generic_model_converter.h" #include "tactic/arith/pb2bv_model_converter.h" #include "tactic/arith/pb2bv_tactic.h" @@ -913,7 +913,9 @@ class pb2bv_tactic : public tactic { return; } - m_bm(*g); + unsigned size = g->size(); + for (unsigned i = 0; i < size; i++) + m_bm(g->form(i), g->dep(i), g->pr(i)); TRACE("pb2bv", m_bm.display(tout);); @@ -924,7 +926,6 @@ class pb2bv_tactic : public tactic { throw_tactic(p.e); } - unsigned size = g->size(); expr_ref_vector new_exprs(m); expr_dependency_ref_vector new_deps(m); @@ -1042,7 +1043,8 @@ struct is_pb_probe : public probe { try { ast_manager & m = g.m(); bound_manager bm(m); - bm(g); + for (unsigned i = 0; i < g.size(); i++) + bm(g.form(i), g.dep(i), g.pr(i)); arith_util a_util(m); pb_util pb(m); expr_fast_mark1 visited; diff --git a/src/tactic/fd_solver/bounded_int2bv_solver.cpp b/src/tactic/fd_solver/bounded_int2bv_solver.cpp index 4ac82c0c2a3..317286e1e83 100644 --- a/src/tactic/fd_solver/bounded_int2bv_solver.cpp +++ b/src/tactic/fd_solver/bounded_int2bv_solver.cpp @@ -23,7 +23,7 @@ Module Name: #include "ast/converters/generic_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "tactic/arith/bv2int_rewriter.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/bv_decl_plugin.h" @@ -330,9 +330,8 @@ class bounded_int2bv_solver : public solver_na2as { if (m_assertions.empty()) return; m_flushed = true; bound_manager& bm = *m_bounds.back(); - for (expr* a : m_assertions) { - bm(a); - } + for (expr* a : m_assertions) + bm(a, nullptr, nullptr); TRACE("int2bv", bm.display(tout);); expr_safe_replace sub(m); accumulate_sub(sub); diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index b8ebbd8a916..e72a63087e2 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -32,14 +32,15 @@ Module Name: #include "tactic/aig/aig_tactic.h" #include "tactic/smtlogics/smt_tactic.h" #include "sat/tactic/sat_tactic.h" -#include "tactic/arith/bound_manager.h" +#include "ast/simplifiers/bound_manager.h" #include "tactic/arith/probe_arith.h" struct quasi_pb_probe : public probe { result operator()(goal const & g) override { bool found_non_01 = false; bound_manager bm(g.m()); - bm(g); + for (unsigned i = 0; i < g.size(); ++i) + bm(g.form(i), g.dep(i), g.pr(i)); rational l, u; bool st; for (expr * t : bm) { if (bm.has_lower(t, l, st) && bm.has_upper(t, u, st) && (l.is_zero() || l.is_one()) && (u.is_zero() || u.is_one())) From e4bd40667514846397a7be251e9778ed5a3c526c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Jan 2023 16:27:33 -0800 Subject: [PATCH 276/597] update version of manylinux --- scripts/nightly.yaml | 2 +- scripts/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 55fcb147f88..a9490e3ea86 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -91,7 +91,7 @@ stages: displayName: "ManyLinux build" pool: vmImage: "ubuntu-latest" - container: "quay.io/pypa/manylinux2010_x86_64:latest" + container: "quay.io/pypa/manylinux2014_x86_64:latest" steps: - script: $(python) scripts/mk_unix_dist.py --nodotnet --nojava - script: git clone https://github.com/z3prover/z3test z3test diff --git a/scripts/release.yml b/scripts/release.yml index 53d937b690f..a82557c1ed1 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -124,7 +124,7 @@ stages: displayName: "ManyLinux build" variables: name: ManyLinux - image: "quay.io/pypa/manylinux2010_x86_64:latest" + image: "quay.io/pypa/manylinux2014_x86_64:latest" python: "/opt/python/cp37-cp37m/bin/python" pool: vmImage: "ubuntu-latest" From 85abbb8188b7d5b32784ac967f2b309ff4046622 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Jan 2023 16:58:42 -0800 Subject: [PATCH 277/597] include apt-get update for doc build Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index a9490e3ea86..f06c3b4991a 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -54,6 +54,7 @@ stages: pool: vmImage: "ubuntu-latest" steps: + - script: sudo apt-get update - script: sudo apt-get install ocaml opam libgmp-dev - script: opam init -y - script: eval `opam config env`; opam install zarith ocamlfind -y From d204413f2aca80615f9fe47023fd4178e7adf6ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Jan 2023 17:54:42 -0800 Subject: [PATCH 278/597] remove update Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index f06c3b4991a..a9490e3ea86 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -54,7 +54,6 @@ stages: pool: vmImage: "ubuntu-latest" steps: - - script: sudo apt-get update - script: sudo apt-get install ocaml opam libgmp-dev - script: opam init -y - script: eval `opam config env`; opam install zarith ocamlfind -y From 0d46787fcf0a0b3633b2e2c002bced187592500b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Jan 2023 17:58:23 -0800 Subject: [PATCH 279/597] update readme Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b1715086760..a72aae7e783 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,7 @@ Version 4.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. + Version 4.12.0 ============== - add clause logging API. @@ -112,6 +113,8 @@ Version 4.12.0 theory clauses. - integration of pre-processing proofs with logging proofs. There is currently no supported bridge to create a end-to-end proof objects. +- experimental API for accessing E-graphs. Exposed over Python. This API should be considered temporary +and subject to be changed depending on use cases or removed. The functions are `Z3_solver_congruence_root`, `Z3_solver_congruence_next`. Version 4.11.2 From d289434b6589160f8840caf6c174bd5f9f161806 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Jan 2023 19:06:30 -0800 Subject: [PATCH 280/597] fix #6535 --- src/test/smt2print_parse.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index ed674de8bac..765a78060bf 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -67,7 +67,7 @@ void test_parseprint(char const* spec) { void test_eval(Z3_context ctx, Z3_string spec, bool shouldFail) { std::cout << "spec:\n" << spec << "\n"; - Z3_string resp; + std::string resp; bool failed = false; try { resp = Z3_eval_smtlib2_string(ctx, spec); @@ -76,6 +76,10 @@ void test_eval(Z3_context ctx, Z3_string spec, bool shouldFail) { resp = e.what(); failed = true; } + catch (...) { + resp = "unknown exception"; + failed = true; + } std::cout << "response:\n" << resp << "\n"; @@ -141,15 +145,21 @@ void test_repeated_eval() { Z3_set_error_handler(ctx, throwError); std::cout << "testing Z3_eval_smtlib2_string\n"; - test_eval(ctx, spec1, false); - std::cout << "successful call after successful call\n"; - test_eval(ctx, spec2, false); - std::cout << "failing call after successful call\n"; - test_eval(ctx, spec3, true); - std::cout << "failing call after failing call\n"; - test_eval(ctx, spec4, true); - std::cout << "successful call after failing call\n"; - test_eval(ctx, spec1, false); + try { + test_eval(ctx, spec1, false); + std::cout << "successful call after successful call\n"; + test_eval(ctx, spec2, false); + std::cout << "failing call after successful call\n"; + test_eval(ctx, spec3, true); + std::cout << "failing call after failing call\n"; + test_eval(ctx, spec4, true); + std::cout << "successful call after failing call\n"; + test_eval(ctx, spec1, false); + } + catch(...) { + std::cout << "Error: uncaught exception\n"; + throw; + } std::cout << "done evaluating\n"; From 42fbf23a8fbf310e861a83679ffad8cc026afb3b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Jan 2023 14:01:15 -0800 Subject: [PATCH 281/597] update code signing Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 ++-- scripts/release.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index a9490e3ea86..ab4e9fc0d32 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -261,7 +261,7 @@ stages: minorVersion: $(Minor) patchVersion: $(Patch) arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.sym.nuspec -Version $(NightlyVersion) -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 continueOnError: true displayName: 'Sign Package' inputs: @@ -289,7 +289,7 @@ stages: SessionTimeout: '60' MaxConcurrency: '50' MaxRetryAttempts: '5' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 continueOnError: true displayName: 'Sign Symbol Package' inputs: diff --git a/scripts/release.yml b/scripts/release.yml index a82557c1ed1..22773c319cb 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -220,7 +220,7 @@ stages: inputs: command: custom arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.sym.nuspec -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 displayName: 'Sign Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' @@ -247,7 +247,7 @@ stages: SessionTimeout: '60' MaxConcurrency: '50' MaxRetryAttempts: '5' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 displayName: 'Sign Symbol Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' From 60fef928ccb8622ffe426f4caca5f01d3bf30fe1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Jan 2023 16:12:48 -0800 Subject: [PATCH 282/597] missing code signing Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 ++-- scripts/release.yml | 4 ++-- src/ast/converters/generic_model_converter.cpp | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index ab4e9fc0d32..f0a4bfed0eb 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -366,7 +366,7 @@ stages: minorVersion: $(Minor) patchVersion: $(Patch) arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.x86.sym.nuspec -Version $(NightlyVersion) -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 continueOnError: true displayName: 'Sign Package' inputs: @@ -394,7 +394,7 @@ stages: SessionTimeout: '60' MaxConcurrency: '50' MaxRetryAttempts: '5' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 continueOnError: true displayName: 'Sign Symbol Package' inputs: diff --git a/scripts/release.yml b/scripts/release.yml index 22773c319cb..ac301316c74 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -319,7 +319,7 @@ stages: inputs: command: custom arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 displayName: 'Sign Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' @@ -346,7 +346,7 @@ stages: SessionTimeout: '60' MaxConcurrency: '50' MaxRetryAttempts: '5' - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 displayName: 'Sign Symbol Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index 9adb9ee4b01..d2ba2793607 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -30,6 +30,7 @@ Module Name: void generic_model_converter::add(func_decl * d, expr* e) { VERIFY(e); + verbose_stream() << d->get_name() << " " << mk_pp(e, m) << "\n"; VERIFY(d->get_range() == e->get_sort()); m_entries.push_back(entry(d, e, m, ADD)); } From f1805138e7611f16d135a301c412e003d2c26eb4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Jan 2023 16:13:20 -0800 Subject: [PATCH 283/597] missing code signing Signed-off-by: Nikolaj Bjorner --- src/ast/converters/generic_model_converter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index d2ba2793607..9adb9ee4b01 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -30,7 +30,6 @@ Module Name: void generic_model_converter::add(func_decl * d, expr* e) { VERIFY(e); - verbose_stream() << d->get_name() << " " << mk_pp(e, m) << "\n"; VERIFY(d->get_range() == e->get_sort()); m_entries.push_back(entry(d, e, m, ADD)); } From 234ff28d180288ce673d38501b99fc46de58c978 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Jan 2023 16:15:27 -0800 Subject: [PATCH 284/597] prepare release script Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index ac301316c74..92b60c5b19e 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -520,7 +520,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,1) + condition: eq(0,1) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From c33b1e3082ad8875863500c747b37d75ebdc01bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Jan 2023 16:27:58 -0800 Subject: [PATCH 285/597] fixup manylinux reference in release script Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/release.yml b/scripts/release.yml index 92b60c5b19e..201e6de20ca 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -124,11 +124,10 @@ stages: displayName: "ManyLinux build" variables: name: ManyLinux - image: "quay.io/pypa/manylinux2014_x86_64:latest" python: "/opt/python/cp37-cp37m/bin/python" pool: vmImage: "ubuntu-latest" - container: "quay.io/pypa/manylinux2010_x86_64:latest" + container: "quay.io/pypa/manylinux2014_x86_64:latest" steps: - task: PythonScript@0 displayName: Build From 54524de784808a2c2170e8e31918aa08c2419c48 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Jan 2023 17:10:36 -0800 Subject: [PATCH 286/597] Update release.yml for Azure Pipelines --- scripts/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/release.yml b/scripts/release.yml index 201e6de20ca..809a8b20493 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -233,14 +233,14 @@ stages: "OperationCode" : "NuGetSign", "Parameters" : {}, "ToolName" : "sign", - "ToolVersion" : "1.0" + "ToolVersion" : "2.0" }, { "KeyCode" : "CP-401405", "OperationCode" : "NuGetVerify", "Parameters" : {}, "ToolName" : "sign", - "ToolVersion" : "1.0" + "ToolVersion" : "2.0" } ] SessionTimeout: '60' @@ -260,14 +260,14 @@ stages: "OperationCode" : "NuGetSign", "Parameters" : {}, "ToolName" : "sign", - "ToolVersion" : "1.0" + "ToolVersion" : "2.0" }, { "KeyCode" : "CP-401405", "OperationCode" : "NuGetVerify", "Parameters" : {}, "ToolName" : "sign", - "ToolVersion" : "1.0" + "ToolVersion" : "2.0" } ] SessionTimeout: '60' From 5dbd0bb65855a46a8d8849fc8357d62513710fea Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Jan 2023 23:33:35 -0800 Subject: [PATCH 287/597] add sign Signed-off-by: Nikolaj Bjorner --- scripts/build-win-signed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-win-signed.yml b/scripts/build-win-signed.yml index f2eba189201..f1e08958dcd 100644 --- a/scripts/build-win-signed.yml +++ b/scripts/build-win-signed.yml @@ -38,7 +38,7 @@ jobs: publishSymbols: true symbolServerType: TeamServices detailedLog: true - - task: EsrpCodeSigning@1 + - task: EsrpCodeSigning@2 displayName: Sign inputs: ConnectedServiceName: 'z3-esrp-signing-2' From feda706d0dda2539176246dd221328122c66f77d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Jan 2023 06:24:26 -0800 Subject: [PATCH 288/597] Update release.yml for Azure Pipelines --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index 809a8b20493..4421f515386 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -519,7 +519,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(0,1) + condition: eq(1,1) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From 4f7f4376b8e9ee12685968ba63f2cec5d19e8962 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Jan 2023 17:20:37 -0500 Subject: [PATCH 289/597] fix bug in new core not detecting conflict, fix #6525, add tactic doc Signed-off-by: Nikolaj Bjorner --- src/ast/ast.cpp | 1 + src/ast/ast.h | 1 + src/ast/simplifiers/dependent_expr_state.cpp | 20 ++++++++++++- src/ast/simplifiers/dependent_expr_state.h | 3 +- src/ast/simplifiers/solve_eqs.cpp | 4 ++- src/nlsat/tactic/nlsat_tactic.h | 19 +++++++++++- src/nlsat/tactic/qfnra_nlsat_tactic.h | 21 ++++++++++++- src/qe/qe_tactic.h | 26 +++++++++++++++- src/sat/smt/arith_diagnostics.cpp | 13 ++++++++ src/sat/smt/arith_solver.cpp | 31 ++++++++++++-------- src/sat/smt/arith_solver.h | 1 + src/sat/tactic/sat_tactic.h | 31 +++++++++++++++++++- src/smt/tactic/ctx_solver_simplify_tactic.h | 12 +++++++- src/smt/tactic/unit_subsumption_tactic.h | 18 ++++++++---- 14 files changed, 175 insertions(+), 26 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 7bb732a5d84..dee55cf98c4 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1673,6 +1673,7 @@ bool ast_manager::are_distinct(expr* a, expr* b) const { } void ast_manager::add_lambda_def(func_decl* f, quantifier* q) { + TRACE("model", tout << "add lambda def " << mk_pp(q, *this) << "\n"); m_lambda_defs.insert(f, q); f->get_info()->set_lambda(true); inc_ref(q); diff --git a/src/ast/ast.h b/src/ast/ast.h index 7514055c574..2400e05a6e7 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1631,6 +1631,7 @@ class ast_manager { void add_lambda_def(func_decl* f, quantifier* q); quantifier* is_lambda_def(func_decl* f); quantifier* is_lambda_def(app* e) { return is_lambda_def(e->get_decl()); } + obj_map const& lambda_defs() const { return m_lambda_defs; } symbol const& lambda_def_qid() const { return m_lambda_def; } diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp index 8e9ecf19040..fec6d28cf71 100644 --- a/src/ast/simplifiers/dependent_expr_state.cpp +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -83,6 +83,23 @@ void dependent_expr_state::freeze_recfun() { m_num_recfun = sz; } +/** +* Freeze all functions used in lambda defined declarations +*/ +void dependent_expr_state::freeze_lambda() { + auto& m = m_frozen_trail.get_manager(); + unsigned sz = m.lambda_defs().size(); + if (m_num_lambdas >= sz) + return; + + ast_mark visited; + for (auto const& [f, body] : m.lambda_defs()) + freeze_terms(body, false, visited); + m_trail.push(value_trail(m_num_lambdas)); + m_num_lambdas = sz; +} + + /** * The current qhead is to be updated to qtail. * Before this update, freeze all functions appearing in formulas. @@ -100,8 +117,9 @@ void dependent_expr_state::freeze_suffix() { if (m_suffix_frozen) return; m_suffix_frozen = true; - auto& m = m_frozen_trail.get_manager(); freeze_recfun(); + freeze_lambda(); + auto& m = m_frozen_trail.get_manager(); ast_mark visited; ptr_vector es; for (unsigned i = qhead(); i < qtail(); ++i) { diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index e8f137e35bb..ef9263686c2 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -43,12 +43,13 @@ Module Name: class dependent_expr_state { unsigned m_qhead = 0; bool m_suffix_frozen = false; - unsigned m_num_recfun = 0; + unsigned m_num_recfun = 0, m_num_lambdas = 0; lbool m_has_quantifiers = l_undef; ast_mark m_frozen; func_decl_ref_vector m_frozen_trail; void freeze_prefix(); void freeze_recfun(); + void freeze_lambda(); void freeze_terms(expr* term, bool only_as_array, ast_mark& visited); void freeze(expr* term); void freeze(func_decl* f); diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index ad58239c3ad..c47a024255c 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -116,10 +116,10 @@ namespace euf { for (auto const& eq : m_next[j]) { auto const& [orig, v, t, d] = eq; SASSERT(j == var2id(v)); - bool is_safe = true; if (m_fmls.frozen(v)) continue; + bool is_safe = true; unsigned todo_sz = todo.size(); // determine if substitution is safe. @@ -223,6 +223,8 @@ namespace euf { void solve_eqs::reduce() { + m_fmls.freeze_suffix(); + for (extract_eq* ex : m_extract_plugins) ex->pre_process(m_fmls); diff --git a/src/nlsat/tactic/nlsat_tactic.h b/src/nlsat/tactic/nlsat_tactic.h index 6a2eab25aa5..56e7863ccc5 100644 --- a/src/nlsat/tactic/nlsat_tactic.h +++ b/src/nlsat/tactic/nlsat_tactic.h @@ -13,7 +13,24 @@ Module Name: Leonardo (leonardo) 2012-01-02 -Notes: +Tactic Documentation: + +## Tactic nlsat + +### Short Description + +(try to) solve goal using a nonlinear arithmetic solver + +### Example + +```z3 +(declare-const x Real) +(declare-const y Real) +(assert (> (* x x) (* y x))) +(assert (> x 0)) +(assert (< y 1)) +(apply (then simplify purify-arith nlsat)) +``` --*/ #pragma once diff --git a/src/nlsat/tactic/qfnra_nlsat_tactic.h b/src/nlsat/tactic/qfnra_nlsat_tactic.h index fe44a786577..f7c2b53400d 100644 --- a/src/nlsat/tactic/qfnra_nlsat_tactic.h +++ b/src/nlsat/tactic/qfnra_nlsat_tactic.h @@ -13,7 +13,26 @@ Module Name: Leonardo (leonardo) 2012-01-23 -Notes: +Tactic Documentation: + +## Tactic qfnra-nlsat + +### Short Description + +Self-contained tactic that attempts to solve goal using a nonlinear arithmetic solver. +It first applies tactics, such as `purify-arith` to convert the goal into a format +where the `nlsat` tactic applies. + +### Example + +```z3 +(declare-const x Real) +(declare-const y Real) +(assert (> (* x x) (* y x))) +(assert (> x 0)) +(assert (< y 1)) +(apply qfnra-nlsat) +``` --*/ #pragma once diff --git a/src/qe/qe_tactic.h b/src/qe/qe_tactic.h index 2f6e3ff2847..e49f6d2ed85 100644 --- a/src/qe/qe_tactic.h +++ b/src/qe/qe_tactic.h @@ -13,9 +13,33 @@ Module Name: Leonardo de Moura (leonardo) 2011-12-28. -Revision History: +Tactic Documentation + +## Tactic qe + +### Short Description + +Apply quantifier elimination on quantified sub-formulas. + +### Long Description + +The tactic applies quantifier elimination procedures on quantified sub-formulas. +It relies on theory plugins that can perform quanifier elimination for selected theories. +These plugins include Booleans, bit-vectors, arithmetic (linear), arrays, and data-types (term algebra). +It performs feasibility checks on cases to throttle the set of sub-formulas where quantifier elimination +is applied. + +### Example + +```z3 +(declare-const x Int) +(declare-const y Int) +(assert (exists ((z Int)) (and (<= x (* 2 z)) (<= (* 3 z) y)))) +(apply qe) +``` --*/ + #pragma once #include "util/params.h" diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index e84646e7bdb..5fd5de07d0a 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -124,6 +124,19 @@ namespace arith { return m_arith_hint.mk(ctx); } + arith_proof_hint const* solver::explain_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { + arith_proof_hint* hint = nullptr; + if (ctx.use_drat()) { + m_arith_hint.set_type(ctx, hint_type::farkas_h); + for (auto lit : core) + m_arith_hint.add_lit(rational::one(), lit); + for (auto const& [a,b] : eqs) + m_arith_hint.add_eq(a, b); + hint = m_arith_hint.mk(ctx); + } + return hint; + } + arith_proof_hint const* solver::explain_implied_eq(lp::explanation const& e, euf::enode* a, euf::enode* b) { if (!ctx.use_drat()) return nullptr; diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index c7318dbd7df..35e0795b7ee 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1196,26 +1196,31 @@ namespace arith { void solver::set_conflict_or_lemma(literal_vector const& core, bool is_conflict) { reset_evidence(); m_core.append(core); - - ++m_num_conflicts; - ++m_stats.m_conflicts; for (auto ev : m_explanation) set_evidence(ev.ci()); + TRACE("arith", tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n"; for (literal c : m_core) tout << literal2expr(c) << "\n"; for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";); - DEBUG_CODE( - if (is_conflict) { + + if (is_conflict) { + DEBUG_CODE( for (literal c : m_core) VERIFY(s().value(c) == l_true); - for (auto p : m_eqs) VERIFY(p.first->get_root() == p.second->get_root()); - }); - for (auto const& eq : m_eqs) - m_core.push_back(ctx.mk_literal(m.mk_eq(eq.first->get_expr(), eq.second->get_expr()))); - for (literal& c : m_core) - c.neg(); - - add_redundant(m_core, explain(hint_type::farkas_h)); + for (auto p : m_eqs) VERIFY(p.first->get_root() == p.second->get_root())); + ++m_num_conflicts; + ++m_stats.m_conflicts; + auto* hint = explain_conflict(m_core, m_eqs); + ctx.set_conflict(euf::th_explain::conflict(*this, m_core, m_eqs, hint)); + } + else { + for (auto const& eq : m_eqs) + m_core.push_back(ctx.mk_literal(m.mk_eq(eq.first->get_expr(), eq.second->get_expr()))); + for (literal& c : m_core) + c.neg(); + + add_redundant(m_core, explain(hint_type::farkas_h)); + } } bool solver::is_infeasible() const { diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 775fecd704e..a13ef66843a 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -478,6 +478,7 @@ namespace arith { arith_proof_hint const* explain(hint_type ty, sat::literal lit = sat::null_literal); arith_proof_hint const* explain_implied_eq(lp::explanation const& e, euf::enode* a, euf::enode* b); arith_proof_hint const* explain_trichotomy(sat::literal le, sat::literal ge, sat::literal eq); + arith_proof_hint const* explain_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs); void explain_assumptions(lp::explanation const& e); diff --git a/src/sat/tactic/sat_tactic.h b/src/sat/tactic/sat_tactic.h index 4bc361ba0e8..c34d3a77d9b 100644 --- a/src/sat/tactic/sat_tactic.h +++ b/src/sat/tactic/sat_tactic.h @@ -13,7 +13,36 @@ Module Name: Leonardo (leonardo) 2011-10-26 -Notes: +Tactic Documentation: + +## Tactic sat + +### Short Description + +Try to solve goal using a SAT solver + +## Tactic sat-preprocess + +### Short Description + +Apply SAT solver preprocessing procedures (bounded resolution, Boolean constant propagation, 2-SAT, subsumption, subsumption resolution). + +### Example + +```z3 +(declare-const a Bool) +(declare-const b Bool) +(declare-const c Bool) +(declare-const d Bool) +(declare-const e Bool) +(declare-const f Bool) +(declare-fun p (Bool) Bool) +(assert (=> a b)) +(assert (=> b c)) +(assert a) +(assert (not c)) +(apply sat-preprocess) +``` --*/ #pragma once diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.h b/src/smt/tactic/ctx_solver_simplify_tactic.h index a1adba0d6d9..ef6b1b8d092 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.h +++ b/src/smt/tactic/ctx_solver_simplify_tactic.h @@ -13,7 +13,17 @@ Module Name: Nikolaj (nbjorner) 2012-3-6 -Notes: +Tactic Documentation: + +## Tactic ctx-solver-simplify + +### Short Description + +A heavy handed version of `ctx-simplify`. It applies SMT checks on sub-formulas to check +if they can be simplified to `true` or `false` within their context. +Note that a sub-formula may occur within multiple contexts due to shared sub-terms. +In this case the tactic is partial and simplifies a limited number of context occurrences. + --*/ #pragma once diff --git a/src/smt/tactic/unit_subsumption_tactic.h b/src/smt/tactic/unit_subsumption_tactic.h index cdb441b3068..d734168da94 100644 --- a/src/smt/tactic/unit_subsumption_tactic.h +++ b/src/smt/tactic/unit_subsumption_tactic.h @@ -13,12 +13,20 @@ Module Name: Nikolaj Bjorner (nbjorner) 2012-9-6 -Notes: +Tactic Documentation: - Background: PDR generates several clauses that subsume each-other. - Simplify a goal assuming it is a conjunction of clauses. - Subsumed clauses are simplified by using unit-propagation - It uses the smt_context for the solver. +## Tactic unit-subsume-simplify + +### Short Description + +implify goal using subsumption based on unit propagation + +### Long Description + +Background: PDR generates several clauses that subsume each-other. +Simplify a goal assuming it is a conjunction of clauses. +Subsumed clauses are simplified by using unit-propagation +It uses the default SMT solver. --*/ #pragma once From d5fde2e578f5600a5b4fc2c969c8931941580d40 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Jan 2023 15:58:29 -0500 Subject: [PATCH 290/597] #6538 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/max_bv_sharing.cpp | 1 + src/sat/sat_solver/sat_smt_preprocess.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ast/simplifiers/max_bv_sharing.cpp b/src/ast/simplifiers/max_bv_sharing.cpp index f708f9dce75..cc56280ddaf 100644 --- a/src/ast/simplifiers/max_bv_sharing.cpp +++ b/src/ast/simplifiers/max_bv_sharing.cpp @@ -22,6 +22,7 @@ Revision History: #include "ast/rewriter/maximize_ac_sharing.h" #include "ast/simplifiers/dependent_expr_state.h" +#include "ast/rewriter/rewriter_def.h" class max_bv_sharing : public dependent_expr_simplifier { diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp index c1a4acc9e95..02c6e88b42e 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ b/src/sat/sat_solver/sat_smt_preprocess.cpp @@ -16,6 +16,7 @@ Module Name: --*/ +#include "ast/rewriter/rewriter_def.h" #include "ast/simplifiers/bit_blaster.h" #include "ast/simplifiers/max_bv_sharing.h" #include "ast/simplifiers/card2bv.h" From dde5218b293075a2267b09fe33722da18691c721 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Jan 2023 22:47:34 -0500 Subject: [PATCH 291/597] fix mbqi value caching issue raised by Clemens and Martin --- src/smt/smt_model_checker.cpp | 17 ++++++------- src/smt/smt_model_generator.cpp | 43 ++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 61173907b11..e555088538c 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -82,22 +82,18 @@ namespace smt { app* fresh_term; if (is_app(val) && to_app(val)->get_num_args() > 0) { ptr_buffer args; - for (expr* arg : *to_app(val)) { + for (expr* arg : *to_app(val)) args.push_back(get_type_compatible_term(arg)); - } fresh_term = m.mk_app(to_app(val)->get_decl(), args.size(), args.data()); } else { expr * sk_term = get_term_from_ctx(val); - if (sk_term != nullptr) { + if (sk_term != nullptr) return sk_term; - } - for (expr* f : m_fresh_exprs) { - if (f->get_sort() == val->get_sort()) { + for (expr* f : m_fresh_exprs) + if (f->get_sort() == val->get_sort()) return f; - } - } fresh_term = m.mk_fresh_const("sk", val->get_sort()); } m_fresh_exprs.push_back(fresh_term); @@ -106,13 +102,16 @@ namespace smt { } void model_checker::init_value2expr() { + if (m_value2expr.empty()) { // populate m_value2expr for (auto const& kv : *m_root2value) { enode * n = kv.m_key; expr * val = kv.m_value; n = n->get_eq_enode_with_min_gen(); - m_value2expr.insert(val, n->get_expr()); + expr* e = n->get_expr(); + if (!m.is_value(e)) + m_value2expr.insert(val, e); } } } diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 73d8cad9d51..6537e638d55 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -330,27 +330,32 @@ namespace smt { enode * n = curr.get_enode(); SASSERT(n->get_root() == n); TRACE("mg_top_sort", tout << curr << "\n";); - dependencies.reset(); - dependency_values.reset(); - model_value_proc * proc = root2proc[n]; - SASSERT(proc); - proc->get_dependencies(dependencies); - for (model_value_dependency const& d : dependencies) { - if (d.is_fresh_value()) { - CTRACE("mg_top_sort", !d.get_value()->get_value(), - tout << "#" << n->get_owner_id() << " " << mk_pp(n->get_expr(), m) << " -> " << d << "\n";); - SASSERT(d.get_value()->get_value()); - dependency_values.push_back(d.get_value()->get_value()); - } - else { - enode * child = d.get_enode(); - TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_expr(), m) << "): " - << mk_pp(child->get_expr(), m) << " " << mk_pp(child->get_root()->get_expr(), m) << "\n";); - child = child->get_root(); - dependency_values.push_back(m_root2value[child]); + app* val = nullptr; + if (m.is_value(n->get_expr())) + val = to_app(n->get_expr()); + else { + dependencies.reset(); + dependency_values.reset(); + model_value_proc * proc = root2proc[n]; + SASSERT(proc); + proc->get_dependencies(dependencies); + for (model_value_dependency const& d : dependencies) { + if (d.is_fresh_value()) { + CTRACE("mg_top_sort", !d.get_value()->get_value(), + tout << "#" << n->get_owner_id() << " " << mk_pp(n->get_expr(), m) << " -> " << d << "\n";); + SASSERT(d.get_value()->get_value()); + dependency_values.push_back(d.get_value()->get_value()); + } + else { + enode * child = d.get_enode(); + TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_expr(), m) << "): " + << mk_pp(child->get_expr(), m) << " " << mk_pp(child->get_root()->get_expr(), m) << "\n";); + child = child->get_root(); + dependency_values.push_back(m_root2value[child]); + } } + val = proc->mk_value(*this, dependency_values); } - app * val = proc->mk_value(*this, dependency_values); register_value(val); m_asts.push_back(val); m_root2value.insert(n, val); From c8f197d0ca32f7dce935db0cca2fd80777b87ae9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Jan 2023 16:30:46 -0500 Subject: [PATCH 292/597] specify macos-11 in nightly to force os11 build Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index f0a4bfed0eb..c93738e58c2 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -11,7 +11,7 @@ stages: - job: Mac displayName: "Mac Build" pool: - vmImage: "macOS-latest" + vmImage: "macOS-11" steps: - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk - script: git clone https://github.com/z3prover/z3test z3test @@ -25,7 +25,7 @@ stages: - job: MacArm64 displayName: "Mac ARM64 Build" pool: - vmImage: "macOS-latest" + vmImage: "macOS-11" steps: - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 --os=osx-11.0 - script: git clone https://github.com/z3prover/z3test z3test From 7368f9f7d3185198fd6b66db483f75f83ab697d4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Jan 2023 14:07:07 -0800 Subject: [PATCH 293/597] increase build version, better propagation in euf-egraph, handle assumptions in sat.smt - increase build version to 4.12.1. This prepares updated release for MacOs-11 build on x86 - move literal propagation mode in euf-egraph to a callback and traversal of equivalence class. Track antecedent by newest equality instead of root. This makes equality propagation to literals have similar behavior as in legacy solver and appears to result in a speedup (10% fewer conflicts on QF_UF/QG-classification/qg5/iso_icl478.smt2 in preliminary testing) - fix interaction of pre-processing and assumptions. Pre-processing has to freeze assumption literals so they don't get eliminated. This is similar to dependencies that are already frozen. --- CMakeLists.txt | 2 +- scripts/mk_project.py | 2 +- scripts/nightly.yaml | 2 +- scripts/release.yml | 2 +- src/ast/euf/euf_egraph.cpp | 49 +++++----- src/ast/euf/euf_egraph.h | 19 +--- src/ast/simplifiers/dependent_expr_state.h | 2 +- .../model_reconstruction_trail.cpp | 27 +++++- .../simplifiers/model_reconstruction_trail.h | 12 ++- src/sat/sat_solver/sat_smt_solver.cpp | 20 ++-- src/sat/smt/euf_solver.cpp | 96 ++++++++++--------- src/sat/smt/euf_solver.h | 2 +- src/smt/smt_context.cpp | 1 + src/tactic/arith/arith_bounds_tactic.h | 2 + src/tactic/core/cofactor_elim_term_ite.cpp | 34 +++---- src/tactic/core/cofactor_term_ite_tactic.h | 13 ++- src/tactic/core/euf_completion_tactic.h | 16 ++++ src/tactic/core/reduce_args_tactic.h | 27 +----- src/tactic/core/tseitin_cnf_tactic.cpp | 10 +- src/tactic/fpa/fpa2bv_tactic.h | 11 ++- src/tactic/fpa/qffp_tactic.h | 11 ++- src/tactic/fpa/qffplra_tactic.h | 3 +- 22 files changed, 201 insertions(+), 162 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d79a01da71f..78929fed61d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.12.0.0 LANGUAGES CXX) +project(Z3 VERSION 4.12.1.0 LANGUAGES CXX) ################################################################################ # Project version diff --git a/scripts/mk_project.py b/scripts/mk_project.py index a16913317f3..3e7386601d5 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 12, 0, 0) # express a default build version or pick up ci build version + set_version(4, 12, 1, 0) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index c93738e58c2..4e602797610 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,7 +1,7 @@ variables: Major: '4' Minor: '12' - Patch: '0' + Patch: '1' AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName) diff --git a/scripts/release.yml b/scripts/release.yml index 4421f515386..42a5bbcc6c0 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.12.0' + ReleaseVersion: '4.12.1' stages: diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index ad88f839fc8..115a3f53f6f 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -91,9 +91,7 @@ namespace euf { m_scopes.push_back(m_updates.size()); m_region.push_scope(); m_updates.push_back(update_record(m_new_th_eqs_qhead, update_record::new_th_eq_qhead())); - m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead())); } - SASSERT(m_new_lits_qhead <= m_new_lits.size()); SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size()); } @@ -156,12 +154,29 @@ namespace euf { } void egraph::add_literal(enode* n, enode* ante) { + /* if (n->bool_var() == sat::null_bool_var) return; - TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";); - m_new_lits.push_back(enode_pair(n, ante)); - m_updates.push_back(update_record(update_record::new_lit())); + */ if (!ante) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits; + if (!ante) + m_on_propagate_literal(n, ante); + else if (m.is_true(ante->get_expr()) || m.is_false(ante->get_expr())) { + for (enode* k : enode_class(n)) { + if (k != ante) { + //verbose_stream() << "eq: " << k->value() << " " <value() << "\n"; + m_on_propagate_literal(k, ante); + } + } + } + else { + for (enode* k : enode_class(n)) { + if (k->value() != ante->value()) { + //verbose_stream() << "eq: " << k->value() << " " <value() << "\n"; + m_on_propagate_literal(k, ante); + } + } + } } void egraph::new_diseq(enode* n) { @@ -339,7 +354,6 @@ namespace euf { num_scopes -= m_num_scopes; m_num_scopes = 0; - SASSERT(m_new_lits_qhead <= m_new_lits.size()); unsigned old_lim = m_scopes.size() - num_scopes; unsigned num_updates = m_scopes[old_lim]; auto undo_node = [&]() { @@ -378,18 +392,12 @@ namespace euf { SASSERT(p.r1->get_th_var(p.m_th_id) != null_theory_var); p.r1->replace_th_var(p.m_old_th_var, p.m_th_id); break; - case update_record::tag_t::is_new_lit: - m_new_lits.pop_back(); - break; case update_record::tag_t::is_new_th_eq: m_new_th_eqs.pop_back(); break; case update_record::tag_t::is_new_th_eq_qhead: m_new_th_eqs_qhead = p.qhead; break; - case update_record::tag_t::is_new_lits_qhead: - m_new_lits_qhead = p.qhead; - break; case update_record::tag_t::is_inconsistent: m_inconsistent = p.m_inconsistent; break; @@ -424,7 +432,6 @@ namespace euf { m_region.pop_scope(num_scopes); m_to_merge.reset(); - SASSERT(m_new_lits_qhead <= m_new_lits.size()); SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size()); // DEBUG_CODE(invariant();); @@ -461,12 +468,6 @@ namespace euf { std::swap(n1, n2); } - if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr()))) - add_literal(n1, r2); - if (r2->value() != l_undef && n1->value() == l_undef) - add_literal(n1, r2); - else if (r1->value() != l_undef && n2->value() == l_undef) - add_literal(n2, r1); remove_parents(r1); push_eq(r1, n1, r2->num_parents()); merge_justification(n1, n2, j); @@ -476,6 +477,13 @@ namespace euf { r2->inc_class_size(r1->class_size()); merge_th_eq(r1, r2); reinsert_parents(r1, r2); + if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr()))) + add_literal(n1, r2); + else if (n2->value() != l_undef && n1->value() != n2->value()) + add_literal(n1, n2); + else if (n1->value() != l_undef && n2->value() != n1->value()) + add_literal(n2, n1); + for (auto& cb : m_on_merge) cb(r2, r1); } @@ -565,7 +573,6 @@ namespace euf { bool egraph::propagate() { - SASSERT(m_new_lits_qhead <= m_new_lits.size()); SASSERT(m_num_scopes == 0 || m_to_merge.empty()); force_push(); for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) { @@ -574,7 +581,6 @@ namespace euf { } m_to_merge.reset(); return - (m_new_lits_qhead < m_new_lits.size()) || (m_new_th_eqs_qhead < m_new_th_eqs.size()) || inconsistent(); } @@ -851,7 +857,6 @@ namespace euf { std::ostream& egraph::display(std::ostream& out) const { out << "updates " << m_updates.size() << "\n"; - out << "newlits " << m_new_lits.size() << " qhead: " << m_new_lits_qhead << "\n"; out << "neweqs " << m_new_th_eqs.size() << " qhead: " << m_new_th_eqs_qhead << "\n"; m_table.display(out); unsigned max_args = 0; diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index 1686be38469..c1b9b784953 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -105,10 +105,8 @@ namespace euf { struct toggle_merge_tf {}; struct add_th_var {}; struct replace_th_var {}; - struct new_lit {}; struct new_th_eq {}; struct new_th_eq_qhead {}; - struct new_lits_qhead {}; struct inconsistent {}; struct value_assignment {}; struct lbl_hash {}; @@ -116,8 +114,8 @@ namespace euf { struct update_children {}; struct set_relevant {}; enum class tag_t { is_set_parent, is_add_node, is_toggle_cgc, is_toggle_merge_tf, is_update_children, - is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq, - is_lbl_hash, is_new_th_eq_qhead, is_new_lits_qhead, + is_add_th_var, is_replace_th_var, is_new_th_eq, + is_lbl_hash, is_new_th_eq_qhead, is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant }; tag_t tag; enode* r1; @@ -145,14 +143,10 @@ namespace euf { tag(tag_t::is_add_th_var), r1(n), n1(nullptr), r2_num_parents(id) {} update_record(enode* n, theory_id id, theory_var v, replace_th_var) : tag(tag_t::is_replace_th_var), r1(n), n1(nullptr), m_th_id(id), m_old_th_var(v) {} - update_record(new_lit) : - tag(tag_t::is_new_lit), r1(nullptr), n1(nullptr), r2_num_parents(0) {} update_record(new_th_eq) : tag(tag_t::is_new_th_eq), r1(nullptr), n1(nullptr), r2_num_parents(0) {} update_record(unsigned qh, new_th_eq_qhead): tag(tag_t::is_new_th_eq_qhead), r1(nullptr), n1(nullptr), qhead(qh) {} - update_record(unsigned qh, new_lits_qhead): - tag(tag_t::is_new_lits_qhead), r1(nullptr), n1(nullptr), qhead(qh) {} update_record(bool inc, inconsistent) : tag(tag_t::is_inconsistent), r1(nullptr), n1(nullptr), m_inconsistent(inc) {} update_record(enode* n, value_assignment) : @@ -187,9 +181,7 @@ namespace euf { enode *m_n1 = nullptr; enode *m_n2 = nullptr; justification m_justification; - unsigned m_new_lits_qhead = 0; unsigned m_new_th_eqs_qhead = 0; - svector m_new_lits; svector m_new_th_eqs; bool_vector m_th_propagates_diseqs; enode_vector m_todo; @@ -198,7 +190,8 @@ namespace euf { bool m_default_relevant = true; uint64_t m_congruence_timestamp = 0; - std::vector> m_on_merge; + std::vector> m_on_merge; + std::function m_on_propagate_literal; std::function m_on_make; std::function m_used_eq; std::function m_used_cc; @@ -290,11 +283,8 @@ namespace euf { is an equality consequence. */ void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq); - bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); } bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); } - enode_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; } th_eq get_th_eq() const { return m_new_th_eqs[m_new_th_eqs_qhead]; } - void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; } void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; } void set_lbl_hash(enode* n); @@ -311,6 +301,7 @@ namespace euf { void set_default_relevant(bool b) { m_default_relevant = b; } void set_on_merge(std::function& on_merge) { m_on_merge.push_back(on_merge); } + void set_on_propagate(std::function& on_propagate) { m_on_propagate_literal = on_propagate; } void set_on_make(std::function& on_make) { m_on_make = on_make; } void set_used_eq(std::function& used_eq) { m_used_eq = used_eq; } void set_used_cc(std::function& used_cc) { m_used_cc = used_cc; } diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index ef9263686c2..c3b5043a5a7 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -51,7 +51,6 @@ class dependent_expr_state { void freeze_recfun(); void freeze_lambda(); void freeze_terms(expr* term, bool only_as_array, ast_mark& visited); - void freeze(expr* term); void freeze(func_decl* f); struct thaw : public trail { unsigned sz; @@ -89,6 +88,7 @@ class dependent_expr_state { /** * Freeze internal functions */ + void freeze(expr* term); bool frozen(func_decl* f) const { return m_frozen.is_marked(f); } bool frozen(expr* f) const { return is_app(f) && m_frozen.is_marked(to_app(f)->get_decl()); } void freeze_suffix(); diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 567546e67b1..e3cfc5c6fec 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -23,11 +23,13 @@ Module Name: // substitutions that use variables from the dependent expressions. // TODO: add filters to skip sections of the trail that do not touch the current free variables. -void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st) { +void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& st) { ast_mark free_vars; scoped_ptr rp = mk_default_expr_replacer(m, false); - for (unsigned i = qhead; i < st.qtail(); ++i) + for (unsigned i = qhead; i < st.qtail(); ++i) add_vars(st[i], free_vars); + for (expr* a : assumptions) + add_vars(a, free_vars); for (auto& t : m_trail) { if (!t->m_active) @@ -63,7 +65,7 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st mrp.insert(head, t->m_def, t->m_dep); dependent_expr de(m, t->m_def, nullptr, t->m_dep); add_vars(de, free_vars); - + for (unsigned i = qhead; i < st.qtail(); ++i) { auto [f, p, dep1] = st[i](); expr_ref g(m); @@ -73,6 +75,15 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st if (f != g) st.update(i, dependent_expr(m, g, nullptr, dep2)); } + for (unsigned i = 0; i < assumptions.size(); ++i) { + expr* a = assumptions.get(i); + expr_ref g(m); + expr_dependency_ref dep(m); + mrp(a, nullptr, g, dep); + if (a != g) + assumptions[i] = g; + // ignore dep. + } continue; } @@ -103,7 +114,15 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n"); add_vars(d, free_vars); st.update(i, d); - } + } + + for (unsigned i = 0; i < assumptions.size(); ++i) { + expr* a = assumptions.get(i); + auto [g, dep2] = rp->replace_with_dep(a); + if (a != g) + assumptions[i] = g; + // ignore dep. + } } } diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 7f8c7dc8f09..4bc09e646a4 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -74,13 +74,17 @@ class model_reconstruction_trail { scoped_ptr_vector m_trail; unsigned m_trail_index = 0; - void add_vars(dependent_expr const& d, ast_mark& free_vars) { - for (expr* t : subterms::all(expr_ref(d.fml(), d.get_manager()))) + void add_vars(expr* e, ast_mark& free_vars) { + for (expr* t : subterms::all(expr_ref(e, m))) free_vars.mark(t, true); } + + void add_vars(dependent_expr const& d, ast_mark& free_vars) { + add_vars(d.fml(), free_vars); + } bool intersects(ast_mark const& free_vars, dependent_expr const& d) { - expr_ref term(d.fml(), d.get_manager()); + expr_ref term(d.fml(), m); auto iter = subterms::all(term); return any_of(iter, [&](expr* t) { return free_vars.is_marked(t); }); } @@ -126,7 +130,7 @@ class model_reconstruction_trail { * register a new depedent expression, update the trail * by removing substitutions that are not equivalence preserving. */ - void replay(unsigned qhead, dependent_expr_state& fmls); + void replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& fmls); /** * retrieve the current model converter corresponding to chaining substitutions from the trail. diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index fd970a44ac5..8060afcd7ac 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -70,7 +70,7 @@ class sat_smt_solver : public solver { return out; } void append(generic_model_converter& mc) { model_trail().append(mc); } - void replay(unsigned qhead) { m_reconstruction_trail.replay(qhead, *this); } + void replay(unsigned qhead, expr_ref_vector& assumptions) { m_reconstruction_trail.replay(qhead, assumptions, *this); } void flatten_suffix() override { expr_mark seen; unsigned j = qhead(); @@ -237,8 +237,10 @@ class sat_smt_solver : public solver { expr_ref_vector assumptions(m); for (unsigned i = 0; i < sz; ++i) assumptions.push_back(ensure_literal(_assumptions[i])); + for (expr* a : assumptions) + m_preprocess_state.freeze(a); TRACE("sat", tout << assumptions << "\n";); - lbool r = internalize_formulas(); + lbool r = internalize_formulas(assumptions); if (r != l_true) return r; @@ -271,7 +273,8 @@ class sat_smt_solver : public solver { void push() override { try { - internalize_formulas(); + expr_ref_vector none(m); + internalize_formulas(none); } catch (...) { push_internal(); @@ -430,7 +433,7 @@ class sat_smt_solver : public solver { } expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override { - lbool r = internalize_formulas(); + lbool r = internalize_formulas(vs); if (r != l_true) { IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n"); return expr_ref_vector(m); @@ -561,7 +564,8 @@ class sat_smt_solver : public solver { void convert_internalized() { m_solver.pop_to_base_level(); - internalize_formulas(); + expr_ref_vector none(m); + internalize_formulas(none); if (!is_internalized() || m_internalized_converted) return; sat2goal s2g; @@ -641,9 +645,9 @@ class sat_smt_solver : public solver { add_assumption(a); } - lbool internalize_formulas() { + lbool internalize_formulas(expr_ref_vector& assumptions) { - if (is_internalized()) + if (is_internalized() && assumptions.empty()) return l_true; unsigned qhead = m_preprocess_state.qhead(); @@ -651,7 +655,7 @@ class sat_smt_solver : public solver { m_internalized_converted = false; - m_preprocess_state.replay(qhead); + m_preprocess_state.replay(qhead, assumptions); m_preprocess.reduce(); if (!m.inc()) return l_undef; diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index c9b0430b584..8efb433ffaf 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -63,6 +63,11 @@ namespace euf { }; m_egraph.set_display_justification(disp); + std::function on_literal = [&](enode* n, enode* ante) { + propagate_literal(n, ante); + }; + m_egraph.set_on_propagate(on_literal); + if (m_relevancy.enabled()) { std::function on_merge = [&](enode* root, enode* other) { @@ -414,7 +419,6 @@ namespace euf { } bool propagated1 = false; if (m_egraph.propagate()) { - propagate_literals(); propagate_th_eqs(); propagated1 = true; } @@ -435,52 +439,52 @@ namespace euf { return propagated; } - void solver::propagate_literals() { - for (; m_egraph.has_literal() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_literal()) { - auto [n, ante] = m_egraph.get_literal(); - expr* e = n->get_expr(); - expr* a = nullptr, *b = nullptr; - bool_var v = n->bool_var(); - SASSERT(m.is_bool(e)); - size_t cnstr; - literal lit; - if (!ante) { - VERIFY(m.is_eq(e, a, b)); - cnstr = eq_constraint().to_index(); - lit = literal(v, false); - } - else { - // - // There are the following three cases for propagation of literals - // - // 1. n == ante is true from equallity, ante = true/false - // 2. n == ante is true from equality, value(ante) != l_undef - // 3. value(n) != l_undef, ante = true/false, merge_tf is set on n - // - lbool val = ante->value(); - if (val == l_undef) { - SASSERT(m.is_value(ante->get_expr())); - val = m.is_true(ante->get_expr()) ? l_true : l_false; - } - auto& c = lit_constraint(ante); - cnstr = c.to_index(); - lit = literal(v, val == l_false); - } - unsigned lvl = s().scope_lvl(); - - CTRACE("euf", s().value(lit) != l_true, tout << lit << " " << s().value(lit) << "@" << lvl << " " << mk_bounded_pp(a, m) << " = " << mk_bounded_pp(b, m) << "\n";); - if (s().value(lit) == l_false && m_ackerman && a && b) - m_ackerman->cg_conflict_eh(a, b); - switch (s().value(lit)) { - case l_true: - if (n->merge_tf() && !m.is_value(n->get_root()->get_expr())) - m_egraph.merge(n, ante, to_ptr(lit)); - break; - case l_undef: - case l_false: - s().assign(lit, sat::justification::mk_ext_justification(lvl, cnstr)); - break; + + void solver::propagate_literal(enode* n, enode* ante) { + expr* e = n->get_expr(); + expr* a = nullptr, *b = nullptr; + bool_var v = n->bool_var(); + if (v == sat::null_bool_var) + return; + SASSERT(m.is_bool(e)); + size_t cnstr; + literal lit; + if (!ante) { + VERIFY(m.is_eq(e, a, b)); + cnstr = eq_constraint().to_index(); + lit = literal(v, false); + } + else { + // + // There are the following three cases for propagation of literals + // + // 1. n == ante is true from equallity, ante = true/false + // 2. n == ante is true from equality, value(ante) != l_undef + // 3. value(n) != l_undef, ante = true/false, merge_tf is set on n + // + lbool val = ante->value(); + if (val == l_undef) { + SASSERT(m.is_value(ante->get_expr())); + val = m.is_true(ante->get_expr()) ? l_true : l_false; } + auto& c = lit_constraint(ante); + cnstr = c.to_index(); + lit = literal(v, val == l_false); + } + unsigned lvl = s().scope_lvl(); + + CTRACE("euf", s().value(lit) != l_true, tout << lit << " " << s().value(lit) << "@" << lvl << " " << mk_bounded_pp(a, m) << " = " << mk_bounded_pp(b, m) << "\n";); + if (s().value(lit) == l_false && m_ackerman && a && b) + m_ackerman->cg_conflict_eh(a, b); + switch (s().value(lit)) { + case l_true: + if (n->merge_tf() && !m.is_value(n->get_root()->get_expr())) + m_egraph.merge(n, ante, to_ptr(lit)); + break; + case l_undef: + case l_false: + s().assign(lit, sat::justification::mk_ext_justification(lvl, cnstr)); + break; } } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 675ec150099..96079fbec92 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -206,7 +206,7 @@ namespace euf { void validate_model(model& mdl); // solving - void propagate_literals(); + void propagate_literal(enode* n, enode* ante); void propagate_th_eqs(); bool is_self_propagated(th_eq const& e); void get_antecedents(literal l, constraint& j, literal_vector& r, bool probing); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 01e0aba7c81..d84535a9c86 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -866,6 +866,7 @@ namespace smt { SASSERT(curr != m_false_enode); bool_var v = enode2bool_var(curr); literal l(v, sign); + CTRACE("propagate", (get_assignment(l) != l_true), tout << enode_pp(curr, *this) << " " << l << "\n"); if (get_assignment(l) != l_true) assign(l, mk_justification(eq_root_propagation_justification(curr))); curr = curr->m_next; diff --git a/src/tactic/arith/arith_bounds_tactic.h b/src/tactic/arith/arith_bounds_tactic.h index 4606f61441d..014a6dde34c 100644 --- a/src/tactic/arith/arith_bounds_tactic.h +++ b/src/tactic/arith/arith_bounds_tactic.h @@ -27,6 +27,8 @@ Module Name: for assembling bounds, but it does not have a way to check for subsumption of atoms. +## Tactic arith-bounds + --*/ #pragma once #include "tactic/tactic.h" diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index eda079e4691..63455864d98 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -128,9 +128,8 @@ struct cofactor_elim_term_ite::imp { fr.m_first = false; bool visited = true; if (is_app(t)) { - unsigned num_args = to_app(t)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) - visit(to_app(t)->get_arg(i), form_ctx, visited); + for (expr* arg : *to_app(t)) + visit(arg, form_ctx, visited); } // ignoring quantifiers if (!visited) @@ -138,16 +137,13 @@ struct cofactor_elim_term_ite::imp { } if (is_app(t)) { - unsigned num_args = to_app(t)->get_num_args(); - unsigned i; - for (i = 0; i < num_args; i++) { - if (m_has_term_ite.is_marked(to_app(t)->get_arg(i))) + for (expr* arg : *to_app(t)) { + if (m_has_term_ite.is_marked(arg)) { + m_has_term_ite.mark(t); + TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); + save_candidate(t, form_ctx); break; - } - if (i < num_args) { - m_has_term_ite.mark(t); - TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); - save_candidate(t, form_ctx); + } } } else { @@ -284,16 +280,14 @@ struct cofactor_elim_term_ite::imp { } expr * best = nullptr; unsigned best_occs = 0; - obj_map::iterator it = occs.begin(); - obj_map::iterator end = occs.end(); - for (; it != end; ++it) { + for (auto const& [k, v] : occs) { if ((!best) || - (get_depth(it->m_key) < get_depth(best)) || - (get_depth(it->m_key) == get_depth(best) && it->m_value > best_occs) || + (get_depth(k) < get_depth(best)) || + (get_depth(k) == get_depth(best) && v > best_occs) || // break ties by giving preference to equalities - (get_depth(it->m_key) == get_depth(best) && it->m_value == best_occs && m.is_eq(it->m_key) && !m.is_eq(best))) { - best = it->m_key; - best_occs = it->m_value; + (get_depth(k) == get_depth(best) && v == best_occs && m.is_eq(k) && !m.is_eq(best))) { + best = k; + best_occs = v; } } visited.reset(); diff --git a/src/tactic/core/cofactor_term_ite_tactic.h b/src/tactic/core/cofactor_term_ite_tactic.h index e10a310f2ae..68568c8cef1 100644 --- a/src/tactic/core/cofactor_term_ite_tactic.h +++ b/src/tactic/core/cofactor_term_ite_tactic.h @@ -8,13 +8,22 @@ Module Name: Abstract: Wrap cofactor_elim_term_ite as a tactic. - Eliminate (ground) term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2012-02-20. -Revision History: +Tactic Documentation: + +## Tactic cofactor-term-ite + +### Short Description +Eliminate (ground) term if-then-else's using cofactors. +It hoists nested if-then-else expressions inside terms into the top level of the formula. + +### Notes + +* does not support proofs, does not support cores --*/ #pragma once diff --git a/src/tactic/core/euf_completion_tactic.h b/src/tactic/core/euf_completion_tactic.h index 36511bbe343..a1c9166ea23 100644 --- a/src/tactic/core/euf_completion_tactic.h +++ b/src/tactic/core/euf_completion_tactic.h @@ -13,6 +13,22 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-10-30 +Tactic Documentation: + +## Tactic euf-completion + +### Short Description + +Uses the ground equalities as a rewrite system. The formulas are simplified +using the rewrite system. + +### Long Description + +The tactic uses congruence closure to represent and orient the rewrite system. Equalities from the formula +are inserted in the an E-graph (congruence closure structure) and then a representative that is most shallow +is extracted. + + --*/ #pragma once diff --git a/src/tactic/core/reduce_args_tactic.h b/src/tactic/core/reduce_args_tactic.h index 615b4d70fc8..749aadd9c06 100644 --- a/src/tactic/core/reduce_args_tactic.h +++ b/src/tactic/core/reduce_args_tactic.h @@ -34,31 +34,10 @@ Thus, we replace the $f(t_1, t_2)$ with Since $f_a$, $f_b$, $f_c$ are new symbols, satisfiability is preserved. This transformation is very similar in spirit to the Ackermman's reduction. +For each function `f` and argument position of `f` it checks if all occurrences of `f` uses a value at position `i`. +The values may be different, but all occurrences have to be values for the reduction to be applicable. +It creates a fresh function for each of the different values at position `i`. -This transformation should work in the following way: - -``` - 1- Create a mapping decl2arg_map from declarations to tuples of booleans, an entry [f -> (true, false, true)] - means that f is a declaration with 3 arguments where the first and third arguments are always values. - 2- Traverse the formula and populate the mapping. - For each function application f(t1, ..., tn) do - a) Create a boolean tuple (is_value(t1), ..., is_value(tn)) and do - the logical-and with the tuple that is already in the mapping. If there is no such tuple - in the mapping, we just add a new entry. - - If all entries are false-tuples, then there is nothing to be done. The transformation is not applicable. - - Now, we create a mapping decl2new_decl from (decl, val_1, ..., val_n) to decls. Note that, n may be different for each entry, - but it is the same for the same declaration. - For example, suppose we have [f -> (true, false, true)] in decl2arg_map, - and applications f(1, a, 2), f(1, b, 2), f(1, b, 3), f(2, b, 3), f(2, c, 3) in the formula. - Then, decl2arg_map would contain - (f, 1, 2) -> f_1_2 - (f, 1, 3) -> f_1_3 - (f, 2, 3) -> f_2_3 - where f_1_2, f_1_3 and f_2_3 are new function symbols. - Using the new map, we can replace the occurrences of f. -``` ### Example diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index 667d53626b9..bd2f58b442e 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -865,11 +865,11 @@ class tseitin_cnf_tactic : public tactic { void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); - r.insert("common_patterns", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by identifing commonly used patterns"); - r.insert("distributivity", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by applying distributivity over unshared subformulas"); - r.insert("distributivity_blowup", CPK_UINT, "(default: 32) maximum overhead for applying distributivity during CNF encoding"); - r.insert("ite_chaing", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by identifing if-then-else chains"); - r.insert("ite_extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas"); + r.insert("common_patterns", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by identifing commonly used patterns", "true"); + r.insert("distributivity", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by applying distributivity over unshared subformulas", "true"); + r.insert("distributivity_blowup", CPK_UINT, "maximum overhead for applying distributivity during CNF encoding", "32"); + r.insert("ite_chaing", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by identifing if-then-else chains", "true"); + r.insert("ite_extra", CPK_BOOL, "add redundant clauses (that improve unit propagation) when encoding if-then-else formulas", "true"); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { diff --git a/src/tactic/fpa/fpa2bv_tactic.h b/src/tactic/fpa/fpa2bv_tactic.h index 0845aafa200..57b6d498529 100644 --- a/src/tactic/fpa/fpa2bv_tactic.h +++ b/src/tactic/fpa/fpa2bv_tactic.h @@ -5,15 +5,16 @@ Module Name: fpa2bv_tactic.h -Abstract: - - Tactic that converts floating points to bit-vectors - Author: Christoph (cwinter) 2012-02-09 -Notes: +Tactic Documentation: + +## Tactic fpa2bv + +### Short Description +Converts floating points to bit-vector representation. --*/ #pragma once diff --git a/src/tactic/fpa/qffp_tactic.h b/src/tactic/fpa/qffp_tactic.h index e4c8eb38699..fb1a46afd5f 100644 --- a/src/tactic/fpa/qffp_tactic.h +++ b/src/tactic/fpa/qffp_tactic.h @@ -13,8 +13,17 @@ Module Name: Christoph (cwinter) 2012-01-16 -Notes: +Tactic Documentation: +## Tactic qffp + +### Short Description +Tactic for QF_FP formulas + +## Tactic qffpbv + +### Short Description +Tactic for QF_FPBV formulas --*/ #pragma once diff --git a/src/tactic/fpa/qffplra_tactic.h b/src/tactic/fpa/qffplra_tactic.h index af862db2997..543e6f23afe 100644 --- a/src/tactic/fpa/qffplra_tactic.h +++ b/src/tactic/fpa/qffplra_tactic.h @@ -14,7 +14,8 @@ Module Name: Christoph (cwinter) 2018-04-24 -Notes: + +## Tactic qffplra --*/ From fcc1bb5da88999ec2ecfb316bca54660b0f46d6d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Jan 2023 14:08:41 -0800 Subject: [PATCH 294/597] updated release notes --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a72aae7e783..9164579e870 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -11,6 +11,10 @@ Version 4.next - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.12.1 +============== +- change macos build to use explicit reference to Macos version 11. Hosted builds are migrating to macos-12 and it broke a user Issue #6539. + Version 4.12.0 ============== - add clause logging API. From 3012293c35eadbfd73e5b94adbe50b0cc44ffb83 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Jan 2023 19:10:26 -0800 Subject: [PATCH 295/597] update release script Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/release.yml b/scripts/release.yml index 42a5bbcc6c0..30378fc6bb6 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -17,7 +17,7 @@ stages: - job: MacBuild displayName: "macOS Build" pool: - vmImage: "macOS-latest" + vmImage: "macOS-11" steps: - task: PythonScript@0 displayName: Build @@ -46,7 +46,7 @@ stages: - job: MacBuildArm64 displayName: "macOS ARM64 Build" pool: - vmImage: "macOS-latest" + vmImage: "macOS-11" steps: - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 --os=osx-11.0 - script: git clone https://github.com/z3prover/z3test z3test From 9290de822394af0a1efb2bb23b1ff8ee17783b1b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Jan 2023 07:57:19 -0800 Subject: [PATCH 296/597] make euf-egraph resilient to when there are no consumers to literal propagation. Signed-off-by: Nikolaj Bjorner --- src/ast/euf/euf_egraph.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 115a3f53f6f..7939c23f2b5 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -154,10 +154,8 @@ namespace euf { } void egraph::add_literal(enode* n, enode* ante) { - /* - if (n->bool_var() == sat::null_bool_var) + if (!m_on_propagate_literal) return; - */ if (!ante) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits; if (!ante) m_on_propagate_literal(n, ante); From 59c41bd8ce19f94f0ea01d6faee86d9ee63ee113 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Jan 2023 07:59:47 -0800 Subject: [PATCH 297/597] increment release version Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- RELEASE_NOTES.md | 3 +++ scripts/mk_project.py | 2 +- scripts/nightly.yaml | 2 +- scripts/release.yml | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78929fed61d..9299efe250c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.12.1.0 LANGUAGES CXX) +project(Z3 VERSION 4.12.2.0 LANGUAGES CXX) ################################################################################ # Project version diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9164579e870..add32c445f3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -11,6 +11,9 @@ Version 4.next - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.12.2 +============== + Version 4.12.1 ============== - change macos build to use explicit reference to Macos version 11. Hosted builds are migrating to macos-12 and it broke a user Issue #6539. diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 3e7386601d5..20bce7c13f7 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 12, 1, 0) # express a default build version or pick up ci build version + set_version(4, 12, 2, 0) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 4e602797610..4925ce45dfd 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,7 +1,7 @@ variables: Major: '4' Minor: '12' - Patch: '1' + Patch: '2' AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName) diff --git a/scripts/release.yml b/scripts/release.yml index 30378fc6bb6..10ae2457741 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.12.1' + ReleaseVersion: '4.12.2' stages: From 523a3f34b09adebabf9f4a830d78dd007d04cd2a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Jan 2023 17:27:07 -0800 Subject: [PATCH 298/597] change to manylinux2014 in setup.py Signed-off-by: Nikolaj Bjorner --- src/api/python/setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/python/setup.py b/src/api/python/setup.py index f78963344a8..81c472232a4 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -277,7 +277,7 @@ def run(self): # linux builds should be built in the centos 5 vm for maximum compatibility # see https://github.com/pypa/manylinux # see also https://github.com/angr/angr-dev/blob/master/admin/bdist.py - plat_name = 'manylinux1_' + platform.machine() + plat_name = 'manylinux2014_' + platform.machine() elif 'mingw' in name: if platform.architecture()[0] == '64bit': plat_name = 'win_amd64' @@ -296,9 +296,9 @@ def run(self): ) elif distos == 'glibc': if arch == 'x64': - plat_name = 'manylinux1_x86_64' + plat_name = 'manylinux2014_x86_64' else: - plat_name = 'manylinux1_i686' + plat_name = 'manylinux2014_i686' elif distos == 'linux' and os_id == 'alpine': if arch == 'x64': plat_name = 'musllinux_1_1_x86_64' From d11e5c8ca68a3691088360a927c6bc3442892100 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Jan 2023 19:02:43 -0800 Subject: [PATCH 299/597] address compiler warnings, and user question #6544 --- src/qe/lite/qe_lite.cpp | 6 +++--- src/sat/sat_drat.cpp | 2 +- src/sat/sat_integrity_checker.cpp | 2 +- src/sat/sat_local_search.cpp | 2 -- src/sat/sat_model_converter.cpp | 1 + src/sat/sat_simplifier.cpp | 3 --- src/sat/sat_solver.cpp | 5 +---- src/sat/sat_solver/sat_smt_solver.cpp | 2 +- src/sat/smt/arith_diagnostics.cpp | 2 -- src/sat/smt/pb_solver.cpp | 7 ------- src/sat/smt/q_eval.cpp | 2 -- src/sat/smt/q_mam.cpp | 2 -- src/sat/smt/tseitin_theory_checker.cpp | 2 +- src/tactic/core/cofactor_elim_term_ite.cpp | 8 ++------ src/tactic/fd_solver/smtfd_solver.cpp | 20 -------------------- src/tactic/smtlogics/qfnia_tactic.cpp | 2 -- 16 files changed, 11 insertions(+), 57 deletions(-) diff --git a/src/qe/lite/qe_lite.cpp b/src/qe/lite/qe_lite.cpp index 75c90ee4d41..90ea3576963 100644 --- a/src/qe/lite/qe_lite.cpp +++ b/src/qe/lite/qe_lite.cpp @@ -2165,7 +2165,6 @@ namespace fm { subsume(); var_vector candidates; sort_candidates(candidates); - unsigned eliminated = 0; unsigned num = candidates.size(); for (unsigned i = 0; i < num; i++) { @@ -2173,8 +2172,9 @@ namespace fm { if (m_counter > m_fm_limit) break; m_counter++; - if (try_eliminate(candidates[i])) - eliminated++; + if (try_eliminate(candidates[i])) { + + } if (m_inconsistent) { m_new_fmls.reset(); m_new_fmls.push_back(m.mk_false()); diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp index 46a60815193..836a76c96b0 100644 --- a/src/sat/sat_drat.cpp +++ b/src/sat/sat_drat.cpp @@ -372,7 +372,7 @@ namespace sat { } } CTRACE("sat_drat", num_true == 0 && num_undef == 1, display(tout);); - SASSERT(num_true != 0 || num_undef != 1); + VERIFY(num_true != 0 || num_undef != 1); } } } diff --git a/src/sat/sat_integrity_checker.cpp b/src/sat/sat_integrity_checker.cpp index 223e58677ba..031ce92029c 100644 --- a/src/sat/sat_integrity_checker.cpp +++ b/src/sat/sat_integrity_checker.cpp @@ -105,7 +105,7 @@ namespace sat { if (c.frozen()) num_frozen++; } - SASSERT(num_frozen == s.m_num_frozen); + VERIFY(num_frozen == s.m_num_frozen); return check_clauses(s.begin_learned(), s.end_learned()); } diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index 0b3c2f7c966..61ddd13d872 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -580,7 +580,6 @@ namespace sat { bool_var v = null_bool_var; unsigned num_unsat = m_unsat_stack.size(); constraint const& c = m_constraints[m_unsat_stack[m_rand() % num_unsat]]; - unsigned reflipped = 0; bool is_core = m_unsat_stack.size() <= 10; if (m_rand() % 10000 <= m_noise) { // take this branch with 98% probability. @@ -684,7 +683,6 @@ namespace sat { } if (false && is_core && c.m_k < constraint_value(c)) { - ++reflipped; goto reflip; } } diff --git a/src/sat/sat_model_converter.cpp b/src/sat/sat_model_converter.cpp index 656270f16ab..27cc6823a36 100644 --- a/src/sat/sat_model_converter.cpp +++ b/src/sat/sat_model_converter.cpp @@ -167,6 +167,7 @@ namespace sat { // end of clause if (!sat) { TRACE("sat_model_bug", tout << "failed eliminated: " << mk_lits_pp(static_cast(it - itbegin), itbegin) << "\n";); + (void)itbegin; ok = false; } sat = false; diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 9ba53609173..56b81604d99 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -288,16 +288,13 @@ namespace sat { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); - unsigned nm = 0; for (; it != end; ++it) { clause & c = *(*it); if (learned && !c.is_learned()) { s.m_clauses.push_back(&c); - ++nm; } else if (!learned && c.is_learned()) { s.m_learned.push_back(&c); - ++nm; } else { *it2 = *it; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 0e5d7aa73e1..7cfa102b7b3 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -4284,7 +4284,7 @@ namespace sat { lbool solver::get_bounded_consequences(literal_vector const& asms, bool_var_vector const& vars, vector& conseq) { bool_var_set unfixed_vars; - unsigned num_units = 0, num_iterations = 0; + unsigned num_units = 0; for (bool_var v : vars) { unfixed_vars.insert(v); } @@ -4316,7 +4316,6 @@ namespace sat { } while (true) { - ++num_iterations; SASSERT(!inconsistent()); lbool r = bounded_search(); @@ -4379,7 +4378,6 @@ namespace sat { checkpoint(); unsigned num_resolves = 0; unsigned num_fixed = 0; - unsigned num_assigned = 0; lbool is_sat = l_true; for (literal lit : unfixed_lits) { if (value(lit) != l_undef) { @@ -4390,7 +4388,6 @@ namespace sat { continue; } push(); - ++num_assigned; assign_scoped(~lit); propagate(false); while (inconsistent()) { diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 8060afcd7ac..8d99153dac0 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -174,10 +174,10 @@ class sat_smt_solver : public solver { public: sat_smt_solver(ast_manager& m, params_ref const& p): solver(m), + m_solver(p, m.limit()), m_preprocess_state(*this), m_preprocess(m, p, m_preprocess_state), m_trail(m_preprocess_state.m_trail), - m_solver(p, m.limit()), m_dep(m, m_trail), m_assumptions(m), m_core(m), m_ors(m), m_aux_fmls(m), m_internalized_fmls(m), m_map(m), diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 5fd5de07d0a..8ead3d980a5 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -83,9 +83,7 @@ namespace arith { } void solver::explain_assumptions(lp::explanation const& e) { - unsigned i = 0; for (auto const & ev : e) { - ++i; auto idx = ev.ci(); if (UINT_MAX == idx) continue; diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index ade48c42bfd..fed6aaf7cf7 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2791,7 +2791,6 @@ namespace pb { bool solver::subsumes(card& c1, card& c2, literal_vector & comp) { if (c2.lit() != sat::null_literal) return false; - unsigned c2_exclusive = 0; unsigned common = 0; comp.reset(); for (literal l : c2) { @@ -2801,9 +2800,6 @@ namespace pb { else if (is_visited(~l)) { comp.push_back(l); } - else { - ++c2_exclusive; - } } unsigned c1_exclusive = c1.size() - common - comp.size(); @@ -3405,16 +3401,13 @@ namespace pb { unsigned slack = 0; unsigned max_level = 0; - unsigned num_max_level = 0; for (wliteral wl : m_wlits) { if (value(wl.second) != l_false) ++slack; unsigned level = lvl(wl.second); if (level > max_level) { max_level = level; - num_max_level = 1; } else if (max_level == level) { - ++num_max_level; } } if (m_overflow) diff --git a/src/sat/smt/q_eval.cpp b/src/sat/smt/q_eval.cpp index ae79dbeeec8..35bdc428528 100644 --- a/src/sat/smt/q_eval.cpp +++ b/src/sat/smt/q_eval.cpp @@ -124,11 +124,9 @@ namespace q { std::swap(t, s); } unsigned sz = evidence.size(); - unsigned count = 0; for (euf::enode* t1 : euf::enode_class(tn)) { if (!t1->is_cgr()) continue; - ++count; expr* t2 = t1->get_expr(); if ((c = compare_rec(n, binding, s, t2, evidence), c != l_undef)) { evidence.push_back(euf::enode_pair(t1, tn)); diff --git a/src/sat/smt/q_mam.cpp b/src/sat/smt/q_mam.cpp index 91f19806a88..1c356f9f22f 100644 --- a/src/sat/smt/q_mam.cpp +++ b/src/sat/smt/q_mam.cpp @@ -3797,7 +3797,6 @@ namespace q { } void rematch(bool use_irrelevant) override { - unsigned lbl = 0; for (auto * t : m_trees) { if (t) { m_interpreter.init(t); @@ -3807,7 +3806,6 @@ namespace q { m_interpreter.execute_core(t, curr); } } - ++lbl; } } diff --git a/src/sat/smt/tseitin_theory_checker.cpp b/src/sat/smt/tseitin_theory_checker.cpp index a15d0277b61..74f4e55b000 100644 --- a/src/sat/smt/tseitin_theory_checker.cpp +++ b/src/sat/smt/tseitin_theory_checker.cpp @@ -190,7 +190,7 @@ namespace tseitin { complement_mark(arg); if (is_marked(x) && is_complement(y)) return true; - if (is_marked(y) & is_complement(x)) + if (is_marked(y) && is_complement(x)) return true; } diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index 63455864d98..2da133409b2 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -438,7 +438,6 @@ struct cofactor_elim_term_ite::imp { if (m_cache.find(s, t)) return true; - unsigned step = 0; TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(s, m) << "\n";); expr_ref curr(m); curr = s; @@ -451,7 +450,6 @@ struct cofactor_elim_term_ite::imp { t = curr.get(); return true; } - step++; expr_ref pos_cofactor(m); expr_ref neg_cofactor(m); m_cofactor.set_cofactor_atom(c); @@ -461,7 +459,7 @@ struct cofactor_elim_term_ite::imp { m_cofactor.set_cofactor_atom(neg_c); m_cofactor(curr, neg_cofactor); curr = m.mk_ite(c, pos_cofactor, neg_cofactor); - TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); + TRACE("cofactor", tout << "cofactor_ite step\n" << mk_ismt2_pp(curr, m) << "\n";); } } return false; @@ -515,7 +513,6 @@ struct cofactor_elim_term_ite::imp { } void cofactor(expr * t, expr_ref & r) { - unsigned step = 0; TRACE("cofactor", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";); expr_ref curr(m); curr = t; @@ -526,7 +523,6 @@ struct cofactor_elim_term_ite::imp { r = curr.get(); return; } - step++; expr_ref pos_cofactor(m); expr_ref neg_cofactor(m); m_cofactor.set_cofactor_atom(c); @@ -548,7 +544,7 @@ struct cofactor_elim_term_ite::imp { curr = m.mk_ite(c, pos_cofactor, neg_cofactor); } TRACE("cofactor", - tout << "cofactor_ite step: " << step << "\n"; + tout << "cofactor_ite step\n"; tout << "cofactor: " << mk_ismt2_pp(c, m) << "\n"; tout << mk_ismt2_pp(curr, m) << "\n";); } diff --git a/src/tactic/fd_solver/smtfd_solver.cpp b/src/tactic/fd_solver/smtfd_solver.cpp index 5676c6ee80c..01370812f75 100644 --- a/src/tactic/fd_solver/smtfd_solver.cpp +++ b/src/tactic/fd_solver/smtfd_solver.cpp @@ -1040,11 +1040,6 @@ namespace smtfd { // A[j] = w: i = j or T[j] = A[j] // void reconcile_stores(app* t, expr* vT, table& tT, expr* vA, table& tA) { - unsigned r = 0; - //if (get_lambda(vA) <= 1) { - // return; - //} - //std::cout << get_lambda(vA) << " " << get_lambda(vT) << "\n"; inc_lambda(vT); for (auto& fA : tA) { f_app fT; @@ -1056,23 +1051,8 @@ namespace smtfd { } if (!tT.find(fA, fT) || (value_of(fA) != value_of(fT) && !eq(m_vargs, fA))) { add_select_store_axiom(t, fA); - ++r; } } -#if 0 - // only up-propagation really needed. - for (auto& fT : tT) { - f_app fA; - if (m_context.at_max()) { - break; - } - if (!tA.find(fT, fA) && t->get_sort() == m.get_sort(fT.m_t->get_arg(0))) { - TRACE("smtfd", tout << "not found\n";); - add_select_store_axiom(t, fT); - ++r; - } - } -#endif } void add_select_store_axiom(app* t, f_app& f) { diff --git a/src/tactic/smtlogics/qfnia_tactic.cpp b/src/tactic/smtlogics/qfnia_tactic.cpp index cf56cced28b..3dd66606df6 100644 --- a/src/tactic/smtlogics/qfnia_tactic.cpp +++ b/src/tactic/smtlogics/qfnia_tactic.cpp @@ -95,8 +95,6 @@ static tactic * mk_qfnia_sat_solver(ast_manager & m, params_ref const & p) { } static tactic * mk_qfnia_nlsat_solver(ast_manager & m, params_ref const & p) { - params_ref nia2sat_p = p; - nia2sat_p.set_uint("nla2bv_max_bv_size", 64); params_ref simp_p = p; simp_p.set_bool("som", true); // expand into sums of monomials simp_p.set_bool("factor", false); From f3d6856736cf86ad8e790f768ae6d725f6bba8fb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Jan 2023 20:24:31 -0800 Subject: [PATCH 300/597] remove msf example, add option to make model converter not reduce models Signed-off-by: Nikolaj Bjorner --- examples/msf/README | 20 - .../App.config | 60 --- .../Properties/AssemblyInfo.cs | 36 -- .../ServiceTests.cs | 92 ----- .../SolverFoundation.Plugin.Z3.Tests.csproj | 70 ---- .../SolverTests.cs | 138 ------- .../SolverFoundation.Plugin.Z3/AbortWorker.cs | 98 ----- .../msf/SolverFoundation.Plugin.Z3/App.config | 60 --- .../Properties/AssemblyInfo.cs | 36 -- .../SolverFoundation.Plugin.Z3.csproj | 149 ------- .../msf/SolverFoundation.Plugin.Z3/Utils.cs | 130 ------ .../Z3BaseDirective.cs | 107 ----- .../Z3BaseParams.cs | 109 ----- .../Z3BaseSolver.cs | 387 ----------------- .../Z3MILPDirective.cs | 15 - .../Z3MILPParams.cs | 25 -- .../Z3MILPSolver.cs | 236 ----------- .../Z3TermDirective.cs | 15 - .../Z3TermParams.cs | 23 -- .../Z3TermSolver.cs | 388 ------------------ examples/msf/Validator/App.config | 60 --- ...crosoftSolverFoundationForExcel.dll.config | 58 --- examples/msf/Validator/Program.cs | 200 --------- .../msf/Validator/Properties/AssemblyInfo.cs | 36 -- examples/msf/Validator/Validator.csproj | 123 ------ examples/msf/Z3MSFPlugin.sln | 125 ------ .../converters/generic_model_converter.cpp | 4 +- src/ast/converters/model_converter.h | 7 +- src/smt/theory_array_base.cpp | 2 +- 29 files changed, 8 insertions(+), 2801 deletions(-) delete mode 100644 examples/msf/README delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/App.config delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Utils.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs delete mode 100644 examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs delete mode 100644 examples/msf/Validator/App.config delete mode 100644 examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config delete mode 100644 examples/msf/Validator/Program.cs delete mode 100644 examples/msf/Validator/Properties/AssemblyInfo.cs delete mode 100644 examples/msf/Validator/Validator.csproj delete mode 100644 examples/msf/Z3MSFPlugin.sln diff --git a/examples/msf/README b/examples/msf/README deleted file mode 100644 index d6e56f72f2f..00000000000 --- a/examples/msf/README +++ /dev/null @@ -1,20 +0,0 @@ -In order to use Z3 MSF plugin, follow the following steps: -1. Compile latest Z3 .NET API (from any branch consisting of opt features) and copy 'libz3.dll' and 'Microsoft.Z3.dll' to the folder of 'Z3MSFPlugin.sln'. -2. Retrieve 'Microsoft.Solver.Foundation.dll' from http://archive.msdn.microsoft.com/solverfoundation/Release/ProjectReleases.aspx?ReleaseId=1799, - preferably using DLL only version. Copy 'Microsoft.Solver.Foundation.dll' to the folder of 'Z3MSFPlugin.sln' -3. Build 'Z3MSFPlugin.sln'. Note that you have to compile using x86 target for Microsoft.Z3.dll 32-bit and x64 target for Microsoft.Z3.dll 64-bit. - -The solution consists of a plugin project, a test project with a few simple test cases and a command line projects for external OML, MPS and SMPS models. -To retrieve SMT2 models which are native to Z3, set the logging file in corresponding directives or solver params e.g. - - var p = new Z3MILPParams(); - p.SMT2LogFile = "model.smt2"; - -For more details, check out the commands in 'Validator\Program.cs'. - -Enjoy! - - - - - \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config b/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config deleted file mode 100644 index 75e2872f15a..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config +++ /dev/null @@ -1,60 +0,0 @@ - - - -
- - - - - - - - - - - - - - - - diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs b/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index b58f97eda99..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("SolverFoundation.Plugin.Z3.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SolverFoundation.Plugin.Z3.Tests")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("27657eee-ca7b-4996-a905-86a3f4584988")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs b/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs deleted file mode 100644 index 196f892457c..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs +++ /dev/null @@ -1,92 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Microsoft.SolverFoundation.Common; -using Microsoft.SolverFoundation.Solvers; -using Microsoft.SolverFoundation.Services; -using Microsoft.SolverFoundation.Plugin.Z3; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.SolverFoundation.Plugin.Z3.Tests -{ - [TestClass] - public class ServiceTests - { - [TestInitialize] - public void SetUp() - { - SolverContext context = SolverContext.GetContext(); - context.ClearModel(); - } - - private void TestService1(Directive directive) - { - SolverContext context = SolverContext.GetContext(); - Model model = context.CreateModel(); - - Decision x1 = new Decision(Domain.RealRange(0, 2), "x1"); - Decision x2 = new Decision(Domain.RealRange(0, 2), "x2"); - - Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); - - model.AddDecisions(x1, x2, z); - - model.AddConstraint("Row0", x1 - z <= 1); - model.AddConstraint("Row1", x2 + z <= 2); - - Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); - - Solution solution = context.Solve(directive); - Assert.IsTrue(goal.ToInt32() == 3); - context.ClearModel(); - } - - private void TestService2(Directive directive) - { - SolverContext context = SolverContext.GetContext(); - Model model = context.CreateModel(); - - Decision x1 = new Decision(Domain.RealNonnegative, "x1"); - Decision x2 = new Decision(Domain.RealNonnegative, "x2"); - - Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); - - Rational M = 100; - - model.AddDecisions(x1, x2, z); - - model.AddConstraint("Row0", x1 + x2 >= 1); - model.AddConstraint("Row1", x1 - z * 100 <= 0); - model.AddConstraint("Row2", x2 + z * 100 <= 100); - - Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); - - Solution solution = context.Solve(directive); - Assert.IsTrue(goal.ToInt32() == 100); - context.ClearModel(); - } - - [TestMethod] - public void TestService1() - { - TestService1(new Z3MILPDirective()); - TestService1(new Z3TermDirective()); - } - - [TestMethod] - public void TestService2() - { - TestService2(new Z3MILPDirective()); - TestService2(new Z3TermDirective()); - } - - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj b/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj deleted file mode 100644 index 24cecfa10f3..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Debug - AnyCPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B} - Library - Properties - Microsoft.SolverFoundation.Plugin.Z3.Tests - SolverFoundation.Plugin.Z3.Tests - v4.0 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - x86 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - x86 - - - - ..\Microsoft.Solver.Foundation.dll - - - - - - - - - - - - - - - - - - - - - {7340e664-f648-4ff7-89b2-f4da424996d3} - SolverFoundation.Plugin.Z3 - - - - - \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs b/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs deleted file mode 100644 index 4913c0f81e7..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs +++ /dev/null @@ -1,138 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Microsoft.SolverFoundation.Common; -using Microsoft.SolverFoundation.Solvers; -using Microsoft.SolverFoundation.Services; -using Microsoft.SolverFoundation.Plugin.Z3; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.SolverFoundation.Plugin.Z3.Tests -{ - [TestClass] - public class SolverTests - { - [TestMethod] - public void TestMILPSolver1() - { - var solver = new Z3MILPSolver(); - int goal; - - solver.AddRow("goal", out goal); - int x1, x2, z; - - // 0 <= x1 <= 2 - solver.AddVariable("x1", out x1); - solver.SetBounds(x1, 0, 2); - - // 0 <= x2 <= 2 - solver.AddVariable("x2", out x2); - solver.SetBounds(x2, 0, 2); - - // z is an integer in [0,1] - solver.AddVariable("z", out z); - solver.SetIntegrality(z, true); - solver.SetBounds(z, 0, 1); - - //max x1 + x2 - solver.SetCoefficient(goal, x1, 1); - solver.SetCoefficient(goal, x2, 1); - solver.AddGoal(goal, 1, false); - - // 0 <= x1 -z <= 1 - int row1; - solver.AddRow("rowI1", out row1); - solver.SetBounds(row1, 0, 1); - solver.SetCoefficient(row1, x1, 1); - solver.SetCoefficient(row1, z, -1); - - // 0 <= x2 + z <= 2 - int row2; - solver.AddRow("rowI2", out row2); - solver.SetBounds(row2, 0, 2); - solver.SetCoefficient(row2, x2, 1); - solver.SetCoefficient(row2, z, 1); - - var p = new Z3MILPParams(); - solver.Solve(p); - - Assert.IsTrue(solver.Result == LinearResult.Optimal); - Assert.AreEqual(solver.GetValue(x1), 2 * Rational.One); - Assert.AreEqual(solver.GetValue(x2), Rational.One); - Assert.AreEqual(solver.GetValue(z), Rational.One); - Assert.AreEqual(solver.GetValue(goal), 3 * Rational.One); - } - - [TestMethod] - public void TestMILPSolver2() - { - var solver = new Z3MILPSolver(); - int goal, extraGoal; - - Rational M = 100; - solver.AddRow("goal", out goal); - int x1, x2, z; - - // 0 <= x1 <= 100 - solver.AddVariable("x1", out x1); - solver.SetBounds(x1, 0, M); - - // 0 <= x2 <= 100 - solver.AddVariable("x2", out x2); - solver.SetBounds(x2, 0, M); - - // z is an integer in [0,1] - solver.AddVariable("z", out z); - solver.SetIntegrality(z, true); - solver.SetBounds(z, 0, 1); - - solver.SetCoefficient(goal, x1, 1); - solver.SetCoefficient(goal, x2, 2); - solver.AddGoal(goal, 1, false); - - solver.AddRow("extraGoal", out extraGoal); - - solver.SetCoefficient(extraGoal, x1, 2); - solver.SetCoefficient(extraGoal, x2, 1); - solver.AddGoal(extraGoal, 2, false); - - // x1 + x2 >= 1 - int row; - solver.AddRow("row", out row); - solver.SetBounds(row, 1, Rational.PositiveInfinity); - solver.SetCoefficient(row, x1, 1); - solver.SetCoefficient(row, x2, 1); - - - // x1 - M*z <= 0 - int row1; - solver.AddRow("rowI1", out row1); - solver.SetBounds(row1, Rational.NegativeInfinity, 0); - solver.SetCoefficient(row1, x1, 1); - solver.SetCoefficient(row1, z, -M); - - // x2 - M* (1-z) <= 0 - int row2; - solver.AddRow("rowI2", out row2); - solver.SetBounds(row2, Rational.NegativeInfinity, M); - solver.SetCoefficient(row2, x2, 1); - solver.SetCoefficient(row2, z, M); - - var p = new Z3MILPParams(); - p.OptKind = OptimizationKind.BoundingBox; - - solver.Solve(p); - Assert.IsTrue(solver.Result == LinearResult.Optimal); - Assert.AreEqual(solver.GetValue(goal), 200 * Rational.One); - Assert.AreEqual(solver.GetValue(extraGoal), 200 * Rational.One); - } - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs b/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs deleted file mode 100644 index 6ce66fa8fbc..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs +++ /dev/null @@ -1,98 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using Microsoft.Z3; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - /// - /// Thread that will wait until the query abort function returns true or - /// the stop method is called. If the abort function returns true at some - /// point it will issue a softCancel() call to Z3. - /// - internal class AbortWorker - { - #region Private Members - - /// The Z3 solver - private Microsoft.Z3.Context _context; - /// The abort function to use to check if we are aborted - private Func _QueryAbortFunction; - /// Flag indicating that worker should stop - private bool _stop = false; - /// Flag indicating that we have been sent an abort signal - private bool _aborted = false; - - #endregion Private Members - - #region Construction - - /// - /// Worker constructor taking a Z3 instance and a function to periodically - /// check for aborts. - /// - /// Z3 instance - /// method to call to check for aborts - public AbortWorker(Context context, Func queryAbortFunction) - { - _context = context; - _QueryAbortFunction = queryAbortFunction; - } - - #endregion Construction - - #region Public Methods - - /// - /// Stop the abort worker. - /// - public void Stop() - { - _stop = true; - } - - /// - /// Is true if we have been aborted. - /// - public bool Aborted - { - get - { - return _aborted; - } - } - - /// - /// Starts the abort worker. The worker checks the abort method - /// periodically until either it is stopped by a call to the Stop() - /// method or it gets an abort signal. In the latter case it will - /// issue a soft abort signal to Z3. - /// - public void Start() - { - // We go until someone stops us - _stop = false; - while (!_stop && !_QueryAbortFunction()) - { - // Wait for a while - Thread.Sleep(10); - } - // If we were stopped on abort, cancel z3 - if (!_stop) - { - _context.Interrupt(); - _aborted = true; - } - } - - #endregion Public Methods - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/App.config b/examples/msf/SolverFoundation.Plugin.Z3/App.config deleted file mode 100644 index 75e2872f15a..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/App.config +++ /dev/null @@ -1,60 +0,0 @@ - - - -
- - - - - - - - - - - - - - - - diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs b/examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs deleted file mode 100644 index 6d495a895f9..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("SolverFoundation.Plugin.Z3")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("SolverFoundation.Plugin.Z3")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("ed1476c0-96de-4d2c-983d-3888b140c3ad")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj b/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj deleted file mode 100644 index 0b30e167790..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj +++ /dev/null @@ -1,149 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {7340E664-F648-4FF7-89B2-F4DA424996D3} - Library - Properties - Microsoft.SolverFoundation.Plugin.Z3 - SolverFoundation.Plugin.Z3 - v4.0 - 512 - false - - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - bin\commercial\ - TRACE - true - pdbonly - AnyCPU - prompt - - - bin\commercial_64\ - TRACE - true - pdbonly - AnyCPU - prompt - - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - prompt - AllRules.ruleset - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - prompt - AllRules.ruleset - - - bin\x86\commercial\ - TRACE - true - pdbonly - x86 - prompt - AllRules.ruleset - - - bin\x86\commercial_64\ - TRACE - true - pdbonly - x86 - prompt - - - - ..\Microsoft.Solver.Foundation.dll - - - False - ..\Microsoft.Z3.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs b/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs deleted file mode 100644 index 5930caee143..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs +++ /dev/null @@ -1,130 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Diagnostics; -using Microsoft.Z3; -using Microsoft.SolverFoundation.Common; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - public class Utils - { - /// - /// Returns the Z3 term corresponding to the MSF rational number. - /// - /// The MSF rational - /// The Z3 term - public static ArithExpr GetNumeral(Context context, Rational rational, Sort sort = null) - { - try - { - sort = rational.IsInteger() ? ((Sort)context.IntSort) : (sort == null ? (Sort)context.RealSort : sort); - return (ArithExpr)context.MkNumeral(rational.ToString(), sort); - } - catch (Z3Exception e) - { - Console.Error.WriteLine("Conversion of {0} failed:\n {1}", rational, e); - throw new NotSupportedException(); - } - } - - private static long BASE = 10 ^ 18; - - private static Rational ToRational(System.Numerics.BigInteger bi) - { - if (System.Numerics.BigInteger.Abs(bi) <= BASE) - { - return (Rational)((long)bi); - } - return BASE * ToRational(bi / BASE) + ToRational(bi % BASE); - } - - public static Rational ToRational(IntNum i) - { - return ToRational(i.BigInteger); - } - - public static Rational ToRational(RatNum r) - { - return ToRational(r.BigIntNumerator) / ToRational(r.BigIntDenominator); - } - - public static Rational ToRational(Expr expr) - { - Debug.Assert(expr is ArithExpr, "Only accept ArithExpr for now."); - var e = expr as ArithExpr; - - if (e is IntNum) - { - Debug.Assert(expr.IsIntNum, "Number should be an integer."); - return ToRational(expr as IntNum); - } - - if (e is RatNum) - { - Debug.Assert(expr.IsRatNum, "Number should be a rational."); - return ToRational(expr as RatNum); - } - - if (e.IsAdd) - { - Rational r = Rational.Zero; - foreach (var arg in e.Args) - { - r += ToRational(arg); - } - return r; - } - - if (e.IsMul) - { - Rational r = Rational.One; - foreach (var arg in e.Args) - { - r *= ToRational(arg); - } - return r; - } - - if (e.IsUMinus) - { - return -ToRational(e.Args[0]); - } - - if (e.IsDiv) - { - return ToRational(e.Args[0]) / ToRational(e.Args[1]); - } - - if (e.IsSub) - { - Rational r = ToRational(e.Args[0]); - for (int i = 1; i < e.Args.Length; ++i) - { - r -= ToRational(e.Args[i]); - } - return r; - } - - if (e.IsConst && e.FuncDecl.Name.ToString() == "oo") - { - return Rational.PositiveInfinity; - } - - if (e.IsConst && e.FuncDecl.Name.ToString() == "epsilon") - { - return Rational.One/Rational.PositiveInfinity; - } - - Debug.Assert(false, "Should not happen"); - return Rational.One; - } - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs deleted file mode 100644 index 199c3fe3531..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs +++ /dev/null @@ -1,107 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Text; -using Microsoft.SolverFoundation.Services; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - /// - /// Combining objective functions - /// - public enum OptimizationKind - { - Lexicographic, - BoundingBox, - ParetoOptimal - }; - - /// - /// Algorithm for solving cardinality constraints - /// - public enum CardinalityAlgorithm - { - FuMalik, - CoreMaxSAT - } - - /// - /// Algorithm for solving pseudo-boolean constraints - /// - public enum PseudoBooleanAlgorithm - { - WeightedMaxSAT, - IterativeWeightedMaxSAT, - BisectionWeightedMaxSAT, - WeightedPartialMaxSAT2 - } - - /// - /// Strategy for solving arithmetic optimization - /// - public enum ArithmeticStrategy - { - Basic, - Farkas - } - - public abstract class Z3BaseDirective : Directive - { - protected OptimizationKind _optKind; - protected CardinalityAlgorithm _cardAlgorithm; - protected PseudoBooleanAlgorithm _pboAlgorithm; - protected ArithmeticStrategy _arithStrategy; - - protected string _smt2LogFile; - - public Z3BaseDirective() - { - Arithmetic = Arithmetic.Exact; - } - - public OptimizationKind OptKind - { - get { return _optKind; } - set { _optKind = value; } - } - - public CardinalityAlgorithm CardinalityAlgorithm - { - get { return _cardAlgorithm; } - set { _cardAlgorithm = value; } - } - - public PseudoBooleanAlgorithm PseudoBooleanAlgorithm - { - get { return _pboAlgorithm; } - set { _pboAlgorithm = value; } - } - - public ArithmeticStrategy ArithmeticStrategy - { - get { return _arithStrategy; } - set { _arithStrategy = value; } - } - - public string SMT2LogFile - { - get { return _smt2LogFile; } - set { _smt2LogFile = value; } - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append(this.GetType().Name); - sb.Append("("); - sb.AppendFormat("OptKind: {0}, ", _optKind); - sb.AppendFormat("SMT2LogFile: {0}", _smt2LogFile); - sb.Append(")"); - return sb.ToString(); - } - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs deleted file mode 100644 index 6d6dd74a7c9..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs +++ /dev/null @@ -1,109 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using Microsoft.SolverFoundation.Services; -using System; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - /// - /// Implementation of the solver parameters for Z3 - /// - public class Z3BaseParams : ISolverParameters - { - #region Private Members - - /// The abort method we can call (defaults to always false) - protected Func _queryAbortFunction = delegate() { return false; }; - - /// The directive to use - protected Directive _directive = null; - - protected OptimizationKind _optKind; - protected CardinalityAlgorithm _cardAlgorithm; - protected PseudoBooleanAlgorithm _pboAlgorithm; - protected ArithmeticStrategy _arithStrategy; - - protected string _smt2LogFile; - - #endregion Private Members - - #region Construction - - public Z3BaseParams() { } - - public Z3BaseParams(Directive directive) - { - _directive = directive; - - var z3Directive = directive as Z3BaseDirective; - if (z3Directive != null) - { - _optKind = z3Directive.OptKind; - _cardAlgorithm = z3Directive.CardinalityAlgorithm; - _pboAlgorithm = z3Directive.PseudoBooleanAlgorithm; - _arithStrategy = z3Directive.ArithmeticStrategy; - _smt2LogFile = z3Directive.SMT2LogFile; - } - } - - public Z3BaseParams(Func queryAbortFunction) - { - _queryAbortFunction = queryAbortFunction; - } - - public Z3BaseParams(Z3BaseParams z3Parameters) - { - _queryAbortFunction = z3Parameters._queryAbortFunction; - } - - #endregion Construction - - #region ISolverParameters Members - - /// - /// Getter for the abort method - /// - public Func QueryAbort - { - get { return _queryAbortFunction; } - set { _queryAbortFunction = value; } - } - - public OptimizationKind OptKind - { - get { return _optKind; } - set { _optKind = value; } - } - - public CardinalityAlgorithm CardinalityAlgorithm - { - get { return _cardAlgorithm; } - set { _cardAlgorithm = value; } - } - - public PseudoBooleanAlgorithm PseudoBooleanAlgorithm - { - get { return _pboAlgorithm; } - set { _pboAlgorithm = value; } - } - - public ArithmeticStrategy ArithmeticStrategy - { - get { return _arithStrategy; } - set { _arithStrategy = value; } - } - - public string SMT2LogFile - { - get { return _smt2LogFile; } - set { _smt2LogFile = value; } - } - - #endregion - } - -} \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs deleted file mode 100644 index 5297d3e676d..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs +++ /dev/null @@ -1,387 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Threading; -using System.IO; -using System.Linq; -using System.Text; -using System.Diagnostics; -using Microsoft.Z3; -using Microsoft.SolverFoundation.Common; -using Microsoft.SolverFoundation.Services; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - internal enum Z3Result - { - Optimal, - LocalOptimal, - Feasible, - Interrupted, - Infeasible - } - - /// - /// The basic solver class to take care of transformation from an MSF instance to an Z3 instance - /// - internal class Z3BaseSolver - { - /// Representing MSF model - private IRowVariableModel _model; - - /// The Z3 solver we are currently using - private Context _context = null; - - /// Default optimization solver - private Optimize _optSolver = null; - - /// Marks when we are inside the Solve() method - private bool _isSolving = false; - - /// A map from MSF variable ids to Z3 variables - private Dictionary _variables = new Dictionary(); - - /// A map from MSF variable ids to Z3 goal ids - private Dictionary _goals = new Dictionary(); - - internal Z3BaseSolver(IRowVariableModel model) - { - _model = model; - } - - internal Context Context - { - get { return _context; } - } - - internal Dictionary Variables - { - get { return _variables; } - } - - internal Dictionary Goals - { - get { return _goals; } - } - - /// - /// Destructs a currently active Z3 solver and the associated data. - /// - internal void DestructSolver(bool checkInSolve) - { - if (_context != null) - { - if (checkInSolve && !_isSolving) - { - _variables.Clear(); - if (!_isSolving) - { - _optSolver.Dispose(); - _context.Dispose(); - } - } - else - { - Console.Error.WriteLine("Z3 destruction is invoked while in Solving phase."); - } - } - } - - /// - /// Constructs a Z3 solver to be used. - /// - internal void ConstructSolver(Z3BaseParams parameters) - { - // If Z3 is there already, kill it - if (_context != null) - { - DestructSolver(false); - } - - _context = new Context(); - _optSolver = _context.MkOptimize(); - var p = _context.MkParams(); - - switch (parameters.OptKind) - { - case OptimizationKind.BoundingBox: - p.Add("priority", _context.MkSymbol("box")); - break; - case OptimizationKind.Lexicographic: - p.Add("priority", _context.MkSymbol("lex")); - break; - case OptimizationKind.ParetoOptimal: - p.Add("priority", _context.MkSymbol("pareto")); - break; - default: - Debug.Assert(false, String.Format("Unknown optimization option {0}", parameters.OptKind)); - break; - } - - switch (parameters.CardinalityAlgorithm) - { - case CardinalityAlgorithm.FuMalik: - p.Add("maxsat_engine", _context.MkSymbol("fu_malik")); - break; - case CardinalityAlgorithm.CoreMaxSAT: - p.Add("maxsat_engine", _context.MkSymbol("core_maxsat")); - break; - default: - Debug.Assert(false, String.Format("Unknown cardinality algorithm option {0}", parameters.CardinalityAlgorithm)); - break; - } - - switch (parameters.PseudoBooleanAlgorithm) - { - case PseudoBooleanAlgorithm.WeightedMaxSAT: - p.Add("wmaxsat_engine", _context.MkSymbol("wmax")); - break; - case PseudoBooleanAlgorithm.IterativeWeightedMaxSAT: - p.Add("wmaxsat_engine", _context.MkSymbol("iwmax")); - break; - case PseudoBooleanAlgorithm.BisectionWeightedMaxSAT: - p.Add("wmaxsat_engine", _context.MkSymbol("bwmax")); - break; - case PseudoBooleanAlgorithm.WeightedPartialMaxSAT2: - p.Add("wmaxsat_engine", _context.MkSymbol("wpm2")); - break; - default: - Debug.Assert(false, String.Format("Unknown pseudo-boolean algorithm option {0}", parameters.PseudoBooleanAlgorithm)); - break; - } - - switch (parameters.ArithmeticStrategy) - { - case ArithmeticStrategy.Basic: - p.Add("engine", _context.MkSymbol("basic")); - break; - case ArithmeticStrategy.Farkas: - p.Add("engine", _context.MkSymbol("farkas")); - break; - default: - Debug.Assert(false, String.Format("Unknown arithmetic strategy option {0}", parameters.ArithmeticStrategy)); - break; - } - - _optSolver.Parameters = p; - } - - internal ArithExpr GetVariable(int vid) - { - Expr variable; - if (!_variables.TryGetValue(vid, out variable)) - { - AddVariable(vid); - variable = _variables[vid]; - } - return (ArithExpr)variable; - } - - internal void AssertBool(BoolExpr row) - { - _optSolver.Assert(row); - } - - internal void AssertArith(int vid, ArithExpr variable) - { - // Get the bounds on the row - Rational lower, upper; - _model.GetBounds(vid, out lower, out upper); - - // Case of equality - if (lower == upper) - { - // Create the equality term - Expr eqConst = GetNumeral(lower, variable.Sort); - BoolExpr constraint = _context.MkEq(eqConst, variable); - // Assert the constraint - _optSolver.Assert(constraint); - } - else - { - // If upper bound is finite assert the upper bound constraint - if (lower.IsFinite) - { - // Create the lower Bound constraint - ArithExpr lowerTerm = GetNumeral(lower, variable.Sort); - BoolExpr constraint = _context.MkLe(lowerTerm, variable); - // Assert the constraint - _optSolver.Assert(constraint); - } - // If lower bound is finite assert the lower bound constraint - if (upper.IsFinite) - { - // Create the upper bound constraint - ArithExpr upperTerm = GetNumeral(upper, variable.Sort); - BoolExpr constraint = _context.MkGe(upperTerm, variable); - // Assert the constraint - _optSolver.Assert(constraint); - } - } - } - - /// - /// Adds a MSF variable with the corresponding assertion to the Z3 variables. - /// - /// The MSF id of the variable - internal void AddVariable(int vid) - { - // Is the variable an integer - bool isInteger = _model.GetIntegrality(vid); - - // Construct the sort we will be using - Sort sort = isInteger ? (Sort)_context.IntSort : (Sort)_context.RealSort; - - // Get the variable key - object key = _model.GetKeyFromIndex(vid); - - // Try to construct the name - string name; - if (key != null) name = String.Format("x_{0}_{1}", key, vid); - else name = String.Format("x_{0}", vid); - ArithExpr variable = (ArithExpr)_context.MkConst(name, sort); - - // Create the variable and add it to the map - Debug.Assert(!_variables.ContainsKey(vid), "Variable names should be unique."); - _variables.Add(vid, variable); - - AssertArith(vid, variable); - } - - internal ArithExpr GetNumeral(Rational rational, Sort sort = null) - { - return Utils.GetNumeral(_context, rational, sort); - } - - internal void Solve(Z3BaseParams parameters, IEnumerable modelGoals, - Action addRow, Func mkGoalRow, Action setResult) - { - _variables.Clear(); - _goals.Clear(); - - try - { - // Mark that we are in solving phase - _isSolving = true; - - // Construct Z3 - ConstructSolver(parameters); - - // Add all the variables - foreach (int vid in _model.VariableIndices) - { - AddVariable(vid); - } - - // Add all the rows - foreach (int rid in _model.RowIndices) - { - addRow(rid); - } - - // Add enabled goals to optimization problem - foreach (IGoal g in modelGoals) - { - if (!g.Enabled) continue; - - ArithExpr gr = mkGoalRow(g.Index); - if (g.Minimize) - _goals.Add(g, _optSolver.MkMinimize(gr)); - else - _goals.Add(g, _optSolver.MkMaximize(gr)); - } - - if (_goals.Any() && parameters.SMT2LogFile != null) - { - Debug.WriteLine("Dumping SMT2 benchmark to log file..."); - File.WriteAllText(parameters.SMT2LogFile, _optSolver.ToString()); - } - - bool aborted = parameters.QueryAbort(); - - if (!aborted) - { - // Start the abort thread - AbortWorker abortWorker = new AbortWorker(_context, parameters.QueryAbort); - Thread abortThread = new Thread(abortWorker.Start); - abortThread.Start(); - - // Now solve the problem - Status status = _optSolver.Check(); - - // Stop the abort thread - abortWorker.Stop(); - abortThread.Join(); - - switch (status) - { - case Status.SATISFIABLE: - Microsoft.Z3.Model model = _optSolver.Model; - Debug.Assert(model != null, "Should be able to get Z3 model."); - // Remember the solution values - foreach (KeyValuePair pair in _variables) - { - var value = Utils.ToRational(model.Eval(pair.Value, true)); - _model.SetValue(pair.Key, value); - } - // Remember all objective values - foreach (var pair in _goals) - { - var optimalValue = Utils.ToRational(pair.Value.Upper); - _model.SetValue(pair.Key.Index, optimalValue); - } - model.Dispose(); - setResult(_goals.Any() ? Z3Result.Optimal : Z3Result.Feasible); - break; - case Status.UNSATISFIABLE: - setResult(Z3Result.Infeasible); - break; - case Status.UNKNOWN: - if (abortWorker.Aborted) - { - Microsoft.Z3.Model subOptimalModel = _optSolver.Model; - if (subOptimalModel != null && subOptimalModel.NumConsts != 0) - { - // Remember the solution values - foreach (KeyValuePair pair in _variables) - { - var value = Utils.ToRational(subOptimalModel.Eval(pair.Value, true)); - _model.SetValue(pair.Key, value); - } - // Remember all objective values - foreach (var pair in _goals) - { - var optimalValue = Utils.ToRational(pair.Value.Upper); - _model.SetValue(pair.Key.Index, optimalValue); - } - subOptimalModel.Dispose(); - - setResult(Z3Result.LocalOptimal); - } - else - setResult(Z3Result.Infeasible); - } - else - setResult(Z3Result.Interrupted); - break; - default: - Debug.Assert(false, "Unrecognized Z3 Status"); - break; - } - } - } - finally - { - _isSolving = false; - } - - // Now kill Z3 - DestructSolver(true); - } - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs deleted file mode 100644 index 4d674563454..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs +++ /dev/null @@ -1,15 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using Microsoft.SolverFoundation.Services; -using System; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - public class Z3MILPDirective : Z3BaseDirective - { - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs deleted file mode 100644 index d01b077251b..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs +++ /dev/null @@ -1,25 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using Microsoft.SolverFoundation.Services; -using System; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - public class Z3MILPParams : Z3BaseParams - { - // Need these constructors for reflection done by plugin model - - public Z3MILPParams() : base() { } - - public Z3MILPParams(Directive directive) : base(directive) { } - - public Z3MILPParams(Func queryAbortFunction) : base(queryAbortFunction) { } - - public Z3MILPParams(Z3BaseParams z3Parameters) : base (z3Parameters) { } - } - -} \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs deleted file mode 100644 index 4f8cdc759f0..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs +++ /dev/null @@ -1,236 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.IO; - -using Microsoft.Z3; -using Microsoft.SolverFoundation.Common; -using Microsoft.SolverFoundation.Services; -using Microsoft.SolverFoundation.Plugin; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - /// - /// The class is implementation of the MSF mixed linear programming solver - /// using the Microsoft Z3 solver as the backend. - /// - public class Z3MILPSolver : LinearModel, ILinearSolver, ILinearSolution, IReportProvider - { - #region Private members - - private LinearResult _result; - private LinearSolutionQuality _solutionQuality; - private Z3BaseSolver _solver; - - #endregion Private members - - #region Solver construction and destruction - - /// Constructor that initializes the base classes - public Z3MILPSolver() : base(null) - { - _result = LinearResult.Feasible; - _solver = new Z3BaseSolver(this); - } - - /// Constructor that initializes the base classes - public Z3MILPSolver(ISolverEnvironment context) : this() { } - - /// - /// Shutdown can be called when when the solver is not active, i.e. - /// when it is done with Solve() or it has gracefully returns from Solve() - /// after an abort. - /// - public void Shutdown() { _solver.DestructSolver(true); } - - #endregion Solver construction and destruction - - #region Obtaining information about the solution - - public ILinearSolverReport GetReport(LinearSolverReportType reportType) - { - // We don't support sensitivity report - return null; - } - - #endregion Obtaining information about the solution - - #region Construction of the problem - - /// - /// Get corresponding Z3 formula of a MSF row. - /// - /// The MSF row id - private ArithExpr MkGoalRow(int rid) - { - // Start with the 0 term - List row = new List(); - - // Now, add all the entries of this row - foreach (LinearEntry entry in GetRowEntries(rid)) - { - // Get the variable and constant in the row - ArithExpr e = _solver.GetVariable(entry.Index); - if (!entry.Value.IsOne) - { - e = _solver.Context.MkMul(_solver.GetNumeral(entry.Value, e.Sort), e); - } - row.Add(e); - } - switch (row.Count) - { - case 0: return _solver.GetNumeral(new Rational()); - case 1: return row[0]; - default: return _solver.Context.MkAdd(row.ToArray()); - } - } - - /// - /// Adds a MSF row to the Z3 assertions. - /// - /// The MSF row id - private void AddRow(int rid) - { - // Start with the 0 term - ArithExpr row = MkGoalRow(rid); - _solver.AssertArith(rid, row); - } - - /// - /// Set results based on internal solver status - /// - private void SetResult(Z3Result status) - { - switch (status) - { - case Z3Result.Optimal: - _result = LinearResult.Optimal; - _solutionQuality = LinearSolutionQuality.Exact; - break; - case Z3Result.LocalOptimal: - _result = LinearResult.Feasible; - _solutionQuality = LinearSolutionQuality.Approximate; - break; - case Z3Result.Feasible: - _result = LinearResult.Feasible; - _solutionQuality = LinearSolutionQuality.Exact; - break; - case Z3Result.Infeasible: - _result = LinearResult.InfeasiblePrimal; - _solutionQuality = LinearSolutionQuality.None; - break; - case Z3Result.Interrupted: - _result = LinearResult.Interrupted; - _solutionQuality = LinearSolutionQuality.None; - break; - default: - Debug.Assert(false, "Unrecognized Z3 Result"); - break; - } - } - - #endregion Construction of the problem - - #region Solving the problem - - /// - /// Starts solving the problem using the Z3 solver. - /// - /// Parameters to the solver - /// The solution to the problem - public ILinearSolution Solve(ISolverParameters parameters) - { - // Get the Z3 parameters - var z3Params = parameters as Z3BaseParams; - Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); - - _solver.Solve(z3Params, Goals, AddRow, MkGoalRow, SetResult); - - return this; - } - - #endregion Solving the problem - - #region ILinearSolution Members - - public Rational GetSolutionValue(int goalIndex) - { - var goal = Goals.ElementAt(goalIndex); - Debug.Assert(goal != null, "Goal should be an element of the goal list."); - return GetValue(goal.Index); - } - - public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) - { - var goal = Goals.ElementAt(goalIndex); - Debug.Assert(goal != null, "Goal should be an element of the goal list."); - key = goal.Key; - vid = goal.Index; - minimize = goal.Minimize; - optimal = _result == LinearResult.Optimal; - } - - // LpResult is LP relaxation assignment. - - public LinearResult LpResult - { - get { return _result; } - } - - public Rational MipBestBound - { - get - { - Debug.Assert(GoalCount > 0, "MipBestBound is only applicable for optimization instances."); - return GetSolutionValue(0); - } - } - - public LinearResult MipResult - { - get { return _result; } - } - - public LinearResult Result - { - get { return _result; } - } - - public LinearSolutionQuality SolutionQuality - { - get { return _solutionQuality; } - } - - public int SolvedGoalCount - { - get { return GoalCount; } - } - - #endregion - - public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) - { - LinearSolutionMapping lpSolutionMapping = solutionMapping as LinearSolutionMapping; - if (lpSolutionMapping == null && solutionMapping != null) - throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); - return new Z3LinearSolverReport(context, this, solution, lpSolutionMapping); - } - } - - /// - /// Class implementing the LinearReport. - /// - public class Z3LinearSolverReport : LinearReport - { - public Z3LinearSolverReport(SolverContext context, ISolver solver, Solution solution, LinearSolutionMapping solutionMapping) - : base(context, solver, solution, solutionMapping) { - } - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs deleted file mode 100644 index ff9e4181a6c..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs +++ /dev/null @@ -1,15 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using Microsoft.SolverFoundation.Services; -using System; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - public class Z3TermDirective : Z3BaseDirective - { - } -} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs deleted file mode 100644 index 283bc9362d7..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs +++ /dev/null @@ -1,23 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using Microsoft.SolverFoundation.Services; -using System; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - public class Z3TermParams : Z3BaseParams - { - public Z3TermParams() : base() { } - - public Z3TermParams(Directive directive) : base(directive) { } - - public Z3TermParams(Func queryAbortFunction) : base(queryAbortFunction) { } - - public Z3TermParams(Z3BaseParams z3Parameters) : base(z3Parameters) { } - } - -} \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs deleted file mode 100644 index de91c7b6e27..00000000000 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs +++ /dev/null @@ -1,388 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Threading; -using System.Globalization; -using System.Collections.Generic; -using Microsoft.SolverFoundation.Common; -using Microsoft.SolverFoundation.Properties; -using Microsoft.SolverFoundation.Solvers; -using Microsoft.SolverFoundation.Services; -using Microsoft.Z3; -using System.Linq; -using System.Diagnostics; -using System.IO; - -namespace Microsoft.SolverFoundation.Plugin.Z3 -{ - /// - /// The class is implementation of the MSF constraint solver - /// using the Microsoft Z3 solver as the backend. - /// This solver supports Int, Real constraints and their arbitrary boolean combinations. - /// - public class Z3TermSolver : TermModel, ITermSolver, INonlinearSolution, IReportProvider - { - private NonlinearResult _result; - private Z3BaseSolver _solver; - - /// Constructor that initializes the base classes - public Z3TermSolver() : base(null) - { - _solver = new Z3BaseSolver(this); - } - - /// Constructor that initializes the base classes - public Z3TermSolver(ISolverEnvironment context) : this() { } - - /// - /// Shutdown can be called when when the solver is not active, i.e. - /// when it is done with Solve() or it has gracefully returns from Solve() - /// after an abort. - /// - public void Shutdown() { _solver.DestructSolver(true); } - - private BoolExpr MkBool(int rid) - { - var context = _solver.Context; - - if (IsConstant(rid)) - { - Rational lower, upper; - GetBounds(rid, out lower, out upper); - Debug.Assert(lower == upper); - if (lower.IsZero) return context.MkFalse(); - return context.MkTrue(); - } - if (IsOperation(rid)) - { - BoolExpr[] children; - ArithExpr[] operands; - TermModelOperation op = GetOperation(rid); - switch(op) { - case TermModelOperation.And: - Debug.Assert(GetOperandCount(rid) >= 2, "Conjunction requires at least two operands."); - children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); - return context.MkAnd(children); - case TermModelOperation.Or: - Debug.Assert(GetOperandCount(rid) >= 2, "Disjunction requires at least two operands."); - children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); - return context.MkOr(children); - case TermModelOperation.Not: - Debug.Assert(GetOperandCount(rid) == 1, "Negation is unary."); - return context.MkNot(MkBool(GetOperand(rid, 0))); - case TermModelOperation.If: - Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); - BoolExpr b = MkBool(GetOperand(rid, 0)); - Expr x1 = MkBool(GetOperand(rid, 1)); - Expr x2 = MkBool(GetOperand(rid, 2)); - return (BoolExpr)context.MkITE(b, x1, x2); - case TermModelOperation.Unequal: - Debug.Assert(GetOperandCount(rid) >= 2, "Distinct should have at least two operands."); - return context.MkDistinct((GetOperands(rid)).Select(x => MkTerm(x)).ToArray()); - case TermModelOperation.Greater: - case TermModelOperation.Less: - case TermModelOperation.GreaterEqual: - case TermModelOperation.LessEqual: - case TermModelOperation.Equal: - Debug.Assert(GetOperandCount(rid) >= 2, "Comparison should have at least two operands."); - operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); - return ReduceComparison(GetOperation(rid), operands); - case TermModelOperation.Identity: - Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); - return MkBool(GetOperand(rid, 0)); - default: - return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); - } - } - return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); - } - - private ArithExpr MkBoolToArith(BoolExpr e) - { - var context = _solver.Context; - return (ArithExpr)context.MkITE(e, _solver.GetNumeral(Rational.One), _solver.GetNumeral(Rational.Zero)); - } - - private ArithExpr MkTerm(int rid) - { - var context = _solver.Context; - - if (IsConstant(rid)) - { - Rational lower, upper; - GetBounds(rid, out lower, out upper); - Debug.Assert(lower == upper); - return _solver.GetNumeral(lower); - } - else if (IsOperation(rid)) - { - ArithExpr[] operands; - TermModelOperation op = GetOperation(rid); - switch(op) - { - case TermModelOperation.And: - case TermModelOperation.Or: - case TermModelOperation.Not: - case TermModelOperation.Unequal: - case TermModelOperation.Greater: - case TermModelOperation.Less: - case TermModelOperation.GreaterEqual: - case TermModelOperation.LessEqual: - case TermModelOperation.Equal: - return MkBoolToArith(MkBool(rid)); - case TermModelOperation.If: - Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); - BoolExpr b = MkBool(GetOperand(rid, 0)); - Expr x1 = MkTerm(GetOperand(rid, 1)); - Expr x2 = MkTerm(GetOperand(rid, 2)); - return (ArithExpr)context.MkITE(b, x1, x2); - case TermModelOperation.Plus: - Debug.Assert(GetOperandCount(rid) >= 2, "Plus takes at least two operands."); - operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); - return context.MkAdd(operands); - case TermModelOperation.Minus: - Debug.Assert(GetOperandCount(rid) == 1, "Minus takes exactly one operand."); - return context.MkUnaryMinus(MkTerm(GetOperand(rid, 0))); - case TermModelOperation.Times: - Debug.Assert(GetOperandCount(rid) >= 2, "Times requires at least two operands."); - operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); - return context.MkMul(operands); - case TermModelOperation.Identity: - Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); - return MkTerm(GetOperand(rid, 0)); - case TermModelOperation.Abs: - Debug.Assert(GetOperandCount(rid) == 1, "Abs takes exactly one operand."); - ArithExpr e = MkTerm(GetOperand(rid, 0)); - ArithExpr minusE = context.MkUnaryMinus(e); - ArithExpr zero = _solver.GetNumeral(Rational.Zero); - return (ArithExpr)context.MkITE(context.MkGe(e, zero), e, minusE); - default: - Console.Error.WriteLine("{0} operation isn't supported.", op); - throw new NotSupportedException(); - } - } - else - { - return _solver.GetVariable(rid); - } - } - - private BoolExpr ReduceComparison(TermModelOperation type, ArithExpr[] operands) - { - var context = _solver.Context; - Debug.Assert(operands.Length >= 2); - Func mkComparison; - switch (type) - { - case TermModelOperation.Greater: - mkComparison = (x, y) => context.MkGt(x, y); - break; - case TermModelOperation.Less: - mkComparison = (x, y) => context.MkLt(x, y); - break; - case TermModelOperation.GreaterEqual: - mkComparison = (x, y) => context.MkGe(x, y); - break; - case TermModelOperation.LessEqual: - mkComparison = (x, y) => context.MkLe(x, y); - break; - case TermModelOperation.Equal: - mkComparison = (x, y) => context.MkEq(x, y); - break; - default: - throw new NotSupportedException(); - } - - BoolExpr current = mkComparison(operands[0], operands[1]); - for (int i = 1; i < operands.Length - 1; ++i) - current = context.MkAnd(current, mkComparison(operands[i], operands[i + 1])); - return current; - } - - private bool IsBoolRow(int rid) - { - Rational lower, upper; - GetBounds(rid, out lower, out upper); - - return lower == upper && lower.IsOne && IsBoolTerm(rid); - } - - private bool IsBoolTerm(int rid) - { - if (IsConstant(rid)) - { - Rational lower, upper; - GetBounds(rid, out lower, out upper); - Debug.Assert(lower == upper); - return lower.IsOne || lower.IsZero; - } - if (IsOperation(rid)) - { - TermModelOperation op = GetOperation(rid); - switch (op) - { - case TermModelOperation.And: - case TermModelOperation.Or: - case TermModelOperation.Not: - case TermModelOperation.LessEqual: - case TermModelOperation.Less: - case TermModelOperation.Greater: - case TermModelOperation.GreaterEqual: - case TermModelOperation.Unequal: - case TermModelOperation.Equal: - return true; - case TermModelOperation.If: - return IsBoolTerm(GetOperand(rid, 1)) && - IsBoolTerm(GetOperand(rid, 2)); - case TermModelOperation.Identity: - return IsBoolTerm(GetOperand(rid, 0)); - default: - return false; - } - } - return false; - } - - /// - /// Adds a MSF row to the Z3 assertions. - /// - /// The MSF row id - private void AddRow(int rid) - { - if (IsConstant(rid)) - return; - - if (IsBoolRow(rid)) - { - _solver.AssertBool(MkBool(rid)); - return; - } - // Start with the 0 term - ArithExpr row = MkTerm(rid); - _solver.AssertArith(rid, row); - } - - private TermModelOperation[] _supportedOperations = - { TermModelOperation.And, - TermModelOperation.Or, - TermModelOperation.Not, - TermModelOperation.Unequal, - TermModelOperation.Greater, - TermModelOperation.Less, - TermModelOperation.GreaterEqual, - TermModelOperation.LessEqual, - TermModelOperation.Equal, - TermModelOperation.If, - TermModelOperation.Plus, - TermModelOperation.Minus, - TermModelOperation.Times, - TermModelOperation.Identity, - TermModelOperation.Abs }; - - /// - /// Gets the operations supported by the solver. - /// - /// All the TermModelOperations supported by the solver. - public IEnumerable SupportedOperations - { - get { return _supportedOperations; } - } - - /// - /// Set results based on internal solver status - /// - private void SetResult(Z3Result status) - { - switch (status) - { - case Z3Result.Optimal: - _result = NonlinearResult.Optimal; - break; - case Z3Result.LocalOptimal: - _result = NonlinearResult.LocalOptimal; - break; - case Z3Result.Feasible: - _result = NonlinearResult.Feasible; - break; - case Z3Result.Infeasible: - _result = NonlinearResult.Infeasible; - break; - case Z3Result.Interrupted: - _result = NonlinearResult.Interrupted; - break; - default: - Debug.Assert(false, "Unrecognized Z3 Result"); - break; - } - } - - /// - /// Starts solving the problem using the Z3 solver. - /// - /// Parameters to the solver - /// The solution to the problem - public INonlinearSolution Solve(ISolverParameters parameters) - { - // Get the Z3 parameters - var z3Params = parameters as Z3BaseParams; - Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); - - _solver.Solve(z3Params, Goals, AddRow, MkTerm, SetResult); - - return this; - } - - double INonlinearSolution.GetValue(int vid) - { - Debug.Assert(_solver.Variables.ContainsKey(vid), "This index should correspond to a variable."); - return GetValue(vid).ToDouble(); - } - - public int SolvedGoalCount - { - get { return GoalCount; } - } - - public double GetSolutionValue(int goalIndex) - { - var goal = Goals.ElementAt(goalIndex); - Debug.Assert(goal != null, "Goal should be an element of the goal list."); - return GetValue(goal.Index).ToDouble(); - } - - public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) - { - var goal = Goals.ElementAt(goalIndex); - Debug.Assert(goal != null, "Goal should be an element of the goal list."); - key = goal.Key; - vid = goal.Index; - minimize = goal.Minimize; - optimal = _result == NonlinearResult.Optimal; - } - - public NonlinearResult Result - { - get { return _result; } - } - - public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) - { - PluginSolutionMapping pluginSolutionMapping = solutionMapping as PluginSolutionMapping; - if (pluginSolutionMapping == null && solutionMapping != null) - throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); - return new Z3TermSolverReport(context, this, solution, pluginSolutionMapping); - } - } - - public class Z3TermSolverReport : Report - { - public Z3TermSolverReport(SolverContext context, ISolver solver, Solution solution, PluginSolutionMapping pluginSolutionMapping) - : base(context, solver, solution, pluginSolutionMapping) - { - } - } -} diff --git a/examples/msf/Validator/App.config b/examples/msf/Validator/App.config deleted file mode 100644 index 75e2872f15a..00000000000 --- a/examples/msf/Validator/App.config +++ /dev/null @@ -1,60 +0,0 @@ - - - -
- - - - - - - - - - - - - - - - diff --git a/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config b/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config deleted file mode 100644 index cd9dcad256e..00000000000 --- a/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config +++ /dev/null @@ -1,58 +0,0 @@ - - - -
- - - - - - - - - - - - - diff --git a/examples/msf/Validator/Program.cs b/examples/msf/Validator/Program.cs deleted file mode 100644 index 8afb28af555..00000000000 --- a/examples/msf/Validator/Program.cs +++ /dev/null @@ -1,200 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; -using Microsoft.SolverFoundation.Common; -using Microsoft.SolverFoundation.Solvers; -using Microsoft.SolverFoundation.Plugin.Z3; -using Microsoft.SolverFoundation.Services; -using System.Text; - -namespace Validator -{ - class Program - { - static void LoadModel(SolverContext context, string fileName) - { - string ext = Path.GetExtension(fileName).ToLower(); - - if (ext == ".mps") - { - context.LoadModel(FileFormat.MPS, Path.GetFullPath(fileName)); - } - else if (ext == ".smps") - { - context.LoadModel(FileFormat.SMPS, Path.GetFullPath(fileName)); - } - else if (ext == ".oml") - { - context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); - } - else - { - throw new NotSupportedException("This file format hasn't been supported."); - } - } - - static void ExecuteZ3(string fileName, Z3BaseDirective directive) - { - SolverContext context = SolverContext.GetContext(); - try - { - LoadModel(context, fileName); - - Solution solution = context.Solve(directive); - Report report = solution.GetReport(); - Console.Write("{0}", report); - } - catch (Exception e) - { - Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", fileName, e.Message); - } - finally - { - context.ClearModel(); - } - } - - static void ConvertToSMT2(string fileName, Z3BaseDirective directive) - { - SolverContext context = SolverContext.GetContext(); - try - { - LoadModel(context, fileName); - - if (context.CurrentModel.Goals.Any()) - { - directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); - context.Solve(() => true, directive); - } - } - catch (Exception e) - { - Console.WriteLine("Skipping unconvertable instance in {0} with error message '{1}'.", fileName, e.Message); - } - finally - { - context.ClearModel(); - } - } - - static void ValidateZ3(string fileName, Z3BaseDirective directive) - { - SolverContext context = SolverContext.GetContext(); - try - { - LoadModel(context, fileName); - - if (context.CurrentModel.Goals.Any()) - { - var msfDirective = (directive is Z3MILPDirective) ? (Directive)new MixedIntegerProgrammingDirective() { TimeLimit = 10000 } - : (Directive)new Directive() { TimeLimit = 10000 }; - var sol1 = context.Solve(msfDirective); - - Console.WriteLine("Solved the model using MSF."); - Console.Write("{0}", sol1.GetReport()); - var expectedGoals = sol1.Goals.Select(x => x.ToDouble()); - context.ClearModel(); - - context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); - directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); - var sol2 = context.Solve(directive); - //Console.Write("{0}", sol2.GetReport()); - var actualGoals = sol2.Goals.Select(x => x.ToDouble()); - - Console.WriteLine("Solved the model using Z3."); - var goalPairs = expectedGoals.Zip(actualGoals, (expected, actual) => new { expected, actual }).ToArray(); - bool validated = goalPairs.All(p => Math.Abs(p.expected - p.actual) <= 0.0001); - if (validated) - { - Console.WriteLine("INFO: Two solvers give approximately the same results."); - } - else - { - Console.Error.WriteLine("ERROR: Discrepancy found between results."); - if (!validated && File.Exists(directive.SMT2LogFile)) - { - var sb = new StringBuilder(); - for(int i = 0; i < goalPairs.Length; i++) - { - sb.AppendFormat("\n(echo \"Goal {0}: actual |-> {1:0.0000}, expected |-> {2:0.0000}\")", - i + 1, goalPairs[i].actual, goalPairs[i].expected); - } - Console.Error.WriteLine(sb.ToString()); - File.AppendAllText(directive.SMT2LogFile, sb.ToString()); - } - } - } - else - { - Console.WriteLine("Ignoring this instance without having any goal."); - } - } - catch (Exception e) - { - Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", - fileName, e.Message); - } - finally - { - context.ClearModel(); - } - } - - static void Main(string[] args) - { - Z3BaseDirective directive = new Z3MILPDirective(); - - for (int i = 0; i < args.Length; ++i) { - if (args[i] == "-s" || args[i] == "-solve") - { - ExecuteZ3(args[i + 1], directive); - return; - } - if (args[i] == "-c" || args[i] == "-convert") - { - ConvertToSMT2(args[i + 1], directive); - return; - } - if (args[i] == "-v" || args[i] == "-validate") - { - ValidateZ3(args[i + 1], directive); - return; - } - if (args[i] == "-t" || args[i] == "-term") - { - directive = new Z3TermDirective(); - } - } - - if (args.Length > 0) - { - ExecuteZ3(args[0], directive); - return; - } - - Console.WriteLine(@" -Validator is a simple command line to migrate benchmarks from OML, MPS and SMPS to SMT2 formats. - -Commands: - -solve : solving the model using Z3 - -convert : converting the model into SMT2 format - -validate : validating by comparing results between Z3 and MSF solvers - -term : change the default Z3 MILP solver to Z3 Term solver - - where is any file with OML, MPS or SMPS extension. - -Examples: - Validator.exe -convert model.mps - Validator.exe -term -solve model.oml - -"); - } - } -} diff --git a/examples/msf/Validator/Properties/AssemblyInfo.cs b/examples/msf/Validator/Properties/AssemblyInfo.cs deleted file mode 100644 index eb2f8ed71e6..00000000000 --- a/examples/msf/Validator/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("testSolver")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("testSolver")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c03c1084-d119-483f-80fe-c639eae75959")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/msf/Validator/Validator.csproj b/examples/msf/Validator/Validator.csproj deleted file mode 100644 index cfea3c80be5..00000000000 --- a/examples/msf/Validator/Validator.csproj +++ /dev/null @@ -1,123 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {54835857-129F-44C9-B529-A42158647B36} - Exe - Properties - Validator - Validator - v4.0 - 512 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - bin\x64\Debug\ - DEBUG;TRACE - full - x86 - true - GlobalSuppressions.cs - prompt - - - bin\x64\Release\ - TRACE - true - pdbonly - x64 - true - GlobalSuppressions.cs - prompt - - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - prompt - MinimumRecommendedRules.ruleset - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - prompt - MinimumRecommendedRules.ruleset - - - - ..\Microsoft.Solver.Foundation.dll - - - - - - - - - - - - - - - - - - - - - - {7340e664-f648-4ff7-89b2-f4da424996d3} - SolverFoundation.Plugin.Z3 - - - - - \ No newline at end of file diff --git a/examples/msf/Z3MSFPlugin.sln b/examples/msf/Z3MSFPlugin.sln deleted file mode 100644 index c3af1dc22b2..00000000000 --- a/examples/msf/Z3MSFPlugin.sln +++ /dev/null @@ -1,125 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3", "SolverFoundation.Plugin.Z3\SolverFoundation.Plugin.Z3.csproj", "{7340E664-F648-4FF7-89B2-F4DA424996D3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3.Tests", "SolverFoundation.Plugin.Z3.Tests\SolverFoundation.Plugin.Z3.Tests.csproj", "{280AEE2F-1FDB-4A27-BE37-14DC154C873B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Validator", "Validator\Validator.csproj", "{54835857-129F-44C9-B529-A42158647B36}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F1E99540-BA5E-46DF-9E29-6146A309CD18}" - ProjectSection(SolutionItems) = preProject - README = README - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - commercial_64|Any CPU = commercial_64|Any CPU - commercial_64|Mixed Platforms = commercial_64|Mixed Platforms - commercial_64|x64 = commercial_64|x64 - commercial_64|x86 = commercial_64|x86 - commercial|Any CPU = commercial|Any CPU - commercial|Mixed Platforms = commercial|Mixed Platforms - commercial|x64 = commercial|x64 - commercial|x86 = commercial|x86 - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.ActiveCfg = commercial_64|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.Build.0 = commercial_64|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.ActiveCfg = commercial_64|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.Build.0 = commercial_64|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x64.ActiveCfg = commercial_64|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.ActiveCfg = commercial_64|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.Build.0 = commercial_64|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.ActiveCfg = commercial|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.Build.0 = commercial|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.ActiveCfg = commercial|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.Build.0 = commercial|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x64.ActiveCfg = commercial|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.ActiveCfg = commercial|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.Build.0 = commercial|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x64.ActiveCfg = Debug|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.ActiveCfg = Debug|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.Build.0 = Debug|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.Build.0 = Release|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.Build.0 = Release|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x64.ActiveCfg = Release|Any CPU - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.ActiveCfg = Release|x86 - {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.Build.0 = Release|x86 - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.Build.0 = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.Build.0 = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x64.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x86.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.Build.0 = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.Build.0 = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x64.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x86.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x64.ActiveCfg = Debug|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.ActiveCfg = Debug|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.Build.0 = Debug|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.Build.0 = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x64.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.ActiveCfg = Release|Any CPU - {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.Build.0 = Release|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.Build.0 = Release|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.ActiveCfg = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.Build.0 = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.ActiveCfg = Release|x64 - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.Build.0 = Release|x64 - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.ActiveCfg = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.Build.0 = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.ActiveCfg = Release|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.Build.0 = Release|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.ActiveCfg = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.Build.0 = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.ActiveCfg = Release|x64 - {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.Build.0 = Release|x64 - {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.ActiveCfg = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.Build.0 = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.ActiveCfg = Debug|x64 - {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.Build.0 = Debug|x64 - {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.ActiveCfg = Debug|x86 - {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.Build.0 = Debug|x86 - {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.Build.0 = Release|Any CPU - {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.Build.0 = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.Release|x64.ActiveCfg = Release|x64 - {54835857-129F-44C9-B529-A42158647B36}.Release|x64.Build.0 = Release|x64 - {54835857-129F-44C9-B529-A42158647B36}.Release|x86.ActiveCfg = Release|x86 - {54835857-129F-44C9-B529-A42158647B36}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index 9adb9ee4b01..1e81f913149 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -38,7 +38,7 @@ void generic_model_converter::operator()(model_ref & md) { TRACE("model_converter", tout << "before generic_model_converter\n"; model_v2_pp(tout, *md); display(tout);); model_evaluator ev(*(md.get())); - ev.set_model_completion(true); + ev.set_model_completion(m_completion); ev.set_expand_array_equalities(false); expr_ref val(m); unsigned arity; @@ -78,7 +78,7 @@ void generic_model_converter::operator()(model_ref & md) { } if (reset_ev) { ev.reset(); - ev.set_model_completion(true); + ev.set_model_completion(m_completion); ev.set_expand_array_equalities(false); } break; diff --git a/src/ast/converters/model_converter.h b/src/ast/converters/model_converter.h index 164becc09d1..720324919c4 100644 --- a/src/ast/converters/model_converter.h +++ b/src/ast/converters/model_converter.h @@ -64,14 +64,17 @@ class smt2_pp_environment; class model_converter : public converter { protected: - smt2_pp_environment* m_env; + smt2_pp_environment* m_env = nullptr; + bool m_completion = true; static void display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e); void display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const; void display_del(std::ostream& out, func_decl* f) const; void display_add(std::ostream& out, ast_manager& m); public: - model_converter(): m_env(nullptr) {} + model_converter() {} + + void set_completion(bool f) { m_completion = f; } virtual void operator()(model_ref & m) = 0; diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index a05fbc68db7..6c2f4038f31 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -529,7 +529,7 @@ namespace smt { // issue #3532, #3529 // if (ctx.is_shared(r) || is_select_arg(r)) { - TRACE("array", tout << "new shared var: #" << r->get_owner_id() << "\n";); + TRACE("array", tout << "new shared var: #" << r->get_owner_id() << " " << is_select_arg(r) << "\n";); theory_var r_th_var = r->get_th_var(get_id()); SASSERT(r_th_var != null_theory_var); result.push_back(r_th_var); From 05957803a30019d9eb1d44e8335242db84c7c7cb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Jan 2023 20:25:30 -0800 Subject: [PATCH 301/597] update release notes for 12.2 Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index add32c445f3..a150c433f42 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -13,6 +13,7 @@ Version 4.next Version 4.12.2 ============== +- remove MSF (Microsoft Solver Foundation) plugin Version 4.12.1 ============== From 37652e7e173d08110c0c8e135c7c5dc50d44d98c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 20 Jan 2023 17:30:40 +0000 Subject: [PATCH 302/597] fix tactic name in docs --- src/tactic/arith/fm_tactic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/arith/fm_tactic.h b/src/tactic/arith/fm_tactic.h index 7021dc27304..8a40c0564ac 100644 --- a/src/tactic/arith/fm_tactic.h +++ b/src/tactic/arith/fm_tactic.h @@ -19,7 +19,7 @@ Use Fourier-Motzkin to eliminate variables. This strategy can handle conditional bounds (i.e., clauses with at most one constraint). -The strategy mk_occf can be used to put the +The tactic occf can be used to put the formula in OCC form. ### Example From 0f4f32c5d020e4d63fa84a64f3136f6ad84aefb2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 20 Jan 2023 13:04:50 -0800 Subject: [PATCH 303/597] apply relevancy filtering on unsupported ops, fix term construction bug in bv2fpa_converter fix #6548 --- src/ast/fpa/bv2fpa_converter.cpp | 4 ++-- src/smt/theory_arith.h | 3 +-- src/smt/theory_arith_aux.h | 3 +-- src/smt/theory_arith_core.h | 26 +++++++++++--------------- src/smt/theory_lra.cpp | 9 ++++++++- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index 597ab9ca6ad..00e9d71c320 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -324,8 +324,8 @@ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl * expr_ref else_value(m.mk_app(to_bv_i, dom.size(), dom.data()), m); result->set_else(else_value); } - else if (m_fpa_util.is_to_real(f)) { - expr_ref_vector dom(m); + else if (m_fpa_util.is_to_real(f)) { + SASSERT(dom.size() == 1); func_decl_ref to_real_i(m.mk_func_decl(fid, OP_FPA_TO_REAL_I, 0, nullptr, dom.size(), dom.data()), m); expr_ref else_value(m.mk_app(to_real_i, dom.size(), dom.data()), m); result->set_else(else_value); diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 07709666b45..34a76d95537 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -436,9 +436,8 @@ namespace smt { theory_arith_params & m_params; arith_util m_util; arith_eq_solver m_arith_eq_solver; - bool m_found_unsupported_op; - bool m_found_underspecified_op; ptr_vector m_underspecified_ops; + ptr_vector m_unsupported_ops; arith_eq_adapter m_arith_eq_adapter; vector m_rows; svector m_dead_rows; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index d5eca0bc47e..470ea5f7b4a 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -2169,9 +2169,8 @@ namespace smt { */ template bool theory_arith::is_shared(theory_var v) const { - if (!m_found_underspecified_op) { + if (m_underspecified_ops.empty()) return false; - } enode * n = get_enode(v); enode * r = n->get_root(); enode_vector::const_iterator it = r->begin_parents(); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 2d18a349652..3a5c86207c2 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -29,22 +29,16 @@ namespace smt { template void theory_arith::found_unsupported_op(app * n) { - if (!m_found_unsupported_op) { - TRACE("arith", tout << "found non supported expression:\n" << mk_pp(n, m) << "\n";); - ctx.push_trail(value_trail(m_found_unsupported_op)); - m_found_unsupported_op = true; - } + CTRACE("arith", m_unsupported_ops.empty(), tout << "found non supported expression:\n" << mk_pp(n, m) << "\n";); + m_unsupported_ops.push_back(n); + ctx.push_trail(push_back_vector>(m_unsupported_ops)); } template void theory_arith::found_underspecified_op(app * n) { + CTRACE("arith", m_underspecified_ops.empty(), tout << "found underspecified expression:\n" << mk_pp(n, m) << "\n";); m_underspecified_ops.push_back(n); ctx.push_trail(push_back_vector>(m_underspecified_ops)); - if (!m_found_underspecified_op) { - TRACE("arith", tout << "found underspecified expression:\n" << mk_pp(n, m) << "\n";); - ctx.push_trail(value_trail(m_found_underspecified_op)); - m_found_underspecified_op = true; - } expr* e = nullptr; if (m_util.is_div(n)) { @@ -1532,9 +1526,13 @@ namespace smt { } } while (m_final_check_idx != old_idx); - if (result == FC_DONE && m_found_unsupported_op) { - TRACE("arith", tout << "Found unsupported operation\n";); - result = FC_GIVEUP; + if (result == FC_DONE) { + for (app* n : m_unsupported_ops) { + if (!ctx.is_relevant(n)) + continue; + TRACE("arith", tout << "Found unsupported operation " << mk_pp(n, m) << "\n"); + result = FC_GIVEUP; + } } return result; } @@ -1733,8 +1731,6 @@ namespace smt { m_params(ctx.get_fparams()), m_util(m), m_arith_eq_solver(m), - m_found_unsupported_op(false), - m_found_underspecified_op(false), m_arith_eq_adapter(*this, m_util), m_asserted_qhead(0), m_row_vars_top(0), diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1309724d2ef..0507f7564ac 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -454,6 +454,11 @@ class theory_lra::imp { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } + else if (a.is_power(n, n1, n2)) { + found_unsupported(n); + st.to_ensure_var().push_back(n1); + st.to_ensure_var().push_back(n2); + } else if (!a.is_div0(n)) { found_unsupported(n); } @@ -543,7 +548,7 @@ class theory_lra::imp { } enode * mk_enode(app * n) { - TRACE("arith", tout << expr_ref(n, m) << " internalized: " << ctx().e_internalized(n) << "\n";); + TRACE("arith", tout << mk_bounded_pp(n, m) << " internalized: " << ctx().e_internalized(n) << "\n";); if (reflect(n)) for (expr* arg : *n) if (!ctx().e_internalized(arg)) @@ -1600,6 +1605,8 @@ class theory_lra::imp { return FC_CONTINUE; } for (expr* e : m_not_handled) { + if (!ctx().is_relevant(e)) + continue; (void) e; // just in case TRACE() is a no-op TRACE("arith", tout << "unhandled operator " << mk_pp(e, m) << "\n";); st = FC_GIVEUP; From 4e6d498a60201156c229de5c888ab5bc3c89a582 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 20 Jan 2023 14:37:05 -0800 Subject: [PATCH 304/597] adding placeholder for refining power of 2 --- src/smt/theory_lra.cpp | 46 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 0507f7564ac..6035208e3aa 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1556,6 +1556,33 @@ class theory_lra::imp { return !m_asserted_atoms.empty(); } + final_check_status eval_power(expr* e) { + return FC_GIVEUP; + expr* x, * y; + VERIFY(a.is_power(e, x, y)); + enode* xn = get_enode(x); + enode* yn = get_enode(y); + if (!xn || !yn) + return FC_GIVEUP; + rational valx, valy, valn; + bool vx = get_value(xn, valx); + bool vy = get_value(yn, valy); + enode* n = get_enode(e); + bool vn = get_value(n, valn); + verbose_stream() << vx << " " << valx << " " << vy << " " << valy << "\n"; + verbose_stream() << vn << " " << valn << "\n"; + // add tangent lemmas for power: + // valx > 0 valy > 0 => n >= rational_floor(valx^valy) + // establish equality using algebraic numerals + return FC_GIVEUP; + } + + final_check_status eval_unsupported(expr* e) { + if (a.is_power(e)) + return eval_power(e); + return FC_GIVEUP; + } + final_check_status final_check_eh() { if (propagate_core()) return FC_CONTINUE; @@ -1607,10 +1634,21 @@ class theory_lra::imp { for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; - (void) e; // just in case TRACE() is a no-op - TRACE("arith", tout << "unhandled operator " << mk_pp(e, m) << "\n";); - st = FC_GIVEUP; - } + st = FC_DONE; + switch (eval_unsupported(e)) { + case FC_CONTINUE: + st = FC_CONTINUE; + break; + case FC_GIVEUP: + if (st != FC_CONTINUE) + st = FC_GIVEUP; + break; + default: + break; + } + if (st == FC_CONTINUE) + break; + } return st; case l_false: get_infeasibility_explanation_and_set_conflict(); From 806a4772bcf8a088e274d6ff4779daa966d39894 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 20 Jan 2023 17:28:24 -0800 Subject: [PATCH 305/597] revert effect of filtering unsupported Signed-off-by: Nikolaj Bjorner --- src/math/lp/factorization.cpp | 2 +- src/smt/theory_lra.cpp | 61 ++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/math/lp/factorization.cpp b/src/math/lp/factorization.cpp index f81a011dcdd..229fca61f83 100644 --- a/src/math/lp/factorization.cpp +++ b/src/math/lp/factorization.cpp @@ -23,7 +23,7 @@ bool const_iterator_mon::get_factors(factor& k, factor& j, rational& sign) const std::sort(k_vars.begin(), k_vars.end()); std::sort(j_vars.begin(), j_vars.end()); - if (false && m_num_failures > 10) { + if (m_num_failures > 1000) { for (bool& m : m_mask) m = true; m_mask[0] = false; m_full_factorization_returned = true; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 6035208e3aa..1b1e0a1959a 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -454,8 +454,9 @@ class theory_lra::imp { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } - else if (a.is_power(n, n1, n2)) { + else if (a.is_power(n, n1, n2)) { found_unsupported(n); + if (!ctx().relevancy()) mk_power_axiom(n, n1, n2); st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } @@ -1099,6 +1100,16 @@ class theory_lra::imp { mk_is_int_axiom(n); else if (m.is_ite(n)) mk_ite_axiom(n); + else if (a.is_power(n, n1, n2)) + mk_power_axiom(n, n1, n2); + } + + void mk_power_axiom(expr* p, expr* x, expr* y) { + rational r; + if (a.is_extended_numeral(x, r) && r.is_unsigned() && r.is_pos()) { + expr_ref zero(a.mk_real(0), m); + mk_axiom(~mk_literal(a.mk_le(p, zero))); + } } // n < 0 || rem(a, n) = mod(a, n) @@ -1557,23 +1568,51 @@ class theory_lra::imp { } final_check_status eval_power(expr* e) { - return FC_GIVEUP; expr* x, * y; VERIFY(a.is_power(e, x, y)); + enode* n = get_enode(e); enode* xn = get_enode(x); enode* yn = get_enode(y); - if (!xn || !yn) + if (!n || !xn || !yn) return FC_GIVEUP; rational valx, valy, valn; - bool vx = get_value(xn, valx); - bool vy = get_value(yn, valy); - enode* n = get_enode(e); - bool vn = get_value(n, valn); - verbose_stream() << vx << " " << valx << " " << vy << " " << valy << "\n"; - verbose_stream() << vn << " " << valn << "\n"; + theory_var v = n->get_th_var(get_id()); + if (!get_value(xn, valx)) + return FC_GIVEUP; + if (!get_value(yn, valy)) + return FC_GIVEUP; + if (!get_value(n, valn)) + return FC_GIVEUP; + + verbose_stream() << valx << " " << valy << " " << valn << "\n"; + // TBD - check that values align so return FC_DONE. + + if (valn < 0 && valx > 0 && valy > 0) { + mk_axiom(mk_literal(a.mk_le(x, a.mk_numeral(rational(0), x->get_sort()))), + ~mk_literal(a.mk_le(e, a.mk_numeral(rational(0), e->get_sort())))); + return FC_CONTINUE; + } + // add tangent lemmas for power: - // valx > 0 valy > 0 => n >= rational_floor(valx^valy) // establish equality using algebraic numerals + + // appears to be useless: + if (false && !valy.is_int() && numerator(valy) == 1 && denominator(valy) > 0) { + rational d = denominator(valy); + if (!d.is_unsigned()) + return FC_GIVEUP; + unsigned den = d.get_unsigned(); + // e = x^{1/y} + // => e^y = x + + ptr_vector es; + for (unsigned i = 0; i < den; ++i) + es.push_back(e); + expr* em = a.mk_mul(es.size(), es.data()); + mk_axiom(~mk_literal(m.mk_eq(y, a.mk_real(valy))), mk_literal(m.mk_eq(em, x))); + return FC_CONTINUE; + + } return FC_GIVEUP; } @@ -1632,7 +1671,7 @@ class theory_lra::imp { return FC_CONTINUE; } for (expr* e : m_not_handled) { - if (!ctx().is_relevant(e)) + if (!ctx().is_relevant(e) && false) continue; st = FC_DONE; switch (eval_unsupported(e)) { From 7d364bf7862c6c84e786693988ec65dee2a7bc55 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 14:39:58 -0800 Subject: [PATCH 306/597] Allow building AC functions without requiring arity check from API --- src/api/api_ast.cpp | 7 +++++-- src/api/python/z3/z3.py | 4 ---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index a8f4d74b740..bc76d02bccb 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -660,11 +660,14 @@ extern "C" { LOG_Z3_get_domain(c, d, i); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); - if (i >= to_func_decl(d)->get_arity()) { + func_decl* _d = to_func_decl(d); + if (_d->is_associative()) + i = 0; + if (i >= _d->get_arity()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } - Z3_sort r = of_sort(to_func_decl(d)->get_domain(i)); + Z3_sort r = of_sort(_d->get_domain(i)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 074570b035d..cf5be3e07f7 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -763,8 +763,6 @@ def domain(self, i): >>> f.domain(1) Real """ - if z3_debug(): - _z3_assert(i < self.arity(), "Index out of bounds") return _to_sort_ref(Z3_get_domain(self.ctx_ref(), self.ast, i), self.ctx) def range(self): @@ -834,8 +832,6 @@ def __call__(self, *args): """ args = _get_args(args) num = len(args) - if z3_debug(): - _z3_assert(num == self.arity(), "Incorrect number of arguments to %s" % self) _args = (Ast * num)() saved = [] for i in range(num): From 021ef699af1c99239f80127423d31d0c63aba077 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 14:40:19 -0800 Subject: [PATCH 307/597] detect bounds from mod --- src/ast/simplifiers/bound_manager.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/ast/simplifiers/bound_manager.cpp b/src/ast/simplifiers/bound_manager.cpp index 0770bf4212a..130d00b5638 100644 --- a/src/ast/simplifiers/bound_manager.cpp +++ b/src/ast/simplifiers/bound_manager.cpp @@ -193,13 +193,11 @@ void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_ } bool bound_manager::is_equality_bound(expr * f, expr_dependency * d) { - expr* x, *y; - if (!m().is_eq(f, x, y)) { + expr* x, *y, *z, *u; + if (!m().is_eq(f, x, y)) return false; - } - if (!is_uninterp_const(x)) { + if (!is_uninterp_const(x)) std::swap(x, y); - } numeral n; bool is_int; if (is_uninterp_const(x) && is_numeral(y, n, is_int)) { @@ -207,9 +205,15 @@ bool bound_manager::is_equality_bound(expr * f, expr_dependency * d) { insert_upper(x, false, n, d); return true; } - else { - return false; + + // x = y mod n => 0 <= x < n + if (m_util.is_mod(y, z, u) && is_numeral(u, n, is_int) && n > 0) { + insert_lower(x, false, rational::zero(), d); + insert_upper(x, false, n - 1, d); + return true; } + + return false; } bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { From e2a6376ddf033917e7fa7617cce7e6144b43ff83 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 14:40:36 -0800 Subject: [PATCH 308/597] detect bounds from mod --- src/tactic/arith/propagate_ineqs_tactic.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp index 871a9ac3bcc..ddd2da3b986 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ b/src/tactic/arith/propagate_ineqs_tactic.cpp @@ -226,6 +226,7 @@ struct propagate_ineqs_tactic::imp { } expr * lhs = to_app(t)->get_arg(0); expr * rhs = to_app(t)->get_arg(1); + expr* a, *b; if (m_util.is_numeral(lhs)) { std::swap(lhs, rhs); if (k == LE) @@ -235,6 +236,16 @@ struct propagate_ineqs_tactic::imp { } rational c; + // x = y mod c => 0 <= x < c + if (k == EQ && m_util.is_mod(rhs, a, b) && m_util.is_numeral(b, c) && c > 0) { + a_var x = mk_linear_pol(lhs); + mpq c_prime; + nm.set(c_prime, (c-1).to_mpq()); + bp.assert_lower(x, mpq(0), false); + bp.assert_upper(x, c_prime, false); + nm.del(c_prime); + return lhs == a; + } if (!m_util.is_numeral(rhs, c)) return false; a_var x = mk_linear_pol(lhs); From dbc299efbb0ad79c5f92dc6f8df2aff34a951411 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 14:41:53 -0800 Subject: [PATCH 309/597] revise bv-bounds-tactic - share common functionality - rename propagate-bv-bounds-new to propagate-bv-bound2 for now - expose configuration options in bounds propagation --- src/tactic/bv/bv_bounds_tactic.cpp | 690 +++++++++++------------- src/tactic/bv/bv_bounds_tactic.h | 2 +- src/tactic/core/dom_simplify_tactic.cpp | 4 + src/tactic/core/dom_simplify_tactic.h | 12 +- 4 files changed, 314 insertions(+), 394 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 58183287d5c..f91068b40a9 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -32,39 +32,35 @@ static uint64_t uMaxInt(unsigned sz) { namespace { - struct interval { - // l < h: [l, h] - // l > h: [0, h] U [l, UMAX_INT] - uint64_t l = 0, h = 0; + template + struct interval_tpl : public Base { + T l, h; unsigned sz = 0; bool tight = true; - - interval() {} - - interval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { - // canonicalize full set - if (is_wrapped() && l == h + 1) { - this->l = 0; - this->h = uMaxInt(sz); - } - SASSERT(invariant()); - } + + interval_tpl(T const& l, T const& h, unsigned sz, bool tight = false): l(l), h(h), sz(sz), tight(tight) {} + interval_tpl() {} bool invariant() const { - return l <= uMaxInt(sz) && h <= uMaxInt(sz) && (!is_wrapped() || l != h+1); + return + 0 <= l && (l <= Base::bound(sz)) && + 0 <= h && (h <= Base::bound(sz)) && + (!is_wrapped() || l != h + 1); } - bool is_full() const { return l == 0 && h == uMaxInt(sz); } + bool is_full() const { + return l == 0 && h == Base::bound(sz); + } bool is_wrapped() const { return l > h; } bool is_singleton() const { return l == h; } - bool operator==(const interval& b) const { + bool operator==(const interval_tpl& b) const { SASSERT(sz == b.sz); return l == b.l && h == b.h && tight == b.tight; } - bool operator!=(const interval& b) const { return !(*this == b); } + bool operator!=(const interval_tpl& b) const { return !(*this == b); } - bool implies(const interval& b) const { + bool implies(const interval_tpl& b) const { if (b.is_full()) return true; else if (is_full()) @@ -81,7 +77,7 @@ namespace { } /// return false if intersection is unsat - bool intersect(const interval& b, interval& result) const { + bool intersect(const interval_tpl& b, interval_tpl& result) const { if (is_full() || *this == b) { result = b; return true; @@ -98,7 +94,7 @@ namespace { else if (b.h >= l) result = *this; else - result = interval(std::max(l, b.l), std::min(h, b.h), sz); + result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz); } else return b.intersect(*this, result); @@ -111,45 +107,145 @@ namespace { if (h >= b.l && l <= b.h) result = b; else if (h >= b.l) - result = interval(b.l, h, sz); + result = interval_tpl(b.l, h, sz); else { // ... l .. b.h .. h .. b.l ... SASSERT(l <= b.h); - result = interval(l, std::min(h, b.h), sz); + result = interval_tpl(l, std::min(h, b.h), sz); } } else { if (l > b.h || h < b.l) return false; // 0 .. l.. l' ... h ... h' - result = interval(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); + result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); } return true; } /// return false if negation is empty - bool negate(interval& result) const { + bool negate(interval_tpl& result) const { if (!tight) - result = interval(0, uMaxInt(sz), true); + result = interval_tpl(Base::zero(), Base::bound(sz), sz, true); else if (is_full()) return false; - else if (l == 0) - result = interval(h + 1, uMaxInt(sz), sz); - else if (uMaxInt(sz) == h) - result = interval(0, l - 1, sz); + else if (l == 0 && Base::bound(sz) == h) + result = interval_tpl(Base::zero(), Base::bound(sz), sz); + else if (l == 0) + result = interval_tpl(h + 1, Base::bound(sz), sz); + else if (Base::bound(sz) == h) + result = interval_tpl(Base::zero(), l - 1, sz); else - result = interval(h + 1, l - 1, sz); + result = interval_tpl(h + 1, l - 1, sz); return true; } + + + }; + + struct rinterval_base { + static rational bound(unsigned sz) { + return rational::power_of_two(sz) - 1; + } + + static rational zero() { return rational::zero(); } + }; + + struct rinterval : public interval_tpl { + rinterval(rational const& l, rational const& h, unsigned sz, bool tight = false) { + this->l = l; this->h = h; this->sz = sz; this->tight = tight; + } + rinterval() { l = 0; h = 0; tight = true; } + }; + + struct iinterval_base { + static uint64_t bound(unsigned sz) { return uMaxInt(sz); } + static uint64_t zero() { return 0; } + }; + + struct iinterval : public interval_tpl { + iinterval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) { + this->l = l; this->h = h; this->sz = sz; this->tight = tight; + } + iinterval() { l = 0; h = 0; sz = 0; tight = true; } + }; + + struct interval { + bool is_small = true; + iinterval i; + rinterval r; + + interval() {} + + interval(rational const& l, rational const& h, unsigned sz, bool tight = false) { + if (sz <= 64) { + is_small = true; + i.l = l.get_uint64(); + i.h = h.get_uint64(); + i.tight = tight; + i.sz = sz; + } + else { + is_small = false; + r.l = l; + r.h = h; + r.tight = tight; + r.sz = sz; + } + } + + unsigned size() const { + return is_small ? i.sz : r.sz; + } + + bool negate(interval& result) const { + result.is_small = is_small; + if (is_small) + return i.negate(result.i); + else + return r.negate(result.r); + } + + bool intersect(interval const& b, interval & result) const { + result.is_small = is_small; + SASSERT(b.is_small == is_small); + if (is_small) + return i.intersect(b.i, result.i); + else + return r.intersect(b.r, result.r); + } + + bool operator==(interval const& other) const { + SASSERT(is_small == other.is_small); + return is_small ? i == other.i : r == other.r; + } + + bool operator!=(interval const& other) const { + return !(*this == other); + } + + bool is_singleton() const { return is_small ? i.is_singleton() : r.is_singleton(); } + + bool is_full() const { return is_small ? i.is_full() : r.is_full(); } + + bool tight() const { return is_small ? i.tight : r.tight; } + + bool implies(const interval& b) const { + SASSERT(is_small == b.is_small); + return is_small ? i.implies(b.i) : r.implies(b.r); + } + + rational lo() const { return is_small ? rational(i.l, rational::ui64()) : r.l; } + rational hi() const { return is_small ? rational(i.h, rational::ui64()) : r.h; } }; -#ifdef _TRACE + std::ostream& operator<<(std::ostream& o, const interval& I) { - o << "[" << I.l << ", " << I.h << "]"; - return o; + if (I.is_small) + return o << "[" << I.i.l << ", " << I.i.h << "]"; + else + return o << "[" << I.r.l << ", " << I.r.h << "]"; } -#endif - struct undo_bound { expr* e = nullptr; @@ -158,113 +254,100 @@ namespace { undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} }; - class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { + struct bv_bounds_base { typedef obj_map map; typedef obj_map expr_set; typedef obj_map expr_cnt; ast_manager& m; - params_ref m_params; - bool m_propagate_eq = false; bv_util m_bv; vector m_scopes; - map m_bound; svector m_expr_vars; svector m_bound_exprs; + map m_bound; + bool m_propagate_eq = false; - bool is_number(expr *e, uint64_t& n, unsigned& sz) const { - rational r; - if (m_bv.is_numeral(e, r, sz) && sz <= 64) { - n = r.get_uint64(); - return true; - } - return false; + bv_bounds_base(ast_manager& m):m(m), m_bv(m) {} + + virtual ~bv_bounds_base() { + for (auto* e : m_expr_vars) + dealloc(e); + for (auto* b : m_bound_exprs) + dealloc(b); } bool is_bound(expr *e, expr*& v, interval& b) const { - uint64_t n; + rational r; expr *lhs = nullptr, *rhs = nullptr; unsigned sz; if (m_bv.is_bv_ule(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { // C ule x <=> x uge C + if (m_bv.is_numeral(lhs, r, sz)) { // C ule x <=> x uge C if (m_bv.is_numeral(rhs)) return false; - b = interval(n, uMaxInt(sz), sz, true); + b = interval(r, rational::power_of_two(sz) - 1, sz, true); v = rhs; - return true; + return true; } - if (is_number(rhs, n, sz)) { // x ule C - b = interval(0, n, sz, true); + if (m_bv.is_numeral(rhs, r, sz)) { // x ule C + b = interval(rational::zero(), r, sz, true); v = lhs; return true; } + // TBD: x + s <= x + q + // x + s <= x + // x <= x + q } else if (m_bv.is_bv_sle(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { // C sle x <=> x sge C + if (m_bv.is_numeral(lhs, r, sz)) { // C sle x <=> x sge C if (m_bv.is_numeral(rhs)) return false; - b = interval(n, (1ull << (sz-1)) - 1, sz, true); + b = interval(r, rational::power_of_two(sz-1) - 1, sz, true); v = rhs; return true; } - if (is_number(rhs, n, sz)) { // x sle C - b = interval(1ull << (sz-1), n, sz, true); + if (m_bv.is_numeral(rhs, r, sz)) { // x sle C + b = interval(rational::power_of_two(sz-1), r, sz, true); v = lhs; return true; } + // TBD: other cases for forbidden intervals } else if (m.is_eq(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { - if (m_bv.is_numeral(rhs)) - return false; - b = interval(n, n, sz, true); + if (m_bv.is_numeral(rhs)) + std::swap(lhs, rhs); + if (m_bv.is_numeral(rhs)) + return false; + if (m_bv.is_numeral(lhs, r, sz)) { + unsigned lo, hi; + expr* rhs2; + if (m_bv.is_extract(rhs, lo, hi, rhs2) && r == 0) { + unsigned sz2 = m_bv.get_bv_size(rhs2); + if (sz2 - 1 == hi) { + b = interval(rational::zero(), rational::power_of_two(lo) - 1, sz2, false); + v = rhs2; + return true; + } + } + b = interval(r, r, sz, true); v = rhs; return true; } - if (is_number(rhs, n, sz)) { - b = interval(n, n, sz, true); - v = lhs; - return true; - } } return false; } - - public: - bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { - updt_params(p); - } - - void updt_params(params_ref const & p) override { - m_propagate_eq = p.get_bool("propagate_eq", false); - } - - static void get_param_descrs(param_descrs& r) { - r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); - } - - ~bv_bounds_simplifier() override { - for (auto* v : m_expr_vars) dealloc(v); - for (auto* b : m_bound_exprs) dealloc(b); - } - - bool assert_expr(expr * t, bool sign) override { - TRACE("bv", tout << expr_ref(t, m) << "\n";); - while (m.is_not(t, t)) { - sign = !sign; - } + bool assert_expr_core(expr * t, bool sign) { + while (m.is_not(t, t)) + sign = !sign; interval b; expr* t1; if (is_bound(t, t1, b)) { - SASSERT(!m_bv.is_numeral(t1)); - if (sign) { - if (!b.negate(b)) { - return false; - } - } + SASSERT(m_bv.get_bv_size(t1) == b.size()); + SASSERT(!m_bv.is_numeral(t1)); + if (sign && !b.negate(b)) + return false; TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); map::obj_map_entry* e = m_bound.find_core(t1); @@ -275,22 +358,44 @@ namespace { return false; if (old == intr) return true; - m_scopes.insert(undo_bound(t1, old, false)); + m_scopes.push_back(undo_bound(t1, old, false)); old = intr; - } else { + SASSERT(old.size() == m_bv.get_bv_size(t1)); + } + else { + SASSERT(b.size() == m_bv.get_bv_size(t1)); m_bound.insert(t1, b); - m_scopes.insert(undo_bound(t1, interval(), true)); + m_scopes.push_back(undo_bound(t1, interval(), true)); } } return true; } - bool simplify(expr* t, expr_ref& result) override { + // + // x + q <= s <=> x not in [s - q + 1, -q[ + // <=> x in [-q, s - q], s != -1 + // + // x in [lo, hi] + // q = -lo + // hi = s + lo => s = hi - lo + // hi - lo != -1 + // + + expr_ref mk_bound(expr* t, rational const& lo, rational const& hi) { + sort* s = t->get_sort(); + + if (lo == hi + 1) + return expr_ref(m.mk_true(), m); + else + return expr_ref(m_bv.mk_ule(m_bv.mk_bv_add(t, m_bv.mk_numeral(-lo, s)), m_bv.mk_numeral(hi - lo, s)), m); + } + + bool simplify_core(expr* t, expr_ref& result) { expr* t1; interval b; if (m_bound.find(t, b) && b.is_singleton()) { - result = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); + result = m_bv.mk_numeral(b.lo(), m_bv.get_bv_size(t)); return true; } @@ -298,14 +403,13 @@ namespace { return false; bool sign = false; - while (m.is_not(t, t)) { + while (m.is_not(t, t)) sign = !sign; - } if (!is_bound(t, t1, b)) return false; - if (sign && b.tight) { + if (sign && b.tight()) { sign = false; if (!b.negate(b)) { result = m.mk_false(); @@ -316,24 +420,26 @@ namespace { interval ctx, intr; result = nullptr; - if (b.is_full() && b.tight) { + if (b.is_full() && b.tight()) result = m.mk_true(); - } else if (m_bound.find(t1, ctx)) { - if (ctx.implies(b)) { - result = m.mk_true(); - } - else if (!b.intersect(ctx, intr)) { - result = m.mk_false(); - } - else if (m_propagate_eq && intr.is_singleton()) { - result = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), t1->get_sort())); - } + else if (!m_bound.find(t1, ctx)) { + } + else if (ctx.implies(b)) + result = m.mk_true(); + else if (!b.intersect(ctx, intr)) + result = m.mk_false(); + else if (m_propagate_eq && intr.is_singleton()) + result = m.mk_eq(t1, m_bv.mk_numeral(intr.lo(), t1->get_sort())); + else if (false && intr != b) + result = mk_bound(t1, intr.lo(), intr.hi()); + else { + TRACE("bv", tout << mk_pp(t, m) << " b: " << b << " ctx: " << ctx << " intr " << intr << "\n"); } - CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); - if (sign && result != 0) + CTRACE("bv", result, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); + if (sign && result) result = m.mk_not(result); - return result != 0; + return result != nullptr; } // check if t contains v @@ -344,18 +450,16 @@ namespace { while (!todo.empty()) { t = todo.back(); todo.pop_back(); - if (mark.is_marked(t)) { - continue; - } + if (mark.is_marked(t)) + continue; if (t == v) { todo.reset(); return true; } mark.mark(t); - if (!is_app(t)) { - continue; - } + if (!is_app(t)) + continue; app* a = to_app(t); todo.append(a->get_num_args(), a->get_args()); } @@ -398,30 +502,7 @@ namespace { return false; } - bool may_simplify(expr* t) override { - if (m_bv.is_numeral(t)) - return false; - - while (m.is_not(t, t)); - - for (auto & v : m_bound) { - if (contains(t, v.m_key)) return true; - } - - expr* t1; - interval b; - // skip common case: single bound constraint without any context for simplification - if (is_bound(t, t1, b)) { - return b.is_full() || m_bound.contains(t1); - } - - if (contains_bound(t)) { - return true; - } - return false; - } - - void pop(unsigned num_scopes) override { + void pop_core(unsigned num_scopes) { TRACE("bv", tout << "pop: " << num_scopes << "\n";); if (m_scopes.empty()) return; @@ -431,107 +512,28 @@ namespace { m_scopes.reset(); return; } - for (unsigned i = m_scopes.size()-1; i >= target; --i) { + for (unsigned i = m_scopes.size(); i-- > target; ) { undo_bound& undo = m_scopes[i]; SASSERT(m_bound.contains(undo.e)); - if (undo.fresh) { + if (undo.fresh) m_bound.erase(undo.e); - } else { - m_bound.insert(undo.e, undo.b); - } + else + m_bound.insert(undo.e, undo.b); } m_scopes.shrink(target); } - simplifier * translate(ast_manager & m) override { - return alloc(bv_bounds_simplifier, m, m_params); - } - - unsigned scope_level() const override { - return m_scopes.size(); - } }; - - class dom_bv_bounds_simplifier : public dom_simplifier { - typedef obj_map map; - typedef obj_map expr_set; - typedef obj_map expr_cnt; - - ast_manager& m; + class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier, public bv_bounds_base { params_ref m_params; - bool m_propagate_eq; - bv_util m_bv; - vector m_scopes; - map m_bound; - svector m_expr_vars; - svector m_bound_exprs; - - bool is_number(expr *e, uint64_t& n, unsigned& sz) const { - rational r; - if (m_bv.is_numeral(e, r, sz) && sz <= 64) { - n = r.get_uint64(); - return true; - } - return false; - } - - bool is_bound(expr *e, expr*& v, interval& b) const { - uint64_t n; - expr *lhs = nullptr, *rhs = nullptr; - unsigned sz = 0; - - if (m_bv.is_bv_ule(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { // C ule x <=> x uge C - if (m_bv.is_numeral(rhs)) - return false; - b = interval(n, uMaxInt(sz), sz, true); - v = rhs; - return true; - } - if (is_number(rhs, n, sz)) { // x ule C - b = interval(0, n, sz, true); - v = lhs; - return true; - } - } - else if (m_bv.is_bv_sle(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { // C sle x <=> x sge C - if (m_bv.is_numeral(rhs)) - return false; - b = interval(n, (1ull << (sz-1)) - 1, sz, true); - v = rhs; - return true; - } - if (is_number(rhs, n, sz)) { // x sle C - b = interval(1ull << (sz-1), n, sz, true); - v = lhs; - return true; - } - } else if (m.is_eq(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { - if (m_bv.is_numeral(rhs)) - return false; - b = interval(n, n, sz, true); - v = rhs; - return true; - } - if (is_number(rhs, n, sz)) { - b = interval(n, n, sz, true); - v = lhs; - return true; - } - } - return false; - } - public: - dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { + bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv_bounds_base(m), m_params(p) { updt_params(p); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_propagate_eq = p.get_bool("propagate_eq", false); } @@ -539,182 +541,92 @@ namespace { r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); } - ~dom_bv_bounds_simplifier() override { - for (auto* e : m_expr_vars) - dealloc(e); - for (auto* b : m_bound_exprs) - dealloc(b); - } + ~bv_bounds_simplifier() override {} bool assert_expr(expr * t, bool sign) override { - while (m.is_not(t, t)) - sign = !sign; - - interval b; - expr* t1; - if (is_bound(t, t1, b)) { - SASSERT(!m_bv.is_numeral(t1)); - if (sign) - VERIFY(b.negate(b)); - - TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); - map::obj_map_entry* e = m_bound.find_core(t1); - if (e) { - interval& old = e->get_data().m_value; - interval intr; - if (!old.intersect(b, intr)) - return false; - if (old == intr) - return true; - m_scopes.push_back(undo_bound(t1, old, false)); - old = intr; - } - else { - m_bound.insert(t1, b); - m_scopes.push_back(undo_bound(t1, interval(), true)); - } - } - return true; + return assert_expr_core(t, sign); } - void operator()(expr_ref& r) override { - expr* t1, * t = r; - interval b; - - if (m_bound.find(t, b) && b.is_singleton()) { - r = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); - return; - } - - if (!m.is_bool(t)) - return; - - bool sign = false; - while (m.is_not(t, t)) - sign = !sign; - - if (!is_bound(t, t1, b)) - return; - - if (sign && b.tight) { - sign = false; - if (!b.negate(b)) { - r = m.mk_false(); - return; - } - } + bool simplify(expr* t, expr_ref& result) override { + return simplify_core(t, result); + } - interval ctx, intr; - bool was_updated = true; - if (b.is_full() && b.tight) - r = m.mk_true(); - else if (m_bound.find(t1, ctx)) { - if (ctx.implies(b)) - r = m.mk_true(); - else if (!b.intersect(ctx, intr)) - r = m.mk_false(); - else if (m_propagate_eq && intr.is_singleton()) - r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), - t1->get_sort())); - else - was_updated = false; - } - else - was_updated = false; + bool may_simplify(expr* t) override { + if (m_bv.is_numeral(t)) + return false; - TRACE("bv", tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); - if (sign && was_updated) - r = m.mk_not(r); - } + while (m.is_not(t, t)); - // check if t contains v - ptr_vector todo; - bool contains(expr* t, expr* v) { - ast_fast_mark1 mark; - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - todo.pop_back(); - if (mark.is_marked(t)) - continue; - if (t == v) { - todo.reset(); + for (auto & v : m_bound) + if (contains(t, v.m_key)) return true; - } - mark.mark(t); - - if (!is_app(t)) - continue; - app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); - } - return false; - } - bool contains_bound(expr* t) { - ast_fast_mark1 mark1; - ast_fast_mark2 mark2; - - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - todo.pop_back(); - if (mark1.is_marked(t)) - continue; - mark1.mark(t); - if (!is_app(t)) - continue; - interval b; - expr* e; - if (is_bound(t, e, b)) { - if (mark2.is_marked(e)) { - todo.reset(); - return true; - } - mark2.mark(e); - if (m_bound.contains(e)) { - todo.reset(); - return true; - } - } + expr* t1; + interval b; + // skip common case: single bound constraint without any context for simplification + if (is_bound(t, t1, b)) + return b.is_full() || m_bound.contains(t1); - app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); - } - return false; + return contains_bound(t); } void pop(unsigned num_scopes) override { - TRACE("bv", tout << "pop: " << num_scopes << "\n";); - if (m_scopes.empty()) - return; - unsigned target = m_scopes.size() - num_scopes; - if (target == 0) { - m_bound.reset(); - m_scopes.reset(); - return; - } - for (unsigned i = m_scopes.size(); i-- > target; ) { - undo_bound& undo = m_scopes[i]; - SASSERT(m_bound.contains(undo.e)); - if (undo.fresh) - m_bound.erase(undo.e); - else - m_bound.insert(undo.e, undo.b); - } - m_scopes.shrink(target); + pop_core(num_scopes); } - dom_simplifier * translate(ast_manager & m) override { - return alloc(dom_bv_bounds_simplifier, m, m_params); + simplifier * translate(ast_manager & m) override { + return alloc(bv_bounds_simplifier, m, m_params); } unsigned scope_level() const override { return m_scopes.size(); } - }; + + class dom_bv_bounds_simplifier : public dom_simplifier, public bv_bounds_base { + params_ref m_params; + + public: + dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv_bounds_base(m), m_params(p) { + updt_params(p); + } + + ~dom_bv_bounds_simplifier() override { + } + + void updt_params(params_ref const & p) override { + m_propagate_eq = p.get_bool("propagate_eq", false); + } + + void collect_param_descrs(param_descrs& r) override { + r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); + } + + bool assert_expr(expr * t, bool sign) override { + return assert_expr_core(t, sign); + } + + void operator()(expr_ref& r) override { + expr_ref result(m); + simplify_core(r, result); + if (result) + r = result; + } + + void pop(unsigned num_scopes) override { + pop_core(num_scopes); + } + + dom_simplifier * translate(ast_manager & m) override { + return alloc(dom_bv_bounds_simplifier, m, m_params); + } + + unsigned scope_level() const override { + return m_scopes.size(); + } + }; + } tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/tactic/bv/bv_bounds_tactic.h b/src/tactic/bv/bv_bounds_tactic.h index 325cca89e2b..8153ad52f75 100644 --- a/src/tactic/bv/bv_bounds_tactic.h +++ b/src/tactic/bv/bv_bounds_tactic.h @@ -46,7 +46,7 @@ tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ ADD_TACTIC("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_tactic(m, p)") - ADD_TACTIC("propagate-bv-bounds-new", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)") + ADD_TACTIC("propagate-bv-bounds2", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)") */ diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 9bf70ab16a7..82fb7ec4452 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -542,6 +542,10 @@ class expr_substitution_simplifier : public dom_simplifier { public: expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {} + void updt_params(params_ref const & p) override {} + + void collect_param_descrs(param_descrs& r) override {} + bool assert_expr(expr * t, bool sign) override { expr* tt; if (m.is_not(t, tt)) diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index b4c83d04eeb..771c8c65499 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -105,9 +105,13 @@ class dom_simplifier { virtual dom_simplifier * translate(ast_manager & m) = 0; virtual unsigned scope_level() const = 0; - + + virtual void updt_params(params_ref const & p) = 0; + + virtual void collect_param_descrs(param_descrs& r) = 0; }; + class dom_simplify_tactic : public tactic { ast_manager& m; dom_simplifier* m_simplifier; @@ -156,13 +160,13 @@ class dom_simplify_tactic : public tactic { char const* name() const override { return "dom_simplify"; } tactic * translate(ast_manager & m) override; - void updt_params(params_ref const & p) override {} - static void get_param_descrs(param_descrs & r) {} - void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } + void updt_params(params_ref const & p) override { m_simplifier->updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_simplifier->collect_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override; void cleanup() override; }; + tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* From 83662701b688000e94b71919626e0298d8165e1d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 16:27:48 -0800 Subject: [PATCH 310/597] Update theory_lra.cpp remove spurious output --- src/smt/theory_lra.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1b1e0a1959a..500e0307c37 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1584,7 +1584,6 @@ class theory_lra::imp { if (!get_value(n, valn)) return FC_GIVEUP; - verbose_stream() << valx << " " << valy << " " << valn << "\n"; // TBD - check that values align so return FC_DONE. if (valn < 0 && valx > 0 && valy > 0) { From db79346ef7ee0bf2a29da844f01512cb267b14bf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 22:07:18 -0800 Subject: [PATCH 311/597] Add new tactic bound-simplifier for integer-based bit-vector reasoning. --- RELEASE_NOTES.md | 5 + src/ast/simplifiers/CMakeLists.txt | 3 + .../simplifiers}/bound_propagator.cpp | 2 +- .../simplifiers}/bound_propagator.h | 2 +- src/ast/simplifiers/bound_simplifier.cpp | 306 ++++++++++++++++++ src/ast/simplifiers/bound_simplifier.h | 90 ++++++ src/ast/simplifiers/extract_eqs.cpp | 6 +- .../simplifiers}/linear_equation.cpp | 2 +- .../simplifiers}/linear_equation.h | 0 src/ast/simplifiers/solve_eqs.cpp | 1 + src/tactic/arith/CMakeLists.txt | 3 +- src/tactic/arith/bound_simplifier_tactic.h | 41 +++ src/tactic/arith/card2bv_tactic.h | 2 +- src/tactic/arith/propagate_ineqs_tactic.cpp | 9 +- 14 files changed, 460 insertions(+), 12 deletions(-) rename src/{tactic/arith => ast/simplifiers}/bound_propagator.cpp (99%) rename src/{tactic/arith => ast/simplifiers}/bound_propagator.h (99%) create mode 100644 src/ast/simplifiers/bound_simplifier.cpp create mode 100644 src/ast/simplifiers/bound_simplifier.h rename src/{tactic/arith => ast/simplifiers}/linear_equation.cpp (99%) rename src/{tactic/arith => ast/simplifiers}/linear_equation.h (100%) create mode 100644 src/tactic/arith/bound_simplifier_tactic.h diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a150c433f42..868e9b690be 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -14,6 +14,11 @@ Version 4.next Version 4.12.2 ============== - remove MSF (Microsoft Solver Foundation) plugin +- add bound_simplifier tactic. + It eliminates occurrences of "mod" operators when bounds information + implies that the modulus is redundant. This tactic is useful for + benchmarks created by converting bit-vector semantics to integer + reasoning. Version 4.12.1 ============== diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index bd39395c043..74c544ffe72 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -2,6 +2,8 @@ z3_add_component(simplifiers SOURCES bit_blaster.cpp bound_manager.cpp + bound_propagator.cpp + bound_simplifier.cpp bv_slice.cpp card2bv.cpp demodulator_simplifier.cpp @@ -11,6 +13,7 @@ z3_add_component(simplifiers eliminate_predicates.cpp euf_completion.cpp extract_eqs.cpp + linear_equation.cpp max_bv_sharing.cpp model_reconstruction_trail.cpp propagate_values.cpp diff --git a/src/tactic/arith/bound_propagator.cpp b/src/ast/simplifiers/bound_propagator.cpp similarity index 99% rename from src/tactic/arith/bound_propagator.cpp rename to src/ast/simplifiers/bound_propagator.cpp index ba58b61603c..c216928be5c 100644 --- a/src/tactic/arith/bound_propagator.cpp +++ b/src/ast/simplifiers/bound_propagator.cpp @@ -17,7 +17,7 @@ Module Name: Revision History: --*/ -#include "tactic/arith/bound_propagator.h" +#include "ast/simplifiers/bound_propagator.h" #include // ------------------------------- diff --git a/src/tactic/arith/bound_propagator.h b/src/ast/simplifiers/bound_propagator.h similarity index 99% rename from src/tactic/arith/bound_propagator.h rename to src/ast/simplifiers/bound_propagator.h index d7b649c393c..2f609d2fc82 100644 --- a/src/tactic/arith/bound_propagator.h +++ b/src/ast/simplifiers/bound_propagator.h @@ -24,7 +24,7 @@ Revision History: #include "util/params.h" #include "util/statistics.h" #include "util/numeral_buffer.h" -#include "tactic/arith/linear_equation.h" +#include "ast/simplifiers/linear_equation.h" class bound_propagator { public: diff --git a/src/ast/simplifiers/bound_simplifier.cpp b/src/ast/simplifiers/bound_simplifier.cpp new file mode 100644 index 00000000000..08553772033 --- /dev/null +++ b/src/ast/simplifiers/bound_simplifier.cpp @@ -0,0 +1,306 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bounds_simplifier.cpp + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-22 + +--*/ + + +#include "ast/ast_pp.h" +#include "ast/simplifiers/bound_simplifier.h" +#include "ast/rewriter/rewriter_def.h" + +struct bound_simplifier::rw_cfg : public default_rewriter_cfg { + ast_manager& m; + bound_propagator& bp; + bound_simplifier& s; + arith_util a; + rw_cfg(bound_simplifier& s):m(s.m), bp(s.bp), s(s), a(m) {} + + br_status reduce_app(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result, proof_ref& pr) { + rational N, hi, lo; + bool strict; + if (a.is_mod(f) && num_args == 2 && a.is_numeral(args[1], N)) { + expr* x = args[0]; + if (!s.has_upper(x, hi, strict) || strict) + return BR_FAILED; + if (!s.has_lower(x, lo, strict) || strict) + return BR_FAILED; + if (hi - lo >= N) + return BR_FAILED; + if (N > hi && lo >= 0) { + result = x; + return BR_DONE; + } + if (2*N > hi && lo >= N) { + result = a.mk_sub(x, a.mk_int(N)); + s.m_rewriter(result); + return BR_DONE; + } + IF_VERBOSE(2, verbose_stream() << "potentially missed simplification: " << mk_pp(x, m) << " " << lo << " " << hi << " not reduced\n"); + } + return BR_FAILED; + } +}; + +struct bound_simplifier::rw : public rewriter_tpl { + rw_cfg m_cfg; + + rw(bound_simplifier& s): + rewriter_tpl(s.m, false, m_cfg), + m_cfg(s) { + } +}; + +void bound_simplifier::reduce() { + + m_updated = true; + for (unsigned i = 0; i < 5 && m_updated; ++i) { + m_updated = false; + reset(); + for (unsigned idx : indices()) + insert_bound(m_fmls[idx]); + for (unsigned idx : indices()) + tighten_bound(m_fmls[idx]); + + rw rw(*this); + + // TODO: take over propagate_ineq: + // bp.propagate(); + // serialize bounds + + proof_ref pr(m); + expr_ref r(m); + for (unsigned idx : indices()) { + auto const& d = m_fmls[idx]; + rw(d.fml(), r, pr); + if (r != d.fml()) { + m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep())); + m_updated = true; + } + } + } +} + +// generalization to summations? + +bool bound_simplifier::is_offset(expr* e, expr* x, rational& n) { + expr* y, *z; + if (a.is_add(e, y, z)) { + if (x != y) + std::swap(y, z); + return x == y && a.is_numeral(z, n); + } + return false; +} + +bool bound_simplifier::insert_bound(dependent_expr const& de) { + if (de.pr()) + return false; + if (de.dep()) + return false; + rational n, n0; + expr* x, *y, *f = de.fml(); + + if (m.is_eq(f, x, y)) { + if (a.is_numeral(y)) + std::swap(x, y); + if (a.is_numeral(x, n)) { + assert_lower(y, n, false); + assert_upper(y, n, false); + return true; + } + } + else if (a.is_le(f, x, y)) { + if (a.is_numeral(x, n)) + assert_lower(y, n, false); + else if (a.is_numeral(y, n)) + assert_upper(x, n, false); + else + return false; + return true; + } + else if (a.is_ge(f, x, y)) { + if (a.is_numeral(x, n)) + assert_upper(y, n, false); + else if (a.is_numeral(y, n)) + assert_lower(x, n, false); + else + return false; + return true; + } + else if (m.is_not(f, f)) { + if (a.is_le(f, x, y)) { + if (a.is_numeral(x, n)) + assert_upper(y, n, true); + else if (a.is_numeral(y, n)) + assert_lower(x, n, true); + else + return false; + return true; + } + else if (a.is_ge(f, x, y)) { + if (a.is_numeral(x, n)) + assert_lower(y, n, true); + else if (a.is_numeral(y, n)) + assert_upper(x, n, true); + else + return false; + return true; + } + } + return false; +} + +void bound_simplifier::tighten_bound(dependent_expr const& de) { + if (de.pr()) + return; + if (de.dep()) + return; + rational n, k; + expr* x, *y, *f = de.fml(); + expr* z, *u; + bool strict; + if (a.is_le(f, x, y)) { + // x <= (x + k) mod N && x >= 0 -> x + k < N + if (a.is_mod(y, z, u) && a.is_numeral(u, n) && has_lower(x, k, strict) && k >= 0 && is_offset(z, x, k) && k > 0 && k < n) { + assert_upper(x, n - k, true); + } + } + if (m.is_not(f, f) && m.is_eq(f, x, y)) { + if (a.is_numeral(x)) + std::swap(x, y); + bool strict; + if (a.is_numeral(y, n)) { + if (has_lower(x, k, strict) && !strict && k == n) + assert_lower(x, k, true); + else if (has_upper(x, k, strict) && !strict && k == n) + assert_upper(x, k, true); + } + } +} + +void bound_simplifier::assert_upper(expr* x, rational const& n, bool strict) { + scoped_mpq c(nm); + nm.set(c, n.to_mpq()); + bp.assert_upper(to_var(x), c, strict); +} + + +void bound_simplifier::assert_lower(expr* x, rational const& n, bool strict) { + scoped_mpq c(nm); + nm.set(c, n.to_mpq()); + bp.assert_lower(to_var(x), c, strict); +} + +// +// TODO: Use math/interval/interval.h +// + +bool bound_simplifier::has_lower(expr* x, rational& n, bool& strict) { + if (is_var(x)) { + unsigned v = to_var(x); + if (bp.has_lower(v)) { + mpq const & q = bp.lower(v, strict); + n = rational(q); + return true; + } + } + if (a.is_numeral(x, n)) { + strict = false; + return true; + } + if (a.is_mod(x)) { + n = rational::zero(); + strict = false; + return true; + } + expr* y, *z; + if (a.is_idiv(x, y, z) && has_lower(z, n, strict) && n > 0 && has_lower(y, n, strict)) + return true; + + if (a.is_add(x)) { + rational bound; + strict = false; + n = 0; + bool is_strict; + for (expr* arg : *to_app(x)) { + if (!has_lower(arg, bound, is_strict)) + return false; + strict |= is_strict; + n += bound; + } + return true; + } + + if (a.is_mul(x, y, z)) { + // TODO: this is done generally using math/interval/interval.h + rational bound1, bound2; + bool strict1, strict2; + if (has_lower(y, bound1, strict1) && !strict1 && + has_lower(z, bound1, strict2) && !strict2 && + bound1 >= 0 && bound2 >= 0) { + n = bound1*bound2; + strict = false; + return true; + } + } + + return false; +} + + +bool bound_simplifier::has_upper(expr* x, rational& n, bool& strict) { + if (is_var(x)) { + unsigned v = to_var(x); + if (bp.has_upper(v)) { + mpq const & q = bp.upper(v, strict); + n = rational(q); + return true; + } + } + + // perform light-weight abstract analysis + // y * (u / y) is bounded by u, if y > 0 + + if (a.is_numeral(x, n)) { + strict = false; + return true; + } + + if (a.is_add(x)) { + rational bound; + strict = false; + n = 0; + bool is_strict; + for (expr* arg : *to_app(x)) { + if (!has_upper(arg, bound, is_strict)) + return false; + strict |= is_strict; + n += bound; + } + return true; + } + + expr* y, *z, *u, *v; + if (a.is_mul(x, y, z) && a.is_idiv(z, u, v) && v == y && has_lower(y, n, strict) && n > 0 && has_upper(u, n, strict)) + return true; + if (a.is_idiv(x, y, z) && has_lower(z, n, strict) && n > 0 && has_upper(y, n, strict)) + return true; + + return false; +} + + +void bound_simplifier::reset() { + bp.reset(); + m_var2expr.reset(); + m_expr2var.reset(); + m_num_vars = 0; +} diff --git a/src/ast/simplifiers/bound_simplifier.h b/src/ast/simplifiers/bound_simplifier.h new file mode 100644 index 00000000000..826800c5135 --- /dev/null +++ b/src/ast/simplifiers/bound_simplifier.h @@ -0,0 +1,90 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bound_simplifier.h + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-22 + +Description: + + Collects bounds of sub-expressions and uses them to simplify modulus + expressions. + propagate_ineqs_tactic handles other propagations with bounds. + +--*/ + +#pragma once + +#include "ast/arith_decl_plugin.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/simplifiers/bound_propagator.h" + + +class bound_simplifier : public dependent_expr_simplifier { + arith_util a; + th_rewriter m_rewriter; + unsynch_mpq_manager nm; + small_object_allocator m_alloc; + bound_propagator bp; + unsigned m_num_vars = 0; + ptr_vector m_var2expr; + unsigned_vector m_expr2var; + bool m_updated = false; + + struct rw_cfg; + struct rw; + + bool insert_bound(dependent_expr const& de); + void tighten_bound(dependent_expr const& de); + + void reset(); + + expr* to_expr(unsigned v) const { + return m_var2expr.get(v, nullptr); + } + + bool is_var(expr* e) const { + return UINT_MAX != m_expr2var.get(e->get_id(), UINT_MAX); + } + + unsigned to_var(expr* e) { + unsigned v = m_expr2var.get(e->get_id(), UINT_MAX); + if (v == UINT_MAX) { + v = m_num_vars++; + bp.mk_var(v, a.is_int(e)); + m_expr2var.setx(e->get_id(), v, UINT_MAX); + m_var2expr.setx(v, e, nullptr); + } + return v; + } + + void assert_lower(expr* x, rational const& n, bool strict); + void assert_upper(expr* x, rational const& n, bool strict); + + bool has_upper(expr* x, rational& n, bool& strict); + bool has_lower(expr* x, rational& n, bool& strict); + + // e = x + offset + bool is_offset(expr* e, expr* x, rational& offset); + +public: + + bound_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + dependent_expr_simplifier(m, fmls), + a(m), + m_rewriter(m), + bp(nm, m_alloc, p) { + } + + char const* name() const override { return "bounds-simplifier"; } + + bool supports_proofs() const override { return false; } + + void reduce() override; +}; + diff --git a/src/ast/simplifiers/extract_eqs.cpp b/src/ast/simplifiers/extract_eqs.cpp index 37d92bcbbf7..8ab6be64144 100644 --- a/src/ast/simplifiers/extract_eqs.cpp +++ b/src/ast/simplifiers/extract_eqs.cpp @@ -88,10 +88,13 @@ namespace euf { expr_ref_vector m_args, m_trail; expr_sparse_mark m_nonzero; bool m_enabled = true; + bool m_eliminate_mod = true; // solve u mod r1 = y -> u = r1*mod!1 + y void solve_mod(expr* orig, expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) { + if (!m_eliminate_mod) + return; expr* u, * z; rational r1, r2; if (!a.is_mod(x, u, z)) @@ -296,13 +299,12 @@ break; add_pos(f); m_bm(f, d, p); } - } - void updt_params(params_ref const& p) override { tactic_params tp(p); m_enabled = p.get_bool("theory_solver", tp.solve_eqs_ite_solver()); + m_eliminate_mod = p.get_bool("eliminate_mod", true); } }; diff --git a/src/tactic/arith/linear_equation.cpp b/src/ast/simplifiers/linear_equation.cpp similarity index 99% rename from src/tactic/arith/linear_equation.cpp rename to src/ast/simplifiers/linear_equation.cpp index 5d9f821e601..81f7a7cecd4 100644 --- a/src/tactic/arith/linear_equation.cpp +++ b/src/ast/simplifiers/linear_equation.cpp @@ -18,7 +18,7 @@ Module Name: Revision History: --*/ -#include "tactic/arith/linear_equation.h" +#include "ast/simplifiers/linear_equation.h" /** \brief Return the position of variable x_i in the linear equation. diff --git a/src/tactic/arith/linear_equation.h b/src/ast/simplifiers/linear_equation.h similarity index 100% rename from src/tactic/arith/linear_equation.h rename to src/ast/simplifiers/linear_equation.h diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index c47a024255c..65fe8374a8a 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -294,6 +294,7 @@ namespace euf { r.insert("theory_solver", CPK_BOOL, "theory solvers.", "true"); r.insert("ite_solver", CPK_BOOL, "use if-then-else solver.", "true"); r.insert("context_solve", CPK_BOOL, "solve equalities under disjunctions.", "false"); + r.insert("eliminate_mod", CPK_BOOL, "eliminate modulus from equations", "true"); } void solve_eqs::collect_statistics(statistics& st) const { diff --git a/src/tactic/arith/CMakeLists.txt b/src/tactic/arith/CMakeLists.txt index 11aa8724238..c2539c2d302 100644 --- a/src/tactic/arith/CMakeLists.txt +++ b/src/tactic/arith/CMakeLists.txt @@ -2,7 +2,6 @@ z3_add_component(arith_tactics SOURCES add_bounds_tactic.cpp arith_bounds_tactic.cpp - bound_propagator.cpp bv2int_rewriter.cpp bv2real_rewriter.cpp degree_shift_tactic.cpp @@ -13,7 +12,6 @@ z3_add_component(arith_tactics fm_tactic.cpp lia2card_tactic.cpp lia2pb_tactic.cpp - linear_equation.cpp nla2bv_tactic.cpp normalize_bounds_tactic.cpp pb2bv_model_converter.cpp @@ -27,6 +25,7 @@ z3_add_component(arith_tactics sat TACTIC_HEADERS add_bounds_tactic.h + bound_simplifier_tactic.h card2bv_tactic.h degree_shift_tactic.h diff_neq_tactic.h diff --git a/src/tactic/arith/bound_simplifier_tactic.h b/src/tactic/arith/bound_simplifier_tactic.h new file mode 100644 index 00000000000..c8bc1318c7c --- /dev/null +++ b/src/tactic/arith/bound_simplifier_tactic.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + bound_simplifier_tactic.h + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-22 + +Tactic Documentation: + +## Tactic bound-simplifier + +### Short Description + +Tactic for simplifying arithmetical expressions modulo bounds + +### Long Description + +The tactic is used to eliminate occurrences of modulus expressions when it is known that terms are within the bounds +of the modulus. + + +--*/ +#pragma once + +#include "util/params.h" +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/bound_simplifier.h" + +inline tactic* mk_bound_simplifier_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }); +} + +/* + ADD_TACTIC("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "mk_bound_simplifier_tactic(m, p)") +*/ diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index f3cd4e2c8dc..416997d45de 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -3,7 +3,7 @@ Copyright (c) 2014 Microsoft Corporation Module Name: - card2bv_tactic.cpp + card2bv_tactic.h Author: diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp index ddd2da3b986..8c29e499dbe 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ b/src/tactic/arith/propagate_ineqs_tactic.cpp @@ -31,7 +31,7 @@ Module Name: --*/ #include "tactic/tactical.h" -#include "tactic/arith/bound_propagator.h" +#include "ast/simplifiers/bound_propagator.h" #include "ast/arith_decl_plugin.h" #include "tactic/core/simplify_tactic.h" #include "ast/ast_smt2_pp.h" @@ -221,9 +221,9 @@ struct propagate_ineqs_tactic::imp { strict = true; } } - else { - return false; - } + else + return false; + expr * lhs = to_app(t)->get_arg(0); expr * rhs = to_app(t)->get_arg(1); expr* a, *b; @@ -251,6 +251,7 @@ struct propagate_ineqs_tactic::imp { a_var x = mk_linear_pol(lhs); mpq c_prime; nm.set(c_prime, c.to_mpq()); + verbose_stream() << mk_ismt2_pp(t, m) << " bound " << c << "\n"; if (k == EQ) { SASSERT(!strict); bp.assert_lower(x, c_prime, false); From 273aff5ed6538706d4a8d4672fbaeacacf9bfaf4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 22:21:23 -0800 Subject: [PATCH 312/597] remove debug out Signed-off-by: Nikolaj Bjorner --- src/tactic/arith/propagate_ineqs_tactic.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp index 8c29e499dbe..56d07a5c2d1 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ b/src/tactic/arith/propagate_ineqs_tactic.cpp @@ -251,7 +251,6 @@ struct propagate_ineqs_tactic::imp { a_var x = mk_linear_pol(lhs); mpq c_prime; nm.set(c_prime, c.to_mpq()); - verbose_stream() << mk_ismt2_pp(t, m) << " bound " << c << "\n"; if (k == EQ) { SASSERT(!strict); bp.assert_lower(x, c_prime, false); From 3b5ae285d908471fede2fc540537130a9df9ecb4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 23:28:36 -0800 Subject: [PATCH 313/597] add outline for interval reasoning Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 2 +- src/ast/simplifiers/CMakeLists.txt | 5 ++- src/ast/simplifiers/bound_simplifier.cpp | 55 ++++++++++++++++++++++++ src/ast/simplifiers/bound_simplifier.h | 12 +++++- 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 20bce7c13f7..2e0e4e73978 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -39,7 +39,7 @@ def init_project_def(): add_lib('macros', ['rewriter'], 'ast/macros') add_lib('model', ['macros']) add_lib('converters', ['model'], 'ast/converters') - add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'converters', 'substitution'], 'ast/simplifiers') + add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'interval', 'converters', 'substitution'], 'ast/simplifiers') add_lib('tactic', ['simplifiers']) add_lib('solver', ['params', 'model', 'tactic', 'proofs']) add_lib('cmd_context', ['solver', 'rewriter', 'params']) diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 74c544ffe72..cd1b34f8c78 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -20,9 +20,10 @@ z3_add_component(simplifiers solve_context_eqs.cpp solve_eqs.cpp COMPONENT_DEPENDENCIES - euf - rewriter bit_blaster + euf + interval normal_forms + rewriter substitution ) diff --git a/src/ast/simplifiers/bound_simplifier.cpp b/src/ast/simplifiers/bound_simplifier.cpp index 08553772033..cf39c78def3 100644 --- a/src/ast/simplifiers/bound_simplifier.cpp +++ b/src/ast/simplifiers/bound_simplifier.cpp @@ -297,6 +297,61 @@ bool bound_simplifier::has_upper(expr* x, rational& n, bool& strict) { return false; } +void bound_simplifier::get_bounds(expr* x, scoped_interval& i) { + i.m().reset_upper(i); + i.m().reset_lower(i); + + rational n; + if (a.is_numeral(x, n)) { + i.m().set(i, n.to_mpq()); + return; + } + + if (is_var(x)) { + unsigned v = to_var(x); + bool strict; + if (bp.has_upper(v)) { + mpq const& q = bp.upper(v, strict); + i_cfg.set_upper_is_open(i, strict); + i_cfg.set_upper(i, q); + } + if (bp.has_lower(v)) { + mpq const& q = bp.lower(v, strict); + i_cfg.set_lower_is_open(i, strict); + i_cfg.set_lower(i, q); + } + } + + if (a.is_add(x)) { + scoped_interval sum_i(i.m()); + scoped_interval arg_i(i.m()); + i.m().set(sum_i, mpq(0)); + for (expr* arg : *to_app(x)) { + get_bounds(arg, arg_i); + i.m().add(sum_i, arg_i, sum_i); + } + // TODO: intersect + i.m().set(i, sum_i); + } + + if (a.is_mul(x)) { + scoped_interval mul_i(i.m()); + scoped_interval arg_i(i.m()); + i.m().set(mul_i, mpq(1)); + for (expr* arg : *to_app(x)) { + get_bounds(arg, arg_i); + i.m().add(mul_i, arg_i, mul_i); + } + // TODO: intersect + i.m().set(i, mul_i); + } + + // etc: + // import interval from special case code for lower and upper. + + + +} void bound_simplifier::reset() { bp.reset(); diff --git a/src/ast/simplifiers/bound_simplifier.h b/src/ast/simplifiers/bound_simplifier.h index 826800c5135..c7739715e6f 100644 --- a/src/ast/simplifiers/bound_simplifier.h +++ b/src/ast/simplifiers/bound_simplifier.h @@ -23,14 +23,21 @@ Module Name: #include "ast/rewriter/th_rewriter.h" #include "ast/simplifiers/dependent_expr_state.h" #include "ast/simplifiers/bound_propagator.h" +#include "math/interval/interval.h" class bound_simplifier : public dependent_expr_simplifier { + typedef interval_manager interval_manager; + typedef interval_manager::interval interval; + typedef _scoped_interval scoped_interval; + arith_util a; th_rewriter m_rewriter; unsynch_mpq_manager nm; small_object_allocator m_alloc; bound_propagator bp; + im_default_config i_cfg; + interval_manager i_manager; unsigned m_num_vars = 0; ptr_vector m_var2expr; unsigned_vector m_expr2var; @@ -68,6 +75,7 @@ class bound_simplifier : public dependent_expr_simplifier { bool has_upper(expr* x, rational& n, bool& strict); bool has_lower(expr* x, rational& n, bool& strict); + void get_bounds(expr* x, scoped_interval&); // e = x + offset bool is_offset(expr* e, expr* x, rational& offset); @@ -78,7 +86,9 @@ class bound_simplifier : public dependent_expr_simplifier { dependent_expr_simplifier(m, fmls), a(m), m_rewriter(m), - bp(nm, m_alloc, p) { + bp(nm, m_alloc, p), + i_cfg(nm), + i_manager(m.limit(), im_default_config(nm)) { } char const* name() const override { return "bounds-simplifier"; } From eb751bec4ceee3e1deed4bf6dc360ac20c05b582 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Jan 2023 23:57:59 -0800 Subject: [PATCH 314/597] fix riscv/aarch/powerpc build warnings Signed-off-by: Nikolaj Bjorner --- src/ast/recfun_decl_plugin.h | 2 +- src/ast/rewriter/macro_replacer.cpp | 3 +-- src/ast/simplifiers/bound_simplifier.h | 8 ++++---- src/math/lp/lp_primal_core_solver_def.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index ae9c060e99f..e2d48066408 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -61,7 +61,7 @@ namespace recfun { expr_ref_vector m_guards; //get_decl(), * d2 = nullptr; + func_decl* d = n->get_decl(); app_ref head(m); expr_ref def(m); expr_dependency_ref dep(m); diff --git a/src/ast/simplifiers/bound_simplifier.h b/src/ast/simplifiers/bound_simplifier.h index c7739715e6f..06e25920c3f 100644 --- a/src/ast/simplifiers/bound_simplifier.h +++ b/src/ast/simplifiers/bound_simplifier.h @@ -27,9 +27,9 @@ Module Name: class bound_simplifier : public dependent_expr_simplifier { - typedef interval_manager interval_manager; - typedef interval_manager::interval interval; - typedef _scoped_interval scoped_interval; + typedef interval_manager _interval_manager; + typedef _interval_manager::interval interval; + typedef _scoped_interval<_interval_manager> scoped_interval; arith_util a; th_rewriter m_rewriter; @@ -37,7 +37,7 @@ class bound_simplifier : public dependent_expr_simplifier { small_object_allocator m_alloc; bound_propagator bp; im_default_config i_cfg; - interval_manager i_manager; + _interval_manager i_manager; unsigned m_num_vars = 0; ptr_vector m_var2expr; unsigned_vector m_expr2var; diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7b5dec945d6..3818b589a5a 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -480,7 +480,7 @@ template int lp_primal_core_solver::find_leaving_ template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) return find_leaving_and_t_with_breakpoints(entering, t); - X theta; + X theta = zero_of_type(); bool unlimited = get_harris_theta(theta); lp_assert(unlimited || theta >= zero_of_type()); if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; From d9f9cceea4450eb5007a82ee4e5fc0599e5c7560 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Jan 2023 14:13:03 -0800 Subject: [PATCH 315/597] use intervals for tracking bounds on arithmetic variables leverage interval propagation for bounds. merge functionality with propagate-ineqs tactic remove the new propagate-bounds tactic and instead use propagate-ineqs --- RELEASE_NOTES.md | 4 +- src/ast/simplifiers/bound_simplifier.cpp | 554 +++++++++++++------ src/ast/simplifiers/bound_simplifier.h | 57 +- src/math/interval/dep_intervals.h | 2 + src/tactic/arith/CMakeLists.txt | 2 - src/tactic/arith/propagate_ineqs_tactic.cpp | 575 -------------------- src/tactic/arith/propagate_ineqs_tactic.h | 12 +- 7 files changed, 443 insertions(+), 763 deletions(-) delete mode 100644 src/tactic/arith/propagate_ineqs_tactic.cpp diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 868e9b690be..76f163b85df 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -14,8 +14,8 @@ Version 4.next Version 4.12.2 ============== - remove MSF (Microsoft Solver Foundation) plugin -- add bound_simplifier tactic. - It eliminates occurrences of "mod" operators when bounds information +- updated propagate-ineqs tactic and implementing it as a simplifier, bound_simplifier. + It now eliminates occurrences of "mod" operators when bounds information implies that the modulus is redundant. This tactic is useful for benchmarks created by converting bit-vector semantics to integer reasoning. diff --git a/src/ast/simplifiers/bound_simplifier.cpp b/src/ast/simplifiers/bound_simplifier.cpp index cf39c78def3..89ed6da057f 100644 --- a/src/ast/simplifiers/bound_simplifier.cpp +++ b/src/ast/simplifiers/bound_simplifier.cpp @@ -3,12 +3,35 @@ Copyright (c) 2022 Microsoft Corporation Module Name: - bounds_simplifier.cpp + bound_simplifier.cpp Author: Nikolaj Bjorner (nbjorner) 2023-01-22 +Description: + +Extract bounds for sub-expressions and use the bounds for propagation and simplification. +It applies the simplificaitons from the bounds_propagator and it applies nested rewriting +of sub-expressions based on bounds information. Initially, rewriting amounts to eliminating +occurrences of mod N. + +From the description of propagate_ineqs_tactic: + + - Propagate bounds using the bound_propagator. + - Eliminate subsumed inequalities. + For example: + x - y >= 3 + can be replaced with true if we know that + x >= 3 and y <= 0 + + - Convert inequalities of the form p <= k and p >= k into p = k, + where p is a polynomial and k is a constant. + + This strategy assumes the input is in arith LHS mode. + This can be achieved by using option :arith-lhs true in the + simplifier. + --*/ @@ -17,74 +40,86 @@ Module Name: #include "ast/rewriter/rewriter_def.h" struct bound_simplifier::rw_cfg : public default_rewriter_cfg { - ast_manager& m; - bound_propagator& bp; bound_simplifier& s; - arith_util a; - rw_cfg(bound_simplifier& s):m(s.m), bp(s.bp), s(s), a(m) {} - + rw_cfg(bound_simplifier& s): s(s) {} br_status reduce_app(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result, proof_ref& pr) { - rational N, hi, lo; - bool strict; - if (a.is_mod(f) && num_args == 2 && a.is_numeral(args[1], N)) { - expr* x = args[0]; - if (!s.has_upper(x, hi, strict) || strict) - return BR_FAILED; - if (!s.has_lower(x, lo, strict) || strict) - return BR_FAILED; - if (hi - lo >= N) - return BR_FAILED; - if (N > hi && lo >= 0) { - result = x; - return BR_DONE; - } - if (2*N > hi && lo >= N) { - result = a.mk_sub(x, a.mk_int(N)); - s.m_rewriter(result); - return BR_DONE; - } - IF_VERBOSE(2, verbose_stream() << "potentially missed simplification: " << mk_pp(x, m) << " " << lo << " " << hi << " not reduced\n"); - } - return BR_FAILED; + return s.reduce_app(f, num_args, args, result, pr); } }; struct bound_simplifier::rw : public rewriter_tpl { rw_cfg m_cfg; - rw(bound_simplifier& s): rewriter_tpl(s.m, false, m_cfg), m_cfg(s) { } }; +br_status bound_simplifier::reduce_app(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result, proof_ref& pr) { + rational N, hi, lo; + if (a.is_mod(f) && num_args == 2 && a.is_numeral(args[1], N)) { + expr* x = args[0]; + auto& im = m_interval; + scoped_dep_interval i(im); + get_bounds(x, i); + if (im.upper_is_inf(i) || im.lower_is_inf(i)) + return BR_FAILED; + if (im.upper_is_open(i) || im.lower_is_open(i)) + return BR_FAILED; + lo = im.lower(i); + hi = im.upper(i); + if (hi - lo >= N) + return BR_FAILED; + if (N > hi && lo >= 0) { + result = x; + return BR_DONE; + } + if (2 * N > hi && lo >= N) { + result = a.mk_sub(x, a.mk_int(N)); + m_rewriter(result); + return BR_DONE; + } + IF_VERBOSE(2, verbose_stream() << "potentially missed simplification: " << mk_pp(x, m) << " " << lo << " " << hi << " not reduced\n"); + } + return BR_FAILED; +} + void bound_simplifier::reduce() { - m_updated = true; - for (unsigned i = 0; i < 5 && m_updated; ++i) { - m_updated = false; + bool updated = true, found_bound = false; + for (unsigned i = 0; i < 5 && updated; ++i) { + updated = false; + found_bound = false; reset(); - for (unsigned idx : indices()) - insert_bound(m_fmls[idx]); + for (unsigned idx : indices()) { + if (insert_bound(m_fmls[idx])) { + m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr, nullptr)); + found_bound = true; + } + } + if (!found_bound) + break; + for (unsigned idx : indices()) tighten_bound(m_fmls[idx]); - rw rw(*this); - - // TODO: take over propagate_ineq: - // bp.propagate(); - // serialize bounds + bp.propagate(); proof_ref pr(m); expr_ref r(m); + rw rw(*this); for (unsigned idx : indices()) { auto const& d = m_fmls[idx]; + if (d.pr()) + continue; rw(d.fml(), r, pr); if (r != d.fml()) { m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep())); - m_updated = true; + ++m_num_reduced; + updated = true; } } + restore_bounds(); } } @@ -169,19 +204,22 @@ void bound_simplifier::tighten_bound(dependent_expr const& de) { bool strict; if (a.is_le(f, x, y)) { // x <= (x + k) mod N && x >= 0 -> x + k < N - if (a.is_mod(y, z, u) && a.is_numeral(u, n) && has_lower(x, k, strict) && k >= 0 && is_offset(z, x, k) && k > 0 && k < n) { + if (a.is_mod(y, z, u) && a.is_numeral(u, n) && has_lower(x, k, strict) && k >= 0 && is_offset(z, x, k) && k > 0 && k < n) assert_upper(x, n - k, true); - } + } + // x != k, k <= x -> k < x if (m.is_not(f, f) && m.is_eq(f, x, y)) { if (a.is_numeral(x)) std::swap(x, y); - bool strict; if (a.is_numeral(y, n)) { - if (has_lower(x, k, strict) && !strict && k == n) - assert_lower(x, k, true); - else if (has_upper(x, k, strict) && !strict && k == n) - assert_upper(x, k, true); + scoped_dep_interval i(m_interval); + get_bounds(x, i); + scoped_mpq k(nm); + if (!i.m().lower_is_inf(i) && !i.m().lower_is_open(i) && i.m().lower(i) == n) + assert_lower(x, n, true); + else if (!i.m().upper_is_inf(i) && !i.m().upper_is_open(i) && i.m().upper(i) == n) + assert_upper(x, n, true); } } } @@ -199,111 +237,33 @@ void bound_simplifier::assert_lower(expr* x, rational const& n, bool strict) { bp.assert_lower(to_var(x), c, strict); } -// -// TODO: Use math/interval/interval.h -// - bool bound_simplifier::has_lower(expr* x, rational& n, bool& strict) { - if (is_var(x)) { - unsigned v = to_var(x); - if (bp.has_lower(v)) { - mpq const & q = bp.lower(v, strict); - n = rational(q); - return true; - } - } - if (a.is_numeral(x, n)) { - strict = false; - return true; - } - if (a.is_mod(x)) { - n = rational::zero(); - strict = false; - return true; - } - expr* y, *z; - if (a.is_idiv(x, y, z) && has_lower(z, n, strict) && n > 0 && has_lower(y, n, strict)) - return true; - - if (a.is_add(x)) { - rational bound; - strict = false; - n = 0; - bool is_strict; - for (expr* arg : *to_app(x)) { - if (!has_lower(arg, bound, is_strict)) - return false; - strict |= is_strict; - n += bound; - } - return true; - } - - if (a.is_mul(x, y, z)) { - // TODO: this is done generally using math/interval/interval.h - rational bound1, bound2; - bool strict1, strict2; - if (has_lower(y, bound1, strict1) && !strict1 && - has_lower(z, bound1, strict2) && !strict2 && - bound1 >= 0 && bound2 >= 0) { - n = bound1*bound2; - strict = false; - return true; - } - } - - return false; + scoped_dep_interval i(m_interval); + get_bounds(x, i); + if (m_interval.lower_is_inf(i)) + return false; + strict = m_interval.lower_is_open(i); + n = m_interval.lower(i); + return true; } - bool bound_simplifier::has_upper(expr* x, rational& n, bool& strict) { - if (is_var(x)) { - unsigned v = to_var(x); - if (bp.has_upper(v)) { - mpq const & q = bp.upper(v, strict); - n = rational(q); - return true; - } - } - - // perform light-weight abstract analysis - // y * (u / y) is bounded by u, if y > 0 - - if (a.is_numeral(x, n)) { - strict = false; - return true; - } - - if (a.is_add(x)) { - rational bound; - strict = false; - n = 0; - bool is_strict; - for (expr* arg : *to_app(x)) { - if (!has_upper(arg, bound, is_strict)) - return false; - strict |= is_strict; - n += bound; - } - return true; - } - - expr* y, *z, *u, *v; - if (a.is_mul(x, y, z) && a.is_idiv(z, u, v) && v == y && has_lower(y, n, strict) && n > 0 && has_upper(u, n, strict)) - return true; - if (a.is_idiv(x, y, z) && has_lower(z, n, strict) && n > 0 && has_upper(y, n, strict)) - return true; - - return false; + scoped_dep_interval i(m_interval); + get_bounds(x, i); + if (m_interval.upper_is_inf(i)) + return false; + strict = m_interval.upper_is_open(i); + n = m_interval.upper(i); + return true; } -void bound_simplifier::get_bounds(expr* x, scoped_interval& i) { - i.m().reset_upper(i); - i.m().reset_lower(i); - +void bound_simplifier::get_bounds(expr* x, scoped_dep_interval& i) { + auto& im = m_interval; + im.reset(i); + scoped_dep_interval arg_i(im); rational n; if (a.is_numeral(x, n)) { - i.m().set(i, n.to_mpq()); + im.set_value(i, n); return; } @@ -311,51 +271,311 @@ void bound_simplifier::get_bounds(expr* x, scoped_interval& i) { unsigned v = to_var(x); bool strict; if (bp.has_upper(v)) { - mpq const& q = bp.upper(v, strict); - i_cfg.set_upper_is_open(i, strict); - i_cfg.set_upper(i, q); + im.set_upper(i, bp.upper(v, strict)); + im.set_upper_is_inf(i, false); + im.set_upper_is_open(i, strict); + } if (bp.has_lower(v)) { - mpq const& q = bp.lower(v, strict); - i_cfg.set_lower_is_open(i, strict); - i_cfg.set_lower(i, q); + im.set_lower(i, bp.lower(v, strict)); + im.set_lower_is_inf(i, false); + im.set_lower_is_open(i, strict); } } if (a.is_add(x)) { - scoped_interval sum_i(i.m()); - scoped_interval arg_i(i.m()); - i.m().set(sum_i, mpq(0)); + scoped_dep_interval tmp_i(im), sum_i(im); + im.set_value(sum_i, rational::zero()); for (expr* arg : *to_app(x)) { get_bounds(arg, arg_i); - i.m().add(sum_i, arg_i, sum_i); + im.add(sum_i, arg_i, tmp_i); + im.set(sum_i, tmp_i); } - // TODO: intersect - i.m().set(i, sum_i); + im.intersect (i, sum_i, i); } if (a.is_mul(x)) { - scoped_interval mul_i(i.m()); - scoped_interval arg_i(i.m()); - i.m().set(mul_i, mpq(1)); + scoped_dep_interval tmp_i(im); + im.set_value(tmp_i, rational::one()); for (expr* arg : *to_app(x)) { get_bounds(arg, arg_i); - i.m().add(mul_i, arg_i, mul_i); + im.mul(tmp_i, arg_i, tmp_i); } - // TODO: intersect - i.m().set(i, mul_i); + im.intersect (i, tmp_i, i); } - // etc: - // import interval from special case code for lower and upper. + expr* y, * z, * u, * v; + if (a.is_mod(x, y, z) && a.is_numeral(z, n) && n > 0) { + scoped_dep_interval tmp_i(im); + im.set_lower_is_inf(tmp_i, false); + im.set_lower_is_open(tmp_i, false); + im.set_lower(tmp_i, mpq(0)); + im.set_upper_is_inf(tmp_i, false); + im.set_upper_is_open(tmp_i, false); + im.set_upper(tmp_i, n - 1); + im.intersect (i, tmp_i, i); + } - - + // x = y*(u div y), y > 0 -> x <= u + if (a.is_mul(x, y, z) && a.is_idiv(z, u, v) && v == y) { + scoped_dep_interval iy(im), iu(im), tmp_i(im); + get_bounds(y, iy); + get_bounds(u, iu); + if (!im.lower_is_inf(iy) && im.lower(iy) > 0 && + !im.upper_is_inf(iu) && im.upper(iu) >= 0) { + im.set_upper_is_inf(tmp_i, false); + im.set_upper_is_open(tmp_i, im.upper_is_open(iu)); + im.set_upper(tmp_i, im.upper(iu)); + im.intersect(i, tmp_i, i); + } + } + + // x = y div z, z > 0 => x <= y + if (a.is_idiv(x, y, z)) { + scoped_dep_interval iy(im), iz(im), tmp_i(im); + get_bounds(y, iy); + get_bounds(z, iz); + if (!im.lower_is_inf(iz) && im.lower(iz) > 0 && + !im.upper_is_inf(iy) && im.upper(iy) >= 0) { + im.set_upper_is_inf(tmp_i, false); + im.set_upper_is_open(tmp_i, im.upper_is_open(iy)); + im.set_upper(tmp_i, im.upper(iy)); + im.set_lower_is_inf(tmp_i, false); + im.set_lower_is_open(tmp_i, false); // TODO - could be refined + im.set_lower(tmp_i, rational::zero()); + im.intersect(i, tmp_i, i); + } + } + if (a.is_div(x, y, z)) { + scoped_dep_interval iy(im), iz(im), tmp_i(im); + get_bounds(y, iy); + get_bounds(z, iz); + im.div(iy, iz, tmp_i); + im.intersect(i, tmp_i, i); + } +} + +void bound_simplifier::expr2linear_pol(expr* t, mpq_buffer& as, var_buffer& xs) { + scoped_mpq c_mpq_val(nm); + if (a.is_add(t)) { + rational c_val; + for (expr* mon : *to_app(t)) { + expr* c, * x; + if (a.is_mul(mon, c, x) && a.is_numeral(c, c_val)) { + nm.set(c_mpq_val, c_val.to_mpq()); + as.push_back(c_mpq_val); + xs.push_back(to_var(x)); + } + else { + as.push_back(mpq(1)); + xs.push_back(to_var(mon)); + } + } + } + else { + as.push_back(mpq(1)); + xs.push_back(to_var(t)); + } +} + +bool bound_simplifier::lower_subsumed(expr* p, mpq const& k, bool strict) { + if (!a.is_add(p)) + return false; + m_num_buffer.reset(); + m_var_buffer.reset(); + expr2linear_pol(p, m_num_buffer, m_var_buffer); + scoped_mpq implied_k(nm); + bool implied_strict; + return + bp.lower(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) && + (nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); +} + +bool bound_simplifier::upper_subsumed(expr* p, mpq const& k, bool strict) { + if (!a.is_add(p)) + return false; + m_num_buffer.reset(); + m_var_buffer.reset(); + expr2linear_pol(p, m_num_buffer, m_var_buffer); + scoped_mpq implied_k(nm); + bool implied_strict; + return + bp.upper(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) && + (nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); +} + +void bound_simplifier::restore_bounds() { + scoped_mpq l(nm), u(nm); + bool strict_l, strict_u, has_l, has_u; + unsigned ts; + unsigned sz = m_var2expr.size(); + + rw rw(*this); + auto add = [&](expr* fml) { + expr_ref tmp(fml, m); + rw(tmp, tmp); + m_rewriter(tmp); + m_fmls.add(dependent_expr(m, tmp, nullptr, nullptr)); + }; + + for (unsigned x = 0; x < sz; x++) { + expr* p = m_var2expr.get(x); + has_l = bp.lower(x, l, strict_l, ts); + has_u = bp.upper(x, u, strict_u, ts); + if (!has_l && !has_u) + continue; + if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) { + // l <= p <= l --> p = l + add(m.mk_eq(p, a.mk_numeral(rational(l), a.is_int(p)))); + continue; + } + if (has_l && !lower_subsumed(p, l, strict_l)) { + if (strict_l) + add(m.mk_not(a.mk_le(p, a.mk_numeral(rational(l), a.is_int(p))))); + else + add(a.mk_ge(p, a.mk_numeral(rational(l), a.is_int(p)))); + } + if (has_u && !upper_subsumed(p, u, strict_u)) { + if (strict_u) + add(m.mk_not(a.mk_ge(p, a.mk_numeral(rational(u), a.is_int(p))))); + else + add(a.mk_le(p, a.mk_numeral(rational(u), a.is_int(p)))); + } + } } + void bound_simplifier::reset() { bp.reset(); m_var2expr.reset(); m_expr2var.reset(); - m_num_vars = 0; } + +#if 0 +void find_ite_bounds(expr* root) { + TRACE("find_ite_bounds_bug", display_bounds(tout);); + expr* n = root; + expr* target = nullptr; + expr* c, * t, * e; + expr* x, * y; + bool has_l, has_u; + mpq l_min, u_max; + bool l_strict, u_strict; + mpq curr; + bool curr_strict; + while (true) { + TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";); + + if (m.is_ite(n, c, t, e)) { + if (is_x_minus_y_eq_0(t, x, y)) + n = e; + else if (is_x_minus_y_eq_0(e, x, y)) + n = t; + else + break; + } + else if (is_x_minus_y_eq_0(n, x, y)) { + n = nullptr; + } + else { + break; + } + + TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n"; + if (target) { + tout << "target: " << mk_ismt2_pp(target, m) << "\n"; + tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n"; + }); + + if (is_unbounded(y)) + std::swap(x, y); + + if (!is_unbounded(x)) { + TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";); + break; + } + + if (target == nullptr) { + target = x; + if (lower(y, curr, curr_strict)) { + has_l = true; + nm.set(l_min, curr); + l_strict = curr_strict; + } + else { + has_l = false; + TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); + } + if (upper(y, curr, curr_strict)) { + has_u = true; + nm.set(u_max, curr); + u_strict = curr_strict; + } + else { + has_u = false; + TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); + } + } + else if (target == x) { + if (has_l) { + if (lower(y, curr, curr_strict)) { + if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) { + nm.set(l_min, curr); + l_strict = curr_strict; + } + } + else { + has_l = false; + TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); + } + } + if (has_u) { + if (upper(y, curr, curr_strict)) { + if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) { + nm.set(u_max, curr); + u_strict = curr_strict; + } + } + else { + has_u = false; + TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); + } + } + } + else { + break; + } + + if (!has_l && !has_u) + break; + + if (n == nullptr) { + TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n"; + tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n"; + tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n"; + tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";); + a_var x = mk_var(target); + if (has_l) + bp.assert_lower(x, l_min, l_strict); + if (has_u) + bp.assert_upper(x, u_max, u_strict); + break; + } + } + nm.del(l_min); + nm.del(u_max); + nm.del(curr); +} + +void find_ite_bounds() { + unsigned sz = m_new_goal->size(); + for (unsigned i = 0; i < sz; i++) { + expr* f = m_new_goal->form(i); + if (m.is_ite(f)) + find_ite_bounds(to_app(f)); + } + bp.propagate(); + TRACE("find_ite_bounds", display_bounds(tout);); +} + +#endif diff --git a/src/ast/simplifiers/bound_simplifier.h b/src/ast/simplifiers/bound_simplifier.h index 06e25920c3f..9d6b0995597 100644 --- a/src/ast/simplifiers/bound_simplifier.h +++ b/src/ast/simplifiers/bound_simplifier.h @@ -23,25 +23,26 @@ Module Name: #include "ast/rewriter/th_rewriter.h" #include "ast/simplifiers/dependent_expr_state.h" #include "ast/simplifiers/bound_propagator.h" -#include "math/interval/interval.h" +#include "math/interval/dep_intervals.h" class bound_simplifier : public dependent_expr_simplifier { - typedef interval_manager _interval_manager; - typedef _interval_manager::interval interval; - typedef _scoped_interval<_interval_manager> scoped_interval; + typedef bound_propagator::var a_var; + typedef numeral_buffer mpq_buffer; + typedef svector var_buffer; arith_util a; + params_ref m_params; th_rewriter m_rewriter; unsynch_mpq_manager nm; small_object_allocator m_alloc; bound_propagator bp; - im_default_config i_cfg; - _interval_manager i_manager; - unsigned m_num_vars = 0; + dep_intervals m_interval; ptr_vector m_var2expr; unsigned_vector m_expr2var; - bool m_updated = false; + mpq_buffer m_num_buffer; + var_buffer m_var_buffer; + unsigned m_num_reduced = 0; struct rw_cfg; struct rw; @@ -62,20 +63,27 @@ class bound_simplifier : public dependent_expr_simplifier { unsigned to_var(expr* e) { unsigned v = m_expr2var.get(e->get_id(), UINT_MAX); if (v == UINT_MAX) { - v = m_num_vars++; + v = m_var2expr.size(); bp.mk_var(v, a.is_int(e)); m_expr2var.setx(e->get_id(), v, UINT_MAX); - m_var2expr.setx(v, e, nullptr); + m_var2expr.push_back(e); } return v; } + br_status reduce_app(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result, proof_ref& pr); + void assert_lower(expr* x, rational const& n, bool strict); void assert_upper(expr* x, rational const& n, bool strict); bool has_upper(expr* x, rational& n, bool& strict); bool has_lower(expr* x, rational& n, bool& strict); - void get_bounds(expr* x, scoped_interval&); + void get_bounds(expr* x, scoped_dep_interval&); + + void expr2linear_pol(expr* t, mpq_buffer& as, var_buffer& xs); + bool lower_subsumed(expr* p, mpq const& k, bool strict); + bool upper_subsumed(expr* p, mpq const& k, bool strict); + void restore_bounds(); // e = x + offset bool is_offset(expr* e, expr* x, rational& offset); @@ -87,14 +95,35 @@ class bound_simplifier : public dependent_expr_simplifier { a(m), m_rewriter(m), bp(nm, m_alloc, p), - i_cfg(nm), - i_manager(m.limit(), im_default_config(nm)) { + m_interval(m.limit()), + m_num_buffer(nm) { + updt_params(p); } - char const* name() const override { return "bounds-simplifier"; } + char const* name() const override { return "propagate-ineqs"; } bool supports_proofs() const override { return false; } void reduce() override; + + void updt_params(params_ref const& p) override { + m_params.append(p); + bp.updt_params(m_params); + } + + void collect_param_descrs(param_descrs & r) override { + bound_propagator::get_param_descrs(r); + } + + void collect_statistics(statistics& st) const override { + st.update("bound-propagations", bp.get_num_propagations()); + st.update("bound-false-alarms", bp.get_num_false_alarms()); + st.update("bound-simplifications", m_num_reduced); + } + + void reset_statistics() override { + m_num_reduced = 0; + bp.reset_statistics(); + } }; diff --git a/src/math/interval/dep_intervals.h b/src/math/interval/dep_intervals.h index d14a0fc53da..d641a294d6f 100644 --- a/src/math/interval/dep_intervals.h +++ b/src/math/interval/dep_intervals.h @@ -172,6 +172,7 @@ class dep_intervals { void set_upper_is_inf(interval& a, bool inf) const { m_config.set_upper_is_inf(a, inf); } void set_lower_dep(interval& a, u_dependency* d) const { m_config.set_lower_dep(a, d); } void set_upper_dep(interval& a, u_dependency* d) const { m_config.set_upper_dep(a, d); } + void reset(interval& a) const { set_lower_is_inf(a, true); set_upper_is_inf(a, true); } void set_value(interval& a, rational const& n) const { set_lower(a, n); set_upper(a, n); @@ -331,6 +332,7 @@ class dep_intervals { } mpq const& lower(interval const& a) const { return m_config.lower(a); } mpq const& upper(interval const& a) const { return m_config.upper(a); } + bool is_empty(interval const& a) const; void set_interval_for_scalar(interval&, const rational&); template diff --git a/src/tactic/arith/CMakeLists.txt b/src/tactic/arith/CMakeLists.txt index c2539c2d302..4eabef4a6c1 100644 --- a/src/tactic/arith/CMakeLists.txt +++ b/src/tactic/arith/CMakeLists.txt @@ -17,7 +17,6 @@ z3_add_component(arith_tactics pb2bv_model_converter.cpp pb2bv_tactic.cpp probe_arith.cpp - propagate_ineqs_tactic.cpp purify_arith_tactic.cpp recover_01_tactic.cpp COMPONENT_DEPENDENCIES @@ -25,7 +24,6 @@ z3_add_component(arith_tactics sat TACTIC_HEADERS add_bounds_tactic.h - bound_simplifier_tactic.h card2bv_tactic.h degree_shift_tactic.h diff_neq_tactic.h diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp deleted file mode 100644 index 56d07a5c2d1..00000000000 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ /dev/null @@ -1,575 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - propagate_ineqs_tactic.h - -Abstract: - - This tactic performs the following tasks: - - - Propagate bounds using the bound_propagator. - - Eliminate subsumed inequalities. - For example: - x - y >= 3 - can be replaced with true if we know that - x >= 3 and y <= 0 - - - Convert inequalities of the form p <= k and p >= k into p = k, - where p is a polynomial and k is a constant. - - This strategy assumes the input is in arith LHS mode. - This can be achieved by using option :arith-lhs true in the - simplifier. - -Author: - - Leonardo (leonardo) 2012-02-19 - -Notes: - ---*/ -#include "tactic/tactical.h" -#include "ast/simplifiers/bound_propagator.h" -#include "ast/arith_decl_plugin.h" -#include "tactic/core/simplify_tactic.h" -#include "ast/ast_smt2_pp.h" - -class propagate_ineqs_tactic : public tactic { - struct imp; - imp * m_imp; - params_ref m_params; -public: - propagate_ineqs_tactic(ast_manager & m, params_ref const & p); - - tactic * translate(ast_manager & m) override { - return alloc(propagate_ineqs_tactic, m, m_params); - } - - ~propagate_ineqs_tactic() override; - - char const* name() const override { return "propagate_ineqs"; } - - void updt_params(params_ref const & p) override; - void collect_param_descrs(param_descrs & r) override {} - - void operator()(goal_ref const & g, goal_ref_buffer & result) override; - - void cleanup() override; -}; - -tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(propagate_ineqs_tactic, m, p)); -} - -struct propagate_ineqs_tactic::imp { - ast_manager & m; - unsynch_mpq_manager nm; - small_object_allocator m_allocator; - bound_propagator bp; - arith_util m_util; - typedef bound_propagator::var a_var; - obj_map m_expr2var; - expr_ref_vector m_var2expr; - - typedef numeral_buffer mpq_buffer; - typedef svector var_buffer; - - mpq_buffer m_num_buffer; - var_buffer m_var_buffer; - goal_ref m_new_goal; - - imp(ast_manager & _m, params_ref const & p): - m(_m), - m_allocator("ineq-simplifier"), - bp(nm, m_allocator, p), - m_util(m), - m_var2expr(m), - m_num_buffer(nm) { - updt_params_core(p); - } - - void updt_params_core(params_ref const & p) { - } - - void updt_params(params_ref const & p) { - updt_params_core(p); - bp.updt_params(p); - } - - void display_bounds(std::ostream & out) { - unsigned sz = m_var2expr.size(); - mpq k; - bool strict; - unsigned ts; - for (unsigned x = 0; x < sz; x++) { - if (bp.lower(x, k, strict, ts)) - out << nm.to_string(k) << " " << (strict ? "<" : "<="); - else - out << "-oo <"; - out << " " << mk_ismt2_pp(m_var2expr.get(x), m) << " "; - if (bp.upper(x, k, strict, ts)) - out << (strict ? "<" : "<=") << " " << nm.to_string(k); - else - out << "< oo"; - out << "\n"; - } - nm.del(k); - } - - a_var mk_var(expr * t) { - if (m_util.is_to_real(t)) - t = to_app(t)->get_arg(0); - a_var x; - if (m_expr2var.find(t, x)) - return x; - x = m_var2expr.size(); - bp.mk_var(x, m_util.is_int(t)); - m_var2expr.push_back(t); - m_expr2var.insert(t, x); - return x; - } - - void expr2linear_pol(expr * t, mpq_buffer & as, var_buffer & xs) { - mpq c_mpq_val; - if (m_util.is_add(t)) { - rational c_val; - for (expr* mon : *to_app(t)) { - expr * c, * x; - if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) { - nm.set(c_mpq_val, c_val.to_mpq()); - as.push_back(c_mpq_val); - xs.push_back(mk_var(x)); - } - else { - as.push_back(mpq(1)); - xs.push_back(mk_var(mon)); - } - } - } - else { - as.push_back(mpq(1)); - xs.push_back(mk_var(t)); - } - nm.del(c_mpq_val); - } - - a_var mk_linear_pol(expr * t) { - a_var x; - if (m_expr2var.find(t, x)) - return x; - x = mk_var(t); - if (m_util.is_add(t)) { - m_num_buffer.reset(); - m_var_buffer.reset(); - expr2linear_pol(t, m_num_buffer, m_var_buffer); - m_num_buffer.push_back(mpq(-1)); - m_var_buffer.push_back(x); - bp.mk_eq(m_num_buffer.size(), m_num_buffer.data(), m_var_buffer.data()); - } - return x; - } - - enum kind { EQ, LE, GE }; - - bool process(expr * t) { - bool sign = false; - while (m.is_not(t, t)) - sign = !sign; - bool strict = false; - kind k; - if (m.is_eq(t)) { - if (sign) - return false; - k = EQ; - } - else if (m_util.is_le(t)) { - if (sign) { - k = GE; - strict = true; - } - else { - k = LE; - } - } - else if (m_util.is_ge(t)) { - if (sign) { - k = LE; - strict = true; - } - else { - k = GE; - } - } - else if (m_util.is_lt(t)) { - if (sign) { - k = GE; - strict = false; - } else { - k = LE; - strict = true; - } - } - else if (m_util.is_gt(t)) { - //x > y == x <=y, strict = false - if (sign) { - k = LE; - strict = false; - } else { - k = GE; - strict = true; - } - } - else - return false; - - expr * lhs = to_app(t)->get_arg(0); - expr * rhs = to_app(t)->get_arg(1); - expr* a, *b; - if (m_util.is_numeral(lhs)) { - std::swap(lhs, rhs); - if (k == LE) - k = GE; - else if (k == GE) - k = LE; - } - - rational c; - // x = y mod c => 0 <= x < c - if (k == EQ && m_util.is_mod(rhs, a, b) && m_util.is_numeral(b, c) && c > 0) { - a_var x = mk_linear_pol(lhs); - mpq c_prime; - nm.set(c_prime, (c-1).to_mpq()); - bp.assert_lower(x, mpq(0), false); - bp.assert_upper(x, c_prime, false); - nm.del(c_prime); - return lhs == a; - } - if (!m_util.is_numeral(rhs, c)) - return false; - a_var x = mk_linear_pol(lhs); - mpq c_prime; - nm.set(c_prime, c.to_mpq()); - if (k == EQ) { - SASSERT(!strict); - bp.assert_lower(x, c_prime, false); - bp.assert_upper(x, c_prime, false); - } - else if (k == LE) { - bp.assert_upper(x, c_prime, strict); - } - else { - SASSERT(k == GE); - bp.assert_lower(x, c_prime, strict); - } - nm.del(c_prime); - return true; - } - - bool collect_bounds(goal const & g) { - bool found = false; - unsigned sz = g.size(); - for (unsigned i = 0; i < sz; i++) { - expr * t = g.form(i); - if (process(t)) - found = true; - else - m_new_goal->assert_expr(t); // save non-bounds here - } - return found; - } - - bool lower_subsumed(expr * p, mpq const & k, bool strict) { - if (!m_util.is_add(p)) - return false; - m_num_buffer.reset(); - m_var_buffer.reset(); - expr2linear_pol(p, m_num_buffer, m_var_buffer); - mpq implied_k; - bool implied_strict; - bool result = - bp.lower(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) && - (nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); - nm.del(implied_k); - return result; - } - - bool upper_subsumed(expr * p, mpq const & k, bool strict) { - if (!m_util.is_add(p)) - return false; - m_num_buffer.reset(); - m_var_buffer.reset(); - expr2linear_pol(p, m_num_buffer, m_var_buffer); - mpq implied_k; - bool implied_strict; - bool result = - bp.upper(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) && - (nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); - nm.del(implied_k); - return result; - } - - void restore_bounds() { - mpq l, u; - bool strict_l, strict_u, has_l, has_u; - unsigned ts; - unsigned sz = m_var2expr.size(); - for (unsigned x = 0; x < sz; x++) { - expr * p = m_var2expr.get(x); - has_l = bp.lower(x, l, strict_l, ts); - has_u = bp.upper(x, u, strict_u, ts); - if (!has_l && !has_u) - continue; - if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) { - // l <= p <= l --> p = l - m_new_goal->assert_expr(m.mk_eq(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))); - continue; - } - if (has_l && !lower_subsumed(p, l, strict_l)) { - if (strict_l) - m_new_goal->assert_expr(m.mk_not(m_util.mk_le(p, m_util.mk_numeral(rational(l), m_util.is_int(p))))); - else - m_new_goal->assert_expr(m_util.mk_ge(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))); - } - if (has_u && !upper_subsumed(p, u, strict_u)) { - if (strict_u) - m_new_goal->assert_expr(m.mk_not(m_util.mk_ge(p, m_util.mk_numeral(rational(u), m_util.is_int(p))))); - else - m_new_goal->assert_expr(m_util.mk_le(p, m_util.mk_numeral(rational(u), m_util.is_int(p)))); - } - } - nm.del(l); - nm.del(u); - } - - bool is_x_minus_y_eq_0(expr * t, expr * & x, expr * & y) { - expr * lhs, * rhs, * m1, * m2; - if (m.is_eq(t, lhs, rhs) && m_util.is_zero(rhs) && m_util.is_add(lhs, m1, m2)) { - if (m_util.is_times_minus_one(m2, y) && is_uninterp_const(m1)) { - x = m1; - return true; - } - if (m_util.is_times_minus_one(m1, y) && is_uninterp_const(m2)) { - x = m2; - return true; - } - } - return false; - } - - bool is_unbounded(expr * t) { - a_var x; - if (m_expr2var.find(t, x)) - return !bp.has_lower(x) && !bp.has_upper(x); - return true; - } - - bool lower(expr * t, mpq & k, bool & strict) { - unsigned ts; - a_var x; - if (m_expr2var.find(t, x)) - return bp.lower(x, k, strict, ts); - return false; - } - - bool upper(expr * t, mpq & k, bool & strict) { - unsigned ts; - a_var x; - if (m_expr2var.find(t, x)) - return bp.upper(x, k, strict, ts); - return false; - } - - void find_ite_bounds(expr * root) { - TRACE("find_ite_bounds_bug", display_bounds(tout);); - expr * n = root; - expr * target = nullptr; - expr * c, * t, * e; - expr * x, * y; - bool has_l, has_u; - mpq l_min, u_max; - bool l_strict, u_strict; - mpq curr; - bool curr_strict; - while (true) { - TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";); - - if (m.is_ite(n, c, t, e)) { - if (is_x_minus_y_eq_0(t, x, y)) - n = e; - else if (is_x_minus_y_eq_0(e, x, y)) - n = t; - else - break; - } - else if (is_x_minus_y_eq_0(n, x, y)) { - n = nullptr; - } - else { - break; - } - - TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n"; - if (target) { - tout << "target: " << mk_ismt2_pp(target, m) << "\n"; - tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n"; - }); - - if (is_unbounded(y)) - std::swap(x, y); - - if (!is_unbounded(x)) { - TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";); - break; - } - - if (target == nullptr) { - target = x; - if (lower(y, curr, curr_strict)) { - has_l = true; - nm.set(l_min, curr); - l_strict = curr_strict; - } - else { - has_l = false; - TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); - } - if (upper(y, curr, curr_strict)) { - has_u = true; - nm.set(u_max, curr); - u_strict = curr_strict; - } - else { - has_u = false; - TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); - } - } - else if (target == x) { - if (has_l) { - if (lower(y, curr, curr_strict)) { - if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) { - nm.set(l_min, curr); - l_strict = curr_strict; - } - } - else { - has_l = false; - TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); - } - } - if (has_u) { - if (upper(y, curr, curr_strict)) { - if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) { - nm.set(u_max, curr); - u_strict = curr_strict; - } - } - else { - has_u = false; - TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); - } - } - } - else { - break; - } - - if (!has_l && !has_u) - break; - - if (n == nullptr) { - TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n"; - tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n"; - tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n"; - tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";); - a_var x = mk_var(target); - if (has_l) - bp.assert_lower(x, l_min, l_strict); - if (has_u) - bp.assert_upper(x, u_max, u_strict); - break; - } - } - nm.del(l_min); - nm.del(u_max); - nm.del(curr); - } - - void find_ite_bounds() { - unsigned sz = m_new_goal->size(); - for (unsigned i = 0; i < sz; i++) { - expr * f = m_new_goal->form(i); - if (m.is_ite(f)) - find_ite_bounds(to_app(f)); - } - bp.propagate(); - TRACE("find_ite_bounds", display_bounds(tout);); - } - - void operator()(goal * g, goal_ref & r) { - tactic_report report("propagate-ineqs", *g); - - m_new_goal = alloc(goal, *g, true); - m_new_goal->inc_depth(); - r = m_new_goal.get(); - if (!collect_bounds(*g)) { - m_new_goal = nullptr; - r = g; - return; // nothing to be done - } - - TRACE("propagate_ineqs_tactic", g->display(tout); display_bounds(tout); tout << "bound propagator:\n"; bp.display(tout);); - - bp.propagate(); - - report_tactic_progress(":bound-propagations", bp.get_num_propagations()); - report_tactic_progress(":bound-false-alarms", bp.get_num_false_alarms()); - - if (bp.inconsistent()) { - r->reset(); - r->assert_expr(m.mk_false()); - return; - } - - // find_ite_bounds(); // did not help - - restore_bounds(); - - TRACE("propagate_ineqs_tactic", tout << "after propagation:\n"; display_bounds(tout); bp.display(tout);); - TRACE("propagate_ineqs_tactic", r->display(tout);); - } - -}; - -propagate_ineqs_tactic::propagate_ineqs_tactic(ast_manager & m, params_ref const & p): - m_params(p) { - m_imp = alloc(imp, m, p); -} - -propagate_ineqs_tactic::~propagate_ineqs_tactic() { - dealloc(m_imp); -} - -void propagate_ineqs_tactic::updt_params(params_ref const & p) { - m_params.append(p); - m_imp->updt_params(m_params); -} - -void propagate_ineqs_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result) { - fail_if_proof_generation("propagate-ineqs", g); - fail_if_unsat_core_generation("propagate-ineqs", g); - result.reset(); - goal_ref r; - (*m_imp)(g.get(), r); - result.push_back(r.get()); - SASSERT(r->is_well_formed()); -} - - -void propagate_ineqs_tactic::cleanup() { - imp * d = alloc(imp, m_imp->m, m_params); - std::swap(d, m_imp); - dealloc(d); -} diff --git a/src/tactic/arith/propagate_ineqs_tactic.h b/src/tactic/arith/propagate_ineqs_tactic.h index 6341bf28165..1dbb0844b4e 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.h +++ b/src/tactic/arith/propagate_ineqs_tactic.h @@ -51,11 +51,17 @@ This can be achieved by using option :arith-lhs true in the simplifier. --*/ #pragma once + #include "util/params.h" -class ast_manager; -class tactic; +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/bound_simplifier.h" + +inline tactic* mk_propagate_ineqs_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }); +} -tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "mk_propagate_ineqs_tactic(m, p)") */ From 3032c9315d72beb20fc7cfafaae1a243986dd2aa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Jan 2023 14:31:24 -0800 Subject: [PATCH 316/597] handle to-real in variable mapping Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/bound_simplifier.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/bound_simplifier.h b/src/ast/simplifiers/bound_simplifier.h index 9d6b0995597..6977261c541 100644 --- a/src/ast/simplifiers/bound_simplifier.h +++ b/src/ast/simplifiers/bound_simplifier.h @@ -64,9 +64,13 @@ class bound_simplifier : public dependent_expr_simplifier { unsigned v = m_expr2var.get(e->get_id(), UINT_MAX); if (v == UINT_MAX) { v = m_var2expr.size(); - bp.mk_var(v, a.is_int(e)); + expr* core_e = e; + a.is_to_real(e, core_e); + bp.mk_var(v, a.is_int(core_e)); m_expr2var.setx(e->get_id(), v, UINT_MAX); - m_var2expr.push_back(e); + if (e != core_e) + m_expr2var.setx(core_e->get_id(), v, UINT_MAX); + m_var2expr.push_back(core_e); } return v; } From 2ae476416c1458190af50c7906cf20c6237ac9de Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Jan 2023 17:38:34 -0800 Subject: [PATCH 317/597] initial outline of exponentiation Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.h | 2 + src/math/lp/nla_solver.cpp | 193 ++++++++++++++++++++++++++++--------- src/math/lp/nla_solver.h | 47 ++++----- src/smt/theory_lra.cpp | 67 +++++-------- 4 files changed, 200 insertions(+), 109 deletions(-) diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 0f199093329..0e759cf59e0 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -465,6 +465,8 @@ class core { lbool check(vector& l_vec); + void set_lemma_vec(vector& l_vec) { m_lemma_vec = &l_vec; } + bool no_lemmas_hold() const; lbool test_check(vector& l); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 0bd54a77e81..e0df39503c6 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -13,63 +13,168 @@ #include "math/lp/factorization.h" #include "math/lp/nla_solver.h" #include "math/lp/nla_core.h" +#include "math/polynomial/algebraic_numbers.h" namespace nla { -nla_settings& solver::settings() { return m_core->m_nla_settings; } + nla_settings& solver::settings() { return m_core->m_nla_settings; } -void solver::add_monic(lpvar v, unsigned sz, lpvar const* vs) { - m_core->add_monic(v, sz, vs); -} + void solver::add_monic(lpvar v, unsigned sz, lpvar const* vs) { + m_core->add_monic(v, sz, vs); + } + + bool solver::is_monic_var(lpvar v) const { + return m_core->is_monic_var(v); + } + + bool solver::need_check() { return true; } + + lbool solver::check(vector& l) { + return m_core->check(l); + } + + void solver::push(){ + m_core->push(); + } + + void solver::pop(unsigned n) { + m_core->pop(n); + } + + solver::solver(lp::lar_solver& s, reslimit& limit): + m_core(alloc(core, s, limit)) { + } + + bool solver::influences_nl_var(lpvar j) const { + return m_core->influences_nl_var(j); + } + + solver::~solver() { + dealloc(m_core); + } + + std::ostream& solver::display(std::ostream& out) const { + m_core->print_monics(out); + if (use_nra_model()) + m_core->m_nra.display(out); + return out; + } + + bool solver::use_nra_model() const { return m_core->use_nra_model(); } -bool solver::is_monic_var(lpvar v) const { - return m_core->is_monic_var(v); -} + core& solver::get_core() { return *m_core; } -bool solver::need_check() { return true; } + nlsat::anum_manager& solver::am() { return m_core->m_nra.am(); } -lbool solver::check(vector& l) { - return m_core->check(l); -} - -void solver::push(){ - m_core->push(); -} + nlsat::anum const& solver::am_value(lp::var_index v) const { + SASSERT(use_nra_model()); + return m_core->m_nra.value(v); + } + + void solver::collect_statistics(::statistics & st) { + m_core->collect_statistics(st); + } -void solver::pop(unsigned n) { - m_core->pop(n); -} + // ensure r = x^y, add abstraction/refinement lemmas + lbool solver::check_power(lpvar r, lpvar x, lpvar y, vector& lemmas) { + if (x == null_lpvar || y == null_lpvar || r == null_lpvar) + return l_undef; -solver::solver(lp::lar_solver& s, reslimit& limit): - m_core(alloc(core, s, limit)) { -} + if (use_nra_model()) + return l_undef; -bool solver::influences_nl_var(lpvar j) const { - return m_core->influences_nl_var(j); -} + auto xval = m_core->val(x); + auto yval = m_core->val(y); + auto rval = m_core->val(r); -solver::~solver() { - dealloc(m_core); -} + core& c = get_core(); + c.set_lemma_vec(lemmas); + lemmas.reset(); -std::ostream& solver::display(std::ostream& out) const { - m_core->print_monics(out); - if( use_nra_model()) { - m_core->m_nra.display(out); - } - return out; -} + // x >= x0 > 0, y >= y0 > 0 => r >= x0^y0 + // x >= x0 > 0, y <= y0 => r <= x0^y0 + // x != 0, y = 0 => r = 1 + // x = 0, y != 0 => r = 0 + // + // for x fixed, it is exponentiation + // => use tangent lemmas and error tolerance. -bool solver::use_nra_model() const { return m_core->use_nra_model(); } -core& solver::get_core() { return *m_core; } -nlsat::anum_manager& solver::am() { return m_core->m_nra.am(); } -nlsat::anum const& solver::am_value(lp::var_index v) const { - SASSERT(use_nra_model()); - return m_core->m_nra.value(v); -} + if (xval > 0 && yval.is_unsigned()) { + auto r2val = power(xval, yval.get_unsigned()); + if (rval == r2val) + return l_true; + if (xval != 0 && yval == 0) { + new_lemma lemma(c, "x != 0 => x^0 = 1"); + lemma |= ineq(x, llc::EQ, rational::zero()); + lemma |= ineq(y, llc::NE, rational::zero()); + lemma |= ineq(r, llc::EQ, rational::one()); + return l_false; + } + if (xval == 0 && yval > 0) { + new_lemma lemma(c, "y != 0 => 0^y = 0"); + lemma |= ineq(x, llc::NE, rational::zero()); + lemma |= ineq(y, llc::EQ, rational::zero()); + lemma |= ineq(r, llc::EQ, rational::zero()); + return l_false; + } + if (xval > 0 && r2val < rval) { + SASSERT(yval > 0); + new_lemma lemma(c, "x >= x0 > 0, y >= y0 > 0 => r >= x0^y0"); + lemma |= ineq(x, llc::LT, xval); + lemma |= ineq(y, llc::LT, yval); + lemma |= ineq(r, llc::GE, r2val); + return l_false; + } + if (xval > 0 && r2val < rval) { + new_lemma lemma(c, "x >= x0 > 0, y <= y0 => r <= x0^y0"); + lemma |= ineq(x, llc::LT, xval); + lemma |= ineq(y, llc::GT, yval); + lemma |= ineq(r, llc::LE, r2val); + return l_false; + } + } + if (xval > 0 && yval > 0 && !yval.is_int()) { + auto ynum = numerator(yval); + auto yden = denominator(yval); + if (!ynum.is_unsigned()) + return l_undef; + if (!yden.is_unsigned()) + return l_undef; + // r = x^{yn/yd} + // <=> + // r^yd = x^yn + auto ryd = power(rval, yden.get_unsigned()); + auto xyn = power(xval, ynum.get_unsigned()); + if (ryd == xyn) + return l_true; +#if 0 + // try some root approximation instead? + if (ryd > xyn) { + // todo + } + if (ryd < xyn) { + // todo + } +#endif -void solver::collect_statistics(::statistics & st) { - m_core->collect_statistics(st); -} + } + + + return l_undef; + + // anum isn't initialized unless nra_solver is invoked. + // there is no proviso for using algebraic numbers outside of the nra solver. + // so either we have a rational refinement version _and_ an algebraic numeral refinement + // loop or we introduce algebraic numerals outside of the nra_solver + +#if 0 + scoped_anum xval(am()), yval(am()), rval(am()); + + am().set(xval, am_value(x)); + am().set(yval, am_value(y)); + am().set(rval, am_value(r)); +#endif + + } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 0754a4970da..f372410d816 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -16,29 +16,30 @@ Copyright (c) 2017 Microsoft Corporation #include "math/lp/nla_settings.h" #include "math/lp/nla_core.h" namespace nra { -class solver; + class solver; } namespace nla { -class core; -// nonlinear integer incremental linear solver -class solver { - core* m_core; -public: - void add_monic(lpvar v, unsigned sz, lpvar const* vs); - solver(lp::lar_solver& s, reslimit& limit); - ~solver(); - nla_settings& settings(); - void push(); - void pop(unsigned scopes); - bool need_check(); - lbool check(vector&); - bool is_monic_var(lpvar) const; - bool influences_nl_var(lpvar) const; - std::ostream& display(std::ostream& out) const; - bool use_nra_model() const; - core& get_core(); - nlsat::anum_manager& am(); - nlsat::anum const& am_value(lp::var_index v) const; - void collect_statistics(::statistics & st); -}; + class core; + // nonlinear integer incremental linear solver + class solver { + core* m_core; + public: + void add_monic(lpvar v, unsigned sz, lpvar const* vs); + solver(lp::lar_solver& s, reslimit& limit); + ~solver(); + nla_settings& settings(); + void push(); + void pop(unsigned scopes); + bool need_check(); + lbool check(vector&); + lbool check_power(lpvar r, lpvar x, lpvar y, vector&); + bool is_monic_var(lpvar) const; + bool influences_nl_var(lpvar) const; + std::ostream& display(std::ostream& out) const; + bool use_nra_model() const; + core& get_core(); + nlsat::anum_manager& am(); + nlsat::anum const& am_value(lp::var_index v) const; + void collect_statistics(::statistics & st); + }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 500e0307c37..df5f1231324 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -797,8 +797,17 @@ class theory_lra::imp { return internalize_linearized_def(term, st); } + lpvar get_lpvar(expr* e) { + return get_lpvar(get_enode(e)); + } + + lpvar get_lpvar(enode* n) { + ensure_column(n); + return n ? get_lpvar(n->get_th_var(get_id())) : lp::null_lpvar; + } + lpvar get_lpvar(theory_var v) const { - return lp().external_to_local(v); + return v == null_theory_var ? lp::null_lpvar : lp().external_to_local(v); } lp::tv get_tv(theory_var v) const { @@ -1424,10 +1433,13 @@ class theory_lra::imp { return v != null_theory_var && lp().external_is_used(v); } + void ensure_column(enode* n) { + ensure_column(n->get_th_var(get_id())); + } + void ensure_column(theory_var v) { - if (!lp().external_is_used(v)) { + if (!lp().external_is_used(v) && v != null_theory_var) register_theory_var_in_lar_solver(v); - } } mutable vector> m_todo_terms; @@ -1570,47 +1582,18 @@ class theory_lra::imp { final_check_status eval_power(expr* e) { expr* x, * y; VERIFY(a.is_power(e, x, y)); - enode* n = get_enode(e); - enode* xn = get_enode(x); - enode* yn = get_enode(y); - if (!n || !xn || !yn) - return FC_GIVEUP; - rational valx, valy, valn; - theory_var v = n->get_th_var(get_id()); - if (!get_value(xn, valx)) - return FC_GIVEUP; - if (!get_value(yn, valy)) - return FC_GIVEUP; - if (!get_value(n, valn)) - return FC_GIVEUP; - - // TBD - check that values align so return FC_DONE. - if (valn < 0 && valx > 0 && valy > 0) { - mk_axiom(mk_literal(a.mk_le(x, a.mk_numeral(rational(0), x->get_sort()))), - ~mk_literal(a.mk_le(e, a.mk_numeral(rational(0), e->get_sort())))); - return FC_CONTINUE; - } - - // add tangent lemmas for power: - // establish equality using algebraic numerals - - // appears to be useless: - if (false && !valy.is_int() && numerator(valy) == 1 && denominator(valy) > 0) { - rational d = denominator(valy); - if (!d.is_unsigned()) - return FC_GIVEUP; - unsigned den = d.get_unsigned(); - // e = x^{1/y} - // => e^y = x - - ptr_vector es; - for (unsigned i = 0; i < den; ++i) - es.push_back(e); - expr* em = a.mk_mul(es.size(), es.data()); - mk_axiom(~mk_literal(m.mk_eq(y, a.mk_real(valy))), mk_literal(m.mk_eq(em, x))); + switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y), m_nla_lemma_vector)) { + case l_true: + return FC_DONE; + case l_false: + for (const nla::lemma & l : m_nla_lemma_vector) + false_case_of_check_nla(l); return FC_CONTINUE; - + case l_undef: + return FC_GIVEUP; + default: + break; } return FC_GIVEUP; } From 4601d1d66493c7fad1fce472188bd3c505c8e453 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 03:37:09 -0800 Subject: [PATCH 318/597] fix #6550 Signed-off-by: Nikolaj Bjorner --- src/api/api_arith.cpp | 4 ++-- src/api/c++/z3++.h | 4 ++-- src/api/z3_api.h | 4 ++-- src/ast/rewriter/arith_rewriter.cpp | 2 +- src/smt/theory_bv.cpp | 2 +- src/smt/theory_fpa.cpp | 11 ++++------- src/smt/theory_lra.cpp | 4 ++-- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index 9671dbc26a0..c0d599de727 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -48,7 +48,7 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den) { + Z3_ast Z3_API Z3_mk_real(Z3_context c, int64_t num, int64_t den) { Z3_TRY; LOG_Z3_mk_real(c, num, den); RESET_ERROR_CODE(); @@ -57,7 +57,7 @@ extern "C" { RETURN_Z3(nullptr); } sort* s = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); - ast* a = mk_c(c)->mk_numeral_core(rational(num, den), s); + ast* a = mk_c(c)->mk_numeral_core(rational(num, rational::i64())/rational(den, rational::i64()), s); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index ec7b451fa9e..9d4ddcfb405 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -400,11 +400,11 @@ namespace z3 { expr int_val(uint64_t n); expr int_val(char const * n); - expr real_val(int n, int d); expr real_val(int n); expr real_val(unsigned n); expr real_val(int64_t n); expr real_val(uint64_t n); + expr real_val(int64_t n, int64_t d); expr real_val(char const * n); expr bv_val(int n, unsigned sz); @@ -3630,7 +3630,7 @@ namespace z3 { inline expr context::int_val(uint64_t n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } - inline expr context::real_val(int n, int d) { Z3_ast r = Z3_mk_real(m_ctx, n, d); check_error(); return expr(*this, r); } + inline expr context::real_val(int64_t n, int64_t d) { Z3_ast r = Z3_mk_real(m_ctx, n, d); check_error(); return expr(*this, r); } inline expr context::real_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(int64_t n) { Z3_ast r = Z3_mk_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } diff --git a/src/api/z3_api.h b/src/api/z3_api.h index ffa0d866510..fd85dd7810e 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3419,9 +3419,9 @@ extern "C" { \sa Z3_mk_int \sa Z3_mk_unsigned_int - def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT), _in(INT))) + def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT64), _in(INT64))) */ - Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den); + Z3_ast Z3_API Z3_mk_real(Z3_context c, int64_t num, int64_t den); /** \brief Create a numeral of an int, bit-vector, or finite-domain sort. diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 15456d41ef2..7cade3bfa82 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -1321,7 +1321,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res bool is_num_y = m_util.is_numeral(arg2, y); auto ensure_real = [&](expr* e) { return m_util.is_int(e) ? m_util.mk_to_real(e) : e; }; - TRACE("arith", tout << mk_pp(arg1, m) << " " << mk_pp(arg2, m) << "\n";); + TRACE("arith", tout << mk_bounded_pp(arg1, m) << " " << mk_bounded_pp(arg2, m) << "\n";); if (is_num_x && x.is_one()) { result = m_util.mk_numeral(x, false); return BR_DONE; diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 62b7a2a20b1..b3a6e77ff4d 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1379,7 +1379,7 @@ namespace smt { } void theory_bv::relevant_eh(app * n) { - TRACE("arith", tout << "relevant: #" << n->get_id() << " " << ctx.e_internalized(n) << ": " << mk_pp(n, m) << "\n";); + TRACE("arith", tout << "relevant: #" << n->get_id() << " " << ctx.e_internalized(n) << ": " << mk_bounded_pp(n, m) << "\n";); TRACE("bv", tout << "relevant: #" << n->get_id() << " " << ctx.e_internalized(n) << ": " << mk_pp(n, m) << "\n";); if (m.is_bool(n)) { bool_var v = ctx.get_bool_var(n); diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index ee547b22d14..d63ff93cd2f 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -650,8 +650,7 @@ namespace smt { theory_var v = n->get_th_var(get_family_id()); if (v != -1) { if (first) out << "fpa theory variables:" << std::endl; - out << v << " -> " << - mk_ismt2_pp(n->get_expr(), m) << std::endl; + out << v << " -> " << enode_pp(n, ctx) << "\n"; first = false; } } @@ -661,22 +660,20 @@ namespace smt { out << "bv theory variables:" << std::endl; for (enode * n : ctx.enodes()) { theory_var v = n->get_th_var(m_bv_util.get_family_id()); - if (v != -1) out << v << " -> " << - mk_ismt2_pp(n->get_expr(), m) << std::endl; + if (v != -1) out << v << " -> " << enode_pp(n, ctx) << "\n"; } out << "arith theory variables:" << std::endl; for (enode* n : ctx.enodes()) { theory_var v = n->get_th_var(m_arith_util.get_family_id()); - if (v != -1) out << v << " -> " << - mk_ismt2_pp(n->get_expr(), m) << std::endl; + if (v != -1) out << v << " -> " << enode_pp(n, ctx) << "\n"; } out << "equivalence classes:\n"; for (enode * n : ctx.enodes()) { expr * e = n->get_expr(); expr * r = n->get_root()->get_expr(); - out << r->get_id() << " --> " << mk_ismt2_pp(e, m) << std::endl; + out << r->get_id() << " --> " << enode_pp(n, ctx) << "\n"; } } }; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index df5f1231324..01282a53bf2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1004,7 +1004,7 @@ class theory_lra::imp { } void assign_eh(bool_var v, bool is_true) { - TRACE("arith", tout << mk_pp(ctx().bool_var2expr(v), m) << " " << (literal(v, !is_true)) << "\n";); + TRACE("arith", tout << mk_bounded_pp(ctx().bool_var2expr(v), m) << " " << (literal(v, !is_true)) << "\n";); m_asserted_atoms.push_back(delayed_atom(v, is_true)); } @@ -1049,7 +1049,7 @@ class theory_lra::imp { } void apply_sort_cnstr(enode* n, sort*) { - TRACE("arith", tout << "sort constraint: " << pp(n, m) << "\n";); + TRACE("arith", tout << "sort constraint: " << enode_pp(n, ctx()) << "\n";); #if 0 if (!th.is_attached_to_var(n)) { mk_var(n->get_owner()); From d5bd20978bd063ca3d243d3ecd728163411e619e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 03:38:19 -0800 Subject: [PATCH 319/597] fix #6550 Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 76f163b85df..2d466c3cd3e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -19,6 +19,7 @@ Version 4.12.2 implies that the modulus is redundant. This tactic is useful for benchmarks created by converting bit-vector semantics to integer reasoning. +- change API function Z3_mk_real to take two int64 as arguments instead of int. Version 4.12.1 ============== From 3f1b7866ca2d2c005aeb7934adf410d6c3034ecf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 03:53:42 -0800 Subject: [PATCH 320/597] convert caml mk_real to int64 Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 8 +------- src/api/ml/z3.mli | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 2fa4acc6567..c271f60cfd4 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1092,13 +1092,7 @@ struct let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x let mk_const (ctx:context) (name:Symbol.symbol) = Expr.mk_const ctx name (mk_sort ctx) let mk_const_s (ctx:context) (name:string) = mk_const ctx (Symbol.mk_string ctx name) - let mk_numeral_nd (ctx:context) (num:int) (den:int) = - if den = 0 then - raise (Error "Denominator is zero") - else if not (check_int32 num) || not (check_int32 den) then - raise (Error "numerals don't fit in 32 bits") - else - Z3native.mk_real ctx num den + let mk_numeral_nd (ctx:context) (num:int64) (den:int64) = Z3native.mk_real ctx num den let mk_numeral_s (ctx:context) (v:string) = Z3native.mk_numeral ctx v (mk_sort ctx) let mk_numeral_i (ctx:context) (v:int) = mk_int_expr ctx v (mk_sort ctx) diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index b7fa27b5ecd..cb61bee6829 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -1264,7 +1264,7 @@ sig (** Create a real numeral from a fraction. @return A Term with rational value and sort Real {!mk_numeral_s} *) - val mk_numeral_nd : context -> int -> int -> Expr.expr + val mk_numeral_nd : context -> int64 -> int64 -> Expr.expr (** Create a real numeral. @return A Term with the given value and sort Real *) From 15d853dc04138c98bf8b193842cb4033b36dedff Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 04:15:52 -0800 Subject: [PATCH 321/597] add trail to avoid stale references in expr2var --- src/ast/simplifiers/bound_simplifier.cpp | 4 ++++ src/ast/simplifiers/bound_simplifier.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/ast/simplifiers/bound_simplifier.cpp b/src/ast/simplifiers/bound_simplifier.cpp index 89ed6da057f..06302ab6fb7 100644 --- a/src/ast/simplifiers/bound_simplifier.cpp +++ b/src/ast/simplifiers/bound_simplifier.cpp @@ -208,6 +208,8 @@ void bound_simplifier::tighten_bound(dependent_expr const& de) { assert_upper(x, n - k, true); } + + // x != k, k <= x -> k < x if (m.is_not(f, f) && m.is_eq(f, x, y)) { if (a.is_numeral(x)) @@ -222,6 +224,7 @@ void bound_simplifier::tighten_bound(dependent_expr const& de) { assert_upper(x, n, true); } } + } void bound_simplifier::assert_upper(expr* x, rational const& n, bool strict) { @@ -449,6 +452,7 @@ void bound_simplifier::reset() { bp.reset(); m_var2expr.reset(); m_expr2var.reset(); + m_trail.reset(); } #if 0 diff --git a/src/ast/simplifiers/bound_simplifier.h b/src/ast/simplifiers/bound_simplifier.h index 6977261c541..7950f418b84 100644 --- a/src/ast/simplifiers/bound_simplifier.h +++ b/src/ast/simplifiers/bound_simplifier.h @@ -40,6 +40,7 @@ class bound_simplifier : public dependent_expr_simplifier { dep_intervals m_interval; ptr_vector m_var2expr; unsigned_vector m_expr2var; + expr_ref_vector m_trail; mpq_buffer m_num_buffer; var_buffer m_var_buffer; unsigned m_num_reduced = 0; @@ -71,6 +72,7 @@ class bound_simplifier : public dependent_expr_simplifier { if (e != core_e) m_expr2var.setx(core_e->get_id(), v, UINT_MAX); m_var2expr.push_back(core_e); + m_trail.push_back(e); } return v; } @@ -100,6 +102,7 @@ class bound_simplifier : public dependent_expr_simplifier { m_rewriter(m), bp(nm, m_alloc, p), m_interval(m.limit()), + m_trail(m), m_num_buffer(nm) { updt_params(p); } From 47c7ed3b1719266c0686c3caaa6f8cf5345e64e2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 04:33:52 -0800 Subject: [PATCH 322/597] update ml example to 64 bit Signed-off-by: Nikolaj Bjorner --- examples/ml/ml_example.ml | 8 ++++---- src/ast/simplifiers/bound_simplifier.cpp | 13 +++++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/examples/ml/ml_example.ml b/examples/ml/ml_example.ml index 5b4e6e9ed1d..6fd795476ab 100644 --- a/examples/ml/ml_example.ml +++ b/examples/ml/ml_example.ml @@ -30,11 +30,11 @@ let model_converter_test ( ctx : context ) = let xr = (Expr.mk_const ctx (Symbol.mk_string ctx "x") (Real.mk_sort ctx)) in let yr = (Expr.mk_const ctx (Symbol.mk_string ctx "y") (Real.mk_sort ctx)) in let g4 = (mk_goal ctx true false false ) in - (Goal.add g4 [ (mk_gt ctx xr (Real.mk_numeral_nd ctx 10 1)) ]) ; + (Goal.add g4 [ (mk_gt ctx xr (Real.mk_numeral_nd ctx 10L 1L)) ]) ; (Goal.add g4 [ (mk_eq ctx yr - (Arithmetic.mk_add ctx [ xr; (Real.mk_numeral_nd ctx 1 1) ])) ]) ; - (Goal.add g4 [ (mk_gt ctx yr (Real.mk_numeral_nd ctx 1 1)) ]) ; + (Arithmetic.mk_add ctx [ xr; (Real.mk_numeral_nd ctx 1L 1L) ])) ]) ; + (Goal.add g4 [ (mk_gt ctx yr (Real.mk_numeral_nd ctx 1L 1L)) ]) ; ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g4 None) in if ((get_num_subgoals ar) == 1 && @@ -163,7 +163,7 @@ let basic_tests ( ctx : context ) = ) ; model_converter_test ctx ; (* Real num/den test. *) - let rn = Real.mk_numeral_nd ctx 42 43 in + let rn = Real.mk_numeral_nd ctx 42L 43L in let inum = (get_numerator rn) in let iden = get_denominator rn in Printf.printf "Numerator: %s Denominator: %s\n" (Real.numeral_to_string inum) (Real.numeral_to_string iden) ; diff --git a/src/ast/simplifiers/bound_simplifier.cpp b/src/ast/simplifiers/bound_simplifier.cpp index 06302ab6fb7..1f4fb9b9d85 100644 --- a/src/ast/simplifiers/bound_simplifier.cpp +++ b/src/ast/simplifiers/bound_simplifier.cpp @@ -200,13 +200,22 @@ void bound_simplifier::tighten_bound(dependent_expr const& de) { return; rational n, k; expr* x, *y, *f = de.fml(); - expr* z, *u; + expr* z, * u, * v, * w; bool strict; if (a.is_le(f, x, y)) { // x <= (x + k) mod N && x >= 0 -> x + k < N if (a.is_mod(y, z, u) && a.is_numeral(u, n) && has_lower(x, k, strict) && k >= 0 && is_offset(z, x, k) && k > 0 && k < n) assert_upper(x, n - k, true); - + // x <= (x + y) mod N && x >= 0 && 0 <= y < N => x + y < N + if (a.is_mod(y, z, u) && a.is_numeral(u, n) && n > 0) { + assert_upper(x, n, true); + if (has_lower(x, k, strict) && k >= 0 && a.is_add(z, v, w)) { + if (x == v && has_upper(w, k, strict) && k < n) + assert_upper(z, n, true); + if (x == w && has_upper(v, k, strict) && k < n) + assert_upper(z, n, true); + } + } } From eac7d7576fd8d21fe4c4a9980b98b9a7ff673bd1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 12:29:42 -0800 Subject: [PATCH 323/597] force to_fp to disambiguate +zero and -zero, #6548, filter unsupported on relevancy Signed-off-by: Nikolaj Bjorner --- src/ast/fpa/fpa2bv_converter.cpp | 2 ++ src/smt/theory_lra.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index c456da29b7d..ab13e751a30 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -2809,6 +2809,8 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * expr * e = m.mk_eq(m_util.mk_to_real(result), x); m_extra_assertions.push_back(e); + // x = 0 -> result = 0+ + m_extra_assertions.push_back(m.mk_implies(m.mk_eq(x, zero), m.mk_eq(result, m_util.mk_pzero(result->get_sort())))); } SASSERT(is_well_sorted(m, result)); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 01282a53bf2..c5b897d781f 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1653,7 +1653,7 @@ class theory_lra::imp { return FC_CONTINUE; } for (expr* e : m_not_handled) { - if (!ctx().is_relevant(e) && false) + if (!ctx().is_relevant(e)) continue; st = FC_DONE; switch (eval_unsupported(e)) { From fa72ec5405b35fb59e8c4686954a3572b4604132 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 13:05:34 -0800 Subject: [PATCH 324/597] switch to expose fresh function instead of changing legacy function Signed-off-by: Nikolaj Bjorner --- examples/ml/ml_example.ml | 8 ++++---- src/api/api_arith.cpp | 18 ++++++++++++++++-- src/api/c++/z3++.h | 2 +- src/api/ml/z3.ml | 8 +++++++- src/api/ml/z3.mli | 2 +- src/api/z3_api.h | 14 ++++++++++++-- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/examples/ml/ml_example.ml b/examples/ml/ml_example.ml index 6fd795476ab..5b4e6e9ed1d 100644 --- a/examples/ml/ml_example.ml +++ b/examples/ml/ml_example.ml @@ -30,11 +30,11 @@ let model_converter_test ( ctx : context ) = let xr = (Expr.mk_const ctx (Symbol.mk_string ctx "x") (Real.mk_sort ctx)) in let yr = (Expr.mk_const ctx (Symbol.mk_string ctx "y") (Real.mk_sort ctx)) in let g4 = (mk_goal ctx true false false ) in - (Goal.add g4 [ (mk_gt ctx xr (Real.mk_numeral_nd ctx 10L 1L)) ]) ; + (Goal.add g4 [ (mk_gt ctx xr (Real.mk_numeral_nd ctx 10 1)) ]) ; (Goal.add g4 [ (mk_eq ctx yr - (Arithmetic.mk_add ctx [ xr; (Real.mk_numeral_nd ctx 1L 1L) ])) ]) ; - (Goal.add g4 [ (mk_gt ctx yr (Real.mk_numeral_nd ctx 1L 1L)) ]) ; + (Arithmetic.mk_add ctx [ xr; (Real.mk_numeral_nd ctx 1 1) ])) ]) ; + (Goal.add g4 [ (mk_gt ctx yr (Real.mk_numeral_nd ctx 1 1)) ]) ; ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g4 None) in if ((get_num_subgoals ar) == 1 && @@ -163,7 +163,7 @@ let basic_tests ( ctx : context ) = ) ; model_converter_test ctx ; (* Real num/den test. *) - let rn = Real.mk_numeral_nd ctx 42L 43L in + let rn = Real.mk_numeral_nd ctx 42 43 in let inum = (get_numerator rn) in let iden = get_denominator rn in Printf.printf "Numerator: %s Denominator: %s\n" (Real.numeral_to_string inum) (Real.numeral_to_string iden) ; diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index c0d599de727..7cfd5a34516 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -48,9 +48,9 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_mk_real(Z3_context c, int64_t num, int64_t den) { + Z3_ast Z3_API Z3_mk_real_int64(Z3_context c, int64_t num, int64_t den) { Z3_TRY; - LOG_Z3_mk_real(c, num, den); + LOG_Z3_mk_real_int64(c, num, den); RESET_ERROR_CODE(); if (den == 0) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); @@ -62,6 +62,20 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den) { + Z3_TRY; + LOG_Z3_mk_real(c, num, den); + RESET_ERROR_CODE(); + if (den == 0) { + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + RETURN_Z3(nullptr); + } + sort* s = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); + ast* a = mk_c(c)->mk_numeral_core(rational(num, den), s); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(nullptr); + } + MK_ARITH_OP(Z3_mk_add, OP_ADD); MK_ARITH_OP(Z3_mk_mul, OP_MUL); MK_BINARY_ARITH_OP(Z3_mk_power, OP_POWER); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 9d4ddcfb405..3f47b6637ba 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -3630,7 +3630,7 @@ namespace z3 { inline expr context::int_val(uint64_t n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } - inline expr context::real_val(int64_t n, int64_t d) { Z3_ast r = Z3_mk_real(m_ctx, n, d); check_error(); return expr(*this, r); } + inline expr context::real_val(int64_t n, int64_t d) { Z3_ast r = Z3_mk_real_int64(m_ctx, n, d); check_error(); return expr(*this, r); } inline expr context::real_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(int64_t n) { Z3_ast r = Z3_mk_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index c271f60cfd4..2fa4acc6567 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1092,7 +1092,13 @@ struct let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x let mk_const (ctx:context) (name:Symbol.symbol) = Expr.mk_const ctx name (mk_sort ctx) let mk_const_s (ctx:context) (name:string) = mk_const ctx (Symbol.mk_string ctx name) - let mk_numeral_nd (ctx:context) (num:int64) (den:int64) = Z3native.mk_real ctx num den + let mk_numeral_nd (ctx:context) (num:int) (den:int) = + if den = 0 then + raise (Error "Denominator is zero") + else if not (check_int32 num) || not (check_int32 den) then + raise (Error "numerals don't fit in 32 bits") + else + Z3native.mk_real ctx num den let mk_numeral_s (ctx:context) (v:string) = Z3native.mk_numeral ctx v (mk_sort ctx) let mk_numeral_i (ctx:context) (v:int) = mk_int_expr ctx v (mk_sort ctx) diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index cb61bee6829..b7fa27b5ecd 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -1264,7 +1264,7 @@ sig (** Create a real numeral from a fraction. @return A Term with rational value and sort Real {!mk_numeral_s} *) - val mk_numeral_nd : context -> int64 -> int64 -> Expr.expr + val mk_numeral_nd : context -> int -> int -> Expr.expr (** Create a real numeral. @return A Term with the given value and sort Real *) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index fd85dd7810e..88aaa8938ee 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3417,11 +3417,21 @@ extern "C" { \sa Z3_mk_numeral \sa Z3_mk_int + \sa Z3_mk_real_int64 \sa Z3_mk_unsigned_int - def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT64), _in(INT64))) + def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT), _in(INT))) */ - Z3_ast Z3_API Z3_mk_real(Z3_context c, int64_t num, int64_t den); + Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den); + + /** + \brief Create a real from a fraction of int64. + + \sa Z3_mk_real + def_API('Z3_mk_real_int64', AST, (_in(CONTEXT), _in(INT64), _in(INT64))) + */ + + Z3_ast Z3_API Z3_mk_real_int64(Z3_context c, int64_t num, int64_t den); /** \brief Create a numeral of an int, bit-vector, or finite-domain sort. From 6a7343aab4addd0d908da1575f975d8691a98a25 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 13:06:41 -0800 Subject: [PATCH 325/597] update julia bindings to use 64-bit mk_real (real_val) Signed-off-by: Nikolaj Bjorner --- src/api/julia/z3jl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/julia/z3jl.cpp b/src/api/julia/z3jl.cpp index 755911f6c9d..2627679e2e3 100644 --- a/src/api/julia/z3jl.cpp +++ b/src/api/julia/z3jl.cpp @@ -692,7 +692,7 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m) .method("real_val", [](context &a, const jlcxx::StrictlyTypedNumber b) { return a.real_val(b.value); }) .method("real_val", [](context &a, const jlcxx::StrictlyTypedNumber b) { return a.real_val(b.value); }) .method("real_val", [](context &a, const jlcxx::StrictlyTypedNumber b) { return a.real_val(b.value); }) - .method("real_val", static_cast(&context::real_val)) + .method("real_val", static_cast(&context::real_val)) .method("real_val", static_cast(&context::real_val)) // .method("bv_val", [](context &a, const jlcxx::StrictlyTypedNumber b, unsigned c) { return a.bv_val(b.value, c); }) From 8757778404bdb3fe11cd824bb098728bbc463ed0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:22:12 -0800 Subject: [PATCH 326/597] Bump mymindstorm/setup-emsdk from 11 to 12 (#6541) Bumps [mymindstorm/setup-emsdk](https://github.com/mymindstorm/setup-emsdk) from 11 to 12. - [Release notes](https://github.com/mymindstorm/setup-emsdk/releases) - [Commits](https://github.com/mymindstorm/setup-emsdk/compare/v11...v12) --- updated-dependencies: - dependency-name: mymindstorm/setup-emsdk dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/wasm-release.yml | 2 +- .github/workflows/wasm.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wasm-release.yml b/.github/workflows/wasm-release.yml index c345717843a..de15a242c1a 100644 --- a/.github/workflows/wasm-release.yml +++ b/.github/workflows/wasm-release.yml @@ -36,7 +36,7 @@ jobs: cp ../../../LICENSE.txt . - name: Setup emscripten - uses: mymindstorm/setup-emsdk@v11 + uses: mymindstorm/setup-emsdk@v12 with: no-install: true version: ${{env.EM_VERSION}} diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 418438635f8..e8ac095e594 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -29,7 +29,7 @@ jobs: node-version: "lts/*" - name: Setup emscripten - uses: mymindstorm/setup-emsdk@v11 + uses: mymindstorm/setup-emsdk@v12 with: no-install: true version: ${{env.EM_VERSION}} From 6c9358ce41d804e332542701bea6bae5a2ba7d9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:22:41 -0800 Subject: [PATCH 327/597] Bump docker/build-push-action from 3.2.0 to 3.3.0 (#6540) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v3.2.0...v3.3.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index abaa26db204..ca41ceaa7a0 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v3.3.0 with: context: . push: true From f100d2f4decb6ea6a426a62aee2ac9f06dd44cca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Jan 2023 17:49:48 -0800 Subject: [PATCH 328/597] add contextual simplification to bv-bounds-tactic Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/bound_simplifier.cpp | 2 ++ src/tactic/bv/bv_bounds_tactic.cpp | 46 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/ast/simplifiers/bound_simplifier.cpp b/src/ast/simplifiers/bound_simplifier.cpp index 1f4fb9b9d85..f5c986425a6 100644 --- a/src/ast/simplifiers/bound_simplifier.cpp +++ b/src/ast/simplifiers/bound_simplifier.cpp @@ -72,11 +72,13 @@ br_status bound_simplifier::reduce_app(func_decl* f, unsigned num_args, expr* co return BR_FAILED; if (N > hi && lo >= 0) { result = x; + TRACE("propagate-ineqs", tout << expr_ref(m.mk_app(f, num_args, args), m) << " -> " << result << "\n"); return BR_DONE; } if (2 * N > hi && lo >= N) { result = a.mk_sub(x, a.mk_int(N)); m_rewriter(result); + TRACE("propagate-ineqs", tout << expr_ref(m.mk_app(f, num_args, args), m) << " -> " << result << "\n"); return BR_DONE; } IF_VERBOSE(2, verbose_stream() << "potentially missed simplification: " << mk_pp(x, m) << " " << lo << " " << hi << " not reduced\n"); diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index f91068b40a9..2e0ce0391ae 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -266,6 +266,7 @@ namespace { svector m_bound_exprs; map m_bound; bool m_propagate_eq = false; + ptr_vector m_args; bv_bounds_base(ast_manager& m):m(m), m_bv(m) {} @@ -390,6 +391,48 @@ namespace { return expr_ref(m_bv.mk_ule(m_bv.mk_bv_add(t, m_bv.mk_numeral(-lo, s)), m_bv.mk_numeral(hi - lo, s)), m); } + // + // use interval information to rewrite sub-terms x to (0 ++ x[hi:0]) + // in other words, identify leading 0s. + // + bool zero_patch(expr* t, expr_ref& result) { + if (!is_app(t)) + return false; + + if (m_bv.is_extract(t)) + return false; + + m_args.reset(); + bool simplified = false; + interval b; + for (expr* arg : *to_app(t)) { + if (!m_bv.is_bv(arg)) { + m_args.push_back(arg); + continue; + } + if (!m_bv.is_extract(arg) && m_bound.find(arg, b)) { + unsigned num_bits = b.hi().get_num_bits(); + unsigned bv_size = m_bv.get_bv_size(arg); + if (0 < num_bits && num_bits < bv_size) { + m_args.push_back(m_bv.mk_concat(m_bv.mk_zero(bv_size - num_bits), + m_bv.mk_extract(num_bits - 1, 0, arg))); + simplified = true; + } + else + m_args.push_back(arg); + } + else + m_args.push_back(arg); + } + + if (simplified) { + result = m.mk_app(to_app(t)->get_decl(), m_args); + return true; + } + + return false; + } + bool simplify_core(expr* t, expr_ref& result) { expr* t1; interval b; @@ -399,6 +442,9 @@ namespace { return true; } + if (zero_patch(t, result)) + return result; + if (!m.is_bool(t)) return false; From b3de7ac595bd0f1a06a79522b1110020741ff571 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2023 11:15:09 -0800 Subject: [PATCH 329/597] remove passing proof parameter to expr-inverter Signed-off-by: Nikolaj Bjorner --- src/ast/converters/expr_inverter.cpp | 14 +++++++------- src/ast/converters/expr_inverter.h | 4 ++-- src/ast/simplifiers/elim_unconstrained.cpp | 4 +--- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 41a60ccd562..abf3125c6fc 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -81,7 +81,7 @@ class basic_expr_inverter : public iexpr_inverter { * */ - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { SASSERT(f->get_family_id() == m.get_basic_family_id()); switch (f->get_decl_kind()) { case OP_ITE: @@ -233,7 +233,7 @@ class arith_expr_inverter : public iexpr_inverter { } - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { SASSERT(f->get_family_id() == a.get_family_id()); switch (f->get_decl_kind()) { case OP_ADD: @@ -531,7 +531,7 @@ class bv_expr_inverter : public iexpr_inverter { * y := 0 * */ - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { SASSERT(f->get_family_id() == bv.get_family_id()); switch (f->get_decl_kind()) { case OP_BADD: @@ -611,7 +611,7 @@ class array_expr_inverter : public iexpr_inverter { family_id get_fid() const override { return a.get_family_id(); } - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { SASSERT(f->get_family_id() == a.get_family_id()); switch (f->get_decl_kind()) { case OP_SELECT: @@ -679,7 +679,7 @@ class dt_expr_inverter : public iexpr_inverter { * head(x) -> fresh * x := cons(fresh, arb) */ - bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r, proof_ref& pr) override { + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { if (dt.is_accessor(f)) { SASSERT(num == 1); if (uncnstr(args[0])) { @@ -799,7 +799,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { } -bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr, proof_ref& pr) { +bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr) { if (num == 0) return false; @@ -812,7 +812,7 @@ bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, ex return false; auto* p = m_inverters.get(fid, nullptr); - return p && (*p)(f, num, args, new_expr, pr); + return p && (*p)(f, num, args, new_expr); } bool expr_inverter::mk_diff(expr* t, expr_ref& r) { diff --git a/src/ast/converters/expr_inverter.h b/src/ast/converters/expr_inverter.h index 60540aff38a..e57820f3504 100644 --- a/src/ast/converters/expr_inverter.h +++ b/src/ast/converters/expr_inverter.h @@ -40,7 +40,7 @@ class iexpr_inverter { virtual void set_model_converter(generic_model_converter* mc) { m_mc = mc; } virtual void set_produce_proofs(bool p) { m_produce_proofs = true; } - virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, proof_ref& pr) = 0; + virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr) = 0; virtual bool mk_diff(expr* t, expr_ref& r) = 0; virtual family_id get_fid() const = 0; }; @@ -51,7 +51,7 @@ class expr_inverter : public iexpr_inverter { public: expr_inverter(ast_manager& m); ~expr_inverter() override; - bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr, proof_ref& pr) override; + bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr) override; bool mk_diff(expr* t, expr_ref& r) override; void set_is_var(std::function& is_var) override; void set_model_converter(generic_model_converter* mc) override; diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 982a3c7dc7e..9c4badd01d5 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -63,7 +63,6 @@ void elim_unconstrained::eliminate() { while (!m_heap.empty()) { expr_ref r(m); - proof_ref pr(m); int v = m_heap.erase_min(); node& n = get_node(v); if (n.m_refcount == 0) @@ -85,7 +84,7 @@ void elim_unconstrained::eliminate() { unsigned sz = m_args.size(); for (expr* arg : *to_app(t)) m_args.push_back(reconstruct_term(get_node(arg))); - bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r, pr); + bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r); n.m_refcount = 0; m_args.shrink(sz); if (!inverted) { @@ -114,7 +113,6 @@ void elim_unconstrained::eliminate() { IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(get_node(v).m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << get_node(e).m_refcount << "\n";); - SASSERT(!pr && "not implemented to add proofs\n"); } } From 9e2ec9d0181d35267ec35a559d49f3f1d59fce03 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2023 13:32:51 -0800 Subject: [PATCH 330/597] add stubs for proof production in elim_unconstrained --- src/ast/simplifiers/elim_unconstrained.cpp | 30 ++++++++++++++++++++-- src/ast/simplifiers/elim_unconstrained.h | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 9c4badd01d5..41877202aae 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -36,6 +36,12 @@ Module Name: Nikolaj Bjorner (nbjorner) 2022-11-11. +Notes: + +proof production is work in progress. +reconstruct_term should assign proof objects with nodes by applying +monotonicity or reflexivity rules. + --*/ @@ -85,6 +91,14 @@ void elim_unconstrained::eliminate() { for (expr* arg : *to_app(t)) m_args.push_back(reconstruct_term(get_node(arg))); bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r); + proof_ref pr(m); + if (inverted && m_enable_proofs) { + expr * s = m.mk_app(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + expr * eq = m.mk_eq(s, r); + proof * pr1 = m.mk_def_intro(eq); + proof * pr = m.mk_apply_def(s, r, pr1); + m_trail.push_back(pr); + } n.m_refcount = 0; m_args.shrink(sz); if (!inverted) { @@ -103,6 +117,7 @@ void elim_unconstrained::eliminate() { m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; + get_node(e).m_proof = pr; get_node(e).m_refcount++; IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); SASSERT(!m_heap.contains(root(e))); @@ -147,11 +162,18 @@ void elim_unconstrained::invalidate_parents(expr* e) { */ void elim_unconstrained::init_nodes() { + m_enable_proofs = false; + m_trail.reset(); m_fmls.freeze_suffix(); expr_ref_vector terms(m); - for (unsigned i : indices()) - terms.push_back(m_fmls[i].fml()); + for (unsigned i : indices()) { + auto [f, p, d] = m_fmls[i](); + terms.push_back(f); + if (p) + m_enable_proofs = true; + } + m_trail.append(terms); m_heap.reset(); m_root.reset(); @@ -163,6 +185,9 @@ void elim_unconstrained::init_nodes() { // top-level terms have reference count > 0 for (expr* e : terms) inc_ref(e); + + m_inverter.set_produce_proofs(m_enable_proofs); + } /** @@ -253,6 +278,7 @@ void elim_unconstrained::gc(expr* t) { } } + expr_ref elim_unconstrained::reconstruct_term(node& n0) { expr* t = n0.m_term; if (!n0.m_dirty) diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index aae14bdcb8c..19af099d0e2 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -26,6 +26,7 @@ class elim_unconstrained : public dependent_expr_simplifier { unsigned m_refcount = 0; expr* m_term = nullptr; expr* m_orig = nullptr; + proof* m_proof = nullptr; bool m_dirty = false; ptr_vector m_parents; }; @@ -49,6 +50,7 @@ class elim_unconstrained : public dependent_expr_simplifier { stats m_stats; unsigned_vector m_root; bool m_created_compound = false; + bool m_enable_proofs = false; bool is_var_lt(int v1, int v2) const; node& get_node(unsigned n) { return m_nodes[n]; } From e41dd91893b8b0f05c3e54f2c2cbe176683ca250 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2023 13:34:13 -0800 Subject: [PATCH 331/597] add module for handling axioms for powers --- src/math/lp/CMakeLists.txt | 1 + src/math/lp/nla_core.cpp | 8 +- src/math/lp/nla_core.h | 106 +--------------------- src/math/lp/nla_powers.cpp | 181 +++++++++++++++++++++++++++++++++++++ src/math/lp/nla_powers.h | 29 ++++++ src/math/lp/nla_solver.cpp | 99 +------------------- src/math/lp/nla_types.h | 120 ++++++++++++++++++++++++ 7 files changed, 343 insertions(+), 201 deletions(-) create mode 100644 src/math/lp/nla_powers.cpp create mode 100644 src/math/lp/nla_powers.h create mode 100644 src/math/lp/nla_types.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 6ec8ba12d60..47757734570 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -38,6 +38,7 @@ z3_add_component(lp nla_intervals.cpp nla_monotone_lemmas.cpp nla_order_lemmas.cpp + nla_powers.cpp nla_solver.cpp nla_tangent_lemmas.cpp nra_solver.cpp diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index efd510824f2..b072112f0ab 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -29,6 +29,7 @@ core::core(lp::lar_solver& s, reslimit & lim) : m_basics(this), m_order(this), m_monotone(this), + m_powers(*this), m_intervals(this, lim), m_monomial_bounds(this), m_horner(this), @@ -120,9 +121,8 @@ bool core::canonize_sign(const monic& m) const { bool core::canonize_sign(const factorization& f) const { bool r = false; - for (const factor & a : f) { + for (const factor & a : f) r ^= canonize_sign(a); - } return r; } @@ -1477,6 +1477,10 @@ void core::check_weighted(unsigned sz, std::pair& l_vec) { + m_lemma_vec = &l_vec; + return m_powers.check(r, x, y, l_vec); +} lbool core::check(vector& l_vec) { lp_settings().stats().m_nla_calls++; diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 0e759cf59e0..1e66e4cd79f 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -19,6 +19,7 @@ #include "math/lp/nla_order_lemmas.h" #include "math/lp/nla_monotone_lemmas.h" #include "math/lp/nla_grobner.h" +#include "math/lp/nla_powers.h" #include "math/lp/emonics.h" #include "math/lp/nla_settings.h" #include "math/lp/nex.h" @@ -42,104 +43,6 @@ bool try_insert(const A& elem, B& collection) { return true; } -typedef lp::constraint_index lpci; -typedef lp::lconstraint_kind llc; -typedef lp::constraint_index lpci; -typedef lp::explanation expl_set; -typedef lp::var_index lpvar; -const lpvar null_lpvar = UINT_MAX; - -inline int rat_sign(const rational& r) { return r.is_pos()? 1 : ( r.is_neg()? -1 : 0); } -inline rational rrat_sign(const rational& r) { return rational(rat_sign(r)); } -inline bool is_set(unsigned j) { return j != null_lpvar; } -inline bool is_even(unsigned k) { return (k & 1) == 0; } -class ineq { - lp::lconstraint_kind m_cmp; - lp::lar_term m_term; - rational m_rs; -public: - ineq(lp::lconstraint_kind cmp, const lp::lar_term& term, const rational& rs) : m_cmp(cmp), m_term(term), m_rs(rs) {} - ineq(const lp::lar_term& term, lp::lconstraint_kind cmp, int i) : m_cmp(cmp), m_term(term), m_rs(rational(i)) {} - ineq(const lp::lar_term& term, lp::lconstraint_kind cmp, const rational& rs) : m_cmp(cmp), m_term(term), m_rs(rs) {} - ineq(lpvar v, lp::lconstraint_kind cmp, int i): m_cmp(cmp), m_term(v), m_rs(rational(i)) {} - ineq(lpvar v, lp::lconstraint_kind cmp, rational const& r): m_cmp(cmp), m_term(v), m_rs(r) {} - bool operator==(const ineq& a) const { - return m_cmp == a.m_cmp && m_term == a.m_term && m_rs == a.m_rs; - } - const lp::lar_term& term() const { return m_term; }; - lp::lconstraint_kind cmp() const { return m_cmp; }; - const rational& rs() const { return m_rs; }; -}; - -class lemma { - vector m_ineqs; - lp::explanation m_expl; -public: - void push_back(const ineq& i) { m_ineqs.push_back(i);} - size_t size() const { return m_ineqs.size() + m_expl.size(); } - const vector& ineqs() const { return m_ineqs; } - vector& ineqs() { return m_ineqs; } - lp::explanation& expl() { return m_expl; } - const lp::explanation& expl() const { return m_expl; } - bool is_conflict() const { return m_ineqs.empty() && !m_expl.empty(); } -}; - -class core; -// -// lemmas are created in a scope. -// when the destructor of new_lemma is invoked -// all constraints are assumed added to the lemma -// correctness of the lemma can be checked at this point. -// -class new_lemma { - char const* name; - core& c; - lemma& current() const; - -public: - new_lemma(core& c, char const* name); - ~new_lemma(); - lemma& operator()() { return current(); } - std::ostream& display(std::ostream& out) const; - new_lemma& operator&=(lp::explanation const& e); - new_lemma& operator&=(const monic& m); - new_lemma& operator&=(const factor& f); - new_lemma& operator&=(const factorization& f); - new_lemma& operator&=(lpvar j); - new_lemma& operator|=(ineq const& i); - new_lemma& explain_fixed(lpvar j); - new_lemma& explain_equiv(lpvar u, lpvar v); - new_lemma& explain_var_separated_from_zero(lpvar j); - new_lemma& explain_existing_lower_bound(lpvar j); - new_lemma& explain_existing_upper_bound(lpvar j); - - lp::explanation& expl() { return current().expl(); } - - unsigned num_ineqs() const { return current().ineqs().size(); } -}; - - -inline std::ostream& operator<<(std::ostream& out, new_lemma const& l) { - return l.display(out); -} - -struct pp_fac { - core const& c; - factor const& f; - pp_fac(core const& c, factor const& f): c(c), f(f) {} -}; - -struct pp_var { - core const& c; - lpvar v; - pp_var(core const& c, lpvar v): c(c), v(v) {} -}; - -struct pp_factorization { - core const& c; - factorization const& f; - pp_factorization(core const& c, factorization const& f): c(c), f(f) {} -}; class core { friend struct common; @@ -149,6 +52,7 @@ class core { friend struct basics; friend struct tangents; friend class monotone; + friend class powers; friend struct nla_settings; friend class intervals; friend class horner; @@ -183,6 +87,7 @@ class core { basics m_basics; order m_order; monotone m_monotone; + powers m_powers; intervals m_intervals; monomial_bounds m_monomial_bounds; nla_settings m_nla_settings; @@ -463,9 +368,8 @@ class core { bool conflict_found() const; - lbool check(vector& l_vec); - - void set_lemma_vec(vector& l_vec) { m_lemma_vec = &l_vec; } + lbool check(vector& l_vec); + lbool check_power(lpvar r, lpvar x, lpvar y, vector& l_vec); bool no_lemmas_hold() const; diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp new file mode 100644 index 00000000000..ee3c41ee894 --- /dev/null +++ b/src/math/lp/nla_powers.cpp @@ -0,0 +1,181 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + nla_powers.cpp + +Author: + Lev Nachmanson (levnach) + Nikolaj Bjorner (nbjorner) + +Description: + Refines bounds on powers. + + Reference: TOCL-2018, Cimatti et al. + + +Special cases: + +1. Exponentiation. x is fixed numeral. + +TOCL18 axioms: + a^y > 0 (if a > 0) + y = 0 <=> a^y = 1 (if a != 0) + y < 0 <=> a^y < 1 (if a > 1) + y > 0 <=> a^y > 1 (if a > 1) + y != 0 <=> a^y > y + 1 (if a >= 2) + y1 < y2 <=> a^y1 < a^y2 (**) + +Other special case: + + y = 1 <= a^y = a + +TOCL18 approach: Polynomial abstractions + +Taylor: a^y = sum_i ln(a)*y^i/i! + +Truncation: P(n, a) = sum_{i=0}^n ln(a)*y^i/i! + +y = 0: handled by axiom a^y = 1 +y < 0: P(2n-1, y) <= a^y <= P(2n, y), n > 0 because Taylor contribution is negative at odd powers. +y > 0: P(n, y) <= a^y <= P(n, y)*(1 - y^{n+1}/(n+1)!) + + +2. Powers. y is fixed positive integer. + +3. Other + +General case: + + For now the solver integrates just weak monotonicity lemmas: + + - x >= x0 > 0, y >= y0 => x^y >= x0^y0 + - 0 < x <= x0, y <= y0 => x^y <= x0^y0 + + +TODO: + +- Comprehensive integration for truncation polynomial approximation. +- TOCL18 approach includes refinement loop based on precision epsilon. +- accept solvability if r is within a small range of x^y, when x^y is not rational. +- integrate algebraic numbers, or even extension fields (for 'e'). +- integrate monotonicy axioms (**) by tracking exponents across instances. + +anum isn't initialized unless nra_solver is invoked. +there is no proviso for using algebraic numbers outside of the nra solver. +so either we have a rational refinement version _and_ an algebraic numeral refinement +loop or we introduce algebraic numerals outside of the nra_solver + +scoped_anum xval(am()), yval(am()), rval(am()); + +am().set(xval, am_value(x)); +am().set(yval, am_value(y)); +am().set(rval, am_value(r)); + +--*/ +#include "math/lp/nla_core.h" + +namespace nla { + + lbool powers::check(lpvar r, lpvar x, lpvar y, vector& lemmas) { + if (x == null_lpvar || y == null_lpvar || r == null_lpvar) + return l_undef; + + core& c = m_core; + + auto xval = c.val(x); + auto yval = c.val(y); + auto rval = c.val(r); + + lemmas.reset(); + + + if (xval != 0 && yval == 0 && rval != 1) { + new_lemma lemma(c, "x != 0 => x^0 = 1"); + lemma |= ineq(x, llc::EQ, rational::zero()); + lemma |= ineq(y, llc::NE, rational::zero()); + lemma |= ineq(r, llc::EQ, rational::one()); + return l_false; + } + + if (xval == 0 && yval > 0 && rval != 0) { + new_lemma lemma(c, "y != 0 => 0^y = 0"); + lemma |= ineq(x, llc::NE, rational::zero()); + lemma |= ineq(y, llc::EQ, rational::zero()); + lemma |= ineq(r, llc::EQ, rational::zero()); + return l_false; + } + + if (xval > 0 && rval < 0 && rval <= 0) { + new_lemma lemma(c, "x > 0 => x^y > 0"); + lemma |= ineq(x, llc::LE, rational::zero()); + lemma |= ineq(r, llc::GT, rational::zero()); + return l_false; + } + + if (xval > 1 && rval >= 1 && yval < 0) { + new_lemma lemma(c, "x > 1, y < 0 => x^y < 1"); + lemma |= ineq(x, llc::LE, rational::one()); + lemma |= ineq(y, llc::GE, rational::zero()); + lemma |= ineq(r, llc::LT, rational::one()); + return l_false; + } + + if (xval > 1 && rval <= 1 && yval > 0) { + new_lemma lemma(c, "x > 1, y > 0 => x^y > 1"); + lemma |= ineq(x, llc::LE, rational::one()); + lemma |= ineq(y, llc::LE, rational::zero()); + lemma |= ineq(r, llc::GT, rational::one()); + return l_false; + } + + if (xval >= 2 && yval != 0 & rval <= yval + 1) { + new_lemma lemma(c, "x >= 2, y != 0 => x^y > y + 1"); + lemma |= ineq(x, llc::LT, rational(2)); + lemma |= ineq(y, llc::EQ, rational::zero()); + lemma |= ineq(lp::lar_term(r, rational::minus_one(), y), llc::GT, rational::one()); + return l_false; + } + + if (xval > 0 && yval.is_unsigned()) { + auto r2val = power(xval, yval.get_unsigned()); + if (rval == r2val) + return l_true; + if (xval > 0 && r2val < rval) { + SASSERT(yval > 0); + new_lemma lemma(c, "x >= x0 > 0, y >= y0 > 0 => r >= x0^y0"); + lemma |= ineq(x, llc::LT, xval); + lemma |= ineq(y, llc::LT, yval); + lemma |= ineq(r, llc::GE, r2val); + return l_false; + } + if (xval > 0 && r2val < rval) { + new_lemma lemma(c, "x >= x0 > 0, y <= y0 => r <= x0^y0"); + lemma |= ineq(x, llc::LT, xval); + lemma |= ineq(y, llc::GT, yval); + lemma |= ineq(r, llc::LE, r2val); + return l_false; + } + } + if (xval > 0 && yval > 0 && !yval.is_int()) { + auto ynum = numerator(yval); + auto yden = denominator(yval); + if (!ynum.is_unsigned()) + return l_undef; + if (!yden.is_unsigned()) + return l_undef; + // r = x^{yn/yd} + // <=> + // r^yd = x^yn + auto ryd = power(rval, yden.get_unsigned()); + auto xyn = power(xval, ynum.get_unsigned()); + if (ryd == xyn) + return l_true; + } + + return l_undef; + + } + +} diff --git a/src/math/lp/nla_powers.h b/src/math/lp/nla_powers.h new file mode 100644 index 00000000000..f74417ae3ab --- /dev/null +++ b/src/math/lp/nla_powers.h @@ -0,0 +1,29 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + nla_powers.h + +Author: + Lev Nachmanson (levnach) + Nikolaj Bjorner (nbjorner) + +Description: + Refines bounds on powers. + +--*/ + +#include "math/lp/nla_types.h" + +namespace nla { + + class core; + + class powers { + core& m_core; + public: + powers(core& c):m_core(c) {} + lbool check(lpvar r, lpvar x, lpvar y, vector&); + }; +} diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index e0df39503c6..b5a030a92e3 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -77,104 +77,7 @@ namespace nla { // ensure r = x^y, add abstraction/refinement lemmas lbool solver::check_power(lpvar r, lpvar x, lpvar y, vector& lemmas) { - if (x == null_lpvar || y == null_lpvar || r == null_lpvar) - return l_undef; - - if (use_nra_model()) - return l_undef; - - auto xval = m_core->val(x); - auto yval = m_core->val(y); - auto rval = m_core->val(r); - - core& c = get_core(); - c.set_lemma_vec(lemmas); - lemmas.reset(); - - // x >= x0 > 0, y >= y0 > 0 => r >= x0^y0 - // x >= x0 > 0, y <= y0 => r <= x0^y0 - // x != 0, y = 0 => r = 1 - // x = 0, y != 0 => r = 0 - // - // for x fixed, it is exponentiation - // => use tangent lemmas and error tolerance. - - if (xval > 0 && yval.is_unsigned()) { - auto r2val = power(xval, yval.get_unsigned()); - if (rval == r2val) - return l_true; - if (xval != 0 && yval == 0) { - new_lemma lemma(c, "x != 0 => x^0 = 1"); - lemma |= ineq(x, llc::EQ, rational::zero()); - lemma |= ineq(y, llc::NE, rational::zero()); - lemma |= ineq(r, llc::EQ, rational::one()); - return l_false; - } - if (xval == 0 && yval > 0) { - new_lemma lemma(c, "y != 0 => 0^y = 0"); - lemma |= ineq(x, llc::NE, rational::zero()); - lemma |= ineq(y, llc::EQ, rational::zero()); - lemma |= ineq(r, llc::EQ, rational::zero()); - return l_false; - } - if (xval > 0 && r2val < rval) { - SASSERT(yval > 0); - new_lemma lemma(c, "x >= x0 > 0, y >= y0 > 0 => r >= x0^y0"); - lemma |= ineq(x, llc::LT, xval); - lemma |= ineq(y, llc::LT, yval); - lemma |= ineq(r, llc::GE, r2val); - return l_false; - } - if (xval > 0 && r2val < rval) { - new_lemma lemma(c, "x >= x0 > 0, y <= y0 => r <= x0^y0"); - lemma |= ineq(x, llc::LT, xval); - lemma |= ineq(y, llc::GT, yval); - lemma |= ineq(r, llc::LE, r2val); - return l_false; - } - } - if (xval > 0 && yval > 0 && !yval.is_int()) { - auto ynum = numerator(yval); - auto yden = denominator(yval); - if (!ynum.is_unsigned()) - return l_undef; - if (!yden.is_unsigned()) - return l_undef; - // r = x^{yn/yd} - // <=> - // r^yd = x^yn - auto ryd = power(rval, yden.get_unsigned()); - auto xyn = power(xval, ynum.get_unsigned()); - if (ryd == xyn) - return l_true; -#if 0 - // try some root approximation instead? - if (ryd > xyn) { - // todo - } - if (ryd < xyn) { - // todo - } -#endif - - } - - - return l_undef; - - // anum isn't initialized unless nra_solver is invoked. - // there is no proviso for using algebraic numbers outside of the nra solver. - // so either we have a rational refinement version _and_ an algebraic numeral refinement - // loop or we introduce algebraic numerals outside of the nra_solver - -#if 0 - scoped_anum xval(am()), yval(am()), rval(am()); - - am().set(xval, am_value(x)); - am().set(yval, am_value(y)); - am().set(rval, am_value(r)); -#endif - + return m_core->check_power(r, x, y, lemmas); } } diff --git a/src/math/lp/nla_types.h b/src/math/lp/nla_types.h new file mode 100644 index 00000000000..8169266ccf6 --- /dev/null +++ b/src/math/lp/nla_types.h @@ -0,0 +1,120 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + nla_types.h + +Author: + Lev Nachmanson (levnach) + Nikolaj Bjorner (nbjorner) + +Description: + Types used for nla solver. + +--*/ + +#pragma once + +namespace nla { + + typedef lp::constraint_index lpci; + typedef lp::lconstraint_kind llc; + typedef lp::constraint_index lpci; + typedef lp::explanation expl_set; + typedef lp::var_index lpvar; + const lpvar null_lpvar = UINT_MAX; + + inline int rat_sign(const rational& r) { return r.is_pos()? 1 : ( r.is_neg()? -1 : 0); } + inline rational rrat_sign(const rational& r) { return rational(rat_sign(r)); } + inline bool is_set(unsigned j) { return j != null_lpvar; } + inline bool is_even(unsigned k) { return (k & 1) == 0; } + class ineq { + lp::lconstraint_kind m_cmp; + lp::lar_term m_term; + rational m_rs; + public: + ineq(lp::lconstraint_kind cmp, const lp::lar_term& term, const rational& rs) : m_cmp(cmp), m_term(term), m_rs(rs) {} + ineq(const lp::lar_term& term, lp::lconstraint_kind cmp, int i) : m_cmp(cmp), m_term(term), m_rs(rational(i)) {} + ineq(const lp::lar_term& term, lp::lconstraint_kind cmp, const rational& rs) : m_cmp(cmp), m_term(term), m_rs(rs) {} + ineq(lpvar v, lp::lconstraint_kind cmp, int i): m_cmp(cmp), m_term(v), m_rs(rational(i)) {} + ineq(lpvar v, lp::lconstraint_kind cmp, rational const& r): m_cmp(cmp), m_term(v), m_rs(r) {} + bool operator==(const ineq& a) const { + return m_cmp == a.m_cmp && m_term == a.m_term && m_rs == a.m_rs; + } + const lp::lar_term& term() const { return m_term; }; + lp::lconstraint_kind cmp() const { return m_cmp; }; + const rational& rs() const { return m_rs; }; + }; + + class lemma { + vector m_ineqs; + lp::explanation m_expl; + public: + void push_back(const ineq& i) { m_ineqs.push_back(i);} + size_t size() const { return m_ineqs.size() + m_expl.size(); } + const vector& ineqs() const { return m_ineqs; } + vector& ineqs() { return m_ineqs; } + lp::explanation& expl() { return m_expl; } + const lp::explanation& expl() const { return m_expl; } + bool is_conflict() const { return m_ineqs.empty() && !m_expl.empty(); } + }; + + class core; + // + // lemmas are created in a scope. + // when the destructor of new_lemma is invoked + // all constraints are assumed added to the lemma + // correctness of the lemma can be checked at this point. + // + class new_lemma { + char const* name; + core& c; + lemma& current() const; + + public: + new_lemma(core& c, char const* name); + ~new_lemma(); + lemma& operator()() { return current(); } + std::ostream& display(std::ostream& out) const; + new_lemma& operator&=(lp::explanation const& e); + new_lemma& operator&=(const monic& m); + new_lemma& operator&=(const factor& f); + new_lemma& operator&=(const factorization& f); + new_lemma& operator&=(lpvar j); + new_lemma& operator|=(ineq const& i); + new_lemma& explain_fixed(lpvar j); + new_lemma& explain_equiv(lpvar u, lpvar v); + new_lemma& explain_var_separated_from_zero(lpvar j); + new_lemma& explain_existing_lower_bound(lpvar j); + new_lemma& explain_existing_upper_bound(lpvar j); + + lp::explanation& expl() { return current().expl(); } + + unsigned num_ineqs() const { return current().ineqs().size(); } + }; + + + inline std::ostream& operator<<(std::ostream& out, new_lemma const& l) { + return l.display(out); + } + + struct pp_fac { + core const& c; + factor const& f; + pp_fac(core const& c, factor const& f): c(c), f(f) {} + }; + + struct pp_var { + core const& c; + lpvar v; + pp_var(core const& c, lpvar v): c(c), v(v) {} + }; + + struct pp_factorization { + core const& c; + factorization const& f; + pp_factorization(core const& c, factorization const& f): c(c), f(f) {} + }; + +} From 8be43ca68b1b8de55a6b92692a4544d711489016 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2023 13:51:19 -0800 Subject: [PATCH 332/597] reshuffle pre-conditions for powers --- src/math/lp/nla_powers.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index ee3c41ee894..847ef7a84a3 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -83,6 +83,8 @@ namespace nla { return l_undef; core& c = m_core; + if (c.use_nra_model()) + return l_undef; auto xval = c.val(x); auto yval = c.val(y); @@ -90,7 +92,6 @@ namespace nla { lemmas.reset(); - if (xval != 0 && yval == 0 && rval != 1) { new_lemma lemma(c, "x != 0 => x^0 = 1"); lemma |= ineq(x, llc::EQ, rational::zero()); @@ -99,7 +100,7 @@ namespace nla { return l_false; } - if (xval == 0 && yval > 0 && rval != 0) { + if (xval == 0 && yval != 0 && rval != 0) { new_lemma lemma(c, "y != 0 => 0^y = 0"); lemma |= ineq(x, llc::NE, rational::zero()); lemma |= ineq(y, llc::EQ, rational::zero()); @@ -107,14 +108,14 @@ namespace nla { return l_false; } - if (xval > 0 && rval < 0 && rval <= 0) { + if (xval > 0 && rval <= 0) { new_lemma lemma(c, "x > 0 => x^y > 0"); lemma |= ineq(x, llc::LE, rational::zero()); lemma |= ineq(r, llc::GT, rational::zero()); return l_false; } - if (xval > 1 && rval >= 1 && yval < 0) { + if (xval > 1 && yval < 0 && rval >= 1) { new_lemma lemma(c, "x > 1, y < 0 => x^y < 1"); lemma |= ineq(x, llc::LE, rational::one()); lemma |= ineq(y, llc::GE, rational::zero()); @@ -122,7 +123,7 @@ namespace nla { return l_false; } - if (xval > 1 && rval <= 1 && yval > 0) { + if (xval > 1 && yval > 0 && rval <= 1) { new_lemma lemma(c, "x > 1, y > 0 => x^y > 1"); lemma |= ineq(x, llc::LE, rational::one()); lemma |= ineq(y, llc::LE, rational::zero()); From ae24b73b19efaa293c480cb202c48eeba13383c8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Jan 2023 21:19:45 -0800 Subject: [PATCH 333/597] bugfixes to incremental linearization for expanding power Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_powers.cpp | 12 ++++++------ src/smt/theory_lra.cpp | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index 847ef7a84a3..4054dd7c190 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -17,7 +17,7 @@ Module Name: Special cases: -1. Exponentiation. x is fixed numeral. +1. Exponentiation. x is fixed numeral a. TOCL18 axioms: a^y > 0 (if a > 0) @@ -29,13 +29,13 @@ TOCL18 axioms: Other special case: - y = 1 <= a^y = a + y = 1 <=> a^y = a TOCL18 approach: Polynomial abstractions Taylor: a^y = sum_i ln(a)*y^i/i! -Truncation: P(n, a) = sum_{i=0}^n ln(a)*y^i/i! +Truncation: P(n, a) = sum_{i=0}^n ln(a)*y^i/i! = 1 + ln(a)*y + ln(a)^2*y^2/2 + y = 0: handled by axiom a^y = 1 y < 0: P(2n-1, y) <= a^y <= P(2n, y), n > 0 because Taylor contribution is negative at odd powers. @@ -131,9 +131,9 @@ namespace nla { return l_false; } - if (xval >= 2 && yval != 0 & rval <= yval + 1) { - new_lemma lemma(c, "x >= 2, y != 0 => x^y > y + 1"); - lemma |= ineq(x, llc::LT, rational(2)); + if (xval >= 3 && yval != 0 & rval <= yval + 1) { + new_lemma lemma(c, "x >= 3, y != 0 => x^y > ln(x)y + 1"); + lemma |= ineq(x, llc::LT, rational(3)); lemma |= ineq(y, llc::EQ, rational::zero()); lemma |= ineq(lp::lar_term(r, rational::minus_one(), y), llc::GT, rational::one()); return l_false; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c5b897d781f..29f34655a89 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1115,7 +1115,8 @@ class theory_lra::imp { void mk_power_axiom(expr* p, expr* x, expr* y) { rational r; - if (a.is_extended_numeral(x, r) && r.is_unsigned() && r.is_pos()) { + // r > 0 => r^y > 0 + if (a.is_extended_numeral(x, r) && r > 0) { expr_ref zero(a.mk_real(0), m); mk_axiom(~mk_literal(a.mk_le(p, zero))); } From d4ca7e53747cb66e4720d49988dbc297e9bb2b35 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Jan 2023 21:39:52 -0800 Subject: [PATCH 334/597] #6555 --- src/ast/ast_smt2_pp.cpp | 8 ++++++++ src/ast/ast_smt2_pp.h | 1 + src/ast/converters/model_converter.cpp | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 927660fbe4c..74bb871ece4 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -1265,6 +1265,7 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen return out; } + std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd, bool reverse) { if (!f) return out << "null"; ast_manager & m = env.get_manager(); @@ -1276,6 +1277,13 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_e return out; } +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { + return ast_smt2_pp(out, f, e, env, p, indent, cmd, false); +} + +std::ostream & ast_smt2_pp_rev(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { + return ast_smt2_pp(out, f, e, env, p, indent, cmd, true); +} std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 2b0b2f37115..64ea2aec9bf 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -105,6 +105,7 @@ std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & e std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun"); std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun", bool reverse = false); +std::ostream & ast_smt2_pp_rev(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun"); std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref()); std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector> const& funs, smt2_pp_environment & env, params_ref const & p = params_ref()); diff --git a/src/ast/converters/model_converter.cpp b/src/ast/converters/model_converter.cpp index 1033cc57b21..716970cba2d 100644 --- a/src/ast/converters/model_converter.cpp +++ b/src/ast/converters/model_converter.cpp @@ -26,7 +26,7 @@ Module Name: void model_converter::display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e) { VERIFY(e); VERIFY(f->get_range() == e->get_sort()); - ast_smt2_pp(out, f, e, env, params_ref(), 0, "model-add", true) << "\n"; + ast_smt2_pp_rev(out, f, e, env, params_ref(), 0, "model-add") << "\n"; } void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const { From 0f3c56213ebffedd5cf52bc6099c14a3cd9d9039 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 27 Jan 2023 17:11:48 -0800 Subject: [PATCH 335/597] move dominator simplifier functionality to rewriter and simplifier, move bv_bounds simplifier functionality to simplifier --- src/ast/rewriter/CMakeLists.txt | 1 + src/ast/rewriter/bv_bounds_base.h | 566 +++++++++++++++++ src/ast/rewriter/dom_simplifier.cpp | 325 ++++++++++ src/ast/rewriter/dom_simplifier.h | 86 +++ src/ast/simplifiers/CMakeLists.txt | 2 + src/ast/simplifiers/bv_bounds_simplifier.cpp | 65 ++ src/ast/simplifiers/bv_bounds_simplifier.h | 18 + src/ast/simplifiers/dominator_simplifier.cpp | 303 +++++++++ src/ast/simplifiers/dominator_simplifier.h | 71 +++ src/tactic/bv/bv_bounds_tactic.cpp | 606 +----------------- src/tactic/core/CMakeLists.txt | 1 - src/tactic/core/dom_simplify_tactic.cpp | 613 ------------------- src/tactic/core/dom_simplify_tactic.h | 131 +--- 13 files changed, 1457 insertions(+), 1331 deletions(-) create mode 100644 src/ast/rewriter/bv_bounds_base.h create mode 100644 src/ast/rewriter/dom_simplifier.cpp create mode 100644 src/ast/rewriter/dom_simplifier.h create mode 100644 src/ast/simplifiers/bv_bounds_simplifier.cpp create mode 100644 src/ast/simplifiers/bv_bounds_simplifier.h create mode 100644 src/ast/simplifiers/dominator_simplifier.cpp create mode 100644 src/ast/simplifiers/dominator_simplifier.h delete mode 100644 src/tactic/core/dom_simplify_tactic.cpp diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index c785804c127..17410807a00 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -14,6 +14,7 @@ z3_add_component(rewriter der.cpp distribute_forall.cpp dl_rewriter.cpp + dom_simplifier.cpp elim_bounds.cpp enum2bv_rewriter.cpp expr_replacer.cpp diff --git a/src/ast/rewriter/bv_bounds_base.h b/src/ast/rewriter/bv_bounds_base.h new file mode 100644 index 00000000000..8ddb429e97c --- /dev/null +++ b/src/ast/rewriter/bv_bounds_base.h @@ -0,0 +1,566 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + bv_bounds_simplifier.h + +Abstract: + + Context dependent simplification for bit-vectors + +Author: + + Nikolaj and Nuno + +--*/ + +#pragma once + +namespace bv { + + template + struct interval_tpl : public Base { + T l, h; + unsigned sz = 0; + bool tight = true; + + interval_tpl(T const& l, T const& h, unsigned sz, bool tight = false): l(l), h(h), sz(sz), tight(tight) {} + interval_tpl() {} + + bool invariant() const { + return + 0 <= l && (l <= Base::bound(sz)) && + 0 <= h && (h <= Base::bound(sz)) && + (!is_wrapped() || l != h + 1); + } + + bool is_full() const { + return l == 0 && h == Base::bound(sz); + } + bool is_wrapped() const { return l > h; } + bool is_singleton() const { return l == h; } + + bool operator==(const interval_tpl& b) const { + SASSERT(sz == b.sz); + return l == b.l && h == b.h && tight == b.tight; + } + bool operator!=(const interval_tpl& b) const { return !(*this == b); } + + bool implies(const interval_tpl& b) const { + if (b.is_full()) + return true; + else if (is_full()) + return false; + else if (is_wrapped()) + // l >= b.l >= b.h >= h + return b.is_wrapped() && h <= b.h && l >= b.l; + else if (b.is_wrapped()) + // b.l > b.h >= h >= l + // h >= l >= b.l > b.h + return h <= b.h || l >= b.l; + else + return l >= b.l && h <= b.h; + } + + /// return false if intersection is unsat + bool intersect(const interval_tpl& b, interval_tpl& result) const { + if (is_full() || *this == b) { + result = b; + return true; + } + if (b.is_full()) { + result = *this; + return true; + } + + if (is_wrapped()) { + if (b.is_wrapped()) { + if (h >= b.l) + result = b; + else if (b.h >= l) + result = *this; + else + result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz); + } + else + return b.intersect(*this, result); + } + else if (b.is_wrapped()) { + // ... b.h ... l ... h ... b.l .. + if (h < b.l && l > b.h) + return false; + // ... l ... b.l ... h ... + if (h >= b.l && l <= b.h) + result = b; + else if (h >= b.l) + result = interval_tpl(b.l, h, sz); + else { + // ... l .. b.h .. h .. b.l ... + SASSERT(l <= b.h); + result = interval_tpl(l, std::min(h, b.h), sz); + } + } else { + if (l > b.h || h < b.l) + return false; + + // 0 .. l.. l' ... h ... h' + result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); + } + return true; + } + + /// return false if negation is empty + bool negate(interval_tpl& result) const { + if (!tight) + result = interval_tpl(Base::zero(), Base::bound(sz), sz, true); + else if (is_full()) + return false; + else if (l == 0 && Base::bound(sz) == h) + result = interval_tpl(Base::zero(), Base::bound(sz), sz); + else if (l == 0) + result = interval_tpl(h + 1, Base::bound(sz), sz); + else if (Base::bound(sz) == h) + result = interval_tpl(Base::zero(), l - 1, sz); + else + result = interval_tpl(h + 1, l - 1, sz); + return true; + } + + + }; + + struct rinterval_base { + static rational bound(unsigned sz) { + return rational::power_of_two(sz) - 1; + } + + static rational zero() { return rational::zero(); } + }; + + struct rinterval : public interval_tpl { + rinterval(rational const& l, rational const& h, unsigned sz, bool tight = false) { + this->l = l; this->h = h; this->sz = sz; this->tight = tight; + } + rinterval() { l = 0; h = 0; tight = true; } + }; + + struct iinterval_base { + static uint64_t uMaxInt(unsigned sz) { + SASSERT(sz <= 64); + return ULLONG_MAX >> (64u - sz); + } + + static uint64_t bound(unsigned sz) { return uMaxInt(sz); } + static uint64_t zero() { return 0; } + }; + + struct iinterval : public interval_tpl { + iinterval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) { + this->l = l; this->h = h; this->sz = sz; this->tight = tight; + } + iinterval() { l = 0; h = 0; sz = 0; tight = true; } + }; + + struct interval { + bool is_small = true; + iinterval i; + rinterval r; + + interval() {} + + interval(rational const& l, rational const& h, unsigned sz, bool tight = false) { + if (sz <= 64) { + is_small = true; + i.l = l.get_uint64(); + i.h = h.get_uint64(); + i.tight = tight; + i.sz = sz; + } + else { + is_small = false; + r.l = l; + r.h = h; + r.tight = tight; + r.sz = sz; + } + } + + unsigned size() const { + return is_small ? i.sz : r.sz; + } + + bool negate(interval& result) const { + result.is_small = is_small; + if (is_small) + return i.negate(result.i); + else + return r.negate(result.r); + } + + bool intersect(interval const& b, interval & result) const { + result.is_small = is_small; + SASSERT(b.is_small == is_small); + if (is_small) + return i.intersect(b.i, result.i); + else + return r.intersect(b.r, result.r); + } + + bool operator==(interval const& other) const { + SASSERT(is_small == other.is_small); + return is_small ? i == other.i : r == other.r; + } + + bool operator!=(interval const& other) const { + return !(*this == other); + } + + bool is_singleton() const { return is_small ? i.is_singleton() : r.is_singleton(); } + + bool is_full() const { return is_small ? i.is_full() : r.is_full(); } + + bool tight() const { return is_small ? i.tight : r.tight; } + + bool implies(const interval& b) const { + SASSERT(is_small == b.is_small); + return is_small ? i.implies(b.i) : r.implies(b.r); + } + + rational lo() const { return is_small ? rational(i.l, rational::ui64()) : r.l; } + rational hi() const { return is_small ? rational(i.h, rational::ui64()) : r.h; } + }; + + + inline std::ostream& operator<<(std::ostream& o, const interval& I) { + if (I.is_small) + return o << "[" << I.i.l << ", " << I.i.h << "]"; + else + return o << "[" << I.r.l << ", " << I.r.h << "]"; + } + + struct undo_bound { + expr* e = nullptr; + interval b; + bool fresh = false; + undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} + }; + + struct bv_bounds_base { + typedef obj_map map; + typedef obj_map expr_set; + typedef obj_map expr_cnt; + + ast_manager& m; + bv_util m_bv; + vector m_scopes; + svector m_expr_vars; + svector m_bound_exprs; + map m_bound; + bool m_propagate_eq = false; + ptr_vector m_args; + + bv_bounds_base(ast_manager& m):m(m), m_bv(m) {} + + virtual ~bv_bounds_base() { + for (auto* e : m_expr_vars) + dealloc(e); + for (auto* b : m_bound_exprs) + dealloc(b); + } + + bool is_bound(expr *e, expr*& v, interval& b) const { + rational r; + expr *lhs = nullptr, *rhs = nullptr; + unsigned sz; + + if (m_bv.is_bv_ule(e, lhs, rhs)) { + if (m_bv.is_numeral(lhs, r, sz)) { // C ule x <=> x uge C + if (m_bv.is_numeral(rhs)) + return false; + b = interval(r, rational::power_of_two(sz) - 1, sz, true); + v = rhs; + return true; + } + if (m_bv.is_numeral(rhs, r, sz)) { // x ule C + b = interval(rational::zero(), r, sz, true); + v = lhs; + return true; + } + // TBD: x + s <= x + q + // x + s <= x + // x <= x + q + } + else if (m_bv.is_bv_sle(e, lhs, rhs)) { + if (m_bv.is_numeral(lhs, r, sz)) { // C sle x <=> x sge C + if (m_bv.is_numeral(rhs)) + return false; + b = interval(r, rational::power_of_two(sz-1) - 1, sz, true); + v = rhs; + return true; + } + if (m_bv.is_numeral(rhs, r, sz)) { // x sle C + b = interval(rational::power_of_two(sz-1), r, sz, true); + v = lhs; + return true; + } + // TBD: other cases for forbidden intervals + } + else if (m.is_eq(e, lhs, rhs)) { + if (m_bv.is_numeral(rhs)) + std::swap(lhs, rhs); + if (m_bv.is_numeral(rhs)) + return false; + if (m_bv.is_numeral(lhs, r, sz)) { + unsigned lo, hi; + expr* rhs2; + if (m_bv.is_extract(rhs, lo, hi, rhs2) && r == 0) { + unsigned sz2 = m_bv.get_bv_size(rhs2); + if (sz2 - 1 == hi) { + b = interval(rational::zero(), rational::power_of_two(lo) - 1, sz2, false); + v = rhs2; + return true; + } + } + b = interval(r, r, sz, true); + v = rhs; + return true; + } + } + return false; + } + + bool assert_expr_core(expr * t, bool sign) { + while (m.is_not(t, t)) + sign = !sign; + + interval b; + expr* t1; + if (is_bound(t, t1, b)) { + SASSERT(m_bv.get_bv_size(t1) == b.size()); + SASSERT(!m_bv.is_numeral(t1)); + if (sign && !b.negate(b)) + return false; + + TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); + map::obj_map_entry* e = m_bound.find_core(t1); + if (e) { + interval& old = e->get_data().m_value; + interval intr; + if (!old.intersect(b, intr)) + return false; + if (old == intr) + return true; + m_scopes.push_back(undo_bound(t1, old, false)); + old = intr; + SASSERT(old.size() == m_bv.get_bv_size(t1)); + } + else { + SASSERT(b.size() == m_bv.get_bv_size(t1)); + m_bound.insert(t1, b); + m_scopes.push_back(undo_bound(t1, interval(), true)); + } + } + return true; + } + + // + // x + q <= s <=> x not in [s - q + 1, -q[ + // <=> x in [-q, s - q], s != -1 + // + // x in [lo, hi] + // q = -lo + // hi = s + lo => s = hi - lo + // hi - lo != -1 + // + + expr_ref mk_bound(expr* t, rational const& lo, rational const& hi) { + sort* s = t->get_sort(); + + if (lo == hi + 1) + return expr_ref(m.mk_true(), m); + else + return expr_ref(m_bv.mk_ule(m_bv.mk_bv_add(t, m_bv.mk_numeral(-lo, s)), m_bv.mk_numeral(hi - lo, s)), m); + } + + // + // use interval information to rewrite sub-terms x to (0 ++ x[hi:0]) + // in other words, identify leading 0s. + // + bool zero_patch(expr* t, expr_ref& result) { + if (!is_app(t)) + return false; + + if (m_bv.is_extract(t)) + return false; + + m_args.reset(); + bool simplified = false; + interval b; + for (expr* arg : *to_app(t)) { + if (!m_bv.is_bv(arg)) { + m_args.push_back(arg); + continue; + } + if (!m_bv.is_extract(arg) && m_bound.find(arg, b)) { + unsigned num_bits = b.hi().get_num_bits(); + unsigned bv_size = m_bv.get_bv_size(arg); + if (0 < num_bits && num_bits < bv_size) { + m_args.push_back(m_bv.mk_concat(m_bv.mk_zero(bv_size - num_bits), + m_bv.mk_extract(num_bits - 1, 0, arg))); + simplified = true; + } + else + m_args.push_back(arg); + } + else + m_args.push_back(arg); + } + + if (simplified) { + result = m.mk_app(to_app(t)->get_decl(), m_args); + return true; + } + + return false; + } + + bool simplify_core(expr* t, expr_ref& result) { + expr* t1; + interval b; + + if (m_bound.find(t, b) && b.is_singleton()) { + result = m_bv.mk_numeral(b.lo(), m_bv.get_bv_size(t)); + return true; + } + + if (zero_patch(t, result)) + return result; + + if (!m.is_bool(t)) + return false; + + bool sign = false; + while (m.is_not(t, t)) + sign = !sign; + + if (!is_bound(t, t1, b)) + return false; + + if (sign && b.tight()) { + sign = false; + if (!b.negate(b)) { + result = m.mk_false(); + return true; + } + } + + interval ctx, intr; + result = nullptr; + + if (b.is_full() && b.tight()) + result = m.mk_true(); + else if (!m_bound.find(t1, ctx)) { + } + else if (ctx.implies(b)) + result = m.mk_true(); + else if (!b.intersect(ctx, intr)) + result = m.mk_false(); + else if (m_propagate_eq && intr.is_singleton()) + result = m.mk_eq(t1, m_bv.mk_numeral(intr.lo(), t1->get_sort())); + else if (false && intr != b) + result = mk_bound(t1, intr.lo(), intr.hi()); + else { + TRACE("bv", tout << mk_pp(t, m) << " b: " << b << " ctx: " << ctx << " intr " << intr << "\n"); + } + + CTRACE("bv", result, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); + if (sign && result) + result = m.mk_not(result); + return result != nullptr; + } + + // check if t contains v + ptr_vector todo; + bool contains(expr* t, expr* v) { + ast_fast_mark1 mark; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (mark.is_marked(t)) + continue; + if (t == v) { + todo.reset(); + return true; + } + mark.mark(t); + + if (!is_app(t)) + continue; + app* a = to_app(t); + todo.append(a->get_num_args(), a->get_args()); + } + return false; + } + + bool contains_bound(expr* t) { + ast_fast_mark1 mark1; + ast_fast_mark2 mark2; + + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (mark1.is_marked(t)) { + continue; + } + mark1.mark(t); + + if (!is_app(t)) { + continue; + } + interval b; + expr* e; + if (is_bound(t, e, b)) { + if (mark2.is_marked(e)) { + todo.reset(); + return true; + } + mark2.mark(e); + if (m_bound.contains(e)) { + todo.reset(); + return true; + } + } + + app* a = to_app(t); + todo.append(a->get_num_args(), a->get_args()); + } + return false; + } + + void pop_core(unsigned num_scopes) { + TRACE("bv", tout << "pop: " << num_scopes << "\n";); + if (m_scopes.empty()) + return; + unsigned target = m_scopes.size() - num_scopes; + if (target == 0) { + m_bound.reset(); + m_scopes.reset(); + return; + } + for (unsigned i = m_scopes.size(); i-- > target; ) { + undo_bound& undo = m_scopes[i]; + SASSERT(m_bound.contains(undo.e)); + if (undo.fresh) + m_bound.erase(undo.e); + else + m_bound.insert(undo.e, undo.b); + } + m_scopes.shrink(target); + } + + }; + +} diff --git a/src/ast/rewriter/dom_simplifier.cpp b/src/ast/rewriter/dom_simplifier.cpp new file mode 100644 index 00000000000..205c81dbb45 --- /dev/null +++ b/src/ast/rewriter/dom_simplifier.cpp @@ -0,0 +1,325 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + dom_simplifier.cpp + +Abstract: + + Dominator-based context simplifer. + +Author: + + Nikolaj and Nuno + + +--*/ + + +#include "ast/ast_util.h" +#include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" +#include "ast/rewriter/dom_simplifier.h" + +/** + \brief compute a post-order traversal for e. + Also populate the set of parents +*/ +void expr_dominators::compute_post_order() { + unsigned post_num = 0; + SASSERT(m_post2expr.empty()); + SASSERT(m_expr2post.empty()); + ast_mark mark; + ptr_vector todo; + todo.push_back(m_root); + while (!todo.empty()) { + expr* e = todo.back(); + if (mark.is_marked(e)) { + todo.pop_back(); + continue; + } + if (is_app(e)) { + app* a = to_app(e); + bool done = true; + for (expr* arg : *a) { + if (!mark.is_marked(arg)) { + todo.push_back(arg); + done = false; + } + } + if (done) { + mark.mark(e, true); + m_expr2post.insert(e, post_num++); + m_post2expr.push_back(e); + todo.pop_back(); + for (expr* arg : *a) { + add_edge(m_parents, arg, a); + } + } + } + else { + mark.mark(e, true); + todo.pop_back(); + } + } +} + +expr* expr_dominators::intersect(expr* x, expr * y) { + unsigned n1 = m_expr2post[x]; + unsigned n2 = m_expr2post[y]; + while (n1 != n2) { + if (n1 < n2) { + x = m_doms[x]; + n1 = m_expr2post[x]; + } + else if (n1 > n2) { + y = m_doms[y]; + n2 = m_expr2post[y]; + } + } + SASSERT(x == y); + return x; +} + +bool expr_dominators::compute_dominators() { + expr * e = m_root; + SASSERT(m_doms.empty()); + m_doms.insert(e, e); + bool change = true; + unsigned iterations = 1; + while (change) { + change = false; + TRACE("simplify", + for (auto & kv : m_doms) { + tout << mk_bounded_pp(kv.m_key, m) << " |-> " << mk_bounded_pp(kv.m_value, m) << "\n"; + }); + + SASSERT(m_post2expr.empty() || m_post2expr.back() == e); + for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) { + expr * child = m_post2expr[i]; + ptr_vector const& p = m_parents[child]; + expr * new_idom = nullptr, *idom2 = nullptr; + + for (expr * pred : p) { + if (m_doms.contains(pred)) { + new_idom = !new_idom ? pred : intersect(new_idom, pred); + } + } + if (!new_idom) { + m_doms.insert(child, p[0]); + change = true; + } + else if (!m_doms.find(child, idom2) || idom2 != new_idom) { + m_doms.insert(child, new_idom); + change = true; + } + } + iterations *= 2; + if (change && iterations > m_post2expr.size()) { + return false; + } + } + return true; +} + +void expr_dominators::extract_tree() { + for (auto const& kv : m_doms) { + add_edge(m_tree, kv.m_value, kv.m_key); + } +} + +bool expr_dominators::compile(expr * e) { + reset(); + m_root = e; + compute_post_order(); + if (!compute_dominators()) return false; + extract_tree(); + TRACE("simplify", display(tout);); + return true; +} + +bool expr_dominators::compile(unsigned sz, expr * const* es) { + expr_ref e(m.mk_and(sz, es), m); + return compile(e); +} + +void expr_dominators::reset() { + m_expr2post.reset(); + m_post2expr.reset(); + m_parents.reset(); + m_doms.reset(); + m_tree.reset(); + m_root.reset(); +} + +std::ostream& expr_dominators::display(std::ostream& out) { + return display(out, 0, m_root); +} + +std::ostream& expr_dominators::display(std::ostream& out, unsigned indent, expr* r) { + for (unsigned i = 0; i < indent; ++i) out << " "; + out << r->get_id() << ": " << mk_bounded_pp(r, m, 1) << "\n"; + if (m_tree.contains(r)) { + for (expr* child : m_tree[r]) { + if (child != r) + display(out, indent + 1, child); + } + } + return out; +} + + +// --------------------- +// expr_substitution_simplifier +namespace { + +class expr_substitution_simplifier : public dom_simplifier { + ast_manager& m; + expr_substitution m_subst; + scoped_expr_substitution m_scoped_substitution; + obj_map m_expr2depth; + expr_ref_vector m_trail; + + // move from asserted_formulas to here.. + void compute_depth(expr* e) { + ptr_vector todo; + todo.push_back(e); + while (!todo.empty()) { + e = todo.back(); + unsigned d = 0; + if (m_expr2depth.contains(e)) { + todo.pop_back(); + continue; + } + if (is_app(e)) { + app* a = to_app(e); + bool visited = true; + for (expr* arg : *a) { + unsigned d1 = 0; + if (m_expr2depth.find(arg, d1)) { + d = std::max(d, d1); + } + else { + visited = false; + todo.push_back(arg); + } + } + if (!visited) { + continue; + } + } + todo.pop_back(); + m_expr2depth.insert(e, d + 1); + } + } + + bool is_gt(expr* lhs, expr* rhs) { + if (lhs == rhs) { + return false; + } + if (m.is_value(rhs)) { + return true; + } + SASSERT(is_ground(lhs) && is_ground(rhs)); + if (depth(lhs) > depth(rhs)) { + return true; + } + if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) { + app* l = to_app(lhs); + app* r = to_app(rhs); + if (l->get_decl()->get_id() != r->get_decl()->get_id()) { + return l->get_decl()->get_id() > r->get_decl()->get_id(); + } + if (l->get_num_args() != r->get_num_args()) { + return l->get_num_args() > r->get_num_args(); + } + for (unsigned i = 0; i < l->get_num_args(); ++i) { + if (l->get_arg(i) != r->get_arg(i)) { + return is_gt(l->get_arg(i), r->get_arg(i)); + } + } + UNREACHABLE(); + } + + return false; + } + + unsigned depth(expr* e) { return m_expr2depth[e]; } + +public: + expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {} + + void updt_params(params_ref const & p) override {} + + void collect_param_descrs(param_descrs& r) override {} + + bool assert_expr(expr * t, bool sign) override { + expr* tt; + if (m.is_not(t, tt)) + return assert_expr(tt, !sign); + if (m.is_false(t)) + return sign; + if (m.is_true(t)) + return !sign; + + TRACE("simplify", tout << t->get_id() << ": " << mk_bounded_pp(t, m) << " " << (sign?" - neg":" - pos") << "\n";); + + m_scoped_substitution.push(); + if (!sign) { + update_substitution(t, nullptr); + } + else { + expr_ref nt(m.mk_not(t), m); + update_substitution(nt, nullptr); + } + return true; + } + + void update_substitution(expr* n, proof* pr) { + expr* lhs, *rhs, *n1; + if (is_ground(n) && m.is_eq(n, lhs, rhs)) { + compute_depth(lhs); + compute_depth(rhs); + m_trail.push_back(lhs); + m_trail.push_back(rhs); + if (is_gt(lhs, rhs)) { + TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";); + m_scoped_substitution.insert(lhs, rhs, pr); + return; + } + if (is_gt(rhs, lhs)) { + TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); + m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr)); + return; + } + TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); + } + if (m.is_not(n, n1)) { + m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); + } + else { + m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); + } + } + + void operator()(expr_ref& r) override { r = m_scoped_substitution.find(r); } + + void pop(unsigned num_scopes) override { m_scoped_substitution.pop(num_scopes); } + + unsigned scope_level() const override { return m_scoped_substitution.scope_level(); } + + dom_simplifier * translate(ast_manager & m) override { + SASSERT(m_subst.empty()); + return alloc(expr_substitution_simplifier, m); + } +}; +} + + +dom_simplifier* mk_expr_substitution_simplifier(ast_manager& m) { + return alloc(expr_substitution_simplifier, m); +} + + + diff --git a/src/ast/rewriter/dom_simplifier.h b/src/ast/rewriter/dom_simplifier.h new file mode 100644 index 00000000000..4d9c63c1e36 --- /dev/null +++ b/src/ast/rewriter/dom_simplifier.h @@ -0,0 +1,86 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + dom_simplifier.h + +Abstract: + + Dominator-based context simplifer. + +Author: + + Nikolaj and Nuno + +--*/ + +#pragma once + +#include "ast/ast.h" +#include "ast/expr_substitution.h" +#include "util/obj_pair_hashtable.h" + +class expr_dominators { +public: + typedef obj_map> tree_t; +private: + ast_manager& m; + expr_ref m_root; + obj_map m_expr2post; // reverse post-order number + ptr_vector m_post2expr; + tree_t m_parents; + obj_map m_doms; + tree_t m_tree; + + void add_edge(tree_t& tree, expr * src, expr* dst) { + tree.insert_if_not_there(src, ptr_vector()).push_back(dst); + } + + void compute_post_order(); + expr* intersect(expr* x, expr * y); + bool compute_dominators(); + void extract_tree(); + + std::ostream& display(std::ostream& out, unsigned indent, expr* r); + +public: + expr_dominators(ast_manager& m): m(m), m_root(m) {} + + bool compile(expr * e); + bool compile(unsigned sz, expr * const* es); + tree_t const& get_tree() { return m_tree; } + void reset(); + expr* idom(expr *e) const { return m_doms[e]; } + + std::ostream& display(std::ostream& out); +}; + +class dom_simplifier { +public: + virtual ~dom_simplifier() = default; + /** + \brief assert_expr performs an implicit push + */ + virtual bool assert_expr(expr * t, bool sign) = 0; + + /** + \brief apply simplification. + */ + virtual void operator()(expr_ref& r) = 0; + + /** + \brief pop scopes accumulated from assertions. + */ + virtual void pop(unsigned num_scopes) = 0; + + virtual dom_simplifier * translate(ast_manager & m) = 0; + + virtual unsigned scope_level() const = 0; + + virtual void updt_params(params_ref const & p) = 0; + + virtual void collect_param_descrs(param_descrs& r) = 0; +}; + +dom_simplifier* mk_expr_substitution_simplifier(ast_manager& m); diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index cd1b34f8c78..826717c5a0c 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -4,10 +4,12 @@ z3_add_component(simplifiers bound_manager.cpp bound_propagator.cpp bound_simplifier.cpp + bv_bounds_simplifier.cpp bv_slice.cpp card2bv.cpp demodulator_simplifier.cpp dependent_expr_state.cpp + dominator_simplifier.cpp distribute_forall.cpp elim_unconstrained.cpp eliminate_predicates.cpp diff --git a/src/ast/simplifiers/bv_bounds_simplifier.cpp b/src/ast/simplifiers/bv_bounds_simplifier.cpp new file mode 100644 index 00000000000..72010c507dc --- /dev/null +++ b/src/ast/simplifiers/bv_bounds_simplifier.cpp @@ -0,0 +1,65 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bv_bounds_simplifier.h + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-27 + +--*/ + +#include "ast/simplifiers/bv_bounds_simplifier.h" +#include "ast/simplifiers/dominator_simplifier.h" +#include "ast/rewriter/bv_bounds_base.h" +#include "ast/rewriter/dom_simplifier.h" + + +class dom_bv_bounds_simplifier : public dom_simplifier, public bv::bv_bounds_base { + params_ref m_params; + +public: + dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv_bounds_base(m), m_params(p) { + updt_params(p); + } + + ~dom_bv_bounds_simplifier() override { + } + + void updt_params(params_ref const & p) override { + m_propagate_eq = p.get_bool("propagate_eq", false); + } + + void collect_param_descrs(param_descrs& r) override { + r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); + } + + bool assert_expr(expr * t, bool sign) override { + return assert_expr_core(t, sign); + } + + void operator()(expr_ref& r) override { + expr_ref result(m); + simplify_core(r, result); + if (result) + r = result; + } + + void pop(unsigned num_scopes) override { + pop_core(num_scopes); + } + + dom_simplifier * translate(ast_manager & m) override { + return alloc(dom_bv_bounds_simplifier, m, m_params); + } + + unsigned scope_level() const override { + return m_scopes.size(); + } +}; + +dependent_expr_simplifier* mk_bv_bounds_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& s) { + return alloc(dominator_simplifier, m, s, alloc(dom_bv_bounds_simplifier, m, p), p); +} diff --git a/src/ast/simplifiers/bv_bounds_simplifier.h b/src/ast/simplifiers/bv_bounds_simplifier.h new file mode 100644 index 00000000000..ed2955bbaed --- /dev/null +++ b/src/ast/simplifiers/bv_bounds_simplifier.h @@ -0,0 +1,18 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + bv_bounds_simplifier.h + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-27 + +--*/ + +#pragma once + +#include "ast/simplifiers/dependent_expr_state.h" + +dependent_expr_simplifier * mk_bv_bounds_simplifier(ast_manager & m, params_ref const & p, dependent_expr_state& fmls); diff --git a/src/ast/simplifiers/dominator_simplifier.cpp b/src/ast/simplifiers/dominator_simplifier.cpp new file mode 100644 index 00000000000..12f2e29417c --- /dev/null +++ b/src/ast/simplifiers/dominator_simplifier.cpp @@ -0,0 +1,303 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + dominator_simplifier.cpp + +Abstract: + + Dominator-based context simplifer. + +Author: + + Nikolaj and Nuno + +--*/ + +#include "ast/ast_util.h" +#include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" +#include "ast/simplifiers/dominator_simplifier.h" + +dominator_simplifier::~dominator_simplifier() { + dealloc(m_simplifier); +} + +expr_ref dominator_simplifier::simplify_ite(app * ite) { + expr_ref r(m); + expr * c = nullptr, *t = nullptr, *e = nullptr; + VERIFY(m.is_ite(ite, c, t, e)); + unsigned old_lvl = scope_level(); + expr_ref new_c = simplify_arg(c); + if (m.is_true(new_c)) { + r = simplify_arg(t); + } + else if (!assert_expr(new_c, false)) { + r = simplify_arg(e); + } + else { + for (expr * child : tree(ite)) + if (is_subexpr(child, t) && !is_subexpr(child, e)) + simplify_rec(child); + + pop(scope_level() - old_lvl); + expr_ref new_t = simplify_arg(t); + reset_cache(); + if (!assert_expr(new_c, true)) { + return new_t; + } + for (expr * child : tree(ite)) + if (is_subexpr(child, e) && !is_subexpr(child, t)) + simplify_rec(child); + pop(scope_level() - old_lvl); + expr_ref new_e = simplify_arg(e); + + if (c == new_c && t == new_t && e == new_e) { + r = ite; + } + else if (new_t == new_e) { + r = new_t; + } + else { + TRACE("simplify", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); + r = m.mk_ite(new_c, new_t, new_e); + } + } + reset_cache(); + return r; +} + + +expr_ref dominator_simplifier::simplify_arg(expr * e) { + expr_ref r(m); + r = get_cached(e); + (*m_simplifier)(r); + CTRACE("simplify", e != r, tout << "depth: " << m_depth << " " << mk_pp(e, m) << " -> " << r << "\n";); + return r; +} + +/** + \brief simplify e recursively. +*/ +expr_ref dominator_simplifier::simplify_rec(expr * e0) { + expr_ref r(m); + expr* e = nullptr; + + if (!m_result.find(e0, e)) { + e = e0; + } + + ++m_depth; + if (m_depth > m_max_depth) { + r = e; + } + else if (m.is_ite(e)) { + r = simplify_ite(to_app(e)); + } + else if (m.is_and(e)) { + r = simplify_and(to_app(e)); + } + else if (m.is_or(e)) { + r = simplify_or(to_app(e)); + } + else if (m.is_not(e)) { + r = simplify_not(to_app(e)); + } + else { + for (expr * child : tree(e)) { + if (child != e) + simplify_rec(child); + } + if (is_app(e)) { + m_args.reset(); + for (expr* arg : *to_app(e)) { + // we don't have a way to distinguish between e.g. + // ite(c, f(c), foo) (which should go to ite(c, f(true), foo)) + // from and(or(x, y), f(x)), where we do a "trial" with x=false + // Trials are good for boolean formula simplification but not sound + // for fn applications. + m_args.push_back(m.is_bool(arg) ? arg : simplify_arg(arg)); + } + r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.data()); + } + else { + r = e; + } + } + CTRACE("simplify", e0 != r, tout << "depth before: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); + (*m_simplifier)(r); + cache(e0, r); + CTRACE("simplify", e0 != r, tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); + --m_depth; + m_subexpr_cache.reset(); + return r; +} + +expr_ref dominator_simplifier::simplify_and_or(bool is_and, app * e) { + expr_ref r(m); + unsigned old_lvl = scope_level(); + + auto is_subexpr_arg = [&](expr * child, expr * except) { + if (!is_subexpr(child, except)) + return false; + for (expr * arg : *e) { + if (arg != except && is_subexpr(child, arg)) + return false; + } + return true; + }; + + expr_ref_vector args(m); + + auto simp_arg = [&](expr* arg) { + for (expr * child : tree(arg)) { + if (is_subexpr_arg(child, arg)) { + simplify_rec(child); + } + } + r = simplify_arg(arg); + args.push_back(r); + if (!assert_expr(r, !is_and)) { + pop(scope_level() - old_lvl); + r = is_and ? m.mk_false() : m.mk_true(); + reset_cache(); + return true; + } + return false; + }; + + if (m_forward) { + for (expr * arg : *e) { + if (simp_arg(arg)) + return r; + } + } + else { + for (unsigned i = e->get_num_args(); i-- > 0; ) { + if (simp_arg(e->get_arg(i))) + return r; + } + args.reverse(); + } + + pop(scope_level() - old_lvl); + reset_cache(); + return { is_and ? mk_and(args) : mk_or(args), m }; +} + +expr_ref dominator_simplifier::simplify_not(app * e) { + expr *ee; + ENSURE(m.is_not(e, ee)); + unsigned old_lvl = scope_level(); + expr_ref t = simplify_rec(ee); + pop(scope_level() - old_lvl); + reset_cache(); + return mk_not(t); +} + + + +bool dominator_simplifier::init() { + expr_ref_vector args(m); + for (auto i : indices()) + if (!m_fmls[i].dep()) + args.push_back(m_fmls[i].fml()); + expr_ref fml = mk_and(args); + m_result.reset(); + m_trail.reset(); + return m_dominators.compile(fml); +} + + +void dominator_simplifier::reduce() { + + m_trail.reset(); + m_args.reset(); + m_result.reset(); + m_dominators.reset(); + + SASSERT(scope_level() == 0); + bool change = true; + unsigned n = 0; + m_depth = 0; + while (change && n < 10) { + change = false; + ++n; + + // go forwards + m_forward = true; + if (!init()) return; + for (unsigned i = qhead(); i < qtail() && !m_fmls.inconsistent(); ++i) { + auto [f, p, d] = m_fmls[i](); + if (d) + continue; + + expr_ref r = simplify_rec(f); + if (!m.is_true(r) && !m.is_false(r) && !p && !assert_expr(r, false)) + r = m.mk_false(); + + CTRACE("simplify", r != f, tout << r << " " << mk_pp(f, m) << "\n";); + change |= r != f; + proof_ref new_pr(m); + if (p) { + new_pr = m.mk_modus_ponens(p, m.mk_rewrite(f, r)); + } + m_fmls.update(i, dependent_expr(m, r, new_pr, d)); + } + pop(scope_level()); + + // go backwards + m_forward = false; + if (!init()) return; + for (unsigned i = qtail(); i-- > qhead() && !m_fmls.inconsistent(); ) { + + auto [f, p, d] = m_fmls[i](); + if (d) + continue; + expr_ref r = simplify_rec(f); + if (!m.is_true(r) && !m.is_false(r) && !p && !assert_expr(r, false)) + r = m.mk_false(); + + change |= r != f; + CTRACE("simplify", r != f, tout << r << " " << mk_pp(f, m) << "\n";); + proof_ref new_pr(m); + if (r) { + new_pr = m.mk_rewrite(f, r); + new_pr = m.mk_modus_ponens(p, new_pr); + } + m_fmls.update(i, dependent_expr(m, r, new_pr, d)); + } + pop(scope_level()); + } + SASSERT(scope_level() == 0); +} + +/** + \brief determine if a is dominated by b. + Walk the immediate dominators of a upwards until hitting b or a term that is deeper than b. + Save intermediary results in a cache to avoid recomputations. +*/ + +bool dominator_simplifier::is_subexpr(expr * a, expr * b) { + if (a == b) + return true; + + bool r; + if (m_subexpr_cache.find(a, b, r)) + return r; + + if (get_depth(a) >= get_depth(b)) { + return false; + } + SASSERT(a != idom(a) && get_depth(idom(a)) > get_depth(a)); + r = is_subexpr(idom(a), b); + m_subexpr_cache.insert(a, b, r); + return r; +} + +ptr_vector const & dominator_simplifier::tree(expr * e) { + if (auto p = m_dominators.get_tree().find_core(e)) + return p->get_data().get_value(); + return m_empty; +} diff --git a/src/ast/simplifiers/dominator_simplifier.h b/src/ast/simplifiers/dominator_simplifier.h new file mode 100644 index 00000000000..562aeace1e5 --- /dev/null +++ b/src/ast/simplifiers/dominator_simplifier.h @@ -0,0 +1,71 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + dom_simplifier.h + +--*/ + +#pragma once +#include "ast/ast.h" +#include "ast/expr_substitution.h" +#include "ast/rewriter/dom_simplifier.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "util/obj_pair_hashtable.h" + +class dominator_simplifier : public dependent_expr_simplifier { + + ast_manager& m; + dom_simplifier* m_simplifier; + params_ref m_params; + expr_ref_vector m_trail, m_args; + obj_map m_result; + expr_dominators m_dominators; + unsigned m_depth; + unsigned m_max_depth; + ptr_vector m_empty; + obj_pair_map m_subexpr_cache; + bool m_forward; + + expr_ref simplify_rec(expr* t); + expr_ref simplify_arg(expr* t); + expr_ref simplify_ite(app * ite); + expr_ref simplify_and(app * e) { return simplify_and_or(true, e); } + expr_ref simplify_or(app * e) { return simplify_and_or(false, e); } + expr_ref simplify_and_or(bool is_and, app * e); + expr_ref simplify_not(app * e); + + bool init(); + + bool is_subexpr(expr * a, expr * b); + + expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } + void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } + void reset_cache() { m_result.reset(); } + + ptr_vector const & tree(expr * e); + expr* idom(expr *e) const { return m_dominators.idom(e); } + + unsigned scope_level() { return m_simplifier->scope_level(); } + void pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); } + bool assert_expr(expr* f, bool sign) { return m_simplifier->assert_expr(f, sign); } + + +public: + dominator_simplifier(ast_manager & m, dependent_expr_state& st, dom_simplifier* s, params_ref const & p = params_ref()): + dependent_expr_simplifier(m, st), + m(m), m_simplifier(s), m_params(p), + m_trail(m), m_args(m), + m_dominators(m), m_depth(0), m_max_depth(1024), m_forward(true) {} + + ~dominator_simplifier() override; + + char const* name() const override { return "dom-simplify"; } + + void reduce() override; + + void updt_params(params_ref const & p) override { m_simplifier->updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_simplifier->collect_param_descrs(r); } +}; + diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 2e0ce0391ae..45ff60ec092 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -18,564 +18,26 @@ Module Name: --*/ -#include "tactic/bv/bv_bounds_tactic.h" -#include "tactic/core/ctx_simplify_tactic.h" -#include "tactic/core/dom_simplify_tactic.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" +#include "ast/simplifiers/dominator_simplifier.h" +#include "tactic/bv/bv_bounds_tactic.h" +#include "tactic/core/ctx_simplify_tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/rewriter/bv_bounds_base.h" +#include "ast/simplifiers/bv_bounds_simplifier.h" + #include -static uint64_t uMaxInt(unsigned sz) { - SASSERT(sz <= 64); - return ULLONG_MAX >> (64u - sz); -} namespace { - template - struct interval_tpl : public Base { - T l, h; - unsigned sz = 0; - bool tight = true; - - interval_tpl(T const& l, T const& h, unsigned sz, bool tight = false): l(l), h(h), sz(sz), tight(tight) {} - interval_tpl() {} - - bool invariant() const { - return - 0 <= l && (l <= Base::bound(sz)) && - 0 <= h && (h <= Base::bound(sz)) && - (!is_wrapped() || l != h + 1); - } - - bool is_full() const { - return l == 0 && h == Base::bound(sz); - } - bool is_wrapped() const { return l > h; } - bool is_singleton() const { return l == h; } - - bool operator==(const interval_tpl& b) const { - SASSERT(sz == b.sz); - return l == b.l && h == b.h && tight == b.tight; - } - bool operator!=(const interval_tpl& b) const { return !(*this == b); } - - bool implies(const interval_tpl& b) const { - if (b.is_full()) - return true; - else if (is_full()) - return false; - else if (is_wrapped()) - // l >= b.l >= b.h >= h - return b.is_wrapped() && h <= b.h && l >= b.l; - else if (b.is_wrapped()) - // b.l > b.h >= h >= l - // h >= l >= b.l > b.h - return h <= b.h || l >= b.l; - else - return l >= b.l && h <= b.h; - } - - /// return false if intersection is unsat - bool intersect(const interval_tpl& b, interval_tpl& result) const { - if (is_full() || *this == b) { - result = b; - return true; - } - if (b.is_full()) { - result = *this; - return true; - } - - if (is_wrapped()) { - if (b.is_wrapped()) { - if (h >= b.l) - result = b; - else if (b.h >= l) - result = *this; - else - result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz); - } - else - return b.intersect(*this, result); - } - else if (b.is_wrapped()) { - // ... b.h ... l ... h ... b.l .. - if (h < b.l && l > b.h) - return false; - // ... l ... b.l ... h ... - if (h >= b.l && l <= b.h) - result = b; - else if (h >= b.l) - result = interval_tpl(b.l, h, sz); - else { - // ... l .. b.h .. h .. b.l ... - SASSERT(l <= b.h); - result = interval_tpl(l, std::min(h, b.h), sz); - } - } else { - if (l > b.h || h < b.l) - return false; - - // 0 .. l.. l' ... h ... h' - result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); - } - return true; - } - - /// return false if negation is empty - bool negate(interval_tpl& result) const { - if (!tight) - result = interval_tpl(Base::zero(), Base::bound(sz), sz, true); - else if (is_full()) - return false; - else if (l == 0 && Base::bound(sz) == h) - result = interval_tpl(Base::zero(), Base::bound(sz), sz); - else if (l == 0) - result = interval_tpl(h + 1, Base::bound(sz), sz); - else if (Base::bound(sz) == h) - result = interval_tpl(Base::zero(), l - 1, sz); - else - result = interval_tpl(h + 1, l - 1, sz); - return true; - } - - - }; - - struct rinterval_base { - static rational bound(unsigned sz) { - return rational::power_of_two(sz) - 1; - } - - static rational zero() { return rational::zero(); } - }; - - struct rinterval : public interval_tpl { - rinterval(rational const& l, rational const& h, unsigned sz, bool tight = false) { - this->l = l; this->h = h; this->sz = sz; this->tight = tight; - } - rinterval() { l = 0; h = 0; tight = true; } - }; - - struct iinterval_base { - static uint64_t bound(unsigned sz) { return uMaxInt(sz); } - static uint64_t zero() { return 0; } - }; - - struct iinterval : public interval_tpl { - iinterval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) { - this->l = l; this->h = h; this->sz = sz; this->tight = tight; - } - iinterval() { l = 0; h = 0; sz = 0; tight = true; } - }; - - struct interval { - bool is_small = true; - iinterval i; - rinterval r; - - interval() {} - - interval(rational const& l, rational const& h, unsigned sz, bool tight = false) { - if (sz <= 64) { - is_small = true; - i.l = l.get_uint64(); - i.h = h.get_uint64(); - i.tight = tight; - i.sz = sz; - } - else { - is_small = false; - r.l = l; - r.h = h; - r.tight = tight; - r.sz = sz; - } - } - - unsigned size() const { - return is_small ? i.sz : r.sz; - } - - bool negate(interval& result) const { - result.is_small = is_small; - if (is_small) - return i.negate(result.i); - else - return r.negate(result.r); - } - - bool intersect(interval const& b, interval & result) const { - result.is_small = is_small; - SASSERT(b.is_small == is_small); - if (is_small) - return i.intersect(b.i, result.i); - else - return r.intersect(b.r, result.r); - } - - bool operator==(interval const& other) const { - SASSERT(is_small == other.is_small); - return is_small ? i == other.i : r == other.r; - } - - bool operator!=(interval const& other) const { - return !(*this == other); - } - - bool is_singleton() const { return is_small ? i.is_singleton() : r.is_singleton(); } - - bool is_full() const { return is_small ? i.is_full() : r.is_full(); } - - bool tight() const { return is_small ? i.tight : r.tight; } - - bool implies(const interval& b) const { - SASSERT(is_small == b.is_small); - return is_small ? i.implies(b.i) : r.implies(b.r); - } - - rational lo() const { return is_small ? rational(i.l, rational::ui64()) : r.l; } - rational hi() const { return is_small ? rational(i.h, rational::ui64()) : r.h; } - }; - - std::ostream& operator<<(std::ostream& o, const interval& I) { - if (I.is_small) - return o << "[" << I.i.l << ", " << I.i.h << "]"; - else - return o << "[" << I.r.l << ", " << I.r.h << "]"; - } - - struct undo_bound { - expr* e = nullptr; - interval b; - bool fresh = false; - undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} - }; - - struct bv_bounds_base { - typedef obj_map map; - typedef obj_map expr_set; - typedef obj_map expr_cnt; - - ast_manager& m; - bv_util m_bv; - vector m_scopes; - svector m_expr_vars; - svector m_bound_exprs; - map m_bound; - bool m_propagate_eq = false; - ptr_vector m_args; - - bv_bounds_base(ast_manager& m):m(m), m_bv(m) {} - - virtual ~bv_bounds_base() { - for (auto* e : m_expr_vars) - dealloc(e); - for (auto* b : m_bound_exprs) - dealloc(b); - } - - bool is_bound(expr *e, expr*& v, interval& b) const { - rational r; - expr *lhs = nullptr, *rhs = nullptr; - unsigned sz; - - if (m_bv.is_bv_ule(e, lhs, rhs)) { - if (m_bv.is_numeral(lhs, r, sz)) { // C ule x <=> x uge C - if (m_bv.is_numeral(rhs)) - return false; - b = interval(r, rational::power_of_two(sz) - 1, sz, true); - v = rhs; - return true; - } - if (m_bv.is_numeral(rhs, r, sz)) { // x ule C - b = interval(rational::zero(), r, sz, true); - v = lhs; - return true; - } - // TBD: x + s <= x + q - // x + s <= x - // x <= x + q - } - else if (m_bv.is_bv_sle(e, lhs, rhs)) { - if (m_bv.is_numeral(lhs, r, sz)) { // C sle x <=> x sge C - if (m_bv.is_numeral(rhs)) - return false; - b = interval(r, rational::power_of_two(sz-1) - 1, sz, true); - v = rhs; - return true; - } - if (m_bv.is_numeral(rhs, r, sz)) { // x sle C - b = interval(rational::power_of_two(sz-1), r, sz, true); - v = lhs; - return true; - } - // TBD: other cases for forbidden intervals - } - else if (m.is_eq(e, lhs, rhs)) { - if (m_bv.is_numeral(rhs)) - std::swap(lhs, rhs); - if (m_bv.is_numeral(rhs)) - return false; - if (m_bv.is_numeral(lhs, r, sz)) { - unsigned lo, hi; - expr* rhs2; - if (m_bv.is_extract(rhs, lo, hi, rhs2) && r == 0) { - unsigned sz2 = m_bv.get_bv_size(rhs2); - if (sz2 - 1 == hi) { - b = interval(rational::zero(), rational::power_of_two(lo) - 1, sz2, false); - v = rhs2; - return true; - } - } - b = interval(r, r, sz, true); - v = rhs; - return true; - } - } - return false; - } - - bool assert_expr_core(expr * t, bool sign) { - while (m.is_not(t, t)) - sign = !sign; - - interval b; - expr* t1; - if (is_bound(t, t1, b)) { - SASSERT(m_bv.get_bv_size(t1) == b.size()); - SASSERT(!m_bv.is_numeral(t1)); - if (sign && !b.negate(b)) - return false; - - TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); - map::obj_map_entry* e = m_bound.find_core(t1); - if (e) { - interval& old = e->get_data().m_value; - interval intr; - if (!old.intersect(b, intr)) - return false; - if (old == intr) - return true; - m_scopes.push_back(undo_bound(t1, old, false)); - old = intr; - SASSERT(old.size() == m_bv.get_bv_size(t1)); - } - else { - SASSERT(b.size() == m_bv.get_bv_size(t1)); - m_bound.insert(t1, b); - m_scopes.push_back(undo_bound(t1, interval(), true)); - } - } - return true; - } - - // - // x + q <= s <=> x not in [s - q + 1, -q[ - // <=> x in [-q, s - q], s != -1 - // - // x in [lo, hi] - // q = -lo - // hi = s + lo => s = hi - lo - // hi - lo != -1 - // - - expr_ref mk_bound(expr* t, rational const& lo, rational const& hi) { - sort* s = t->get_sort(); - - if (lo == hi + 1) - return expr_ref(m.mk_true(), m); - else - return expr_ref(m_bv.mk_ule(m_bv.mk_bv_add(t, m_bv.mk_numeral(-lo, s)), m_bv.mk_numeral(hi - lo, s)), m); - } - - // - // use interval information to rewrite sub-terms x to (0 ++ x[hi:0]) - // in other words, identify leading 0s. - // - bool zero_patch(expr* t, expr_ref& result) { - if (!is_app(t)) - return false; - - if (m_bv.is_extract(t)) - return false; - - m_args.reset(); - bool simplified = false; - interval b; - for (expr* arg : *to_app(t)) { - if (!m_bv.is_bv(arg)) { - m_args.push_back(arg); - continue; - } - if (!m_bv.is_extract(arg) && m_bound.find(arg, b)) { - unsigned num_bits = b.hi().get_num_bits(); - unsigned bv_size = m_bv.get_bv_size(arg); - if (0 < num_bits && num_bits < bv_size) { - m_args.push_back(m_bv.mk_concat(m_bv.mk_zero(bv_size - num_bits), - m_bv.mk_extract(num_bits - 1, 0, arg))); - simplified = true; - } - else - m_args.push_back(arg); - } - else - m_args.push_back(arg); - } - - if (simplified) { - result = m.mk_app(to_app(t)->get_decl(), m_args); - return true; - } - - return false; - } - - bool simplify_core(expr* t, expr_ref& result) { - expr* t1; - interval b; - - if (m_bound.find(t, b) && b.is_singleton()) { - result = m_bv.mk_numeral(b.lo(), m_bv.get_bv_size(t)); - return true; - } - - if (zero_patch(t, result)) - return result; - - if (!m.is_bool(t)) - return false; - - bool sign = false; - while (m.is_not(t, t)) - sign = !sign; - - if (!is_bound(t, t1, b)) - return false; - - if (sign && b.tight()) { - sign = false; - if (!b.negate(b)) { - result = m.mk_false(); - return true; - } - } - - interval ctx, intr; - result = nullptr; - - if (b.is_full() && b.tight()) - result = m.mk_true(); - else if (!m_bound.find(t1, ctx)) { - } - else if (ctx.implies(b)) - result = m.mk_true(); - else if (!b.intersect(ctx, intr)) - result = m.mk_false(); - else if (m_propagate_eq && intr.is_singleton()) - result = m.mk_eq(t1, m_bv.mk_numeral(intr.lo(), t1->get_sort())); - else if (false && intr != b) - result = mk_bound(t1, intr.lo(), intr.hi()); - else { - TRACE("bv", tout << mk_pp(t, m) << " b: " << b << " ctx: " << ctx << " intr " << intr << "\n"); - } - - CTRACE("bv", result, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); - if (sign && result) - result = m.mk_not(result); - return result != nullptr; - } - - // check if t contains v - ptr_vector todo; - bool contains(expr* t, expr* v) { - ast_fast_mark1 mark; - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - todo.pop_back(); - if (mark.is_marked(t)) - continue; - if (t == v) { - todo.reset(); - return true; - } - mark.mark(t); - - if (!is_app(t)) - continue; - app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); - } - return false; - } - - bool contains_bound(expr* t) { - ast_fast_mark1 mark1; - ast_fast_mark2 mark2; - - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - todo.pop_back(); - if (mark1.is_marked(t)) { - continue; - } - mark1.mark(t); - - if (!is_app(t)) { - continue; - } - interval b; - expr* e; - if (is_bound(t, e, b)) { - if (mark2.is_marked(e)) { - todo.reset(); - return true; - } - mark2.mark(e); - if (m_bound.contains(e)) { - todo.reset(); - return true; - } - } - - app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); - } - return false; - } - - void pop_core(unsigned num_scopes) { - TRACE("bv", tout << "pop: " << num_scopes << "\n";); - if (m_scopes.empty()) - return; - unsigned target = m_scopes.size() - num_scopes; - if (target == 0) { - m_bound.reset(); - m_scopes.reset(); - return; - } - for (unsigned i = m_scopes.size(); i-- > target; ) { - undo_bound& undo = m_scopes[i]; - SASSERT(m_bound.contains(undo.e)); - if (undo.fresh) - m_bound.erase(undo.e); - else - m_bound.insert(undo.e, undo.b); - } - m_scopes.shrink(target); - } - - }; - - class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier, public bv_bounds_base { + class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier, public bv::bv_bounds_base { params_ref m_params; public: - bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv_bounds_base(m), m_params(p) { + bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv::bv_bounds_base(m), m_params(p) { updt_params(p); } @@ -608,7 +70,7 @@ namespace { return true; expr* t1; - interval b; + bv::interval b; // skip common case: single bound constraint without any context for simplification if (is_bound(t, t1, b)) return b.is_full() || m_bound.contains(t1); @@ -629,56 +91,12 @@ namespace { } }; - - class dom_bv_bounds_simplifier : public dom_simplifier, public bv_bounds_base { - params_ref m_params; - - public: - dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv_bounds_base(m), m_params(p) { - updt_params(p); - } - - ~dom_bv_bounds_simplifier() override { - } - - void updt_params(params_ref const & p) override { - m_propagate_eq = p.get_bool("propagate_eq", false); - } - - void collect_param_descrs(param_descrs& r) override { - r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); - } - - bool assert_expr(expr * t, bool sign) override { - return assert_expr_core(t, sign); - } - - void operator()(expr_ref& r) override { - expr_ref result(m); - simplify_core(r, result); - if (result) - r = result; - } - - void pop(unsigned num_scopes) override { - pop_core(num_scopes); - } - - dom_simplifier * translate(ast_manager & m) override { - return alloc(dom_bv_bounds_simplifier, m, m_params); - } - - unsigned scope_level() const override { - return m_scopes.size(); - } - }; - } tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(ctx_simplify_tactic, m, alloc(bv_bounds_simplifier, m, p), p)); } -tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(dom_simplify_tactic, m, alloc(dom_bv_bounds_simplifier, m, p), p)); +tactic* mk_dom_bv_bounds_tactic(ast_manager& m, params_ref const& p) { + return alloc(dependent_expr_state_tactic, m, p, mk_bv_bounds_simplifier); } diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index d4a115dcc24..0827c12fbd0 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -6,7 +6,6 @@ z3_add_component(core_tactics collect_statistics_tactic.cpp ctx_simplify_tactic.cpp der_tactic.cpp - dom_simplify_tactic.cpp elim_term_ite_tactic.cpp elim_uncnstr_tactic.cpp euf_completion_tactic.cpp diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp deleted file mode 100644 index 82fb7ec4452..00000000000 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ /dev/null @@ -1,613 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - dom_simplify_tactic.cpp - -Abstract: - - Dominator-based context simplifer. - -Author: - - Nikolaj and Nuno - -Notes: - ---*/ - - -#include "ast/ast_util.h" -#include "ast/ast_pp.h" -#include "ast/ast_ll_pp.h" -#include "tactic/core/dom_simplify_tactic.h" - -/** - \brief compute a post-order traversal for e. - Also populate the set of parents -*/ -void expr_dominators::compute_post_order() { - unsigned post_num = 0; - SASSERT(m_post2expr.empty()); - SASSERT(m_expr2post.empty()); - ast_mark mark; - ptr_vector todo; - todo.push_back(m_root); - while (!todo.empty()) { - expr* e = todo.back(); - if (mark.is_marked(e)) { - todo.pop_back(); - continue; - } - if (is_app(e)) { - app* a = to_app(e); - bool done = true; - for (expr* arg : *a) { - if (!mark.is_marked(arg)) { - todo.push_back(arg); - done = false; - } - } - if (done) { - mark.mark(e, true); - m_expr2post.insert(e, post_num++); - m_post2expr.push_back(e); - todo.pop_back(); - for (expr* arg : *a) { - add_edge(m_parents, arg, a); - } - } - } - else { - mark.mark(e, true); - todo.pop_back(); - } - } -} - -expr* expr_dominators::intersect(expr* x, expr * y) { - unsigned n1 = m_expr2post[x]; - unsigned n2 = m_expr2post[y]; - while (n1 != n2) { - if (n1 < n2) { - x = m_doms[x]; - n1 = m_expr2post[x]; - } - else if (n1 > n2) { - y = m_doms[y]; - n2 = m_expr2post[y]; - } - } - SASSERT(x == y); - return x; -} - -bool expr_dominators::compute_dominators() { - expr * e = m_root; - SASSERT(m_doms.empty()); - m_doms.insert(e, e); - bool change = true; - unsigned iterations = 1; - while (change) { - change = false; - TRACE("simplify", - for (auto & kv : m_doms) { - tout << mk_bounded_pp(kv.m_key, m) << " |-> " << mk_bounded_pp(kv.m_value, m) << "\n"; - }); - - SASSERT(m_post2expr.empty() || m_post2expr.back() == e); - for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) { - expr * child = m_post2expr[i]; - ptr_vector const& p = m_parents[child]; - expr * new_idom = nullptr, *idom2 = nullptr; - - for (expr * pred : p) { - if (m_doms.contains(pred)) { - new_idom = !new_idom ? pred : intersect(new_idom, pred); - } - } - if (!new_idom) { - m_doms.insert(child, p[0]); - change = true; - } - else if (!m_doms.find(child, idom2) || idom2 != new_idom) { - m_doms.insert(child, new_idom); - change = true; - } - } - iterations *= 2; - if (change && iterations > m_post2expr.size()) { - return false; - } - } - return true; -} - -void expr_dominators::extract_tree() { - for (auto const& kv : m_doms) { - add_edge(m_tree, kv.m_value, kv.m_key); - } -} - -bool expr_dominators::compile(expr * e) { - reset(); - m_root = e; - compute_post_order(); - if (!compute_dominators()) return false; - extract_tree(); - TRACE("simplify", display(tout);); - return true; -} - -bool expr_dominators::compile(unsigned sz, expr * const* es) { - expr_ref e(m.mk_and(sz, es), m); - return compile(e); -} - -void expr_dominators::reset() { - m_expr2post.reset(); - m_post2expr.reset(); - m_parents.reset(); - m_doms.reset(); - m_tree.reset(); - m_root.reset(); -} - -std::ostream& expr_dominators::display(std::ostream& out) { - return display(out, 0, m_root); -} - -std::ostream& expr_dominators::display(std::ostream& out, unsigned indent, expr* r) { - for (unsigned i = 0; i < indent; ++i) out << " "; - out << r->get_id() << ": " << mk_bounded_pp(r, m, 1) << "\n"; - if (m_tree.contains(r)) { - for (expr* child : m_tree[r]) { - if (child != r) - display(out, indent + 1, child); - } - } - return out; -} - - -// ----------------------- -// dom_simplify_tactic - -dom_simplify_tactic::~dom_simplify_tactic() { - dealloc(m_simplifier); -} - -tactic * dom_simplify_tactic::translate(ast_manager & m) { - return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params); -} - - -void dom_simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result) { - tactic_report report("dom-simplify", *in.get()); - simplify_goal(*(in.get())); - in->inc_depth(); - result.push_back(in.get()); -} - -void dom_simplify_tactic::cleanup() { - m_trail.reset(); - m_args.reset(); - m_result.reset(); - m_dominators.reset(); -} - -expr_ref dom_simplify_tactic::simplify_ite(app * ite) { - expr_ref r(m); - expr * c = nullptr, *t = nullptr, *e = nullptr; - VERIFY(m.is_ite(ite, c, t, e)); - unsigned old_lvl = scope_level(); - expr_ref new_c = simplify_arg(c); - if (m.is_true(new_c)) { - r = simplify_arg(t); - } - else if (!assert_expr(new_c, false)) { - r = simplify_arg(e); - } - else { - for (expr * child : tree(ite)) - if (is_subexpr(child, t) && !is_subexpr(child, e)) - simplify_rec(child); - - pop(scope_level() - old_lvl); - expr_ref new_t = simplify_arg(t); - reset_cache(); - if (!assert_expr(new_c, true)) { - return new_t; - } - for (expr * child : tree(ite)) - if (is_subexpr(child, e) && !is_subexpr(child, t)) - simplify_rec(child); - pop(scope_level() - old_lvl); - expr_ref new_e = simplify_arg(e); - - if (c == new_c && t == new_t && e == new_e) { - r = ite; - } - else if (new_t == new_e) { - r = new_t; - } - else { - TRACE("simplify", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); - r = m.mk_ite(new_c, new_t, new_e); - } - } - reset_cache(); - return r; -} - -expr_ref dom_simplify_tactic::simplify_arg(expr * e) { - expr_ref r(m); - r = get_cached(e); - (*m_simplifier)(r); - CTRACE("simplify", e != r, tout << "depth: " << m_depth << " " << mk_pp(e, m) << " -> " << r << "\n";); - return r; -} - -/** - \brief simplify e recursively. -*/ -expr_ref dom_simplify_tactic::simplify_rec(expr * e0) { - expr_ref r(m); - expr* e = nullptr; - - if (!m_result.find(e0, e)) { - e = e0; - } - - ++m_depth; - if (m_depth > m_max_depth) { - r = e; - } - else if (m.is_ite(e)) { - r = simplify_ite(to_app(e)); - } - else if (m.is_and(e)) { - r = simplify_and(to_app(e)); - } - else if (m.is_or(e)) { - r = simplify_or(to_app(e)); - } - else if (m.is_not(e)) { - r = simplify_not(to_app(e)); - } - else { - for (expr * child : tree(e)) { - if (child != e) - simplify_rec(child); - } - if (is_app(e)) { - m_args.reset(); - for (expr* arg : *to_app(e)) { - // we don't have a way to distinguish between e.g. - // ite(c, f(c), foo) (which should go to ite(c, f(true), foo)) - // from and(or(x, y), f(x)), where we do a "trial" with x=false - // Trials are good for boolean formula simplification but not sound - // for fn applications. - m_args.push_back(m.is_bool(arg) ? arg : simplify_arg(arg)); - } - r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.data()); - } - else { - r = e; - } - } - CTRACE("simplify", e0 != r, tout << "depth before: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); - (*m_simplifier)(r); - cache(e0, r); - CTRACE("simplify", e0 != r, tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); - --m_depth; - m_subexpr_cache.reset(); - return r; -} - -expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { - expr_ref r(m); - unsigned old_lvl = scope_level(); - - auto is_subexpr_arg = [&](expr * child, expr * except) { - if (!is_subexpr(child, except)) - return false; - for (expr * arg : *e) { - if (arg != except && is_subexpr(child, arg)) - return false; - } - return true; - }; - - expr_ref_vector args(m); - - auto simp_arg = [&](expr* arg) { - for (expr * child : tree(arg)) { - if (is_subexpr_arg(child, arg)) { - simplify_rec(child); - } - } - r = simplify_arg(arg); - args.push_back(r); - if (!assert_expr(r, !is_and)) { - pop(scope_level() - old_lvl); - r = is_and ? m.mk_false() : m.mk_true(); - reset_cache(); - return true; - } - return false; - }; - - if (m_forward) { - for (expr * arg : *e) { - if (simp_arg(arg)) - return r; - } - } - else { - for (unsigned i = e->get_num_args(); i-- > 0; ) { - if (simp_arg(e->get_arg(i))) - return r; - } - args.reverse(); - } - - pop(scope_level() - old_lvl); - reset_cache(); - return { is_and ? mk_and(args) : mk_or(args), m }; -} - -expr_ref dom_simplify_tactic::simplify_not(app * e) { - expr *ee; - ENSURE(m.is_not(e, ee)); - unsigned old_lvl = scope_level(); - expr_ref t = simplify_rec(ee); - pop(scope_level() - old_lvl); - reset_cache(); - return mk_not(t); -} - - -bool dom_simplify_tactic::init(goal& g) { - expr_ref_vector args(m); - unsigned sz = g.size(); - for (unsigned i = 0; i < sz; ++i) args.push_back(g.form(i)); - expr_ref fml = mk_and(args); - m_result.reset(); - m_trail.reset(); - return m_dominators.compile(fml); -} - -void dom_simplify_tactic::simplify_goal(goal& g) { - - SASSERT(scope_level() == 0); - bool change = true; - unsigned n = 0; - m_depth = 0; - while (change && n < 10) { - change = false; - ++n; - - // go forwards - m_forward = true; - if (!init(g)) return; - unsigned sz = g.size(); - for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { - expr_ref r = simplify_rec(g.form(i)); - if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { - r = m.mk_false(); - } - CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); - change |= r != g.form(i); - proof_ref new_pr(m); - if (g.proofs_enabled() && g.pr(i)) { - new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(g.form(i), r)); - } - g.update(i, r, new_pr, g.dep(i)); - } - pop(scope_level()); - - // go backwards - m_forward = false; - if (!init(g)) return; - sz = g.size(); - for (unsigned i = sz; !g.inconsistent() && i > 0; ) { - --i; - expr_ref r = simplify_rec(g.form(i)); - if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { - r = m.mk_false(); - } - change |= r != g.form(i); - CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); - proof_ref new_pr(m); - if (g.proofs_enabled() && g.pr(i)) { - new_pr = m.mk_rewrite(g.form(i), r); - new_pr = m.mk_modus_ponens(g.pr(i), new_pr); - } - g.update(i, r, new_pr, g.dep(i)); - } - pop(scope_level()); - } - SASSERT(scope_level() == 0); -} - -/** - \brief determine if a is dominated by b. - Walk the immediate dominators of a upwards until hitting b or a term that is deeper than b. - Save intermediary results in a cache to avoid recomputations. -*/ - -bool dom_simplify_tactic::is_subexpr(expr * a, expr * b) { - if (a == b) - return true; - - bool r; - if (m_subexpr_cache.find(a, b, r)) - return r; - - if (get_depth(a) >= get_depth(b)) { - return false; - } - SASSERT(a != idom(a) && get_depth(idom(a)) > get_depth(a)); - r = is_subexpr(idom(a), b); - m_subexpr_cache.insert(a, b, r); - return r; -} - -ptr_vector const & dom_simplify_tactic::tree(expr * e) { - if (auto p = m_dominators.get_tree().find_core(e)) - return p->get_data().get_value(); - return m_empty; -} - - -// --------------------- -// expr_substitution_simplifier -namespace { - -class expr_substitution_simplifier : public dom_simplifier { - ast_manager& m; - expr_substitution m_subst; - scoped_expr_substitution m_scoped_substitution; - obj_map m_expr2depth; - expr_ref_vector m_trail; - - // move from asserted_formulas to here.. - void compute_depth(expr* e) { - ptr_vector todo; - todo.push_back(e); - while (!todo.empty()) { - e = todo.back(); - unsigned d = 0; - if (m_expr2depth.contains(e)) { - todo.pop_back(); - continue; - } - if (is_app(e)) { - app* a = to_app(e); - bool visited = true; - for (expr* arg : *a) { - unsigned d1 = 0; - if (m_expr2depth.find(arg, d1)) { - d = std::max(d, d1); - } - else { - visited = false; - todo.push_back(arg); - } - } - if (!visited) { - continue; - } - } - todo.pop_back(); - m_expr2depth.insert(e, d + 1); - } - } - - bool is_gt(expr* lhs, expr* rhs) { - if (lhs == rhs) { - return false; - } - if (m.is_value(rhs)) { - return true; - } - SASSERT(is_ground(lhs) && is_ground(rhs)); - if (depth(lhs) > depth(rhs)) { - return true; - } - if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) { - app* l = to_app(lhs); - app* r = to_app(rhs); - if (l->get_decl()->get_id() != r->get_decl()->get_id()) { - return l->get_decl()->get_id() > r->get_decl()->get_id(); - } - if (l->get_num_args() != r->get_num_args()) { - return l->get_num_args() > r->get_num_args(); - } - for (unsigned i = 0; i < l->get_num_args(); ++i) { - if (l->get_arg(i) != r->get_arg(i)) { - return is_gt(l->get_arg(i), r->get_arg(i)); - } - } - UNREACHABLE(); - } - - return false; - } - - unsigned depth(expr* e) { return m_expr2depth[e]; } - -public: - expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {} - - void updt_params(params_ref const & p) override {} - - void collect_param_descrs(param_descrs& r) override {} - - bool assert_expr(expr * t, bool sign) override { - expr* tt; - if (m.is_not(t, tt)) - return assert_expr(tt, !sign); - if (m.is_false(t)) - return sign; - if (m.is_true(t)) - return !sign; - - TRACE("simplify", tout << t->get_id() << ": " << mk_bounded_pp(t, m) << " " << (sign?" - neg":" - pos") << "\n";); - - m_scoped_substitution.push(); - if (!sign) { - update_substitution(t, nullptr); - } - else { - expr_ref nt(m.mk_not(t), m); - update_substitution(nt, nullptr); - } - return true; - } - - void update_substitution(expr* n, proof* pr) { - expr* lhs, *rhs, *n1; - if (is_ground(n) && m.is_eq(n, lhs, rhs)) { - compute_depth(lhs); - compute_depth(rhs); - m_trail.push_back(lhs); - m_trail.push_back(rhs); - if (is_gt(lhs, rhs)) { - TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";); - m_scoped_substitution.insert(lhs, rhs, pr); - return; - } - if (is_gt(rhs, lhs)) { - TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); - m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr)); - return; - } - TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); - } - if (m.is_not(n, n1)) { - m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); - } - else { - m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); - } - } - - void operator()(expr_ref& r) override { r = m_scoped_substitution.find(r); } - - void pop(unsigned num_scopes) override { m_scoped_substitution.pop(num_scopes); } - - unsigned scope_level() const override { return m_scoped_substitution.scope_level(); } - - dom_simplifier * translate(ast_manager & m) override { - SASSERT(m_subst.empty()); - return alloc(expr_substitution_simplifier, m); - } -}; -} - -tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(dom_simplify_tactic, m, alloc(expr_substitution_simplifier, m), p)); -} diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 771c8c65499..2dba378b7c1 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -44,130 +44,15 @@ tree are visited. Since the paths selected by the dominator trees are limited, t #include "ast/ast.h" #include "ast/expr_substitution.h" +#include "ast/rewriter/dom_simplifier.h" #include "tactic/tactic.h" -#include "tactic/tactical.h" -#include "util/obj_pair_hashtable.h" - - -class expr_dominators { -public: - typedef obj_map> tree_t; -private: - ast_manager& m; - expr_ref m_root; - obj_map m_expr2post; // reverse post-order number - ptr_vector m_post2expr; - tree_t m_parents; - obj_map m_doms; - tree_t m_tree; - - void add_edge(tree_t& tree, expr * src, expr* dst) { - tree.insert_if_not_there(src, ptr_vector()).push_back(dst); - } - - void compute_post_order(); - expr* intersect(expr* x, expr * y); - bool compute_dominators(); - void extract_tree(); - - std::ostream& display(std::ostream& out, unsigned indent, expr* r); - -public: - expr_dominators(ast_manager& m): m(m), m_root(m) {} - - bool compile(expr * e); - bool compile(unsigned sz, expr * const* es); - tree_t const& get_tree() { return m_tree; } - void reset(); - expr* idom(expr *e) const { return m_doms[e]; } - - std::ostream& display(std::ostream& out); -}; - -class dom_simplifier { - public: - virtual ~dom_simplifier() = default; - /** - \brief assert_expr performs an implicit push - */ - virtual bool assert_expr(expr * t, bool sign) = 0; - - /** - \brief apply simplification. - */ - virtual void operator()(expr_ref& r) = 0; - - /** - \brief pop scopes accumulated from assertions. - */ - virtual void pop(unsigned num_scopes) = 0; - - virtual dom_simplifier * translate(ast_manager & m) = 0; - - virtual unsigned scope_level() const = 0; - - virtual void updt_params(params_ref const & p) = 0; - - virtual void collect_param_descrs(param_descrs& r) = 0; -}; - - -class dom_simplify_tactic : public tactic { - ast_manager& m; - dom_simplifier* m_simplifier; - params_ref m_params; - expr_ref_vector m_trail, m_args; - obj_map m_result; - expr_dominators m_dominators; - unsigned m_depth; - unsigned m_max_depth; - ptr_vector m_empty; - obj_pair_map m_subexpr_cache; - bool m_forward; - - expr_ref simplify_rec(expr* t); - expr_ref simplify_arg(expr* t); - expr_ref simplify_ite(app * ite); - expr_ref simplify_and(app * e) { return simplify_and_or(true, e); } - expr_ref simplify_or(app * e) { return simplify_and_or(false, e); } - expr_ref simplify_and_or(bool is_and, app * e); - expr_ref simplify_not(app * e); - void simplify_goal(goal& g); - - bool is_subexpr(expr * a, expr * b); - - expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } - void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } - void reset_cache() { m_result.reset(); } - - ptr_vector const & tree(expr * e); - expr* idom(expr *e) const { return m_dominators.idom(e); } - - unsigned scope_level() { return m_simplifier->scope_level(); } - void pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); } - bool assert_expr(expr* f, bool sign) { return m_simplifier->assert_expr(f, sign); } - - bool init(goal& g); - -public: - dom_simplify_tactic(ast_manager & m, dom_simplifier* s, params_ref const & p = params_ref()): - m(m), m_simplifier(s), m_params(p), - m_trail(m), m_args(m), - m_dominators(m), m_depth(0), m_max_depth(1024), m_forward(true) {} - - ~dom_simplify_tactic() override; - - char const* name() const override { return "dom_simplify"; } - - tactic * translate(ast_manager & m) override; - void updt_params(params_ref const & p) override { m_simplifier->updt_params(p); } - void collect_param_descrs(param_descrs & r) override { m_simplifier->collect_param_descrs(r); } - void operator()(goal_ref const & in, goal_ref_buffer & result) override; - void cleanup() override; -}; - - -tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/dominator_simplifier.h" + +inline tactic* mk_dom_simplify_tactic(ast_manager& m, params_ref const& p) { + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* { return alloc(dominator_simplifier, m, s, mk_expr_substitution_simplifier(m), p); }); +} /* ADD_TACTIC("dom-simplify", "apply dominator simplification rules.", "mk_dom_simplify_tactic(m, p)") From 91d6082f2fd764340f7c0fa0aa0c97861a104d9b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 27 Jan 2023 17:55:36 -0800 Subject: [PATCH 336/597] Move modular interval to interval directory --- src/ast/rewriter/CMakeLists.txt | 1 + src/ast/rewriter/bv_bounds_base.h | 221 +------------------------- src/math/interval/mod_interval.h | 239 +++++++++++++++++++++++++++++ src/tactic/bv/bv_bounds_tactic.cpp | 5 +- 4 files changed, 244 insertions(+), 222 deletions(-) create mode 100644 src/math/interval/mod_interval.h diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index 17410807a00..df803b0f186 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -47,5 +47,6 @@ z3_add_component(rewriter ast params automata + interval polynomial ) diff --git a/src/ast/rewriter/bv_bounds_base.h b/src/ast/rewriter/bv_bounds_base.h index 8ddb429e97c..840cf6e3ee8 100644 --- a/src/ast/rewriter/bv_bounds_base.h +++ b/src/ast/rewriter/bv_bounds_base.h @@ -17,227 +17,10 @@ Module Name: #pragma once -namespace bv { - - template - struct interval_tpl : public Base { - T l, h; - unsigned sz = 0; - bool tight = true; - - interval_tpl(T const& l, T const& h, unsigned sz, bool tight = false): l(l), h(h), sz(sz), tight(tight) {} - interval_tpl() {} - - bool invariant() const { - return - 0 <= l && (l <= Base::bound(sz)) && - 0 <= h && (h <= Base::bound(sz)) && - (!is_wrapped() || l != h + 1); - } - - bool is_full() const { - return l == 0 && h == Base::bound(sz); - } - bool is_wrapped() const { return l > h; } - bool is_singleton() const { return l == h; } - - bool operator==(const interval_tpl& b) const { - SASSERT(sz == b.sz); - return l == b.l && h == b.h && tight == b.tight; - } - bool operator!=(const interval_tpl& b) const { return !(*this == b); } - - bool implies(const interval_tpl& b) const { - if (b.is_full()) - return true; - else if (is_full()) - return false; - else if (is_wrapped()) - // l >= b.l >= b.h >= h - return b.is_wrapped() && h <= b.h && l >= b.l; - else if (b.is_wrapped()) - // b.l > b.h >= h >= l - // h >= l >= b.l > b.h - return h <= b.h || l >= b.l; - else - return l >= b.l && h <= b.h; - } - - /// return false if intersection is unsat - bool intersect(const interval_tpl& b, interval_tpl& result) const { - if (is_full() || *this == b) { - result = b; - return true; - } - if (b.is_full()) { - result = *this; - return true; - } - - if (is_wrapped()) { - if (b.is_wrapped()) { - if (h >= b.l) - result = b; - else if (b.h >= l) - result = *this; - else - result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz); - } - else - return b.intersect(*this, result); - } - else if (b.is_wrapped()) { - // ... b.h ... l ... h ... b.l .. - if (h < b.l && l > b.h) - return false; - // ... l ... b.l ... h ... - if (h >= b.l && l <= b.h) - result = b; - else if (h >= b.l) - result = interval_tpl(b.l, h, sz); - else { - // ... l .. b.h .. h .. b.l ... - SASSERT(l <= b.h); - result = interval_tpl(l, std::min(h, b.h), sz); - } - } else { - if (l > b.h || h < b.l) - return false; - - // 0 .. l.. l' ... h ... h' - result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); - } - return true; - } - - /// return false if negation is empty - bool negate(interval_tpl& result) const { - if (!tight) - result = interval_tpl(Base::zero(), Base::bound(sz), sz, true); - else if (is_full()) - return false; - else if (l == 0 && Base::bound(sz) == h) - result = interval_tpl(Base::zero(), Base::bound(sz), sz); - else if (l == 0) - result = interval_tpl(h + 1, Base::bound(sz), sz); - else if (Base::bound(sz) == h) - result = interval_tpl(Base::zero(), l - 1, sz); - else - result = interval_tpl(h + 1, l - 1, sz); - return true; - } - - - }; - - struct rinterval_base { - static rational bound(unsigned sz) { - return rational::power_of_two(sz) - 1; - } - - static rational zero() { return rational::zero(); } - }; - - struct rinterval : public interval_tpl { - rinterval(rational const& l, rational const& h, unsigned sz, bool tight = false) { - this->l = l; this->h = h; this->sz = sz; this->tight = tight; - } - rinterval() { l = 0; h = 0; tight = true; } - }; - - struct iinterval_base { - static uint64_t uMaxInt(unsigned sz) { - SASSERT(sz <= 64); - return ULLONG_MAX >> (64u - sz); - } - - static uint64_t bound(unsigned sz) { return uMaxInt(sz); } - static uint64_t zero() { return 0; } - }; - - struct iinterval : public interval_tpl { - iinterval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) { - this->l = l; this->h = h; this->sz = sz; this->tight = tight; - } - iinterval() { l = 0; h = 0; sz = 0; tight = true; } - }; - - struct interval { - bool is_small = true; - iinterval i; - rinterval r; - - interval() {} - - interval(rational const& l, rational const& h, unsigned sz, bool tight = false) { - if (sz <= 64) { - is_small = true; - i.l = l.get_uint64(); - i.h = h.get_uint64(); - i.tight = tight; - i.sz = sz; - } - else { - is_small = false; - r.l = l; - r.h = h; - r.tight = tight; - r.sz = sz; - } - } - - unsigned size() const { - return is_small ? i.sz : r.sz; - } - - bool negate(interval& result) const { - result.is_small = is_small; - if (is_small) - return i.negate(result.i); - else - return r.negate(result.r); - } - - bool intersect(interval const& b, interval & result) const { - result.is_small = is_small; - SASSERT(b.is_small == is_small); - if (is_small) - return i.intersect(b.i, result.i); - else - return r.intersect(b.r, result.r); - } - - bool operator==(interval const& other) const { - SASSERT(is_small == other.is_small); - return is_small ? i == other.i : r == other.r; - } - - bool operator!=(interval const& other) const { - return !(*this == other); - } - - bool is_singleton() const { return is_small ? i.is_singleton() : r.is_singleton(); } - - bool is_full() const { return is_small ? i.is_full() : r.is_full(); } - - bool tight() const { return is_small ? i.tight : r.tight; } - - bool implies(const interval& b) const { - SASSERT(is_small == b.is_small); - return is_small ? i.implies(b.i) : r.implies(b.r); - } - - rational lo() const { return is_small ? rational(i.l, rational::ui64()) : r.l; } - rational hi() const { return is_small ? rational(i.h, rational::ui64()) : r.h; } - }; +#include "math/interval/mod_interval.h" +namespace bv { - inline std::ostream& operator<<(std::ostream& o, const interval& I) { - if (I.is_small) - return o << "[" << I.i.l << ", " << I.i.h << "]"; - else - return o << "[" << I.r.l << ", " << I.r.h << "]"; - } struct undo_bound { expr* e = nullptr; diff --git a/src/math/interval/mod_interval.h b/src/math/interval/mod_interval.h new file mode 100644 index 00000000000..88acdd8d67f --- /dev/null +++ b/src/math/interval/mod_interval.h @@ -0,0 +1,239 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + mod_interval.h + +Abstract: + + Modular interval for bit-vector comparisons + +Author: + + Nikolaj and Nuno + +--*/ + +namespace bv { + + template + struct interval_tpl : public Base { + T l, h; + unsigned sz = 0; + bool tight = true; + + interval_tpl(T const& l, T const& h, unsigned sz, bool tight = false): l(l), h(h), sz(sz), tight(tight) {} + interval_tpl() {} + + bool invariant() const { + return + 0 <= l && (l <= Base::bound(sz)) && + 0 <= h && (h <= Base::bound(sz)) && + (!is_wrapped() || l != h + 1); + } + + bool is_full() const { + return l == 0 && h == Base::bound(sz); + } + bool is_wrapped() const { return l > h; } + bool is_singleton() const { return l == h; } + + bool operator==(const interval_tpl& b) const { + SASSERT(sz == b.sz); + return l == b.l && h == b.h && tight == b.tight; + } + bool operator!=(const interval_tpl& b) const { return !(*this == b); } + + bool implies(const interval_tpl& b) const { + if (b.is_full()) + return true; + else if (is_full()) + return false; + else if (is_wrapped()) + // l >= b.l >= b.h >= h + return b.is_wrapped() && h <= b.h && l >= b.l; + else if (b.is_wrapped()) + // b.l > b.h >= h >= l + // h >= l >= b.l > b.h + return h <= b.h || l >= b.l; + else + return l >= b.l && h <= b.h; + } + + /// return false if intersection is unsat + bool intersect(const interval_tpl& b, interval_tpl& result) const { + if (is_full() || *this == b) { + result = b; + return true; + } + if (b.is_full()) { + result = *this; + return true; + } + + if (is_wrapped()) { + if (b.is_wrapped()) { + if (h >= b.l) + result = b; + else if (b.h >= l) + result = *this; + else + result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz); + } + else + return b.intersect(*this, result); + } + else if (b.is_wrapped()) { + // ... b.h ... l ... h ... b.l .. + if (h < b.l && l > b.h) + return false; + // ... l ... b.l ... h ... + if (h >= b.l && l <= b.h) + result = b; + else if (h >= b.l) + result = interval_tpl(b.l, h, sz); + else { + // ... l .. b.h .. h .. b.l ... + SASSERT(l <= b.h); + result = interval_tpl(l, std::min(h, b.h), sz); + } + } else { + if (l > b.h || h < b.l) + return false; + + // 0 .. l.. l' ... h ... h' + result = interval_tpl(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); + } + return true; + } + + /// return false if negation is empty + bool negate(interval_tpl& result) const { + if (!tight) + result = interval_tpl(Base::zero(), Base::bound(sz), sz, true); + else if (is_full()) + return false; + else if (l == 0 && Base::bound(sz) == h) + result = interval_tpl(Base::zero(), Base::bound(sz), sz); + else if (l == 0) + result = interval_tpl(h + 1, Base::bound(sz), sz); + else if (Base::bound(sz) == h) + result = interval_tpl(Base::zero(), l - 1, sz); + else + result = interval_tpl(h + 1, l - 1, sz); + return true; + } + + + }; + + struct rinterval_base { + static rational bound(unsigned sz) { + return rational::power_of_two(sz) - 1; + } + + static rational zero() { return rational::zero(); } + }; + + struct rinterval : public interval_tpl { + rinterval(rational const& l, rational const& h, unsigned sz, bool tight = false) { + this->l = l; this->h = h; this->sz = sz; this->tight = tight; + } + rinterval() { l = 0; h = 0; tight = true; } + }; + + struct iinterval_base { + static uint64_t uMaxInt(unsigned sz) { + SASSERT(sz <= 64); + return ULLONG_MAX >> (64u - sz); + } + + static uint64_t bound(unsigned sz) { return uMaxInt(sz); } + static uint64_t zero() { return 0; } + }; + + struct iinterval : public interval_tpl { + iinterval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) { + this->l = l; this->h = h; this->sz = sz; this->tight = tight; + } + iinterval() { l = 0; h = 0; sz = 0; tight = true; } + }; + + struct interval { + bool is_small = true; + iinterval i; + rinterval r; + + interval() {} + + interval(rational const& l, rational const& h, unsigned sz, bool tight = false) { + if (sz <= 64) { + is_small = true; + i.l = l.get_uint64(); + i.h = h.get_uint64(); + i.tight = tight; + i.sz = sz; + } + else { + is_small = false; + r.l = l; + r.h = h; + r.tight = tight; + r.sz = sz; + } + } + + unsigned size() const { + return is_small ? i.sz : r.sz; + } + + bool negate(interval& result) const { + result.is_small = is_small; + if (is_small) + return i.negate(result.i); + else + return r.negate(result.r); + } + + bool intersect(interval const& b, interval & result) const { + result.is_small = is_small; + SASSERT(b.is_small == is_small); + if (is_small) + return i.intersect(b.i, result.i); + else + return r.intersect(b.r, result.r); + } + + bool operator==(interval const& other) const { + SASSERT(is_small == other.is_small); + return is_small ? i == other.i : r == other.r; + } + + bool operator!=(interval const& other) const { + return !(*this == other); + } + + bool is_singleton() const { return is_small ? i.is_singleton() : r.is_singleton(); } + + bool is_full() const { return is_small ? i.is_full() : r.is_full(); } + + bool tight() const { return is_small ? i.tight : r.tight; } + + bool implies(const interval& b) const { + SASSERT(is_small == b.is_small); + return is_small ? i.implies(b.i) : r.implies(b.r); + } + + rational lo() const { return is_small ? rational(i.l, rational::ui64()) : r.l; } + rational hi() const { return is_small ? rational(i.h, rational::ui64()) : r.h; } + }; + + + inline std::ostream& operator<<(std::ostream& o, const interval& I) { + if (I.is_small) + return o << "[" << I.i.l << ", " << I.i.h << "]"; + else + return o << "[" << I.r.l << ", " << I.r.h << "]"; + } +} diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 45ff60ec092..5f856800e8b 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -20,13 +20,12 @@ Module Name: #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" +#include "ast/rewriter/bv_bounds_base.h" #include "ast/simplifiers/dominator_simplifier.h" +#include "ast/simplifiers/bv_bounds_simplifier.h" #include "tactic/bv/bv_bounds_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/dependent_expr_state_tactic.h" -#include "ast/rewriter/bv_bounds_base.h" -#include "ast/simplifiers/bv_bounds_simplifier.h" - #include From fb1f4f3a2caaa144a3ef8ff254e0ca87ad0308e8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 27 Jan 2023 18:03:06 -0800 Subject: [PATCH 337/597] add pragma Signed-off-by: Nikolaj Bjorner --- src/math/interval/mod_interval.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/math/interval/mod_interval.h b/src/math/interval/mod_interval.h index 88acdd8d67f..ed189fe7b6e 100644 --- a/src/math/interval/mod_interval.h +++ b/src/math/interval/mod_interval.h @@ -15,6 +15,8 @@ Module Name: --*/ +#pragma once + namespace bv { template From 83bd3d1e21b91338cec1df68da7a019f19d8a117 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 27 Jan 2023 18:04:58 -0800 Subject: [PATCH 338/597] fix mk-project file for python build Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 2e0e4e73978..e87c36d851f 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -31,7 +31,7 @@ def init_project_def(): add_lib('sat', ['params', 'util', 'dd', 'grobner']) 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('rewriter', ['ast', 'polynomial', 'interval', 'automata', 'params'], 'ast/rewriter') add_lib('bit_blaster', ['rewriter'], 'ast/rewriter/bit_blaster') add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') add_lib('substitution', ['rewriter'], 'ast/substitution') @@ -39,7 +39,7 @@ def init_project_def(): add_lib('macros', ['rewriter'], 'ast/macros') add_lib('model', ['macros']) add_lib('converters', ['model'], 'ast/converters') - add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'interval', 'converters', 'substitution'], 'ast/simplifiers') + add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'converters', 'substitution'], 'ast/simplifiers') add_lib('tactic', ['simplifiers']) add_lib('solver', ['params', 'model', 'tactic', 'proofs']) add_lib('cmd_context', ['solver', 'rewriter', 'params']) From 246d6f7b7717f3793e7069abe5ec55185dd481fc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 28 Jan 2023 03:47:18 -0800 Subject: [PATCH 339/597] fix #6561 --- src/tactic/core/reduce_args_tactic.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index 785409e2701..d31fb838770 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -398,9 +398,11 @@ struct reduce_args_tactic::imp { var_ref_vector new_vars(m); ptr_buffer new_eqs; generic_model_converter * f_mc = alloc(generic_model_converter, m, "reduce_args"); - for (auto const& kv : decl2arg2funcs) { - func_decl * f = kv.m_key; - arg2func * map = kv.m_value; + for (auto const& [f, map] : decl2arg2funcs) + for (auto const& [t, new_def] : *map) + f_mc->hide(new_def); + + for (auto const& [f, map] : decl2arg2funcs) { expr * def = nullptr; SASSERT(decl2args.contains(f)); bit_vector & bv = decl2args.find(f); @@ -412,7 +414,7 @@ struct reduce_args_tactic::imp { new_args.push_back(new_vars.back()); } for (auto const& [t, new_def] : *map) { - f_mc->hide(new_def); + // f_mc->hide(new_def); SASSERT(new_def->get_arity() == new_args.size()); app * new_t = m.mk_app(new_def, new_args); if (def == nullptr) { From 8ea49eed8ec34b8743fd097707d1ecb83bf317e1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 28 Jan 2023 20:12:14 -0800 Subject: [PATCH 340/597] convert reduce-args to a simplifier - convert reduce-args to a simplifier. Currently exposed as reduce-args2 tactic until the old tactic code gets removed. - bug fixes in model_reconstruction trail - allow multiple defs to be added with same pool of removed formulas - fix tracking of function symbols instead of expressions to filter replay - add nla_divisions to track (cheap) divisibility lemmas. - --- src/ast/ast_util.h | 2 + src/ast/converters/generic_model_converter.h | 2 - src/ast/simplifiers/CMakeLists.txt | 1 + src/ast/simplifiers/dependent_expr_state.cpp | 7 +- src/ast/simplifiers/dependent_expr_state.h | 2 +- .../model_reconstruction_trail.cpp | 36 +- .../simplifiers/model_reconstruction_trail.h | 49 +- .../simplifiers/reduce_args_simplifier.cpp | 428 ++++++++++++++++++ src/ast/simplifiers/reduce_args_simplifier.h | 16 + src/math/lp/CMakeLists.txt | 1 + src/math/lp/nla_core.cpp | 9 + src/math/lp/nla_core.h | 4 + src/math/lp/nla_divisions.cpp | 65 +++ src/math/lp/nla_divisions.h | 31 ++ src/math/lp/nla_solver.cpp | 4 + src/math/lp/nla_solver.h | 1 + src/math/lp/var_eqs.h | 3 +- src/sat/sat_solver/sat_smt_solver.cpp | 72 ++- src/smt/theory_lra.cpp | 7 + src/tactic/core/reduce_args_tactic.cpp | 10 +- src/tactic/core/reduce_args_tactic.h | 10 + src/tactic/dependent_expr_state_tactic.h | 56 ++- src/util/bit_vector.h | 16 + 23 files changed, 740 insertions(+), 92 deletions(-) create mode 100644 src/ast/simplifiers/reduce_args_simplifier.cpp create mode 100644 src/ast/simplifiers/reduce_args_simplifier.h create mode 100644 src/math/lp/nla_divisions.cpp create mode 100644 src/math/lp/nla_divisions.h diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index 1c56b64b368..8e07ccd272d 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -101,6 +101,8 @@ expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx); */ expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args); app * mk_and(ast_manager & m, unsigned num_args, app * const * args); +inline expr * mk_and(ast_manager & m, ptr_vector const& args) { return mk_and(m, args.size(), args.data()); } +inline expr * mk_and(ast_manager & m, ptr_buffer const& args) { return mk_and(m, args.size(), args.data()); } inline expr * mk_and(ast_manager & m, expr* a, expr* b) { expr* args[2] = { a, b }; return mk_and(m, 2, args); } inline app_ref mk_and(app_ref_vector const& args) { return app_ref(mk_and(args.get_manager(), args.size(), args.data()), args.get_manager()); } inline expr_ref mk_and(expr_ref_vector const& args) { return expr_ref(mk_and(args.get_manager(), args.size(), args.data()), args.get_manager()); } diff --git a/src/ast/converters/generic_model_converter.h b/src/ast/converters/generic_model_converter.h index cf551c92a48..8a1c6234774 100644 --- a/src/ast/converters/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -68,8 +68,6 @@ class generic_model_converter : public model_converter { void get_units(obj_map& units) override; vector const& entries() const { return m_entries; } - - void shrink(unsigned j) { m_entries.shrink(j); } }; typedef ref generic_model_converter_ref; diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 826717c5a0c..127db786a3a 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -19,6 +19,7 @@ z3_add_component(simplifiers max_bv_sharing.cpp model_reconstruction_trail.cpp propagate_values.cpp + reduce_args_simplifier.cpp solve_context_eqs.cpp solve_eqs.cpp COMPONENT_DEPENDENCIES diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp index fec6d28cf71..d784e6fe42e 100644 --- a/src/ast/simplifiers/dependent_expr_state.cpp +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -31,8 +31,13 @@ void dependent_expr_state::freeze(func_decl* f) { } void dependent_expr_state::freeze(expr* term) { - if (is_app(term)) + if (is_app(term) && to_app(term)->get_num_args() == 0) freeze(to_app(term)->get_decl()); + else { + ast_mark visited; + freeze_terms(term, false, visited); + } + } /** diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index c3b5043a5a7..0fedf484ceb 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -80,7 +80,7 @@ class dependent_expr_state { m_trail.push(value_trail(m_qhead)); m_trail.push(thaw(*this)); } - void pop(unsigned n) { m_trail.pop_scope(n); } + void pop(unsigned n) { m_trail.pop_scope(n); } void advance_qhead() { freeze_prefix(); m_suffix_frozen = false; m_has_quantifiers = l_undef; m_qhead = qtail(); } unsigned num_exprs(); diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index e3cfc5c6fec..893af71065c 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -13,6 +13,7 @@ Module Name: #include "ast/for_each_expr.h" +#include "ast/ast_ll_pp.h" #include "ast/rewriter/macro_replacer.h" #include "ast/simplifiers/model_reconstruction_trail.h" #include "ast/simplifiers/dependent_expr_state.h" @@ -24,6 +25,10 @@ Module Name: // TODO: add filters to skip sections of the trail that do not touch the current free variables. void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& st) { + TRACE("simplifier", + for (unsigned i = qhead; i < st.qtail(); ++i) + tout << mk_bounded_pp(st[i].fml(), m) << "\n"; + ); ast_mark free_vars; scoped_ptr rp = mk_default_expr_replacer(m, false); for (unsigned i = qhead; i < st.qtail(); ++i) @@ -32,6 +37,7 @@ void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumpt add_vars(a, free_vars); for (auto& t : m_trail) { + TRACE("simplifier", tout << " active " << t->m_active << " hide " << t->is_hide() << " intersects " << t->intersects(free_vars) << "\n"); if (!t->m_active) continue; @@ -56,15 +62,17 @@ void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumpt if (t->is_def()) { macro_replacer mrp(m); - app_ref head(m); - func_decl* d = t->m_decl; - ptr_buffer args; - for (unsigned i = 0; i < d->get_arity(); ++i) - args.push_back(m.mk_var(i, d->get_domain(i))); - head = m.mk_app(d, args); - mrp.insert(head, t->m_def, t->m_dep); - dependent_expr de(m, t->m_def, nullptr, t->m_dep); - add_vars(de, free_vars); + for (auto const& [d, def, dep] : t->m_defs) { + app_ref head(m); + ptr_buffer args; + for (unsigned i = 0; i < d->get_arity(); ++i) + args.push_back(m.mk_var(i, d->get_domain(i))); + head = m.mk_app(d, args); + mrp.insert(head, def, dep); + TRACE("simplifier", tout << d << " " << def << " " << dep << "\n"); + dependent_expr de(m, def, nullptr, dep); + add_vars(de, free_vars); + } for (unsigned i = qhead; i < st.qtail(); ++i) { auto [f, p, dep1] = st[i](); @@ -140,6 +148,7 @@ model_converter_ref model_reconstruction_trail::get_model_converter() { * Append model conversions starting at index i */ void model_reconstruction_trail::append(generic_model_converter& mc, unsigned& i) { + TRACE("simplifier", display(tout)); for (; i < m_trail.size(); ++i) { auto* t = m_trail[i]; if (!t->m_active) @@ -147,7 +156,8 @@ void model_reconstruction_trail::append(generic_model_converter& mc, unsigned& i else if (t->is_hide()) mc.hide(t->m_decl); else if (t->is_def()) - mc.add(t->m_decl, t->m_def); + for (auto const& [f, def, dep] : t->m_defs) + mc.add(f, def); else { for (auto const& [v, def] : t->m_subst->sub()) mc.add(v, def); @@ -167,8 +177,10 @@ std::ostream& model_reconstruction_trail::display(std::ostream& out) const { continue; else if (t->is_hide()) out << "hide " << t->m_decl->get_name() << "\n"; - else if (t->is_def()) - out << t->m_decl->get_name() << " <- " << mk_pp(t->m_def, m) << "\n"; + else if (t->is_def()) { + for (auto const& [f, def, dep] : t->m_defs) + out << f->get_name() << " <- " << mk_pp(def, m) << "\n"; + } else { for (auto const& [v, def] : t->m_subst->sub()) out << mk_pp(v, m) << " <- " << mk_pp(def, m) << "\n"; diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 4bc09e646a4..c1b045a43c7 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -39,34 +39,46 @@ class model_reconstruction_trail { scoped_ptr m_subst; vector m_removed; func_decl_ref m_decl; - expr_ref m_def; - expr_dependency_ref m_dep; + vector> m_defs; + bool m_active = true; entry(ast_manager& m, expr_substitution* s, vector const& rem) : - m_subst(s), m_removed(rem), m_decl(m), m_def(m), m_dep(m) {} + m_subst(s), m_removed(rem), m_decl(m) {} - entry(ast_manager& m, func_decl* h) : m_decl(h, m), m_def(m), m_dep(m) {} + entry(ast_manager& m, func_decl* h) : m_decl(h, m) {} entry(ast_manager& m, func_decl* f, expr* def, expr_dependency* dep, vector const& rem) : - m_removed(rem), m_decl(f, m), m_def(def, m), m_dep(dep, m) {} + m_removed(rem), + m_decl(m){ + m_defs.push_back({ func_decl_ref(f, m), expr_ref(def, m), expr_dependency_ref(dep, m) }); + } + + entry(ast_manager& m, vector> const& defs, vector const& rem) : + m_removed(rem), + m_decl(m), + m_defs(defs) { + } bool is_loose() const { return !m_removed.empty(); } bool intersects(ast_mark const& free_vars) const { if (is_hide()) return false; - if (is_def()) - return free_vars.is_marked(m_decl); - for (auto const& [k, v] : m_subst->sub()) - if (free_vars.is_marked(k)) + for (auto const& [f, def, dep] : m_defs) + if (free_vars.is_marked(f)) return true; + if (m_subst) { + for (auto const& [k, v] : m_subst->sub()) + if (free_vars.is_marked(k)) + return true; + } return false; } - bool is_hide() const { return m_decl && !m_def; } - bool is_def() const { return m_decl && m_def; } - bool is_subst() const { return !m_decl; } + bool is_hide() const { return m_decl && m_defs.empty(); } + bool is_def() const { return !m_defs.empty(); } + bool is_subst() const { return m_subst && !m_subst->empty(); } }; ast_manager& m; @@ -76,7 +88,8 @@ class model_reconstruction_trail { void add_vars(expr* e, ast_mark& free_vars) { for (expr* t : subterms::all(expr_ref(e, m))) - free_vars.mark(t, true); + if (is_app(t)) + free_vars.mark(to_app(t)->get_decl(), true); } void add_vars(dependent_expr const& d, ast_mark& free_vars) { @@ -86,7 +99,7 @@ class model_reconstruction_trail { bool intersects(ast_mark const& free_vars, dependent_expr const& d) { expr_ref term(d.fml(), m); auto iter = subterms::all(term); - return any_of(iter, [&](expr* t) { return free_vars.is_marked(t); }); + return any_of(iter, [&](expr* t) { return is_app(t) && free_vars.is_marked(to_app(t)->get_decl()); }); } bool intersects(ast_mark const& free_vars, vector const& added) { @@ -126,6 +139,14 @@ class model_reconstruction_trail { m_trail_stack.push(push_back_vector(m_trail)); } + /** + * add definitions + */ + void push(vector> const& defs, vector const& removed) { + m_trail.push_back(alloc(entry, m, defs, removed)); + m_trail_stack.push(push_back_vector(m_trail)); + } + /** * register a new depedent expression, update the trail * by removing substitutions that are not equivalence preserving. diff --git a/src/ast/simplifiers/reduce_args_simplifier.cpp b/src/ast/simplifiers/reduce_args_simplifier.cpp new file mode 100644 index 00000000000..4ebd4912752 --- /dev/null +++ b/src/ast/simplifiers/reduce_args_simplifier.cpp @@ -0,0 +1,428 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + reduce_args_simplifier.cpp + +Abstract: + + Reduce the number of arguments in function applications. + +Author: + + Leonardo (leonardo) 2012-02-19 + +Notes: + +--*/ + +#include "util/map.h" +#include "ast/ast_smt2_pp.h" +#include "ast/ast_util.h" +#include "ast/has_free_vars.h" +#include "ast/rewriter/rewriter_def.h" +#include "ast/simplifiers/dependent_expr_state.h" + +/** + \brief Reduce the number of arguments in function applications. + + Example, suppose we have a function f with 2 arguments. + There are 1000 applications of this function, but the first argument is always "a", "b" or "c". + Thus, we replace the f(t1, t2) + with + f_a(t2) if t1 = a + f_b(t2) if t2 = b + f_c(t2) if t2 = c + + Since f_a, f_b, f_c are new symbols, satisfiability is preserved. + + This transformation is very similar in spirit to the Ackermman's reduction. + + This transformation should work in the following way: + + 1- Create a mapping decl2arg_map from declarations to tuples of booleans, an entry [f -> (true, false, true)] + means that f is a declaration with 3 arguments where the first and third arguments are always values. + 2- Traverse the formula and populate the mapping. + For each function application f(t1, ..., tn) do + a) Create a boolean tuple (is_value(t1), ..., is_value(tn)) and do + the logical-and with the tuple that is already in the mapping. If there is no such tuple + in the mapping, we just add a new entry. + + If all entries are false-tuples, then there is nothing to be done. The transformation is not applicable. + + Now, we create a mapping decl2new_decl from (decl, val_1, ..., val_n) to decls. Note that, n may be different for each entry, + but it is the same for the same declaration. + For example, suppose we have [f -> (true, false, true)] in decl2arg_map, and applications f(1, a, 2), f(1, b, 2), f(1, b, 3), f(2, b, 3), f(2, c, 3) in the formula. + Then, decl2arg_map would contain + (f, 1, 2) -> f_1_2 + (f, 1, 3) -> f_1_3 + (f, 2, 3) -> f_2_3 + where f_1_2, f_1_3 and f_2_3 are new function symbols. + Using the new map, we can replace the occurrences of f. +*/ + +class reduce_args_simplifier : public dependent_expr_simplifier { + bv_util m_bv; + + static bool is_var_plus_offset(ast_manager& m, bv_util& bv, expr* e, expr*& base) { + expr *lhs, *rhs; + if (bv.is_bv_add(e, lhs, rhs) && bv.is_numeral(lhs)) + base = rhs; + else + base = e; + return !has_free_vars(base); + } + + static bool may_be_unique(ast_manager& m, bv_util& bv, expr* e, expr*& base) { + base = nullptr; + return m.is_unique_value(e) || is_var_plus_offset(m, bv, e, base); + } + + static bool may_be_unique(ast_manager& m, bv_util& bv, expr* e) { + expr* base; + return may_be_unique(m, bv, e, base); + } + + struct find_non_candidates_proc { + ast_manager & m; + bv_util & m_bv; + obj_hashtable & m_non_candidates; + + find_non_candidates_proc(ast_manager & m, bv_util & bv, obj_hashtable & non_candidates): + m(m), + m_bv(bv), + m_non_candidates(non_candidates) { + } + + void operator()(var * n) {} + + void operator()(quantifier *n) {} + + void operator()(app * n) { + if (!is_uninterp(n)) + return; + func_decl * d; + if (n->get_num_args() == 0) + return; // ignore constants + d = n->get_decl(); + if (m_non_candidates.contains(d)) + return; // it is already in the set. + for (expr* arg : *n) + if (may_be_unique(m, m_bv, arg)) + return; + m_non_candidates.insert(d); + } + }; + + /** + \brief Populate the table non_candidates with function declarations \c f + such that there is a function application (f t1 ... tn) where t1 ... tn are not values. + */ + void find_non_candidates(obj_hashtable & non_candidates) { + non_candidates.reset(); + find_non_candidates_proc proc(m, m_bv, non_candidates); + expr_fast_mark1 visited; + for (auto i : indices()) + quick_for_each_expr(proc, visited, m_fmls[i].fml()); + + TRACE("reduce_args", tout << "non_candidates:\n"; for (func_decl* d : non_candidates) tout << d->get_name() << "\n";); + } + + struct populate_decl2args_proc { + reduce_args_simplifier& m_owner; + ast_manager & m; + bv_util & m_bv; + obj_hashtable & m_non_candidates; + obj_map & m_decl2args; + obj_map > m_decl2base; // for args = base + offset + + populate_decl2args_proc(reduce_args_simplifier& o, ast_manager & m, bv_util & bv, obj_hashtable & nc, obj_map & d): + m_owner(o), m(m), m_bv(bv), m_non_candidates(nc), m_decl2args(d) {} + + void operator()(var * n) {} + void operator()(quantifier * n) {} + void operator()(app * n) { + if (n->get_num_args() == 0) + return; // ignore constants + func_decl * d = n->get_decl(); + if (d->get_family_id() != null_family_id) + return; // ignore interpreted symbols + if (m_non_candidates.contains(d)) + return; // declaration is not a candidate + if (m_owner.m_fmls.frozen(d)) + return; + + unsigned j = n->get_num_args(); + obj_map::iterator it = m_decl2args.find_iterator(d); + expr* base; + if (it == m_decl2args.end()) { + m_decl2args.insert(d, bit_vector()); + svector& bases = m_decl2base.insert_if_not_there(d, svector()); + bases.resize(j); + it = m_decl2args.find_iterator(d); + SASSERT(it != m_decl2args.end()); + it->m_value.reserve(j); + while (j > 0) { + --j; + it->m_value.set(j, may_be_unique(m, m_bv, n->get_arg(j), base)); + bases[j] = base; + } + } else { + svector& bases = m_decl2base[d]; + SASSERT(j == it->m_value.size()); + while (j > 0) { + --j; + it->m_value.set(j, it->m_value.get(j) && may_be_unique(m, m_bv, n->get_arg(j), base) && bases[j] == base); + } + } + } + }; + + void populate_decl2args(obj_hashtable & non_candidates, + obj_map & decl2args) { + expr_fast_mark1 visited; + decl2args.reset(); + populate_decl2args_proc proc(*this, m, m_bv, non_candidates, decl2args); + for (auto i : indices()) + quick_for_each_expr(proc, visited, m_fmls[i].fml()); + + // Remove all cases where the simplification is not applicable. + ptr_buffer bad_decls; + for (auto const& [k, v] : decl2args) + if (all_of(v, [&](auto b) { return !b;})) + bad_decls.push_back(k); + + for (func_decl* a : bad_decls) + decl2args.erase(a); + + TRACE("reduce_args", tout << "decl2args:" << std::endl; + for (auto const& [k, v] : decl2args) { + tout << k->get_name() << ": "; + for (unsigned i = 0; i < v.size(); ++i) + tout << (v.get(i) ? "1" : "0"); + tout << std::endl; + }); + } + + struct arg2func_hash_proc { + bit_vector const & m_bv; + + arg2func_hash_proc(bit_vector const & bv):m_bv(bv) {} + unsigned operator()(app const * n) const { + // compute the hash-code using only the arguments where m_bv is true. + unsigned a = 0x9e3779b9; + unsigned num_args = n->get_num_args(); + for (unsigned i = 0; i < num_args; i++) { + if (!m_bv.get(i)) + continue; // ignore argument + a = hash_u_u(a, n->get_arg(i)->get_id()); + } + return a; + } + }; + + struct arg2func_eq_proc { + bit_vector const & m_bv; + + arg2func_eq_proc(bit_vector const & bv):m_bv(bv) {} + bool operator()(app const * n1, app const * n2) const { + // compare only the arguments where m_bv is true + SASSERT(n1->get_num_args() == n2->get_num_args()); + unsigned num_args = n1->get_num_args(); + for (unsigned i = 0; i < num_args; i++) { + if (!m_bv.get(i)) + continue; // ignore argument + if (n1->get_arg(i) != n2->get_arg(i)) + return false; + } + return true; + } + }; + + typedef map arg2func; + typedef obj_map decl2arg2func_map; + + struct reduce_args_ctx { + ast_manager & m; + decl2arg2func_map m_decl2arg2funcs; + + reduce_args_ctx(ast_manager & m): m(m) { + } + + ~reduce_args_ctx() { + for (auto const& [_, map] : m_decl2arg2funcs) { + for (auto const& [k, v] : *map) { + m.dec_ref(k); + m.dec_ref(v); + } + dealloc(map); + } + } + }; + + struct reduce_args_rw_cfg : public default_rewriter_cfg { + ast_manager & m; + reduce_args_simplifier& m_owner; + obj_map & m_decl2args; + decl2arg2func_map & m_decl2arg2funcs; + + reduce_args_rw_cfg(reduce_args_simplifier& owner, obj_map & decl2args, decl2arg2func_map & decl2arg2funcs): + m(owner.m), + m_owner(owner), + m_decl2args(decl2args), + m_decl2arg2funcs(decl2arg2funcs) { + } + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + result_pr = nullptr; + if (f->get_arity() == 0) + return BR_FAILED; // ignore constants + if (f->get_family_id() != null_family_id) + return BR_FAILED; // ignore interpreted symbols + obj_map::iterator it = m_decl2args.find_iterator(f); + if (it == m_decl2args.end()) + return BR_FAILED; + + bit_vector & bv = it->m_value; + arg2func *& map = m_decl2arg2funcs.insert_if_not_there(f, 0); + if (!map) { + map = alloc(arg2func, arg2func_hash_proc(bv), arg2func_eq_proc(bv)); + } + + app_ref tmp(m.mk_app(f, num, args), m); + func_decl *& new_f = map->insert_if_not_there(tmp, nullptr); + if (!new_f) { + // create fresh symbol + ptr_buffer domain; + unsigned arity = f->get_arity(); + for (unsigned i = 0; i < arity; ++i) { + if (!bv.get(i)) + domain.push_back(f->get_domain(i)); + } + new_f = m.mk_fresh_func_decl(f->get_name(), symbol::null, domain.size(), domain.data(), f->get_range()); + m.inc_ref(tmp); + m.inc_ref(new_f); + } + + ptr_buffer new_args; + for (unsigned i = 0; i < num; i++) { + if (!bv.get(i)) + new_args.push_back(args[i]); + } + result = m.mk_app(new_f, new_args.size(), new_args.data()); + return BR_DONE; + } + }; + + struct reduce_args_rw : rewriter_tpl { + reduce_args_rw_cfg m_cfg; + public: + reduce_args_rw(reduce_args_simplifier & owner, obj_map & decl2args, decl2arg2func_map & decl2arg2funcs): + rewriter_tpl(owner.m, false, m_cfg), + m_cfg(owner, decl2args, decl2arg2funcs) { + } + }; + + void mk_mc(obj_map & decl2args, decl2arg2func_map & decl2arg2funcs, vector const& removed) { + ptr_buffer new_args; + var_ref_vector new_vars(m); + ptr_buffer new_eqs; + generic_model_converter * f_mc = alloc(generic_model_converter, m, "reduce_args"); + for (auto const& [f, map] : decl2arg2funcs) + for (auto const& [t, new_def] : *map) + m_fmls.model_trail().hide(new_def); + + vector> defs; + for (auto const& [f, map] : decl2arg2funcs) { + expr * def = nullptr; + SASSERT(decl2args.contains(f)); + bit_vector & bv = decl2args.find(f); + new_vars.reset(); + new_args.reset(); + for (unsigned i = 0; i < f->get_arity(); i++) { + new_vars.push_back(m.mk_var(i, f->get_domain(i))); + if (!bv.get(i)) + new_args.push_back(new_vars.back()); + } + for (auto const& [t, new_def] : *map) { + SASSERT(new_def->get_arity() == new_args.size()); + app * new_t = m.mk_app(new_def, new_args); + if (def == nullptr) { + def = new_t; + } + else { + new_eqs.reset(); + for (unsigned i = 0; i < f->get_arity(); i++) + if (bv.get(i)) + new_eqs.push_back(m.mk_eq(new_vars.get(i), t->get_arg(i))); + SASSERT(new_eqs.size() > 0); + expr * cond = mk_and(m, new_eqs); + def = m.mk_ite(cond, new_t, def); + } + } + SASSERT(def); + expr_dependency* dep = nullptr; + defs.push_back({ func_decl_ref(f,m), expr_ref(def, m), expr_dependency_ref(dep, m) }); + } + m_fmls.model_trail().push(defs, removed); + } + + unsigned m_num_decls = 0; + +public: + reduce_args_simplifier(ast_manager& m, dependent_expr_state& st, params_ref const& p) : + dependent_expr_simplifier(m, st), + m_bv(m) + {} + + ~reduce_args_simplifier() override {} + + char const* name() const override { return "reduce-args"; } + + void collect_statistics(statistics& st) const override { + st.update("reduced-funcs", m_num_decls); + } + + void reset_statistics() override { + m_num_decls = 0; + } + + void reduce() override { + m_fmls.freeze_suffix(); + + obj_hashtable non_candidates; + obj_map decl2args; + find_non_candidates(non_candidates); + populate_decl2args(non_candidates, decl2args); + + if (decl2args.empty()) + return; + + m_num_decls += decl2args.size(); + + reduce_args_ctx ctx(m); + reduce_args_rw rw(*this, decl2args, ctx.m_decl2arg2funcs); + vector removed; + // if not global scope then what? + // cannot just use in incremental mode. + for (auto i : indices()) { + auto [f, p, d] = m_fmls[i](); + if (p) + continue; + expr_ref new_f(m); + rw(f, new_f); + if (f != new_f) { + removed.push_back(m_fmls[i]); + m_fmls.update(i, dependent_expr(m, new_f, p, d)); + } + } + mk_mc(decl2args, ctx.m_decl2arg2funcs, removed); + } + +}; + +dependent_expr_simplifier* mk_reduce_args_simplifier(ast_manager & m, dependent_expr_state& st, params_ref const & p) { + return alloc(reduce_args_simplifier, m, st, p); +} + diff --git a/src/ast/simplifiers/reduce_args_simplifier.h b/src/ast/simplifiers/reduce_args_simplifier.h new file mode 100644 index 00000000000..f6c4dd7851e --- /dev/null +++ b/src/ast/simplifiers/reduce_args_simplifier.h @@ -0,0 +1,16 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + reduce_args_simplifier.h + +Abstract: + + Reduce the number of arguments in function applications. + +--*/ +#pragma once + +dependent_expr_simplifier* mk_reduce_args_simplifier(ast_manager & m, dependent_expr_state& st, params_ref const & p); + diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 47757734570..9f0fae6bcca 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -34,6 +34,7 @@ z3_add_component(lp nla_basics_lemmas.cpp nla_common.cpp nla_core.cpp + nla_divisions.cpp nla_grobner.cpp nla_intervals.cpp nla_monotone_lemmas.cpp diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index b072112f0ab..0ec7d42c01f 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -30,6 +30,7 @@ core::core(lp::lar_solver& s, reslimit & lim) : m_order(this), m_monotone(this), m_powers(*this), + m_divisions(*this), m_intervals(this, lim), m_monomial_bounds(this), m_horner(this), @@ -136,6 +137,11 @@ void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) { } m_emons.add(v, m_add_buffer); } + +void core::add_idivision(lpvar r, lpvar x, lpvar y) { + m_divisions.add_idivision(r, x, y); +} + void core::push() { TRACE("nla_solver_verbose", tout << "\n";); @@ -1519,6 +1525,9 @@ lbool core::check(vector& l_vec) { if (l_vec.empty() && !done()) m_basics.basic_lemma(false); + if (l_vec.empty() && !done()) + m_divisions.check(l_vec); + #if 0 if (l_vec.empty() && !done() && !run_horner) m_horner.horner_lemmas(); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 1e66e4cd79f..c595a0b0fa0 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -20,6 +20,7 @@ #include "math/lp/nla_monotone_lemmas.h" #include "math/lp/nla_grobner.h" #include "math/lp/nla_powers.h" +#include "math/lp/nla_divisions.h" #include "math/lp/emonics.h" #include "math/lp/nla_settings.h" #include "math/lp/nex.h" @@ -88,6 +89,7 @@ class core { order m_order; monotone m_monotone; powers m_powers; + divisions m_divisions; intervals m_intervals; monomial_bounds m_monomial_bounds; nla_settings m_nla_settings; @@ -199,8 +201,10 @@ class core { void deregister_monic_from_tables(const monic & m, unsigned i); void add_monic(lpvar v, unsigned sz, lpvar const* vs); + void add_idivision(lpvar r, lpvar x, lpvar y); void push(); void pop(unsigned n); + trail_stack& trail() { return m_emons.get_trail_stack(); } rational mon_value_by_vars(unsigned i) const; rational product_value(const monic & m) const; diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp new file mode 100644 index 00000000000..dd58fcd59e1 --- /dev/null +++ b/src/math/lp/nla_divisions.cpp @@ -0,0 +1,65 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + nla_divisions.cpp + +Author: + Lev Nachmanson (levnach) + Nikolaj Bjorner (nbjorner) + +Description: + + Check divisions + +--*/ +#include "math/lp/nla_core.h" + +namespace nla { + + void divisions::add_idivision(lpvar r, lpvar x, lpvar y) { + m_idivisions.push_back({r, x, y}); + m_core.trail().push(push_back_vector(m_idivisions)); + } + + typedef lp::lar_term term; + + // y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2 + // y2 <= y1 < 0 & x1 >= x2 => x1/y1 <= x2/y2 + void divisions::check(vector& lemmas) { + core& c = m_core; + if (c.use_nra_model()) + return; + + for (auto const & [r, x, y] : m_idivisions) { + auto xval = c.val(x); + auto yval = c.val(y); + auto rval = c.val(r); + if (!c.var_is_int(x)) + continue; + if (yval == 0) + continue; + // idiv semantics + if (rval == div(xval, yval)) + continue; + for (auto const& [r2, x2, y2] : m_idivisions) { + if (r2 == r) + continue; + auto x2val = c.val(x2); + auto y2val = c.val(y2); + auto r2val = c.val(r2); + if (yval >= y2val && y2val > 0 && xval <= x2val && rval > r2val) { + new_lemma lemma(c, "y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2"); + lemma |= ineq(term(y, rational(-1), y2), llc::LT, rational::zero()); + lemma |= ineq(y2, llc::LE, rational::zero()); + lemma |= ineq(term(x, rational(-1), x2), llc::GT, rational::zero()); + lemma |= ineq(term(r, rational(-1), r2), llc::LE, rational::zero()); + return; + } + } + } + + } + +} diff --git a/src/math/lp/nla_divisions.h b/src/math/lp/nla_divisions.h new file mode 100644 index 00000000000..8eb5c676b57 --- /dev/null +++ b/src/math/lp/nla_divisions.h @@ -0,0 +1,31 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + nla_divisions.h + +Author: + Lev Nachmanson (levnach) + Nikolaj Bjorner (nbjorner) + +Description: + Check division constraints. + +--*/ + +#include "math/lp/nla_types.h" + +namespace nla { + + class core; + + class divisions { + core& m_core; + vector> m_idivisions; + public: + divisions(core& c):m_core(c) {} + void add_idivision(lpvar r, lpvar x, lpvar y); + void check(vector&); + }; +} diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index b5a030a92e3..eb7e5283dc1 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -22,6 +22,10 @@ namespace nla { void solver::add_monic(lpvar v, unsigned sz, lpvar const* vs) { m_core->add_monic(v, sz, vs); } + + void solver::add_idivision(lpvar r, lpvar x, lpvar y) { + m_core->add_idivision(r, x, y); + } bool solver::is_monic_var(lpvar v) const { return m_core->is_monic_var(v); diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index f372410d816..9379939dbbf 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -25,6 +25,7 @@ namespace nla { core* m_core; public: void add_monic(lpvar v, unsigned sz, lpvar const* vs); + void add_idivision(lpvar r, lpvar x, lpvar y); solver(lp::lar_solver& s, reslimit& limit); ~solver(); nla_settings& settings(); diff --git a/src/math/lp/var_eqs.h b/src/math/lp/var_eqs.h index 5a2eb5b5f09..998779dc61a 100644 --- a/src/math/lp/var_eqs.h +++ b/src/math/lp/var_eqs.h @@ -68,8 +68,7 @@ class var_eqs { T* m_merge_handler; union_find m_uf; - lp::incremental_vector> - m_trail; + lp::incremental_vector> m_trail; vector> m_eqs; // signed_var.index() -> the edges adjacent to signed_var.index() trail_stack m_stack; diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 8d99153dac0..4e5f8fd37e2 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -161,7 +161,6 @@ class sat_smt_solver : public solver { expr_ref_vector m_assumptions, m_core, m_ors, m_aux_fmls, m_internalized_fmls; atom2bool_var m_map; generic_model_converter_ref m_mc; - unsigned m_mc_size = 0; mutable model_converter_ref m_cached_mc; mutable ref m_sat_mc; std::string m_unknown = "no reason given"; @@ -180,8 +179,7 @@ class sat_smt_solver : public solver { m_trail(m_preprocess_state.m_trail), m_dep(m, m_trail), m_assumptions(m), m_core(m), m_ors(m), m_aux_fmls(m), m_internalized_fmls(m), - m_map(m), - m_mc(alloc(generic_model_converter, m, "sat-smt-solver")) { + m_map(m) { updt_params(p); init_preprocess(); m_solver.set_incremental(true); @@ -211,7 +209,6 @@ class sat_smt_solver : public solver { for (expr* f : m_internalized_fmls) result->m_internalized_fmls.push_back(tr(f)); if (m_mc) result->m_mc = dynamic_cast(m_mc->translate(tr)); result->m_dep.copy(tr, m_dep); - result->m_mc_size = m_mc_size; if (m_sat_mc) result->m_sat_mc = dynamic_cast(m_sat_mc->translate(tr)); result->m_internalized_converted = m_internalized_converted; return result; @@ -291,7 +288,6 @@ class sat_smt_solver : public solver { m_preprocess.push(); m_trail.push(restore_vector(m_assumptions)); m_trail.push(restore_vector(m_fmls)); - m_trail.push(value_trail(m_mc_size)); } void pop(unsigned n) override { @@ -302,7 +298,6 @@ class sat_smt_solver : public solver { m_map.pop(n); m_goal2sat.user_pop(n); m_solver.user_pop(n); - m_mc->shrink(m_mc_size); } void set_phase(expr* e) override { @@ -549,6 +544,7 @@ class sat_smt_solver : public solver { model_converter_ref get_model_converter() const override { const_cast(this)->convert_internalized(); + verbose_stream() << "get model converter " << (m_cached_mc.get() != nullptr) << "\n"; if (m_cached_mc) return m_cached_mc; if (is_internalized() && m_internalized_converted) { @@ -660,6 +656,7 @@ class sat_smt_solver : public solver { if (!m.inc()) return l_undef; m_preprocess_state.advance_qhead(); + m_mc = alloc(generic_model_converter, m, "sat-model-converter"); m_preprocess_state.append(*m_mc); m_solver.pop_to_base_level(); m_aux_fmls.reset(); @@ -754,42 +751,43 @@ class sat_smt_solver : public solver { if (m_sat_mc) (*m_sat_mc)(mdl); m_goal2sat.update_model(mdl); - TRACE("sat", m_mc->display(tout);); - (*m_mc)(mdl); + TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); - if (!gparams::get_ref().get_bool("model_validate", false)) - return; - IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";); - model_evaluator eval(*mdl); - eval.set_model_completion(true); - bool all_true = true; - for (dependent_expr const& d : m_fmls) { - if (has_quantifiers(d.fml())) - continue; - expr_ref tmp(m); - eval(d.fml(), tmp); - if (m.limit().is_canceled()) - return; - CTRACE("sat", !m.is_true(tmp), - tout << "Evaluation failed: " << mk_pp(d.fml(), m) << " to " << tmp << "\n"; - model_smt2_pp(tout, m, *(mdl.get()), 0);); - if (m.is_false(tmp)) { - IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(d.fml(), m) << "\n"); - IF_VERBOSE(0, verbose_stream() << "evaluated to " << tmp << "\n"); - all_true = false; + if (gparams::get_ref().get_bool("model_validate", false)) { + IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";); + model_evaluator eval(*mdl); + eval.set_model_completion(true); + bool all_true = true; + for (dependent_expr const& d : m_fmls) { + if (has_quantifiers(d.fml())) + continue; + expr_ref tmp(m); + eval(d.fml(), tmp); + if (m.limit().is_canceled()) + return; + CTRACE("sat", !m.is_true(tmp), + tout << "Evaluation failed: " << mk_pp(d.fml(), m) << " to " << tmp << "\n"; + model_smt2_pp(tout, m, *(mdl.get()), 0);); + if (m.is_false(tmp)) { + IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(d.fml(), m) << "\n"); + IF_VERBOSE(0, verbose_stream() << "evaluated to " << tmp << "\n"); + all_true = false; + } + } + if (!all_true) { + IF_VERBOSE(0, verbose_stream() << m_params << "\n"); + IF_VERBOSE(0, if (m_mc) m_mc->display(verbose_stream() << "mc0\n")); + IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); + exit(0); + } + else { + IF_VERBOSE(1, verbose_stream() << "solution verified\n"); } } - if (!all_true) { - IF_VERBOSE(0, verbose_stream() << m_params << "\n"); - IF_VERBOSE(0, if (m_mc) m_mc->display(verbose_stream() << "mc0\n")); - IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); - exit(0); - } - else { - IF_VERBOSE(1, verbose_stream() << "solution verified\n"); - } + TRACE("sat", m_mc->display(tout);); + (*m_mc)(mdl); } }; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 29f34655a89..880026610ae 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -435,6 +435,13 @@ class theory_lra::imp { app_ref mod(a.mk_mod(n1, n2), m); ctx().internalize(mod, false); if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); +#if 0 + // shortcut to create non-linear division axioms. + theory_var r = mk_var(n); + theory_var x = mk_var(n1); + theory_var y = mk_var(n2); + m_nla->add_idivision(get_lpvar(n), get_lpvar(n1), get_lpvar(n2)); +#endif } else if (a.is_mod(n, n1, n2)) { if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n); diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index d31fb838770..0a57e6b7077 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -18,6 +18,7 @@ Module Name: --*/ #include "tactic/tactical.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_util.h" #include "ast/array_decl_plugin.h" #include "ast/has_free_vars.h" #include "util/map.h" @@ -397,7 +398,7 @@ struct reduce_args_tactic::imp { ptr_buffer new_args; var_ref_vector new_vars(m); ptr_buffer new_eqs; - generic_model_converter * f_mc = alloc(generic_model_converter, m, "reduce_args"); + generic_model_converter * f_mc = alloc(generic_model_converter, m, "reduce_args"); for (auto const& [f, map] : decl2arg2funcs) for (auto const& [t, new_def] : *map) f_mc->hide(new_def); @@ -414,7 +415,6 @@ struct reduce_args_tactic::imp { new_args.push_back(new_vars.back()); } for (auto const& [t, new_def] : *map) { - // f_mc->hide(new_def); SASSERT(new_def->get_arity() == new_args.size()); app * new_t = m.mk_app(new_def, new_args); if (def == nullptr) { @@ -427,11 +427,7 @@ struct reduce_args_tactic::imp { new_eqs.push_back(m.mk_eq(new_vars.get(i), t->get_arg(i))); } SASSERT(new_eqs.size() > 0); - expr * cond; - if (new_eqs.size() == 1) - cond = new_eqs[0]; - else - cond = m.mk_and(new_eqs); + expr * cond = mk_and(m, new_eqs); def = m.mk_ite(cond, new_t, def); } } diff --git a/src/tactic/core/reduce_args_tactic.h b/src/tactic/core/reduce_args_tactic.h index 749aadd9c06..fa90837bfde 100644 --- a/src/tactic/core/reduce_args_tactic.h +++ b/src/tactic/core/reduce_args_tactic.h @@ -63,6 +63,8 @@ It creates a fresh function for each of the different values at position `i`. #pragma once #include "util/params.h" +#include "ast/simplifiers/reduce_args_simplifier.h" +#include "tactic/dependent_expr_state_tactic.h" class ast_manager; class tactic; @@ -71,3 +73,11 @@ tactic * mk_reduce_args_tactic(ast_manager & m, params_ref const & p = params_re ADD_TACTIC("reduce-args", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "mk_reduce_args_tactic(m, p)") */ +inline tactic* mk_reduce_args_tactic2(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* { return mk_reduce_args_simplifier(m, s, p); }); +} +/* + ADD_TACTIC("reduce-args2", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "mk_reduce_args_tactic2(m, p)") +*/ + diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 931fc7c26c4..347e147fb33 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -23,13 +23,14 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { public: using factoryTy = dependent_expr_simplifier(*(*)(ast_manager& m, params_ref const& p, dependent_expr_state& s)); private: - ast_manager& m; + ast_manager& m; params_ref m_params; trail_stack m_trail; goal_ref m_goal; dependent_expr m_dep; statistics m_st; factoryTy m_factory; + expr_ref_vector m_frozen; scoped_ptr m_simp; scoped_ptr m_model_trail; @@ -37,6 +38,9 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { if (!m_simp) { m_simp = m_factory(m, m_params, *this); m_st.reset(); + push(); + for (expr* e : m_frozen) + freeze(e); } if (!m_model_trail) m_model_trail = alloc(model_reconstruction_trail, m, m_trail); @@ -44,14 +48,20 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { public: - dependent_expr_state_tactic(ast_manager& m, params_ref const& p, factoryTy f): + dependent_expr_state_tactic(ast_manager& m, params_ref const& p, factoryTy f) : dependent_expr_state(m), m(m), m_params(p), m_dep(m, m.mk_true(), nullptr, nullptr), - m_factory(f) + m_factory(f), + m_frozen(m) {} + ~dependent_expr_state_tactic() override { + if (m_simp) + pop(1); + } + /** * size(), [](), update() and inconsisent() implement the abstract interface of dependent_expr_state */ @@ -61,14 +71,14 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { m_dep = dependent_expr(m, m_goal->form(i), m_goal->pr(i), m_goal->dep(i)); return m_dep; } - + void update(unsigned i, dependent_expr const& j) override { if (inconsistent()) return; auto [f, p, d] = j(); m_goal->update(i, f, p, d); } - + void add(dependent_expr const& j) override { if (inconsistent()) return; @@ -83,10 +93,10 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { model_reconstruction_trail& model_trail() override { return *m_model_trail; } - - char const* name() const override { return m_simp?m_simp->name():"null"; } - void updt_params(params_ref const & p) override { + char const* name() const override { return m_simp ? m_simp->name() : "null"; } + + void updt_params(params_ref const& p) override { m_params.append(p); init(); m_simp->updt_params(m_params); @@ -97,12 +107,12 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { m_simp->collect_param_descrs(r); } - tactic * translate(ast_manager & m) override { + tactic* translate(ast_manager& m) override { return alloc(dependent_expr_state_tactic, m, m_params, m_factory); } - void operator()(goal_ref const & in, - goal_ref_buffer & result) override { + void operator()(goal_ref const& in, + goal_ref_buffer& result) override { init(); statistics_report sreport(*this); tactic_report report(name(), *in); @@ -124,25 +134,39 @@ class dependent_expr_state_tactic : public tactic, public dependent_expr_state { } void cleanup() override { - if (m_simp) + if (m_simp) { m_simp->collect_statistics(m_st); + pop(1); + } m_simp = nullptr; m_model_trail = nullptr; m_goal = nullptr; m_dep = dependent_expr(m, m.mk_true(), nullptr, nullptr); } - void collect_statistics(statistics & st) const override { - if (m_simp) + void collect_statistics(statistics& st) const override { + 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(); } -}; + void user_propagate_register_expr(expr* e) override { + freeze(e); + m_frozen.push_back(e); + } + + void user_propagate_clear() override { + if (m_simp) { + pop(1); + push(); + } + m_frozen.reset(); + } +}; diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 31cb0028160..cb29bdd9cc6 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -211,6 +211,22 @@ class bit_vector { bool contains(const bit_vector & other) const; + class iterator { + bit_vector const& b; + unsigned m_curr; + public: + iterator(bit_vector const& b, unsigned i) : b(b), m_curr(i) {} + bool operator*(unsigned i) const { return b.get(m_curr); } + bool operator*() const { return b.get(m_curr); } + iterator& operator++() { ++m_curr; return *this; } + iterator operator++(int) { iterator tmp = *this; ++* this; return tmp; } + bool operator==(iterator const& it) const { return m_curr == it.m_curr; } + bool operator!=(iterator const& it) const { return m_curr != it.m_curr; } + }; + + iterator begin() const { return iterator(*this, 0); } + iterator end() const { return iterator(*this, size()); } + }; inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) { From 4ffe3fab050f0f85e6f9cd3a34767dc28baa8aeb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 28 Jan 2023 21:51:51 -0800 Subject: [PATCH 341/597] fix build Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/model_reconstruction_trail.cpp | 2 +- src/ast/simplifiers/reduce_args_simplifier.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 893af71065c..82d56f49117 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -69,7 +69,7 @@ void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumpt args.push_back(m.mk_var(i, d->get_domain(i))); head = m.mk_app(d, args); mrp.insert(head, def, dep); - TRACE("simplifier", tout << d << " " << def << " " << dep << "\n"); + TRACE("simplifier", tout << mk_pp(d, m) << " " << mk_pp(def,m) << " " << "\n"); dependent_expr de(m, def, nullptr, dep); add_vars(de, free_vars); } diff --git a/src/ast/simplifiers/reduce_args_simplifier.cpp b/src/ast/simplifiers/reduce_args_simplifier.cpp index 4ebd4912752..f9c7887920a 100644 --- a/src/ast/simplifiers/reduce_args_simplifier.cpp +++ b/src/ast/simplifiers/reduce_args_simplifier.cpp @@ -328,7 +328,6 @@ class reduce_args_simplifier : public dependent_expr_simplifier { ptr_buffer new_args; var_ref_vector new_vars(m); ptr_buffer new_eqs; - generic_model_converter * f_mc = alloc(generic_model_converter, m, "reduce_args"); for (auto const& [f, map] : decl2arg2funcs) for (auto const& [t, new_def] : *map) m_fmls.model_trail().hide(new_def); From 8e37e2f913ab949123643a327e1d6a9866379a41 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 29 Jan 2023 17:22:57 -0800 Subject: [PATCH 342/597] handle non-linear division axioms, consolidate backtracking state in nla_core this update enables new incremental linear axioms based on division terms. It also consolidates some of the backtracking state in nla_core / emons to use stack traces instead of custom backtracking state. --- src/math/lp/emonics.cpp | 101 +++++++++++++++---------------- src/math/lp/emonics.h | 6 +- src/math/lp/nla_core.cpp | 11 ++-- src/math/lp/nla_core.h | 9 ++- src/math/lp/nla_divisions.cpp | 111 ++++++++++++++++++++++++++++++---- src/math/lp/nla_divisions.h | 2 + src/math/lp/nla_solver.cpp | 8 +++ src/math/lp/nla_solver.h | 8 ++- src/smt/theory_lra.cpp | 21 +++++-- 9 files changed, 196 insertions(+), 81 deletions(-) diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index 060c2b1b926..21bfe679234 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -40,43 +40,39 @@ void emonics::push() { TRACE("nla_solver_mons", display(tout << "push\n");); SASSERT(invariant()); m_u_f_stack.push_scope(); - m_lim.push_back(m_monics.size()); - m_region.push_scope(); m_ve.push(); SASSERT(monics_are_canonized()); SASSERT(invariant()); } +void emonics::pop_monic() { + m_ve.pop(1); + monic& m = m_monics.back(); + TRACE("nla_solver_mons", display(tout << m << "\n");); + remove_cg_mon(m); + m_var2index[m.var()] = UINT_MAX; + do_canonize(m); + // variables in vs are in the same state as they were when add was called + lpvar last_var = UINT_MAX; + for (lpvar v : m.rvars()) { + if (v != last_var) { + remove_cell(m_use_lists[v]); + last_var = v; + } + } + m_ve.pop(1); + m_monics.pop_back(); +} + void emonics::pop(unsigned n) { TRACE("nla_solver_mons", tout << "pop: " << n << "\n";); SASSERT(invariant()); - for (unsigned j = 0; j < n; ++j) { - unsigned old_sz = m_lim[m_lim.size() - 1]; - for (unsigned i = m_monics.size(); i-- > old_sz; ) { - m_ve.pop(1); - monic & m = m_monics[i]; - TRACE("nla_solver_mons", display(tout << m << "\n");); - remove_cg_mon(m); - m_var2index[m.var()] = UINT_MAX; - do_canonize(m); - // variables in vs are in the same state as they were when add was called - lpvar last_var = UINT_MAX; - for (lpvar v : m.rvars()) { - if (v != last_var) { - remove_cell(m_use_lists[v]); - last_var = v; - } - } - m_ve.pop(1); - } - m_ve.pop(1); - m_monics.shrink(old_sz); - m_region.pop_scope(1); - m_lim.pop_back(); + for (unsigned i = 0; i < n; ++i) { m_u_f_stack.pop_scope(1); - SASSERT(invariant()); - SASSERT(monics_are_canonized()); + m_ve.pop(1); } + SASSERT(invariant()); + SASSERT(monics_are_canonized()); } void emonics::remove_cell(head_tail& v) { @@ -96,7 +92,7 @@ void emonics::remove_cell(head_tail& v) { void emonics::insert_cell(head_tail& v, unsigned mIndex) { cell*& cur_head = v.m_head; cell*& cur_tail = v.m_tail; - cell* new_head = new (m_region) cell(mIndex, cur_head); + cell* new_head = new (m_u_f_stack.get_region()) cell(mIndex, cur_head); cur_head = new_head; if (!cur_tail) cur_tail = new_head; cur_tail->m_next = new_head; @@ -331,6 +327,14 @@ void emonics::add(lpvar v, unsigned sz, lpvar const* vs) { m_monics.push_back(monic(v, sz, vs, idx)); do_canonize(m_monics.back()); + class pop_mon : public trail { + emonics& p; + public: + pop_mon(emonics& p) :p(p) {} + void undo() override { p.pop_monic(); } + }; + m_u_f_stack.push(pop_mon(*this)); + // variables in m_vs are canonical and sorted, // so use last_var to skip duplicates, // while updating use-lists @@ -351,9 +355,8 @@ void emonics::add(lpvar v, unsigned sz, lpvar const* vs) { void emonics::do_canonize(monic & m) const { TRACE("nla_solver_mons", tout << m << "\n";); m.reset_rfields(); - for (lpvar v : m.vars()) { - m.push_rvar(m_ve.find(v)); - } + for (lpvar v : m.vars()) + m.push_rvar(m_ve.find(v)); m.sort_rvars(); TRACE("nla_solver_mons", tout << m << "\n";); } @@ -365,40 +368,34 @@ bool emonics::is_canonized(const monic & m) const { } void emonics::ensure_canonized() { - for (auto & m : m_monics) { - do_canonize(m); - } + for (auto & m : m_monics) + do_canonize(m); } bool emonics::monics_are_canonized() const { - for (auto & m: m_monics) { - if (!is_canonized(m)) { - return false; - } - } + for (auto & m: m_monics) + if (!is_canonized(m)) + return false; return true; } bool emonics::canonize_divides(monic& m, monic & n) const { - if (m.size() > n.size()) return false; + if (m.size() > n.size()) + return false; unsigned ms = m.size(), ns = n.size(); unsigned i = 0, j = 0; while (true) { - if (i == ms) { - return true; - } - else if (j == ns) { - return false; - } + if (i == ms) + return true; + else if (j == ns) + return false; else if (m.rvars()[i] == n.rvars()[j]) { ++i; ++j; } - else if (m.rvars()[i] < n.rvars()[j]) { - return false; - } - else { - ++j; - } + else if (m.rvars()[i] < n.rvars()[j]) + return false; + else + ++j; } } diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index 0d0c20fcb39..d26d96ad66f 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -87,15 +87,15 @@ class emonics { var_eqs& m_ve; mutable vector m_monics; // set of monics mutable unsigned_vector m_var2index; // var_mIndex -> mIndex - unsigned_vector m_lim; // backtracking point mutable unsigned m_visited; // timestamp of visited monics during pf_iterator - region m_region; // region for allocating linked lists mutable svector m_use_lists; // use list of monics where variables occur. hash_canonical m_cg_hash; eq_canonical m_cg_eq; map m_cg_table; // congruence (canonical) table. + void pop_monic(); + void inc_visited() const; void remove_cell(head_tail& v); @@ -115,6 +115,8 @@ class emonics { std::ostream& display_use(std::ostream& out) const; std::ostream& display_uf(std::ostream& out) const; std::ostream& display(std::ostream& out, cell* c) const; + + public: unsigned number_of_monics() const { return m_monics.size(); } /** diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 0ec7d42c01f..e00bfd1b051 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -137,11 +137,6 @@ void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) { } m_emons.add(v, m_add_buffer); } - -void core::add_idivision(lpvar r, lpvar x, lpvar y) { - m_divisions.add_idivision(r, x, y); -} - void core::push() { TRACE("nla_solver_verbose", tout << "\n";); @@ -164,7 +159,13 @@ rational core::product_value(const monic& m) const { } // return true iff the monic value is equal to the product of the values of the factors +// or if the variable associated with the monomial is not relevant. bool core::check_monic(const monic& m) const { +#if 0 + // TODO test this + if (!is_relevant(m.var())) + return true; +#endif SASSERT((!m_lar_solver.column_is_int(m.var())) || m_lar_solver.get_column_value(m.var()).is_int()); bool ret = product_value(m) == m_lar_solver.get_column_value(m.var()).x; CTRACE("nla_solver_check_monic", !ret, print_monic(m, tout) << '\n';); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index c595a0b0fa0..eedc671f5b8 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -82,6 +82,7 @@ class core { lp::lar_solver& m_lar_solver; reslimit& m_reslim; + std::function m_relevant; vector * m_lemma_vec; lp::u_set m_to_refine; tangents m_tangents; @@ -201,9 +202,15 @@ class core { void deregister_monic_from_tables(const monic & m, unsigned i); void add_monic(lpvar v, unsigned sz, lpvar const* vs); - void add_idivision(lpvar r, lpvar x, lpvar y); + void add_idivision(lpvar r, lpvar x, lpvar y) { m_divisions.add_idivision(r, x, y); } + void add_rdivision(lpvar r, lpvar x, lpvar y) { m_divisions.add_rdivision(r, x, y); } + + void set_relevant(std::function& is_relevant) { m_relevant = is_relevant; } + bool is_relevant(lpvar v) const { return !m_relevant || m_relevant(v); } + void push(); void pop(unsigned n); + trail_stack& trail() { return m_emons.get_trail_stack(); } rational mon_value_by_vars(unsigned i) const; diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index dd58fcd59e1..ce875b9c49d 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -23,43 +23,128 @@ namespace nla { m_core.trail().push(push_back_vector(m_idivisions)); } + void divisions::add_rdivision(lpvar r, lpvar x, lpvar y) { + m_rdivisions.push_back({ r, x, y }); + m_core.trail().push(push_back_vector(m_rdivisions)); + } + typedef lp::lar_term term; // y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2 - // y2 <= y1 < 0 & x1 >= x2 => x1/y1 <= x2/y2 + // y2 <= y1 < 0 & x1 >= x2 >= 0 => x1/y1 <= x2/y2 + // y2 <= y1 < 0 & x1 <= x2 <= 0 => x1/y1 >= x2/y2 + void divisions::check(vector& lemmas) { core& c = m_core; if (c.use_nra_model()) return; + auto monotonicity1 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { + if (y1val >= y2val && y2val > 0 && x1val <= x2val && r1val > r2val) { + new_lemma lemma(c, "y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2"); + lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); + lemma |= ineq(y2, llc::LE, 0); + lemma |= ineq(term(x1, rational(-1), x2), llc::GT, 0); + lemma |= ineq(term(r1, rational(-1), r2), llc::LE, 0); + return true; + } + return false; + }; + + auto monotonicity2 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { + if (y2val <= y1val && y1val < 0 && x1val >= x2val && x2val >= 0 && r1val > r2val) { + new_lemma lemma(c, "y2 <= y1 < 0 & x1 >= x2 >= 0 => x1/y1 <= x2/y2"); + lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); + lemma |= ineq(y1, llc::GE, 0); + lemma |= ineq(term(x1, rational(-1), x2), llc::LT, 0); + lemma |= ineq(x2, llc::LT, 0); + lemma |= ineq(term(r1, rational(-1), r2), llc::LE, 0); + return true; + } + return false; + }; + + auto monotonicity3 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { + if (y2val <= y1val && y1val < 0 && x1val <= x2val && x2val <= 0 && r1val < r2val) { + new_lemma lemma(c, "y2 <= y1 < 0 & x1 <= x2 <= 0 => x1/y1 >= x2/y2"); + lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); + lemma |= ineq(y1, llc::GE, 0); + lemma |= ineq(term(x1, rational(-1), x2), llc::GT, 0); + lemma |= ineq(x2, llc::GT, 0); + lemma |= ineq(term(r1, rational(-1), r2), llc::GE, 0); + return true; + } + return false; + }; + + auto monotonicity = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { + if (monotonicity1(x1, x1val, y1, y1val, r1, r1val, x2, x2val, y2, y2val, r2, r2val)) + return true; + if (monotonicity1(x2, x2val, y2, y2val, r2, r2val, x1, x1val, y1, y1val, r1, r1val)) + return true; + if (monotonicity2(x1, x1val, y1, y1val, r1, r1val, x2, x2val, y2, y2val, r2, r2val)) + return true; + if (monotonicity2(x2, x2val, y2, y2val, r2, r2val, x1, x1val, y1, y1val, r1, r1val)) + return true; + if (monotonicity3(x1, x1val, y1, y1val, r1, r1val, x2, x2val, y2, y2val, r2, r2val)) + return true; + if (monotonicity3(x2, x2val, y2, y2val, r2, r2val, x1, x1val, y1, y1val, r1, r1val)) + return true; + return false; + }; + for (auto const & [r, x, y] : m_idivisions) { + if (!c.is_relevant(r)) + continue; auto xval = c.val(x); auto yval = c.val(y); auto rval = c.val(r); - if (!c.var_is_int(x)) - continue; - if (yval == 0) - continue; // idiv semantics - if (rval == div(xval, yval)) + if (!xval.is_int() || !yval.is_int() || yval == 0 || rval == div(xval, yval)) continue; for (auto const& [r2, x2, y2] : m_idivisions) { if (r2 == r) continue; + if (!c.is_relevant(r2)) + continue; auto x2val = c.val(x2); auto y2val = c.val(y2); auto r2val = c.val(r2); - if (yval >= y2val && y2val > 0 && xval <= x2val && rval > r2val) { - new_lemma lemma(c, "y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2"); - lemma |= ineq(term(y, rational(-1), y2), llc::LT, rational::zero()); - lemma |= ineq(y2, llc::LE, rational::zero()); - lemma |= ineq(term(x, rational(-1), x2), llc::GT, rational::zero()); - lemma |= ineq(term(r, rational(-1), r2), llc::LE, rational::zero()); + if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, r2, r2val)) + return; + } + } + + for (auto const& [r, x, y] : m_rdivisions) { + if (!c.is_relevant(r)) + continue; + auto xval = c.val(x); + auto yval = c.val(y); + auto rval = c.val(r); + // / semantics + if (yval == 0 || rval == xval / yval) + continue; + for (auto const& [r2, x2, y2] : m_rdivisions) { + if (r2 == r) + continue; + if (!c.is_relevant(r2)) + continue; + auto x2val = c.val(x2); + auto y2val = c.val(y2); + auto r2val = c.val(r2); + if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, r2, r2val)) return; - } } } } + + // if p is bounded, q a value, r = eval(p): + // p <= q * div(r, q) + q - 1 => div(p, q) <= div(r, q) + // p >= q * div(r, q) => div(r, q) <= div(p, q) } diff --git a/src/math/lp/nla_divisions.h b/src/math/lp/nla_divisions.h index 8eb5c676b57..56056e1f468 100644 --- a/src/math/lp/nla_divisions.h +++ b/src/math/lp/nla_divisions.h @@ -23,9 +23,11 @@ namespace nla { class divisions { core& m_core; vector> m_idivisions; + vector> m_rdivisions; public: divisions(core& c):m_core(c) {} void add_idivision(lpvar r, lpvar x, lpvar y); + void add_rdivision(lpvar r, lpvar x, lpvar y); void check(vector&); }; } diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index eb7e5283dc1..50842f3336c 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -26,6 +26,14 @@ namespace nla { void solver::add_idivision(lpvar r, lpvar x, lpvar y) { m_core->add_idivision(r, x, y); } + + void solver::add_rdivision(lpvar r, lpvar x, lpvar y) { + m_core->add_rdivision(r, x, y); + } + + void solver::set_relevant(std::function& is_relevant) { + m_core->set_relevant(is_relevant); + } bool solver::is_monic_var(lpvar v) const { return m_core->is_monic_var(v); diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 9379939dbbf..6667dff31fc 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -24,10 +24,14 @@ namespace nla { class solver { core* m_core; public: - void add_monic(lpvar v, unsigned sz, lpvar const* vs); - void add_idivision(lpvar r, lpvar x, lpvar y); + solver(lp::lar_solver& s, reslimit& limit); ~solver(); + + void add_monic(lpvar v, unsigned sz, lpvar const* vs); + void add_idivision(lpvar r, lpvar x, lpvar y); + void add_rdivision(lpvar r, lpvar x, lpvar y); + void set_relevant(std::function& is_relevant); nla_settings& settings(); void push(); void pop(unsigned scopes); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 880026610ae..8a9f41e8707 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -275,6 +275,11 @@ class theory_lra::imp { (void)_s; m_nla->push(); } + std::function is_relevant = [&](lpvar v) { + theory_var u = lp().local_to_external(v); + return ctx().is_relevant(th.get_enode(u)); + }; + m_nla->set_relevant(is_relevant); smt_params_helper prms(ctx().get_params()); m_nla->settings().run_order = prms.arith_nl_order(); m_nla->settings().run_tangents = prms.arith_nl_tangents(); @@ -435,12 +440,14 @@ class theory_lra::imp { app_ref mod(a.mk_mod(n1, n2), m); ctx().internalize(mod, false); if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); -#if 0 - // shortcut to create non-linear division axioms. - theory_var r = mk_var(n); - theory_var x = mk_var(n1); - theory_var y = mk_var(n2); - m_nla->add_idivision(get_lpvar(n), get_lpvar(n1), get_lpvar(n2)); +#if 1 + if (m_nla && !a.is_numeral(n2)) { + // shortcut to create non-linear division axioms. + theory_var r = mk_var(n); + theory_var x = mk_var(n1); + theory_var y = mk_var(n2); + m_nla->add_idivision(get_lpvar(n), get_lpvar(n1), get_lpvar(n2)); + } #endif } else if (a.is_mod(n, n1, n2)) { @@ -1801,6 +1808,8 @@ class theory_lra::imp { for (unsigned j = 0; j < m_idiv_terms.size(); ++j) { unsigned i = (offset + j) % m_idiv_terms.size(); expr* n = m_idiv_terms[i]; + if (!ctx().is_relevant(n)) + continue; expr* p = nullptr, *q = nullptr; VERIFY(a.is_idiv(n, p, q)); theory_var v = internalize_def(to_app(n)); From 2c4a9c2f5c8aee2001ca76ddbf0eaa8e87dd0fa4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2023 08:20:26 -0800 Subject: [PATCH 343/597] fix division filter Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_divisions.cpp | 172 +++++++++++++++++++++++++++------- src/math/lp/nla_divisions.h | 6 +- src/smt/theory_lra.cpp | 2 +- 3 files changed, 143 insertions(+), 37 deletions(-) diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index ce875b9c49d..06122ddc84c 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -18,13 +18,22 @@ Module Name: namespace nla { - void divisions::add_idivision(lpvar r, lpvar x, lpvar y) { - m_idivisions.push_back({r, x, y}); + void divisions::add_idivision(lpvar q, lpvar x, lpvar y) { + if (x == null_lpvar || y == null_lpvar || q == null_lpvar) + return; + if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(q)) + return; + verbose_stream() << q << " " << x << " " << y << " " << null_lpvar << "\n"; + m_idivisions.push_back({q, x, y}); m_core.trail().push(push_back_vector(m_idivisions)); } - void divisions::add_rdivision(lpvar r, lpvar x, lpvar y) { - m_rdivisions.push_back({ r, x, y }); + void divisions::add_rdivision(lpvar q, lpvar x, lpvar y) { + if (x == null_lpvar || y == null_lpvar || q == null_lpvar) + return; + if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(q)) + return; + m_rdivisions.push_back({ q, x, y }); m_core.trail().push(push_back_vector(m_rdivisions)); } @@ -39,60 +48,60 @@ namespace nla { if (c.use_nra_model()) return; - auto monotonicity1 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, - auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { - if (y1val >= y2val && y2val > 0 && x1val <= x2val && r1val > r2val) { + auto monotonicity1 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& q1, auto& q1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& q2, auto& q2val) { + if (y1val >= y2val && y2val > 0 && x1val <= x2val && q1val > q2val) { new_lemma lemma(c, "y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2"); lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); lemma |= ineq(y2, llc::LE, 0); lemma |= ineq(term(x1, rational(-1), x2), llc::GT, 0); - lemma |= ineq(term(r1, rational(-1), r2), llc::LE, 0); + lemma |= ineq(term(q1, rational(-1), q2), llc::LE, 0); return true; } return false; }; - auto monotonicity2 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, - auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { - if (y2val <= y1val && y1val < 0 && x1val >= x2val && x2val >= 0 && r1val > r2val) { + auto monotonicity2 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& q1, auto& q1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& q2, auto& q2val) { + if (y2val <= y1val && y1val < 0 && x1val >= x2val && x2val >= 0 && q1val > q2val) { new_lemma lemma(c, "y2 <= y1 < 0 & x1 >= x2 >= 0 => x1/y1 <= x2/y2"); lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); lemma |= ineq(y1, llc::GE, 0); lemma |= ineq(term(x1, rational(-1), x2), llc::LT, 0); lemma |= ineq(x2, llc::LT, 0); - lemma |= ineq(term(r1, rational(-1), r2), llc::LE, 0); + lemma |= ineq(term(q1, rational(-1), q2), llc::LE, 0); return true; } return false; }; - auto monotonicity3 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, - auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { - if (y2val <= y1val && y1val < 0 && x1val <= x2val && x2val <= 0 && r1val < r2val) { + auto monotonicity3 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& q1, auto& q1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& q2, auto& q2val) { + if (y2val <= y1val && y1val < 0 && x1val <= x2val && x2val <= 0 && q1val < q2val) { new_lemma lemma(c, "y2 <= y1 < 0 & x1 <= x2 <= 0 => x1/y1 >= x2/y2"); lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); lemma |= ineq(y1, llc::GE, 0); lemma |= ineq(term(x1, rational(-1), x2), llc::GT, 0); lemma |= ineq(x2, llc::GT, 0); - lemma |= ineq(term(r1, rational(-1), r2), llc::GE, 0); + lemma |= ineq(term(q1, rational(-1), q2), llc::GE, 0); return true; } return false; }; - auto monotonicity = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& r1, auto& r1val, - auto x2, auto& x2val, auto y2, auto& y2val, auto& r2, auto& r2val) { - if (monotonicity1(x1, x1val, y1, y1val, r1, r1val, x2, x2val, y2, y2val, r2, r2val)) + auto monotonicity = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& q1, auto& q1val, + auto x2, auto& x2val, auto y2, auto& y2val, auto& q2, auto& q2val) { + if (monotonicity1(x1, x1val, y1, y1val, q1, q1val, x2, x2val, y2, y2val, q2, q2val)) return true; - if (monotonicity1(x2, x2val, y2, y2val, r2, r2val, x1, x1val, y1, y1val, r1, r1val)) + if (monotonicity1(x2, x2val, y2, y2val, q2, q2val, x1, x1val, y1, y1val, q1, q1val)) return true; - if (monotonicity2(x1, x1val, y1, y1val, r1, r1val, x2, x2val, y2, y2val, r2, r2val)) + if (monotonicity2(x1, x1val, y1, y1val, q1, q1val, x2, x2val, y2, y2val, q2, q2val)) return true; - if (monotonicity2(x2, x2val, y2, y2val, r2, r2val, x1, x1val, y1, y1val, r1, r1val)) + if (monotonicity2(x2, x2val, y2, y2val, q2, q2val, x1, x1val, y1, y1val, q1, q1val)) return true; - if (monotonicity3(x1, x1val, y1, y1val, r1, r1val, x2, x2val, y2, y2val, r2, r2val)) + if (monotonicity3(x1, x1val, y1, y1val, q1, q1val, x2, x2val, y2, y2val, q2, q2val)) return true; - if (monotonicity3(x2, x2val, y2, y2val, r2, r2val, x1, x1val, y1, y1val, r1, r1val)) + if (monotonicity3(x2, x2val, y2, y2val, q2, q2val, x1, x1val, y1, y1val, q1, q1val)) return true; return false; }; @@ -106,15 +115,15 @@ namespace nla { // idiv semantics if (!xval.is_int() || !yval.is_int() || yval == 0 || rval == div(xval, yval)) continue; - for (auto const& [r2, x2, y2] : m_idivisions) { - if (r2 == r) + for (auto const& [q2, x2, y2] : m_idivisions) { + if (q2 == r) continue; - if (!c.is_relevant(r2)) + if (!c.is_relevant(q2)) continue; auto x2val = c.val(x2); auto y2val = c.val(y2); - auto r2val = c.val(r2); - if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, r2, r2val)) + auto q2val = c.val(q2); + if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, q2, q2val)) return; } } @@ -128,15 +137,15 @@ namespace nla { // / semantics if (yval == 0 || rval == xval / yval) continue; - for (auto const& [r2, x2, y2] : m_rdivisions) { - if (r2 == r) + for (auto const& [q2, x2, y2] : m_rdivisions) { + if (q2 == r) continue; - if (!c.is_relevant(r2)) + if (!c.is_relevant(q2)) continue; auto x2val = c.val(x2); auto y2val = c.val(y2); - auto r2val = c.val(r2); - if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, r2, r2val)) + auto q2val = c.val(q2); + if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, q2, q2val)) return; } } @@ -146,5 +155,100 @@ namespace nla { // if p is bounded, q a value, r = eval(p): // p <= q * div(r, q) + q - 1 => div(p, q) <= div(r, q) // p >= q * div(r, q) => div(r, q) <= div(p, q) + +#if 0 + bool check_idiv_bounds() { + if (m_idiv_terms.empty()) { + return true; + } + bool all_divs_valid = true; + unsigned count = 0; + unsigned offset = ctx().get_random_value(); + for (unsigned j = 0; j < m_idiv_terms.size(); ++j) { + unsigned i = (offset + j) % m_idiv_terms.size(); + expr* n = m_idiv_terms[i]; + if (!ctx().is_relevant(n)) + continue; + expr* p = nullptr, * q = nullptr; + VERIFY(a.is_idiv(n, p, q)); + theory_var v = internalize_def(to_app(n)); + theory_var v1 = internalize_def(to_app(p)); + + if (!is_registered_var(v1)) + continue; + lp::impq q1 = get_ivalue(v1); + rational q2; + + if (!q1.x.is_int() || q1.x.is_neg() || !q1.y.is_zero()) { + // TBD + // q1 = 223/4, q2 = 2, r = 219/8 + // take ceil(q1), floor(q1), ceil(q2), floor(q2), for floor(q2) > 0 + // then + // p/q <= ceil(q1)/floor(q2) => n <= div(ceil(q1), floor(q2)) + // p/q >= floor(q1)/ceil(q2) => n >= div(floor(q1), ceil(q2)) + continue; + } + + + if (a.is_numeral(q, q2) && q2.is_pos()) { + if (!a.is_bounded(n)) { + TRACE("arith", tout << "unbounded " << expr_ref(n, m) << "\n";); + continue; + } + if (!is_registered_var(v)) + continue; + lp::impq val_v = get_ivalue(v); + if (val_v.y.is_zero() && val_v.x == div(q1.x, q2)) + continue; + + TRACE("arith", tout << get_value(v) << " != " << q1 << " div " << q2 << "\n";); + rational div_r = div(q1.x, q2); + // p <= q * div(q1, q) + q - 1 => div(p, q) <= div(q1, q2) + // p >= q * div(q1, q) => div(q1, q) <= div(p, q) + rational mul(1); + rational hi = q2 * div_r + q2 - 1; + rational lo = q2 * div_r; + + // used to normalize inequalities so they + // don't appear as 8*x >= 15, but x >= 2 + expr* n1 = nullptr, * n2 = nullptr; + if (a.is_mul(p, n1, n2) && a.is_extended_numeral(n1, mul) && mul.is_pos()) { + p = n2; + hi = floor(hi / mul); + lo = ceil(lo / mul); + } + literal p_le_q1 = mk_literal(a.mk_le(p, a.mk_numeral(hi, true))); + literal p_ge_q1 = mk_literal(a.mk_ge(p, a.mk_numeral(lo, true))); + literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div_r, true))); + literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div_r, true))); + { + scoped_trace_stream _sts(th, ~p_le_q1, n_le_div); + mk_axiom(~p_le_q1, n_le_div); + } + { + scoped_trace_stream _sts(th, ~p_ge_q1, n_ge_div); + mk_axiom(~p_ge_q1, n_ge_div); + } + + all_divs_valid = false; + ++count; + + + TRACE("arith", + tout << q1 << " div " << q2 << "\n"; + literal_vector lits; + lits.push_back(~p_le_q1); + lits.push_back(n_le_div); + ctx().display_literals_verbose(tout, lits) << "\n\n"; + lits[0] = ~p_ge_q1; + lits[1] = n_ge_div; + ctx().display_literals_verbose(tout, lits) << "\n";); + continue; + } + } + + return all_divs_valid; + } +#endif } diff --git a/src/math/lp/nla_divisions.h b/src/math/lp/nla_divisions.h index 56056e1f468..81fb0574a58 100644 --- a/src/math/lp/nla_divisions.h +++ b/src/math/lp/nla_divisions.h @@ -24,10 +24,12 @@ namespace nla { core& m_core; vector> m_idivisions; vector> m_rdivisions; + vector> m_bounded_divisions; public: divisions(core& c):m_core(c) {} - void add_idivision(lpvar r, lpvar x, lpvar y); - void add_rdivision(lpvar r, lpvar x, lpvar y); + void add_idivision(lpvar q, lpvar x, lpvar y); + void add_rdivision(lpvar q, lpvar x, lpvar y); + void add_bounded_division(lpvar q, lpvar r, lpvar x, lpvar y); void check(vector&); }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 8a9f41e8707..0daab80c13f 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1808,7 +1808,7 @@ class theory_lra::imp { for (unsigned j = 0; j < m_idiv_terms.size(); ++j) { unsigned i = (offset + j) % m_idiv_terms.size(); expr* n = m_idiv_terms[i]; - if (!ctx().is_relevant(n)) + if (false && !ctx().is_relevant(n)) continue; expr* p = nullptr, *q = nullptr; VERIFY(a.is_idiv(n, p, q)); From 03ca330926a77f84a6c6976f304c58225b9d0431 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2023 08:23:17 -0800 Subject: [PATCH 344/597] fix division filter Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_divisions.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index 06122ddc84c..4f5bd4e81e0 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -23,7 +23,6 @@ namespace nla { return; if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(q)) return; - verbose_stream() << q << " " << x << " " << y << " " << null_lpvar << "\n"; m_idivisions.push_back({q, x, y}); m_core.trail().push(push_back_vector(m_idivisions)); } From 304b316314c91fdb759def38df38ca47149b5ae0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2023 11:11:04 -0800 Subject: [PATCH 345/597] move bounded division lemmas to nla solver/ nla_divisions. --- src/math/lp/nla_core.cpp | 10 ++- src/math/lp/nla_core.h | 19 +++-- src/math/lp/nla_divisions.cpp | 135 ++++++++++++---------------------- src/math/lp/nla_divisions.h | 8 +- src/math/lp/nla_solver.cpp | 18 +++-- src/math/lp/nla_solver.h | 6 +- src/smt/theory_lra.cpp | 130 ++++++-------------------------- 7 files changed, 110 insertions(+), 216 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index e00bfd1b051..386ad296d3b 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -985,6 +985,9 @@ bool core::rm_check(const monic& rm) const { return check_monic(m_emons[rm.var()]); } +bool core::has_relevant_monomial() const { + return any_of(emons(), [&](auto const& m) { return is_relevant(m.var()); }); +} bool core::find_bfc_to_refine_on_monic(const monic& m, factorization & bf) { for (auto f : factorization_factory_imp(m, *this)) { @@ -1489,6 +1492,11 @@ lbool core::check_power(lpvar r, lpvar x, lpvar y, vector& l_vec) { return m_powers.check(r, x, y, l_vec); } +void core::check_bounded_divisions(vector& l_vec) { + m_lemma_vec = &l_vec; + m_divisions.check_bounded_divisions(); +} + lbool core::check(vector& l_vec) { lp_settings().stats().m_nla_calls++; TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); @@ -1527,7 +1535,7 @@ lbool core::check(vector& l_vec) { m_basics.basic_lemma(false); if (l_vec.empty() && !done()) - m_divisions.check(l_vec); + m_divisions.check(); #if 0 if (l_vec.empty() && !done() && !run_horner) diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index eedc671f5b8..016ff6e9dec 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -112,6 +112,9 @@ class core { void check_weighted(unsigned sz, std::pair>* checks); public: + // constructor + core(lp::lar_solver& s, reslimit&); + void insert_to_refine(lpvar j); void erase_from_to_refine(lpvar j); @@ -120,9 +123,7 @@ class core { void insert_to_active_var_set(unsigned j) const { m_active_var_set.insert(j); } - void clear_active_var_set() const { - m_active_var_set.clear(); - } + void clear_active_var_set() const { m_active_var_set.clear(); } void clear_and_resize_active_var_set() const { m_active_var_set.clear(); @@ -134,9 +135,9 @@ class core { reslimit& reslim() { return m_reslim; } emonics& emons() { return m_emons; } const emonics& emons() const { return m_emons; } - // constructor - core(lp::lar_solver& s, reslimit &); - + + bool has_relevant_monomial() const; + bool compare_holds(const rational& ls, llc cmp, const rational& rs) const; rational value(const lp::lar_term& r) const; @@ -202,8 +203,9 @@ class core { void deregister_monic_from_tables(const monic & m, unsigned i); void add_monic(lpvar v, unsigned sz, lpvar const* vs); - void add_idivision(lpvar r, lpvar x, lpvar y) { m_divisions.add_idivision(r, x, y); } - void add_rdivision(lpvar r, lpvar x, lpvar y) { m_divisions.add_rdivision(r, x, y); } + void add_idivision(lpvar q, lpvar x, lpvar y) { m_divisions.add_idivision(q, x, y); } + void add_rdivision(lpvar q, lpvar x, lpvar y) { m_divisions.add_rdivision(q, x, y); } + void add_bounded_division(lpvar q, lpvar x, lpvar y) { m_divisions.add_bounded_division(q, x, y); } void set_relevant(std::function& is_relevant) { m_relevant = is_relevant; } bool is_relevant(lpvar v) const { return !m_relevant || m_relevant(v); } @@ -381,6 +383,7 @@ class core { lbool check(vector& l_vec); lbool check_power(lpvar r, lpvar x, lpvar y, vector& l_vec); + void check_bounded_divisions(vector&); bool no_lemmas_hold() const; diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index 4f5bd4e81e0..91b674d58c4 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -36,13 +36,22 @@ namespace nla { m_core.trail().push(push_back_vector(m_rdivisions)); } + void divisions::add_bounded_division(lpvar q, lpvar x, lpvar y) { + if (x == null_lpvar || y == null_lpvar || q == null_lpvar) + return; + if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(q)) + return; + m_bounded_divisions.push_back({ q, x, y }); + m_core.trail().push(push_back_vector(m_bounded_divisions)); + } + typedef lp::lar_term term; // y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2 // y2 <= y1 < 0 & x1 >= x2 >= 0 => x1/y1 <= x2/y2 // y2 <= y1 < 0 & x1 <= x2 <= 0 => x1/y1 >= x2/y2 - void divisions::check(vector& lemmas) { + void divisions::check() { core& c = m_core; if (c.use_nra_model()) return; @@ -155,99 +164,45 @@ namespace nla { // p <= q * div(r, q) + q - 1 => div(p, q) <= div(r, q) // p >= q * div(r, q) => div(r, q) <= div(p, q) -#if 0 - bool check_idiv_bounds() { - if (m_idiv_terms.empty()) { - return true; - } - bool all_divs_valid = true; - unsigned count = 0; - unsigned offset = ctx().get_random_value(); - for (unsigned j = 0; j < m_idiv_terms.size(); ++j) { - unsigned i = (offset + j) % m_idiv_terms.size(); - expr* n = m_idiv_terms[i]; - if (!ctx().is_relevant(n)) - continue; - expr* p = nullptr, * q = nullptr; - VERIFY(a.is_idiv(n, p, q)); - theory_var v = internalize_def(to_app(n)); - theory_var v1 = internalize_def(to_app(p)); + void divisions::check_bounded_divisions() { + core& c = m_core; + unsigned offset = c.random(), sz = m_bounded_divisions.size(); - if (!is_registered_var(v1)) + for (unsigned j = 0; j < sz; ++j) { + unsigned i = (offset + j) % sz; + auto [q, x, y] = m_bounded_divisions[i]; + if (!c.is_relevant(q)) continue; - lp::impq q1 = get_ivalue(v1); - rational q2; - - if (!q1.x.is_int() || q1.x.is_neg() || !q1.y.is_zero()) { - // TBD - // q1 = 223/4, q2 = 2, r = 219/8 - // take ceil(q1), floor(q1), ceil(q2), floor(q2), for floor(q2) > 0 - // then - // p/q <= ceil(q1)/floor(q2) => n <= div(ceil(q1), floor(q2)) - // p/q >= floor(q1)/ceil(q2) => n >= div(floor(q1), ceil(q2)) + auto xv = c.val(x); + auto yv = c.val(y); + auto qv = c.val(q); + if (xv < 0 || !xv.is_int()) continue; - } - - - if (a.is_numeral(q, q2) && q2.is_pos()) { - if (!a.is_bounded(n)) { - TRACE("arith", tout << "unbounded " << expr_ref(n, m) << "\n";); - continue; - } - if (!is_registered_var(v)) - continue; - lp::impq val_v = get_ivalue(v); - if (val_v.y.is_zero() && val_v.x == div(q1.x, q2)) - continue; - - TRACE("arith", tout << get_value(v) << " != " << q1 << " div " << q2 << "\n";); - rational div_r = div(q1.x, q2); - // p <= q * div(q1, q) + q - 1 => div(p, q) <= div(q1, q2) - // p >= q * div(q1, q) => div(q1, q) <= div(p, q) - rational mul(1); - rational hi = q2 * div_r + q2 - 1; - rational lo = q2 * div_r; - - // used to normalize inequalities so they - // don't appear as 8*x >= 15, but x >= 2 - expr* n1 = nullptr, * n2 = nullptr; - if (a.is_mul(p, n1, n2) && a.is_extended_numeral(n1, mul) && mul.is_pos()) { - p = n2; - hi = floor(hi / mul); - lo = ceil(lo / mul); - } - literal p_le_q1 = mk_literal(a.mk_le(p, a.mk_numeral(hi, true))); - literal p_ge_q1 = mk_literal(a.mk_ge(p, a.mk_numeral(lo, true))); - literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div_r, true))); - literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div_r, true))); - { - scoped_trace_stream _sts(th, ~p_le_q1, n_le_div); - mk_axiom(~p_le_q1, n_le_div); - } - { - scoped_trace_stream _sts(th, ~p_ge_q1, n_ge_div); - mk_axiom(~p_ge_q1, n_ge_div); - } - - all_divs_valid = false; - ++count; - - - TRACE("arith", - tout << q1 << " div " << q2 << "\n"; - literal_vector lits; - lits.push_back(~p_le_q1); - lits.push_back(n_le_div); - ctx().display_literals_verbose(tout, lits) << "\n\n"; - lits[0] = ~p_ge_q1; - lits[1] = n_ge_div; - ctx().display_literals_verbose(tout, lits) << "\n";); + if (yv <= 0 || !yv.is_int()) + continue; + if (qv == div(xv, yv)) continue; + + rational div_v = div(xv, yv); + // y = yv & x <= yv * div(xv, yv) + yv - 1 => div(x, y) <= div(xv, yv) + // y = yv & x >= y * div(xv, yv) => div(xv, yv) <= div(x, y) + rational mul(1); + rational hi = yv * div_v + yv - 1; + rational lo = yv * div_v; + if (xv > hi) { + new_lemma lemma(c, "y = yv & x <= yv * div(xv, yv) + yv - 1 => div(p, y) <= div(xv, yv)"); + lemma |= ineq(y, llc::NE, yv); + lemma |= ineq(x, llc::GT, hi); + lemma |= ineq(q, llc::LE, div_v); + return; + } + if (xv < lo) { + new_lemma lemma(c, "y = yv & x >= yv * div(xv, yv) => div(xv, yv) <= div(x, y)"); + lemma |= ineq(y, llc::NE, yv); + lemma |= ineq(x, llc::LT, lo); + lemma |= ineq(q, llc::GE, div_v); + return; } } - - return all_divs_valid; - } -#endif - + } } diff --git a/src/math/lp/nla_divisions.h b/src/math/lp/nla_divisions.h index 81fb0574a58..80bf5be4e78 100644 --- a/src/math/lp/nla_divisions.h +++ b/src/math/lp/nla_divisions.h @@ -24,12 +24,14 @@ namespace nla { core& m_core; vector> m_idivisions; vector> m_rdivisions; - vector> m_bounded_divisions; + vector> m_bounded_divisions; + public: divisions(core& c):m_core(c) {} void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); - void add_bounded_division(lpvar q, lpvar r, lpvar x, lpvar y); - void check(vector&); + void add_bounded_division(lpvar q, lpvar x, lpvar y); + void check(); + void check_bounded_divisions(); }; } diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 50842f3336c..bd0f1953ce0 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -23,12 +23,16 @@ namespace nla { m_core->add_monic(v, sz, vs); } - void solver::add_idivision(lpvar r, lpvar x, lpvar y) { - m_core->add_idivision(r, x, y); + void solver::add_idivision(lpvar q, lpvar x, lpvar y) { + m_core->add_idivision(q, x, y); } - void solver::add_rdivision(lpvar r, lpvar x, lpvar y) { - m_core->add_rdivision(r, x, y); + void solver::add_rdivision(lpvar q, lpvar x, lpvar y) { + m_core->add_rdivision(q, x, y); + } + + void solver::add_bounded_division(lpvar q, lpvar x, lpvar y) { + m_core->add_bounded_division(q, x, y); } void solver::set_relevant(std::function& is_relevant) { @@ -39,7 +43,7 @@ namespace nla { return m_core->is_monic_var(v); } - bool solver::need_check() { return true; } + bool solver::need_check() { return m_core->has_relevant_monomial(); } lbool solver::check(vector& l) { return m_core->check(l); @@ -92,4 +96,8 @@ namespace nla { return m_core->check_power(r, x, y, lemmas); } + void solver::check_bounded_divisions(vector& lemmas) { + m_core->check_bounded_divisions(lemmas); + } + } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 6667dff31fc..d04ff8e516c 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -29,8 +29,10 @@ namespace nla { ~solver(); void add_monic(lpvar v, unsigned sz, lpvar const* vs); - void add_idivision(lpvar r, lpvar x, lpvar y); - void add_rdivision(lpvar r, lpvar x, lpvar y); + void add_idivision(lpvar q, lpvar x, lpvar y); + void add_rdivision(lpvar q, lpvar x, lpvar y); + void add_bounded_division(lpvar q, lpvar x, lpvar y); + void check_bounded_divisions(vector&); void set_relevant(std::function& is_relevant); nla_settings& settings(); void push(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 0daab80c13f..c5b706fded3 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -62,7 +62,6 @@ class theory_lra::imp { struct scope { unsigned m_bounds_lim; - unsigned m_idiv_lim; unsigned m_asserted_qhead; unsigned m_asserted_atoms_lim; }; @@ -161,7 +160,6 @@ class theory_lra::imp { svector m_asserted_atoms; ptr_vector m_not_handled; ptr_vector m_underspecified; - ptr_vector m_idiv_terms; vector > m_use_list; // bounds where variables are used. // attributes for incremental version: @@ -436,19 +434,23 @@ class theory_lra::imp { } else if (a.is_idiv(n, n1, n2)) { if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n); - m_idiv_terms.push_back(n); app_ref mod(a.mk_mod(n1, n2), m); ctx().internalize(mod, false); if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); -#if 1 if (m_nla && !a.is_numeral(n2)) { // shortcut to create non-linear division axioms. - theory_var r = mk_var(n); + theory_var q = mk_var(n); theory_var x = mk_var(n1); theory_var y = mk_var(n2); - m_nla->add_idivision(get_lpvar(n), get_lpvar(n1), get_lpvar(n2)); - } -#endif + m_nla->add_idivision(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y)); + } + if (a.is_numeral(n2) && a.is_bounded(n1)) { + ensure_nla(); + theory_var q = mk_var(n); + theory_var x = mk_var(n1); + theory_var y = mk_var(n2); + m_nla->add_bounded_division(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y)); + } } else if (a.is_mod(n, n1, n2)) { if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n); @@ -1077,7 +1079,6 @@ class theory_lra::imp { scope& sc = m_scopes.back(); sc.m_bounds_lim = m_bounds_trail.size(); sc.m_asserted_qhead = m_asserted_qhead; - sc.m_idiv_lim = m_idiv_terms.size(); sc.m_asserted_atoms_lim = m_asserted_atoms.size(); lp().push(); if (m_nla) @@ -1092,7 +1093,6 @@ class theory_lra::imp { } unsigned old_size = m_scopes.size() - num_scopes; del_bounds(m_scopes[old_size].m_bounds_lim); - m_idiv_terms.shrink(m_scopes[old_size].m_idiv_lim); m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; m_scopes.resize(old_size); @@ -1480,7 +1480,7 @@ class theory_lra::imp { } void random_update() { - if (m_nla) + if (m_nla && m_nla->need_check()) return; m_tmp_var_set.clear(); m_tmp_var_set.resize(th.get_num_vars()); @@ -1799,96 +1799,13 @@ class theory_lra::imp { */ bool check_idiv_bounds() { - if (m_idiv_terms.empty()) { + if (!m_nla) return true; - } - bool all_divs_valid = true; - unsigned count = 0; - unsigned offset = ctx().get_random_value(); - for (unsigned j = 0; j < m_idiv_terms.size(); ++j) { - unsigned i = (offset + j) % m_idiv_terms.size(); - expr* n = m_idiv_terms[i]; - if (false && !ctx().is_relevant(n)) - continue; - expr* p = nullptr, *q = nullptr; - VERIFY(a.is_idiv(n, p, q)); - theory_var v = internalize_def(to_app(n)); - theory_var v1 = internalize_def(to_app(p)); - - if (!is_registered_var(v1)) - continue; - lp::impq r1 = get_ivalue(v1); - rational r2; - - if (!r1.x.is_int() || r1.x.is_neg() || !r1.y.is_zero()) { - // TBD - // r1 = 223/4, r2 = 2, r = 219/8 - // take ceil(r1), floor(r1), ceil(r2), floor(r2), for floor(r2) > 0 - // then - // p/q <= ceil(r1)/floor(r2) => n <= div(ceil(r1), floor(r2)) - // p/q >= floor(r1)/ceil(r2) => n >= div(floor(r1), ceil(r2)) - continue; - } - - - if (a.is_numeral(q, r2) && r2.is_pos()) { - if (!a.is_bounded(n)) { - TRACE("arith", tout << "unbounded " << expr_ref(n, m) << "\n";); - continue; - } - if (!is_registered_var(v)) - continue; - lp::impq val_v = get_ivalue(v); - if (val_v.y.is_zero() && val_v.x == div(r1.x, r2)) - continue; - - TRACE("arith", tout << get_value(v) << " != " << r1 << " div " << r2 << "\n";); - rational div_r = div(r1.x, r2); - // p <= q * div(r1, q) + q - 1 => div(p, q) <= div(r1, r2) - // p >= q * div(r1, q) => div(r1, q) <= div(p, q) - rational mul(1); - rational hi = r2 * div_r + r2 - 1; - rational lo = r2 * div_r; - - // used to normalize inequalities so they - // don't appear as 8*x >= 15, but x >= 2 - expr *n1 = nullptr, *n2 = nullptr; - if (a.is_mul(p, n1, n2) && a.is_extended_numeral(n1, mul) && mul.is_pos()) { - p = n2; - hi = floor(hi/mul); - lo = ceil(lo/mul); - } - literal p_le_r1 = mk_literal(a.mk_le(p, a.mk_numeral(hi, true))); - literal p_ge_r1 = mk_literal(a.mk_ge(p, a.mk_numeral(lo, true))); - literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div_r, true))); - literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div_r, true))); - { - scoped_trace_stream _sts(th, ~p_le_r1, n_le_div); - mk_axiom(~p_le_r1, n_le_div); - } - { - scoped_trace_stream _sts(th, ~p_ge_r1, n_ge_div); - mk_axiom(~p_ge_r1, n_ge_div); - } - - all_divs_valid = false; - ++count; - - - TRACE("arith", - tout << r1 << " div " << r2 << "\n"; - literal_vector lits; - lits.push_back(~p_le_r1); - lits.push_back(n_le_div); - ctx().display_literals_verbose(tout, lits) << "\n\n"; - lits[0] = ~p_ge_r1; - lits[1] = n_ge_div; - ctx().display_literals_verbose(tout, lits) << "\n";); - continue; - } - } - - return all_divs_valid; + m_nla_lemma_vector.reset(); + m_nla->check_bounded_divisions(m_nla_lemma_vector); + for (auto & lemma : m_nla_lemma_vector) + false_case_of_check_nla(lemma); + return m_nla_lemma_vector.empty(); } expr_ref var2expr(lpvar v) { @@ -2105,9 +2022,8 @@ class theory_lra::imp { lbool r = m_nla->check(m_nla_lemma_vector); switch (r) { case l_false: { - for (const nla::lemma & l : m_nla_lemma_vector) { - false_case_of_check_nla(l); - } + for (const nla::lemma & l : m_nla_lemma_vector) + false_case_of_check_nla(l); break; } case l_true: @@ -2126,11 +2042,11 @@ class theory_lra::imp { TRACE("arith", tout << "canceled\n";); return l_undef; } - if (!m_nla) { - TRACE("arith", tout << "no nla\n";); + CTRACE("arith",!m_nla, tout << "no nla\n";); + if (!m_nla) + return l_true; + if (!m_nla->need_check()) return l_true; - } - if (!m_nla->need_check()) return l_true; return check_nla_continue(); } From dd0decfe5d6c11780a04ba3eec971fc42e1010c3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2023 16:12:25 -0800 Subject: [PATCH 346/597] create simplifier_solver wrapper to supply simplifier layer move sat_smt_preprocess to solver fix bugs in model_reconstruction_trail for dependency replay This is a preparatory step for exposing pre-processing as tactics. --- scripts/mk_project.py | 6 +- src/CMakeLists.txt | 4 +- .../simplifiers/model_reconstruction_trail.h | 6 +- src/sat/sat_solver/CMakeLists.txt | 1 - src/sat/sat_solver/sat_smt_preprocess.cpp | 106 ------ src/sat/sat_solver/sat_smt_solver.cpp | 173 +++------- src/solver/CMakeLists.txt | 4 + src/solver/simplifier_solver.cpp | 317 ++++++++++++++++++ src/solver/simplifier_solver.h | 25 ++ src/solver/solver_preprocess.cpp | 78 +++++ .../solver_preprocess.h} | 2 +- 11 files changed, 474 insertions(+), 248 deletions(-) delete mode 100644 src/sat/sat_solver/sat_smt_preprocess.cpp create mode 100644 src/solver/simplifier_solver.cpp create mode 100644 src/solver/simplifier_solver.h create mode 100644 src/solver/solver_preprocess.cpp rename src/{sat/sat_solver/sat_smt_preprocess.h => solver/solver_preprocess.h} (94%) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index e87c36d851f..44e436daf0a 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -41,7 +41,9 @@ def init_project_def(): add_lib('converters', ['model'], 'ast/converters') add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'converters', 'substitution'], 'ast/simplifiers') add_lib('tactic', ['simplifiers']) - add_lib('solver', ['params', 'model', 'tactic', 'proofs']) + add_lib('mbp', ['model', 'simplex'], 'qe/mbp') + add_lib('qe_lite', ['tactic', 'mbp'], 'qe/lite') + add_lib('solver', ['params', 'smt_params', 'model', 'tactic', 'qe_lite', 'proofs']) add_lib('cmd_context', ['solver', 'rewriter', 'params']) add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern') @@ -50,8 +52,6 @@ def init_project_def(): add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa') add_lib('core_tactics', ['tactic', 'macros', 'normal_forms', 'rewriter', 'pattern'], 'tactic/core') add_lib('arith_tactics', ['core_tactics', 'sat'], 'tactic/arith') - add_lib('mbp', ['model', 'simplex'], 'qe/mbp') - add_lib('qe_lite', ['tactic', 'mbp'], 'qe/lite') add_lib('solver_assertions', ['pattern','smt_params','cmd_context','qe_lite'], 'solver/assertions') add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 652aef4ac51..e091db15f64 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,6 +55,8 @@ add_subdirectory(ast/converters) add_subdirectory(ast/substitution) add_subdirectory(ast/simplifiers) add_subdirectory(tactic) +add_subdirectory(qe/mbp) +add_subdirectory(qe/lite) add_subdirectory(smt/params) add_subdirectory(parsers/util) add_subdirectory(math/grobner) @@ -68,8 +70,6 @@ add_subdirectory(solver) add_subdirectory(cmd_context) add_subdirectory(cmd_context/extra_cmds) add_subdirectory(parsers/smt2) -add_subdirectory(qe/mbp) -add_subdirectory(qe/lite) add_subdirectory(solver/assertions) add_subdirectory(ast/pattern) add_subdirectory(math/lp) diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index c1b045a43c7..36ddde9ffdb 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -70,7 +70,7 @@ class model_reconstruction_trail { return true; if (m_subst) { for (auto const& [k, v] : m_subst->sub()) - if (free_vars.is_marked(k)) + if (free_vars.is_marked(to_app(k)->get_decl())) return true; } return false; @@ -88,8 +88,10 @@ class model_reconstruction_trail { void add_vars(expr* e, ast_mark& free_vars) { for (expr* t : subterms::all(expr_ref(e, m))) - if (is_app(t)) + if (is_app(t) && is_uninterp(t)) { + TRACE("simplifier", tout << "add var " << to_app(t)->get_decl()->get_name() << "\n"); free_vars.mark(to_app(t)->get_decl(), true); + } } void add_vars(dependent_expr const& d, ast_mark& free_vars) { diff --git a/src/sat/sat_solver/CMakeLists.txt b/src/sat/sat_solver/CMakeLists.txt index 725ca16f799..ad26aceabdb 100644 --- a/src/sat/sat_solver/CMakeLists.txt +++ b/src/sat/sat_solver/CMakeLists.txt @@ -1,7 +1,6 @@ z3_add_component(sat_solver SOURCES inc_sat_solver.cpp - sat_smt_preprocess.cpp sat_smt_solver.cpp COMPONENT_DEPENDENCIES aig_tactic diff --git a/src/sat/sat_solver/sat_smt_preprocess.cpp b/src/sat/sat_solver/sat_smt_preprocess.cpp deleted file mode 100644 index 02c6e88b42e..00000000000 --- a/src/sat/sat_solver/sat_smt_preprocess.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/*++ -Copyright (c) 2022 Microsoft Corporation - -Module Name: - - sat_smt_preprocess.cpp - -Abstract: - - SAT pre-process - -Author: - - Nikolaj Bjorner (nbjorner) 2022-11-28 - ---*/ - - -#include "ast/rewriter/rewriter_def.h" -#include "ast/simplifiers/bit_blaster.h" -#include "ast/simplifiers/max_bv_sharing.h" -#include "ast/simplifiers/card2bv.h" -#include "ast/simplifiers/propagate_values.h" -#include "ast/simplifiers/rewriter_simplifier.h" -#include "ast/simplifiers/solve_eqs.h" -#include "ast/simplifiers/bv_slice.h" -#include "ast/simplifiers/eliminate_predicates.h" -#include "ast/simplifiers/elim_unconstrained.h" -#include "ast/simplifiers/pull_nested_quantifiers.h" -#include "ast/simplifiers/distribute_forall.h" -#include "ast/simplifiers/refine_inj_axiom.h" -#include "ast/simplifiers/elim_bounds.h" -#include "ast/simplifiers/bit2int.h" -#include "ast/simplifiers/bv_elim.h" -#include "ast/simplifiers/push_ite.h" -#include "ast/simplifiers/elim_term_ite.h" -#include "ast/simplifiers/flatten_clauses.h" -#include "ast/simplifiers/cnf_nnf.h" -#include "sat/sat_params.hpp" -#include "smt/params/smt_params.h" -#include "sat/sat_solver/sat_smt_preprocess.h" -#include "qe/lite/qe_lite.h" - -void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { - - sat_params sp(p); - smt_params smtp(p); - if (sp.euf() || sp.smt()) { - s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); - if (smtp.m_propagate_values) s.add_simplifier(alloc(propagate_values, m, p, st)); - if (smtp.m_solve_eqs) s.add_simplifier(alloc(euf::solve_eqs, m, st)); - if (smtp.m_elim_unconstrained) s.add_simplifier(alloc(elim_unconstrained, m, st)); - if (smtp.m_nnf_cnf) s.add_simplifier(alloc(cnf_nnf_simplifier, m, p, st)); - if (smtp.m_macro_finder || smtp.m_quasi_macros) s.add_simplifier(alloc(eliminate_predicates, m, st)); - if (smtp.m_qe_lite) s.add_simplifier(mk_qe_lite_simplifer(m, p, st)); - if (smtp.m_pull_nested_quantifiers) s.add_simplifier(alloc(pull_nested_quantifiers_simplifier, m, p, st)); - if (smtp.m_max_bv_sharing) s.add_simplifier(mk_max_bv_sharing(m, p, st)); - if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); - if (smtp.m_bv_size_reduce) s.add_simplifier(alloc(bv::slice, m, st)); - if (smtp.m_distribute_forall) s.add_simplifier(alloc(distribute_forall_simplifier, m, p, st)); - if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); - if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); - if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); - if (smtp.m_eliminate_term_ite && smtp.m_lift_ite != lift_ite_kind::LI_FULL) s.add_simplifier(alloc(elim_term_ite_simplifier, m, p, st)); - if (smtp.m_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(push_ite_simplifier, m, p, st, smtp.m_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); - if (smtp.m_ng_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(ng_push_ite_simplifier, m, p, st, smtp.m_ng_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); - s.add_simplifier(alloc(flatten_clauses, m, p, st)); - - // - // add: - // euf_completion? - // - // add: make it externally programmable - // -#if 0 - if (!invoke(m_apply_quasi_macros)) return; -#endif - - } - else { - params_ref simp1_p = p; - simp1_p.set_bool("som", true); - simp1_p.set_bool("pull_cheap_ite", true); - simp1_p.set_bool("push_ite_bv", false); - simp1_p.set_bool("local_ctx", true); - simp1_p.set_uint("local_ctx_limit", 10000000); - simp1_p.set_bool("flat", true); // required by som - simp1_p.set_bool("hoist_mul", false); // required by som - simp1_p.set_bool("elim_and", true); - simp1_p.set_bool("blast_distinct", true); - simp1_p.set_bool("flat_and_or", false); - - params_ref simp2_p = p; - simp2_p.set_bool("flat", false); - simp2_p.set_bool("flat_and_or", false); - - s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); - s.add_simplifier(alloc(propagate_values, m, p, st)); - s.add_simplifier(alloc(card2bv, m, p, st)); - s.add_simplifier(alloc(rewriter_simplifier, m, simp1_p, st)); - s.add_simplifier(mk_max_bv_sharing(m, p, st)); - s.add_simplifier(alloc(bit_blaster_simplifier, m, p, st)); - s.add_simplifier(alloc(rewriter_simplifier, m, simp2_p, st)); - } -} - diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 4e5f8fd37e2..dbc2d950517 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -17,14 +17,10 @@ Module Name: Notes: - - add translation for preprocess state. - - If the pre-processors are stateful, they need to be properly translated. + - add back get_consequences, maybe or just have them handled by inc_sat_solver - could also port the layered solver used by smtfd and used by get_consequences to simplifiers - - port various pre-processing to simplifiers - - qe-lite, fm-elimination, ite-lifting, other from asserted_formulas - --*/ @@ -36,7 +32,7 @@ Module Name: #include "model/model_smt2_pp.h" #include "model/model_evaluator.h" #include "sat/sat_solver.h" -#include "sat/sat_solver/sat_smt_preprocess.h" +#include "solver/simplifier_solver.h" #include "sat/sat_params.hpp" #include "sat/smt/euf_solver.h" #include "sat/tactic/goal2sat.h" @@ -47,54 +43,6 @@ Module Name: // incremental SAT solver. class sat_smt_solver : public solver { - struct dep_expr_state : public dependent_expr_state { - sat_smt_solver& s; - model_reconstruction_trail m_reconstruction_trail; - dep_expr_state(sat_smt_solver& s):dependent_expr_state(s.m), s(s), m_reconstruction_trail(s.m, m_trail) {} - ~dep_expr_state() override {} - virtual unsigned qtail() const override { return s.m_fmls.size(); } - dependent_expr const& operator[](unsigned i) override { return s.m_fmls[i]; } - void update(unsigned i, dependent_expr const& j) override { SASSERT(j.fml()); s.m_fmls[i] = j; } - void add(dependent_expr const& j) override { s.m_fmls.push_back(j); } - bool inconsistent() override { return s.m_solver.inconsistent(); } - model_reconstruction_trail& model_trail() override { return m_reconstruction_trail; } - std::ostream& display(std::ostream& out) const override { - unsigned i = 0; - for (auto const& d : s.m_fmls) { - if (i > 0 && i == qhead()) - out << "---- head ---\n"; - out << d << "\n"; - ++i; - } - m_reconstruction_trail.display(out); - return out; - } - void append(generic_model_converter& mc) { model_trail().append(mc); } - void replay(unsigned qhead, expr_ref_vector& assumptions) { m_reconstruction_trail.replay(qhead, assumptions, *this); } - void flatten_suffix() override { - expr_mark seen; - unsigned j = qhead(); - for (unsigned i = qhead(); i < qtail(); ++i) { - expr* f = s.m_fmls[i].fml(); - if (seen.is_marked(f)) - continue; - seen.mark(f, true); - if (s.m.is_true(f)) - continue; - if (s.m.is_and(f)) { - auto* d = s.m_fmls[i].dep(); - for (expr* arg : *to_app(f)) - s.m_fmls.push_back(dependent_expr(s.m, arg, nullptr, d)); - continue; - } - if (i != j) - s.m_fmls[j] = s.m_fmls[i]; - ++j; - } - s.m_fmls.shrink(j); - } - }; - struct dependency2assumptions { ast_manager& m; trail_stack& m_trail; @@ -152,15 +100,12 @@ class sat_smt_solver : public solver { mutable sat::solver m_solver; params_ref m_params; - vector m_fmls; - dep_expr_state m_preprocess_state; - seq_simplifier m_preprocess; - trail_stack& m_trail; + trail_stack m_trail; dependency2assumptions m_dep; goal2sat m_goal2sat; - expr_ref_vector m_assumptions, m_core, m_ors, m_aux_fmls, m_internalized_fmls; + unsigned m_qhead = 0; + expr_ref_vector m_assumptions, m_core, m_ors, m_fmls, m_internalized_fmls; atom2bool_var m_map; - generic_model_converter_ref m_mc; mutable model_converter_ref m_cached_mc; mutable ref m_sat_mc; std::string m_unknown = "no reason given"; @@ -168,20 +113,16 @@ class sat_smt_solver : public solver { // this allows to access the internal state of the SAT solver and carry on partial results. bool m_internalized_converted = false; // have internalized formulas been converted back - bool is_internalized() const { return m_preprocess_state.qhead() == m_fmls.size(); } + bool is_internalized() const { return m_qhead == m_fmls.size(); } public: sat_smt_solver(ast_manager& m, params_ref const& p): solver(m), m_solver(p, m.limit()), - m_preprocess_state(*this), - m_preprocess(m, p, m_preprocess_state), - m_trail(m_preprocess_state.m_trail), m_dep(m, m_trail), - m_assumptions(m), m_core(m), m_ors(m), m_aux_fmls(m), m_internalized_fmls(m), + m_assumptions(m), m_core(m), m_ors(m), m_fmls(m), m_internalized_fmls(m), m_map(m) { updt_params(p); - init_preprocess(); m_solver.set_incremental(true); } @@ -203,13 +144,10 @@ class sat_smt_solver : public solver { } // TODO: copy preprocess state for (auto const& [k, v] : m_dep.m_dep2orig) result->m_dep.insert(tr(v), tr(k)); - for (dependent_expr const& f : m_fmls) result->m_fmls.push_back(dependent_expr(tr, f)); for (expr* f : m_assumptions) result->m_assumptions.push_back(tr(f)); for (auto & kv : m_map) result->m_map.insert(tr(kv.m_key), kv.m_value); for (expr* f : m_internalized_fmls) result->m_internalized_fmls.push_back(tr(f)); - if (m_mc) result->m_mc = dynamic_cast(m_mc->translate(tr)); result->m_dep.copy(tr, m_dep); - if (m_sat_mc) result->m_sat_mc = dynamic_cast(m_sat_mc->translate(tr)); result->m_internalized_converted = m_internalized_converted; return result; } @@ -234,8 +172,6 @@ class sat_smt_solver : public solver { expr_ref_vector assumptions(m); for (unsigned i = 0; i < sz; ++i) assumptions.push_back(ensure_literal(_assumptions[i])); - for (expr* a : assumptions) - m_preprocess_state.freeze(a); TRACE("sat", tout << assumptions << "\n";); lbool r = internalize_formulas(assumptions); if (r != l_true) @@ -284,17 +220,15 @@ class sat_smt_solver : public solver { m_solver.user_push(); m_goal2sat.user_push(); m_map.push(); - m_preprocess_state.push(); - m_preprocess.push(); + m_trail.push_scope(); m_trail.push(restore_vector(m_assumptions)); m_trail.push(restore_vector(m_fmls)); + m_trail.push(value_trail(m_qhead)); } void pop(unsigned n) override { n = std::min(n, m_trail.get_num_scopes()); // allow sat_smt_solver to take over for another solver. - - m_preprocess.pop(n); - m_preprocess_state.pop(n); + m_trail.pop_scope(n); m_map.pop(n); m_goal2sat.user_pop(n); m_solver.user_pop(n); @@ -345,18 +279,32 @@ class sat_smt_solver : public solver { return a; expr* new_dep = m.mk_fresh_const("dep", m.mk_bool_sort()); expr* fml = m.mk_iff(new_dep, a); - m_fmls.push_back(dependent_expr(m, fml, nullptr, nullptr)); + m_fmls.push_back(fml); m_dep.insert(a, new_dep); return new_dep; } void assert_expr_core2(expr * t, expr * a) override { - a = ensure_literal(a); - m_fmls.push_back(dependent_expr(m, t, nullptr, m.mk_leaf(a))); + m_ors.reset(); + m_ors.push_back(t); + if (m.is_and(a)) { + for (expr* arg : *to_app(a)) { + arg = ensure_literal(arg); + m_ors.push_back(mk_not(m, arg)); + m_assumptions.push_back(arg); + } + } + else { + a = ensure_literal(a); + m_assumptions.push_back(a); + m_ors.push_back(mk_not(m, a)); + } + flatten_or(m_ors); + m_fmls.push_back(mk_or(m_ors)); } void assert_expr_core(expr * t) override { - m_fmls.push_back(dependent_expr(m, t, nullptr, nullptr)); + m_fmls.push_back(t); } ast_manager& get_manager() const override { return m; } @@ -367,7 +315,6 @@ class sat_smt_solver : public solver { solver::collect_param_descrs(r); goal2sat::collect_param_descrs(r); sat::solver::collect_param_descrs(r); - m_preprocess.collect_param_descrs(r); } void updt_params(params_ref const & p) override { @@ -377,13 +324,11 @@ class sat_smt_solver : public solver { m_params.set_sym("pb.solver", sp.pb_solver()); m_solver.updt_params(m_params); m_solver.set_incremental(true); - m_preprocess.updt_params(m_params); if (sp.smt()) ensure_euf(); } void collect_statistics(statistics & st) const override { - m_preprocess.collect_statistics(st); m_solver.collect_statistics(st); } @@ -531,7 +476,7 @@ class sat_smt_solver : public solver { expr * get_assertion(unsigned idx) const override { if (is_internalized() && m_internalized_converted) return m_internalized_fmls[idx]; - return m_fmls[idx].fml(); + return m_fmls.get(idx); } unsigned get_num_assumptions() const override { @@ -549,7 +494,7 @@ class sat_smt_solver : public solver { return m_cached_mc; if (is_internalized() && m_internalized_converted) { if (m_sat_mc) m_sat_mc->flush_smc(m_solver, m_map); - m_cached_mc = concat(solver::get_model_converter().get(), m_mc.get(), m_sat_mc.get()); + m_cached_mc = concat(solver::get_model_converter().get(), m_sat_mc.get()); TRACE("sat", m_cached_mc->display(tout);); return m_cached_mc; } @@ -574,10 +519,6 @@ class sat_smt_solver : public solver { m_internalized_converted = true; } - void init_preprocess() { - ::init_preprocess(m, m_params, m_preprocess, m_preprocess_state); - } - euf::solver* get_euf() { return dynamic_cast(m_solver.get_extension()); } @@ -646,50 +587,20 @@ class sat_smt_solver : public solver { if (is_internalized() && assumptions.empty()) return l_true; - unsigned qhead = m_preprocess_state.qhead(); - TRACE("sat", tout << "qhead " << qhead << "\n"); + TRACE("sat", tout << "qhead " << m_qhead << "\n"); m_internalized_converted = false; - m_preprocess_state.replay(qhead, assumptions); - m_preprocess.reduce(); - if (!m.inc()) - return l_undef; - m_preprocess_state.advance_qhead(); - m_mc = alloc(generic_model_converter, m, "sat-model-converter"); - m_preprocess_state.append(*m_mc); m_solver.pop_to_base_level(); - m_aux_fmls.reset(); - for (; qhead < m_fmls.size(); ++qhead) - add_with_dependency(m_fmls[qhead]); init_goal2sat(); - m_goal2sat(m_aux_fmls.size(), m_aux_fmls.data()); + m_goal2sat(m_fmls.size() - m_qhead, m_fmls.data() + m_qhead); if (!m_sat_mc) m_sat_mc = alloc(sat2goal::mc, m); m_sat_mc->flush_smc(m_solver, m_map); + m_qhead = m_fmls.size(); return m.inc() ? l_true : l_undef; } - ptr_vector m_deps; - void add_with_dependency(dependent_expr const& de) { - if (!de.dep()) { - m_aux_fmls.push_back(de.fml()); - return; - } - m_deps.reset(); - m.linearize(de.dep(), m_deps); - m_ors.reset(); - m_ors.push_back(de.fml()); - flatten_or(m_ors); - for (expr* d : m_deps) { - SASSERT(m.is_bool(d)); - SASSERT(is_literal(d)); - m_assumptions.push_back(d); - m_ors.push_back(mk_not(m, d)); - } - m_aux_fmls.push_back(mk_or(m_ors)); - } - void extract_core() { m_core.reset(); if (m_dep.m_literals.empty()) @@ -720,7 +631,7 @@ class sat_smt_solver : public solver { mdl = nullptr; if (!m_solver.model_is_current()) return; - if (m_fmls.size() > m_preprocess_state.qhead()) + if (m_fmls.size() > m_qhead) return; TRACE("sat", m_solver.display_model(tout);); CTRACE("sat", m_sat_mc, m_sat_mc->display(tout);); @@ -751,7 +662,6 @@ class sat_smt_solver : public solver { if (m_sat_mc) (*m_sat_mc)(mdl); m_goal2sat.update_model(mdl); - TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); @@ -760,25 +670,24 @@ class sat_smt_solver : public solver { model_evaluator eval(*mdl); eval.set_model_completion(true); bool all_true = true; - for (dependent_expr const& d : m_fmls) { - if (has_quantifiers(d.fml())) + for (expr* f : m_fmls) { + if (has_quantifiers(f)) continue; expr_ref tmp(m); - eval(d.fml(), tmp); + eval(f, tmp); if (m.limit().is_canceled()) return; CTRACE("sat", !m.is_true(tmp), - tout << "Evaluation failed: " << mk_pp(d.fml(), m) << " to " << tmp << "\n"; + tout << "Evaluation failed: " << mk_pp(f, m) << " to " << tmp << "\n"; model_smt2_pp(tout, m, *(mdl.get()), 0);); if (m.is_false(tmp)) { - IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(d.fml(), m) << "\n"); + IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(f, m) << "\n"); IF_VERBOSE(0, verbose_stream() << "evaluated to " << tmp << "\n"); all_true = false; } } if (!all_true) { IF_VERBOSE(0, verbose_stream() << m_params << "\n"); - IF_VERBOSE(0, if (m_mc) m_mc->display(verbose_stream() << "mc0\n")); IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); exit(0); } @@ -786,13 +695,11 @@ class sat_smt_solver : public solver { IF_VERBOSE(1, verbose_stream() << "solution verified\n"); } } - TRACE("sat", m_mc->display(tout);); - (*m_mc)(mdl); } }; solver* mk_sat_smt_solver(ast_manager& m, params_ref const& p) { - return alloc(sat_smt_solver, m, p); + return mk_simplifier_solver(alloc(sat_smt_solver, m, p)); } diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index 3bea76dd00c..088f2cbb282 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -5,15 +5,19 @@ z3_add_component(solver combined_solver.cpp mus.cpp parallel_tactical.cpp + simplifier_solver.cpp smt_logics.cpp solver.cpp solver_na2as.cpp solver_pool.cpp + solver_preprocess.cpp solver2tactic.cpp tactic2solver.cpp COMPONENT_DEPENDENCIES model tactic + smt_params + qe_lite PYG_FILES combined_solver_params.pyg parallel_params.pyg diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp new file mode 100644 index 00000000000..17a39509e44 --- /dev/null +++ b/src/solver/simplifier_solver.cpp @@ -0,0 +1,317 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + simplifier_solver.cpp + +Abstract: + + Implements a solver with simplifying pre-processing. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-30 + + Notes: + + - add translation for preprocess state. + - If the pre-processors are stateful, they need to be properly translated. + +--*/ +#include "util/params.h" +#include "ast/ast_util.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/simplifiers/seq_simplifier.h" +#include "solver/solver.h" +#include "solver/simplifier_solver.h" +#include "solver/solver_preprocess.h" + + +class simplifier_solver : public solver { + + + struct dep_expr_state : public dependent_expr_state { + simplifier_solver& s; + model_reconstruction_trail m_reconstruction_trail; + dep_expr_state(simplifier_solver& s) :dependent_expr_state(s.m), s(s), m_reconstruction_trail(s.m, m_trail) {} + ~dep_expr_state() override {} + virtual unsigned qtail() const override { return s.m_fmls.size(); } + dependent_expr const& operator[](unsigned i) override { return s.m_fmls[i]; } + void update(unsigned i, dependent_expr const& j) override { + SASSERT(j.fml()); + check_false(j.fml()); + s.m_fmls[i] = j; + } + void add(dependent_expr const& j) override { check_false(j.fml()); s.m_fmls.push_back(j); } + bool inconsistent() override { return s.m_inconsistent; } + model_reconstruction_trail& model_trail() override { return m_reconstruction_trail; } + std::ostream& display(std::ostream& out) const override { + unsigned i = 0; + for (auto const& d : s.m_fmls) { + if (i > 0 && i == qhead()) + out << "---- head ---\n"; + out << d << "\n"; + ++i; + } + m_reconstruction_trail.display(out); + return out; + } + void check_false(expr* f) { + if (s.m.is_false(f)) + s.set_inconsistent(); + } + void append(generic_model_converter& mc) { model_trail().append(mc); } + void replay(unsigned qhead, expr_ref_vector& assumptions) { m_reconstruction_trail.replay(qhead, assumptions, *this); } + void flatten_suffix() override { + expr_mark seen; + unsigned j = qhead(); + for (unsigned i = qhead(); i < qtail(); ++i) { + expr* f = s.m_fmls[i].fml(); + if (seen.is_marked(f)) + continue; + seen.mark(f, true); + if (s.m.is_true(f)) + continue; + if (s.m.is_and(f)) { + auto* d = s.m_fmls[i].dep(); + for (expr* arg : *to_app(f)) + add(dependent_expr(s.m, arg, nullptr, d)); + continue; + } + if (i != j) + s.m_fmls[j] = s.m_fmls[i]; + ++j; + } + s.m_fmls.shrink(j); + } + }; + + ast_manager& m; + scoped_ptr s; + vector m_fmls; + dep_expr_state m_preprocess_state; + seq_simplifier m_preprocess; + expr_ref_vector m_assumptions; + generic_model_converter_ref m_mc; + bool m_inconsistent = false; + + void flush(expr_ref_vector& assumptions) { + unsigned qhead = m_preprocess_state.qhead(); + if (qhead < m_fmls.size()) { + for (expr* a : assumptions) + m_preprocess_state.freeze(a); + TRACE("solver", tout << "qhead " << qhead << "\n"); + m_preprocess_state.replay(qhead, assumptions); + m_preprocess.reduce(); + if (!m.inc()) + return; + m_preprocess_state.advance_qhead(); + } + m_mc = alloc(generic_model_converter, m, "simplifier-model-converter"); + m_cached_mc = nullptr; + m_preprocess_state.append(*m_mc); + for (; qhead < m_fmls.size(); ++qhead) + add_with_dependency(m_fmls[qhead]); + } + + ptr_vector m_deps; + void add_with_dependency(dependent_expr const& de) { + if (!de.dep()) { + s->assert_expr(de.fml()); + return; + } + m_deps.reset(); + m.linearize(de.dep(), m_deps); + m_assumptions.reset(); + for (expr* d : m_deps) + m_assumptions.push_back(d); + s->assert_expr(de.fml(), mk_and(m_assumptions)); + } + + bool inconsistent() const { + return m_inconsistent; + } + + void set_inconsistent() { + if (!m_inconsistent) { + m_preprocess_state.m_trail.push(value_trail(m_inconsistent)); + m_inconsistent = true; + } + } + +public: + + simplifier_solver(solver* s) : + solver(s->get_manager()), + m(s->get_manager()), + s(s), + m_preprocess_state(*this), + m_preprocess(m, s->get_params(), m_preprocess_state), + m_assumptions(m), + m_proof(m) + { + init_preprocess(m, s->get_params(), m_preprocess, m_preprocess_state); + } + + void assert_expr_core2(expr* t, expr* a) override { + m_cached_model = nullptr; + m_cached_mc = nullptr; + proof* pr = m.proofs_enabled() ? m.mk_asserted(t) : nullptr; + m_fmls.push_back(dependent_expr(m, t, pr, m.mk_leaf(a))); + } + + void assert_expr_core(expr* t) override { + m_cached_model = nullptr; + m_cached_mc = nullptr; + proof* pr = m.proofs_enabled() ? m.mk_asserted(t) : nullptr; + m_fmls.push_back(dependent_expr(m, t, pr, nullptr)); + } + + void push() override { + expr_ref_vector none(m); + flush(none); + m_preprocess_state.push(); + m_preprocess.push(); + m_preprocess_state.m_trail.push(restore_vector(m_fmls)); + s->push(); + } + + void pop(unsigned n) override { + s->pop(n); + m_cached_model = nullptr; + m_preprocess.pop(n); + m_preprocess_state.pop(n); + } + + lbool check_sat_core(unsigned num_assumptions, expr* const* assumptions) override { + expr_ref_vector _assumptions(m, num_assumptions, assumptions); + flush(_assumptions); + return s->check_sat_core(num_assumptions, assumptions); + } + + void collect_statistics(statistics& st) const override { + s->collect_statistics(st); + m_preprocess.collect_statistics(st); + } + + model_ref m_cached_model; + void get_model_core(model_ref& m) override { + if (m_cached_model) { + m = m_cached_model; + return; + } + s->get_model(m); + if (m_mc) + (*m_mc)(m); + m_cached_model = m; + } + + proof_ref m_proof; + proof* get_proof_core() { + proof* p = s->get_proof(); + m_proof = p; + if (p) { + expr_ref tmp(p, m); + expr_safe_replace sub(m); + for (auto const& d : m_fmls) { + if (d.pr()) + sub.insert(m.mk_asserted(d.fml()), d.pr()); + } + sub(tmp); + SASSERT(is_app(tmp)); + m_proof = to_app(tmp); + } + return m_proof; + } + + solver* translate(ast_manager& m, params_ref const& p) override { + solver* new_s = s->translate(m, p); + ast_translation tr(get_manager(), m); + simplifier_solver* result = alloc(simplifier_solver, new_s); + for (dependent_expr const& f : m_fmls) + result->m_fmls.push_back(dependent_expr(tr, f)); + if (m_mc) + result->m_mc = dynamic_cast(m_mc->translate(tr)); + + // copy m_preprocess_state? + return result; + } + + void updt_params(params_ref const& p) override { + s->updt_params(p); + m_preprocess.updt_params(p); + } + + mutable model_converter_ref m_cached_mc; + model_converter_ref get_model_converter() const override { + if (!m_cached_mc) + m_cached_mc = concat(solver::get_model_converter().get(), m_mc.get(), s->get_model_converter().get()); + return m_cached_mc; + } + + unsigned get_num_assertions() const override { return s->get_num_assertions(); } + expr* get_assertion(unsigned idx) const override { return s->get_assertion(idx); } + std::string reason_unknown() const override { return s->reason_unknown(); } + void set_reason_unknown(char const* msg) override { s->set_reason_unknown(msg); } + void get_labels(svector& r) override { s->get_labels(r); } + void get_unsat_core(expr_ref_vector& r) { s->get_unsat_core(r); } + ast_manager& get_manager() const override { return s->get_manager(); } + void reset_params(params_ref const& p) override { s->reset_params(p); } + params_ref const& get_params() const override { return s->get_params(); } + void collect_param_descrs(param_descrs& r) override { s->collect_param_descrs(r); } + void push_params() override { s->push_params(); } + void pop_params() override { s->pop_params(); } + void set_produce_models(bool f) override { s->set_produce_models(f); } + void set_phase(expr* e) override { s->set_phase(e); } + void move_to_front(expr* e) override { s->move_to_front(e); } + phase* get_phase() override { return s->get_phase(); } + void set_phase(phase* p) override { s->set_phase(p); } + unsigned get_num_assumptions() const override { return s->get_num_assumptions(); } + expr* get_assumption(unsigned idx) const override { return s->get_assumption(idx); } + unsigned get_scope_level() const override { return s->get_scope_level(); } + lbool check_sat_cc(expr_ref_vector const& cube, vector const& clauses) override { return check_sat_cc(cube, clauses); } + void set_progress_callback(progress_callback* callback) override { s->set_progress_callback(callback); } + lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { + return s->get_consequences(asms, vars, consequences); + } + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return s->find_mutexes(vars, mutexes); } + lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override { return s->preferred_sat(asms, cores); } + + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { return s->cube(vars, backtrack_level); } + expr* congruence_root(expr* e) override { return s->congruence_root(e); } + expr* congruence_next(expr* e) override { return s->congruence_next(e); } + std::ostream& display(std::ostream& out, unsigned n, expr* const* assumptions) const override { + return s->display(out, n, assumptions); + } + void get_units_core(expr_ref_vector& units) override { s->get_units_core(units); } + expr_ref_vector get_trail(unsigned max_level) override { return s->get_trail(max_level); } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { s->get_levels(vars, depth); } + + void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause) override { + s->register_on_clause(ctx, on_clause); + } + + void user_propagate_init( + void* ctx, + user_propagator::push_eh_t& push_eh, + user_propagator::pop_eh_t& pop_eh, + user_propagator::fresh_eh_t& fresh_eh) override { + s->user_propagate_init(ctx, push_eh, pop_eh, fresh_eh); + } + void user_propagate_register_fixed(user_propagator::fixed_eh_t& fixed_eh) override { s->user_propagate_register_fixed(fixed_eh); } + void user_propagate_register_final(user_propagator::final_eh_t& final_eh) override { s->user_propagate_register_final(final_eh); } + void user_propagate_register_eq(user_propagator::eq_eh_t& eq_eh) override { s->user_propagate_register_eq(eq_eh); } + void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh) override { s->user_propagate_register_diseq(diseq_eh); } + void user_propagate_register_expr(expr* e) override { /*m_preprocess.user_propagate_register_expr(e); */ s->user_propagate_register_expr(e); } + void user_propagate_register_created(user_propagator::created_eh_t& r) override { s->user_propagate_register_created(r); } + // void user_propagate_clear() override { m_preprocess.user_propagate_clear(); s->user_propagate_clear(); } + + +}; + +solver* mk_simplifier_solver(solver* s) { + return alloc(simplifier_solver, s); +} + diff --git a/src/solver/simplifier_solver.h b/src/solver/simplifier_solver.h new file mode 100644 index 00000000000..3d97a3869a0 --- /dev/null +++ b/src/solver/simplifier_solver.h @@ -0,0 +1,25 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + simplifier_solver.cpp + +Abstract: + + Implements a solver with simplifying pre-processing. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-30 + +--*/ +#pragma once + +#include "util/params.h" + +class solver; +class solver_factory; + +solver * mk_simplifier_solver(solver * s); + diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp new file mode 100644 index 00000000000..71c6d975a68 --- /dev/null +++ b/src/solver/solver_preprocess.cpp @@ -0,0 +1,78 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + solver_preprocess.cpp + +Abstract: + + pre-process initialization module for solver + +Author: + + Nikolaj Bjorner (nbjorner) 2022-11-28 + +Notes: + + - port various pre-processing to simplifiers + - qe-lite, fm-elimination, ite-lifting, other from asserted_formulas +--*/ + + +#include "ast/rewriter/rewriter_def.h" +#include "ast/simplifiers/bit_blaster.h" +#include "ast/simplifiers/max_bv_sharing.h" +#include "ast/simplifiers/card2bv.h" +#include "ast/simplifiers/propagate_values.h" +#include "ast/simplifiers/rewriter_simplifier.h" +#include "ast/simplifiers/solve_eqs.h" +#include "ast/simplifiers/bv_slice.h" +#include "ast/simplifiers/eliminate_predicates.h" +#include "ast/simplifiers/elim_unconstrained.h" +#include "ast/simplifiers/pull_nested_quantifiers.h" +#include "ast/simplifiers/distribute_forall.h" +#include "ast/simplifiers/refine_inj_axiom.h" +#include "ast/simplifiers/elim_bounds.h" +#include "ast/simplifiers/bit2int.h" +#include "ast/simplifiers/bv_elim.h" +#include "ast/simplifiers/push_ite.h" +#include "ast/simplifiers/elim_term_ite.h" +#include "ast/simplifiers/flatten_clauses.h" +#include "ast/simplifiers/cnf_nnf.h" +#include "smt/params/smt_params.h" +#include "solver/solver_preprocess.h" +#include "qe/lite/qe_lite.h" + +void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { + + smt_params smtp(p); + s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); + if (smtp.m_propagate_values) s.add_simplifier(alloc(propagate_values, m, p, st)); + if (smtp.m_solve_eqs) s.add_simplifier(alloc(euf::solve_eqs, m, st)); + if (smtp.m_elim_unconstrained) s.add_simplifier(alloc(elim_unconstrained, m, st)); + if (smtp.m_nnf_cnf) s.add_simplifier(alloc(cnf_nnf_simplifier, m, p, st)); + if (smtp.m_macro_finder || smtp.m_quasi_macros) s.add_simplifier(alloc(eliminate_predicates, m, st)); + if (smtp.m_qe_lite) s.add_simplifier(mk_qe_lite_simplifer(m, p, st)); + if (smtp.m_pull_nested_quantifiers) s.add_simplifier(alloc(pull_nested_quantifiers_simplifier, m, p, st)); + if (smtp.m_max_bv_sharing) s.add_simplifier(mk_max_bv_sharing(m, p, st)); + if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); + if (smtp.m_bv_size_reduce) s.add_simplifier(alloc(bv::slice, m, st)); + if (smtp.m_distribute_forall) s.add_simplifier(alloc(distribute_forall_simplifier, m, p, st)); + if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); + if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); + if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); + if (smtp.m_eliminate_term_ite && smtp.m_lift_ite != lift_ite_kind::LI_FULL) s.add_simplifier(alloc(elim_term_ite_simplifier, m, p, st)); + if (smtp.m_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(push_ite_simplifier, m, p, st, smtp.m_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); + if (smtp.m_ng_lift_ite != lift_ite_kind::LI_NONE) s.add_simplifier(alloc(ng_push_ite_simplifier, m, p, st, smtp.m_ng_lift_ite == lift_ite_kind::LI_CONSERVATIVE)); + s.add_simplifier(alloc(flatten_clauses, m, p, st)); + + // + // add: + // euf_completion? + // + // add: make it externally programmable + // + +} + diff --git a/src/sat/sat_solver/sat_smt_preprocess.h b/src/solver/solver_preprocess.h similarity index 94% rename from src/sat/sat_solver/sat_smt_preprocess.h rename to src/solver/solver_preprocess.h index e7b40a8a34b..c0dfc42f3fd 100644 --- a/src/sat/sat_solver/sat_smt_preprocess.h +++ b/src/solver/solver_preprocess.h @@ -3,7 +3,7 @@ Copyright (c) 2022 Microsoft Corporation Module Name: - sat_smt_preprocess.h + solver_preprocess.h Abstract: From 6022c17131218640ba1a305f72fbcd0d4ca7eb98 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2023 22:38:51 -0800 Subject: [PATCH 347/597] Add simplification customization for SMTLIB2 Add the ability to customize incremental pre-processing simplification for the SMTLIB2 front-end. The main new capability is to use pre-processing tactics in incremental mode that were previously not available. The main new capabilities are - solve-eqs - reduce-args - elim-unconstrained There are several more. Documentation and exposed simplifiers are populated incrementally. The current set of supported simplifiers can be inspected by using z3 with the --simplifiers flag or referring to https://microsoft.github.io/z3guide/docs/strategies/simplifiers Some pending features are: - add the ability to update parameters to simplifiers similar to how tactics can be controlled using parameters. - expose simplification solvers over the binary API. --- scripts/mk_genfile_common.py | 23 ++- src/ast/simplifiers/CMakeLists.txt | 4 + src/ast/simplifiers/bit_blaster.h | 3 + src/ast/simplifiers/dependent_expr_state.h | 14 ++ .../model_reconstruction_trail.cpp | 14 +- .../simplifiers/model_reconstruction_trail.h | 5 - src/ast/simplifiers/rewriter_simplifier.h | 4 + src/cmd_context/CMakeLists.txt | 1 + src/cmd_context/cmd_context.cpp | 4 +- src/cmd_context/cmd_context.h | 4 +- src/cmd_context/simplifier_cmds.cpp | 158 ++++++++++++++++++ src/cmd_context/simplifier_cmds.h | 42 +++++ src/cmd_context/tactic_manager.cpp | 43 +++-- src/cmd_context/tactic_manager.h | 37 ++-- src/sat/sat_solver/sat_smt_solver.cpp | 2 +- src/shell/main.cpp | 7 + src/shell/smtlib_frontend.cpp | 35 ++++ src/shell/smtlib_frontend.h | 2 + src/solver/simplifier_solver.cpp | 18 +- src/solver/simplifier_solver.h | 5 +- src/tactic/arith/bound_simplifier_tactic.h | 1 + src/tactic/arith/card2bv_tactic.h | 1 + src/tactic/arith/propagate_ineqs_tactic.h | 1 + src/tactic/bv/bv_bounds_tactic.h | 2 + src/tactic/core/demodulator_tactic.h | 1 + src/tactic/core/distribute_forall_tactic.h | 1 + src/tactic/core/dom_simplify_tactic.h | 1 + src/tactic/core/elim_uncnstr2_tactic.h | 1 + src/tactic/core/eliminate_predicates_tactic.h | 1 + src/tactic/core/propagate_values2_tactic.h | 1 + src/tactic/core/reduce_args_tactic.h | 2 + src/tactic/core/solve_eqs_tactic.h | 1 + 32 files changed, 370 insertions(+), 69 deletions(-) create mode 100644 src/cmd_context/simplifier_cmds.cpp create mode 100644 src/cmd_context/simplifier_cmds.h diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index 3b19b8c2502..e242fa443f4 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -672,6 +672,7 @@ def mk_install_tactic_cpp_internal(h_files_full_path, path): components. """ ADD_TACTIC_DATA = [] + ADD_SIMPLIFIER_DATA = [] ADD_PROBE_DATA = [] def ADD_TACTIC(name, descr, cmd): ADD_TACTIC_DATA.append((name, descr, cmd)) @@ -679,9 +680,13 @@ def ADD_TACTIC(name, descr, cmd): def ADD_PROBE(name, descr, cmd): ADD_PROBE_DATA.append((name, descr, cmd)) + def ADD_SIMPLIFIER(name, descr, cmd): + ADD_SIMPLIFIER_DATA.append((name, descr, cmd)) + eval_globals = { 'ADD_TACTIC': ADD_TACTIC, 'ADD_PROBE': ADD_PROBE, + 'ADD_SIMPLIFIER': ADD_SIMPLIFIER } assert isinstance(h_files_full_path, list) @@ -691,9 +696,11 @@ def ADD_PROBE(name, descr, cmd): fout.write('// Automatically generated file.\n') fout.write('#include "tactic/tactic.h"\n') fout.write('#include "cmd_context/tactic_cmds.h"\n') + fout.write('#include "cmd_context/simplifier_cmds.h"\n') fout.write('#include "cmd_context/cmd_context.h"\n') tactic_pat = re.compile('[ \t]*ADD_TACTIC\(.*\)') - probe_pat = re.compile('[ \t]*ADD_PROBE\(.*\)') + probe_pat = re.compile('[ \t]*ADD_PROBE\(.*\)') + simplifier_pat = re.compile('[ \t]*ADD_SIMPLIFIER\(.*\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False try: @@ -719,17 +726,31 @@ def ADD_PROBE(name, descr, cmd): _logger.error("Failed processing ADD_PROBE command at '{}'\n{}".format( fullname, line)) raise e + if simplifier_pat.match(line): + if not added_include: + added_include = True + fout.write('#include "%s"\n' % path_after_src(h_file)) + try: + eval(line.strip('\n '), eval_globals, None) + except Exception as e: + _logger.error("Failed processing ADD_SIMPLIFIER command at '{}'\n{}".format( + fullname, line)) + raise e + except Exception as e: _logger.error("Failed to read file {}\n".format(h_file)) raise e # First pass will just generate the tactic factories fout.write('#define ADD_TACTIC_CMD(NAME, DESCR, CODE) ctx.insert(alloc(tactic_cmd, symbol(NAME), DESCR, [](ast_manager &m, const params_ref &p) { return CODE; }))\n') fout.write('#define ADD_PROBE(NAME, DESCR, PROBE) ctx.insert(alloc(probe_info, symbol(NAME), DESCR, PROBE))\n') + fout.write('#define ADD_SIMPLIFIER_CMD(NAME, DESCR, FUNCTION) ctx.insert(alloc(simplifier_cmd, symbol(NAME), DESCR, FUNCTION))\n') fout.write('void install_tactics(tactic_manager & ctx) {\n') for data in ADD_TACTIC_DATA: fout.write(' ADD_TACTIC_CMD("%s", "%s", %s);\n' % data) for data in ADD_PROBE_DATA: fout.write(' ADD_PROBE("%s", "%s", %s);\n' % data) + for data in ADD_SIMPLIFIER_DATA: + fout.write(' ADD_SIMPLIFIER_CMD("%s", "%s", %s);\n' % data) fout.write('}\n') fout.close() return fullname diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index 127db786a3a..e30224402ab 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -29,4 +29,8 @@ z3_add_component(simplifiers normal_forms rewriter substitution + TACTIC_HEADERS + bit_blaster.h + rewriter_simplifier.h + ) diff --git a/src/ast/simplifiers/bit_blaster.h b/src/ast/simplifiers/bit_blaster.h index 5724cc07599..82043170621 100644 --- a/src/ast/simplifiers/bit_blaster.h +++ b/src/ast/simplifiers/bit_blaster.h @@ -49,3 +49,6 @@ class bit_blaster_simplifier : public dependent_expr_simplifier { }; +/* + ADD_SIMPLIFIER("bit-blaster", "reduce bit-vector expressions into SAT.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bit_blaster_simplifier, m, p, s); }") +*/ diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index 0fedf484ceb..d4d449cf846 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -32,6 +32,7 @@ Module Name: #include "util/trail.h" #include "util/statistics.h" #include "util/params.h" +#include "util/z3_exception.h" #include "ast/converters/model_converter.h" #include "ast/simplifiers/dependent_expr.h" #include "ast/simplifiers/model_reconstruction_trail.h" @@ -98,6 +99,17 @@ class dependent_expr_state { bool has_quantifiers(); }; +class default_dependent_expr_state : public dependent_expr_state { +public: + default_dependent_expr_state(ast_manager& m): dependent_expr_state(m) {} + virtual unsigned qtail() const { return 0; } + virtual dependent_expr const& operator[](unsigned i) { throw default_exception("unexpected access"); } + virtual void update(unsigned i, dependent_expr const& j) { throw default_exception("unexpected update"); } + virtual void add(dependent_expr const& j) { throw default_exception("unexpected addition"); } + virtual bool inconsistent() { return false; } + virtual model_reconstruction_trail& model_trail() { throw default_exception("unexpected access to model reconstruction"); } +}; + inline std::ostream& operator<<(std::ostream& out, dependent_expr_state& st) { return st.display(out); } @@ -150,3 +162,5 @@ class dependent_expr_simplifier { ast_manager& get_manager() { return m; } dependent_expr_state& get_fmls() { return m_fmls; } }; + +typedef std::function simplifier_factory; diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 82d56f49117..22b600deda9 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -139,18 +139,15 @@ void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumpt */ model_converter_ref model_reconstruction_trail::get_model_converter() { generic_model_converter_ref mc = alloc(generic_model_converter, m, "dependent-expr-model"); - unsigned i = 0; - append(*mc, i); + append(*mc); return model_converter_ref(mc.get()); } /** * Append model conversions starting at index i */ -void model_reconstruction_trail::append(generic_model_converter& mc, unsigned& i) { - TRACE("simplifier", display(tout)); - for (; i < m_trail.size(); ++i) { - auto* t = m_trail[i]; +void model_reconstruction_trail::append(generic_model_converter& mc) { + for (auto* t : m_trail) { if (!t->m_active) continue; else if (t->is_hide()) @@ -163,13 +160,10 @@ void model_reconstruction_trail::append(generic_model_converter& mc, unsigned& i mc.add(v, def); } } + TRACE("simplifier", display(tout); mc.display(tout)); } -void model_reconstruction_trail::append(generic_model_converter& mc) { - m_trail_stack.push(value_trail(m_trail_index)); - append(mc, m_trail_index); -} std::ostream& model_reconstruction_trail::display(std::ostream& out) const { for (auto* t : m_trail) { diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 36ddde9ffdb..89a112c243f 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -84,7 +84,6 @@ class model_reconstruction_trail { ast_manager& m; trail_stack& m_trail_stack; scoped_ptr_vector m_trail; - unsigned m_trail_index = 0; void add_vars(expr* e, ast_mark& free_vars) { for (expr* t : subterms::all(expr_ref(e, m))) @@ -108,10 +107,6 @@ class model_reconstruction_trail { return any_of(added, [&](dependent_expr const& d) { return intersects(free_vars, d); }); } - /** - * Append new updates to model converter, update the current index into the trail in the process. - */ - void append(generic_model_converter& mc, unsigned& index); public: model_reconstruction_trail(ast_manager& m, trail_stack& tr): diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h index f3dc91a51f3..f74c213d461 100644 --- a/src/ast/simplifiers/rewriter_simplifier.h +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -53,3 +53,7 @@ class rewriter_simplifier : public dependent_expr_simplifier { void updt_params(params_ref const& p) override { m_params.append(p); m_rewriter.updt_params(m_params); } void collect_param_descrs(param_descrs& r) override { th_rewriter::get_param_descrs(r); } }; + +/* + ADD_SIMPLIFIER("simplify", "apply simplification rules.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(rewriter_simplifier, m, p, s); }") + */ diff --git a/src/cmd_context/CMakeLists.txt b/src/cmd_context/CMakeLists.txt index f8c1aa38f5f..f3cdb3c0347 100644 --- a/src/cmd_context/CMakeLists.txt +++ b/src/cmd_context/CMakeLists.txt @@ -9,6 +9,7 @@ z3_add_component(cmd_context parametric_cmd.cpp pdecl.cpp simplify_cmd.cpp + simplifier_cmds.cpp tactic_cmds.cpp tactic_manager.cpp COMPONENT_DEPENDENCIES diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 5602f53a37b..89733a7eacb 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -546,6 +546,7 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): install_basic_cmds(*this); install_ext_basic_cmds(*this); install_core_tactic_cmds(*this); + install_core_simplifier_cmds(*this); m_mcs.push_back(nullptr); SASSERT(m != 0 || !has_manager()); if (m_main_ctx) { @@ -559,8 +560,7 @@ cmd_context::~cmd_context() { } pop(m_scopes.size()); finalize_cmds(); - finalize_tactic_cmds(); - finalize_probes(); + finalize_tactic_manager(); m_proof_cmds = nullptr; reset(true); m_mcs.reset(); diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 93d2c0d3e15..f5a2477943b 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -318,7 +318,7 @@ class cmd_context : public progress_callback, public tactic_manager, public ast_ void register_builtin_ops(decl_plugin * p); void load_plugin(symbol const & name, bool install_names, svector& fids); void init_manager_core(bool new_manager); - void init_manager(); + void init_external_manager(); void reset_cmds(); void finalize_cmds(); @@ -414,7 +414,9 @@ class cmd_context : public progress_callback, public tactic_manager, public ast_ sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; } proof_cmds* get_proof_cmds() { return m_proof_cmds.get(); } + void init_manager(); solver* get_solver() { return m_solver.get(); } + void set_solver(solver* s) { m_solver = s; } void set_proof_cmds(proof_cmds* pc) { m_proof_cmds = pc; } void set_solver_factory(solver_factory * s); diff --git a/src/cmd_context/simplifier_cmds.cpp b/src/cmd_context/simplifier_cmds.cpp new file mode 100644 index 00000000000..b8ce15081e0 --- /dev/null +++ b/src/cmd_context/simplifier_cmds.cpp @@ -0,0 +1,158 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + simplifier_cmds.h + +Abstract: + Support for simplifier commands in SMT 2.0 front-end + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-30 + +--*/ +#include +#include "cmd_context/simplifier_cmds.h" +#include "cmd_context/cmd_context.h" +#include "cmd_context/cmd_util.h" +#include "cmd_context/parametric_cmd.h" +#include "model/model_smt2_pp.h" +#include "ast/ast_smt2_pp.h" +#include "ast/simplifiers/seq_simplifier.h" +#include "solver/simplifier_solver.h" + +typedef dependent_expr_simplifier simplifier; + +static simplifier_factory mk_and_then(cmd_context & ctx, sexpr * n) { + SASSERT(n->is_composite()); + unsigned num_children = n->get_num_children(); + if (num_children < 2) + throw cmd_exception("invalid and-then combinator, at least one argument expected", n->get_line(), n->get_pos()); + if (num_children == 2) + return sexpr2simplifier(ctx, n->get_child(1)); + vector args; + for (unsigned i = 1; i < num_children; i++) + args.push_back(sexpr2simplifier(ctx, n->get_child(i))); + simplifier_factory result = [args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { + scoped_ptr s = alloc(seq_simplifier, m, p, st); + for (auto & simp : args) + s->add_simplifier(simp(m, p, st)); + return s.detach(); + }; + return result; +} + +static simplifier_factory mk_using_params(cmd_context & ctx, sexpr * n) { + SASSERT(n->is_composite()); + unsigned num_children = n->get_num_children(); + if (num_children < 2) + throw cmd_exception("invalid using-params combinator, at least one argument expected", n->get_line(), n->get_pos()); + if (num_children == 2) + return sexpr2simplifier(ctx, n->get_child(1)); + simplifier_factory t = sexpr2simplifier(ctx, n->get_child(1)); +#if 0 + // hoist parameter parsing code from tactic_cmds to share. + param_descrs descrs; + t->collect_param_descrs(descrs); + +#endif + return t; +// return using_params(t.detach(), p); +} + + +simplifier_factory sexpr2simplifier(cmd_context & ctx, sexpr * n) { + if (n->is_symbol()) { + simplifier_cmd * cmd = ctx.find_simplifier_cmd(n->get_symbol()); + if (cmd != nullptr) + return cmd->factory(); + throw cmd_exception("invalid tactic, unknown tactic ", n->get_symbol(), n->get_line(), n->get_pos()); + } + else if (n->is_composite()) { + unsigned num_children = n->get_num_children(); + if (num_children == 0) + throw cmd_exception("invalid tactic, arguments expected", n->get_line(), n->get_pos()); + sexpr * head = n->get_child(0); + if (!head->is_symbol()) + throw cmd_exception("invalid tactic, symbol expected", n->get_line(), n->get_pos()); + symbol const & cmd_name = head->get_symbol(); + if (cmd_name == "and-then" || cmd_name == "then") + return mk_and_then(ctx, n); + else + throw cmd_exception("invalid tactic, unknown tactic combinator ", cmd_name, n->get_line(), n->get_pos()); + } + else { + throw cmd_exception("invalid tactic, unexpected input", n->get_line(), n->get_pos()); + } +} + + +void help_simplifier(cmd_context & ctx) { + std::ostringstream buf; + buf << "combinators:\n"; + buf << "- (and-then +) executes the given simplifiers sequentially.\n"; + // buf << "- (using-params *) executes the given tactic using the given attributes, where ::= . ! is a syntax sugar for using-params.\n"; + buf << "builtin simplifiers:\n"; + for (simplifier_cmd* cmd : ctx.simplifiers()) { + buf << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n"; + auto fac = cmd->factory(); + param_descrs descrs; + ast_manager& m = ctx.get_ast_manager(); + default_dependent_expr_state st(m); + params_ref p; + scoped_ptr s = fac(m, p, st); + s->collect_param_descrs(descrs); + descrs.display(buf, 4); + } + ctx.regular_stream() << '"' << escaped(buf.str()) << "\"\n"; +} + +ATOMIC_CMD(help_simplifier_cmd, "help-simplifier", "display the simplifier combinators and primitives.", help_simplifier(ctx);); + +class set_simplifier_cmd : public parametric_cmd { +protected: + sexpr * m_simplifier; +public: + set_simplifier_cmd(): + parametric_cmd("set-simplifier") {} + + char const * get_usage() const override { return " ( )*"; } + + void prepare(cmd_context & ctx) override { + parametric_cmd::prepare(ctx); + m_simplifier = nullptr; + } + + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_simplifier == nullptr) return CPK_SEXPR; + return parametric_cmd::next_arg_kind(ctx); + } + + void set_next_arg(cmd_context & ctx, sexpr * arg) override { + m_simplifier = arg; + } + + char const * get_main_descr() const override { return "update main solver with simplification pre-processing."; } + + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { + } + + void execute(cmd_context & ctx) override { + if (!m_simplifier) + throw cmd_exception("set-simplifier needs a simplifier argument"); + + auto simplifier_factory = sexpr2simplifier(ctx, m_simplifier); + ctx.init_manager(); + auto* s = ctx.get_solver(); + if (s) + ctx.set_solver(mk_simplifier_solver(s, &simplifier_factory)); + } +}; + + +void install_core_simplifier_cmds(cmd_context & ctx) { + ctx.insert(alloc(set_simplifier_cmd)); + ctx.insert(alloc(help_simplifier_cmd)); +} diff --git a/src/cmd_context/simplifier_cmds.h b/src/cmd_context/simplifier_cmds.h new file mode 100644 index 00000000000..e75376dfb9c --- /dev/null +++ b/src/cmd_context/simplifier_cmds.h @@ -0,0 +1,42 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + simplifier_cmds.h + +Abstract: + Support for simplifier commands in SMT 2.0 front-end + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-30 + +--*/ +#pragma once + +#include "ast/ast.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "util/params.h" +#include "util/cmd_context_types.h" +#include "util/ref.h" + + +class simplifier_cmd { + symbol m_name; + char const * m_descr; + simplifier_factory m_factory; +public: + simplifier_cmd(symbol const & n, char const * d, simplifier_factory f): + m_name(n), m_descr(d), m_factory(f) {} + + symbol get_name() const { return m_name; } + + char const * get_descr() const { return m_descr; } + + simplifier_factory factory() { return m_factory; } +}; + +simplifier_factory sexpr2simplifier(cmd_context & ctx, sexpr * n); + +void install_core_simplifier_cmds(cmd_context & ctx); diff --git a/src/cmd_context/tactic_manager.cpp b/src/cmd_context/tactic_manager.cpp index d4b3374b4b1..1691f5b4584 100644 --- a/src/cmd_context/tactic_manager.cpp +++ b/src/cmd_context/tactic_manager.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - Collection of tactics & probes + Collection of tactics, simplifiers & probes Author: @@ -19,8 +19,21 @@ Module Name: #include "cmd_context/tactic_manager.h" tactic_manager::~tactic_manager() { - finalize_tactic_cmds(); - finalize_probes(); + finalize_tactic_manager(); +} + +void tactic_manager::finalize_tactic_manager() { + std::for_each(m_tactics.begin(), m_tactics.end(), delete_proc()); + m_tactics.reset(); + m_name2tactic.reset(); + + std::for_each(m_simplifiers.begin(), m_simplifiers.end(), delete_proc()); + m_simplifiers.reset(); + m_name2simplifier.reset(); + + std::for_each(m_probes.begin(), m_probes.end(), delete_proc()); + m_probes.reset(); + m_name2probe.reset(); } void tactic_manager::insert(tactic_cmd * c) { @@ -30,6 +43,13 @@ void tactic_manager::insert(tactic_cmd * c) { m_tactics.push_back(c); } +void tactic_manager::insert(simplifier_cmd * c) { + symbol const & s = c->get_name(); + SASSERT(!m_name2simplifier.contains(s)); + m_name2simplifier.insert(s, c); + m_simplifiers.push_back(c); +} + void tactic_manager::insert(probe_info * p) { symbol const & s = p->get_name(); SASSERT(!m_name2probe.contains(s)); @@ -43,20 +63,15 @@ tactic_cmd * tactic_manager::find_tactic_cmd(symbol const & s) const { return c; } +simplifier_cmd * tactic_manager::find_simplifier_cmd(symbol const & s) const { + simplifier_cmd * c = nullptr; + m_name2simplifier.find(s, c); + return c; +} + probe_info * tactic_manager::find_probe(symbol const & s) const { probe_info * p = nullptr; m_name2probe.find(s, p); return p; } -void tactic_manager::finalize_tactic_cmds() { - std::for_each(m_tactics.begin(), m_tactics.end(), delete_proc()); - m_tactics.reset(); - m_name2tactic.reset(); -} - -void tactic_manager::finalize_probes() { - std::for_each(m_probes.begin(), m_probes.end(), delete_proc()); - m_probes.reset(); - m_name2probe.reset(); -} diff --git a/src/cmd_context/tactic_manager.h b/src/cmd_context/tactic_manager.h index 3a57d6297d6..ec60b087324 100644 --- a/src/cmd_context/tactic_manager.h +++ b/src/cmd_context/tactic_manager.h @@ -18,54 +18,37 @@ Module Name: #pragma once #include "cmd_context/tactic_cmds.h" +#include "cmd_context/simplifier_cmds.h" #include "util/dictionary.h" class tactic_manager { protected: dictionary m_name2tactic; dictionary m_name2probe; + dictionary m_name2simplifier; ptr_vector m_tactics; + ptr_vector m_simplifiers; ptr_vector m_probes; - void finalize_tactic_cmds(); - void finalize_probes(); + void finalize_tactic_manager(); public: ~tactic_manager(); void insert(tactic_cmd * c); + void insert(simplifier_cmd* c); void insert(probe_info * p); tactic_cmd * find_tactic_cmd(symbol const & s) const; probe_info * find_probe(symbol const & s) const; + simplifier_cmd* find_simplifier_cmd(symbol const& s) const; unsigned num_tactics() const { return m_tactics.size(); } unsigned num_probes() const { return m_probes.size(); } tactic_cmd * get_tactic(unsigned i) const { return m_tactics[i]; } probe_info * get_probe(unsigned i) const { return m_probes[i]; } + + ptr_vector const& simplifiers() const { return m_simplifiers; } + ptr_vector const& tactics() const { return m_tactics; } + ptr_vector const& probes() const { return m_probes; } - typedef ptr_vector::const_iterator tactic_cmd_iterator; - tactic_cmd_iterator begin_tactic_cmds() const { return m_tactics.begin(); } - tactic_cmd_iterator end_tactic_cmds() const { return m_tactics.end(); } - class tactics_iterator { - tactic_manager const& m; - public: - tactics_iterator(tactic_manager const& m):m(m) {} - tactic_cmd_iterator begin() const { return m.begin_tactic_cmds(); } - tactic_cmd_iterator end() const { return m.end_tactic_cmds(); } - }; - tactics_iterator tactics() const { return tactics_iterator(*this); } - - typedef ptr_vector::const_iterator probe_iterator; - probe_iterator begin_probes() const { return m_probes.begin(); } - probe_iterator end_probes() const { return m_probes.end(); } - - class probes_iterator { - tactic_manager const& m; - public: - probes_iterator(tactic_manager const& m):m(m) {} - probe_iterator begin() const { return m.begin_probes(); } - probe_iterator end() const { return m.end_probes(); } - }; - - probes_iterator probes() const { return probes_iterator(*this); } }; diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index dbc2d950517..82aeded6faa 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -700,6 +700,6 @@ class sat_smt_solver : public solver { solver* mk_sat_smt_solver(ast_manager& m, params_ref const& p) { - return mk_simplifier_solver(alloc(sat_smt_solver, m, p)); + return mk_simplifier_solver(alloc(sat_smt_solver, m, p), nullptr); } diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 2fdd95d79e8..4c26d91d968 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -98,6 +98,7 @@ void display_usage() { std::cout << " -pmmd:name display Z3 module ('name') parameters in Markdown format.\n"; std::cout << " -pp:name display Z3 parameter description, if 'name' is not provided, then all module names are listed.\n"; std::cout << " -tactics[:name] display built-in tactics or if argument is given, display detailed information on tactic.\n"; + std::cout << " -simplifiers[:name] display built-in simplifiers or if argument is given, display detailed information on simplifier.\n"; std::cout << " -probes display avilable probes.\n"; std::cout << " --" << " all remaining arguments are assumed to be part of the input file name. This option allows Z3 to read files with strange names such as: -foo.smt2.\n"; std::cout << "\nResources:\n"; @@ -295,6 +296,12 @@ static void parse_cmd_line_args(std::string& input_file, int argc, char ** argv) else help_tactic(opt_arg, false); } + else if (strcmp(opt_name, "simplifiers") == 0) { + if (!opt_arg) + help_simplifiers(); + else + help_simplifier(opt_arg, false); + } else if (strcmp(opt_name, "tacticsmd") == 0 && opt_arg) help_tactic(opt_arg, true); else if (strcmp(opt_name, "probes") == 0) diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 698f0f23962..008142c1a72 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -88,6 +88,22 @@ void help_tactics() { std::cout << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n"; } +void help_simplifiers() { + struct cmp { + bool operator()(simplifier_cmd* a, simplifier_cmd* b) const { + return a->get_name().str() < b->get_name().str(); + } + }; + cmd_context ctx; + ptr_vector cmds; + for (auto cmd : ctx.simplifiers()) + cmds.push_back(cmd); + cmp lt; + std::sort(cmds.begin(), cmds.end(), lt); + for (auto cmd : cmds) + std::cout << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n"; +} + void help_tactic(char const* name, bool markdown) { cmd_context ctx; for (auto cmd : ctx.tactics()) { @@ -103,6 +119,25 @@ void help_tactic(char const* name, bool markdown) { } } +void help_simplifier(char const* name, bool markdown) { + cmd_context ctx; + for (auto cmd : ctx.simplifiers()) { + if (cmd->get_name() == name) { + auto fac = cmd->factory(); + param_descrs descrs; + ast_manager& m = ctx.m(); + default_dependent_expr_state st(m); + params_ref p; + scoped_ptr s = fac(m, p, st); + s->collect_param_descrs(descrs); + if (markdown) + descrs.display_markdown(std::cout); + else + descrs.display(std::cout, 4); + } + } +} + void help_probes() { struct cmp { bool operator()(probe_info* a, probe_info* b) const { diff --git a/src/shell/smtlib_frontend.h b/src/shell/smtlib_frontend.h index 9769472f263..36818f3f61f 100644 --- a/src/shell/smtlib_frontend.h +++ b/src/shell/smtlib_frontend.h @@ -21,7 +21,9 @@ Revision History: unsigned read_smtlib_file(char const * benchmark_file); unsigned read_smtlib2_commands(char const * command_file); void help_tactics(); +void help_simplifiers(); void help_probes(); void help_tactic(char const* name, bool markdown); +void help_simplifier(char const* name, bool markdown); diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 17a39509e44..faaf5b28f4d 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -89,7 +89,7 @@ class simplifier_solver : public solver { }; ast_manager& m; - scoped_ptr s; + solver_ref s; vector m_fmls; dep_expr_state m_preprocess_state; seq_simplifier m_preprocess; @@ -143,7 +143,7 @@ class simplifier_solver : public solver { public: - simplifier_solver(solver* s) : + simplifier_solver(solver* s, simplifier_factory* fac) : solver(s->get_manager()), m(s->get_manager()), s(s), @@ -152,7 +152,10 @@ class simplifier_solver : public solver { m_assumptions(m), m_proof(m) { - init_preprocess(m, s->get_params(), m_preprocess, m_preprocess_state); + if (fac) + m_preprocess.add_simplifier((*fac)(m, s->get_params(), m_preprocess_state)); + else + init_preprocess(m, s->get_params(), m_preprocess, m_preprocess_state); } void assert_expr_core2(expr* t, expr* a) override { @@ -197,7 +200,8 @@ class simplifier_solver : public solver { } model_ref m_cached_model; - void get_model_core(model_ref& m) override { + void get_model_core(model_ref& m) override { + CTRACE("simplifier", m_mc.get(), m_mc->display(tout)); if (m_cached_model) { m = m_cached_model; return; @@ -229,7 +233,7 @@ class simplifier_solver : public solver { solver* translate(ast_manager& m, params_ref const& p) override { solver* new_s = s->translate(m, p); ast_translation tr(get_manager(), m); - simplifier_solver* result = alloc(simplifier_solver, new_s); + simplifier_solver* result = alloc(simplifier_solver, new_s, nullptr); // factory? for (dependent_expr const& f : m_fmls) result->m_fmls.push_back(dependent_expr(tr, f)); if (m_mc) @@ -311,7 +315,7 @@ class simplifier_solver : public solver { }; -solver* mk_simplifier_solver(solver* s) { - return alloc(simplifier_solver, s); +solver* mk_simplifier_solver(solver* s, simplifier_factory* fac) { + return alloc(simplifier_solver, s, fac); } diff --git a/src/solver/simplifier_solver.h b/src/solver/simplifier_solver.h index 3d97a3869a0..afc701ca052 100644 --- a/src/solver/simplifier_solver.h +++ b/src/solver/simplifier_solver.h @@ -20,6 +20,9 @@ Module Name: class solver; class solver_factory; +class dependent_expr_simplifier; +class dependent_expr_state; +typedef std::function simplifier_factory; -solver * mk_simplifier_solver(solver * s); +solver * mk_simplifier_solver(solver * s, simplifier_factory* fac); diff --git a/src/tactic/arith/bound_simplifier_tactic.h b/src/tactic/arith/bound_simplifier_tactic.h index c8bc1318c7c..5451caa206f 100644 --- a/src/tactic/arith/bound_simplifier_tactic.h +++ b/src/tactic/arith/bound_simplifier_tactic.h @@ -38,4 +38,5 @@ inline tactic* mk_bound_simplifier_tactic(ast_manager& m, params_ref const& p = /* ADD_TACTIC("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "mk_bound_simplifier_tactic(m, p)") + ADD_SIMPLIFIER("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }") */ diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index 416997d45de..032e964f95a 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -69,4 +69,5 @@ inline tactic* mk_card2bv_tactic(ast_manager& m, params_ref const& p = params_re /* ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)") + ADD_SIMPLIFIER("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(card2bv, m, p, s); }") */ diff --git a/src/tactic/arith/propagate_ineqs_tactic.h b/src/tactic/arith/propagate_ineqs_tactic.h index 1dbb0844b4e..68156c410ee 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.h +++ b/src/tactic/arith/propagate_ineqs_tactic.h @@ -64,5 +64,6 @@ inline tactic* mk_propagate_ineqs_tactic(ast_manager& m, params_ref const& p = p /* ADD_TACTIC("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "mk_propagate_ineqs_tactic(m, p)") + ADD_SIMPLIFIER("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }") */ diff --git a/src/tactic/bv/bv_bounds_tactic.h b/src/tactic/bv/bv_bounds_tactic.h index 8153ad52f75..eb75ceca7bc 100644 --- a/src/tactic/bv/bv_bounds_tactic.h +++ b/src/tactic/bv/bv_bounds_tactic.h @@ -37,6 +37,7 @@ Contextual bounds simplification tactic. --*/ #pragma once #include "tactic/tactic.h" +#include "ast/simplifiers/bv_bounds_simplifier.h" tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); @@ -45,6 +46,7 @@ tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ /* ADD_TACTIC("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_tactic(m, p)") + ADD_SIMPLIFIER("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_simplifier") ADD_TACTIC("propagate-bv-bounds2", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)") diff --git a/src/tactic/core/demodulator_tactic.h b/src/tactic/core/demodulator_tactic.h index 9ffaa6ca90e..507b4e5c5bd 100644 --- a/src/tactic/core/demodulator_tactic.h +++ b/src/tactic/core/demodulator_tactic.h @@ -100,4 +100,5 @@ inline tactic * mk_demodulator_tactic(ast_manager& m, params_ref const& p = para /* ADD_TACTIC("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "mk_demodulator_tactic(m, p)") + ADD_SIMPLIFIER("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(demodulator_simplifier, m, p, s); }") */ diff --git a/src/tactic/core/distribute_forall_tactic.h b/src/tactic/core/distribute_forall_tactic.h index 328a62b2f01..419eba71e62 100644 --- a/src/tactic/core/distribute_forall_tactic.h +++ b/src/tactic/core/distribute_forall_tactic.h @@ -50,5 +50,6 @@ inline tactic * mk_distribute_forall_tactic(ast_manager& m, params_ref const& p /* ADD_TACTIC("distribute-forall", "distribute forall over conjunctions.", "mk_distribute_forall_tactic(m, p)") + ADD_SIMPLIFIER("distribute-forall", "distribute forall over conjunctions.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(distribute_forall_simplifier, m, p, s); }") */ diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 2dba378b7c1..795db0f261c 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -56,5 +56,6 @@ inline tactic* mk_dom_simplify_tactic(ast_manager& m, params_ref const& p) { /* ADD_TACTIC("dom-simplify", "apply dominator simplification rules.", "mk_dom_simplify_tactic(m, p)") +ADD_SIMPLIFIER("dom-simplify", "apply dominator simplification rules.", "[](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* { return alloc(dominator_simplifier, m, s, mk_expr_substitution_simplifier(m), p); }") */ diff --git a/src/tactic/core/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h index f1c9b9bd7b2..fc249da643d 100644 --- a/src/tactic/core/elim_uncnstr2_tactic.h +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -118,4 +118,5 @@ inline tactic * mk_elim_uncnstr2_tactic(ast_manager & m, params_ref const & p = /* ADD_TACTIC("elim-uncnstr2", "eliminate unconstrained variables.", "mk_elim_uncnstr2_tactic(m, p)") + ADD_SIMPLIFIER("elim-unconstrained", "eliminate unconstrained variables.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(elim_unconstrained, m, s); }") */ diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index 4d90b1acfdb..b53892e8b8d 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -64,4 +64,5 @@ inline tactic * mk_eliminate_predicates_tactic(ast_manager& m, params_ref const& /* ADD_TACTIC("elim-predicates", "eliminate predicates.", "mk_eliminate_predicates_tactic(m, p)") + ADD_SIMPLIFIER("elim-predicates", "eliminate predicates.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(eliminate_predicates, m, s); }") */ diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h index e380ce7d382..466ef87a106 100644 --- a/src/tactic/core/propagate_values2_tactic.h +++ b/src/tactic/core/propagate_values2_tactic.h @@ -55,4 +55,5 @@ inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & /* ADD_TACTIC("propagate-values2", "propagate constants.", "mk_propagate_values2_tactic(m, p)") + ADD_SIMPLIFIER("propagate-values", "propagate constants.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(propagate_values, m, p, s); }") */ diff --git a/src/tactic/core/reduce_args_tactic.h b/src/tactic/core/reduce_args_tactic.h index fa90837bfde..4bfb7ed2f16 100644 --- a/src/tactic/core/reduce_args_tactic.h +++ b/src/tactic/core/reduce_args_tactic.h @@ -79,5 +79,7 @@ inline tactic* mk_reduce_args_tactic2(ast_manager& m, params_ref const& p = para } /* ADD_TACTIC("reduce-args2", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "mk_reduce_args_tactic2(m, p)") + ADD_SIMPLIFIER("reduce-args", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "[](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* { return mk_reduce_args_simplifier(m, s, p); }") + */ diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index fec46bf045c..af27d45760f 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -79,4 +79,5 @@ inline tactic * mk_solve_eqs_tactic(ast_manager& m, params_ref const& p = params /* ADD_TACTIC("solve-eqs", "solve for variables.", "mk_solve_eqs_tactic(m, p)") + ADD_SIMPLIFIER("solve-eqs", "solve for variables.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(euf::solve_eqs, m, s); }") */ From 5794d080b5d03fd815d8213ba27931f7b604fae2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2023 22:52:32 -0800 Subject: [PATCH 348/597] updated doc generation script Signed-off-by: Nikolaj Bjorner --- doc/mk_tactic_doc.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index fe1a495c146..8558ed2abb9 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -18,6 +18,7 @@ def doc_path(path): is_doc = re.compile("Tactic Documentation") is_doc_end = re.compile("\-\-\*\/") is_tac_name = re.compile("## Tactic (.*)") +is_simplifier = re.compile("ADD_SIMPLIFIER\(.*\"([^\"]*)\".*,.*\"([^\"]*)\".*,.*\"([^\"]*)\"\.*\)") def is_ws(s): return all([0 for ch in s if ch != ' ' and ch != '\n']) @@ -53,6 +54,19 @@ def extract_tactic_doc(ous, f): if is_doc.search(line): generate_tactic_doc(ous, f, ins) +def generate_simplifier_doc(ous, name, desc): + ous.write("## Simplifier [" + name + "](tactic-summary/#" + name + ")\n") + ous.write("### Description\n" + desc + "\n") + + +def extract_simplifier_doc(ous, f): + with open(f) as ins: + for line in ins: + m = is_simplifier.search(line) + if m: + generate_simplifier_doc(ous, m.group(1), m.group(2)) + return + def find_tactic_name(path): with open(path) as ins: for line in ins: @@ -66,13 +80,15 @@ def presort_files(): tac_files = [] for root, dirs, files in os.walk(doc_path("../src")): for f in files: - if f.endswith("tactic.h"): + if f.endswith("~"): + continue + if f.endswith("tactic.h") or "simplifiers" in root: tac_files += [(f, os.path.join(root, f))] tac_files = sorted(tac_files, key = lambda x: find_tactic_name(x[1])) return tac_files + def help(ous): - presort_files() ous.write("---\n") ous.write("title: Tactics Summary\n") ous.write("sidebar_position: 5\n") @@ -81,6 +97,17 @@ def help(ous): for file, path in tac_files: extract_tactic_doc(ous, path) + + +def help_simplifier(ous): + ous.write("---\n") + ous.write("title: Simplifier Summary\n") + ous.write("sidebar_position: 6\n") + ous.write("---\n") + tac_files = presort_files() + for file, path in tac_files: + extract_simplifier_doc(ous, path) + def mk_dir(d): if not os.path.exists(d): os.makedirs(d) @@ -89,3 +116,6 @@ def mk_dir(d): with open(OUTPUT_DIRECTORY + "/md/tactics-summary.md",'w') as ous: help(ous) + +with open(OUTPUT_DIRECTORY + "/md/simplifier-summary.md",'w') as ous: + help_simplifier(ous) From 238d604a101831a57c4e48baa5fe50e8643fe018 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2023 23:00:44 -0800 Subject: [PATCH 349/597] android 16 byte alignment for stack allocated memory? Signed-off-by: Nikolaj Bjorner --- doc/mk_tactic_doc.py | 2 +- src/cmd_context/simplifier_cmds.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index 8558ed2abb9..38b9be40a46 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -55,7 +55,7 @@ def extract_tactic_doc(ous, f): generate_tactic_doc(ous, f, ins) def generate_simplifier_doc(ous, name, desc): - ous.write("## Simplifier [" + name + "](tactic-summary/#" + name + ")\n") + ous.write("## Simplifier [" + name + "](summary/#tactic-" + name + ")\n") ous.write("### Description\n" + desc + "\n") diff --git a/src/cmd_context/simplifier_cmds.cpp b/src/cmd_context/simplifier_cmds.cpp index b8ce15081e0..47f20f3fd73 100644 --- a/src/cmd_context/simplifier_cmds.cpp +++ b/src/cmd_context/simplifier_cmds.cpp @@ -32,13 +32,15 @@ static simplifier_factory mk_and_then(cmd_context & ctx, sexpr * n) { throw cmd_exception("invalid and-then combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2simplifier(ctx, n->get_child(1)); - vector args; + scoped_ptr> args = alloc(vector); for (unsigned i = 1; i < num_children; i++) - args.push_back(sexpr2simplifier(ctx, n->get_child(i))); - simplifier_factory result = [args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { + args->push_back(sexpr2simplifier(ctx, n->get_child(i))); + vector* _args = args.detach(); + simplifier_factory result = [_args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { scoped_ptr s = alloc(seq_simplifier, m, p, st); - for (auto & simp : args) + for (auto & simp : *_args) s->add_simplifier(simp(m, p, st)); + dealloc(_args); return s.detach(); }; return result; From 971b9d4081a694ad0bd5482977639a963aa53296 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 09:32:34 -0800 Subject: [PATCH 350/597] fix #6564 fixes to simplifier command front-end --- doc/mk_tactic_doc.py | 2 +- src/api/java/Context.java | 4 +-- src/cmd_context/simplifier_cmds.cpp | 38 +++++++++++++++++------------ src/cmd_context/tactic_cmds.cpp | 21 +++++++++++----- src/cmd_context/tactic_cmds.h | 1 + src/solver/simplifier_solver.cpp | 3 +-- 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index 38b9be40a46..86e32aa1840 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -55,7 +55,7 @@ def extract_tactic_doc(ous, f): generate_tactic_doc(ous, f, ins) def generate_simplifier_doc(ous, name, desc): - ous.write("## Simplifier [" + name + "](summary/#tactic-" + name + ")\n") + ous.write("## Simplifier [" + name + "](../summary/#tactic-" + name + ")\n") ous.write("### Description\n" + desc + "\n") diff --git a/src/api/java/Context.java b/src/api/java/Context.java index bb3f6fe8e77..d4810123559 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2110,7 +2110,7 @@ public BoolExpr mkContains(Expr> s1, Expr * Check if the string s1 is lexicographically strictly less than s2. */ - public BoolExpr MkStringLt(SeqSort s1, SeqSort s2) + public BoolExpr MkStringLt(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return new BoolExpr(this, Native.mkStrLt(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2119,7 +2119,7 @@ public BoolExpr MkStringLt(SeqSort s1, SeqSort s2) /** * Check if the string s1 is lexicographically less or equal to s2. */ - public BoolExpr MkStringLe(SeqSort s1, SeqSort s2) + public BoolExpr MkStringLe(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return new BoolExpr(this, Native.mkStrLe(nCtx(), s1.getNativeObject(), s2.getNativeObject())); diff --git a/src/cmd_context/simplifier_cmds.cpp b/src/cmd_context/simplifier_cmds.cpp index 47f20f3fd73..0ea88e09518 100644 --- a/src/cmd_context/simplifier_cmds.cpp +++ b/src/cmd_context/simplifier_cmds.cpp @@ -14,6 +14,7 @@ Module Name: --*/ #include +#include #include "cmd_context/simplifier_cmds.h" #include "cmd_context/cmd_context.h" #include "cmd_context/cmd_util.h" @@ -32,15 +33,13 @@ static simplifier_factory mk_and_then(cmd_context & ctx, sexpr * n) { throw cmd_exception("invalid and-then combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2simplifier(ctx, n->get_child(1)); - scoped_ptr> args = alloc(vector); + std::vector args; for (unsigned i = 1; i < num_children; i++) - args->push_back(sexpr2simplifier(ctx, n->get_child(i))); - vector* _args = args.detach(); - simplifier_factory result = [_args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { + args.push_back(sexpr2simplifier(ctx, n->get_child(i))); + simplifier_factory result = [args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { scoped_ptr s = alloc(seq_simplifier, m, p, st); - for (auto & simp : *_args) + for (auto & simp : args) s->add_simplifier(simp(m, p, st)); - dealloc(_args); return s.detach(); }; return result; @@ -53,15 +52,22 @@ static simplifier_factory mk_using_params(cmd_context & ctx, sexpr * n) { throw cmd_exception("invalid using-params combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2simplifier(ctx, n->get_child(1)); - simplifier_factory t = sexpr2simplifier(ctx, n->get_child(1)); -#if 0 - // hoist parameter parsing code from tactic_cmds to share. + ast_manager& m = ctx.get_ast_manager(); + default_dependent_expr_state st(m); + + simplifier_factory fac = sexpr2simplifier(ctx, n->get_child(1)); + params_ref p; param_descrs descrs; - t->collect_param_descrs(descrs); - -#endif - return t; -// return using_params(t.detach(), p); + scoped_ptr s = fac(m, p, st); + s->collect_param_descrs(descrs); + params_ref params = sexpr2params(ctx, n, descrs); + simplifier_factory result = [params, fac](auto& m, auto& p, auto& s) { + params_ref pp; + pp.append(params); + pp.append(p); + return fac(m, pp, s); + }; + return result; } @@ -82,6 +88,8 @@ simplifier_factory sexpr2simplifier(cmd_context & ctx, sexpr * n) { symbol const & cmd_name = head->get_symbol(); if (cmd_name == "and-then" || cmd_name == "then") return mk_and_then(ctx, n); + else if (cmd_name == "!" || cmd_name == "using-params" || cmd_name == "with") + return mk_using_params(ctx, n); else throw cmd_exception("invalid tactic, unknown tactic combinator ", cmd_name, n->get_line(), n->get_pos()); } @@ -95,7 +103,7 @@ void help_simplifier(cmd_context & ctx) { std::ostringstream buf; buf << "combinators:\n"; buf << "- (and-then +) executes the given simplifiers sequentially.\n"; - // buf << "- (using-params *) executes the given tactic using the given attributes, where ::= . ! is a syntax sugar for using-params.\n"; + buf << "- (using-params *) executes the given simplifier using the given attributes, where ::= . ! is syntax sugar for using-params.\n"; buf << "builtin simplifiers:\n"; for (simplifier_cmd* cmd : ctx.simplifiers()) { buf << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n"; diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index af030e139ad..4559586f3f4 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -481,16 +481,11 @@ static tactic * mk_repeat(cmd_context & ctx, sexpr * n) { return repeat(t, max); } -static tactic * mk_using_params(cmd_context & ctx, sexpr * n) { +params_ref sexpr2params(cmd_context& ctx, sexpr * n, param_descrs const& descrs) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid using-params combinator, at least one argument expected", n->get_line(), n->get_pos()); - if (num_children == 2) - return sexpr2tactic(ctx, n->get_child(1)); - tactic_ref t = sexpr2tactic(ctx, n->get_child(1)); - param_descrs descrs; - t->collect_param_descrs(descrs); params_ref p; unsigned i = 2; while (i < num_children) { @@ -535,6 +530,20 @@ static tactic * mk_using_params(cmd_context & ctx, sexpr * n) { throw cmd_exception("invalid using-params combinator, unsupported parameter kind"); } } + return p; +} + +static tactic * mk_using_params(cmd_context & ctx, sexpr * n) { + SASSERT(n->is_composite()); + unsigned num_children = n->get_num_children(); + if (num_children < 2) + throw cmd_exception("invalid using-params combinator, at least one argument expected", n->get_line(), n->get_pos()); + if (num_children == 2) + return sexpr2tactic(ctx, n->get_child(1)); + tactic_ref t = sexpr2tactic(ctx, n->get_child(1)); + param_descrs descrs; + t->collect_param_descrs(descrs); + params_ref p = sexpr2params(ctx, n, descrs); return using_params(t.get(), p); } diff --git a/src/cmd_context/tactic_cmds.h b/src/cmd_context/tactic_cmds.h index be094840ce7..5096ae9629b 100644 --- a/src/cmd_context/tactic_cmds.h +++ b/src/cmd_context/tactic_cmds.h @@ -44,6 +44,7 @@ class tactic_cmd { void install_core_tactic_cmds(cmd_context & ctx); tactic * sexpr2tactic(cmd_context & ctx, sexpr * n); +params_ref sexpr2params(cmd_context& ctx, sexpr * n, param_descrs const& descr); class probe_info { symbol m_name; diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index faaf5b28f4d..6cdb4cd596f 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -308,9 +308,8 @@ class simplifier_solver : public solver { void user_propagate_register_final(user_propagator::final_eh_t& final_eh) override { s->user_propagate_register_final(final_eh); } void user_propagate_register_eq(user_propagator::eq_eh_t& eq_eh) override { s->user_propagate_register_eq(eq_eh); } void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh) override { s->user_propagate_register_diseq(diseq_eh); } - void user_propagate_register_expr(expr* e) override { /*m_preprocess.user_propagate_register_expr(e); */ s->user_propagate_register_expr(e); } + void user_propagate_register_expr(expr* e) override { m_preprocess_state.freeze(e); s->user_propagate_register_expr(e); } void user_propagate_register_created(user_propagator::created_eh_t& r) override { s->user_propagate_register_created(r); } - // void user_propagate_clear() override { m_preprocess.user_propagate_clear(); s->user_propagate_clear(); } }; From d263b373edc551709e8d4f222af6d2e12a65baf1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 12:19:33 -0800 Subject: [PATCH 351/597] update release notes Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 7 ++++++ doc/mk_tactic_doc.py | 24 ++++++++++++------- scripts/mk_genfile_common.py | 2 +- src/api/api_qe.cpp | 2 +- src/ast/simplifiers/CMakeLists.txt | 7 +++++- src/ast/simplifiers/bit2int.h | 3 +++ src/ast/simplifiers/bit_blaster.h | 4 ++-- src/ast/simplifiers/elim_bounds.h | 3 +++ src/ast/simplifiers/elim_term_ite.h | 3 +++ src/ast/simplifiers/pull_nested_quantifiers.h | 4 ++++ src/ast/simplifiers/push_ite.h | 6 +++++ src/ast/simplifiers/refine_inj_axiom.h | 4 ++++ src/ast/simplifiers/rewriter_simplifier.h | 2 +- src/muz/base/dl_rule.h | 2 +- src/muz/spacer/spacer_legacy_mbp.cpp | 2 +- src/muz/spacer/spacer_legacy_mev.cpp | 2 +- src/muz/spacer/spacer_qe_project.cpp | 2 +- src/muz/spacer/spacer_util.cpp | 2 +- src/muz/tab/tab_context.cpp | 2 +- src/qe/lite/CMakeLists.txt | 4 ++-- .../lite/{qe_lite.cpp => qe_lite_tactic.cpp} | 6 ++--- src/qe/lite/{qe_lite.h => qe_lite_tactic.h} | 6 +++-- src/qe/qe_mbp.cpp | 2 +- src/sat/smt/q_solver.cpp | 2 +- src/solver/assertions/asserted_formulas.h | 2 +- src/solver/solver_preprocess.cpp | 4 ++-- src/tactic/arith/bound_simplifier_tactic.h | 2 +- src/tactic/arith/card2bv_tactic.h | 2 +- src/tactic/arith/propagate_ineqs_tactic.h | 2 +- src/tactic/bv/CMakeLists.txt | 1 - src/tactic/bv/bv_bounds_tactic.h | 2 +- src/tactic/bv/bv_slice_tactic.cpp | 10 +------- src/tactic/bv/bv_slice_tactic.h | 11 ++++++++- src/tactic/core/demodulator_tactic.h | 2 +- src/tactic/core/distribute_forall_tactic.h | 2 +- src/tactic/core/dom_simplify_tactic.h | 2 +- src/tactic/core/elim_uncnstr2_tactic.h | 2 +- src/tactic/core/eliminate_predicates_tactic.h | 4 ++-- src/tactic/core/euf_completion_tactic.h | 4 ++++ src/tactic/core/propagate_values2_tactic.h | 2 +- src/tactic/core/reduce_args_tactic.h | 2 +- src/tactic/core/solve_eqs_tactic.h | 2 +- src/tactic/smtlogics/nra_tactic.cpp | 2 +- src/tactic/smtlogics/quant_tactics.cpp | 2 +- 44 files changed, 107 insertions(+), 58 deletions(-) rename src/qe/lite/{qe_lite.cpp => qe_lite_tactic.cpp} (99%) rename src/qe/lite/{qe_lite.h => qe_lite_tactic.h} (89%) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2d466c3cd3e..473be8aaeff 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -20,6 +20,13 @@ Version 4.12.2 benchmarks created by converting bit-vector semantics to integer reasoning. - change API function Z3_mk_real to take two int64 as arguments instead of int. +- Add _simplifiers_ as optional incremental pre-processing to solvers. + They are exposed over the SMTLIB API using the command [`set-simplifier`](https://microsoft.github.io/z3guide/docs/strategies/simplifiers). + Simplifiers are similar to tactics, but they operate on solver state that can be incrementally updated. + The exposed simplifiers cover all the pre-processing techniques used internally with some additional simplifiers, such as `solve-eqs` + and `elim-predicates` that go beyond incremental pre-processing used internally. The advantage of using `solve-eqs` during pre-processing + can be significant. Incremental pre-processing simplification using `solve-eqs` and other simplifiers that change interpretations + was not possible before. Version 4.12.1 ============== diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index 86e32aa1840..e825731349b 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -65,7 +65,6 @@ def extract_simplifier_doc(ous, f): m = is_simplifier.search(line) if m: generate_simplifier_doc(ous, m.group(1), m.group(2)) - return def find_tactic_name(path): with open(path) as ins: @@ -76,7 +75,16 @@ def find_tactic_name(path): print(f"no tactic in {path}") return "" -def presort_files(): +def find_simplifier_name(path): + with open(path) as ins: + for line in ins: + m = is_simplifier.search(line) + if m: + return m.group(1) + print(f"no simplifier in {path}") + return "" + +def presort_files(find_fn): tac_files = [] for root, dirs, files in os.walk(doc_path("../src")): for f in files: @@ -84,16 +92,16 @@ def presort_files(): continue if f.endswith("tactic.h") or "simplifiers" in root: tac_files += [(f, os.path.join(root, f))] - tac_files = sorted(tac_files, key = lambda x: find_tactic_name(x[1])) + tac_files = sorted(tac_files, key = lambda x: find_fn(x[1])) return tac_files def help(ous): ous.write("---\n") ous.write("title: Tactics Summary\n") - ous.write("sidebar_position: 5\n") + ous.write("sidebar_position: 6\n") ous.write("---\n") - tac_files = presort_files() + tac_files = presort_files(find_tactic_name) for file, path in tac_files: extract_tactic_doc(ous, path) @@ -101,10 +109,10 @@ def help(ous): def help_simplifier(ous): ous.write("---\n") - ous.write("title: Simplifier Summary\n") - ous.write("sidebar_position: 6\n") + ous.write("title: Simplifiers Summary\n") + ous.write("sidebar_position: 7\n") ous.write("---\n") - tac_files = presort_files() + tac_files = presort_files(find_simplifier_name) for file, path in tac_files: extract_simplifier_doc(ous, path) diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index e242fa443f4..bb6d884e6eb 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -743,7 +743,7 @@ def ADD_SIMPLIFIER(name, descr, cmd): # First pass will just generate the tactic factories fout.write('#define ADD_TACTIC_CMD(NAME, DESCR, CODE) ctx.insert(alloc(tactic_cmd, symbol(NAME), DESCR, [](ast_manager &m, const params_ref &p) { return CODE; }))\n') fout.write('#define ADD_PROBE(NAME, DESCR, PROBE) ctx.insert(alloc(probe_info, symbol(NAME), DESCR, PROBE))\n') - fout.write('#define ADD_SIMPLIFIER_CMD(NAME, DESCR, FUNCTION) ctx.insert(alloc(simplifier_cmd, symbol(NAME), DESCR, FUNCTION))\n') + fout.write('#define ADD_SIMPLIFIER_CMD(NAME, DESCR, CODE) ctx.insert(alloc(simplifier_cmd, symbol(NAME), DESCR, [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return CODE; }))\n') fout.write('void install_tactics(tactic_manager & ctx) {\n') for data in ADD_TACTIC_DATA: fout.write(' ADD_TACTIC_CMD("%s", "%s", %s);\n' % data) diff --git a/src/api/api_qe.cpp b/src/api/api_qe.cpp index 328b1f249d3..94067135fc4 100644 --- a/src/api/api_qe.cpp +++ b/src/api/api_qe.cpp @@ -25,7 +25,7 @@ Module Name: #include "api/api_model.h" #include "api/api_ast_map.h" #include "api/api_ast_vector.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "muz/spacer/spacer_util.h" extern "C" diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt index e30224402ab..2affb793ba6 100644 --- a/src/ast/simplifiers/CMakeLists.txt +++ b/src/ast/simplifiers/CMakeLists.txt @@ -31,6 +31,11 @@ z3_add_component(simplifiers substitution TACTIC_HEADERS bit_blaster.h + bit2int.h + elim_bounds.h + elim_term_ite.h + pull_nested_quantifiers.h + push_ite.h + refine_inj_axiom.h rewriter_simplifier.h - ) diff --git a/src/ast/simplifiers/bit2int.h b/src/ast/simplifiers/bit2int.h index 64c7f3ecf6d..b899e6b588d 100644 --- a/src/ast/simplifiers/bit2int.h +++ b/src/ast/simplifiers/bit2int.h @@ -42,3 +42,6 @@ class bit2int_simplifier : public dependent_expr_simplifier { bool supports_proofs() const override { return true; } }; +/* + ADD_SIMPLIFIER("bit2int", "simplify bit2int expressions.", "alloc(bit2int_simplifier, m, p, s)") + */ diff --git a/src/ast/simplifiers/bit_blaster.h b/src/ast/simplifiers/bit_blaster.h index 82043170621..e9f2198483d 100644 --- a/src/ast/simplifiers/bit_blaster.h +++ b/src/ast/simplifiers/bit_blaster.h @@ -33,7 +33,7 @@ class bit_blaster_simplifier : public dependent_expr_simplifier { m_rewriter(m, p) { updt_params(p); } - char const* name() const override { return "bit-blaster"; } + char const* name() const override { return "bit-blast"; } void updt_params(params_ref const & p) override; void collect_param_descrs(param_descrs & r) override; void reduce() override; @@ -50,5 +50,5 @@ class bit_blaster_simplifier : public dependent_expr_simplifier { }; /* - ADD_SIMPLIFIER("bit-blaster", "reduce bit-vector expressions into SAT.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bit_blaster_simplifier, m, p, s); }") + ADD_SIMPLIFIER("bit-blast", "reduce bit-vector expressions into SAT.", "alloc(bit_blaster_simplifier, m, p, s)") */ diff --git a/src/ast/simplifiers/elim_bounds.h b/src/ast/simplifiers/elim_bounds.h index ed93faeba26..17a4290e27f 100644 --- a/src/ast/simplifiers/elim_bounds.h +++ b/src/ast/simplifiers/elim_bounds.h @@ -43,3 +43,6 @@ class elim_bounds_simplifier : public dependent_expr_simplifier { } }; +/* + ADD_SIMPLIFIER("cheap-fourier-motzkin", "eliminate variables from quantifiers using partial Fourier-Motzkin elimination.", "alloc(elim_bounds_simplifier, m, p, s)") + */ diff --git a/src/ast/simplifiers/elim_term_ite.h b/src/ast/simplifiers/elim_term_ite.h index 1b8f58f9e5d..10f03927996 100644 --- a/src/ast/simplifiers/elim_term_ite.h +++ b/src/ast/simplifiers/elim_term_ite.h @@ -49,3 +49,6 @@ class elim_term_ite_simplifier : public dependent_expr_simplifier { void pop(unsigned n) override { m_rewriter.pop(n); m_df.pop(n); dependent_expr_simplifier::pop(n); } }; +/* + ADD_SIMPLIFIER("elim-term-ite", "eliminate if-then-else term by hoisting them top top-level.", "alloc(elim_term_ite_simplifier, m, p, s)") +*/ diff --git a/src/ast/simplifiers/pull_nested_quantifiers.h b/src/ast/simplifiers/pull_nested_quantifiers.h index fac605d4971..f41d4282b7a 100644 --- a/src/ast/simplifiers/pull_nested_quantifiers.h +++ b/src/ast/simplifiers/pull_nested_quantifiers.h @@ -46,3 +46,7 @@ class pull_nested_quantifiers_simplifier : public dependent_expr_simplifier { bool supports_proofs() const override { return true; } }; + +/* + ADD_SIMPLIFIER("pull-nested-quantifiers", "pull nested quantifiers to top-level.", "alloc(pull_nested_quantifiers_simplifier, m, p, s)") +*/ diff --git a/src/ast/simplifiers/push_ite.h b/src/ast/simplifiers/push_ite.h index 8e508bf5344..f0db764a0bf 100644 --- a/src/ast/simplifiers/push_ite.h +++ b/src/ast/simplifiers/push_ite.h @@ -64,3 +64,9 @@ class ng_push_ite_simplifier : public dependent_expr_simplifier { } }; +/* + ADD_SIMPLIFIER("push-app-ite-conservative", "Push functions over if-then else.", "alloc(push_ite_simplifier, m, p, s, true)") + ADD_SIMPLIFIER("push-app-ite", "Push functions over if-then else.", "alloc(push_ite_simplifier, m, p, s, false)") + ADD_SIMPLIFIER("ng-push-app-ite-conservative", "Push functions over if-then-else within non-ground terms only.", "alloc(ng_push_ite_simplifier, m, p, s, true)") + ADD_SIMPLIFIER("ng-push-app-ite", "Push functions over if-then-else within non-ground terms only.", "alloc(ng_push_ite_simplifier, m, p, s, false)") +*/ diff --git a/src/ast/simplifiers/refine_inj_axiom.h b/src/ast/simplifiers/refine_inj_axiom.h index 53c96081270..35128f3d949 100644 --- a/src/ast/simplifiers/refine_inj_axiom.h +++ b/src/ast/simplifiers/refine_inj_axiom.h @@ -42,3 +42,7 @@ class refine_inj_axiom_simplifier : public dependent_expr_simplifier { } } }; + +/* + ADD_SIMPLIFIER("refine-injectivity", "refine injectivity axioms.", "alloc(refine_inj_axiom_simplifier, m, p, s)") +*/ diff --git a/src/ast/simplifiers/rewriter_simplifier.h b/src/ast/simplifiers/rewriter_simplifier.h index f74c213d461..7ae409df0c4 100644 --- a/src/ast/simplifiers/rewriter_simplifier.h +++ b/src/ast/simplifiers/rewriter_simplifier.h @@ -55,5 +55,5 @@ class rewriter_simplifier : public dependent_expr_simplifier { }; /* - ADD_SIMPLIFIER("simplify", "apply simplification rules.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(rewriter_simplifier, m, p, s); }") + ADD_SIMPLIFIER("simplify", "apply simplification rules.", "alloc(rewriter_simplifier, m, p, s)") */ diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 60b2bbe8fb4..0a8fd955cbb 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -28,7 +28,7 @@ Revision History: #include "ast/rewriter/ast_counter.h" #include "ast/rewriter/rewriter.h" #include "muz/base/hnf.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "ast/rewriter/var_subst.h" #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/label_rewriter.h" diff --git a/src/muz/spacer/spacer_legacy_mbp.cpp b/src/muz/spacer/spacer_legacy_mbp.cpp index 324368cec14..0f49381c19d 100644 --- a/src/muz/spacer/spacer_legacy_mbp.cpp +++ b/src/muz/spacer/spacer_legacy_mbp.cpp @@ -35,7 +35,7 @@ Module Name: #include "ast/rewriter/expr_replacer.h" #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "muz/spacer/spacer_qe_project.h" #include "model/model_pp.h" #include "ast/rewriter/expr_safe_replace.h" diff --git a/src/muz/spacer/spacer_legacy_mev.cpp b/src/muz/spacer/spacer_legacy_mev.cpp index e6da53e323b..a0c95fbd90b 100644 --- a/src/muz/spacer/spacer_legacy_mev.cpp +++ b/src/muz/spacer/spacer_legacy_mev.cpp @@ -23,7 +23,7 @@ Copyright (c) 2017 Arie Gurfinkel #include "ast/rewriter/expr_replacer.h" #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "muz/spacer/spacer_qe_project.h" #include "model/model_pp.h" #include "ast/rewriter/expr_safe_replace.h" diff --git a/src/muz/spacer/spacer_qe_project.cpp b/src/muz/spacer/spacer_qe_project.cpp index b170a59d077..e52e909ae00 100644 --- a/src/muz/spacer/spacer_qe_project.cpp +++ b/src/muz/spacer/spacer_qe_project.cpp @@ -36,7 +36,7 @@ Revision History: #include "model/model_pp.h" #include "qe/qe.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "muz/spacer/spacer_mev_array.h" #include "muz/spacer/spacer_qe_project.h" diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index bc9224771b2..4e1da577004 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -51,7 +51,7 @@ Revision History: #include "model/model_smt2_pp.h" #include "smt/params/smt_params.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "qe/mbp/mbp_plugin.h" #include "qe/mbp/mbp_term_graph.h" #include "qe/qe_mbp.h" diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index b8ca9babbe2..f65661e9eaf 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -23,7 +23,7 @@ Revision History: #include "muz/base/dl_context.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "smt/smt_kernel.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "ast/datatype_decl_plugin.h" diff --git a/src/qe/lite/CMakeLists.txt b/src/qe/lite/CMakeLists.txt index 27f9bb09d3d..fc942d4aebc 100644 --- a/src/qe/lite/CMakeLists.txt +++ b/src/qe/lite/CMakeLists.txt @@ -1,9 +1,9 @@ z3_add_component(qe_lite SOURCES - qe_lite.cpp + qe_lite_tactic.cpp COMPONENT_DEPENDENCIES tactic mbp TACTIC_HEADERS - qe_lite.h + qe_lite_tactic.h ) diff --git a/src/qe/lite/qe_lite.cpp b/src/qe/lite/qe_lite_tactic.cpp similarity index 99% rename from src/qe/lite/qe_lite.cpp rename to src/qe/lite/qe_lite_tactic.cpp index 90ea3576963..32d11786cea 100644 --- a/src/qe/lite/qe_lite.cpp +++ b/src/qe/lite/qe_lite_tactic.cpp @@ -34,7 +34,7 @@ Revision History: #include "ast/datatype_decl_plugin.h" #include "tactic/tactical.h" #include "qe/mbp/mbp_solve_plugin.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "tactic/dependent_expr_state_tactic.h" @@ -2444,9 +2444,9 @@ namespace { } tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { - return alloc(dependent_expr_state_tactic, m, p, mk_qe_lite_simplifer); + return alloc(dependent_expr_state_tactic, m, p, mk_qe_lite_simplifier); } -dependent_expr_simplifier* mk_qe_lite_simplifer(ast_manager& m, params_ref const& p, dependent_expr_state& st) { +dependent_expr_simplifier* mk_qe_lite_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st) { return alloc(qe_lite_simplifier, m, p, st); } diff --git a/src/qe/lite/qe_lite.h b/src/qe/lite/qe_lite_tactic.h similarity index 89% rename from src/qe/lite/qe_lite.h rename to src/qe/lite/qe_lite_tactic.h index 9a4d4c0f621..07ce60f35b8 100644 --- a/src/qe/lite/qe_lite.h +++ b/src/qe/lite/qe_lite_tactic.h @@ -68,8 +68,10 @@ class qe_lite { }; tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p = params_ref()); + +dependent_expr_simplifier* mk_qe_lite_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& st); + /* ADD_TACTIC("qe-light", "apply light-weight quantifier elimination.", "mk_qe_lite_tactic(m, p)") + ADD_SIMPLIFIER("qe-light", "apply light-weight quantifier elimination.", "mk_qe_lite_simplifier(m, p, s)") */ - -dependent_expr_simplifier* mk_qe_lite_simplifer(ast_manager& m, params_ref const& p, dependent_expr_state& st); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 6b0e3cf32d4..9f5d9063cd0 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -30,7 +30,7 @@ Revision History: #include "qe/mbp/mbp_arith.h" #include "qe/mbp/mbp_arrays.h" #include "qe/mbp/mbp_datatypes.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "model/model_pp.h" #include "model/model_evaluator.h" diff --git a/src/sat/smt/q_solver.cpp b/src/sat/smt/q_solver.cpp index 26c99180b19..fff11898c7d 100644 --- a/src/sat/smt/q_solver.cpp +++ b/src/sat/smt/q_solver.cpp @@ -23,7 +23,7 @@ Module Name: #include "sat/smt/q_solver.h" #include "sat/smt/euf_solver.h" #include "sat/smt/sat_th.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include diff --git a/src/solver/assertions/asserted_formulas.h b/src/solver/assertions/asserted_formulas.h index c4da977042e..481af58b737 100644 --- a/src/solver/assertions/asserted_formulas.h +++ b/src/solver/assertions/asserted_formulas.h @@ -38,7 +38,7 @@ Revision History: #include "ast/normal_forms/elim_term_ite.h" #include "ast/pattern/pattern_inference.h" #include "smt/params/smt_params.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" class asserted_formulas { diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp index 71c6d975a68..38b0584b52b 100644 --- a/src/solver/solver_preprocess.cpp +++ b/src/solver/solver_preprocess.cpp @@ -42,7 +42,7 @@ Module Name: #include "ast/simplifiers/cnf_nnf.h" #include "smt/params/smt_params.h" #include "solver/solver_preprocess.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { @@ -53,7 +53,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep if (smtp.m_elim_unconstrained) s.add_simplifier(alloc(elim_unconstrained, m, st)); if (smtp.m_nnf_cnf) s.add_simplifier(alloc(cnf_nnf_simplifier, m, p, st)); if (smtp.m_macro_finder || smtp.m_quasi_macros) s.add_simplifier(alloc(eliminate_predicates, m, st)); - if (smtp.m_qe_lite) s.add_simplifier(mk_qe_lite_simplifer(m, p, st)); + if (smtp.m_qe_lite) s.add_simplifier(mk_qe_lite_simplifier(m, p, st)); if (smtp.m_pull_nested_quantifiers) s.add_simplifier(alloc(pull_nested_quantifiers_simplifier, m, p, st)); if (smtp.m_max_bv_sharing) s.add_simplifier(mk_max_bv_sharing(m, p, st)); if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); diff --git a/src/tactic/arith/bound_simplifier_tactic.h b/src/tactic/arith/bound_simplifier_tactic.h index 5451caa206f..b61a100048d 100644 --- a/src/tactic/arith/bound_simplifier_tactic.h +++ b/src/tactic/arith/bound_simplifier_tactic.h @@ -38,5 +38,5 @@ inline tactic* mk_bound_simplifier_tactic(ast_manager& m, params_ref const& p = /* ADD_TACTIC("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "mk_bound_simplifier_tactic(m, p)") - ADD_SIMPLIFIER("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }") + ADD_SIMPLIFIER("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "alloc(bound_simplifier, m, p, s)") */ diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index 032e964f95a..c84b1b04e47 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -69,5 +69,5 @@ inline tactic* mk_card2bv_tactic(ast_manager& m, params_ref const& p = params_re /* ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)") - ADD_SIMPLIFIER("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(card2bv, m, p, s); }") + ADD_SIMPLIFIER("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "alloc(card2bv, m, p, s)") */ diff --git a/src/tactic/arith/propagate_ineqs_tactic.h b/src/tactic/arith/propagate_ineqs_tactic.h index 68156c410ee..706276fd749 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.h +++ b/src/tactic/arith/propagate_ineqs_tactic.h @@ -64,6 +64,6 @@ inline tactic* mk_propagate_ineqs_tactic(ast_manager& m, params_ref const& p = p /* ADD_TACTIC("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "mk_propagate_ineqs_tactic(m, p)") - ADD_SIMPLIFIER("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }") + ADD_SIMPLIFIER("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "alloc(bound_simplifier, m, p, s)") */ diff --git a/src/tactic/bv/CMakeLists.txt b/src/tactic/bv/CMakeLists.txt index 50dd941e0e9..9009e6fa5ef 100644 --- a/src/tactic/bv/CMakeLists.txt +++ b/src/tactic/bv/CMakeLists.txt @@ -8,7 +8,6 @@ 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 COMPONENT_DEPENDENCIES diff --git a/src/tactic/bv/bv_bounds_tactic.h b/src/tactic/bv/bv_bounds_tactic.h index eb75ceca7bc..453f6d27f82 100644 --- a/src/tactic/bv/bv_bounds_tactic.h +++ b/src/tactic/bv/bv_bounds_tactic.h @@ -46,7 +46,7 @@ tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ /* ADD_TACTIC("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_tactic(m, p)") - ADD_SIMPLIFIER("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_simplifier") + ADD_SIMPLIFIER("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_simplifier(m, p, s)") ADD_TACTIC("propagate-bv-bounds2", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)") diff --git a/src/tactic/bv/bv_slice_tactic.cpp b/src/tactic/bv/bv_slice_tactic.cpp index d470f6e7275..bb3d437d962 100644 --- a/src/tactic/bv/bv_slice_tactic.cpp +++ b/src/tactic/bv/bv_slice_tactic.cpp @@ -15,12 +15,4 @@ Module Name: --*/ -#include "ast/simplifiers/bv_slice.h" -#include "tactic/tactic.h" -#include "tactic/dependent_expr_state_tactic.h" -#include "tactic/bv/bv_slice_tactic.h" - -tactic* mk_bv_slice_tactic(ast_manager& m, params_ref const& p) { - return alloc(dependent_expr_state_tactic, m, p, - [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bv::slice, m, s); }); -} + diff --git a/src/tactic/bv/bv_slice_tactic.h b/src/tactic/bv/bv_slice_tactic.h index 77ff081d831..b16aa4c7c14 100644 --- a/src/tactic/bv/bv_slice_tactic.h +++ b/src/tactic/bv/bv_slice_tactic.h @@ -45,13 +45,22 @@ simplification #pragma once #include "util/params.h" +#include "tactic/tactic.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/bv_slice.h" + class ast_manager; class tactic; -tactic * mk_bv_slice_tactic(ast_manager & m, params_ref const & p = params_ref()); +inline tactic* mk_bv_slice_tactic(ast_manager& m, params_ref const& p = params_ref()) { + return alloc(dependent_expr_state_tactic, m, p, + [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bv::slice, m, s); }); +} + /* ADD_TACTIC("bv-slice", "simplify using bit-vector slices.", "mk_bv_slice_tactic(m, p)") + ADD_SIMPLIFIER("bv-slice", "simplify using bit-vector slices.", "alloc(bv::slice, m, s)") */ diff --git a/src/tactic/core/demodulator_tactic.h b/src/tactic/core/demodulator_tactic.h index 507b4e5c5bd..31916a71eb7 100644 --- a/src/tactic/core/demodulator_tactic.h +++ b/src/tactic/core/demodulator_tactic.h @@ -100,5 +100,5 @@ inline tactic * mk_demodulator_tactic(ast_manager& m, params_ref const& p = para /* ADD_TACTIC("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "mk_demodulator_tactic(m, p)") - ADD_SIMPLIFIER("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(demodulator_simplifier, m, p, s); }") + ADD_SIMPLIFIER("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "alloc(demodulator_simplifier, m, p, s)") */ diff --git a/src/tactic/core/distribute_forall_tactic.h b/src/tactic/core/distribute_forall_tactic.h index 419eba71e62..dea00931147 100644 --- a/src/tactic/core/distribute_forall_tactic.h +++ b/src/tactic/core/distribute_forall_tactic.h @@ -50,6 +50,6 @@ inline tactic * mk_distribute_forall_tactic(ast_manager& m, params_ref const& p /* ADD_TACTIC("distribute-forall", "distribute forall over conjunctions.", "mk_distribute_forall_tactic(m, p)") - ADD_SIMPLIFIER("distribute-forall", "distribute forall over conjunctions.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(distribute_forall_simplifier, m, p, s); }") + ADD_SIMPLIFIER("distribute-forall", "distribute forall over conjunctions.", "alloc(distribute_forall_simplifier, m, p, s)") */ diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 795db0f261c..349c96b4965 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -56,6 +56,6 @@ inline tactic* mk_dom_simplify_tactic(ast_manager& m, params_ref const& p) { /* ADD_TACTIC("dom-simplify", "apply dominator simplification rules.", "mk_dom_simplify_tactic(m, p)") -ADD_SIMPLIFIER("dom-simplify", "apply dominator simplification rules.", "[](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* { return alloc(dominator_simplifier, m, s, mk_expr_substitution_simplifier(m), p); }") +ADD_SIMPLIFIER("dom-simplify", "apply dominator simplification rules.", "alloc(dominator_simplifier, m, s, mk_expr_substitution_simplifier(m), p)") */ diff --git a/src/tactic/core/elim_uncnstr2_tactic.h b/src/tactic/core/elim_uncnstr2_tactic.h index fc249da643d..a07833058d2 100644 --- a/src/tactic/core/elim_uncnstr2_tactic.h +++ b/src/tactic/core/elim_uncnstr2_tactic.h @@ -118,5 +118,5 @@ inline tactic * mk_elim_uncnstr2_tactic(ast_manager & m, params_ref const & p = /* ADD_TACTIC("elim-uncnstr2", "eliminate unconstrained variables.", "mk_elim_uncnstr2_tactic(m, p)") - ADD_SIMPLIFIER("elim-unconstrained", "eliminate unconstrained variables.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(elim_unconstrained, m, s); }") + ADD_SIMPLIFIER("elim-unconstrained", "eliminate unconstrained variables.", "alloc(elim_unconstrained, m, s)") */ diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index b53892e8b8d..c2eb90742e1 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -63,6 +63,6 @@ inline tactic * mk_eliminate_predicates_tactic(ast_manager& m, params_ref const& } /* - ADD_TACTIC("elim-predicates", "eliminate predicates.", "mk_eliminate_predicates_tactic(m, p)") - ADD_SIMPLIFIER("elim-predicates", "eliminate predicates.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(eliminate_predicates, m, s); }") + ADD_TACTIC("elim-predicates", "eliminate predicates, macros and implicit definitions.", "mk_eliminate_predicates_tactic(m, p)") + ADD_SIMPLIFIER("elim-predicates", "eliminate predicates, macros and implicit definitions.", "alloc(eliminate_predicates, m, s)") */ diff --git a/src/tactic/core/euf_completion_tactic.h b/src/tactic/core/euf_completion_tactic.h index a1c9166ea23..cfeda5ac182 100644 --- a/src/tactic/core/euf_completion_tactic.h +++ b/src/tactic/core/euf_completion_tactic.h @@ -33,6 +33,9 @@ is extracted. #pragma once #include "util/params.h" +#include "tactic/dependent_expr_state_tactic.h" +#include "ast/simplifiers/euf_completion.h" + class ast_manager; class tactic; @@ -40,6 +43,7 @@ tactic * mk_euf_completion_tactic(ast_manager & m, params_ref const & p = params /* ADD_TACTIC("euf-completion", "simplify using equalities.", "mk_euf_completion_tactic(m, p)") + ADD_SIMPLIFIER("euf-completion", "simplify modulo congruence closure.", "alloc(euf::completion, m, s)") */ diff --git a/src/tactic/core/propagate_values2_tactic.h b/src/tactic/core/propagate_values2_tactic.h index 466ef87a106..57e94965b3f 100644 --- a/src/tactic/core/propagate_values2_tactic.h +++ b/src/tactic/core/propagate_values2_tactic.h @@ -55,5 +55,5 @@ inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & /* ADD_TACTIC("propagate-values2", "propagate constants.", "mk_propagate_values2_tactic(m, p)") - ADD_SIMPLIFIER("propagate-values", "propagate constants.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(propagate_values, m, p, s); }") + ADD_SIMPLIFIER("propagate-values", "propagate constants.", "alloc(propagate_values, m, p, s)") */ diff --git a/src/tactic/core/reduce_args_tactic.h b/src/tactic/core/reduce_args_tactic.h index 4bfb7ed2f16..eeb5bff707b 100644 --- a/src/tactic/core/reduce_args_tactic.h +++ b/src/tactic/core/reduce_args_tactic.h @@ -79,7 +79,7 @@ inline tactic* mk_reduce_args_tactic2(ast_manager& m, params_ref const& p = para } /* ADD_TACTIC("reduce-args2", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "mk_reduce_args_tactic2(m, p)") - ADD_SIMPLIFIER("reduce-args", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "[](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* { return mk_reduce_args_simplifier(m, s, p); }") + ADD_SIMPLIFIER("reduce-args", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "mk_reduce_args_simplifier(m, s, p)") */ diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index af27d45760f..1e0e5dc5b5f 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -79,5 +79,5 @@ inline tactic * mk_solve_eqs_tactic(ast_manager& m, params_ref const& p = params /* ADD_TACTIC("solve-eqs", "solve for variables.", "mk_solve_eqs_tactic(m, p)") - ADD_SIMPLIFIER("solve-eqs", "solve for variables.", "[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(euf::solve_eqs, m, s); }") + ADD_SIMPLIFIER("solve-eqs", "solve for variables.", "alloc(euf::solve_eqs, m, s)") */ diff --git a/src/tactic/smtlogics/nra_tactic.cpp b/src/tactic/smtlogics/nra_tactic.cpp index e29694bc28b..4f3d18c1f31 100644 --- a/src/tactic/smtlogics/nra_tactic.cpp +++ b/src/tactic/smtlogics/nra_tactic.cpp @@ -24,7 +24,7 @@ Module Name: #include "tactic/smtlogics/smt_tactic.h" #include "qe/qe_tactic.h" #include "qe/nlqsat.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "nlsat/tactic/qfnra_nlsat_tactic.h" tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index daf020a14e5..38cb1690bca 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -21,7 +21,7 @@ Revision History: #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" -#include "qe/lite/qe_lite.h" +#include "qe/lite/qe_lite_tactic.h" #include "qe/qsat.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/core/elim_term_ite_tactic.h" From e6f8fe359e2bb5e8216d409e312d1e6250c09be7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 12:32:28 -0800 Subject: [PATCH 352/597] remove empty file Signed-off-by: Nikolaj Bjorner --- doc/mk_tactic_doc.py | 2 +- src/tactic/bv/bv_slice_tactic.cpp | 18 ------------------ src/tactic/core/euf_completion_tactic.cpp | 2 -- 3 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 src/tactic/bv/bv_slice_tactic.cpp diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index e825731349b..a22201e2712 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -55,7 +55,7 @@ def extract_tactic_doc(ous, f): generate_tactic_doc(ous, f, ins) def generate_simplifier_doc(ous, name, desc): - ous.write("## Simplifier [" + name + "](../summary/#tactic-" + name + ")\n") + ous.write("## Simplifier [" + name + "](https://microsoft.github.io/z3guide/docs/strategies/summary/#tactic-" + name + ")\n") ous.write("### Description\n" + desc + "\n") diff --git a/src/tactic/bv/bv_slice_tactic.cpp b/src/tactic/bv/bv_slice_tactic.cpp deleted file mode 100644 index bb3d437d962..00000000000 --- a/src/tactic/bv/bv_slice_tactic.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/*++ -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 - ---*/ - - diff --git a/src/tactic/core/euf_completion_tactic.cpp b/src/tactic/core/euf_completion_tactic.cpp index ec440878299..af2ca9ed723 100644 --- a/src/tactic/core/euf_completion_tactic.cpp +++ b/src/tactic/core/euf_completion_tactic.cpp @@ -16,8 +16,6 @@ Module Name: --*/ #include "tactic/tactic.h" -#include "tactic/dependent_expr_state_tactic.h" -#include "ast/simplifiers/euf_completion.h" #include "tactic/core/euf_completion_tactic.h" tactic * mk_euf_completion_tactic(ast_manager& m, params_ref const& p) { From 6d8d8f197105d86d3af180e382533f2839756f0c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 12:43:05 -0800 Subject: [PATCH 353/597] fix release message Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 473be8aaeff..6cac7cc496d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -19,7 +19,7 @@ Version 4.12.2 implies that the modulus is redundant. This tactic is useful for benchmarks created by converting bit-vector semantics to integer reasoning. -- change API function Z3_mk_real to take two int64 as arguments instead of int. +- add API function Z3_mk_real_int64 to take two int64 as arguments. The Z3_mk_real function takes integers. - Add _simplifiers_ as optional incremental pre-processing to solvers. They are exposed over the SMTLIB API using the command [`set-simplifier`](https://microsoft.github.io/z3guide/docs/strategies/simplifiers). Simplifiers are similar to tactics, but they operate on solver state that can be incrementally updated. From 8495be11f97862a61a184789ed55e73bb43f1246 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 13:34:52 -0800 Subject: [PATCH 354/597] add shortcut filter to avoid traversing model reconstruction trail if there are no intersections with model --- .../model_reconstruction_trail.cpp | 16 ++++++--- .../simplifiers/model_reconstruction_trail.h | 35 ++++++++++++++++--- src/cmd_context/simplifier_cmds.cpp | 2 +- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 22b600deda9..65635c8e459 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -22,20 +22,26 @@ Module Name: // accumulate a set of dependent exprs, updating m_trail to exclude loose // substitutions that use variables from the dependent expressions. -// TODO: add filters to skip sections of the trail that do not touch the current free variables. void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& st) { - TRACE("simplifier", - for (unsigned i = qhead; i < st.qtail(); ++i) - tout << mk_bounded_pp(st[i].fml(), m) << "\n"; - ); + ast_mark free_vars; + m_intersects_with_model = false; scoped_ptr rp = mk_default_expr_replacer(m, false); for (unsigned i = qhead; i < st.qtail(); ++i) add_vars(st[i], free_vars); for (expr* a : assumptions) add_vars(a, free_vars); + TRACE("simplifier", + tout << "intersects " << m_intersects_with_model << "\n"; + for (unsigned i = qhead; i < st.qtail(); ++i) + tout << mk_bounded_pp(st[i].fml(), m) << "\n"; + ); + + if (!m_intersects_with_model) + return; + for (auto& t : m_trail) { TRACE("simplifier", tout << " active " << t->m_active << " hide " << t->is_hide() << " intersects " << t->intersects(free_vars) << "\n"); if (!t->m_active) diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index 89a112c243f..d9b51cb22cd 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -40,7 +40,6 @@ class model_reconstruction_trail { vector m_removed; func_decl_ref m_decl; vector> m_defs; - bool m_active = true; entry(ast_manager& m, expr_substitution* s, vector const& rem) : @@ -84,12 +83,35 @@ class model_reconstruction_trail { ast_manager& m; trail_stack& m_trail_stack; scoped_ptr_vector m_trail; + func_decl_ref_vector m_model_vars_trail; + ast_mark m_model_vars; + bool m_intersects_with_model = false; + + struct undo_model_var : public trail { + model_reconstruction_trail& s; + undo_model_var(model_reconstruction_trail& s) : s(s) {} + virtual void undo() { + s.m_model_vars.mark(s.m_model_vars_trail.back(), false); + s.m_model_vars_trail.pop_back(); + } + }; + + void add_model_var(func_decl* f) { + if (!m_model_vars.is_marked(f)) { + m_model_vars_trail.push_back(f); + m_model_vars.mark(f, true); + m_trail_stack.push(undo_model_var(*this)); + } + } void add_vars(expr* e, ast_mark& free_vars) { for (expr* t : subterms::all(expr_ref(e, m))) if (is_app(t) && is_uninterp(t)) { - TRACE("simplifier", tout << "add var " << to_app(t)->get_decl()->get_name() << "\n"); - free_vars.mark(to_app(t)->get_decl(), true); + func_decl* f = to_app(t)->get_decl(); + TRACE("simplifier", tout << "add var " << f->get_name() << "\n"); + free_vars.mark(f, true); + if (m_model_vars.is_marked(f)) + m_intersects_with_model = true; } } @@ -117,7 +139,9 @@ class model_reconstruction_trail { */ void push(expr_substitution* s, vector const& removed) { m_trail.push_back(alloc(entry, m, s, removed)); - m_trail_stack.push(push_back_vector(m_trail)); + m_trail_stack.push(push_back_vector(m_trail)); + for (auto& [k, v] : s->sub()) + add_model_var(to_app(k)->get_decl()); } /** @@ -134,6 +158,7 @@ class model_reconstruction_trail { void push(func_decl* f, expr* def, expr_dependency* dep, vector const& removed) { m_trail.push_back(alloc(entry, m, f, def, dep, removed)); m_trail_stack.push(push_back_vector(m_trail)); + add_model_var(f); } /** @@ -142,6 +167,8 @@ class model_reconstruction_trail { void push(vector> const& defs, vector const& removed) { m_trail.push_back(alloc(entry, m, defs, removed)); m_trail_stack.push(push_back_vector(m_trail)); + for (auto const& [f, def, dep] : defs) + add_model_var(f); } /** diff --git a/src/cmd_context/simplifier_cmds.cpp b/src/cmd_context/simplifier_cmds.cpp index 0ea88e09518..0050dc54839 100644 --- a/src/cmd_context/simplifier_cmds.cpp +++ b/src/cmd_context/simplifier_cmds.cpp @@ -123,7 +123,7 @@ ATOMIC_CMD(help_simplifier_cmd, "help-simplifier", "display the simplifier combi class set_simplifier_cmd : public parametric_cmd { protected: - sexpr * m_simplifier; + sexpr * m_simplifier = nullptr; public: set_simplifier_cmd(): parametric_cmd("set-simplifier") {} From 88bf3c6e516276bb813b2e57be6309aec529a9a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 13:35:43 -0800 Subject: [PATCH 355/597] check if trail is empty to avoid collecting variables --- src/ast/simplifiers/model_reconstruction_trail.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 65635c8e459..95f73fd7a76 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -25,6 +25,9 @@ Module Name: void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& st) { + if (m_trail.empty()) + return; + ast_mark free_vars; m_intersects_with_model = false; scoped_ptr rp = mk_default_expr_replacer(m, false); From ebc2cd572b1952635842ada67d239b52d739df06 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 14:53:04 -0800 Subject: [PATCH 356/597] fix build Signed-off-by: Nikolaj Bjorner --- .../simplifiers/model_reconstruction_trail.h | 26 ++++++++++++------- src/solver/simplifier_solver.cpp | 6 ++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/ast/simplifiers/model_reconstruction_trail.h b/src/ast/simplifiers/model_reconstruction_trail.h index d9b51cb22cd..79a38401ab9 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.h +++ b/src/ast/simplifiers/model_reconstruction_trail.h @@ -96,6 +96,9 @@ class model_reconstruction_trail { } }; + /** + * register that f occurs in the model reconstruction trail. + */ void add_model_var(func_decl* f) { if (!m_model_vars.is_marked(f)) { m_model_vars_trail.push_back(f); @@ -104,6 +107,11 @@ class model_reconstruction_trail { } } + /** + * walk the free functions of 'e' and add them to 'free_vars'. + * record if there is an intersection with the model_vars that are + * registered when updates are added to the trail. + */ void add_vars(expr* e, ast_mark& free_vars) { for (expr* t : subterms::all(expr_ref(e, m))) if (is_app(t) && is_uninterp(t)) { @@ -129,10 +137,15 @@ class model_reconstruction_trail { return any_of(added, [&](dependent_expr const& d) { return intersects(free_vars, d); }); } + /** + * Append new updates to model converter. + */ + void append(generic_model_converter& mc); + public: model_reconstruction_trail(ast_manager& m, trail_stack& tr): - m(m), m_trail_stack(tr) {} + m(m), m_trail_stack(tr), m_model_vars_trail(m) {} /** * add a new substitution to the trail @@ -177,16 +190,11 @@ class model_reconstruction_trail { */ void replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& fmls); - /** - * retrieve the current model converter corresponding to chaining substitutions from the trail. - */ - model_converter_ref get_model_converter(); - /** - * Append new updates to model converter, update m_trail_index in the process. - */ - void append(generic_model_converter& mc); + * retrieve the current model converter corresponding to chaining substitutions from the trail. + */ + model_converter_ref get_model_converter(); std::ostream& display(std::ostream& out) const; }; diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 6cdb4cd596f..219b2c46beb 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -62,7 +62,6 @@ class simplifier_solver : public solver { if (s.m.is_false(f)) s.set_inconsistent(); } - void append(generic_model_converter& mc) { model_trail().append(mc); } void replay(unsigned qhead, expr_ref_vector& assumptions) { m_reconstruction_trail.replay(qhead, assumptions, *this); } void flatten_suffix() override { expr_mark seen; @@ -94,7 +93,7 @@ class simplifier_solver : public solver { dep_expr_state m_preprocess_state; seq_simplifier m_preprocess; expr_ref_vector m_assumptions; - generic_model_converter_ref m_mc; + model_converter_ref m_mc; bool m_inconsistent = false; void flush(expr_ref_vector& assumptions) { @@ -109,9 +108,8 @@ class simplifier_solver : public solver { return; m_preprocess_state.advance_qhead(); } - m_mc = alloc(generic_model_converter, m, "simplifier-model-converter"); + m_mc = m_preprocess_state.model_trail().get_model_converter(); m_cached_mc = nullptr; - m_preprocess_state.append(*m_mc); for (; qhead < m_fmls.size(); ++qhead) add_with_dependency(m_fmls[qhead]); } From 550619bfcf7bb7fcf958f18389d34d7b3e6286ed Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 17:06:03 -0800 Subject: [PATCH 357/597] add API for creating and attaching simplifiers Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 6 +- src/api/api_solver.cpp | 17 +++- src/api/api_solver.h | 3 + src/api/api_tactic.cpp | 150 +++++++++++++++++++++++++++++++ src/api/api_tactic.h | 10 +++ src/api/c++/z3++.h | 43 +++++++++ src/api/python/z3/z3.py | 58 ++++++++++++ src/api/python/z3/z3types.py | 6 ++ src/api/z3_api.h | 96 +++++++++++++++++++- src/cmd_context/tactic_manager.h | 4 +- 10 files changed, 389 insertions(+), 4 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index a9753ec23dd..62b961d67aa 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -312,7 +312,7 @@ def display_args_to_z3(params): Unwrapped = [ 'Z3_del_context', 'Z3_get_error_code' ] Unchecked = frozenset([ 'Z3_dec_ref', 'Z3_params_dec_ref', 'Z3_model_dec_ref', 'Z3_func_interp_dec_ref', 'Z3_func_entry_dec_ref', - 'Z3_goal_dec_ref', 'Z3_tactic_dec_ref', 'Z3_probe_dec_ref', + 'Z3_goal_dec_ref', 'Z3_tactic_dec_ref', 'Z3_simplifier_dec_ref', 'Z3_probe_dec_ref', 'Z3_fixedpoint_dec_ref', 'Z3_param_descrs_dec_ref', 'Z3_ast_vector_dec_ref', 'Z3_ast_map_dec_ref', 'Z3_apply_result_dec_ref', 'Z3_solver_dec_ref', @@ -1176,6 +1176,8 @@ def ml_plus_type(ts): return 'Z3_goal_plus' elif ts == 'Z3_tactic': return 'Z3_tactic_plus' + elif ts == 'Z3_simplifier': + return 'Z3_simplifier_plus' elif ts == 'Z3_probe': return 'Z3_probe_plus' elif ts == 'Z3_apply_result': @@ -1220,6 +1222,8 @@ def ml_minus_type(ts): return 'Z3_goal' elif ts == 'Z3_tactic_plus': return 'Z3_tactic' + elif ts == 'Z3_simplifier_plus': + return 'Z3_simplifier' elif ts == 'Z3_probe_plus': return 'Z3_probe' elif ts == 'Z3_apply_result_plus': diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 2ca54a5996e..0a3b35aed6b 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -44,6 +44,7 @@ Revision History: #include "sat/tactic/goal2sat.h" #include "sat/tactic/sat2goal.h" #include "cmd_context/extra_cmds/proof_cmds.h" +#include "solver/simplifier_solver.h" extern "C" { @@ -232,12 +233,26 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_solver Z3_API Z3_solver_add_simplifier(Z3_context c, Z3_solver solver, Z3_simplifier simplifier) { + Z3_TRY; + LOG_Z3_solver_add_simplifier(c, solver, simplifier); + init_solver(c, solver); + auto simp = to_simplifier_ref(simplifier); + auto* slv = mk_simplifier_solver(to_solver_ref(solver), simp); + Z3_solver_ref* sr = alloc(Z3_solver_ref, *mk_c(c), slv); + mk_c(c)->save_object(sr); + // ?? init_solver_log(c, sr) + RETURN_Z3(of_solver(sr)); + Z3_CATCH_RETURN(nullptr); + } + + Z3_solver Z3_API Z3_solver_translate(Z3_context c, Z3_solver s, Z3_context target) { Z3_TRY; LOG_Z3_solver_translate(c, s, target); RESET_ERROR_CODE(); params_ref const& p = to_solver(s)->m_params; - Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), nullptr); + Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), (solver_factory *)nullptr); init_solver(c, s); sr->m_solver = to_solver(s)->m_solver->translate(mk_c(target)->m(), p); mk_c(target)->save_object(sr); diff --git a/src/api/api_solver.h b/src/api/api_solver.h index 71b7f9a4665..a24668e491b 100644 --- a/src/api/api_solver.h +++ b/src/api/api_solver.h @@ -52,6 +52,9 @@ struct Z3_solver_ref : public api::object { Z3_solver_ref(api::context& c, solver_factory * f): api::object(c), m_solver_factory(f), m_solver(nullptr), m_logic(symbol::null), m_eh(nullptr) {} + Z3_solver_ref(api::context& c, solver * s): + api::object(c), m_solver_factory(nullptr), m_solver(s), m_logic(symbol::null), m_eh(nullptr) {} + void assert_expr(expr* e); void assert_expr(expr* e, expr* t); void set_eh(event_handler* eh); diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index b3e3ca9228b..4e5156ba93b 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -20,9 +20,11 @@ Revision History: #include "api/api_context.h" #include "api/api_tactic.h" #include "api/api_model.h" +#include "api/api_solver.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" +#include "ast/simplifiers/seq_simplifier.h" Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) { } @@ -45,6 +47,14 @@ extern "C" { RETURN_Z3(_result_); \ } +#define RETURN_SIMPLIFIER(_t_) { \ + Z3_simplifier_ref * _ref_ = alloc(Z3_simplifier_ref, *mk_c(c)); \ + _ref_->m_simplifier = _t_; \ + mk_c(c)->save_object(_ref_); \ + Z3_simplifier _result_ = of_simplifier(_ref_); \ + RETURN_Z3(_result_); \ +} + Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_mk_tactic(c, name); @@ -517,6 +527,146 @@ extern "C" { RETURN_Z3(result); Z3_CATCH_RETURN(nullptr); } + + + + Z3_simplifier Z3_API Z3_mk_simplifier(Z3_context c, Z3_string name) { + Z3_TRY; + LOG_Z3_mk_simplifier(c, name); + RESET_ERROR_CODE(); + simplifier_cmd * t = mk_c(c)->find_simplifier_cmd(symbol(name)); + if (t == nullptr) { + std::stringstream err; + err << "unknown simplifier " << name; + SET_ERROR_CODE(Z3_INVALID_ARG, err.str()); + RETURN_Z3(nullptr); + } + simplifier_factory new_t = t->factory(); + RETURN_SIMPLIFIER(new_t); + Z3_CATCH_RETURN(nullptr); + } + + void Z3_API Z3_simplifier_inc_ref(Z3_context c, Z3_simplifier t) { + Z3_TRY; + LOG_Z3_simplifier_inc_ref(c, t); + RESET_ERROR_CODE(); + to_simplifier(t)->inc_ref(); + Z3_CATCH; + } + + void Z3_API Z3_simplifier_dec_ref(Z3_context c, Z3_simplifier t) { + Z3_TRY; + LOG_Z3_simplifier_dec_ref(c, t); + if (t) + to_simplifier(t)->dec_ref(); + Z3_CATCH; + } + + unsigned Z3_API Z3_get_num_simplifiers(Z3_context c) { + Z3_TRY; + LOG_Z3_get_num_simplifiers(c); + RESET_ERROR_CODE(); + return mk_c(c)->num_simplifiers(); + Z3_CATCH_RETURN(0); + } + + Z3_string Z3_API Z3_get_simplifier_name(Z3_context c, unsigned idx) { + Z3_TRY; + LOG_Z3_get_simplifier_name(c, idx); + RESET_ERROR_CODE(); + if (idx >= mk_c(c)->num_simplifiers()) { + SET_ERROR_CODE(Z3_IOB, nullptr); + return ""; + } + return mk_c(c)->mk_external_string(mk_c(c)->get_simplifier(idx)->get_name().str().c_str()); + Z3_CATCH_RETURN(""); + } + + Z3_simplifier Z3_API Z3_simplifier_and_then(Z3_context c, Z3_simplifier t1, Z3_simplifier t2) { + Z3_TRY; + LOG_Z3_simplifier_and_then(c, t1, t2); + RESET_ERROR_CODE(); + auto fac1 = *to_simplifier_ref(t1); + auto fac2 = *to_simplifier_ref(t2); + auto new_s = [fac1, fac2](auto& m, auto& p, auto& st) { + auto* r = alloc(seq_simplifier, m, p, st); + r->add_simplifier(fac1(m, p, st)); + r->add_simplifier(fac2(m, p, st)); + return r; + }; + RETURN_SIMPLIFIER(new_s); + Z3_CATCH_RETURN(nullptr); + } + + Z3_simplifier Z3_API Z3_simplifier_using_params(Z3_context c, Z3_simplifier t, Z3_params p) { + Z3_TRY; + LOG_Z3_simplifier_using_params(c, t, p); + RESET_ERROR_CODE(); + param_descrs r; + ast_manager& m = mk_c(c)->m(); + default_dependent_expr_state st(m); + params_ref p1; + auto fac = (*to_simplifier_ref(t)); + scoped_ptr simp = fac(m, p1, st); + simp->collect_param_descrs(r); + auto params = to_param_ref(p); + params.validate(r); + auto new_s = [params, fac](auto& m, auto& p, auto& st) { + params_ref pp; + pp.append(params); + pp.append(p); + return fac(m, pp, st); + }; + RETURN_SIMPLIFIER(new_s); + Z3_CATCH_RETURN(nullptr); + } + + + Z3_string Z3_API Z3_simplifier_get_help(Z3_context c, Z3_simplifier t) { + Z3_TRY; + LOG_Z3_simplifier_get_help(c, t); + RESET_ERROR_CODE(); + std::ostringstream buffer; + param_descrs descrs; + ast_manager& m = mk_c(c)->m(); + default_dependent_expr_state st(m); + params_ref p; + scoped_ptr simp = (*to_simplifier_ref(t))(m, p, st); + simp->collect_param_descrs(descrs); + descrs.display(buffer); + return mk_c(c)->mk_external_string(buffer.str()); + Z3_CATCH_RETURN(""); + } + + Z3_param_descrs Z3_API Z3_simplifier_get_param_descrs(Z3_context c, Z3_simplifier t) { + Z3_TRY; + LOG_Z3_simplifier_get_param_descrs(c, t); + RESET_ERROR_CODE(); + Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c)); + mk_c(c)->save_object(d); + ast_manager& m = mk_c(c)->m(); + default_dependent_expr_state st(m); + params_ref p; + scoped_ptr simp = (*to_simplifier_ref(t))(m, p, st); + simp->collect_param_descrs(d->m_descrs); + Z3_param_descrs r = of_param_descrs(d); + RETURN_Z3(r); + Z3_CATCH_RETURN(nullptr); + } + + Z3_string Z3_API Z3_simplifier_get_descr(Z3_context c, Z3_string name) { + Z3_TRY; + LOG_Z3_simplifier_get_descr(c, name); + RESET_ERROR_CODE(); + simplifier_cmd * t = mk_c(c)->find_simplifier_cmd(symbol(name)); + if (t == nullptr) { + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + return ""; + } + return t->get_descr(); + Z3_CATCH_RETURN(""); + } + }; diff --git a/src/api/api_tactic.h b/src/api/api_tactic.h index 91e2d76ab36..4a1da24bdac 100644 --- a/src/api/api_tactic.h +++ b/src/api/api_tactic.h @@ -19,6 +19,7 @@ Revision History: #include "api/api_goal.h" #include "tactic/tactical.h" +#include "ast/simplifiers/dependent_expr_state.h" namespace api { class context; @@ -35,10 +36,19 @@ struct Z3_probe_ref : public api::object { Z3_probe_ref(api::context& c):api::object(c) {} }; +struct Z3_simplifier_ref : public api::object { + simplifier_factory m_simplifier; + Z3_simplifier_ref(api::context& c):api::object(c) {} +}; + inline Z3_tactic_ref * to_tactic(Z3_tactic g) { return reinterpret_cast(g); } inline Z3_tactic of_tactic(Z3_tactic_ref * g) { return reinterpret_cast(g); } inline tactic * to_tactic_ref(Z3_tactic g) { return g == nullptr ? nullptr : to_tactic(g)->m_tactic.get(); } +inline Z3_simplifier_ref * to_simplifier(Z3_simplifier g) { return reinterpret_cast(g); } +inline Z3_simplifier of_simplifier(Z3_simplifier_ref * g) { return reinterpret_cast(g); } +inline simplifier_factory * to_simplifier_ref(Z3_simplifier g) { return g == nullptr ? nullptr : &to_simplifier(g)->m_simplifier; } + inline Z3_probe_ref * to_probe(Z3_probe g) { return reinterpret_cast(g); } inline Z3_probe of_probe(Z3_probe_ref * g) { return reinterpret_cast(g); } inline probe * to_probe_ref(Z3_probe g) { return g == nullptr ? nullptr : to_probe(g)->m_probe.get(); } diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 3f47b6637ba..82081816801 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -63,6 +63,7 @@ namespace z3 { class solver; class goal; class tactic; + class simplifier; class probe; class model; class func_interp; @@ -2695,6 +2696,7 @@ namespace z3 { solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); check_error(); } solver(context & c, solver const& src, translate): object(c) { Z3_solver s = Z3_solver_translate(src.ctx(), src, c); check_error(); init(s); } solver(solver const & s):object(s) { init(s.m_solver); } + solver(solver const& s, simplifier const& simp); ~solver() { Z3_solver_dec_ref(ctx(), m_solver); } operator Z3_solver() const { return m_solver; } solver & operator=(solver const & s) { @@ -3065,6 +3067,47 @@ namespace z3 { return tactic(t1.ctx(), r); } + class simplifier : public object { + Z3_simplifier m_simplifier; + void init(Z3_simplifier s) { + m_simplifier = s; + Z3_simplifier_inc_ref(ctx(), s); + } + public: + simplifier(context & c, char const * name):object(c) { Z3_simplifier r = Z3_mk_simplifier(c, name); check_error(); init(r); } + simplifier(context & c, Z3_simplifier s):object(c) { init(s); } + simplifier(simplifier const & s):object(s) { init(s.m_simplifier); } + ~simplifier() { Z3_simplifier_dec_ref(ctx(), m_simplifier); } + operator Z3_simplifier() const { return m_simplifier; } + simplifier & operator=(simplifier const & s) { + Z3_simplifier_inc_ref(s.ctx(), s.m_simplifier); + Z3_simplifier_dec_ref(ctx(), m_simplifier); + object::operator=(s); + m_simplifier = s.m_simplifier; + return *this; + } + std::string help() const { char const * r = Z3_simplifier_get_help(ctx(), m_simplifier); check_error(); return r; } + friend simplifier operator&(simplifier const & t1, simplifier const & t2); + friend simplifier with(simplifier const & t, params const & p); + param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_simplifier_get_param_descrs(ctx(), m_simplifier)); } + }; + + inline solver::solver(solver const& s, simplifier const& simp):object(s) { init(Z3_solver_add_simplifier(s.ctx(), s, simp)); } + + + inline simplifier operator&(simplifier const & t1, simplifier const & t2) { + check_context(t1, t2); + Z3_simplifier r = Z3_simplifier_and_then(t1.ctx(), t1, t2); + t1.check_error(); + return simplifier(t1.ctx(), r); + } + + inline simplifier with(simplifier const & t, params const & p) { + Z3_simplifier r = Z3_simplifier_using_params(t.ctx(), t, p); + t.check_error(); + return simplifier(t.ctx(), r); + } + class probe : public object { Z3_probe m_probe; void init(Z3_probe s) { diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index cf5be3e07f7..0f7ef89990c 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -8169,6 +8169,64 @@ def as_expr(self): else: return Or([self[i].as_expr() for i in range(len(self))]) +######################################### +# +# Simplifiers +# +######################################### + +class Simplifier: + """Simplifiers act as pre-processing utilities for solvers. + Build a custom simplifier and add it to a solver""" + + def __init__(self, simplifier, ctx=None): + self.ctx = _get_ctx(ctx) + self.simplifier = None + if isinstance(simplifier, SimplifierObj): + self.simplifier = simplifier + elif isinstance(simplifier, list): + simps = [Simplifier(s, ctx) for s in simplifier] + self.simplifier = simps[0].simplifier + for i in range(1, len(simps)): + self.simplifier = Z3_simplifier_and_then(self.ctx.ref(), self.simplifier, simps[i].simplifier) + Z3_simplifier_inc_ref(self.ctx.ref(), self.simplifier) + return + else: + if z3_debug(): + _z3_assert(isinstance(simplifier, str), "simplifier name expected") + try: + self.simplifier = Z3_mk_simplifier(self.ctx.ref(), str(simplifier)) + except Z3Exception: + raise Z3Exception("unknown simplifier '%s'" % simplifier) + Z3_simplifier_inc_ref(self.ctx.ref(), self.simplifier) + + def __deepcopy__(self, memo={}): + return Simplifier(self.simplifier, self.ctx) + + def __del__(self): + if self.simplifier is not None and self.ctx.ref() is not None and Z3_simplifier_dec_ref is not None: + Z3_simplifier_dec_ref(self.ctx.ref(), self.simplifier) + + def using_params(self, *args, **keys): + """Return a simplifier that uses the given configuration options""" + p = args2params(args, keys, self.ctx) + return Simplifier(Z3_simplifier_using_params(self.ctx.ref(), self.simplifier, p.params), self.ctx) + + def add(self, solver): + """Return a solver that applies the simplification pre-processing specified by the simplifier""" + print(solver.solver) + print(self.simplifier) + return Solver(Z3_solver_add_simplifier(self.ctx.ref(), solver.solver, self.simplifier), self.ctx) + + def help(self): + """Display a string containing a description of the available options for the `self` simplifier.""" + print(Z3_simplifier_get_help(self.ctx.ref(), self.simplifier)) + + def param_descrs(self): + """Return the parameter description set.""" + return ParamDescrsRef(Z3_simplifier_get_param_descrs(self.ctx.ref(), self.simplifier), self.ctx) + + ######################################### # # Tactics diff --git a/src/api/python/z3/z3types.py b/src/api/python/z3/z3types.py index 500e3606ec9..9244e37d978 100644 --- a/src/api/python/z3/z3types.py +++ b/src/api/python/z3/z3types.py @@ -120,6 +120,12 @@ def __init__(self, tactic): def from_param(obj): return obj +class SimplifierObj(ctypes.c_void_p): + def __init__(self, simplifier): + self._as_parameter_ = simplifier + + def from_param(obj): + return obj class ProbeObj(ctypes.c_void_p): def __init__(self, probe): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 88aaa8938ee..0582ffa3700 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -23,6 +23,7 @@ DEFINE_TYPE(Z3_param_descrs); DEFINE_TYPE(Z3_parser_context); DEFINE_TYPE(Z3_goal); DEFINE_TYPE(Z3_tactic); +DEFINE_TYPE(Z3_simplifier); DEFINE_TYPE(Z3_probe); DEFINE_TYPE(Z3_stats); DEFINE_TYPE(Z3_solver); @@ -69,6 +70,7 @@ DEFINE_TYPE(Z3_rcf_num); - \c Z3_ast_map: mapping from \c Z3_ast to \c Z3_ast objects. - \c Z3_goal: set of formulas that can be solved and/or transformed using tactics and solvers. - \c Z3_tactic: basic building block for creating custom solvers for specific problem domains. + - \c Z3_simplifier: basic building block for creating custom pre-processing simplifiers. - \c Z3_probe: function/predicate used to inspect a goal and collect information that may be used to decide which solver and/or preprocessing step will be used. - \c Z3_apply_result: collection of subgoals resulting from applying of a tactic to a goal. - \c Z3_solver: (incremental) solver, possibly specialized by a particular tactic or logic. @@ -1403,6 +1405,7 @@ typedef enum def_Type('PARSER_CONTEXT', 'Z3_parser_context', 'ParserContextObj') def_Type('GOAL', 'Z3_goal', 'GoalObj') def_Type('TACTIC', 'Z3_tactic', 'TacticObj') + def_Type('SIMPLIFIER', 'Z3_simplifier', 'SimplifierObj') def_Type('PARAMS', 'Z3_params', 'Params') def_Type('PROBE', 'Z3_probe', 'ProbeObj') def_Type('STATS', 'Z3_stats', 'StatsObj') @@ -6207,7 +6210,7 @@ extern "C" { /**@}*/ - /** @name Tactics and Probes */ + /** @name Tactics, Simplifiers and Probes */ /**@{*/ /** \brief Return a tactic associated with the given name. @@ -6359,6 +6362,97 @@ extern "C" { */ Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p); + + /** + \brief Return a simplifier associated with the given name. + The complete list of simplifiers may be obtained using the procedures #Z3_get_num_simplifiers and #Z3_get_simplifier_name. + It may also be obtained using the command \ccode{(help-simplifier)} in the SMT 2.0 front-end. + + Simplifiers are the basic building block for creating custom solvers for specific problem domains. + + def_API('Z3_mk_simplifier', SIMPLIFIER, (_in(CONTEXT), _in(STRING))) + */ + Z3_simplifier Z3_API Z3_mk_simplifier(Z3_context c, Z3_string name); + + /** + \brief Increment the reference counter of the given simplifier. + + def_API('Z3_simplifier_inc_ref', VOID, (_in(CONTEXT), _in(SIMPLIFIER))) + */ + void Z3_API Z3_simplifier_inc_ref(Z3_context c, Z3_simplifier t); + + /** + \brief Decrement the reference counter of the given simplifier. + + def_API('Z3_simplifier_dec_ref', VOID, (_in(CONTEXT), _in(SIMPLIFIER))) + */ + void Z3_API Z3_simplifier_dec_ref(Z3_context c, Z3_simplifier g); + + /** + \brief Attach simplifier to a solver. The solver will use the simplifier for incremental pre-processing. + + def_API('Z3_solver_add_simplifier', SOLVER, (_in(CONTEXT), _in(SOLVER), _in(SIMPLIFIER))) + */ + Z3_solver Z3_API Z3_solver_add_simplifier(Z3_context c, Z3_solver solver, Z3_simplifier simplifier); + + /** + \brief Return a simplifier that applies \c t1 to a given goal and \c t2 + to every subgoal produced by \c t1. + + def_API('Z3_simplifier_and_then', SIMPLIFIER, (_in(CONTEXT), _in(SIMPLIFIER), _in(SIMPLIFIER))) + */ + Z3_simplifier Z3_API Z3_simplifier_and_then(Z3_context c, Z3_simplifier t1, Z3_simplifier t2); + + /** + \brief Return a simplifier that applies \c t using the given set of parameters. + + def_API('Z3_simplifier_using_params', SIMPLIFIER, (_in(CONTEXT), _in(SIMPLIFIER), _in(PARAMS))) + */ + Z3_simplifier Z3_API Z3_simplifier_using_params(Z3_context c, Z3_simplifier t, Z3_params p); + + + /** + \brief Return the number of builtin simplifiers available in Z3. + + \sa Z3_get_simplifier_name + + def_API('Z3_get_num_simplifiers', UINT, (_in(CONTEXT),)) + */ + unsigned Z3_API Z3_get_num_simplifiers(Z3_context c); + + /** + \brief Return the name of the idx simplifier. + + \pre i < Z3_get_num_simplifiers(c) + + \sa Z3_get_num_simplifiers + + def_API('Z3_get_simplifier_name', STRING, (_in(CONTEXT), _in(UINT))) + */ + Z3_string Z3_API Z3_get_simplifier_name(Z3_context c, unsigned i); + + /** + \brief Return a string containing a description of parameters accepted by the given simplifier. + + def_API('Z3_simplifier_get_help', STRING, (_in(CONTEXT), _in(SIMPLIFIER))) + */ + Z3_string Z3_API Z3_simplifier_get_help(Z3_context c, Z3_simplifier t); + + /** + \brief Return the parameter description set for the given simplifier object. + + def_API('Z3_simplifier_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(SIMPLIFIER))) + */ + Z3_param_descrs Z3_API Z3_simplifier_get_param_descrs(Z3_context c, Z3_simplifier t); + + /** + \brief Return a string containing a description of the simplifier with the given name. + + def_API('Z3_simplifier_get_descr', STRING, (_in(CONTEXT), _in(STRING))) + */ + Z3_string Z3_API Z3_simplifier_get_descr(Z3_context c, Z3_string name); + + /** \brief Return a probe that always evaluates to val. diff --git a/src/cmd_context/tactic_manager.h b/src/cmd_context/tactic_manager.h index ec60b087324..b1e48a5edc8 100644 --- a/src/cmd_context/tactic_manager.h +++ b/src/cmd_context/tactic_manager.h @@ -37,13 +37,15 @@ class tactic_manager { void insert(simplifier_cmd* c); void insert(probe_info * p); tactic_cmd * find_tactic_cmd(symbol const & s) const; - probe_info * find_probe(symbol const & s) const; + probe_info * find_probe(symbol const & s) const; simplifier_cmd* find_simplifier_cmd(symbol const& s) const; unsigned num_tactics() const { return m_tactics.size(); } unsigned num_probes() const { return m_probes.size(); } + unsigned num_simplifiers() const { return m_simplifiers.size(); } tactic_cmd * get_tactic(unsigned i) const { return m_tactics[i]; } probe_info * get_probe(unsigned i) const { return m_probes[i]; } + simplifier_cmd *get_simplifier(unsigned i) const { return m_simplifiers[i]; } ptr_vector const& simplifiers() const { return m_simplifiers; } ptr_vector const& tactics() const { return m_tactics; } From 4c6d44f974d16210436c51b07c3f57ab8f0e649d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 18:58:18 -0800 Subject: [PATCH 358/597] add ocaml signature for simplifier Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 29 +++++++++++++++++++++++++++++ src/api/ml/z3.mli | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 2fa4acc6567..ffb815233fb 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1734,6 +1734,34 @@ struct let interrupt = Z3native.interrupt end +module Simplifier = +struct + type simplifier = Z3native.simplifier + let gc = Z3native.context_of_simplifier + let mk_simplifier = Z3native.mk_simplifier + + let get_num_simplifiers = Z3native.get_num_simplifiers + + let get_simplifier_names (ctx:context) = + let n = get_num_simplifiers ctx in + let f i = Z3native.get_simplifier_name ctx i in + mk_list f n + + let get_help (x:simplifier) = Z3native.simplifier_get_help (gc x) x + let get_param_descrs (x:simplifier) = Z3native.simplifier_get_param_descrs (gc x) x + + let and_then (ctx:context) (t1:simplifier) (t2:simplifier) (ts:simplifier list) = + let f p c = (match p with + | None -> Some c + | Some(x) -> Some (Z3native.simplifier_and_then ctx c x)) in + match (List.fold_left f None ts) with + | None -> Z3native.simplifier_and_then ctx t1 t2 + | Some(x) -> let o = Z3native.simplifier_and_then ctx t2 x in + Z3native.simplifier_and_then ctx t1 o + let using_params = Z3native.simplifier_using_params + let with_ = using_params + +end module Statistics = struct @@ -1868,6 +1896,7 @@ struct let mk_solver_s ctx logic = mk_solver ctx (Some (Symbol.mk_string ctx logic)) let mk_simple_solver = Z3native.mk_simple_solver let mk_solver_t = Z3native.mk_solver_from_tactic + let add_simplifier = Z3native.solver_add_simplifier let translate x = Z3native.solver_translate (gc x) x let to_string x = Z3native.solver_to_string (gc x) x end diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index b7fa27b5ecd..6c0f0f60b8e 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3102,6 +3102,40 @@ sig val interrupt : context -> unit end +moduls Simplifier : +sig + type simplifier + + (** A string containing a description of parameters accepted by the simplifier. *) + val get_help : simplifier -> string + + (** Retrieves parameter descriptions for Simplifiers. *) + val get_param_descrs : simplifier -> Params.ParamDescrs.param_descrs + + (** Apply the simplifier to the goal. *) + val apply : simplifier -> Goal.goal -> Params.params option -> ApplyResult.apply_result + + (** The number of supported simplifiers. *) + val get_num_simplifiers : context -> int + + (** The names of all supported simplifiers. *) + val get_simplifier_names : context -> string list + + (** Returns a string containing a description of the simplifier with the given name. *) + val get_simplifier_description : context -> string -> string + + (** Creates a new Simplifier. *) + val mk_simplifier : context -> string -> simplifier + + (** Create a simplifier that applies one simplifier to a Goal and + then another one to every subgoal produced by the first one. *) + val and_then : context -> simplifier -> simplifier -> simplifier list -> simplifier + + (** Create a simplifier that applies a simplifier using the given set of parameters. *) + val using_params : context -> simplifier -> Params.params -> simplifier + +end + (** Objects that track statistical information. *) module Statistics : sig @@ -3265,6 +3299,9 @@ sig will always solve each check from scratch. *) val mk_solver_t : context -> Tactic.tactic -> solver + (** Create a solver with simplifying pre-processing **) + val add_simplifier : context -> solver -> Simplifier.simplifier -> solver + (** Create a clone of the current solver with respect to a context. *) val translate : solver -> context -> solver From 162fa3dc96f8aafadc2562a6dd528871694d18fe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 19:06:20 -0800 Subject: [PATCH 359/597] disambiguate overloaded with for Julia bindings Signed-off-by: Nikolaj Bjorner --- src/api/julia/z3jl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/julia/z3jl.cpp b/src/api/julia/z3jl.cpp index 2627679e2e3..5aef2f41db0 100644 --- a/src/api/julia/z3jl.cpp +++ b/src/api/julia/z3jl.cpp @@ -528,7 +528,7 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m) m.BINARY_OP(tactic, &, &); m.BINARY_OP(tactic, |, |); m.method("repeat", &repeat); - m.method("with", &with); + m.method("with", static_cast(&with)); m.method("try_for", &try_for); m.method("par_or", &par_or); m.method("par_and_then", &par_and_then); From 17bae9b4c1897f6ac2163aeb7f84b869b3abda77 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 19:09:37 -0800 Subject: [PATCH 360/597] update ml api Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3native.ml.pre | 1 + src/api/ml/z3native_stubs.c.pre | 1 + 2 files changed, 2 insertions(+) diff --git a/src/api/ml/z3native.ml.pre b/src/api/ml/z3native.ml.pre index 93df8ad07ae..1d75d5d1efa 100644 --- a/src/api/ml/z3native.ml.pre +++ b/src/api/ml/z3native.ml.pre @@ -20,6 +20,7 @@ and solver = ptr and solver_callback = ptr and goal = ptr and tactic = ptr +and simplifier = ptr and params = ptr and parser_context = ptr and probe = ptr diff --git a/src/api/ml/z3native_stubs.c.pre b/src/api/ml/z3native_stubs.c.pre index e9cfa443b1c..038b80725ee 100644 --- a/src/api/ml/z3native_stubs.c.pre +++ b/src/api/ml/z3native_stubs.c.pre @@ -424,6 +424,7 @@ MK_PLUS_OBJ(func_interp, 32) MK_PLUS_OBJ(func_entry, 32) MK_PLUS_OBJ(goal, 64) MK_PLUS_OBJ(tactic, 64) +MK_PLUS_OBJ(simplifier, 64) MK_PLUS_OBJ(probe, 64) MK_PLUS_OBJ(apply_result, 32) MK_PLUS_OBJ(solver, 20 * 1000) From 9a94a9aa6f49fd7bd3e21f0a34563e7fab0c4456 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 19:14:24 -0800 Subject: [PATCH 361/597] update ml api Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.mli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 6c0f0f60b8e..51116935642 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3102,7 +3102,7 @@ sig val interrupt : context -> unit end -moduls Simplifier : +module Simplifier : sig type simplifier From 1289937d1a7b0aa28b311dcbb59a4338a7de8149 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 19:19:41 -0800 Subject: [PATCH 362/597] update ml api Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.mli | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 51116935642..96d68d1398a 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3112,9 +3112,6 @@ sig (** Retrieves parameter descriptions for Simplifiers. *) val get_param_descrs : simplifier -> Params.ParamDescrs.param_descrs - (** Apply the simplifier to the goal. *) - val apply : simplifier -> Goal.goal -> Params.params option -> ApplyResult.apply_result - (** The number of supported simplifiers. *) val get_num_simplifiers : context -> int From d51d518f96778ad27a67160ed214c959829843c6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 19:24:45 -0800 Subject: [PATCH 363/597] update ml api Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 11 ++++++++--- src/api/ml/z3.mli | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index ffb815233fb..d71b3b21fbd 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1738,7 +1738,10 @@ module Simplifier = struct type simplifier = Z3native.simplifier let gc = Z3native.context_of_simplifier - let mk_simplifier = Z3native.mk_simplifier + + let get_help (x:simplifier) = Z3native.simplifier_get_help (gc x) x + + let get_param_descrs (x:simplifier) = Z3native.simplifier_get_param_descrs (gc x) x let get_num_simplifiers = Z3native.get_num_simplifiers @@ -1747,8 +1750,9 @@ struct let f i = Z3native.get_simplifier_name ctx i in mk_list f n - let get_help (x:simplifier) = Z3native.simplifier_get_help (gc x) x - let get_param_descrs (x:simplifier) = Z3native.simplifier_get_param_descrs (gc x) x + let get_simplifier_description (ctx:context) (s:string) = Z3native.simplifier_get_descr + + let mk_simplifier = Z3native.mk_simplifier let and_then (ctx:context) (t1:simplifier) (t2:simplifier) (ts:simplifier list) = let f p c = (match p with @@ -1758,6 +1762,7 @@ struct | None -> Z3native.simplifier_and_then ctx t1 t2 | Some(x) -> let o = Z3native.simplifier_and_then ctx t2 x in Z3native.simplifier_and_then ctx t1 o + let using_params = Z3native.simplifier_using_params let with_ = using_params diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 96d68d1398a..27b0992ca62 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3130,6 +3130,7 @@ sig (** Create a simplifier that applies a simplifier using the given set of parameters. *) val using_params : context -> simplifier -> Params.params -> simplifier + val with_ : context -> simplifier -> Params.params -> simplifier end From 63c0f35978773c24f8c29c6279e1f0d3c99215ac Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Jan 2023 19:27:17 -0800 Subject: [PATCH 364/597] update ml api Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index d71b3b21fbd..ac21902fe6c 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1750,7 +1750,7 @@ struct let f i = Z3native.get_simplifier_name ctx i in mk_list f n - let get_simplifier_description (ctx:context) (s:string) = Z3native.simplifier_get_descr + let get_simplifier_description = Z3native.simplifier_get_descr let mk_simplifier = Z3native.mk_simplifier From 151a62338cf3cfb9858d7bc7ad8d84154dcf44a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 12:13:09 +0000 Subject: [PATCH 365/597] Bump docker/build-push-action from 3.3.0 to 4.0.0 (#6562) --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index ca41ceaa7a0..d9baccd39a7 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v3.3.0 + uses: docker/build-push-action@v4.0.0 with: context: . push: true From 19fed09122a07cf177361d7012fc0682849e6374 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Feb 2023 08:35:23 -0800 Subject: [PATCH 366/597] protecting add_simplifier API against mis-use Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 34 ++++++++++++++++++++++++++++------ src/api/python/z3/z3.py | 2 -- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 0a3b35aed6b..ca39329bbe4 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -233,16 +233,38 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + /** + * attach a simplifier to solver. + * This is legal when the solver is fresh, does not already have assertions (and scopes). + * To allow recycling the argument solver, we create a fresh copy of it and pass it to + * mk_simplifier_solver. + */ Z3_solver Z3_API Z3_solver_add_simplifier(Z3_context c, Z3_solver solver, Z3_simplifier simplifier) { Z3_TRY; LOG_Z3_solver_add_simplifier(c, solver, simplifier); - init_solver(c, solver); + solver_ref s_fresh; + if (to_solver(solver)->m_solver) { + s_fresh = to_solver_ref(solver)->translate(mk_c(c)->m(), to_solver(solver)->m_params); + } + else { + // create the solver, but hijack it for internal uses. + init_solver(c, solver); + s_fresh = to_solver(solver)->m_solver; + to_solver(solver)->m_solver = nullptr; + } + if (!s_fresh) { + SET_ERROR_CODE(Z3_INVALID_ARG, "unexpected empty solver state"); + RETURN_Z3(nullptr); + } + if (s_fresh->get_num_assertions() > 0) { + SET_ERROR_CODE(Z3_INVALID_ARG, "adding a simplifier to a solver with assertions is not allowed."); + RETURN_Z3(nullptr); + } auto simp = to_simplifier_ref(simplifier); - auto* slv = mk_simplifier_solver(to_solver_ref(solver), simp); - Z3_solver_ref* sr = alloc(Z3_solver_ref, *mk_c(c), slv); - mk_c(c)->save_object(sr); - // ?? init_solver_log(c, sr) - RETURN_Z3(of_solver(sr)); + auto* simplifier_solver = mk_simplifier_solver(s_fresh.get(), simp); + Z3_solver_ref* result = alloc(Z3_solver_ref, *mk_c(c), simplifier_solver); + mk_c(c)->save_object(result); + RETURN_Z3(of_solver(result)); Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 0f7ef89990c..5cb84723003 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -8214,8 +8214,6 @@ def using_params(self, *args, **keys): def add(self, solver): """Return a solver that applies the simplification pre-processing specified by the simplifier""" - print(solver.solver) - print(self.simplifier) return Solver(Z3_solver_add_simplifier(self.ctx.ref(), solver.solver, self.simplifier), self.ctx) def help(self): From 0f86a00229991b7e42ba86119d4460db853106d9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Jan 2023 13:53:51 -0800 Subject: [PATCH 367/597] use setter method to easier track updates to settings. --- src/math/lp/lar_core_solver.h | 2 +- src/math/lp/lar_solver.cpp | 10 +++++----- src/math/lp/lp_settings.h | 4 ++-- src/shell/lp_frontend.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 0043ed23d93..bcd33966f90 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -222,7 +222,7 @@ class lar_core_solver { m_d_x.resize(m_d_A.column_count()); pop_basis(k); m_stacked_simplex_strategy.pop(k); - settings().simplex_strategy() = m_stacked_simplex_strategy; + settings().set_simplex_strategy(m_stacked_simplex_strategy); lp_assert(m_r_solver.basis_heading_is_correct()); lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index f78dab1197a..46ed0b5a93b 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -300,7 +300,7 @@ namespace lp { m_term_register.shrink(m_term_count); m_terms.resize(m_term_count); m_simplex_strategy.pop(k); - m_settings.simplex_strategy() = m_simplex_strategy; + m_settings.set_simplex_strategy(m_simplex_strategy); lp_assert(sizes_are_correct()); lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_usage_in_terms.pop(k); @@ -465,10 +465,10 @@ namespace lp { switch (settings().simplex_strategy()) { case simplex_strategy_enum::tableau_rows: - settings().simplex_strategy() = simplex_strategy_enum::tableau_costs; + settings().set_simplex_strategy(simplex_strategy_enum::tableau_costs); prepare_costs_for_r_solver(term); ret = maximize_term_on_tableau(term, term_max); - settings().simplex_strategy() = simplex_strategy_enum::tableau_rows; + settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows); set_costs_to_zero(term); m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); return ret; @@ -2006,10 +2006,10 @@ namespace lp { void lar_solver::decide_on_strategy_and_adjust_initial_state() { lp_assert(strategy_is_undecided()); if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { - m_settings.simplex_strategy() = simplex_strategy_enum::lu; + m_settings.set_simplex_strategy(simplex_strategy_enum::lu); } else { - m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs? + m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? } adjust_initial_state(); } diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index aa06cb26361..2245f6f4ed1 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -336,8 +336,8 @@ struct lp_settings { return m_simplex_strategy; } - simplex_strategy_enum & simplex_strategy() { - return m_simplex_strategy; + void set_simplex_strategy(simplex_strategy_enum s) { + m_simplex_strategy = s; } bool use_lu() const { diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp index 70d2cffb1f0..8d6425533c8 100644 --- a/src/shell/lp_frontend.cpp +++ b/src/shell/lp_frontend.cpp @@ -80,7 +80,7 @@ void run_solver(smt_params_helper & params, char const * mps_file_name) { solver->settings().set_message_ostream(&std::cout); solver->settings().report_frequency = params.arith_rep_freq(); solver->settings().print_statistics = params.arith_print_stats(); - solver->settings().simplex_strategy() = lp:: simplex_strategy_enum::lu; + solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); solver->find_maximal_solution(); From 682e86812927f00fe69b532fb7660018aa2de64e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Feb 2023 10:14:24 -0800 Subject: [PATCH 368/597] initialize field Signed-off-by: Nikolaj Bjorner --- src/math/lp/lp_core_solver_base.h | 2 +- src/math/lp/lp_core_solver_base_def.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 673ef240441..5cde4485d00 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -74,9 +74,9 @@ class lp_core_solver_base { vector & m_x; // a feasible solution, the fist time set in the constructor vector & m_costs; lp_settings & m_settings; + lu> * m_factorization = nullptr; vector m_y; // the buffer for yB = cb // a device that is able to solve Bx=c, xB=d, and change the basis - lu> * m_factorization; const column_namer & m_column_names; indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index c1b64492b62..f85de111128 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -55,7 +55,6 @@ lp_core_solver_base(static_matrix & A, m_costs(costs), m_settings(settings), m_y(m_m()), - m_factorization(nullptr), m_column_names(column_names), m_w(m_m()), m_d(m_n()), From 38d526ee4505c9109ecdc9e1f134946539c4e3a1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Feb 2023 10:18:03 -0800 Subject: [PATCH 369/597] fix warning Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/eliminate_predicates.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 2166913da04..6c1eccea00c 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -567,9 +567,9 @@ void eliminate_predicates::try_find_macro(clause& cl) { return false; app* x = to_app(_x); return - can_be_quasi_macro_head(x, cl.m_bound.size()) && - is_macro_safe(y) && - !occurs(x->get_decl(), y); + can_be_quasi_macro_head(x, cl.m_bound.size()) && + is_macro_safe(y) && + !occurs(x->get_decl(), y); }; if (cl.is_unit() && m.is_eq(cl.atom(0), x, y)) { @@ -592,7 +592,8 @@ void eliminate_predicates::try_find_macro(clause& cl) { } if (cl.is_unit()) { expr* body = cl.sign(0) ? m.mk_false() : m.mk_true(); - if (can_be_qdef(cl.atom(0), body)) { + expr* x = cl.atom(0); + if (can_be_qdef(x, body)) { insert_quasi_macro(to_app(x), body, cl); return; } From 30fa37e3936fc5508a34c262a68b9444368e53c8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Feb 2023 10:31:34 -0800 Subject: [PATCH 370/597] fix warnings Signed-off-by: Nikolaj Bjorner --- src/ast/recfun_decl_plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index e2d48066408..66544bec7b3 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -60,7 +60,7 @@ namespace recfun { func_decl_ref m_pred; // Date: Wed, 1 Feb 2023 11:07:47 -0800 Subject: [PATCH 371/597] fix test Signed-off-by: Nikolaj Bjorner --- src/test/lp/lp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 547985f266f..c82cdd0a4b4 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1391,7 +1391,7 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { settings.set_random_seed(n); } if (get_int_from_args_parser("--simplex_strategy", args_parser, n)) { - settings.simplex_strategy() = static_cast(n); + settings.set_simplex_strategy(static_cast(n)); } } From 6c7dd4a8634886e6ede8ccfaf71bb21f2a9d6641 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Feb 2023 19:47:58 -0800 Subject: [PATCH 372/597] fix incremental pre-processing to work with assumptions/cores and consequences Signed-off-by: Nikolaj Bjorner --- src/solver/simplifier_solver.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 219b2c46beb..29021af765a 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -95,10 +95,21 @@ class simplifier_solver : public solver { expr_ref_vector m_assumptions; model_converter_ref m_mc; bool m_inconsistent = false; + expr_safe_replace m_core_replace; + + void replace(expr_ref_vector& r) { + expr_ref tmp(m); + for (unsigned i = 0; i < r.size(); ++i) { + m_core_replace(r.get(i), tmp); + r[i] = tmp; + } + } void flush(expr_ref_vector& assumptions) { unsigned qhead = m_preprocess_state.qhead(); - if (qhead < m_fmls.size()) { + expr_ref_vector orig_assumptions(assumptions); + m_core_replace.reset(); + if (qhead < m_fmls.size() || !assumptions.empty()) { for (expr* a : assumptions) m_preprocess_state.freeze(a); TRACE("solver", tout << "qhead " << qhead << "\n"); @@ -107,6 +118,8 @@ class simplifier_solver : public solver { if (!m.inc()) return; m_preprocess_state.advance_qhead(); + for (unsigned i = 0; i < assumptions.size(); ++i) + m_core_replace.insert(assumptions.get(i), orig_assumptions.get(i)); } m_mc = m_preprocess_state.model_trail().get_model_converter(); m_cached_mc = nullptr; @@ -148,6 +161,7 @@ class simplifier_solver : public solver { m_preprocess_state(*this), m_preprocess(m, s->get_params(), m_preprocess_state), m_assumptions(m), + m_core_replace(m), m_proof(m) { if (fac) @@ -189,7 +203,7 @@ class simplifier_solver : public solver { lbool check_sat_core(unsigned num_assumptions, expr* const* assumptions) override { expr_ref_vector _assumptions(m, num_assumptions, assumptions); flush(_assumptions); - return s->check_sat_core(num_assumptions, assumptions); + return s->check_sat_core(num_assumptions, _assumptions.data()); } void collect_statistics(statistics& st) const override { @@ -258,7 +272,7 @@ class simplifier_solver : public solver { std::string reason_unknown() const override { return s->reason_unknown(); } void set_reason_unknown(char const* msg) override { s->set_reason_unknown(msg); } void get_labels(svector& r) override { s->get_labels(r); } - void get_unsat_core(expr_ref_vector& r) { s->get_unsat_core(r); } + void get_unsat_core(expr_ref_vector& r) { s->get_unsat_core(r); replace(r); } ast_manager& get_manager() const override { return s->get_manager(); } void reset_params(params_ref const& p) override { s->reset_params(p); } params_ref const& get_params() const override { return s->get_params(); } @@ -276,7 +290,13 @@ class simplifier_solver : public solver { lbool check_sat_cc(expr_ref_vector const& cube, vector const& clauses) override { return check_sat_cc(cube, clauses); } void set_progress_callback(progress_callback* callback) override { s->set_progress_callback(callback); } lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { - return s->get_consequences(asms, vars, consequences); + expr_ref_vector es(m); + es.append(asms); + es.append(vars); + flush(es); + lbool r = s->get_consequences(asms, vars, consequences); + replace(consequences); + return r; } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return s->find_mutexes(vars, mutexes); } lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override { return s->preferred_sat(asms, cores); } From 72e7a8a4817537e82cd851d93f1879b0f51635bf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Feb 2023 20:00:38 -0800 Subject: [PATCH 373/597] fix incremental pre-processing to work with consequences/cubes Signed-off-by: Nikolaj Bjorner --- src/solver/simplifier_solver.cpp | 48 ++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 29021af765a..0c41d4f7eb2 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -287,21 +287,59 @@ class simplifier_solver : public solver { unsigned get_num_assumptions() const override { return s->get_num_assumptions(); } expr* get_assumption(unsigned idx) const override { return s->get_assumption(idx); } unsigned get_scope_level() const override { return s->get_scope_level(); } - lbool check_sat_cc(expr_ref_vector const& cube, vector const& clauses) override { return check_sat_cc(cube, clauses); } void set_progress_callback(progress_callback* callback) override { s->set_progress_callback(callback); } + lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { expr_ref_vector es(m); es.append(asms); es.append(vars); flush(es); - lbool r = s->get_consequences(asms, vars, consequences); + expr_ref_vector asms1(m, asms.size(), es.data()); + expr_ref_vector vars1(m, vars.size(), es.data() + asms.size()); + lbool r = s->get_consequences(asms1, vars1, consequences); replace(consequences); return r; } - lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return s->find_mutexes(vars, mutexes); } - lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override { return s->preferred_sat(asms, cores); } + + lbool check_sat_cc(expr_ref_vector const& cube, vector const& clauses) override { + expr_ref_vector es(m); + es.append(cube); + for (auto const& c : clauses) + es.append(c); + flush(es); + expr_ref_vector cube1(m, cube.size(), es.data()); + vector clauses1; + unsigned offset = cube.size(); + for (auto const& c : clauses) { + clauses1.push_back(expr_ref_vector(m, c.size(), es.data() + offset)); + offset += c.size(); + } + return check_sat_cc(cube1, clauses1); + } + + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { + expr_ref_vector vars1(vars); + flush(vars1); + lbool r = s->find_mutexes(vars1, mutexes); + for (auto& mux : mutexes) + replace(mux); + return r; + } + + lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override { + expr_ref_vector asms1(asms); + flush(asms1); + lbool r = s->preferred_sat(asms1, cores); + for (auto& c : cores) + replace(c); + return r; + } - expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { return s->cube(vars, backtrack_level); } + // todo flush? + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { + return s->cube(vars, backtrack_level); + } + expr* congruence_root(expr* e) override { return s->congruence_root(e); } expr* congruence_next(expr* e) override { return s->congruence_next(e); } std::ostream& display(std::ostream& out, unsigned n, expr* const* assumptions) const override { From 2e068e3f563a04a0ddf085b55fd249ccb226b92d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 17:41:00 -0800 Subject: [PATCH 374/597] add simplifiers to .net API --- src/api/dotnet/CMakeLists.txt | 1 + src/api/dotnet/Context.cs | 116 +++++++++++++++++++++ src/api/dotnet/Simplifiers.cs | 78 ++++++++++++++ src/ast/simplifiers/dependent_expr_state.h | 3 +- src/solver/simplifier_solver.cpp | 5 +- 5 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 src/api/dotnet/Simplifiers.cs diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index a9344aa8655..fcd7b0d8586 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -103,6 +103,7 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE SeqExpr.cs SeqSort.cs SetSort.cs + Simplifiers.cs Solver.cs Sort.cs Statistics.cs diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 80b5a95f17d..6365852a6d9 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -3726,6 +3726,110 @@ public void Interrupt() } #endregion + #region Simplifiers + /// + /// The number of supported simplifiers. + /// + public uint NumSimplifiers + { + get { return Native.Z3_get_num_simplifiers(nCtx); } + } + + /// + /// The names of all supported tactics. + /// + public string[] SimplifierNames + { + get + { + + uint n = NumSimplifiers; + string[] res = new string[n]; + for (uint i = 0; i < n; i++) + res[i] = Native.Z3_get_simplifier_name(nCtx, i); + return res; + } + } + + /// + /// Returns a string containing a description of the simplifier with the given name. + /// + public string SimplifierDescription(string name) + { + + return Native.Z3_simplifier_get_descr(nCtx, name); + } + + /// + /// Creates a new Tactic. + /// + public Simplifier MkSimplifier(string name) + { + + return new Simplifier(this, name); + } + + /// + /// Create a simplifie that applies and + /// then . + /// + public Simplifier AndThen(Simplifier t1, Simplifier t2, params Simplifier[] ts) + { + Debug.Assert(t1 != null); + Debug.Assert(t2 != null); + // Debug.Assert(ts == null || Contract.ForAll(0, ts.Length, j => ts[j] != null)); + + + CheckContextMatch(t1); + CheckContextMatch(t2); + CheckContextMatch(ts); + + IntPtr last = IntPtr.Zero; + if (ts != null && ts.Length > 0) + { + last = ts[ts.Length - 1].NativeObject; + for (int i = ts.Length - 2; i >= 0; i--) + last = Native.Z3_simplifier_and_then(nCtx, ts[i].NativeObject, last); + } + if (last != IntPtr.Zero) + { + last = Native.Z3_simplifier_and_then(nCtx, t2.NativeObject, last); + return new Simplifier(this, Native.Z3_simplifier_and_then(nCtx, t1.NativeObject, last)); + } + else + return new Simplifier(this, Native.Z3_simplifier_and_then(nCtx, t1.NativeObject, t2.NativeObject)); + } + + /// + /// Create a simplifier that applies and then + /// then . + /// + /// + /// Shorthand for AndThen. + /// + public Simplifier Then(Simplifier t1, Simplifier t2, params Simplifier[] ts) + { + Debug.Assert(t1 != null); + Debug.Assert(t2 != null); + // Debug.Assert(ts == null || Contract.ForAll(0, ts.Length, j => ts[j] != null)); + + return AndThen(t1, t2, ts); + } + + /// + /// Create a tactic that applies using the given set of parameters . + /// + public Simplifier UsingParams(Simplifier t, Params p) + { + Debug.Assert(t != null); + Debug.Assert(p != null); + + CheckContextMatch(t); + CheckContextMatch(p); + return new Simplifier(this, Native.Z3_simplifier_using_params(nCtx, t.NativeObject, p.NativeObject)); + } + #endregion + #region Probes /// /// The number of supported Probes. @@ -3926,6 +4030,16 @@ public Solver MkSimpleSolver() return new Solver(this, Native.Z3_mk_simple_solver(nCtx)); } + /// + /// Creates a solver that uses an incremental simplifier. + /// + public Solver MkSolver(Solver s, Simplifier t) + { + Debug.Assert(t != null); + Debug.Assert(s != null); + return new Solver(this, Native.Z3_solver_add_simplifier(nCtx, s.NativeObject, t.NativeObject)); + } + /// /// Creates a solver that is implemented using the given tactic. /// @@ -3939,6 +4053,8 @@ public Solver MkSolver(Tactic t) return new Solver(this, Native.Z3_mk_solver_from_tactic(nCtx, t.NativeObject)); } + + #endregion #region Fixedpoints diff --git a/src/api/dotnet/Simplifiers.cs b/src/api/dotnet/Simplifiers.cs new file mode 100644 index 00000000000..a7fbe185fe7 --- /dev/null +++ b/src/api/dotnet/Simplifiers.cs @@ -0,0 +1,78 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + Tactic.cs + +Abstract: + + Z3 Managed API: Simplifiers + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-21 + +--*/ + +using System; +using System.Diagnostics; + +namespace Microsoft.Z3 +{ + /// + /// Simplifiers are the basic building block for creating custom solvers with incremental pre-processing. + /// The complete list of simplifiers may be obtained using Context.NumSimplifiers + /// and Context.SimplifierNames. + /// It may also be obtained using the command (help-simplifier) in the SMT 2.0 front-end. + /// + public class Simplifier : Z3Object + { + /// + /// A string containing a description of parameters accepted by the tactic. + /// + public string Help + { + get + { + + return Native.Z3_simplifier_get_help(Context.nCtx, NativeObject); + } + } + + /// + /// Retrieves parameter descriptions for Simplifiers. + /// + public ParamDescrs ParameterDescriptions + { + get { return new ParamDescrs(Context, Native.Z3_simplifier_get_param_descrs(Context.nCtx, NativeObject)); } + } + + #region Internal + internal Simplifier(Context ctx, IntPtr obj) + : base(ctx, obj) + { + Debug.Assert(ctx != null); + } + internal Simplifier(Context ctx, string name) + : base(ctx, Native.Z3_mk_simplifier(ctx.nCtx, name)) + { + Debug.Assert(ctx != null); + } + + internal override void IncRef(IntPtr o) + { + Native.Z3_simplifier_inc_ref(Context.nCtx, o); + } + + internal override void DecRef(IntPtr o) + { + lock (Context) + { + if (Context.nCtx != IntPtr.Zero) + Native.Z3_simplifier_dec_ref(Context.nCtx, o); + } + } + #endregion + } +} diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index d4d449cf846..b4fe4e9d45d 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -90,7 +90,8 @@ class dependent_expr_state { * Freeze internal functions */ void freeze(expr* term); - bool frozen(func_decl* f) const { return m_frozen.is_marked(f); } + void freeze(expr_ref_vector const& terms) { for (expr* t : terms) freeze(t); } + bool frozen(func_decl* f) const { return m_frozen.is_marked(f); } bool frozen(expr* f) const { return is_app(f) && m_frozen.is_marked(to_app(f)->get_decl()); } void freeze_suffix(); diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 0c41d4f7eb2..3ac84ea3675 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -110,10 +110,9 @@ class simplifier_solver : public solver { expr_ref_vector orig_assumptions(assumptions); m_core_replace.reset(); if (qhead < m_fmls.size() || !assumptions.empty()) { - for (expr* a : assumptions) - m_preprocess_state.freeze(a); TRACE("solver", tout << "qhead " << qhead << "\n"); - m_preprocess_state.replay(qhead, assumptions); + m_preprocess_state.replay(qhead, assumptions); + m_preprocess_state.freeze(assumptions); m_preprocess.reduce(); if (!m.inc()) return; From 4143c542571948368a6a36334a21392ed50ddb3f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 19:06:26 -0800 Subject: [PATCH 375/597] add simplifier to java API --- src/api/dotnet/Simplifiers.cs | 2 +- src/api/java/CMakeLists.txt | 2 + src/api/java/Context.java | 115 ++++++++++++++++++++++++ src/api/java/Simplifier.java | 58 ++++++++++++ src/api/java/SimplifierDecRefQueue.java | 31 +++++++ 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 src/api/java/Simplifier.java create mode 100644 src/api/java/SimplifierDecRefQueue.java diff --git a/src/api/dotnet/Simplifiers.cs b/src/api/dotnet/Simplifiers.cs index a7fbe185fe7..28469c1e557 100644 --- a/src/api/dotnet/Simplifiers.cs +++ b/src/api/dotnet/Simplifiers.cs @@ -3,7 +3,7 @@ Module Name: - Tactic.cs + Simplifiers.cs Abstract: diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index e0d6bd0a006..4b13a25b1a4 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -165,6 +165,8 @@ set(Z3_JAVA_JAR_SOURCE_FILES SeqExpr.java SeqSort.java SetSort.java + Simplifier.java + SimplifierDecRefQueue.java SolverDecRefQueue.java Solver.java Sort.java diff --git a/src/api/java/Context.java b/src/api/java/Context.java index d4810123559..0f15d9411f3 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -3081,6 +3081,106 @@ public void interrupt() Native.interrupt(nCtx()); } + /** + * The number of supported simplifiers. + **/ + public int getNumSimplifiers() + { + return Native.getNumSimplifiers(nCtx()); + } + + /** + * The names of all supported simplifiers. + **/ + public String[] getSimplifierNames() + { + + int n = getNumSimplifiers(); + String[] res = new String[n]; + for (int i = 0; i < n; i++) + res[i] = Native.getSimplifierName(nCtx(), i); + return res; + } + + /** + * Returns a string containing a description of the simplifier with the given + * name. + **/ + public String getSimplifierDescription(String name) + { + return Native.simplifierGetDescr(nCtx(), name); + } + + /** + * Creates a new Simplifier. + **/ + public Simplifier mkSimplifier(String name) + { + return new Simplifier(this, name); + } + + /** + * Create a simplifier that applies {@code t1} and then {@code t1} + **/ + public Simplifier andThen(Simplifier t1, Simplifier t2, Simplifier... ts) + + { + checkContextMatch(t1); + checkContextMatch(t2); + checkContextMatch(ts); + + long last = 0; + if (ts != null && ts.length > 0) + { + last = ts[ts.length - 1].getNativeObject(); + for (int i = ts.length - 2; i >= 0; i--) { + last = Native.simplifierAndThen(nCtx(), ts[i].getNativeObject(), + last); + } + } + if (last != 0) + { + last = Native.simplifierAndThen(nCtx(), t2.getNativeObject(), last); + return new Simplifier(this, Native.simplifierAndThen(nCtx(), + t1.getNativeObject(), last)); + } else + return new Simplifier(this, Native.simplifierAndThen(nCtx(), + t1.getNativeObject(), t2.getNativeObject())); + } + + /** + * Create a simplifier that applies {@code t1} and then {@code t2} + * + * Remarks: Shorthand for {@code AndThen}. + **/ + public Simplifier then(Simplifier t1, Simplifier t2, Simplifier... ts) + { + return andThen(t1, t2, ts); + } + + /** + * Create a simplifier that applies {@code t} using the given set of + * parameters {@code p}. + **/ + public Simplifier usingParams(Simplifier t, Params p) + { + checkContextMatch(t); + checkContextMatch(p); + return new Simplifier(this, Native.simplifierUsingParams(nCtx(), + t.getNativeObject(), p.getNativeObject())); + } + + /** + * Create a simplifier that applies {@code t} using the given set of + * parameters {@code p}. + * Remarks: Alias for + * {@code UsingParams} + **/ + public Simplifier with(Simplifier t, Params p) + { + return usingParams(t, p); + } + /** * The number of supported Probes. **/ @@ -3279,6 +3379,14 @@ public Solver mkSolver(Tactic t) t.getNativeObject())); } + /** + * Creates a solver that is uses the simplifier pre-processing. + **/ + public Solver mkSolver(Solver s, Simplifier simp) + { + return new Solver(this, Native.solverAddSimplifier(nCtx(), s.getNativeObject(), simp.getNativeObject())); + } + /** * Create a Fixedpoint context. **/ @@ -4209,6 +4317,7 @@ void checkContextMatch(Z3Object[] arr) private SolverDecRefQueue m_Solver_DRQ = new SolverDecRefQueue(); private StatisticsDecRefQueue m_Statistics_DRQ = new StatisticsDecRefQueue(); private TacticDecRefQueue m_Tactic_DRQ = new TacticDecRefQueue(); + private SimplifierDecRefQueue m_Simplifier_DRQ = new SimplifierDecRefQueue(); private FixedpointDecRefQueue m_Fixedpoint_DRQ = new FixedpointDecRefQueue(); private OptimizeDecRefQueue m_Optimize_DRQ = new OptimizeDecRefQueue(); private ConstructorDecRefQueue m_Constructor_DRQ = new ConstructorDecRefQueue(); @@ -4293,6 +4402,11 @@ public IDecRefQueue getTacticDRQ() return m_Tactic_DRQ; } + public IDecRefQueue getSimplifierDRQ() + { + return m_Simplifier_DRQ; + } + public IDecRefQueue getFixedpointDRQ() { return m_Fixedpoint_DRQ; @@ -4323,6 +4437,7 @@ public void close() m_Optimize_DRQ.forceClear(this); m_Statistics_DRQ.forceClear(this); m_Tactic_DRQ.forceClear(this); + m_Simplifier_DRQ.forceClear(this); m_Fixedpoint_DRQ.forceClear(this); m_boolSort = null; diff --git a/src/api/java/Simplifier.java b/src/api/java/Simplifier.java new file mode 100644 index 00000000000..b3fc89ccf11 --- /dev/null +++ b/src/api/java/Simplifier.java @@ -0,0 +1,58 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + Simplifiers.cs + +Abstract: + + Z3 Managed API: Simplifiers + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-21 + +--*/ + +package com.microsoft.z3; + + +public class Simplifier extends Z3Object { + /* + * A string containing a description of parameters accepted by the simplifier. + */ + + public String getHelp() + { + return Native.simplifierGetHelp(getContext().nCtx(), getNativeObject()); + } + + /* + * Retrieves parameter descriptions for Simplifiers. + */ + public ParamDescrs getParameterDescriptions() { + return new ParamDescrs(getContext(), Native.simplifierGetParamDescrs(getContext().nCtx(), getNativeObject())); + } + + Simplifier(Context ctx, long obj) + { + super(ctx, obj); + } + + Simplifier(Context ctx, String name) + { + super(ctx, Native.mkSimplifier(ctx.nCtx(), name)); + } + + @Override + void incRef() + { + Native.simplifierIncRef(getContext().nCtx(), getNativeObject()); + } + + @Override + void addToReferenceQueue() { + getContext().getSimplifierDRQ().storeReference(getContext(), this); + } +} \ No newline at end of file diff --git a/src/api/java/SimplifierDecRefQueue.java b/src/api/java/SimplifierDecRefQueue.java new file mode 100644 index 00000000000..ba15dd5be38 --- /dev/null +++ b/src/api/java/SimplifierDecRefQueue.java @@ -0,0 +1,31 @@ +/** +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + SimplifierDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ + +package com.microsoft.z3; + +class SimplifierDecRefQueue extends IDecRefQueue { + public SimplifierDecRefQueue() + { + super(); + } + + @Override + protected void decRef(Context ctx, long obj) + { + Native.simplifierDecRef(ctx.nCtx(), obj); + } +} From ed4a84e5d3637d3a0e4088dd1613055a7972cf07 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 19:21:34 -0800 Subject: [PATCH 376/597] compiler warning Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_cluster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_cluster.cpp b/src/muz/spacer/spacer_cluster.cpp index b03562b1263..fac3a920f19 100644 --- a/src/muz/spacer/spacer_cluster.cpp +++ b/src/muz/spacer/spacer_cluster.cpp @@ -378,7 +378,7 @@ void lemma_cluster_finder::cluster(lemma_ref &lemma) { << pattern << "\n" << " and lemma cube: " << lcube << "\n";); - for (const lemma_ref &l : neighbours) { + for (auto l : neighbours) { SASSERT(cluster->can_contain(l)); bool added = cluster->add_lemma(l, false); (void)added; From efbecb19b162a767afe3ee62291018a757413791 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 19:23:30 -0800 Subject: [PATCH 377/597] compiler warning Signed-off-by: Nikolaj Bjorner --- src/smt/theory_fpa.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index d63ff93cd2f..2ecc17c45e3 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -671,7 +671,6 @@ namespace smt { out << "equivalence classes:\n"; for (enode * n : ctx.enodes()) { - expr * e = n->get_expr(); expr * r = n->get_root()->get_expr(); out << r->get_id() << " --> " << enode_pp(n, ctx) << "\n"; } From 741634b7032a9c449c19d571f8e8622e0df61176 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 19:26:51 -0800 Subject: [PATCH 378/597] compiler warning fix Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_powers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index 4054dd7c190..3af31a48816 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -131,7 +131,7 @@ namespace nla { return l_false; } - if (xval >= 3 && yval != 0 & rval <= yval + 1) { + if (xval >= 3 && yval != 0 && rval <= yval + 1) { new_lemma lemma(c, "x >= 3, y != 0 => x^y > ln(x)y + 1"); lemma |= ineq(x, llc::LT, rational(3)); lemma |= ineq(y, llc::EQ, rational::zero()); From 0d05104d8c68eaa15ce2659eb8b7c1f3de277af0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 19:33:23 -0800 Subject: [PATCH 379/597] remove unused field Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/dominator_simplifier.cpp | 14 +++++++------- src/ast/simplifiers/dominator_simplifier.h | 2 +- src/ast/substitution/demodulator_rewriter.cpp | 1 - src/ast/substitution/demodulator_rewriter.h | 1 - 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ast/simplifiers/dominator_simplifier.cpp b/src/ast/simplifiers/dominator_simplifier.cpp index 12f2e29417c..2ef4528ab22 100644 --- a/src/ast/simplifiers/dominator_simplifier.cpp +++ b/src/ast/simplifiers/dominator_simplifier.cpp @@ -41,7 +41,7 @@ expr_ref dominator_simplifier::simplify_ite(app * ite) { if (is_subexpr(child, t) && !is_subexpr(child, e)) simplify_rec(child); - pop(scope_level() - old_lvl); + local_pop(scope_level() - old_lvl); expr_ref new_t = simplify_arg(t); reset_cache(); if (!assert_expr(new_c, true)) { @@ -50,7 +50,7 @@ expr_ref dominator_simplifier::simplify_ite(app * ite) { for (expr * child : tree(ite)) if (is_subexpr(child, e) && !is_subexpr(child, t)) simplify_rec(child); - pop(scope_level() - old_lvl); + local_pop(scope_level() - old_lvl); expr_ref new_e = simplify_arg(e); if (c == new_c && t == new_t && e == new_e) { @@ -159,7 +159,7 @@ expr_ref dominator_simplifier::simplify_and_or(bool is_and, app * e) { r = simplify_arg(arg); args.push_back(r); if (!assert_expr(r, !is_and)) { - pop(scope_level() - old_lvl); + local_pop(scope_level() - old_lvl); r = is_and ? m.mk_false() : m.mk_true(); reset_cache(); return true; @@ -181,7 +181,7 @@ expr_ref dominator_simplifier::simplify_and_or(bool is_and, app * e) { args.reverse(); } - pop(scope_level() - old_lvl); + local_pop(scope_level() - old_lvl); reset_cache(); return { is_and ? mk_and(args) : mk_or(args), m }; } @@ -191,7 +191,7 @@ expr_ref dominator_simplifier::simplify_not(app * e) { ENSURE(m.is_not(e, ee)); unsigned old_lvl = scope_level(); expr_ref t = simplify_rec(ee); - pop(scope_level() - old_lvl); + local_pop(scope_level() - old_lvl); reset_cache(); return mk_not(t); } @@ -245,7 +245,7 @@ void dominator_simplifier::reduce() { } m_fmls.update(i, dependent_expr(m, r, new_pr, d)); } - pop(scope_level()); + local_pop(scope_level()); // go backwards m_forward = false; @@ -268,7 +268,7 @@ void dominator_simplifier::reduce() { } m_fmls.update(i, dependent_expr(m, r, new_pr, d)); } - pop(scope_level()); + local_pop(scope_level()); } SASSERT(scope_level() == 0); } diff --git a/src/ast/simplifiers/dominator_simplifier.h b/src/ast/simplifiers/dominator_simplifier.h index 562aeace1e5..b412f6db0bb 100644 --- a/src/ast/simplifiers/dominator_simplifier.h +++ b/src/ast/simplifiers/dominator_simplifier.h @@ -48,7 +48,7 @@ class dominator_simplifier : public dependent_expr_simplifier { expr* idom(expr *e) const { return m_dominators.idom(e); } unsigned scope_level() { return m_simplifier->scope_level(); } - void pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); } + void local_pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); } bool assert_expr(expr* f, bool sign) { return m_simplifier->assert_expr(f, sign); } diff --git a/src/ast/substitution/demodulator_rewriter.cpp b/src/ast/substitution/demodulator_rewriter.cpp index c25647c01e6..f174b149186 100644 --- a/src/ast/substitution/demodulator_rewriter.cpp +++ b/src/ast/substitution/demodulator_rewriter.cpp @@ -780,7 +780,6 @@ void demodulator_rewriter::operator()(expr_ref_vector const& exprs, demodulator_match_subst::demodulator_match_subst(ast_manager & m): - m(m), m_subst(m) { } diff --git a/src/ast/substitution/demodulator_rewriter.h b/src/ast/substitution/demodulator_rewriter.h index 3a1e442d5e2..8a1e6feb535 100644 --- a/src/ast/substitution/demodulator_rewriter.h +++ b/src/ast/substitution/demodulator_rewriter.h @@ -111,7 +111,6 @@ class demodulator_match_subst { typedef std::pair expr_pair; typedef obj_pair_hashtable cache; - ast_manager & m; substitution m_subst; cache m_cache; svector m_todo; From 39d28189235aba92da2f50a36ad864e7cf7fd66e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 19:36:22 -0800 Subject: [PATCH 380/597] compiler warnings/bugs Signed-off-by: Nikolaj Bjorner --- src/solver/simplifier_solver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 3ac84ea3675..a717c4932d7 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -224,7 +224,7 @@ class simplifier_solver : public solver { } proof_ref m_proof; - proof* get_proof_core() { + proof* get_proof_core() override { proof* p = s->get_proof(); m_proof = p; if (p) { @@ -271,7 +271,7 @@ class simplifier_solver : public solver { std::string reason_unknown() const override { return s->reason_unknown(); } void set_reason_unknown(char const* msg) override { s->set_reason_unknown(msg); } void get_labels(svector& r) override { s->get_labels(r); } - void get_unsat_core(expr_ref_vector& r) { s->get_unsat_core(r); replace(r); } + void get_unsat_core(expr_ref_vector& r) override { s->get_unsat_core(r); replace(r); } ast_manager& get_manager() const override { return s->get_manager(); } void reset_params(params_ref const& p) override { s->reset_params(p); } params_ref const& get_params() const override { return s->get_params(); } @@ -313,7 +313,7 @@ class simplifier_solver : public solver { clauses1.push_back(expr_ref_vector(m, c.size(), es.data() + offset)); offset += c.size(); } - return check_sat_cc(cube1, clauses1); + return s->check_sat_cc(cube1, clauses1); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { From 839f87a10c919d1c61040c1e91e4d831c3d46071 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Feb 2023 20:50:46 -0800 Subject: [PATCH 381/597] don't apply tactics in parse mode Signed-off-by: Nikolaj Bjorner --- src/cmd_context/tactic_cmds.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index 4559586f3f4..a0805110bf4 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -296,9 +296,10 @@ class apply_tactic_cmd : public exec_given_tactic_cmd { } void execute(cmd_context & ctx) override { - if (!m_tactic) { + if (!m_tactic) throw cmd_exception("apply needs a tactic argument"); - } + if (ctx.ignore_check()) + return; params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); { From be44ace995b0f653406d9e9867a183cbe657ea3c Mon Sep 17 00:00:00 2001 From: Frederick Robinson Date: Fri, 3 Feb 2023 13:08:35 -0800 Subject: [PATCH 382/597] fix typo (#6569) --- src/api/python/z3/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 5cb84723003..92bb2aa24d7 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -9072,7 +9072,7 @@ def PbGe(args, k): def PbEq(args, k, ctx=None): - """Create a Pseudo-Boolean inequality k constraint. + """Create a Pseudo-Boolean equality k constraint. >>> a, b, c = Bools('a b c') >>> f = PbEq(((a,1),(b,3),(c,2)), 3) From d69155b9e992df203c7b1240964c1b5829fc548f Mon Sep 17 00:00:00 2001 From: Jakob Rath Date: Fri, 3 Feb 2023 22:08:47 +0100 Subject: [PATCH 383/597] Shared features from polysat branch (#6567) * Allow setting default debug action * Fix dlist and add iterator * Add var_queue iterator * Add some helpers * rational: machine_div2k and pseudo_inverse * Basic support for non-copyable types in map * tbv helpers * pdd updates * Remove duplicate functions gcc doesn't like having both versions --- src/math/dd/dd_pdd.cpp | 106 +++++++++++++++++++++++++---- src/math/dd/dd_pdd.h | 36 ++++++++-- src/test/pdd.cpp | 33 +++++++++ src/util/debug.cpp | 67 ++++++++++++++----- src/util/debug.h | 12 ++++ src/util/dlist.h | 147 ++++++++++++++++++++++++++++++++++++----- src/util/map.h | 8 +++ src/util/mpq.h | 8 +++ src/util/rational.cpp | 18 +++++ src/util/rational.h | 9 +++ src/util/tbv.h | 27 +++++--- src/util/util.h | 43 ++++++++++-- src/util/var_queue.h | 4 ++ 13 files changed, 456 insertions(+), 62 deletions(-) diff --git a/src/math/dd/dd_pdd.cpp b/src/math/dd/dd_pdd.cpp index cca2d5e049d..291af3b5cb7 100644 --- a/src/math/dd/dd_pdd.cpp +++ b/src/math/dd/dd_pdd.cpp @@ -165,6 +165,47 @@ namespace dd { return true; } + unsigned pdd_manager::min_parity(PDD p) { + if (m_semantics != mod2N_e) + return 0; + + if (is_val(p)) { + rational v = val(p); + if (v.is_zero()) + return m_power_of_2 + 1; + unsigned r = 0; + while (v.is_even() && v > 0) + r++, v /= 2; + return r; + } + init_mark(); + PDD q = p; + m_todo.push_back(hi(q)); + while (!is_val(q)) { + q = lo(q); + m_todo.push_back(hi(q)); + } + unsigned p2 = val(q).trailing_zeros(); + init_mark(); + while (p2 != 0 && !m_todo.empty()) { + PDD r = m_todo.back(); + m_todo.pop_back(); + if (is_marked(r)) + continue; + set_mark(r); + if (!is_val(r)) { + m_todo.push_back(lo(r)); + m_todo.push_back(hi(r)); + } + else if (val(r).is_zero()) + continue; + else if (val(r).trailing_zeros() < p2) + p2 = val(r).trailing_zeros(); + } + m_todo.reset(); + return p2; + } + pdd pdd_manager::subst_val(pdd const& p, pdd const& s) { return pdd(apply(p.root, s.root, pdd_subst_val_op), this); } @@ -185,7 +226,20 @@ namespace dd { pdd v_val = mk_var(v) + val; return pdd(apply(s.root, v_val.root, pdd_subst_add_op), this); } - + + bool pdd_manager::subst_get(pdd const& s, unsigned v, rational& out_val) { + unsigned level_v = m_var2level[v]; + PDD p = s.root; + while (/* !is_val(p) && */ level(p) > level_v) { + SASSERT(is_val(lo(p))); + p = hi(p); + } + if (!is_val(p) && level(p) == level_v) { + out_val = val(lo(p)); + return true; + } + return false; + } pdd_manager::PDD pdd_manager::apply(PDD arg1, PDD arg2, pdd_op op) { bool first = true; @@ -1154,6 +1208,11 @@ namespace dd { return true; } + /** Return true iff p contains no variables other than v. */ + bool pdd_manager::is_univariate_in(PDD p, unsigned v) { + return (is_val(p) || var(p) == v) && is_univariate(p); + } + /** * Push coefficients of univariate polynomial in order of ascending degree. * Example: a*x^2 + b*x + c ==> [ c, b, a ] @@ -1532,7 +1591,6 @@ namespace dd { } void pdd_manager::gc() { - m_gc_generation++; init_dmark(); m_free_nodes.reset(); SASSERT(well_formed()); @@ -1617,26 +1675,26 @@ namespace dd { std::ostream& pdd_manager::display(std::ostream& out, pdd const& b) { auto mons = to_monomials(b); bool first = true; - for (auto& m : mons) { + for (auto& [a, vs] : mons) { if (!first) out << " "; - if (m.first.is_neg()) + if (a.is_neg()) out << "- "; else if (!first) out << "+ "; first = false; - rational c = abs(m.first); - m.second.reverse(); - if (!c.is_one() || m.second.empty()) { - if (m_semantics == mod2N_e && mod(-c, m_mod2N) < c) - out << -mod(-c, m_mod2N); - else + rational c = abs(a); + vs.reverse(); + if (!c.is_one() || vs.empty()) { + if (m_semantics == mod2N_e) + out << val_pp(*this, c, !vs.empty()); + else out << c; - if (!m.second.empty()) out << "*"; + if (!vs.empty()) out << "*"; } unsigned v_prev = UINT_MAX; unsigned pow = 0; - for (unsigned v : m.second) { + for (unsigned v : vs) { if (v == v_prev) { pow++; continue; @@ -1660,6 +1718,23 @@ namespace dd { return out; } + std::ostream& val_pp::display(std::ostream& out) const { + if (m.get_semantics() != pdd_manager::mod2N_e) + return out << val; + unsigned pow; + if (val.is_power_of_two(pow) && pow > 10) + return out << "2^" << pow; + for (int offset : {-2, -1, 1, 2}) + if (val < m.max_value() && (val - offset).is_power_of_two(pow) && pow > 10 && pow < m.power_of_2()) + return out << lparen() << "2^" << pow << (offset >= 0 ? "+" : "") << offset << rparen(); + rational neg_val = mod(-val, m.two_to_N()); + if (neg_val < val) { // keep this condition so we don't suddenly print negative values where we wouldn't otherwise + if (neg_val.is_power_of_two(pow) && pow > 10) + return out << "-2^" << pow; + } + return out << m.normalize(val); + } + bool pdd_manager::well_formed() { bool ok = true; for (unsigned n : m_free_nodes) { @@ -1737,6 +1812,13 @@ namespace dd { return p.val(); } + rational const& pdd::offset() const { + pdd p = *this; + while (!p.is_val()) + p = p.lo(); + return p.val(); + } + pdd pdd::shl(unsigned n) const { return (*this) * rational::power_of_two(n); } diff --git a/src/math/dd/dd_pdd.h b/src/math/dd/dd_pdd.h index aef0eb6f7e4..6dee7977ff6 100644 --- a/src/math/dd/dd_pdd.h +++ b/src/math/dd/dd_pdd.h @@ -10,7 +10,7 @@ Module Name: Poly DD package It is a mild variant of ZDDs. - In PDDs arithmetic is either standard or using mod 2 (over GF2). + In PDDs arithmetic is either standard or using mod 2^n. Non-leaf nodes are of the form x*hi + lo where @@ -208,7 +208,6 @@ namespace dd { rational m_mod2N; unsigned m_power_of_2 = 0; rational m_max_value; - unsigned m_gc_generation = 0; ///< will be incremented on each GC void reset_op_cache(); void init_nodes(unsigned_vector const& l2v); @@ -254,7 +253,9 @@ namespace dd { inline bool is_val(PDD p) const { return m_nodes[p].is_val(); } inline bool is_internal(PDD p) const { return m_nodes[p].is_internal(); } inline bool is_var(PDD p) const { return !is_val(p) && is_zero(lo(p)) && is_one(hi(p)); } + inline bool is_max(PDD p) const { SASSERT(m_semantics == mod2_e || m_semantics == mod2N_e); return is_val(p) && val(p) == max_value(); } bool is_never_zero(PDD p); + unsigned min_parity(PDD p); inline unsigned level(PDD p) const { return m_nodes[p].m_level; } inline unsigned var(PDD p) const { return m_level2var[level(p)]; } inline PDD lo(PDD p) const { return m_nodes[p].m_lo; } @@ -315,6 +316,11 @@ namespace dd { pdd_manager(unsigned num_vars, semantics s = free_e, unsigned power_of_2 = 0); ~pdd_manager(); + pdd_manager(pdd_manager const&) = delete; + pdd_manager(pdd_manager&&) = delete; + pdd_manager& operator=(pdd_manager const&) = delete; + pdd_manager& operator=(pdd_manager&&) = delete; + semantics get_semantics() const { return m_semantics; } void reset(unsigned_vector const& level2var); @@ -343,6 +349,7 @@ namespace dd { pdd subst_val(pdd const& a, unsigned v, rational const& val); pdd subst_val(pdd const& a, pdd const& s); pdd subst_add(pdd const& s, unsigned v, rational const& val); + bool subst_get(pdd const& s, unsigned v, rational& out_val); bool resolve(unsigned v, pdd const& p, pdd const& q, pdd& r); pdd reduce(unsigned v, pdd const& a, pdd const& b); void quot_rem(pdd const& a, pdd const& b, pdd& q, pdd& r); @@ -357,6 +364,7 @@ namespace dd { bool is_monomial(PDD p); bool is_univariate(PDD p); + bool is_univariate_in(PDD p, unsigned v); void get_univariate_coefficients(PDD p, vector& coeff); // create an spoly r if leading monomials of a and b overlap @@ -375,6 +383,8 @@ namespace dd { unsigned power_of_2() const { return m_power_of_2; } rational const& max_value() const { return m_max_value; } rational const& two_to_N() const { return m_mod2N; } + rational normalize(rational const& n) const { return mod(-n, m_mod2N) < n ? -mod(-n, m_mod2N) : n; } + unsigned_vector const& free_vars(pdd const& p); @@ -406,21 +416,26 @@ namespace dd { unsigned var() const { return m.var(root); } rational const& val() const { SASSERT(is_val()); return m.val(root); } rational const& leading_coefficient() const; + rational const& offset() const; bool is_val() const { return m.is_val(root); } bool is_one() const { return m.is_one(root); } bool is_zero() const { return m.is_zero(root); } bool is_linear() const { return m.is_linear(root); } bool is_var() const { return m.is_var(root); } - /** Polynomial is of the form a * x + b for numerals a, b. */ + bool is_max() const { return m.is_max(root); } + /** Polynomial is of the form a * x + b for some numerals a, b. */ bool is_unilinear() const { return !is_val() && lo().is_val() && hi().is_val(); } + /** Polynomial is of the form a * x for some numeral a. */ bool is_unary() const { return !is_val() && lo().is_zero() && hi().is_val(); } bool is_offset() const { return !is_val() && lo().is_val() && hi().is_one(); } bool is_binary() const { return m.is_binary(root); } bool is_monomial() const { return m.is_monomial(root); } bool is_univariate() const { return m.is_univariate(root); } + bool is_univariate_in(unsigned v) const { return m.is_univariate_in(root, v); } void get_univariate_coefficients(vector& coeff) const { m.get_univariate_coefficients(root, coeff); } vector get_univariate_coefficients() const { vector coeff; m.get_univariate_coefficients(root, coeff); return coeff; } bool is_never_zero() const { return m.is_never_zero(root); } + unsigned min_parity() const { return m.min_parity(root); } bool var_is_leaf(unsigned v) const { return m.var_is_leaf(root, v); } pdd operator-() const { return m.minus(*this); } @@ -455,7 +470,8 @@ namespace dd { pdd subst_val0(vector> const& s) const { return m.subst_val0(*this, s); } pdd subst_val(pdd const& s) const { return m.subst_val(*this, s); } pdd subst_val(unsigned v, rational const& val) const { return m.subst_val(*this, v, val); } - pdd subst_add(unsigned var, rational const& val) { return m.subst_add(*this, var, val); } + pdd subst_add(unsigned var, rational const& val) const { return m.subst_add(*this, var, val); } + bool subst_get(unsigned var, rational& out_val) const { return m.subst_get(*this, var, out_val); } /** * \brief substitute variable v by r. @@ -538,6 +554,18 @@ namespace dd { bool operator!=(pdd_iterator const& other) const { return m_nodes != other.m_nodes; } }; + class val_pp { + pdd_manager const& m; + rational const& val; + bool require_parens; + char const* lparen() const { return require_parens ? "(" : ""; } + char const* rparen() const { return require_parens ? ")" : ""; } + public: + val_pp(pdd_manager const& m, rational const& val, bool require_parens = false): m(m), val(val), require_parens(require_parens) {} + std::ostream& display(std::ostream& out) const; + }; + + inline std::ostream& operator<<(std::ostream& out, val_pp const& v) { return v.display(out); } } diff --git a/src/test/pdd.cpp b/src/test/pdd.cpp index a0946d81d9d..0c9b0f85c2f 100644 --- a/src/test/pdd.cpp +++ b/src/test/pdd.cpp @@ -571,6 +571,38 @@ class test { } } + static void subst_get() { + std::cout << "subst_get\n"; + pdd_manager m(4, pdd_manager::mod2N_e, 32); + + unsigned const va = 0; + unsigned const vb = 1; + unsigned const vc = 2; + unsigned const vd = 3; + + rational val; + pdd s = m.one(); + std::cout << s << "\n"; + VERIFY(!s.subst_get(va, val)); + VERIFY(!s.subst_get(vb, val)); + VERIFY(!s.subst_get(vc, val)); + VERIFY(!s.subst_get(vd, val)); + + s = s.subst_add(va, rational(5)); + std::cout << s << "\n"; + VERIFY(s.subst_get(va, val) && val == 5); + VERIFY(!s.subst_get(vb, val)); + VERIFY(!s.subst_get(vc, val)); + VERIFY(!s.subst_get(vd, val)); + + s = s.subst_add(vc, rational(7)); + std::cout << s << "\n"; + VERIFY(s.subst_get(va, val) && val == 5); + VERIFY(!s.subst_get(vb, val)); + VERIFY(s.subst_get(vc, val) && val == 7); + VERIFY(!s.subst_get(vd, val)); + } + static void univariate() { std::cout << "univariate\n"; pdd_manager m(4, pdd_manager::mod2N_e, 4); @@ -671,6 +703,7 @@ void tst_pdd() { dd::test::binary_resolve(); dd::test::pow(); dd::test::subst_val(); + dd::test::subst_get(); dd::test::univariate(); dd::test::factors(); } diff --git a/src/util/debug.cpp b/src/util/debug.cpp index f97a2b57bb2..c9ca9fc312a 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -75,32 +75,62 @@ bool is_debug_enabled(const char * tag) { return g_enabled_debug_tags->contains(tag); } +atomic g_default_debug_action(debug_action::ask); + +debug_action get_default_debug_action() { + return g_default_debug_action; +} + +void set_default_debug_action(debug_action a) { + g_default_debug_action = a; +} + +debug_action ask_debug_action(std::istream& in) { + std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n"; + char result; + bool ok = bool(in >> result); + if (!ok) + exit(ERR_INTERNAL_FATAL); // happens if std::cin is eof or unattached. + switch(result) { + case 'C': + case 'c': + return debug_action::cont; + case 'A': + case 'a': + return debug_action::abort; + case 'S': + case 's': + return debug_action::stop; + case 't': + case 'T': + return debug_action::throw_exception; + case 'G': + case 'g': + return debug_action::invoke_debugger; + default: + std::cerr << "INVALID COMMAND\n"; + return debug_action::ask; + } +} + #if !defined(_WINDOWS) && !defined(NO_Z3_DEBUGGER) void invoke_gdb() { std::string buffer; - int * x = nullptr; + int *x = nullptr; + debug_action a = get_default_debug_action(); for (;;) { - std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n"; - char result; - bool ok = bool(std::cin >> result); - if (!ok) exit(ERR_INTERNAL_FATAL); // happens if std::cin is eof or unattached. - switch(result) { - case 'C': - case 'c': + switch (a) { + case debug_action::cont: return; - case 'A': - case 'a': + case debug_action::abort: exit(1); - case 'S': - case 's': + case debug_action::stop: // force seg fault... *x = 0; return; - case 't': - case 'T': + case debug_action::throw_exception: throw default_exception("assertion violation"); - case 'G': - case 'g': + case debug_action::invoke_debugger: buffer = "gdb -nw /proc/" + std::to_string(getpid()) + "/exe " + std::to_string(getpid()); std::cerr << "invoking GDB...\n"; if (system(buffer.c_str()) == 0) { @@ -109,12 +139,13 @@ void invoke_gdb() { else { std::cerr << "error starting GDB...\n"; // forcing seg fault. - int * x = nullptr; + int *x = nullptr; *x = 0; } return; + case debug_action::ask: default: - std::cerr << "INVALID COMMAND\n"; + a = ask_debug_action(std::cin); } } } diff --git a/src/util/debug.h b/src/util/debug.h index 795976eacf4..5f092b181ed 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -19,10 +19,22 @@ Revision History: #pragma once #include +#include void enable_assertions(bool f); bool assertions_enabled(); +enum class debug_action { + ask, + cont, + abort, + stop, + throw_exception, + invoke_debugger, +}; +debug_action get_default_debug_action(); +void set_default_debug_action(debug_action a); + #include "util/error_codes.h" #include "util/warning.h" diff --git a/src/util/dlist.h b/src/util/dlist.h index 7efe5bb53bd..e5c95b8cf38 100644 --- a/src/util/dlist.h +++ b/src/util/dlist.h @@ -17,20 +17,38 @@ Revision History: --*/ #pragma once +#include +#include "util/debug.h" +#include "util/util.h" +#define DLIST_EXTRA_ASSERTIONS 0 -template +template class dll_iterator; + +template class dll_base { - T* m_next { nullptr }; - T* m_prev { nullptr }; + T* m_next = nullptr; + T* m_prev = nullptr; + +protected: + dll_base() = default; + ~dll_base() = default; + public: + dll_base(dll_base const&) = delete; + dll_base(dll_base&&) = delete; + dll_base& operator=(dll_base const&) = delete; + dll_base& operator=(dll_base&&) = delete; T* prev() { return m_prev; } T* next() { return m_next; } + T const* prev() const { return m_prev; } + T const* next() const { return m_next; } void init(T* t) { m_next = t; m_prev = t; + SASSERT(invariant()); } static T* pop(T*& list) { @@ -41,23 +59,63 @@ class dll_base { return head; } - void insert_after(T* elem) { + void insert_after(T* other) { +#if DLIST_EXTRA_ASSERTIONS + SASSERT(other); + SASSERT(invariant()); + SASSERT(other->invariant()); + size_t const old_sz1 = count_if(*static_cast(this), [](T const&) { return true; }); + size_t const old_sz2 = count_if(*other, [](T const&) { return true; }); +#endif + // have: this -> next -> ... + // insert: other -> ... -> other_end + // result: this -> other -> ... -> other_end -> next -> ... T* next = this->m_next; - elem->m_prev = next->m_prev; - elem->m_next = next; - this->m_next = elem; - next->m_prev = elem; + T* other_end = other->m_prev; + this->m_next = other; + other->m_prev = static_cast(this); + other_end->m_next = next; + next->m_prev = other_end; +#if DLIST_EXTRA_ASSERTIONS + SASSERT(invariant()); + SASSERT(other->invariant()); + size_t const new_sz = count_if(*static_cast(this), [](T const&) { return true; }); + SASSERT_EQ(new_sz, old_sz1 + old_sz2); +#endif } - void insert_before(T* elem) { + void insert_before(T* other) { +#if DLIST_EXTRA_ASSERTIONS + SASSERT(other); + SASSERT(invariant()); + SASSERT(other->invariant()); + size_t const old_sz1 = count_if(*static_cast(this), [](T const&) { return true; }); + size_t const old_sz2 = count_if(*other, [](T const&) { return true; }); +#endif + // have: prev -> this -> ... + // insert: other -> ... -> other_end + // result: prev -> other -> ... -> other_end -> this -> ... T* prev = this->m_prev; - elem->m_next = prev->m_next; - elem->m_prev = prev; - prev->m_next = elem; - this->m_prev = elem; + T* other_end = other->m_prev; + prev->m_next = other; + other->m_prev = prev; + other_end->m_next = static_cast(this); + this->m_prev = other_end; +#if DLIST_EXTRA_ASSERTIONS + SASSERT(invariant()); + SASSERT(other->invariant()); + size_t const new_sz = count_if(*static_cast(this), [](T const&) { return true; }); + SASSERT_EQ(new_sz, old_sz1 + old_sz2); +#endif } static void remove_from(T*& list, T* elem) { +#if DLIST_EXTRA_ASSERTIONS + SASSERT(list); + SASSERT(elem); + SASSERT(list->invariant()); + SASSERT(elem->invariant()); +#endif if (list->m_next == list) { SASSERT(elem == list); list = nullptr; @@ -69,6 +127,9 @@ class dll_base { auto* prev = elem->m_prev; prev->m_next = next; next->m_prev = prev; +#if DLIST_EXTRA_ASSERTIONS + SASSERT(list->invariant()); +#endif } static void push_to_front(T*& list, T* elem) { @@ -105,11 +166,10 @@ class dll_base { return true; } - - static bool contains(T* list, T* elem) { + static bool contains(T const* list, T const* elem) { if (!list) return false; - T* first = list; + T const* first = list; do { if (list == elem) return true; @@ -120,5 +180,60 @@ class dll_base { } }; +template +class dll_iterator { + T const* m_elem; + bool m_first; + + dll_iterator(T const* elem, bool first): m_elem(elem), m_first(first) { } + +public: + static dll_iterator mk_begin(T const* elem) { + // Setting first==(bool)elem makes this also work for elem==nullptr; + // but we can't implement top-level begin/end for pointers because it clashes with the definition for arrays. + return {elem, (bool)elem}; + } + + static dll_iterator mk_end(T const* elem) { + return {elem, false}; + } + using value_type = T; + using pointer = T const*; + using reference = T const&; + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + + dll_iterator& operator++() { + m_elem = m_elem->next(); + m_first = false; + return *this; + } + + T const& operator*() const { + return *m_elem; + } + + bool operator==(dll_iterator const& other) const { + return m_elem == other.m_elem && m_first == other.m_first; + } + + bool operator!=(dll_iterator const& other) const { + return !operator==(other); + } +}; +template < typename T + , typename U = std::enable_if_t, T>> // should only match if T actually inherits from dll_base + > +dll_iterator begin(T const& elem) { + return dll_iterator::mk_begin(&elem); +} + +template < typename T + , typename U = std::enable_if_t, T>> // should only match if T actually inherits from dll_base + > +dll_iterator end(T const& elem) +{ + return dll_iterator::mk_end(&elem); +} diff --git a/src/util/map.h b/src/util/map.h index 602c042fb39..e9880e0a074 100644 --- a/src/util/map.h +++ b/src/util/map.h @@ -33,6 +33,10 @@ struct _key_data { m_key(k), m_value(v) { } + _key_data(Key const& k, Value&& v): + m_key(k), + m_value(std::move(v)) { + } }; template @@ -106,6 +110,10 @@ class table2map { void insert(key const & k, value const & v) { m_table.insert(key_data(k, v)); } + + void insert(key const& k, value&& v) { + m_table.insert(key_data(k, std::move(v))); + } bool insert_if_not_there_core(key const & k, value const & v, entry *& et) { return m_table.insert_if_not_there_core(key_data(k,v), et); diff --git a/src/util/mpq.h b/src/util/mpq.h index 31ffbeab895..e254ade697b 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -487,6 +487,8 @@ class mpq_manager : public mpz_manager { void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz & d) { mpz_manager::machine_div_rem(a, b, c, d); } + void machine_div2k(mpz const & a, unsigned k, mpz & c) { mpz_manager::machine_div2k(a, k, c); } + void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::div(a, b, c); } void rat_div(mpz const & a, mpz const & b, mpq & c) { @@ -513,6 +515,12 @@ class mpq_manager : public mpz_manager { machine_div(a.m_num, b.m_num, c); } + void machine_idiv2k(mpq const & a, unsigned k, mpq & c) { + SASSERT(is_int(a)); + machine_div2k(a.m_num, k, c.m_num); + reset_denominator(c); + } + void idiv(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); div(a.m_num, b.m_num, c.m_num); diff --git a/src/util/rational.cpp b/src/util/rational.cpp index af3c89ced3c..54b40ac58d8 100644 --- a/src/util/rational.cpp +++ b/src/util/rational.cpp @@ -153,3 +153,21 @@ bool rational::mult_inverse(unsigned num_bits, rational & result) const { return true; } +/** + * Compute the smallest multiplicative pseudo-inverse modulo 2^num_bits: + * + * mod(n * n.pseudo_inverse(bits), 2^bits) == 2^k, + * where k is maximal such that 2^k divides n. + * + * Precondition: number is non-zero. + */ +rational rational::pseudo_inverse(unsigned num_bits) const { + rational result; + rational const& n = *this; + SASSERT(!n.is_zero()); // TODO: or we define pseudo-inverse of 0 as 0. + unsigned const k = n.trailing_zeros(); + rational const odd = machine_div2k(n, k); + VERIFY(odd.mult_inverse(num_bits - k, result)); + SASSERT_EQ(mod(n * result, rational::power_of_two(num_bits)), rational::power_of_two(k)); + return result; +} diff --git a/src/util/rational.h b/src/util/rational.h index 4203a54eaf4..f47fddefefc 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -56,6 +56,8 @@ class rational { explicit rational(char const * v) { m().set(m_val, v); } + explicit rational(unsigned const * v, unsigned sz) { m().set(m_val, sz, v); } + struct i64 {}; rational(int64_t i, i64) { m().set(m_val, i); } @@ -227,6 +229,12 @@ class rational { return r; } + friend inline rational machine_div2k(rational const & r1, unsigned k) { + rational r; + rational::m().machine_idiv2k(r1.m_val, k, r.m_val); + return r; + } + friend inline rational mod(rational const & r1, rational const & r2) { rational r; rational::m().mod(r1.m_val, r2.m_val, r.m_val); @@ -353,6 +361,7 @@ class rational { } bool mult_inverse(unsigned num_bits, rational & result) const; + rational pseudo_inverse(unsigned num_bits) const; static rational const & zero() { return m_zero; diff --git a/src/util/tbv.h b/src/util/tbv.h index 2a337be1f3a..cffdc2460c4 100644 --- a/src/util/tbv.h +++ b/src/util/tbv.h @@ -27,10 +27,10 @@ Revision History: class tbv; enum tbit { - BIT_z = 0x0, - BIT_0 = 0x1, - BIT_1 = 0x2, - BIT_x = 0x3 + BIT_z = 0x0, // unknown + BIT_0 = 0x1, // for sure 0 + BIT_1 = 0x2, // for sure 1 + BIT_x = 0x3 // don't care }; inline tbit neg(tbit t) { @@ -43,6 +43,7 @@ class tbv_manager { ptr_vector allocated_tbvs; public: tbv_manager(unsigned n): m(2*n) {} + tbv_manager(tbv_manager const& m) = delete; ~tbv_manager(); void reset(); tbv* allocate(); @@ -132,8 +133,9 @@ class tbv_ref { tbv_manager& mgr; tbv* d; public: - tbv_ref(tbv_manager& mgr):mgr(mgr),d(nullptr) {} - tbv_ref(tbv_manager& mgr, tbv* d):mgr(mgr),d(d) {} + tbv_ref(tbv_manager& mgr) : mgr(mgr), d(nullptr) {} + tbv_ref(tbv_manager& mgr, tbv* d) : mgr(mgr), d(d) {} + tbv_ref(tbv_ref&& d) : mgr(d.mgr), d(d.detach()) {} ~tbv_ref() { if (d) mgr.deallocate(d); } @@ -144,8 +146,17 @@ class tbv_ref { } tbv& operator*() { return *d; } tbv* operator->() { return d; } - tbv* get() { return d; } + tbit operator[](unsigned idx) const { return (*d)[idx]; } + tbv* get() const { return d; } tbv* detach() { tbv* result = d; d = nullptr; return result; } + tbv_manager& manager() const { return mgr; } + unsigned num_tbits() const { return mgr.num_tbits(); } }; - +inline std::ostream& operator<<(std::ostream& out, tbv_ref const& c) { + char const* names[] = { "z", "0", "1", "x" }; + for (unsigned i = c.num_tbits(); i-- > 0; ) { + out << names[static_cast(c[i])]; + } + return out; +} diff --git a/src/util/util.h b/src/util/util.h index 1210314924c..6d4efb671c7 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -20,12 +20,14 @@ Revision History: #include "util/debug.h" #include "util/memory_manager.h" -#include -#include -#include -#include +#include +#include +#include +#include #include #include +#include +#include #ifndef SIZE_MAX #define SIZE_MAX std::numeric_limits::max() @@ -410,3 +412,36 @@ inline size_t megabytes_to_bytes(unsigned mb) { r = SIZE_MAX; return r; } + +/** Compact version of std::count */ +template +std::size_t count(Container const& c, Item x) +{ + using std::begin, std::end; // allows begin(c) to also find c.begin() + return std::count(begin(c), end(c), std::forward(x)); +} + +/** Compact version of std::count_if */ +template +std::size_t count_if(Container const& c, Predicate p) +{ + using std::begin, std::end; // allows begin(c) to also find c.begin() + return std::count_if(begin(c), end(c), std::forward(p)); +} + +/** Basic version of https://en.cppreference.com/w/cpp/experimental/scope_exit */ +template +class on_scope_exit final { + Callable m_ef; +public: + on_scope_exit(Callable&& ef) + : m_ef(std::forward(ef)) + { } + ~on_scope_exit() { + m_ef(); + } +}; + +/** Helper type for std::visit, see examples on https://en.cppreference.com/w/cpp/utility/variant/visit */ +template +struct always_false : std::false_type {}; diff --git a/src/util/var_queue.h b/src/util/var_queue.h index 62df777847c..7245153ca03 100644 --- a/src/util/var_queue.h +++ b/src/util/var_queue.h @@ -89,6 +89,10 @@ class var_queue { } return out; } + + using const_iterator = decltype(m_queue)::const_iterator; + const_iterator begin() const { return m_queue.begin(); } + const_iterator end() const { return m_queue.end(); } }; inline std::ostream& operator<<(std::ostream& out, var_queue const& queue) { From 3712cbdbfd61237779219c49d06fb69e4d77f852 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 4 Feb 2023 13:33:40 -0800 Subject: [PATCH 384/597] fix #6559 Signed-off-by: Nikolaj Bjorner --- src/qe/qe.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index f4ebce594ad..12365b2040e 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1513,10 +1513,11 @@ namespace qe { propagate_assignment(*model_eval); VERIFY(CHOOSE_VAR == update_current(*model_eval, true)); SASSERT(m_current->fml()); - if (l_true != m_solver.check()) { - return l_true; - } + if (l_true != m_solver.check()) + return l_true; m_solver.get_model(model); + if (!model) + return l_undef; model_eval = alloc(model_evaluator, *model); search_tree* st = m_current; update_current(*model_eval, false); From 992793bd56d0c9736ae8c407f1fb44ec52df1e1f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Feb 2023 21:34:37 -0800 Subject: [PATCH 385/597] update nuget packaging targets #6570 --- scripts/mk_nuget_task.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index dd81349dfdd..e22057a0f79 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -25,9 +25,11 @@ def mk_dir(d): 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), 'glibc-2.31' : ('so', 'linux-x64'), + 'glibc' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), - 'osx' : ('dylib', 'osx-x64'), + 'x64-osx' : ('dylib', 'osx-x64'), + 'arm64-osx' : ('dylib', 'osx-arm64'), 'debian' : ('so', 'linux-x64') } From 75c573877d0bec64e36a7cf69689915a52c1ba81 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Feb 2023 21:35:18 -0800 Subject: [PATCH 386/597] updates to ddfw, initial local search phase option --- src/sat/sat_config.cpp | 2 + src/sat/sat_config.h | 1 + src/sat/sat_ddfw.cpp | 118 ++++++++++++++++++++++++----------------- src/sat/sat_ddfw.h | 64 +++++++++++++--------- src/sat/sat_solver.cpp | 79 +++++++++++++++++---------- src/sat/sat_solver.h | 2 + src/sat/sat_types.h | 3 +- 7 files changed, 165 insertions(+), 104 deletions(-) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index eb2d0071dea..0a9e803a959 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -65,6 +65,8 @@ namespace sat { m_phase = PS_RANDOM; else if (s == symbol("frozen")) m_phase = PS_FROZEN; + else if (s == symbol("local_search")) + m_phase = PS_LOCAL_SEARCH; else throw sat_param_exception("invalid phase selection strategy: always_false, always_true, basic_caching, caching, random"); diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 8adfc13edb6..f8c0775b110 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -28,6 +28,7 @@ namespace sat { PS_ALWAYS_FALSE, PS_BASIC_CACHING, PS_SAT_CACHING, + PS_LOCAL_SEARCH, PS_FROZEN, PS_RANDOM }; diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index ecfc13aa69b..f1493232c80 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -49,6 +49,7 @@ namespace sat { else if (should_parallel_sync()) do_parallel_sync(); else shift_weights(); } + log(); return m_min_sz == 0 ? l_true : l_undef; } @@ -66,9 +67,9 @@ namespace sat { << std::setw(10) << kflips_per_sec << std::setw(10) << m_flips << std::setw(10) << m_restart_count - << std::setw(10) << m_reinit_count - << std::setw(10) << m_unsat_vars.size() - << std::setw(10) << m_shifts; + << std::setw(11) << m_reinit_count + << std::setw(13) << m_unsat_vars.size() + << std::setw(9) << m_shifts; if (m_par) verbose_stream() << std::setw(10) << m_parsync_count; verbose_stream() << ")\n"); m_stopwatch.start(); @@ -90,18 +91,18 @@ namespace sat { unsigned n = 1; bool_var v0 = null_bool_var; for (bool_var v : m_unsat_vars) { - int r = reward(v); - if (r > 0) { + double r = reward(v); + if (r > 0.0) { sum_pos += score(r); } - else if (r == 0 && sum_pos == 0 && (m_rand() % (n++)) == 0) { + else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0) { v0 = v; } } if (sum_pos > 0) { double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos; for (bool_var v : m_unsat_vars) { - int r = reward(v); + double r = reward(v); if (r > 0) { lim_pos -= score(r); if (lim_pos <= 0) { @@ -121,7 +122,7 @@ namespace sat { * TBD: map reward value to a score, possibly through an exponential function, such as * exp(-tau/r), where tau > 0 */ - double ddfw::mk_score(unsigned r) { + double ddfw::mk_score(double r) { return r; } @@ -201,7 +202,7 @@ namespace sat { m_shifts = 0; m_stopwatch.start(); } - + void ddfw::reinit(solver& s) { add(s); add_assumptions(); @@ -235,7 +236,7 @@ namespace sat { for (unsigned cls_idx : use_list(*this, lit)) { clause_info& ci = m_clauses[cls_idx]; ci.del(lit); - unsigned w = ci.m_weight; + double w = ci.m_weight; // cls becomes false: flip any variable in clause to receive reward w switch (ci.m_num_trues) { case 0: { @@ -257,7 +258,7 @@ namespace sat { } for (unsigned cls_idx : use_list(*this, nlit)) { clause_info& ci = m_clauses[cls_idx]; - unsigned w = ci.m_weight; + double w = ci.m_weight; // the clause used to have a single true (pivot) literal, now it has two. // Then the previous pivot is no longer penalized for flipping. switch (ci.m_num_trues) { @@ -406,9 +407,8 @@ namespace sat { void ddfw::save_best_values() { if (m_unsat.empty()) { m_model.reserve(num_vars()); - for (unsigned i = 0; i < num_vars(); ++i) { + for (unsigned i = 0; i < num_vars(); ++i) m_model[i] = to_lbool(value(i)); - } } if (m_unsat.size() < m_min_sz) { m_models.reset(); @@ -422,13 +422,11 @@ namespace sat { } unsigned h = value_hash(); if (!m_models.contains(h)) { - for (unsigned v = 0; v < num_vars(); ++v) { + for (unsigned v = 0; v < num_vars(); ++v) bias(v) += value(v) ? 1 : -1; - } m_models.insert(h); - if (m_models.size() > m_config.m_max_num_models) { + if (m_models.size() > m_config.m_max_num_models) m_models.erase(*m_models.begin()); - } } m_min_sz = m_unsat.size(); } @@ -450,10 +448,9 @@ namespace sat { 3. select multiple clauses instead of just one per clause in unsat. */ - bool ddfw::select_clause(unsigned max_weight, unsigned max_trues, clause_info const& cn, unsigned& n) { - if (cn.m_num_trues == 0 || cn.m_weight < max_weight) { + bool ddfw::select_clause(double max_weight, clause_info const& cn, unsigned& n) { + if (cn.m_num_trues == 0 || cn.m_weight + 1e-5 < max_weight) return false; - } if (cn.m_weight > max_weight) { n = 2; return true; @@ -462,51 +459,72 @@ namespace sat { } unsigned ddfw::select_max_same_sign(unsigned cf_idx) { - clause const& c = get_clause(cf_idx); - unsigned max_weight = 2; - unsigned max_trues = 0; + auto& ci = m_clauses[cf_idx]; unsigned cl = UINT_MAX; // clause pointer to same sign, max weight satisfied clause. + clause const& c = *ci.m_clause; + double max_weight = m_init_weight; unsigned n = 1; for (literal lit : c) { for (unsigned cn_idx : use_list(*this, lit)) { auto& cn = m_clauses[cn_idx]; - if (select_clause(max_weight, max_trues, cn, n)) { + if (select_clause(max_weight, cn, n)) { cl = cn_idx; max_weight = cn.m_weight; - max_trues = cn.m_num_trues; } } } return cl; } + void ddfw::transfer_weight(unsigned from, unsigned to, double w) { + auto& cf = m_clauses[to]; + auto& cn = m_clauses[from]; + if (cn.m_weight < w) + return; + cf.m_weight += w; + cn.m_weight -= w; + + for (literal lit : get_clause(to)) + inc_reward(lit, w); + if (cn.m_num_trues == 1) + inc_reward(to_literal(cn.m_trues), w); + } + + unsigned ddfw::select_random_true_clause() { + unsigned num_clauses = m_clauses.size(); + unsigned rounds = 100 * num_clauses; + for (unsigned i = 0; i < rounds; ++i) { + unsigned idx = (m_rand() * m_rand()) % num_clauses; + auto & cn = m_clauses[idx]; + if (cn.is_true() && cn.m_weight >= m_init_weight) + return idx; + } + return UINT_MAX; + } + + // 1% chance to disregard neighbor + inline bool ddfw::disregard_neighbor() { + return false; // rand() % 1000 == 0; + } + + double ddfw::calculate_transfer_weight(double w) { + return (w > m_init_weight) ? m_init_weight : 1; + } + void ddfw::shift_weights() { ++m_shifts; - for (unsigned cf_idx : m_unsat) { - auto& cf = m_clauses[cf_idx]; + for (unsigned to_idx : m_unsat) { + auto& cf = m_clauses[to_idx]; SASSERT(!cf.is_true()); - unsigned cn_idx = select_max_same_sign(cf_idx); - while (cn_idx == UINT_MAX) { - unsigned idx = (m_rand() * m_rand()) % m_clauses.size(); - auto & cn = m_clauses[idx]; - if (cn.is_true() && cn.m_weight >= 2) { - cn_idx = idx; - } - } - auto & cn = m_clauses[cn_idx]; + unsigned from_idx = select_max_same_sign(to_idx); + if (from_idx == UINT_MAX || disregard_neighbor()) + from_idx = select_random_true_clause(); + if (from_idx == UINT_MAX) + continue; + auto & cn = m_clauses[from_idx]; SASSERT(cn.is_true()); - unsigned wn = cn.m_weight; - SASSERT(wn >= 2); - unsigned inc = (wn > 2) ? 2 : 1; - SASSERT(wn - inc >= 1); - cf.m_weight += inc; - cn.m_weight -= inc; - for (literal lit : get_clause(cf_idx)) { - inc_reward(lit, inc); - } - if (cn.m_num_trues == 1) { - inc_reward(to_literal(cn.m_trues), inc); - } + double w = calculate_transfer_weight(cn.m_weight); + transfer_weight(from_idx, to_idx, w); } // DEBUG_CODE(invariant();); } @@ -543,7 +561,7 @@ namespace sat { VERIFY(found); } for (unsigned v = 0; v < num_vars(); ++v) { - int v_reward = 0; + double v_reward = 0; literal lit(v, !value(v)); for (unsigned j : m_use_list[lit.index()]) { clause_info const& ci = m_clauses[j]; @@ -559,7 +577,7 @@ namespace sat { } } IF_VERBOSE(0, if (v_reward != reward(v)) verbose_stream() << v << " " << v_reward << " " << reward(v) << "\n"); - SASSERT(reward(v) == v_reward); + // SASSERT(reward(v) == v_reward); } DEBUG_CODE( for (auto const& ci : m_clauses) { diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 1cad8736336..1d28a82c407 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -34,10 +34,10 @@ namespace sat { class ddfw : public i_local_search { struct clause_info { - clause_info(clause* cl, unsigned init_weight): m_weight(init_weight), m_trues(0), m_num_trues(0), m_clause(cl) {} - unsigned m_weight; // weight of clause - unsigned m_trues; // set of literals that are true - unsigned m_num_trues; // size of true set + clause_info(clause* cl, double init_weight): m_weight(init_weight), m_clause(cl) {} + double m_weight; // weight of clause + unsigned m_trues = 0; // set of literals that are true + unsigned m_num_trues = 0; // size of true set clause* m_clause; bool is_true() const { return m_num_trues > 0; } void add(literal lit) { ++m_num_trues; m_trues += lit.index(); } @@ -65,23 +65,24 @@ namespace sat { }; struct var_info { - var_info(): m_value(false), m_reward(0), m_make_count(0), m_bias(0), m_reward_avg(1e-5) {} - bool m_value; - int m_reward; - unsigned m_make_count; - int m_bias; - ema m_reward_avg; + var_info() {} + bool m_value = false; + double m_reward = 0; + unsigned m_make_count = 0; + int m_bias = 0; + ema m_reward_avg = 1e-5; }; - config m_config; - reslimit m_limit; - clause_allocator m_alloc; + config m_config; + reslimit m_limit; + clause_allocator m_alloc; svector m_clauses; literal_vector m_assumptions; svector m_vars; // var -> info svector m_probs; // var -> probability of flipping svector m_scores; // reward -> score model m_model; // var -> best assignment + unsigned m_init_weight = 2; vector m_use_list; unsigned_vector m_flat_use_list; @@ -90,11 +91,11 @@ namespace sat { indexed_uint_set m_unsat; indexed_uint_set m_unsat_vars; // set of variables that are in unsat clauses random_gen m_rand; - unsigned m_num_non_binary_clauses{ 0 }; - unsigned m_restart_count{ 0 }, m_reinit_count{ 0 }, m_parsync_count{ 0 }; - uint64_t m_restart_next{ 0 }, m_reinit_next{ 0 }, m_parsync_next{ 0 }; - uint64_t m_flips{ 0 }, m_last_flips{ 0 }, m_shifts{ 0 }; - unsigned m_min_sz{ 0 }; + unsigned m_num_non_binary_clauses = 0; + unsigned m_restart_count = 0, m_reinit_count = 0, m_parsync_count = 0; + uint64_t m_restart_next = 0, m_reinit_next = 0, m_parsync_next = 0; + uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0; + unsigned m_min_sz = 0; hashtable> m_models; stopwatch m_stopwatch; @@ -112,9 +113,9 @@ namespace sat { void flatten_use_list(); - double mk_score(unsigned r); + double mk_score(double r); - inline double score(unsigned r) { return r; } // TBD: { for (unsigned sz = m_scores.size(); sz <= r; ++sz) m_scores.push_back(mk_score(sz)); return m_scores[r]; } + inline double score(double r) { return r; } // TBD: { for (unsigned sz = m_scores.size(); sz <= r; ++sz) m_scores.push_back(mk_score(sz)); return m_scores[r]; } inline unsigned num_vars() const { return m_vars.size(); } @@ -124,9 +125,9 @@ namespace sat { inline bool value(bool_var v) const { return m_vars[v].m_value; } - inline int& reward(bool_var v) { return m_vars[v].m_reward; } + inline double& reward(bool_var v) { return m_vars[v].m_reward; } - inline int reward(bool_var v) const { return m_vars[v].m_reward; } + inline double reward(bool_var v) const { return m_vars[v].m_reward; } inline int& bias(bool_var v) { return m_vars[v].m_bias; } @@ -136,7 +137,7 @@ namespace sat { inline clause const& get_clause(unsigned idx) const { return *m_clauses[idx].m_clause; } - inline unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } + inline double get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } inline bool is_true(unsigned idx) const { return m_clauses[idx].is_true(); } @@ -154,9 +155,9 @@ namespace sat { if (--make_count(v) == 0) m_unsat_vars.remove(v); } - inline void inc_reward(literal lit, int inc) { reward(lit.var()) += inc; } + inline void inc_reward(literal lit, double w) { reward(lit.var()) += w; } - inline void dec_reward(literal lit, int inc) { reward(lit.var()) -= inc; } + inline void dec_reward(literal lit, double w) { reward(lit.var()) -= w; } // flip activity bool do_flip(); @@ -166,17 +167,20 @@ namespace sat { // shift activity void shift_weights(); + inline double calculate_transfer_weight(double w); // reinitialize weights activity bool should_reinit_weights(); void do_reinit_weights(); - inline bool select_clause(unsigned max_weight, unsigned max_trues, clause_info const& cn, unsigned& n); + inline bool select_clause(double max_weight, clause_info const& cn, unsigned& n); // restart activity bool should_restart(); void do_restart(); void reinit_values(); + unsigned select_random_true_clause(); + // parallel integration bool should_parallel_sync(); void do_parallel_sync(); @@ -193,6 +197,10 @@ namespace sat { void add_assumptions(); + inline void transfer_weight(unsigned from, unsigned to, double w); + + inline bool disregard_neighbor(); + public: ddfw(): m_par(nullptr) {} @@ -210,6 +218,10 @@ namespace sat { void set_seed(unsigned n) override { m_rand.set_seed(n); } void add(solver const& s) override; + + void set_bias(bool_var v, int bias) override { m_vars[v].m_bias = bias; } + + bool get_value(bool_var v) const override { return value(v); } std::ostream& display(std::ostream& out) const; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 7cfa102b7b3..61fc816e1ac 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1330,17 +1330,37 @@ namespace sat { ERROR_EX }; + struct solver::scoped_ls { + solver& s; + scoped_ls(solver& s): s(s) {} + ~scoped_ls() { + dealloc(s.m_local_search); + s.m_local_search = nullptr; + } + }; + + void solver::bounded_local_search() { + literal_vector _lits; + scoped_limits scoped_rl(rlimit()); + m_local_search = alloc(ddfw); + scoped_ls _ls(*this); + SASSERT(m_local_search); + m_local_search->add(*this); + m_local_search->updt_params(m_params); + m_local_search->set_seed(m_rand()); + scoped_rl.push_child(&(m_local_search->rlimit())); + m_local_search->rlimit().push(500000); + m_local_search->reinit(*this); + m_local_search->check(_lits.size(), _lits.data(), nullptr); + for (unsigned i = 0; i < m_phase.size(); ++i) + m_best_phase[i] = m_local_search->get_value(i); + } + + lbool solver::invoke_local_search(unsigned num_lits, literal const* lits) { literal_vector _lits(num_lits, lits); - for (literal lit : m_user_scope_literals) _lits.push_back(~lit); - struct scoped_ls { - solver& s; - scoped_ls(solver& s): s(s) {} - ~scoped_ls() { - dealloc(s.m_local_search); - s.m_local_search = nullptr; - } - }; + for (literal lit : m_user_scope_literals) + _lits.push_back(~lit); scoped_ls _ls(*this); if (inconsistent()) return l_false; @@ -1610,27 +1630,28 @@ namespace sat { bool solver::guess(bool_var next) { lbool lphase = m_ext ? m_ext->get_phase(next) : l_undef; - + if (lphase != l_undef) return lphase == l_true; switch (m_config.m_phase) { - case PS_ALWAYS_TRUE: - return true; - case PS_ALWAYS_FALSE: - return false; - case PS_BASIC_CACHING: + case PS_ALWAYS_TRUE: + return true; + case PS_ALWAYS_FALSE: + return false; + case PS_BASIC_CACHING: + return m_phase[next]; + case PS_FROZEN: + return m_best_phase[next]; + case PS_SAT_CACHING: + case PS_LOCAL_SEARCH: + if (m_search_state == s_unsat) return m_phase[next]; - case PS_FROZEN: - return m_best_phase[next]; - case PS_SAT_CACHING: - if (m_search_state == s_unsat) - return m_phase[next]; - return m_best_phase[next]; - case PS_RANDOM: - return (m_rand() % 2) == 0; - default: - UNREACHABLE(); - return false; + return m_best_phase[next]; + case PS_RANDOM: + return (m_rand() % 2) == 0; + default: + UNREACHABLE(); + return false; } } @@ -2822,7 +2843,7 @@ namespace sat { } bool solver::is_two_phase() const { - return m_config.m_phase == PS_SAT_CACHING; + return m_config.m_phase == PS_SAT_CACHING || m_config.m_phase == PS_LOCAL_SEARCH; } bool solver::is_sat_phase() const { @@ -2922,6 +2943,10 @@ namespace sat { case PS_RANDOM: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; + case PS_LOCAL_SEARCH: + if (m_search_state == s_sat) + bounded_local_search(); + break; default: UNREACHABLE(); break; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 227568f3d86..524f6b06de3 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -589,7 +589,9 @@ namespace sat { lbool do_ddfw_search(unsigned num_lits, literal const* lits); lbool do_prob_search(unsigned num_lits, literal const* lits); lbool invoke_local_search(unsigned num_lits, literal const* lits); + void bounded_local_search(); lbool do_unit_walk(); + struct scoped_ls; // ----------------------- // diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 4e119a2ae1a..626a3e6061a 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -91,7 +91,8 @@ namespace sat { virtual model const& get_model() const = 0; virtual void collect_statistics(statistics& st) const = 0; virtual double get_priority(bool_var v) const { return 0; } - + virtual void set_bias(bool_var v, int bias) {} + virtual bool get_value(bool_var v) const { return true; } }; class proof_hint { From 03a4920f3df09ed6ee04dd5f13777fde74ee3a89 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Feb 2023 21:41:07 -0800 Subject: [PATCH 387/597] fix build Signed-off-by: Nikolaj Bjorner --- src/sat/sat_ddfw.h | 2 -- src/sat/sat_types.h | 1 - 2 files changed, 3 deletions(-) diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 1d28a82c407..ed9936f0ac9 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -219,8 +219,6 @@ namespace sat { void add(solver const& s) override; - void set_bias(bool_var v, int bias) override { m_vars[v].m_bias = bias; } - bool get_value(bool_var v) const override { return value(v); } std::ostream& display(std::ostream& out) const; diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 626a3e6061a..c92a8bbeb4f 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -91,7 +91,6 @@ namespace sat { virtual model const& get_model() const = 0; virtual void collect_statistics(statistics& st) const = 0; virtual double get_priority(bool_var v) const { return 0; } - virtual void set_bias(bool_var v, int bias) {} virtual bool get_value(bool_var v) const { return true; } }; From c1c26f07269539707b3dda264f8bac1843ff8fc2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 09:21:35 -0800 Subject: [PATCH 388/597] restart after sat solution Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 61fc816e1ac..29710ed29ed 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1351,9 +1351,15 @@ namespace sat { scoped_rl.push_child(&(m_local_search->rlimit())); m_local_search->rlimit().push(500000); m_local_search->reinit(*this); - m_local_search->check(_lits.size(), _lits.data(), nullptr); + lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); for (unsigned i = 0; i < m_phase.size(); ++i) m_best_phase[i] = m_local_search->get_value(i); + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + do_restart(true); + } } From a7231027c369ff8d7b4a7aee10b70eb8ff96f956 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 16:04:54 -0800 Subject: [PATCH 389/597] try side-by-side nightly --- scripts/mk_nuget_task.py | 2 +- scripts/nightly.yaml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index e22057a0f79..b48f3775935 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -25,7 +25,7 @@ def mk_dir(d): 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), 'glibc-2.31' : ('so', 'linux-x64'), - 'glibc' : ('so', 'linux-x64'), + 'glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 4925ce45dfd..dc7f923bae1 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -35,6 +35,20 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) + - job: Ubuntu + displayName: "Ubuntu build" + pool: + vmImage: "ubuntu-20.04" + steps: + - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - script: git clone https://github.com/z3prover/z3test z3test + - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 + - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'Ubuntu-20.04' + targetPath: $(Build.ArtifactStagingDirectory) + - job: Ubuntu displayName: "Ubuntu build" pool: @@ -512,6 +526,11 @@ stages: inputs: artifactName: 'Ubuntu' targetPath: tmp + - task: DownloadPipelineArtifact@2 + displayName: "Download Ubuntu-20.04" + inputs: + artifactName: 'Ubuntu-20.04' + targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Doc" inputs: From c1cadd37cc1284e22b15c50d24a76206bd0ea77e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 16:07:12 -0800 Subject: [PATCH 390/597] update stage name --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index dc7f923bae1..d8ff50b9274 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -35,8 +35,8 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) - - job: Ubuntu - displayName: "Ubuntu build" + - job: Ubuntu-20 + displayName: "Ubuntu-20 build" pool: vmImage: "ubuntu-20.04" steps: From f3ae7692ca6bfe40f677245e8b98d127b709991d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 16:08:14 -0800 Subject: [PATCH 391/597] update stage name --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index d8ff50b9274..4aeb44b352d 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -35,8 +35,8 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) - - job: Ubuntu-20 - displayName: "Ubuntu-20 build" + - job: Ubuntu20 + displayName: "Ubuntu20 build" pool: vmImage: "ubuntu-20.04" steps: From 90a75866fbe1eb66813444ea7c7d0d9a26046fb4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 03:17:52 -0800 Subject: [PATCH 392/597] elaborating on local-search rephase strategy --- src/sat/sat_ddfw.cpp | 52 +++++++++++++------------- src/sat/sat_ddfw.h | 2 + src/sat/sat_parallel.cpp | 20 ++++++---- src/sat/sat_parallel.h | 4 +- src/sat/sat_params.pyg | 2 +- src/sat/sat_prob.h | 10 +++-- src/sat/sat_solver.cpp | 80 +++++++++++++++++++++++++++++++++------- src/sat/sat_solver.h | 15 ++++++++ src/sat/sat_types.h | 2 +- 9 files changed, 131 insertions(+), 56 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index f1493232c80..418070c64c9 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -57,11 +57,11 @@ namespace sat { double sec = m_stopwatch.get_current_seconds(); double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec); if (m_last_flips == 0) { - IF_VERBOSE(0, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts"; + IF_VERBOSE(1, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts"; if (m_par) verbose_stream() << " :par"; verbose_stream() << ")\n"); } - IF_VERBOSE(0, verbose_stream() << "(sat.ddfw " + IF_VERBOSE(1, verbose_stream() << "(sat.ddfw " << std::setw(07) << m_min_sz << std::setw(07) << m_models.size() << std::setw(10) << kflips_per_sec @@ -106,7 +106,6 @@ namespace sat { if (r > 0) { lim_pos -= score(r); if (lim_pos <= 0) { - if (m_par) update_reward_avg(v); return v; } } @@ -139,9 +138,8 @@ namespace sat { } void ddfw::add(solver const& s) { - for (auto& ci : m_clauses) { + for (auto& ci : m_clauses) m_alloc.del_clause(ci.m_clause); - } m_clauses.reset(); m_use_list.reset(); m_num_non_binary_clauses = 0; @@ -281,6 +279,7 @@ namespace sat { ci.add(nlit); } value(v) = !value(v); + update_reward_avg(v); } bool ddfw::should_reinit_weights() { @@ -379,36 +378,35 @@ namespace sat { return m_par != nullptr && m_flips >= m_parsync_next; } + void ddfw::save_priorities() { + m_probs.reset(); + for (unsigned v = 0; v < num_vars(); ++v) + m_probs.push_back(-m_vars[v].m_reward_avg); + } + void ddfw::do_parallel_sync() { - if (m_par->from_solver(*this)) { - // Sum exp(xi) / exp(a) = Sum exp(xi - a) - double max_avg = 0; - for (unsigned v = 0; v < num_vars(); ++v) { - max_avg = std::max(max_avg, (double)m_vars[v].m_reward_avg); - } - double sum = 0; - for (unsigned v = 0; v < num_vars(); ++v) { - sum += exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)); - } - if (sum == 0) { - sum = 0.01; - } - m_probs.reset(); - for (unsigned v = 0; v < num_vars(); ++v) { - m_probs.push_back(exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)) / sum); - } + if (m_par->from_solver(*this)) m_par->to_solver(*this); - } + ++m_parsync_count; m_parsync_next *= 3; m_parsync_next /= 2; } + void ddfw::save_model() { + m_model.reserve(num_vars()); + for (unsigned i = 0; i < num_vars(); ++i) + m_model[i] = to_lbool(value(i)); + save_priorities(); + } + + void ddfw::save_best_values() { - if (m_unsat.empty()) { - m_model.reserve(num_vars()); - for (unsigned i = 0; i < num_vars(); ++i) - m_model[i] = to_lbool(value(i)); + if (m_unsat.empty()) + save_model(); + else if (m_unsat.size() < m_min_sz) { + if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11) + save_model(); } if (m_unsat.size() < m_min_sz) { m_models.reset(); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ed9936f0ac9..d5e7df77374 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -164,6 +164,8 @@ namespace sat { bool_var pick_var(); void flip(bool_var v); void save_best_values(); + void save_model(); + void save_priorities(); // shift activity void shift_weights(); diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index 2f7a195588b..3e493168a0c 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -214,14 +214,17 @@ namespace sat { } - bool parallel::_to_solver(solver& s) { - if (m_priorities.empty()) { - return false; - } + void parallel::_to_solver(solver& s) { + return; +#if 0 + if (m_priorities.empty()) + return; + for (bool_var v = 0; v < m_priorities.size(); ++v) { s.update_activity(v, m_priorities[v]); } - return true; + s.m_activity_inc = 128; +#endif } void parallel::from_solver(solver& s) { @@ -229,16 +232,19 @@ namespace sat { _from_solver(s); } - bool parallel::to_solver(solver& s) { + void parallel::to_solver(solver& s) { lock_guard lock(m_mux); - return _to_solver(s); + _to_solver(s); } void parallel::_to_solver(i_local_search& s) { + return; +#if 0 m_priorities.reset(); for (bool_var v = 0; m_solver_copy && v < m_solver_copy->num_vars(); ++v) { m_priorities.push_back(s.get_priority(v)); } +#endif } bool parallel::_from_solver(i_local_search& s) { diff --git a/src/sat/sat_parallel.h b/src/sat/sat_parallel.h index 68266760ba0..65ae091835e 100644 --- a/src/sat/sat_parallel.h +++ b/src/sat/sat_parallel.h @@ -51,7 +51,7 @@ namespace sat { bool enable_add(clause const& c) const; void _get_clauses(solver& s); void _from_solver(solver& s); - bool _to_solver(solver& s); + void _to_solver(solver& s); bool _from_solver(i_local_search& s); void _to_solver(i_local_search& s); @@ -102,7 +102,7 @@ namespace sat { // exchange from solver state to local search and back. void from_solver(solver& s); - bool to_solver(solver& s); + void to_solver(solver& s); bool from_solver(i_local_search& s); void to_solver(i_local_search& s); diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index f9d7c643ac6..6aedf1c89e9 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -2,7 +2,7 @@ def_module_params('sat', export=True, description='propositional SAT solver', params=(max_memory_param(), - ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching'), + ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching, local_search'), ('phase.sticky', BOOL, True, 'use sticky phase caching'), ('search.unsat.conflicts', UINT, 400, 'period for solving for unsat (in number of conflicts)'), ('search.sat.conflicts', UINT, 400, 'period for solving for sat (in number of conflicts)'), diff --git a/src/sat/sat_prob.h b/src/sat/sat_prob.h index 305e76b8bcc..f05365e3902 100644 --- a/src/sat/sat_prob.h +++ b/src/sat/sat_prob.h @@ -58,7 +58,7 @@ namespace sat { clause_vector m_clause_db; svector m_clauses; bool_vector m_values, m_best_values; - unsigned m_best_min_unsat{ 0 }; + unsigned m_best_min_unsat = 0; vector m_use_list; unsigned_vector m_flat_use_list; unsigned_vector m_use_list_index; @@ -67,9 +67,9 @@ namespace sat { indexed_uint_set m_unsat; random_gen m_rand; unsigned_vector m_breaks; - uint64_t m_flips{ 0 }; - uint64_t m_next_restart{ 0 }; - unsigned m_restart_count{ 0 }; + uint64_t m_flips = 0; + uint64_t m_next_restart = 0; + unsigned m_restart_count = 0; stopwatch m_stopwatch; model m_model; @@ -139,6 +139,8 @@ namespace sat { void add(solver const& s) override; model const& get_model() const override { return m_model; } + + double get_priority(bool_var v) const { return 0; } std::ostream& display(std::ostream& out) const; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 29710ed29ed..373885f341b 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -40,6 +40,26 @@ Revision History: namespace sat { + /** + * Special cases of kissat style general backoff calculation. + * The version here calculates + * limit := value*log(C)^2*n*log(n) + * (effort calculation in kissat is based on ticks not clauses) + * + * respectively + * limit := conflicts + value*log(C)^2*n*log(n) + */ + void backoff::delta_effort(solver& s) { + count++; + unsigned d = value * count * log2(count + 1); + unsigned cl = log2(s.num_clauses() + 2); + limit = cl * cl * d; + } + + void backoff::delta_conflicts(solver& s) { + delta_effort(s); + limit += s.m_conflicts_since_init; + } solver::solver(params_ref const & p, reslimit& l): solver_core(l), @@ -1349,16 +1369,43 @@ namespace sat { m_local_search->updt_params(m_params); m_local_search->set_seed(m_rand()); scoped_rl.push_child(&(m_local_search->rlimit())); - m_local_search->rlimit().push(500000); + + m_backoffs.m_local_search.delta_effort(*this); + m_local_search->rlimit().push(m_backoffs.m_local_search.limit); + m_local_search->reinit(*this); lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); - for (unsigned i = 0; i < m_phase.size(); ++i) - m_best_phase[i] = m_local_search->get_value(i); - if (r == l_true) { - m_conflicts_since_restart = 0; - m_conflicts_since_gc = 0; - m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + auto const& mdl = m_local_search->get_model(); + if (mdl.size() == m_best_phase.size()) { + for (unsigned i = 0; i < m_best_phase.size(); ++i) + m_best_phase[i] = l_true == mdl[i]; + + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + } do_restart(true); +#if 0 + // move higher priority variables to front + // eg., move the first 10% variables to front + svector> priorities(mdl.size()); + for (unsigned i = 0; i < mdl.size(); ++i) + priorities[i] = { m_local_search->get_priority(i), i }; + std::sort(priorities.begin(), priorities.end(), [](auto& x, auto& y) { return x.first > y.first; }); + for (unsigned i = priorities.size() / 10; i-- > 0; ) + move_to_front(priorities[i].second); +#endif + + + if (l_true == r) { + for (clause const* cp : m_clauses) { + bool is_true = any_of(*cp, [&](auto lit) { return lit.sign() != m_best_phase[lit.var()]; }); + if (!is_true) { + verbose_stream() << "clause is false " << *cp << "\n"; + } + } + } } } @@ -1693,7 +1740,7 @@ namespace sat { if (!is_pos) next_lit.neg(); - + TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";); assign_scoped(next_lit); return true; @@ -1913,6 +1960,7 @@ namespace sat { m_rephase_lim = 0; m_rephase_inc = 0; m_reorder_lim = m_config.m_reorder_base; + m_backoffs.m_local_search.value = 500; m_reorder_inc = 0; m_conflicts_since_restart = 0; m_force_conflict_analysis = false; @@ -1928,6 +1976,7 @@ namespace sat { m_next_simplify = m_config.m_simplify_delay; m_min_d_tk = 1.0; m_search_lvl = 0; + if (m_learned.size() <= 2*m_clauses.size()) m_conflicts_since_gc = 0; m_restart_next_out = 0; @@ -2030,9 +2079,7 @@ namespace sat { if (m_par) { m_par->from_solver(*this); - if (m_par->to_solver(*this)) { - m_activity_inc = 128; - } + m_par->to_solver(*this); } if (m_config.m_binspr && !inconsistent()) { @@ -2944,14 +2991,19 @@ namespace sat { case PS_SAT_CACHING: if (m_search_state == s_sat) for (unsigned i = 0; i < m_phase.size(); ++i) - m_phase[i] = m_best_phase[i]; + m_phase[i] = m_best_phase[i]; break; case PS_RANDOM: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; case PS_LOCAL_SEARCH: - if (m_search_state == s_sat) - bounded_local_search(); + if (m_search_state == s_sat) { + if (m_rand() % 2 == 0) + bounded_local_search(); + for (unsigned i = 0; i < m_phase.size(); ++i) + m_phase[i] = m_best_phase[i]; + } + break; default: UNREACHABLE(); diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 524f6b06de3..ca738ce9b14 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -87,10 +87,23 @@ namespace sat { struct no_drat_params : public params_ref { no_drat_params() { set_bool("drat.disable", true); } }; + + struct backoff { + unsigned value = 1; + unsigned lo = 0; + unsigned hi = 0; + unsigned limit = 0; + unsigned count = 0; + void delta_effort(solver& s); + void delta_conflicts(solver& s); + }; class solver : public solver_core { public: struct abort_solver {}; + struct backoffs { + backoff m_local_search; + }; protected: enum search_state { s_sat, s_unsat }; @@ -159,6 +172,7 @@ namespace sat { unsigned m_search_next_toggle; unsigned m_phase_counter; unsigned m_best_phase_size; + backoffs m_backoffs; unsigned m_rephase_lim; unsigned m_rephase_inc; unsigned m_reorder_lim; @@ -237,6 +251,7 @@ namespace sat { friend class lut_finder; friend class npn3_finder; friend class proof_trim; + friend struct backoff; public: solver(params_ref const & p, reslimit& l); ~solver() override; diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index c92a8bbeb4f..3026b3c5e00 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -90,7 +90,7 @@ namespace sat { virtual reslimit& rlimit() = 0; virtual model const& get_model() const = 0; virtual void collect_statistics(statistics& st) const = 0; - virtual double get_priority(bool_var v) const { return 0; } + virtual double get_priority(bool_var v) const = 0; virtual bool get_value(bool_var v) const { return true; } }; From a150e58893b242b093d627b32f1aacd500fc9945 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 09:21:02 -0800 Subject: [PATCH 393/597] update release script Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 4 ++-- scripts/release.yml | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index b48f3775935..073b6b99a74 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -24,8 +24,8 @@ def mk_dir(d): os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), - 'glibc-2.31' : ('so', 'linux-x64'), - 'glibc-2.35' : ('so', 'linux-x64'), + 'glibc' : ('so', 'linux-x64'), + #'glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), diff --git a/scripts/release.yml b/scripts/release.yml index 10ae2457741..7d3ec1085a0 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -85,6 +85,35 @@ stages: artifactName: 'UbuntuBuild' targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuBuild20 + displayName: "Ubuntu build 20" + pool: + vmImage: "ubuntu-20.04" + steps: + - task: PythonScript@0 + displayName: Build + inputs: + scriptSource: 'filepath' + scriptPath: scripts/mk_unix_dist.py + arguments: --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - script: git clone https://github.com/z3prover/z3test z3test + displayName: 'Clone z3test' + - task: PythonScript@0 + displayName: Test + inputs: + scriptSource: 'filepath' + scriptPath: z3test/scripts/test_benchmarks.py + arguments: build-dist/z3 z3test/regressions/smt2 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'UbuntuBuild20' + targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuDoc displayName: "Ubuntu Doc build" pool: @@ -191,6 +220,11 @@ stages: inputs: artifact: 'UbuntuBuild' path: $(Agent.TempDirectory)\package + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu20 Build' + inputs: + artifact: 'UbuntuBuild20' + path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download macOS Build' inputs: @@ -436,6 +470,11 @@ stages: pool: vmImage: "windows-latest" steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu Build' + inputs: + artifact: 'UbuntuBuild20' + path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 displayName: 'Download Ubuntu Build' inputs: From 601e506d54874fe9216ca9881134d517ea34a83d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 10:40:49 -0800 Subject: [PATCH 394/597] remove debug out --- src/muz/base/dl_util.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 623f287f795..401d8e81670 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -324,17 +324,14 @@ namespace datalog { if (cycle_len < 2) return; auto aux = container[permutation_cycle[0]]; - verbose_stream() << "xx " << cycle_len << "\n"; for (unsigned i = 1; i < cycle_len; i++) container[permutation_cycle[i-1]] = container[permutation_cycle[i]]; container[permutation_cycle[cycle_len-1]] = aux; } template void permute_by_cycle(ref_vector & container, unsigned cycle_len, const unsigned * permutation_cycle) { - if (cycle_len<2) { + if (cycle_len < 2) return; - } - verbose_stream() << "ptr\n"; T * aux = container.get(permutation_cycle[0]); for (unsigned i=1; i < cycle_len; i++) { container.set(permutation_cycle[i-1], container.get(permutation_cycle[i])); From 6a2d60a6ba2c8b7012cd3afd32366991d2eb08c3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 11:04:58 -0800 Subject: [PATCH 395/597] fix #6571 most solvers don't support background properties --- src/muz/base/dl_context.cpp | 5 +++++ src/muz/base/rule_properties.cpp | 6 ++++++ src/muz/base/rule_properties.h | 1 + 3 files changed, 12 insertions(+) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index c9d2c779745..230a452df2b 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -577,6 +577,7 @@ namespace datalog { m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_nested_free(); m_rule_properties.check_infinite_sorts(); + m_rule_properties.check_background_free(); break; case SPACER_ENGINE: m_rule_properties.collect(r); @@ -584,6 +585,7 @@ namespace datalog { m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_quantifier_free(exists_k); + m_rule_properties.check_background_free(); break; case BMC_ENGINE: m_rule_properties.collect(r); @@ -598,13 +600,16 @@ namespace datalog { m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_background_free(); break; case CLP_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_background_free(); break; case DDNF_ENGINE: + m_rule_properties.check_background_free(); break; case LAST_ENGINE: default: diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp index 7632a0c2fa4..239fa73b68d 100644 --- a/src/muz/base/rule_properties.cpp +++ b/src/muz/base/rule_properties.cpp @@ -139,6 +139,12 @@ void rule_properties::check_nested_free() { } } +void rule_properties::check_background_free() { + if (m_ctx.get_num_assertions() > 0) + throw default_exception("engine does not support background assertions"); +} + + void rule_properties::check_existential_tail() { ast_mark visited; ptr_vector todo, tocheck; diff --git a/src/muz/base/rule_properties.h b/src/muz/base/rule_properties.h index 896b1bb1689..a7ef9a0dec3 100644 --- a/src/muz/base/rule_properties.h +++ b/src/muz/base/rule_properties.h @@ -68,6 +68,7 @@ namespace datalog { void check_for_negated_predicates(); void check_nested_free(); void check_infinite_sorts(); + void check_background_free(); bool is_monotone() { return m_is_monotone; } void operator()(var* n); void operator()(quantifier* n); From 96d815b9049e7feba128fa32c27a81aa2c42350c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 19:27:19 -0800 Subject: [PATCH 396/597] adding arith sls --- src/sat/sat_ddfw.cpp | 14 +- src/sat/sat_ddfw.h | 2 +- src/sat/sat_extension.h | 1 + src/sat/sat_local_search.cpp | 11 +- src/sat/sat_local_search.h | 2 +- src/sat/sat_parallel.cpp | 2 +- src/sat/sat_prob.h | 2 +- src/sat/sat_solver.cpp | 2 +- src/sat/sat_types.h | 2 +- src/sat/smt/CMakeLists.txt | 2 + src/sat/smt/arith_diagnostics.cpp | 11 + src/sat/smt/arith_local_search.cpp | 352 +++++++++++++++++++++++++++++ src/sat/smt/arith_solver.cpp | 1 + src/sat/smt/arith_solver.h | 105 +++++++-- src/sat/smt/euf_local_search.cpp | 126 +++++++++++ src/sat/smt/euf_solver.h | 16 +- src/sat/smt/sat_th.h | 9 + 17 files changed, 625 insertions(+), 35 deletions(-) create mode 100644 src/sat/smt/arith_local_search.cpp create mode 100644 src/sat/smt/euf_local_search.cpp diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 418070c64c9..723b3858688 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -200,16 +200,14 @@ namespace sat { m_shifts = 0; m_stopwatch.start(); } - - void ddfw::reinit(solver& s) { + + void ddfw::reinit(solver& s, bool_vector const& phase) { add(s); add_assumptions(); - if (s.m_best_phase_size > 0) { - for (unsigned v = 0; v < num_vars(); ++v) { - value(v) = s.m_best_phase[v]; - reward(v) = 0; - make_count(v) = 0; - } + for (unsigned v = 0; v < phase.size(); ++v) { + value(v) = phase[v]; + reward(v) = 0; + make_count(v) = 0; } init_clause_data(); flatten_use_list(); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index d5e7df77374..ce5ff9fdff3 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -227,7 +227,7 @@ namespace sat { // for parallel integration unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; } - void reinit(solver& s) override; + void reinit(solver& s, bool_vector const& phase) override; void collect_statistics(statistics& st) const override {} diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index d6a956a3236..3a1f363a368 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -126,6 +126,7 @@ namespace sat { virtual void add_assumptions(literal_set& ext_assumptions) {} virtual bool tracking_assumptions() { return false; } virtual bool enable_self_propagate() const { return false; } + virtual void local_search(bool_vector& phase) {} virtual bool extract_pb(std::function& card, std::function& pb) { diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index 61ddd13d872..8cc90f05e6a 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -359,13 +359,10 @@ namespace sat { m_par(nullptr) { } - void local_search::reinit(solver& s) { - import(s, true); - if (s.m_best_phase_size > 0) { - for (unsigned i = num_vars(); i-- > 0; ) { - set_phase(i, s.m_best_phase[i]); - } - } + void local_search::reinit(solver& s, bool_vector const& phase) { + import(s, true); + for (unsigned i = phase.size(); i-- > 0; ) + set_phase(i, phase[i]); } void local_search::import(solver const& s, bool _init) { diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h index e46d4b00992..7295b851a1b 100644 --- a/src/sat/sat_local_search.h +++ b/src/sat/sat_local_search.h @@ -248,7 +248,7 @@ namespace sat { void set_seed(unsigned n) override { config().set_random_seed(n); } - void reinit(solver& s) override; + void reinit(solver& s, bool_vector const& phase) override; // used by unit-walk void set_phase(bool_var v, bool f); diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index 3e493168a0c..cdb13706fae 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -252,7 +252,7 @@ namespace sat { m_consumer_ready = true; if (m_solver_copy) { copied = true; - s.reinit(*m_solver_copy.get()); + s.reinit(*m_solver_copy.get(), m_solver_copy->m_best_phase); } return copied; } diff --git a/src/sat/sat_prob.h b/src/sat/sat_prob.h index f05365e3902..d8d58d0911a 100644 --- a/src/sat/sat_prob.h +++ b/src/sat/sat_prob.h @@ -150,7 +150,7 @@ namespace sat { void collect_statistics(statistics& st) const override {} - void reinit(solver& s) override { UNREACHABLE(); } + void reinit(solver& s, bool_vector const& phase) override { UNREACHABLE(); } }; } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 373885f341b..10aac6dcbd7 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1373,7 +1373,7 @@ namespace sat { m_backoffs.m_local_search.delta_effort(*this); m_local_search->rlimit().push(m_backoffs.m_local_search.limit); - m_local_search->reinit(*this); + m_local_search->reinit(*this, m_best_phase); lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); auto const& mdl = m_local_search->get_model(); if (mdl.size() == m_best_phase.size()) { diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 3026b3c5e00..d5d457cb0f8 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -85,7 +85,7 @@ namespace sat { virtual void updt_params(params_ref const& p) = 0; virtual void set_seed(unsigned s) = 0; virtual lbool check(unsigned sz, literal const* assumptions, parallel* par) = 0; - virtual void reinit(solver& s) = 0; + virtual void reinit(solver& s, bool_vector const& phase) = 0; virtual unsigned num_non_binary_clauses() const = 0; virtual reslimit& rlimit() = 0; virtual model const& get_model() const = 0; diff --git a/src/sat/smt/CMakeLists.txt b/src/sat/smt/CMakeLists.txt index 22fc9963ce8..dbbfc385626 100644 --- a/src/sat/smt/CMakeLists.txt +++ b/src/sat/smt/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(sat_smt arith_axioms.cpp arith_diagnostics.cpp arith_internalize.cpp + arith_local_search.cpp arith_solver.cpp array_axioms.cpp array_diagnostics.cpp @@ -20,6 +21,7 @@ z3_add_component(sat_smt euf_ackerman.cpp euf_internalize.cpp euf_invariant.cpp + euf_local_search.cpp euf_model.cpp euf_proof.cpp euf_proof_checker.cpp diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 8ead3d980a5..a3e48256ded 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -23,6 +23,17 @@ Module Name: namespace arith { + + void arith_proof_hint_builder::set_type(euf::solver& ctx, hint_type ty) { + ctx.push(value_trail(m_eq_tail)); + ctx.push(value_trail(m_lit_tail)); + m_ty = ty; + reset(); + } + + arith_proof_hint* arith_proof_hint_builder::mk(euf::solver& s) { + return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); + } std::ostream& solver::display(std::ostream& out) const { lp().display(out); diff --git a/src/sat/smt/arith_local_search.cpp b/src/sat/smt/arith_local_search.cpp new file mode 100644 index 00000000000..c6d02e436bf --- /dev/null +++ b/src/sat/smt/arith_local_search.cpp @@ -0,0 +1,352 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + arith_local_search.cpp + +Abstract: + + Local search dispatch for SMT + +Author: + + Nikolaj Bjorner (nbjorner) 2023-02-07 + +--*/ +#include "sat/sat_solver.h" +#include "sat/smt/arith_solver.h" + + +namespace arith { + + + /// + /// need access to clauses + /// need access to m_unsat + /// need update of phase + /// need to initialize ineqs (arithmetical atoms) + /// + + solver::sls::sls(solver& s): + s(s), m(s.m) {} + + void solver::sls::operator()(bool_vector& phase) { + + // need to init variables/atoms/ineqs + + m.limit().push(m_max_arith_steps); + + unsigned m_best_min_unsat = 1; + unsigned best = m_best_min_unsat; + + while (m.inc() && m_best_min_unsat > 0) { + // unsigned prev = m_unsat.size(); + if (!flip()) + return; +#if 0 + if (m_unsat.size() < best) { + best = m_unsat.size(); + num_steps = 0; + } + if (m_unsat.size() < m_best_min_unsat) + save_best_values(); +#endif + } + } + + void solver::sls::set_bounds_begin() { + m_max_arith_steps = 0; + } + + void solver::sls::set_bounds_end(unsigned num_literals) { + // m_max_arith_steps = s.ctx.m_sl_config.L * + } + + void solver::sls::set_bounds(enode* n) { + ++m_max_arith_steps; + } + + bool solver::sls::flip() { + ++m_stats.m_num_flips; + log(); + if (flip_unsat()) + return true; + if (flip_clauses()) + return true; + if (flip_dscore()) + return true; + return false; + } + + // distance to true + rational solver::sls::dtt(rational const& args, ineq const& ineq) const { + switch (ineq.m_op) { + case ineq_kind::LE: + if (args <= ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound; + case ineq_kind::EQ: + if (args == ineq.m_bound) + return rational::zero(); + return rational::one(); + case ineq_kind::NE: + if (args == ineq.m_bound) + return rational::one(); + return rational::zero(); + case ineq_kind::LT: + default: + if (args < ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound + 1; + } + } + + rational solver::sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { + auto new_args_value = ineq.m_args_value; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + new_args_value += coeff * (new_value - m_vars[w].m_value); + break; + } + } + return dtt(new_args_value, ineq); + } + + // critical move + bool solver::sls::cm(ineq const& ineq, var_t v, rational& new_value) { + SASSERT(!ineq.is_true()); + auto delta = ineq.m_args_value - ineq.m_bound; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + if (coeff > 0) + new_value = value(v) - abs(ceil(delta / coeff)); + else + new_value = value(v) + abs(floor(delta / coeff)); + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(delta + coeff * (new_value - value(v)) <= 0); + return true; + case ineq_kind::EQ: + return delta + coeff * (new_value - value(v)) == 0; + case ineq_kind::NE: + return delta + coeff * (new_value - value(v)) != 0; + case ineq_kind::LT: + return delta + coeff * (new_value - value(v)) < 0; + default: + UNREACHABLE(); break; + } + } + } + return false; + } + +#if 0 + + bool solver::sls::flip_unsat() { + unsigned start = m_rand(); + for (unsigned i = m_unsat.size(); i-- > 0; ) { + unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); + if (flip(m_clauses[cl])) + return true; + } + return false; + } + + bool solver::sls::flip_clauses() { + unsigned start = m_rand(); + for (unsigned i = m_clauses.size(); i-- > 0; ) + if (flip_arith(m_clauses[(i + start) % m_clauses.size()])) + return true; + return false; + } + + bool solver::sls::flip_dscore() { + paws(); + unsigned start = m_rand(); + for (unsigned i = m_unsat.size(); i-- > 0; ) { + unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); + if (flip_dscore(m_clauses[cl])) + return true; + } + std::cout << "flip dscore\n"; + IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << m_unsat.size() << ")\n"); + return false; + } + + bool solver::sls::flip_dscore(clause const& clause) { + rational new_value, min_value, min_score(-1); + var_t min_var = UINT_MAX; + for (auto a : clause.m_arith) { + auto const& ai = m_atoms[a]; + ineq const& ineq = ai.m_ineq; + for (auto const& [coeff, v] : ineq.m_args) { + if (!ineq.is_true() && cm(ineq, v, new_value)) { + rational score = dscore(v, new_value); + if (UINT_MAX == min_var || score < min_score) { + min_var = v; + min_value = new_value; + min_score = score; + } + } + } + } + if (min_var != UINT_MAX) { + update(min_var, min_value); + return true; + } + return false; + } + + void solver::sls::paws() { + for (auto& clause : m_clauses) { + bool above = 10000 * m_config.sp <= (m_rand() % 10000); + if (!above && clause.is_true() && clause.m_weight > 1) + clause.m_weight -= 1; + if (above && !clause.is_true()) + clause.m_weight += 1; + } + } + + void solver::sls::update(var_t v, rational const& new_value) { + auto& vi = m_vars[v]; + auto const& old_value = vi.m_value; + for (auto const& [coeff, atm] : vi.m_atoms) { + auto& ai = m_atoms[atm]; + SASSERT(!ai.m_is_bool); + auto& clause = m_clauses[ai.m_clause_idx]; + rational dtt_old = dtt(ai.m_ineq); + ai.m_ineq.m_args_value += coeff * (new_value - old_value); + rational dtt_new = dtt(ai.m_ineq); + bool was_true = clause.is_true(); + if (dtt_new < clause.m_dts) { + if (was_true && clause.m_dts > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { + for (auto lit : clause.m_bools) { + if (is_true(lit)) { + dec_break(lit); + break; + } + } + } + clause.m_dts = dtt_new; + if (!was_true && clause.is_true()) + m_unsat.remove(ai.m_clause_idx); + } + else if (clause.m_dts == dtt_old && dtt_old < dtt_new) { + clause.m_dts = dts(clause); + if (was_true && !clause.is_true()) + m_unsat.insert(ai.m_clause_idx); + if (was_true && clause.is_true() && clause.m_dts > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { + for (auto lit : clause.m_bools) { + if (is_true(lit)) { + inc_break(lit); + break; + } + } + } + } + SASSERT(clause.m_dts >= 0); + } + vi.m_value = new_value; + } + + bool solver::sls::flip_arith(clause const& clause) { + rational new_value; + for (auto a : clause.m_arith) { + auto const& ai = m_atoms[a]; + ineq const& ineq = ai.m_ineq; + for (auto const& [coeff, v] : ineq.m_args) { + if (!ineq.is_true() && cm(ineq, v, new_value)) { + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = m_unsat.size(); + update(v, new_value); + std::cout << "score " << v << " " << score << "\n"; + std::cout << num_unsat << " -> " << m_unsat.size() << "\n"; + return true; + } + } + } + return false; + } + + + + rational solver::sls::dts(clause const& cl) const { + rational d(1), d2; + bool first = true; + for (auto a : cl.m_arith) { + auto const& ai = m_atoms[a]; + d2 = dtt(ai.m_ineq); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + rational solver::sls::dts(clause const& cl, var_t v, rational const& new_value) const { + rational d(1), d2; + bool first = true; + for (auto a : cl.m_arith) { + auto const& ai = m_atoms[a]; + d2 = dtt(ai.m_ineq, v, new_value); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + // + // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) + // + rational solver::sls::dscore(var_t v, rational const& new_value) const { + auto const& vi = m_vars[v]; + rational score(0); + for (auto const& [coeff, atm] : vi.m_atoms) { + auto const& ai = m_atoms[atm]; + auto const& cl = m_clauses[ai.m_clause_idx]; + score += (cl.m_dts - dts(cl, v, new_value)) * rational(cl.m_weight); + } + return score; + } + + int solver::sls::cm_score(var_t v, rational const& new_value) { + int score = 0; + auto& vi = m_vars[v]; + for (auto const& [coeff, atm] : vi.m_atoms) { + auto const& ai = m_atoms[atm]; + auto const& clause = m_clauses[ai.m_clause_idx]; + rational dtt_old = dtt(ai.m_ineq); + rational dtt_new = dtt(ai.m_ineq, v, new_value); + if (!clause.is_true()) { + if (dtt_new == 0) + ++score; + } + else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) + continue; + else { + bool has_true = false; + for (auto a : clause.m_arith) { + auto const& ai = m_atoms[a]; + rational d = dtt(ai.m_ineq, v, new_value); + has_true |= (d == 0); + } + if (!has_true) + --score; + } + } + return score; + } + +#endif +} + diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 35e0795b7ee..fd507ed15c9 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -24,6 +24,7 @@ namespace arith { solver::solver(euf::solver& ctx, theory_id id) : th_euf_solver(ctx, symbol("arith"), id), m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), + m_local_search(*this), m_resource_limit(*this), m_bp(*this), a(m), diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index a13ef66843a..1717b93d146 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -78,12 +78,7 @@ namespace arith { m_eq_tail++; } public: - void set_type(euf::solver& ctx, hint_type ty) { - ctx.push(value_trail(m_eq_tail)); - ctx.push(value_trail(m_lit_tail)); - m_ty = ty; - reset(); - } + void set_type(euf::solver& ctx, hint_type ty); void set_num_le(unsigned n) { m_num_le = n; } void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); } void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); } @@ -96,12 +91,9 @@ namespace arith { } std::pair const& lit(unsigned i) const { return m_literals[i]; } std::tuple const& eq(unsigned i) const { return m_eqs[i]; } - arith_proof_hint* mk(euf::solver& s) { - return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); - } + arith_proof_hint* mk(euf::solver& s); }; - class solver : public euf::th_euf_solver { friend struct arith_proof_hint; @@ -144,7 +136,7 @@ namespace arith { }; int_hashtable m_model_eqs; - bool m_new_eq { false }; + bool m_new_eq = false; // temporary values kept during internalization @@ -198,6 +190,85 @@ namespace arith { } }; + // local search portion for arithmetic + class sls { + enum class ineq_kind { EQ, LE, LT, NE }; + enum class var_kind { INT, REAL }; + typedef unsigned var_t; + typedef unsigned atom_t; + + struct stats { + unsigned m_num_flips = 0; + }; + // encode args <= bound, args = bound, args < bound + struct ineq { + vector> m_args; + ineq_kind m_op = ineq_kind::LE; + rational m_bound; + rational m_args_value; + + bool is_true() const { + switch (m_op) { + case ineq_kind::LE: + return m_args_value <= m_bound; + case ineq_kind::EQ: + return m_args_value == m_bound; + case ineq_kind::NE: + return m_args_value != m_bound; + default: + return m_args_value < m_bound; + } + } + }; + + struct var_info { + rational m_value; + rational m_best_value; + var_kind m_kind = var_kind::INT; + vector> m_atoms; + }; + + struct atom_info { + ineq m_ineq; + unsigned m_clause_idx; + bool m_is_bool = false; + bool m_phase = false; + bool m_best_phase = false; + unsigned m_breaks = 0; + }; + + solver& s; + ast_manager& m; + unsigned m_max_arith_steps = 0; + stats m_stats; + vector m_atoms; + vector m_vars; + + bool flip(); + void log() {} + bool flip_unsat() { return false; } + bool flip_clauses() { return false; } + bool flip_dscore() { return false; } +// bool flip_dscore(clause const&); +// bool flip(clause const&); + rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } + rational dtt(rational const& args, ineq const& ineq) const; + rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; +// rational dts(clause const& cl, var_t v, rational const& new_value) const; +// rational dts(clause const& cl) const; + bool cm(ineq const& ineq, var_t v, rational& new_value); + + rational value(var_t v) const { return m_vars[v].m_value; } + public: + sls(solver& s); + void operator ()(bool_vector& phase); + void set_bounds_begin(); + void set_bounds_end(unsigned num_literals); + void set_bounds(enode* n); + }; + + sls m_local_search; + typedef vector> var_coeffs; vector m_columns; var_coeffs m_left_side; // constraint left side @@ -233,10 +304,10 @@ namespace arith { unsigned m_asserted_qhead = 0; svector > m_assume_eq_candidates; - unsigned m_assume_eq_head{ 0 }; + unsigned m_assume_eq_head = 0; lp::u_set m_tmp_var_set; - unsigned m_num_conflicts{ 0 }; + unsigned m_num_conflicts = 0; lp_api::stats m_stats; svector m_scopes; @@ -515,6 +586,11 @@ namespace arith { bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); } bool has_unhandled() const override { return m_not_handled != nullptr; } + void set_bounds_begin() override { m_local_search.set_bounds_begin(); } + void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } + void set_bounds(enode* n) override { m_local_search.set_bounds(n); } + void local_search(bool_vector& phase) override { m_local_search(phase); } + // bounds and equality propagation callbacks lp::lar_solver& lp() { return *m_solver; } lp::lar_solver const& lp() const { return *m_solver; } @@ -523,4 +599,7 @@ namespace arith { void consume(rational const& v, lp::constraint_index j); bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const; }; + + + } diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp new file mode 100644 index 00000000000..5ea30b96c6e --- /dev/null +++ b/src/sat/smt/euf_local_search.cpp @@ -0,0 +1,126 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + euf_local_search.cpp + +Abstract: + + Local search dispatch for SMT + +Author: + + Nikolaj Bjorner (nbjorner) 2023-02-07 + +--*/ +#include "sat/sat_solver.h" +#include "sat/sat_ddfw.h" +#include "sat/smt/euf_solver.h" + + +namespace euf { + + void solver::local_search(bool_vector& phase) { + + + scoped_limits scoped_rl(m.limit()); + sat::ddfw bool_search; + bool_search.add(s()); + bool_search.updt_params(s().params()); + bool_search.set_seed(rand()); + scoped_rl.push_child(&(bool_search.rlimit())); + + unsigned rounds = 0; + unsigned max_rounds = 30; + + sat::model mdl(s().num_vars()); + for (unsigned v = 0; v < s().num_vars(); ++v) + mdl[v] = s().value(v); + + + while (m.inc() && rounds < max_rounds) { + setup_bounds(mdl); + bool_search.reinit(s(), phase); + + // Non-boolean literals are assumptions to Boolean search + literal_vector _lits; + for (unsigned v = 0; v < mdl.size(); ++v) + if (!is_propositional(literal(v))) + _lits.push_back(literal(v, mdl[v] == l_false)); + + bool_search.rlimit().push(m_max_bool_steps); + + lbool r = bool_search.check(_lits.size(), _lits.data(), nullptr); + + + auto const& mdl = bool_search.get_model(); + for (unsigned i = 0; i < mdl.size(); ++i) + phase[i] = mdl[i] == l_true; + + for (auto* th : m_solvers) + th->local_search(phase); + ++rounds; + // if is_sat break; + } + + } + + bool solver::is_propositional(sat::literal lit) { + expr* e = m_bool_var2expr.get(lit.var(), nullptr); + if (!e) + return true; + if (is_uninterp_const(e)) + return true; + euf::enode* n = m_egraph.find(e); + if (!n) + return true; + } + + void solver::setup_bounds(sat::model const& mdl) { + unsigned num_literals = 0; + unsigned num_bool = 0; + for (auto* th : m_solvers) + th->set_bounds_begin(); + + auto init_literal = [&](sat::literal l) { + if (is_propositional(l)) { + ++num_bool; + return; + } + euf::enode* n = m_egraph.find(m_bool_var2expr.get(l.var(), nullptr)); + for (auto const& thv : enode_th_vars(n)) { + auto* th = m_id2solver.get(thv.get_id(), nullptr); + if (th) + th->set_bounds(n); + } + }; + + auto is_true = [&](auto lit) { + return mdl[lit.var()] == to_lbool(!lit.sign()); + }; + + svector bin_clauses; + s().collect_bin_clauses(bin_clauses, false, false); + for (auto* cp : s().clauses()) { + if (any_of(*cp, [&](auto lit) { return is_true(lit); })) + continue; + num_literals += cp->size(); + for (auto l : *cp) + init_literal(l); + } + + for (auto [l1, l2] : bin_clauses) { + if (is_true(l1) || is_true(l2)) + continue; + num_literals += 2; + init_literal(l1); + init_literal(l2); + }; + + m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; + + for (auto* th : m_solvers) + th->set_bounds_end(num_literals); + } +} diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 96079fbec92..44e3df4b022 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -100,6 +100,14 @@ namespace euf { scope(unsigned l) : m_var_lim(l) {} }; + struct local_search_config { + double cb = 0.0; + unsigned L = 20; + unsigned t = 45; + unsigned max_no_improve = 500000; + double sp = 0.0003; + }; + size_t* to_ptr(sat::literal l) { return TAG(size_t*, reinterpret_cast((size_t)(l.index() << 4)), 1); } size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast(jst), 2); } @@ -119,6 +127,7 @@ namespace euf { sat::sat_internalizer& si; relevancy m_relevancy; smt_params m_config; + local_search_config m_ls_config; euf::egraph m_egraph; trail_stack m_trail; stats m_stats; @@ -253,6 +262,11 @@ namespace euf { constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); } constraint& lit_constraint(enode* n); + // local search + unsigned m_max_bool_steps = 10; + bool is_propositional(sat::literal lit); + void setup_bounds(sat::model const& mdl); + // user propagator void check_for_user_propagator() { if (!m_user_propagator) @@ -339,6 +353,7 @@ namespace euf { void add_assumptions(sat::literal_set& assumptions) override; bool tracking_assumptions() override; std::string reason_unknown() override { return m_reason_unknown; } + void local_search(bool_vector& phase) override; void propagate(literal lit, ext_justification_idx idx); bool propagate(enode* a, enode* b, ext_justification_idx idx); @@ -551,4 +566,3 @@ namespace euf { inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) { return s.display(out); } - diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index a3b81a08d57..2101dfd6458 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -136,6 +136,15 @@ namespace euf { sat::status status() const { return sat::status::th(false, get_id()); } + /** + * Local search interface + */ + virtual void set_bounds_begin() {} + + virtual void set_bounds_end(unsigned num_literals) {} + + virtual void set_bounds(enode* n) {} + }; class th_proof_hint : public sat::proof_hint { From b3ebce3966aa4923550076064ab66193dc5c8632 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 19:30:45 -0800 Subject: [PATCH 397/597] fix compilation --- src/sat/smt/euf_local_search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 5ea30b96c6e..a614d1656ee 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -72,9 +72,7 @@ namespace euf { return true; if (is_uninterp_const(e)) return true; - euf::enode* n = m_egraph.find(e); - if (!n) - return true; + return !m_egraph.find(e); } void solver::setup_bounds(sat::model const& mdl) { From a8335f2d5eb634a0ed6591cf96c8c2fcff8e00a5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 19:50:45 -0800 Subject: [PATCH 398/597] use phase --- src/sat/smt/euf_local_search.cpp | 27 +++++++-------------------- src/sat/smt/euf_solver.h | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index a614d1656ee..90889ca3ed1 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -22,8 +22,6 @@ Module Name: namespace euf { void solver::local_search(bool_vector& phase) { - - scoped_limits scoped_rl(m.limit()); sat::ddfw bool_search; bool_search.add(s()); @@ -31,23 +29,17 @@ namespace euf { bool_search.set_seed(rand()); scoped_rl.push_child(&(bool_search.rlimit())); - unsigned rounds = 0; unsigned max_rounds = 30; - sat::model mdl(s().num_vars()); - for (unsigned v = 0; v < s().num_vars(); ++v) - mdl[v] = s().value(v); - - - while (m.inc() && rounds < max_rounds) { - setup_bounds(mdl); + for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { + setup_bounds(phase); bool_search.reinit(s(), phase); // Non-boolean literals are assumptions to Boolean search literal_vector _lits; - for (unsigned v = 0; v < mdl.size(); ++v) + for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) - _lits.push_back(literal(v, mdl[v] == l_false)); + _lits.push_back(literal(v, !phase[v])); bool_search.rlimit().push(m_max_bool_steps); @@ -60,7 +52,6 @@ namespace euf { for (auto* th : m_solvers) th->local_search(phase); - ++rounds; // if is_sat break; } @@ -68,14 +59,10 @@ namespace euf { bool solver::is_propositional(sat::literal lit) { expr* e = m_bool_var2expr.get(lit.var(), nullptr); - if (!e) - return true; - if (is_uninterp_const(e)) - return true; - return !m_egraph.find(e); + return !e || is_uninterp_const(e) || !m_egraph.find(e); } - void solver::setup_bounds(sat::model const& mdl) { + void solver::setup_bounds(bool_vector const& phase) { unsigned num_literals = 0; unsigned num_bool = 0; for (auto* th : m_solvers) @@ -95,7 +82,7 @@ namespace euf { }; auto is_true = [&](auto lit) { - return mdl[lit.var()] == to_lbool(!lit.sign()); + return phase[lit.var()] == !lit.sign(); }; svector bin_clauses; diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 44e3df4b022..a19dbca5d9b 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -265,7 +265,7 @@ namespace euf { // local search unsigned m_max_bool_steps = 10; bool is_propositional(sat::literal lit); - void setup_bounds(sat::model const& mdl); + void setup_bounds(bool_vector const& mdl); // user propagator void check_for_user_propagator() { From 02d48adae556ca0e472ec19dfc1dab414e2e55b1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Feb 2023 08:24:33 -0800 Subject: [PATCH 399/597] fix #6573 --- src/ast/seq_decl_plugin.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 54ef58e324f..9292a7be62d 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -663,19 +663,21 @@ void seq_decl_plugin::add_map_sig() { m_sigs[OP_SEQ_MAP] = alloc(psig, m, "seq.map", 2, 2, arrABseqA, seqB); m_sigs[OP_SEQ_MAPI] = alloc(psig, m, "seq.mapi", 2, 3, arrIABintTseqA, seqB); m_sigs[OP_SEQ_FOLDL] = alloc(psig, m, "seq.fold_left", 2, 3, arrBAB_BseqA, B); - m_sigs[OP_SEQ_FOLDLI] = alloc(psig, m, "seq.fold_leftli", 2, 4, arrIBABintTBseqA, B); + m_sigs[OP_SEQ_FOLDLI] = alloc(psig, m, "seq.fold_lefti", 2, 4, arrIBABintTBseqA, B); } void seq_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { init(); for (unsigned i = 0; i < m_sigs.size(); ++i) { - if (m_sigs[i]) - op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i)); + if (m_sigs[i]) + op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i)); } op_names.push_back(builtin_name("seq.map", OP_SEQ_MAP)); op_names.push_back(builtin_name("seq.mapi", OP_SEQ_MAPI)); op_names.push_back(builtin_name("seq.foldl", OP_SEQ_FOLDL)); op_names.push_back(builtin_name("seq.foldli", OP_SEQ_FOLDLI)); + op_names.push_back(builtin_name("seq.fold_lefti", OP_SEQ_FOLDLI)); + op_names.push_back(builtin_name("seq.fold_left", OP_SEQ_FOLDL)); op_names.push_back(builtin_name("str.in.re", _OP_STRING_IN_REGEXP)); op_names.push_back(builtin_name("str.in-re", _OP_STRING_IN_REGEXP)); op_names.push_back(builtin_name("str.to.re", _OP_STRING_TO_REGEXP)); From d52e893528249b512ef9a302ac291e6ffc1f4ba7 Mon Sep 17 00:00:00 2001 From: Julian Parsert Date: Fri, 10 Feb 2023 18:00:26 +0000 Subject: [PATCH 400/597] Added overloaded versions of context::recfun in the c++ api that allow for the declaration of recursive functions where the domain is given by a z3::sort_vector instead of an arity and sort* (#6576) Co-authored-by: Julian Parsert --- src/api/c++/z3++.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 82081816801..f2c0f3f243e 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -360,6 +360,8 @@ namespace z3 { func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range); + func_decl recfun(symbol const & name, const sort_vector& domain, sort const & range); + func_decl recfun(char const * name, sort_vector const& domain, sort const & range); func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl recfun(char const * name, sort const & domain, sort const & range); func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); @@ -2712,6 +2714,16 @@ namespace z3 { void set(char const * k, double v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, symbol const & v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, char const* v) { params p(ctx()); p.set(k, v); set(p); } + /** + \brief Create a backtracking point. + + The solver contains a stack of assertions. + + \sa Z3_solver_get_num_scopes + \sa Z3_solver_pop + + def_API('Z3_solver_push', VOID, (_in(CONTEXT), _in(SOLVER))) + */ void push() { Z3_solver_push(ctx(), m_solver); check_error(); } void pop(unsigned n = 1) { Z3_solver_pop(ctx(), m_solver, n); check_error(); } void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } @@ -3604,6 +3616,19 @@ namespace z3 { } + inline func_decl context::recfun(symbol const & name, sort_vector const& domain, sort const & range) { + check_context(domain, range); + array domain1(domain); + Z3_func_decl f = Z3_mk_rec_func_decl(m_ctx, name, domain1.size(), domain1.ptr(), range); + check_error(); + return func_decl(*this, f); + } + + inline func_decl context::recfun(char const * name, sort_vector const& domain, sort const & range) { + return recfun(str_symbol(name), domain, range); + + } + inline func_decl context::recfun(char const * name, unsigned arity, sort const * domain, sort const & range) { return recfun(str_symbol(name), arity, domain, range); } From 1b0c76e3f0585cde680efcf14bb6b304fca09fc8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 10 Feb 2023 16:55:55 -0800 Subject: [PATCH 401/597] fixes to mbqi in the new core based on #6575 --- src/ast/ast.cpp | 2 +- src/ast/ast.h | 2 +- src/ast/ast_pp_util.cpp | 6 +++--- src/ast/decl_collector.cpp | 19 ++++++++++++------- src/ast/decl_collector.h | 6 +++--- src/sat/smt/q_mbi.cpp | 3 +-- src/smt/smt_clause_proof.cpp | 2 +- src/solver/check_sat_result.cpp | 6 ++++-- src/solver/check_sat_result.h | 2 +- 9 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index dee55cf98c4..0a34d3e12d8 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -3296,7 +3296,7 @@ proof * ast_manager::mk_redundant_del(expr* e) { return mk_clause_trail_elem(nullptr, e, PR_REDUNDANT_DEL); } -proof * ast_manager::mk_clause_trail(unsigned n, proof* const* ps) { +proof * ast_manager::mk_clause_trail(unsigned n, expr* const* ps) { ptr_buffer args; args.append(n, (expr**) ps); return mk_app(basic_family_id, PR_CLAUSE_TRAIL, 0, nullptr, args.size(), args.data()); diff --git a/src/ast/ast.h b/src/ast/ast.h index 2400e05a6e7..8fae83b26c6 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -2335,7 +2335,7 @@ class ast_manager { proof * mk_th_assumption_add(proof* pr, expr* e); proof * mk_th_lemma_add(proof* pr, expr* e); proof * mk_redundant_del(expr* e); - proof * mk_clause_trail(unsigned n, proof* const* ps); + proof * mk_clause_trail(unsigned n, expr* const* ps); proof * mk_def_axiom(expr * ax); proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs); diff --git a/src/ast/ast_pp_util.cpp b/src/ast/ast_pp_util.cpp index c0608522fbe..2b7e72a9192 100644 --- a/src/ast/ast_pp_util.cpp +++ b/src/ast/ast_pp_util.cpp @@ -43,11 +43,11 @@ void ast_pp_util::display_decls(std::ostream& out) { for (unsigned i = m_sorts; i < n; ++i) pp.display_sort_decl(out, coll.get_sorts()[i], seen); m_sorts = n; - + n = coll.get_num_decls(); for (unsigned i = m_decls; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; - if (f->get_family_id() == null_family_id && !m_removed.contains(f)) + if (coll.should_declare(f) && !m_removed.contains(f)) ast_smt2_pp(out, f, m_env) << "\n"; } m_decls = n; @@ -80,7 +80,7 @@ void ast_pp_util::display_skolem_decls(std::ostream& out) { unsigned n = coll.get_num_decls(); for (unsigned i = m_decls; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; - if (f->get_family_id() == null_family_id && !m_removed.contains(f) && f->is_skolem()) + if (coll.should_declare(f) && !m_removed.contains(f) && f->is_skolem()) ast_smt2_pp(out, f, m_env) << "\n"; } m_decls = n; diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index 7786a79d42c..5619f546b89 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -24,7 +24,7 @@ Revision History: void decl_collector::visit_sort(sort * n) { SASSERT(!m_visited.is_marked(n)); family_id fid = n->get_family_id(); - if (m().is_uninterp(n)) + if (m.is_uninterp(n)) m_sorts.push_back(n); else if (fid == m_dt_fid) { m_sorts.push_back(n); @@ -43,7 +43,7 @@ void decl_collector::visit_sort(sort * n) { } bool decl_collector::is_bool(sort * s) { - return m().is_bool(s); + return m.is_bool(s); } void decl_collector::visit_func(func_decl * n) { @@ -51,10 +51,10 @@ void decl_collector::visit_func(func_decl * n) { if (!m_visited.is_marked(n)) { family_id fid = n->get_family_id(); - if (fid == null_family_id) + if (should_declare(n)) m_decls.push_back(n); else if (fid == m_rec_fid) { - recfun::util u(m()); + recfun::util u(m); if (u.has_def(n)) { m_rec_decls.push_back(n); m_todo.push_back(u.get_def(n).get_rhs()); @@ -69,12 +69,17 @@ void decl_collector::visit_func(func_decl * n) { } } +bool decl_collector::should_declare(func_decl* f) const { + return f->get_family_id() == null_family_id || m.is_model_value(f); +} + + decl_collector::decl_collector(ast_manager & m): - m_manager(m), + m(m), m_trail(m), m_dt_util(m), m_ar_util(m) { - m_basic_fid = m_manager.get_basic_family_id(); + m_basic_fid = m.get_basic_family_id(); m_dt_fid = m_dt_util.get_family_id(); recfun::util rec_util(m); m_rec_fid = rec_util.get_family_id(); @@ -83,7 +88,7 @@ decl_collector::decl_collector(ast_manager & m): void decl_collector::visit(ast* n) { if (m_visited.is_marked(n)) return; - datatype_util util(m()); + datatype_util util(m); m_todo.push_back(n); while (!m_todo.empty()) { n = m_todo.back(); diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 31cabe796ef..a7f79d008ab 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -26,7 +26,7 @@ Revision History: #include "ast/array_decl_plugin.h" class decl_collector { - ast_manager & m_manager; + ast_manager & m; lim_svector m_sorts; lim_svector m_decls; lim_svector m_rec_decls; @@ -48,10 +48,10 @@ class decl_collector { void collect_deps(top_sort& st); void collect_deps(sort* s, sort_set& set); - public: decl_collector(ast_manager & m); - ast_manager & m() { return m_manager; } + + bool should_declare(func_decl* f) const; void reset() { m_sorts.reset(); m_decls.reset(); m_visited.reset(); m_trail.reset(); } void visit_func(func_decl* n); diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 21830c16200..21842ec7699 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -357,8 +357,7 @@ namespace q { return expr_ref(m); } else if (!(*p)(*m_model, vars, fmls)) { - TRACE("q", tout << "theory projection failed\n"); - return expr_ref(m); + TRACE("q", tout << "theory projection failed - use value\n"); } } for (app* v : vars) { diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index 521510b596c..3bb2a1fdfb3 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -233,7 +233,7 @@ namespace smt { TRACE("context", tout << "get-proof " << ctx.get_fparams().m_clause_proof << "\n";); if (!ctx.get_fparams().m_clause_proof) return proof_ref(m); - proof_ref_vector ps(m); + expr_ref_vector ps(m); for (auto& info : m_trail) { expr_ref fact = mk_or(info.m_clause); proof* pr = info.m_proof; diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index e946dd430e0..c0f3979aa3a 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -41,8 +41,10 @@ void check_sat_result::set_reason_unknown(event_handler& eh) { proof* check_sat_result::get_proof() { if (!m_log.empty() && !m_proof) { - app* last = m_log.back(); - m_log.push_back(to_app(m.get_fact(last))); + SASSERT(is_app(m_log.back())); + app* last = to_app(m_log.back()); + expr* fact = m.get_fact(last); + m_log.push_back(fact); m_proof = m.mk_clause_trail(m_log.size(), m_log.data()); } if (m_proof) diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 936f6d3dfa3..2269a14449a 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -40,7 +40,7 @@ Module Name: class check_sat_result { protected: ast_manager& m; - proof_ref_vector m_log; + expr_ref_vector m_log; proof_ref m_proof; unsigned m_ref_count = 0; lbool m_status = l_undef; From d22e4aa5259137961bc5ae2c45403b9655aa5f1e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 Feb 2023 15:52:32 -0800 Subject: [PATCH 402/597] wip - integrating arithmetic local search --- src/sat/sat_ddfw.cpp | 27 ++- src/sat/sat_ddfw.h | 15 +- src/sat/sat_solver.h | 1 + src/sat/smt/arith_local_search.cpp | 304 +++++++++++++++++------------ src/sat/smt/arith_solver.h | 58 ++++-- src/sat/smt/euf_local_search.cpp | 23 +-- src/sat/smt/sat_th.h | 3 + 7 files changed, 271 insertions(+), 160 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 723b3858688..74a0e7777ae 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -129,6 +129,7 @@ namespace sat { void ddfw::add(unsigned n, literal const* c) { clause* cls = m_alloc.mk_clause(n, c, false); unsigned idx = m_clauses.size(); + m_clauses.push_back(clause_info(cls, m_config.m_init_clause_weight)); for (literal lit : *cls) { m_use_list.reserve(2*(lit.var()+1)); @@ -137,6 +138,18 @@ namespace sat { } } + /** + * Remove the last clause that was added + */ + void ddfw::del() { + auto& info = m_clauses.back(); + for (literal lit : *info.m_clause) + m_use_list[lit.index()].pop_back(); + m_alloc.del_clause(info.m_clause); + m_clauses.pop_back(); + m_unsat.remove(m_clauses.size()); + } + void ddfw::add(solver const& s) { for (auto& ci : m_clauses) m_alloc.del_clause(ci.m_clause); @@ -169,9 +182,17 @@ namespace sat { } void ddfw::add_assumptions() { - for (unsigned i = 0; i < m_assumptions.size(); ++i) { - add(1, m_assumptions.data() + i); - } + for (unsigned i = 0; i < m_assumptions.size(); ++i) + add(1, m_assumptions.data() + i); + } + + void ddfw::remove_assumptions() { + for (unsigned i = 0; i < m_assumptions.size(); ++i) + del(); + m_unsat_vars.reset(); + for (auto idx : m_unsat) + for (auto lit : get_clause(idx)) + m_unsat_vars.insert(lit.var()); } void ddfw::init(unsigned sz, literal const* assumptions) { diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ce5ff9fdff3..7dd69de8103 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -32,7 +32,7 @@ namespace sat { class parallel; class ddfw : public i_local_search { - + public: struct clause_info { clause_info(clause* cl, double init_weight): m_weight(init_weight), m_clause(cl) {} double m_weight; // weight of clause @@ -43,6 +43,7 @@ namespace sat { void add(literal lit) { ++m_num_trues; m_trues += lit.index(); } void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); } }; + protected: struct config { config() { reset(); } @@ -197,6 +198,8 @@ namespace sat { void add(unsigned sz, literal const* c); + void del(); + void add_assumptions(); inline void transfer_weight(unsigned from, unsigned to, double w); @@ -232,6 +235,16 @@ namespace sat { void collect_statistics(statistics& st) const override {} double get_priority(bool_var v) const override { return m_probs[v]; } + + // access clause information and state of Boolean search + indexed_uint_set& unsat_set() { return m_unsat; } + + unsigned num_clauses() const { return m_clauses.size(); } + + clause_info& get_clause_info(unsigned idx) { return m_clauses[idx]; } + + void remove_assumptions(); + }; } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index ca738ce9b14..703b36dd069 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -347,6 +347,7 @@ namespace sat { s.m_checkpoint_enabled = true; } }; + unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; diff --git a/src/sat/smt/arith_local_search.cpp b/src/sat/smt/arith_local_search.cpp index c6d02e436bf..787280e7346 100644 --- a/src/sat/smt/arith_local_search.cpp +++ b/src/sat/smt/arith_local_search.cpp @@ -36,22 +36,19 @@ namespace arith { // need to init variables/atoms/ineqs m.limit().push(m_max_arith_steps); - - unsigned m_best_min_unsat = 1; - unsigned best = m_best_min_unsat; - - while (m.inc() && m_best_min_unsat > 0) { - // unsigned prev = m_unsat.size(); + m_best_min_unsat = unsat().size(); + unsigned num_steps = 0; + while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) return; -#if 0 - if (m_unsat.size() < best) { - best = m_unsat.size(); + ++m_stats.m_num_flips; + ++num_steps; + unsigned num_unsat = unsat().size(); + if (num_unsat < m_best_min_unsat) { + m_best_min_unsat = num_unsat; num_steps = 0; - } - if (m_unsat.size() < m_best_min_unsat) save_best_values(); -#endif + } } } @@ -68,7 +65,6 @@ namespace arith { } bool solver::sls::flip() { - ++m_stats.m_num_flips; log(); if (flip_unsat()) return true; @@ -141,45 +137,72 @@ namespace arith { return false; } -#if 0 - bool solver::sls::flip_unsat() { - unsigned start = m_rand(); - for (unsigned i = m_unsat.size(); i-- > 0; ) { - unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); - if (flip(m_clauses[cl])) + unsigned start = s.random(); + unsigned sz = unsat().size(); + for (unsigned i = sz; i-- > 0; ) { + unsigned cl = unsat().elem_at((i + start) % sz); + if (flip(cl)) return true; } return false; } + + bool solver::sls::flip(unsigned cl) { + auto const& clause = get_clause(cl); + rational new_value; + for (literal lit : clause) { + auto const* ai = atom(lit); + if (!ai) + continue; + ineq const& ineq = ai->m_ineq; + for (auto const& [coeff, v] : ineq.m_args) { + if (!ineq.is_true() && cm(ineq, v, new_value)) { + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = unsat().size(); + update(v, new_value); + IF_VERBOSE(0, + verbose_stream() << "score " << v << " " << score << "\n" + << num_unsat << " -> " << unsat().size() << "\n"); + return true; + } + } + } + return false; + } + bool solver::sls::flip_clauses() { - unsigned start = m_rand(); - for (unsigned i = m_clauses.size(); i-- > 0; ) - if (flip_arith(m_clauses[(i + start) % m_clauses.size()])) + unsigned start = s.random(); + for (unsigned i = num_clauses(); i-- > 0; ) + if (flip((i + start) % num_clauses())) return true; return false; } bool solver::sls::flip_dscore() { paws(); - unsigned start = m_rand(); - for (unsigned i = m_unsat.size(); i-- > 0; ) { - unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); - if (flip_dscore(m_clauses[cl])) + unsigned start = s.random(); + for (unsigned i = unsat().size(); i-- > 0; ) { + unsigned cl = unsat().elem_at((i + start) % unsat().size()); + if (flip_dscore(cl)) return true; } - std::cout << "flip dscore\n"; - IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << m_unsat.size() << ")\n"); + IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); return false; } - bool solver::sls::flip_dscore(clause const& clause) { + bool solver::sls::flip_dscore(unsigned cl) { + auto const& clause = get_clause(cl); rational new_value, min_value, min_score(-1); var_t min_var = UINT_MAX; - for (auto a : clause.m_arith) { - auto const& ai = m_atoms[a]; - ineq const& ineq = ai.m_ineq; + for (auto lit : clause) { + auto const* ai = atom(lit); + if (!ai) + continue; + ineq const& ineq = ai->m_ineq; for (auto const& [coeff, v] : ineq.m_args) { if (!ineq.is_true() && cm(ineq, v, new_value)) { rational score = dscore(v, new_value); @@ -199,8 +222,9 @@ namespace arith { } void solver::sls::paws() { - for (auto& clause : m_clauses) { - bool above = 10000 * m_config.sp <= (m_rand() % 10000); + for (unsigned cl = num_clauses(); cl-- > 0; ) { + auto& clause = get_clause_info(cl); + bool above = 10000 * m_config.sp <= (s.random() % 10000); if (!above && clause.is_true() && clause.m_weight > 1) clause.m_weight -= 1; if (above && !clause.is_true()) @@ -208,77 +232,58 @@ namespace arith { } } - void solver::sls::update(var_t v, rational const& new_value) { - auto& vi = m_vars[v]; - auto const& old_value = vi.m_value; + // + // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) + // + rational solver::sls::dscore(var_t v, rational const& new_value) const { + auto const& vi = m_vars[v]; + rational score(0); for (auto const& [coeff, atm] : vi.m_atoms) { - auto& ai = m_atoms[atm]; - SASSERT(!ai.m_is_bool); - auto& clause = m_clauses[ai.m_clause_idx]; - rational dtt_old = dtt(ai.m_ineq); - ai.m_ineq.m_args_value += coeff * (new_value - old_value); - rational dtt_new = dtt(ai.m_ineq); - bool was_true = clause.is_true(); - if (dtt_new < clause.m_dts) { - if (was_true && clause.m_dts > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { - for (auto lit : clause.m_bools) { - if (is_true(lit)) { - dec_break(lit); - break; - } - } - } - clause.m_dts = dtt_new; - if (!was_true && clause.is_true()) - m_unsat.remove(ai.m_clause_idx); - } - else if (clause.m_dts == dtt_old && dtt_old < dtt_new) { - clause.m_dts = dts(clause); - if (was_true && !clause.is_true()) - m_unsat.insert(ai.m_clause_idx); - if (was_true && clause.is_true() && clause.m_dts > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { - for (auto lit : clause.m_bools) { - if (is_true(lit)) { - inc_break(lit); - break; - } - } - } - } - SASSERT(clause.m_dts >= 0); + auto const& ai = *m_atoms[atm]; + auto const& cl = get_clause_info(ai.m_clause_idx); + // score += (dts(cl) - dts(cl, v, new_value)) * rational(cl.m_weight); } - vi.m_value = new_value; + return score; } - bool solver::sls::flip_arith(clause const& clause) { - rational new_value; - for (auto a : clause.m_arith) { - auto const& ai = m_atoms[a]; - ineq const& ineq = ai.m_ineq; - for (auto const& [coeff, v] : ineq.m_args) { - if (!ineq.is_true() && cm(ineq, v, new_value)) { - int score = cm_score(v, new_value); - if (score <= 0) + int solver::sls::cm_score(var_t v, rational const& new_value) { + int score = 0; + auto& vi = m_vars[v]; + for (auto const& [coeff, atm] : vi.m_atoms) { + auto const& ai = *m_atoms[atm]; + auto const& clause = get_clause_info(ai.m_clause_idx); + rational dtt_old = dtt(ai.m_ineq); + rational dtt_new = dtt(ai.m_ineq, v, new_value); + if (!clause.is_true()) { + if (dtt_new == 0) + ++score; + } + else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) + continue; + else { + bool has_true = false; + for (auto lit : *clause.m_clause) { + if (!atom(lit)) continue; - unsigned num_unsat = m_unsat.size(); - update(v, new_value); - std::cout << "score " << v << " " << score << "\n"; - std::cout << num_unsat << " -> " << m_unsat.size() << "\n"; - return true; + auto const& ai = *atom(lit); + rational d = dtt(ai.m_ineq, v, new_value); + has_true |= (d == 0); } + if (!has_true) + --score; } } - return false; + return score; } - - - rational solver::sls::dts(clause const& cl) const { + rational solver::sls::dts(unsigned cl) const { rational d(1), d2; bool first = true; - for (auto a : cl.m_arith) { - auto const& ai = m_atoms[a]; - d2 = dtt(ai.m_ineq); + for (auto a : get_clause(cl)) { + auto const* ai = atom(a); + if (!ai) + continue; + d2 = dtt(ai->m_ineq); if (first) d = d2, first = false; else @@ -289,12 +294,14 @@ namespace arith { return d; } - rational solver::sls::dts(clause const& cl, var_t v, rational const& new_value) const { + rational solver::sls::dts(unsigned cl, var_t v, rational const& new_value) const { rational d(1), d2; bool first = true; - for (auto a : cl.m_arith) { - auto const& ai = m_atoms[a]; - d2 = dtt(ai.m_ineq, v, new_value); + for (auto lit : get_clause(cl)) { + auto const* ai = atom(lit); + if (!ai) + continue; + d2 = dtt(ai->m_ineq, v, new_value); if (first) d = d2, first = false; else @@ -305,48 +312,85 @@ namespace arith { return d; } - // - // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) - // - rational solver::sls::dscore(var_t v, rational const& new_value) const { - auto const& vi = m_vars[v]; - rational score(0); - for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = m_atoms[atm]; - auto const& cl = m_clauses[ai.m_clause_idx]; - score += (cl.m_dts - dts(cl, v, new_value)) * rational(cl.m_weight); - } - return score; - } - - int solver::sls::cm_score(var_t v, rational const& new_value) { - int score = 0; + void solver::sls::update(var_t v, rational const& new_value) { auto& vi = m_vars[v]; + auto const& old_value = vi.m_value; for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = m_atoms[atm]; - auto const& clause = m_clauses[ai.m_clause_idx]; + auto& ai = *m_atoms[atm]; + SASSERT(!ai.m_is_bool); + auto& clause = get_clause_info(ai.m_clause_idx); rational dtt_old = dtt(ai.m_ineq); - rational dtt_new = dtt(ai.m_ineq, v, new_value); - if (!clause.is_true()) { - if (dtt_new == 0) - ++score; + ai.m_ineq.m_args_value += coeff * (new_value - old_value); + rational dtt_new = dtt(ai.m_ineq); + bool was_true = clause.is_true(); + auto& dts_value = dts(ai.m_clause_idx); + if (dtt_new < dts_value) { + if (was_true && dts_value > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { + for (auto lit : *clause.m_clause) { +#if false + TODO + if (is_true(lit)) { + dec_break(lit); + break; + } +#endif + } + } + dts_value = dtt_new; + if (!was_true && clause.is_true()) + unsat().remove(ai.m_clause_idx); } - else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) - continue; - else { - bool has_true = false; - for (auto a : clause.m_arith) { - auto const& ai = m_atoms[a]; - rational d = dtt(ai.m_ineq, v, new_value); - has_true |= (d == 0); + else if (dts_value == dtt_old && dtt_old < dtt_new) { + dts_value = dts(ai.m_clause_idx); + if (was_true && !clause.is_true()) + unsat().insert(ai.m_clause_idx); + if (was_true && clause.is_true() && dts_value > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { + for (auto lit : *clause.m_clause) { +#if false + TODO + if (is_true(lit)) { + inc_break(lit); + break; + } +#endif + } } - if (!has_true) - --score; } + SASSERT(dts_value >= 0); } - return score; + vi.m_value = new_value; + } + +#if 0 + + + + + + + void solver::sls::add_clause(sat::clause* cl) { + unsigned clause_idx = m_clauses.size(); + m_clauses.push_back({ cl, 1, rational::zero() }); + clause& cls = m_clauses.back(); + cls.m_dts = dts(cls); + for (sat::literal lit : *cl) { + if (is_true(lit)) + cls.add(lit); + } + + for (auto a : arith) + m_atoms[a].m_clause_idx = clause_idx; + + if (!cl.is_true()) { + m_best_min_unsat++; + m_unsat.insert(clause_idx); + } + else if (cl.m_dts > 0 && cl.m_num_trues == 1) + inc_break(sat::to_literal(cl.m_trues)); + } + #endif } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 1717b93d146..17207bd806b 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -30,6 +30,7 @@ Module Name: #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial.h" #include "sat/smt/sat_th.h" +#include "sat/sat_ddfw.h" namespace euf { class solver; @@ -197,6 +198,14 @@ namespace arith { typedef unsigned var_t; typedef unsigned atom_t; + struct config { + double cb = 0.0; + unsigned L = 20; + unsigned t = 45; + unsigned max_no_improve = 500000; + double sp = 0.0003; + }; + struct stats { unsigned m_num_flips = 0; }; @@ -237,26 +246,49 @@ namespace arith { unsigned m_breaks = 0; }; - solver& s; - ast_manager& m; + struct clause { + unsigned m_weight = 1; + rational m_dts = rational::one(); + }; + + solver& s; + ast_manager& m; + sat::ddfw* m_bool_search = nullptr; unsigned m_max_arith_steps = 0; + unsigned m_best_min_unsat = UINT_MAX; stats m_stats; - vector m_atoms; - vector m_vars; - + config m_config; + scoped_ptr_vector m_atoms; + vector m_vars; + vector m_clauses; + + indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } + unsigned num_clauses() const { return m_bool_search->num_clauses(); } + sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; } + sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } + sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } + sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } + + atom_info* atom(sat::literal lit) const { return m_atoms[lit.index()]; } + rational& dts(unsigned idx) { return m_clauses[idx].m_dts; } bool flip(); void log() {} - bool flip_unsat() { return false; } - bool flip_clauses() { return false; } - bool flip_dscore() { return false; } -// bool flip_dscore(clause const&); -// bool flip(clause const&); + bool flip_unsat(); + bool flip_clauses(); + bool flip_dscore(); + bool flip_dscore(unsigned cl); + bool flip(unsigned cl); rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } rational dtt(rational const& args, ineq const& ineq) const; rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; -// rational dts(clause const& cl, var_t v, rational const& new_value) const; -// rational dts(clause const& cl) const; + rational dts(unsigned cl, var_t v, rational const& new_value) const; + rational dts(unsigned cl) const; bool cm(ineq const& ineq, var_t v, rational& new_value); + int cm_score(var_t v, rational const& new_value); + void update(var_t v, rational const& new_value); + void paws(); + rational dscore(var_t v, rational const& new_value) const; + void save_best_values() {} rational value(var_t v) const { return m_vars[v].m_value; } public: @@ -265,6 +297,7 @@ namespace arith { void set_bounds_begin(); void set_bounds_end(unsigned num_literals); void set_bounds(enode* n); + void set(sat::ddfw* d) { m_bool_search = d; } }; sls m_local_search; @@ -590,6 +623,7 @@ namespace arith { void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } void set_bounds(enode* n) override { m_local_search.set_bounds(n); } void local_search(bool_vector& phase) override { m_local_search(phase); } + void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); } // bounds and equality propagation callbacks lp::lar_solver& lp() { return *m_solver; } diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 90889ca3ed1..4ee6490b7f4 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -31,19 +31,24 @@ namespace euf { unsigned max_rounds = 30; + for (auto* th : m_solvers) + th->set_bool_search(&bool_search); + for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { - setup_bounds(phase); + bool_search.reinit(s(), phase); + setup_bounds(phase); + // Non-boolean literals are assumptions to Boolean search - literal_vector _lits; + literal_vector assumptions; for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) - _lits.push_back(literal(v, !phase[v])); + assumptions.push_back(literal(v, !phase[v])); bool_search.rlimit().push(m_max_bool_steps); - lbool r = bool_search.check(_lits.size(), _lits.data(), nullptr); + lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); auto const& mdl = bool_search.get_model(); @@ -85,8 +90,6 @@ namespace euf { return phase[lit.var()] == !lit.sign(); }; - svector bin_clauses; - s().collect_bin_clauses(bin_clauses, false, false); for (auto* cp : s().clauses()) { if (any_of(*cp, [&](auto lit) { return is_true(lit); })) continue; @@ -95,14 +98,6 @@ namespace euf { init_literal(l); } - for (auto [l1, l2] : bin_clauses) { - if (is_true(l1) || is_true(l2)) - continue; - num_literals += 2; - init_literal(l1); - init_literal(l2); - }; - m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; for (auto* th : m_solvers) diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index 2101dfd6458..e226566b8f8 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -18,6 +18,7 @@ Module Name: #include "util/top_sort.h" #include "sat/smt/sat_smt.h" +#include "sat/sat_ddfw.h" #include "ast/euf/euf_egraph.h" #include "model/model.h" #include "smt/params/smt_params.h" @@ -139,6 +140,8 @@ namespace euf { /** * Local search interface */ + virtual void set_bool_search(sat::ddfw* ddfw) {} + virtual void set_bounds_begin() {} virtual void set_bounds_end(unsigned num_literals) {} From 46c8d78ecef31bb24577370400acda501c59522c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 09:32:53 -0800 Subject: [PATCH 403/597] fixes for #6577 - the literal false should not appear in clauses - the literal true forces a tautology - fix early return in is_cnf check. It should check all clauses for nested Booleans. --- src/api/api_goal.cpp | 2 +- src/ast/display_dimacs.cpp | 8 ++++++++ src/tactic/goal.cpp | 19 +++++++------------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index 086e8f302e5..cfe0974df0a 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -198,7 +198,7 @@ extern "C" { RESET_ERROR_CODE(); std::ostringstream buffer; if (!to_goal_ref(g)->is_cnf()) { - SET_ERROR_CODE(Z3_INVALID_ARG, "If this is not what you want, then preprocess by optional bit-blasting and applying tseitin-cnf"); + SET_ERROR_CODE(Z3_INVALID_ARG, "Goal is not converted into CNF. Preprocess by optional bit-blasting and applying tseitin-cnf"); RETURN_Z3(nullptr); } to_goal_ref(g)->display_dimacs(buffer, include_names); diff --git a/src/ast/display_dimacs.cpp b/src/ast/display_dimacs.cpp index 6b2ef6658dd..9440987ea6c 100644 --- a/src/ast/display_dimacs.cpp +++ b/src/ast/display_dimacs.cpp @@ -47,6 +47,8 @@ struct dimacs_pp { } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; + if (m.is_false(l)) + continue; if (m.is_not(l)) l = to_app(l)->get_arg(0); if (!is_uninterp_const(l)) @@ -101,6 +103,12 @@ struct dimacs_pp { } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; + if (m.is_false(l)) + continue; + if (m.is_true(l)) { + out << "1 -1 "; + continue; + } if (m.is_not(l)) { out << "-"; l = to_app(l)->get_arg(0); diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index b6fe76f6a88..23e3ff969eb 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -692,28 +692,23 @@ bool goal::is_cnf() const { for (unsigned i = 0; i < size(); i++) { expr * f = form(i); if (m_manager.is_or(f)) { - for (expr* lit : *to_app(f)) { - if (!is_literal(lit)) { + for (expr* lit : *to_app(f)) + if (!is_literal(lit)) return false; - } - } - return true; } - if (!is_literal(f)) { + if (!is_literal(f)) return false; - } } return true; } bool goal::is_literal(expr* f) const { m_manager.is_not(f, f); - if (!is_app(f)) return false; - if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) { + if (!is_app(f)) + return false; + if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) for (expr* arg : *to_app(f)) - if (m_manager.is_bool(arg)) { + if (m_manager.is_bool(arg)) return false; - } - } return true; } From 7bef2f3e6f312b979077ed53dfb4218659321b84 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 09:33:35 -0800 Subject: [PATCH 404/597] wip - local search for euf/arithmetic --- src/sat/sat_ddfw.cpp | 38 +-- src/sat/sat_ddfw.h | 26 +- src/sat/sat_local_search.cpp | 17 +- src/sat/sat_local_search.h | 16 +- src/sat/smt/CMakeLists.txt | 2 +- src/sat/smt/arith_sls.cpp | 511 +++++++++++++++++++++++++++++++ src/sat/smt/arith_sls.h | 147 +++++++++ src/sat/smt/arith_solver.h | 113 +------ src/sat/smt/euf_local_search.cpp | 18 +- 9 files changed, 716 insertions(+), 172 deletions(-) create mode 100644 src/sat/smt/arith_sls.cpp create mode 100644 src/sat/smt/arith_sls.h diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 74a0e7777ae..98e3ce2bd0f 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -49,6 +49,7 @@ namespace sat { else if (should_parallel_sync()) do_parallel_sync(); else shift_weights(); } + remove_assumptions(); log(); return m_min_sz == 0 ? l_true : l_undef; } @@ -244,7 +245,6 @@ namespace sat { m_use_list_index.push_back(m_flat_use_list.size()); } - void ddfw::flip(bool_var v) { ++m_flips; literal lit = literal(v, !value(v)); @@ -309,19 +309,15 @@ namespace sat { log(); if (m_reinit_count % 2 == 0) { - for (auto& ci : m_clauses) { - ci.m_weight += 1; - } + for (auto& ci : m_clauses) + ci.m_weight += 1; } else { - for (auto& ci : m_clauses) { - if (ci.is_true()) { - ci.m_weight = m_config.m_init_clause_weight; - } - else { - ci.m_weight = m_config.m_init_clause_weight + 1; - } - } + for (auto& ci : m_clauses) + if (ci.is_true()) + ci.m_weight = m_config.m_init_clause_weight; + else + ci.m_weight = m_config.m_init_clause_weight + 1; } init_clause_data(); ++m_reinit_count; @@ -341,11 +337,9 @@ namespace sat { clause const& c = get_clause(i); ci.m_trues = 0; ci.m_num_trues = 0; - for (literal lit : c) { - if (is_true(lit)) { - ci.add(lit); - } - } + for (literal lit : c) + if (is_true(lit)) + ci.add(lit); switch (ci.m_num_trues) { case 0: for (literal lit : c) { @@ -384,12 +378,10 @@ namespace sat { void ddfw::reinit_values() { for (unsigned i = 0; i < num_vars(); ++i) { int b = bias(i); - if (0 == (m_rand() % (1 + abs(b)))) { - value(i) = (m_rand() % 2) == 0; - } - else { - value(i) = bias(i) > 0; - } + if (0 == (m_rand() % (1 + abs(b)))) + value(i) = (m_rand() % 2) == 0; + else + value(i) = bias(i) > 0; } } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 7dd69de8103..9971033e3f6 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -43,6 +43,17 @@ namespace sat { void add(literal lit) { ++m_num_trues; m_trues += lit.index(); } void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); } }; + + class use_list { + ddfw& p; + unsigned i; + public: + use_list(ddfw& p, literal lit) : + p(p), i(lit.index()) {} + unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; } + unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; } + }; + protected: struct config { @@ -102,15 +113,7 @@ namespace sat { parallel* m_par; - class use_list { - ddfw& p; - unsigned i; - public: - use_list(ddfw& p, literal lit): - p(p), i(lit.index()) {} - unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; } - unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; } - }; + void flatten_use_list(); @@ -163,7 +166,6 @@ namespace sat { // flip activity bool do_flip(); bool_var pick_var(); - void flip(bool_var v); void save_best_values(); void save_model(); void save_priorities(); @@ -245,6 +247,10 @@ namespace sat { void remove_assumptions(); + void flip(bool_var v); + + use_list get_use_list(literal lit) { return use_list(*this, lit); } + }; } diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index 8cc90f05e6a..c3cb0fb37b1 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -353,10 +353,7 @@ namespace sat { DEBUG_CODE(verify_unsat_stack();); } - local_search::local_search() : - m_is_unsat(false), - m_initializing(false), - m_par(nullptr) { + local_search::local_search() { } void local_search::reinit(solver& s, bool_vector const& phase) { @@ -375,11 +372,10 @@ namespace sat { m_vars.reserve(s.num_vars()); m_config.set_config(s.get_config()); - if (m_config.phase_sticky()) { - unsigned v = 0; + unsigned v = 0; + if (m_config.phase_sticky()) for (var_info& vi : m_vars) - vi.m_bias = s.m_phase[v++] ? 98 : 2; - } + vi.m_bias = s.m_phase[v++] ? 98 : 2; // copy units unsigned trail_sz = s.init_trail_size(); @@ -419,9 +415,8 @@ namespace sat { if (ext && (!ext->is_pb() || !ext->extract_pb(card, pb))) throw default_exception("local search is incomplete with extensions beyond PB"); - if (_init) { - init(); - } + if (_init) + init(); } local_search::~local_search() { diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h index 7295b851a1b..b6223452285 100644 --- a/src/sat/sat_local_search.h +++ b/src/sat/sat_local_search.h @@ -133,21 +133,21 @@ namespace sat { vector m_constraints; // all constraints literal_vector m_assumptions; // temporary assumptions literal_vector m_prop_queue; // propagation queue - unsigned m_num_non_binary_clauses; - bool m_is_pb; - bool m_is_unsat; + unsigned m_num_non_binary_clauses = 0; + bool m_is_pb = false; + bool m_is_unsat = false; unsigned_vector m_unsat_stack; // store all the unsat constraints unsigned_vector m_index_in_unsat_stack; // which position is a constraint in the unsat_stack // configuration changed decreasing variables (score>0 and conf_change==true) bool_var_vector m_goodvar_stack; - bool m_initializing; + bool m_initializing = false; // information about solution - unsigned m_best_unsat; - double m_best_unsat_rate; - double m_last_best_unsat_rate; + unsigned m_best_unsat = 0; + double m_best_unsat_rate = 0; + double m_last_best_unsat_rate = 0; // for non-known instance, set as maximal int m_best_known_value = INT_MAX; // best known value for this instance @@ -159,7 +159,7 @@ namespace sat { reslimit m_limit; random_gen m_rand; - parallel* m_par; + parallel* m_par = nullptr; model m_model; inline int score(bool_var v) const { return m_vars[v].m_score; } diff --git a/src/sat/smt/CMakeLists.txt b/src/sat/smt/CMakeLists.txt index dbbfc385626..4a899ca9d8e 100644 --- a/src/sat/smt/CMakeLists.txt +++ b/src/sat/smt/CMakeLists.txt @@ -3,7 +3,7 @@ z3_add_component(sat_smt arith_axioms.cpp arith_diagnostics.cpp arith_internalize.cpp - arith_local_search.cpp + arith_sls.cpp arith_solver.cpp array_axioms.cpp array_diagnostics.cpp diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp new file mode 100644 index 00000000000..460bdedce81 --- /dev/null +++ b/src/sat/smt/arith_sls.cpp @@ -0,0 +1,511 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + arith_local_search.cpp + +Abstract: + + Local search dispatch for SMT + +Author: + + Nikolaj Bjorner (nbjorner) 2023-02-07 + +--*/ +#include "sat/sat_solver.h" +#include "sat/smt/arith_solver.h" + + +namespace arith { + + + /// + /// need to initialize ineqs (arithmetical atoms) + /// + + + sls::sls(solver& s): + s(s), m(s.m) {} + + void sls::operator()(bool_vector& phase) { + + m_best_min_unsat = unsat().size(); + unsigned num_steps = 0; + while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { + if (!flip()) + break; + ++m_stats.m_num_flips; + ++num_steps; + unsigned num_unsat = unsat().size(); + if (num_unsat < m_best_min_unsat) { + m_best_min_unsat = num_unsat; + num_steps = 0; + save_best_values(); + } + } + IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); + } + + void sls::save_best_values() { + // first compute assignment to terms + // then update non-basic variables in tableau, assuming a sat solution was found. +#if false + for (auto const& [t, v] : terms) { + rational val; + lp::lar_term const& term = lp().get_term(t); + for (lp::lar_term::ival arg : term) { + auto t2 = lp().column2tv(arg.column()); + auto w = lp().local_to_external(t2.id()); + val += arg.coeff() * local_search.value(w); + } + update(v, val); + } +#endif + + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + if (s.is_bool(v)) + continue; + if (!s.lp().external_is_used(v)) + continue; + rational old_value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); + rational new_value = value(v); + if (old_value == new_value) + continue; + s.ensure_column(v); + lp::column_index vj = s.lp().to_column_index(v); + SASSERT(!vj.is_null()); + if (!s.lp().is_base(vj.index())) { + lp::impq val(new_value); + s.lp().set_value_for_nbasic_column(vj.index(), val); + } + } + } + + void sls::set(sat::ddfw* d) { + m_bool_search = d; + add_vars(); + m_clauses.resize(d->num_clauses()); + for (unsigned i = 0; i < d->num_clauses(); ++i) + for (sat::literal lit : *d->get_clause_info(i).m_clause) + init_literal(lit); + } + + void sls::set_bounds_begin() { + m_max_arith_steps = 0; + } + + void sls::set_bounds(enode* n) { + ++m_max_arith_steps; + } + + void sls::set_bounds_end(unsigned num_literals) { + m_max_arith_steps = (m_config.L * m_max_arith_steps) / num_literals; + } + + bool sls::flip() { + log(); + return flip_unsat() || flip_clauses() || flip_dscore(); + } + + // distance to true + rational sls::dtt(rational const& args, ineq const& ineq) const { + switch (ineq.m_op) { + case ineq_kind::LE: + if (args <= ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound; + case ineq_kind::EQ: + if (args == ineq.m_bound) + return rational::zero(); + return rational::one(); + case ineq_kind::NE: + if (args == ineq.m_bound) + return rational::one(); + return rational::zero(); + case ineq_kind::LT: + if (args < ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound + 1; + default: + UNREACHABLE(); + return rational::zero(); + } + } + + rational sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { + auto new_args_value = ineq.m_args_value; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + new_args_value += coeff * (new_value - m_vars[w].m_value); + break; + } + } + return dtt(new_args_value, ineq); + } + + // critical move + bool sls::cm(ineq const& ineq, var_t v, rational& new_value) { + SASSERT(!ineq.is_true()); + auto delta = ineq.m_args_value - ineq.m_bound; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + if (coeff > 0) + new_value = value(v) - abs(ceil(delta / coeff)); + else + new_value = value(v) + abs(floor(delta / coeff)); + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(delta + coeff * (new_value - value(v)) <= 0); + return true; + case ineq_kind::EQ: + return delta + coeff * (new_value - value(v)) == 0; + case ineq_kind::NE: + return delta + coeff * (new_value - value(v)) != 0; + case ineq_kind::LT: + return delta + coeff * (new_value - value(v)) < 0; + default: + UNREACHABLE(); + break; + } + } + } + return false; + } + + bool sls::flip_unsat() { + unsigned start = s.random(); + unsigned sz = unsat().size(); + for (unsigned i = sz; i-- > 0; ) + if (flip(unsat().elem_at((i + start) % sz))) + return true; + return false; + } + + bool sls::flip(unsigned cl) { + auto const& clause = get_clause(cl); + rational new_value; + for (literal lit : clause) { + auto const* ineq = atom(lit); + if (!ineq || ineq->is_true()) + continue; + for (auto const& [coeff, v] : ineq->m_args) { + if (!cm(*ineq, v, new_value)) + continue; + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = unsat().size(); + update(v, new_value); + IF_VERBOSE(2, + verbose_stream() << "score " << v << " " << score << "\n" + << num_unsat << " -> " << unsat().size() << "\n"); + return true; + } + } + return false; + } + + bool sls::flip_clauses() { + unsigned start = s.random(); + unsigned sz = m_bool_search->num_clauses(); + for (unsigned i = sz; i-- > 0; ) + if (flip((i + start) % sz)) + return true; + return false; + } + + bool sls::flip_dscore() { + paws(); + unsigned start = s.random(); + unsigned sz = unsat().size(); + for (unsigned i = sz; i-- > 0; ) + if (flip_dscore(unsat().elem_at((i + start) % sz))) + return true; + return false; + } + + bool sls::flip_dscore(unsigned cl) { + auto const& clause = get_clause(cl); + rational new_value, min_value, min_score(-1); + var_t min_var = UINT_MAX; + for (auto lit : clause) { + auto const* ineq = atom(lit); + if (!ineq || ineq->is_true()) + continue; + for (auto const& [coeff, v] : ineq->m_args) { + if (cm(*ineq, v, new_value)) { + rational score = dscore(v, new_value); + if (UINT_MAX == min_var || score < min_score) { + min_var = v; + min_value = new_value; + min_score = score; + } + } + } + } + if (min_var != UINT_MAX) { + update(min_var, min_value); + return true; + } + return false; + } + + /** + * redistribute weights of clauses. TODO - re-use ddfw weights instead. + */ + void sls::paws() { + for (unsigned cl = num_clauses(); cl-- > 0; ) { + auto& clause = get_clause_info(cl); + bool above = 10000 * m_config.sp <= (s.random() % 10000); + if (!above && clause.is_true() && get_weight(cl) > 1) + get_weight(cl) -= 1; + if (above && !clause.is_true()) + get_weight(cl) += 1; + } + } + + // + // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) + // + rational sls::dscore(var_t v, rational const& new_value) const { + auto const& vi = m_vars[v]; + rational score(0); + for (auto const& [coeff, lit] : vi.m_literals) + for (auto cl : m_bool_search->get_use_list(lit)) + score += (dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); + return score; + } + + int sls::cm_score(var_t v, rational const& new_value) { + int score = 0; + auto& vi = m_vars[v]; + for (auto const& [coeff, lit] : vi.m_literals) { + auto const& ineq = *atom(lit); + rational dtt_old = dtt(ineq); + rational dtt_new = dtt(ineq, v, new_value); + for (auto cl : m_bool_search->get_use_list(lit)) { + auto const& clause = get_clause_info(cl); + if (!clause.is_true()) { + if (dtt_new == 0) + ++score; // false -> true + } + else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) // true -> true ?? TODO + continue; + else if (all_of(*clause.m_clause, [&](auto lit) { return !atom(lit) || dtt(*atom(lit), v, new_value) > 0; })) // ?? TODO + --score; + } + } + return score; + } + + rational sls::dts(unsigned cl) const { + rational d(1), d2; + bool first = true; + for (auto a : get_clause(cl)) { + auto const* ineq = atom(a); + if (!ineq) + continue; + d2 = dtt(*ineq); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + rational sls::dts(unsigned cl, var_t v, rational const& new_value) const { + rational d(1), d2; + bool first = true; + for (auto lit : get_clause(cl)) { + auto const* ineq = atom(lit); + if (!ineq) + continue; + d2 = dtt(*ineq, v, new_value); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + void sls::update(var_t v, rational const& new_value) { + auto& vi = m_vars[v]; + auto const& old_value = vi.m_value; + for (auto const& [coeff, lit] : vi.m_literals) { + auto& ineq = *atom(lit); + rational dtt_old = dtt(ineq); + ineq.m_args_value += coeff * (new_value - old_value); + rational dtt_new = dtt(ineq); + SASSERT(!(dtt_new == 0 && dtt_new < dtt_old) || m_bool_search->get_value(lit.var()) == lit.sign()); + SASSERT(!(dtt_old == 0 && dtt_new > dtt_old) || m_bool_search->get_value(lit.var()) != lit.sign()); + if (dtt_new == 0 && dtt_new < dtt_old) // flip from false to true + m_bool_search->flip(lit.var()); + else if (dtt_old == 0 && dtt_old < dtt_new) // flip from true to false + m_bool_search->flip(lit.var()); + dtt(ineq) = dtt_new; + SASSERT((dtt_new == 0) == (m_bool_search->get_value(lit.var()) != lit.sign())); + } + vi.m_value = new_value; + } + + void sls::add_vars() { + SASSERT(m_vars.empty()); + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + rational value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); + value = s.is_int(v) ? ceil(value) : value; + auto k = s.is_int(v) ? sls::var_kind::INT : sls::var_kind::REAL; + m_vars.push_back({ value, value, k, {} }); + } + } + + sls::ineq& sls::new_ineq(ineq_kind op, rational const& bound) { + auto* i = alloc(ineq); + i->m_bound = bound; + i->m_op = op; + return *i; + } + + void sls::add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v) { + ineq.m_args.push_back({ c, v }); + ineq.m_args_value += c * value(v); + m_vars[v].m_literals.push_back({ c, lit }); + } + + void sls::add_bounds(sat::literal_vector& bounds) { + unsigned bvars = s.s().num_vars(); + auto add_ineq = [&](sat::literal lit, ineq& i) { + m_literals.set(lit.index(), &i); + bounds.push_back(lit); + }; + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + rational lo, hi; + bool is_strict_lo = false, is_strict_hi = false; + lp::constraint_index ci; + if (!s.is_registered_var(v)) + continue; + lp::column_index vi = s.lp().to_column_index(v); + if (vi.is_null()) + continue; + bool has_lo = s.lp().has_lower_bound(vi.index(), ci, lo, is_strict_lo); + bool has_hi = s.lp().has_upper_bound(vi.index(), ci, hi, is_strict_hi); + + if (has_lo && has_hi && lo == hi) { + auto& ineq = new_ineq(sls::ineq_kind::EQ, lo); + sat::literal lit(bvars++); + add_arg(lit, ineq, rational::one(), v); + add_ineq(lit, ineq); + continue; + } + if (has_lo) { + auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, -lo); + sat::literal lit(bvars++); + add_arg(lit, ineq, -rational::one(), v); + add_ineq(lit, ineq); + } + if (has_hi) { + auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, hi); + sat::literal lit(bvars++); + add_arg(lit, ineq, rational::one(), v); + add_ineq(lit, ineq); + } + } + } + + + void sls::add_args(ineq& ineq, lp::tv t, theory_var v, rational sign) { + if (t.is_term()) { + lp::lar_term const& term = s.lp().get_term(t); + + for (lp::lar_term::ival arg : term) { + auto t2 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + ineq.m_args.push_back({ sign * arg.coeff(), w }); + } + } + else + ineq.m_args.push_back({ sign, s.lp().local_to_external(t.id()) }); + } + + + void sls::init_literal(sat::literal lit) { + if (m_literals.get(lit.index(), nullptr)) + return; + api_bound* b = nullptr; + s.m_bool_var2bound.find(lit.var(), b); + if (b) { + auto t = b->tv(); + rational bound = b->get_value(); + bool should_minus = false; + sls::ineq_kind op; + if (!lit.sign()) { + should_minus = b->get_bound_kind() == lp::GE; + op = sls::ineq_kind::LE; + } + else { + should_minus = b->get_bound_kind() == lp::LE; + if (s.is_int(b->get_var())) { + bound -= 1; + op = sls::ineq_kind::LE; + } + else + op = sls::ineq_kind::LT; + + } + if (should_minus) + bound.neg(); + auto& ineq = new_ineq(op, bound); + + add_args(ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); + set_literal(lit, ineq); + return; + } + + expr* e = s.bool_var2expr(lit.var()); + expr* l = nullptr, * r = nullptr; + if (e && m.is_eq(e, l, r) && s.a.is_int_real(l)) { + theory_var u = s.get_th_var(l); + theory_var v = s.get_th_var(r); + lp::tv tu = s.get_tv(u); + lp::tv tv = s.get_tv(v); + auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); + add_args(ineq, tu, u, rational::one()); + add_args(ineq, tv, v, -rational::one()); + set_literal(lit, ineq); + return; + } + } + + /** + * Associate literal with inequality and synchronize truth assignment based on arithmetic values. + */ + void sls::set_literal(sat::literal lit, ineq& ineq) { + m_literals.set(lit.index(), &ineq); + if (m_bool_search->get_value(lit.var())) { + if (dtt(ineq) != 0) + m_bool_search->flip(lit.var()); + } + else { + if (dtt(ineq) == 0) + m_bool_search->flip(lit.var()); + } + } + +#if 0 + + { + + } +} + + +#endif +} + diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h new file mode 100644 index 00000000000..dcb9356105e --- /dev/null +++ b/src/sat/smt/arith_sls.h @@ -0,0 +1,147 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + arith_local_search.h + +Abstract: + + Theory plugin for arithmetic local search + +Author: + + Nikolaj Bjorner (nbjorner) 2020-09-08 + +--*/ +#pragma once + +#include "util/obj_pair_set.h" +#include "ast/ast_trail.h" +#include "ast/arith_decl_plugin.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" +#include "math/lp/indexed_value.h" +#include "math/lp/lar_solver.h" +#include "math/lp/nla_solver.h" +#include "math/lp/lp_types.h" +#include "math/lp/lp_api.h" +#include "math/polynomial/algebraic_numbers.h" +#include "math/polynomial/polynomial.h" +#include "sat/smt/sat_th.h" +#include "sat/sat_ddfw.h" + +namespace arith { + + class solver; + + // local search portion for arithmetic + class sls { + enum class ineq_kind { EQ, LE, LT, NE }; + enum class var_kind { INT, REAL }; + typedef unsigned var_t; + typedef unsigned atom_t; + + struct config { + double cb = 0.0; + unsigned L = 20; + unsigned t = 45; + unsigned max_no_improve = 500000; + double sp = 0.0003; + }; + + struct stats { + unsigned m_num_flips = 0; + }; + + // encode args <= bound, args = bound, args < bound + struct ineq { + vector> m_args; + ineq_kind m_op = ineq_kind::LE; + rational m_bound; + rational m_args_value; + + bool is_true() const { + switch (m_op) { + case ineq_kind::LE: + return m_args_value <= m_bound; + case ineq_kind::EQ: + return m_args_value == m_bound; + case ineq_kind::NE: + return m_args_value != m_bound; + default: + return m_args_value < m_bound; + } + } + }; + + struct var_info { + rational m_value; + rational m_best_value; + var_kind m_kind = var_kind::INT; + vector> m_literals; + }; + + struct clause { + unsigned m_weight = 1; + }; + + solver& s; + ast_manager& m; + sat::ddfw* m_bool_search = nullptr; + unsigned m_max_arith_steps = 0; + unsigned m_best_min_unsat = UINT_MAX; + stats m_stats; + config m_config; + scoped_ptr_vector m_literals; + vector m_vars; + vector m_clauses; + + indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } + unsigned num_clauses() const { return m_bool_search->num_clauses(); } + sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; } + sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } + sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } + sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } + + ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } + unsigned& get_weight(unsigned idx) { return m_clauses[idx].m_weight; } + unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } + bool flip(); + void log() {} + bool flip_unsat(); + bool flip_clauses(); + bool flip_dscore(); + bool flip_dscore(unsigned cl); + bool flip(unsigned cl); + rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } + rational dtt(rational const& args, ineq const& ineq) const; + rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; + rational dts(unsigned cl, var_t v, rational const& new_value) const; + rational dts(unsigned cl) const; + bool cm(ineq const& ineq, var_t v, rational& new_value); + int cm_score(var_t v, rational const& new_value); + void update(var_t v, rational const& new_value); + void paws(); + rational dscore(var_t v, rational const& new_value) const; + void save_best_values(); + void add_vars(); + sls::ineq& new_ineq(ineq_kind op, rational const& bound); + void add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v); + void add_bounds(sat::literal_vector& bounds); + void add_args(ineq& ineq, lp::tv t, euf::theory_var v, rational sign); + void init_literal(sat::literal lit); + void set_literal(sat::literal lit, ineq& ineq); + + rational value(var_t v) const { return m_vars[v].m_value; } + public: + sls(solver& s); + void operator ()(bool_vector& phase); + void set_bounds_begin(); + void set_bounds_end(unsigned num_literals); + void set_bounds(euf::enode* n); + void set(sat::ddfw* d); + }; + +} diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 17207bd806b..732e291b10f 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -30,6 +30,7 @@ Module Name: #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial.h" #include "sat/smt/sat_th.h" +#include "sat/smt/arith_sls.h" #include "sat/sat_ddfw.h" namespace euf { @@ -98,6 +99,7 @@ namespace arith { class solver : public euf::th_euf_solver { friend struct arith_proof_hint; + friend class sls; struct scope { unsigned m_bounds_lim; @@ -190,116 +192,7 @@ namespace arith { coeffs().pop_back(); } }; - - // local search portion for arithmetic - class sls { - enum class ineq_kind { EQ, LE, LT, NE }; - enum class var_kind { INT, REAL }; - typedef unsigned var_t; - typedef unsigned atom_t; - - struct config { - double cb = 0.0; - unsigned L = 20; - unsigned t = 45; - unsigned max_no_improve = 500000; - double sp = 0.0003; - }; - - struct stats { - unsigned m_num_flips = 0; - }; - // encode args <= bound, args = bound, args < bound - struct ineq { - vector> m_args; - ineq_kind m_op = ineq_kind::LE; - rational m_bound; - rational m_args_value; - - bool is_true() const { - switch (m_op) { - case ineq_kind::LE: - return m_args_value <= m_bound; - case ineq_kind::EQ: - return m_args_value == m_bound; - case ineq_kind::NE: - return m_args_value != m_bound; - default: - return m_args_value < m_bound; - } - } - }; - - struct var_info { - rational m_value; - rational m_best_value; - var_kind m_kind = var_kind::INT; - vector> m_atoms; - }; - - struct atom_info { - ineq m_ineq; - unsigned m_clause_idx; - bool m_is_bool = false; - bool m_phase = false; - bool m_best_phase = false; - unsigned m_breaks = 0; - }; - - struct clause { - unsigned m_weight = 1; - rational m_dts = rational::one(); - }; - - solver& s; - ast_manager& m; - sat::ddfw* m_bool_search = nullptr; - unsigned m_max_arith_steps = 0; - unsigned m_best_min_unsat = UINT_MAX; - stats m_stats; - config m_config; - scoped_ptr_vector m_atoms; - vector m_vars; - vector m_clauses; - - indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } - unsigned num_clauses() const { return m_bool_search->num_clauses(); } - sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; } - sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } - sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } - sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } - - atom_info* atom(sat::literal lit) const { return m_atoms[lit.index()]; } - rational& dts(unsigned idx) { return m_clauses[idx].m_dts; } - bool flip(); - void log() {} - bool flip_unsat(); - bool flip_clauses(); - bool flip_dscore(); - bool flip_dscore(unsigned cl); - bool flip(unsigned cl); - rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } - rational dtt(rational const& args, ineq const& ineq) const; - rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; - rational dts(unsigned cl, var_t v, rational const& new_value) const; - rational dts(unsigned cl) const; - bool cm(ineq const& ineq, var_t v, rational& new_value); - int cm_score(var_t v, rational const& new_value); - void update(var_t v, rational const& new_value); - void paws(); - rational dscore(var_t v, rational const& new_value) const; - void save_best_values() {} - - rational value(var_t v) const { return m_vars[v].m_value; } - public: - sls(solver& s); - void operator ()(bool_vector& phase); - void set_bounds_begin(); - void set_bounds_end(unsigned num_literals); - void set_bounds(enode* n); - void set(sat::ddfw* d) { m_bool_search = d; } - }; - + sls m_local_search; typedef vector> var_coeffs; diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 4ee6490b7f4..873a64b7e58 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -24,7 +24,7 @@ namespace euf { void solver::local_search(bool_vector& phase) { scoped_limits scoped_rl(m.limit()); sat::ddfw bool_search; - bool_search.add(s()); + bool_search.reinit(s(), phase); bool_search.updt_params(s().params()); bool_search.set_seed(rand()); scoped_rl.push_child(&(bool_search.rlimit())); @@ -36,29 +36,29 @@ namespace euf { for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { - bool_search.reinit(s(), phase); - setup_bounds(phase); // Non-boolean literals are assumptions to Boolean search literal_vector assumptions; for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) - assumptions.push_back(literal(v, !phase[v])); + assumptions.push_back(literal(v, !bool_search.get_value(v))); bool_search.rlimit().push(m_max_bool_steps); lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); - - - auto const& mdl = bool_search.get_model(); - for (unsigned i = 0; i < mdl.size(); ++i) - phase[i] = mdl[i] == l_true; + bool_search.rlimit().pop(); for (auto* th : m_solvers) th->local_search(phase); // if is_sat break; + } + + + auto const& mdl = bool_search.get_model(); + for (unsigned i = 0; i < mdl.size(); ++i) + phase[i] = mdl[i] == l_true; } From 4b2c166e8b749f55e420b08653f3e7b7fbb919d6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 10:19:24 -0800 Subject: [PATCH 405/597] fixes to build Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_local_search.cpp | 396 ----------------------------- src/sat/smt/arith_sls.cpp | 40 +-- src/sat/smt/arith_sls.h | 3 +- 3 files changed, 16 insertions(+), 423 deletions(-) delete mode 100644 src/sat/smt/arith_local_search.cpp diff --git a/src/sat/smt/arith_local_search.cpp b/src/sat/smt/arith_local_search.cpp deleted file mode 100644 index 787280e7346..00000000000 --- a/src/sat/smt/arith_local_search.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/*++ -Copyright (c) 2020 Microsoft Corporation - -Module Name: - - arith_local_search.cpp - -Abstract: - - Local search dispatch for SMT - -Author: - - Nikolaj Bjorner (nbjorner) 2023-02-07 - ---*/ -#include "sat/sat_solver.h" -#include "sat/smt/arith_solver.h" - - -namespace arith { - - - /// - /// need access to clauses - /// need access to m_unsat - /// need update of phase - /// need to initialize ineqs (arithmetical atoms) - /// - - solver::sls::sls(solver& s): - s(s), m(s.m) {} - - void solver::sls::operator()(bool_vector& phase) { - - // need to init variables/atoms/ineqs - - m.limit().push(m_max_arith_steps); - m_best_min_unsat = unsat().size(); - unsigned num_steps = 0; - while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { - if (!flip()) - return; - ++m_stats.m_num_flips; - ++num_steps; - unsigned num_unsat = unsat().size(); - if (num_unsat < m_best_min_unsat) { - m_best_min_unsat = num_unsat; - num_steps = 0; - save_best_values(); - } - } - } - - void solver::sls::set_bounds_begin() { - m_max_arith_steps = 0; - } - - void solver::sls::set_bounds_end(unsigned num_literals) { - // m_max_arith_steps = s.ctx.m_sl_config.L * - } - - void solver::sls::set_bounds(enode* n) { - ++m_max_arith_steps; - } - - bool solver::sls::flip() { - log(); - if (flip_unsat()) - return true; - if (flip_clauses()) - return true; - if (flip_dscore()) - return true; - return false; - } - - // distance to true - rational solver::sls::dtt(rational const& args, ineq const& ineq) const { - switch (ineq.m_op) { - case ineq_kind::LE: - if (args <= ineq.m_bound) - return rational::zero(); - return args - ineq.m_bound; - case ineq_kind::EQ: - if (args == ineq.m_bound) - return rational::zero(); - return rational::one(); - case ineq_kind::NE: - if (args == ineq.m_bound) - return rational::one(); - return rational::zero(); - case ineq_kind::LT: - default: - if (args < ineq.m_bound) - return rational::zero(); - return args - ineq.m_bound + 1; - } - } - - rational solver::sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { - auto new_args_value = ineq.m_args_value; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { - new_args_value += coeff * (new_value - m_vars[w].m_value); - break; - } - } - return dtt(new_args_value, ineq); - } - - // critical move - bool solver::sls::cm(ineq const& ineq, var_t v, rational& new_value) { - SASSERT(!ineq.is_true()); - auto delta = ineq.m_args_value - ineq.m_bound; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { - if (coeff > 0) - new_value = value(v) - abs(ceil(delta / coeff)); - else - new_value = value(v) + abs(floor(delta / coeff)); - switch (ineq.m_op) { - case ineq_kind::LE: - SASSERT(delta + coeff * (new_value - value(v)) <= 0); - return true; - case ineq_kind::EQ: - return delta + coeff * (new_value - value(v)) == 0; - case ineq_kind::NE: - return delta + coeff * (new_value - value(v)) != 0; - case ineq_kind::LT: - return delta + coeff * (new_value - value(v)) < 0; - default: - UNREACHABLE(); break; - } - } - } - return false; - } - - bool solver::sls::flip_unsat() { - unsigned start = s.random(); - unsigned sz = unsat().size(); - for (unsigned i = sz; i-- > 0; ) { - unsigned cl = unsat().elem_at((i + start) % sz); - if (flip(cl)) - return true; - } - return false; - } - - - bool solver::sls::flip(unsigned cl) { - auto const& clause = get_clause(cl); - rational new_value; - for (literal lit : clause) { - auto const* ai = atom(lit); - if (!ai) - continue; - ineq const& ineq = ai->m_ineq; - for (auto const& [coeff, v] : ineq.m_args) { - if (!ineq.is_true() && cm(ineq, v, new_value)) { - int score = cm_score(v, new_value); - if (score <= 0) - continue; - unsigned num_unsat = unsat().size(); - update(v, new_value); - IF_VERBOSE(0, - verbose_stream() << "score " << v << " " << score << "\n" - << num_unsat << " -> " << unsat().size() << "\n"); - return true; - } - } - } - return false; - } - - bool solver::sls::flip_clauses() { - unsigned start = s.random(); - for (unsigned i = num_clauses(); i-- > 0; ) - if (flip((i + start) % num_clauses())) - return true; - return false; - } - - bool solver::sls::flip_dscore() { - paws(); - unsigned start = s.random(); - for (unsigned i = unsat().size(); i-- > 0; ) { - unsigned cl = unsat().elem_at((i + start) % unsat().size()); - if (flip_dscore(cl)) - return true; - } - IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); - return false; - } - - bool solver::sls::flip_dscore(unsigned cl) { - auto const& clause = get_clause(cl); - rational new_value, min_value, min_score(-1); - var_t min_var = UINT_MAX; - for (auto lit : clause) { - auto const* ai = atom(lit); - if (!ai) - continue; - ineq const& ineq = ai->m_ineq; - for (auto const& [coeff, v] : ineq.m_args) { - if (!ineq.is_true() && cm(ineq, v, new_value)) { - rational score = dscore(v, new_value); - if (UINT_MAX == min_var || score < min_score) { - min_var = v; - min_value = new_value; - min_score = score; - } - } - } - } - if (min_var != UINT_MAX) { - update(min_var, min_value); - return true; - } - return false; - } - - void solver::sls::paws() { - for (unsigned cl = num_clauses(); cl-- > 0; ) { - auto& clause = get_clause_info(cl); - bool above = 10000 * m_config.sp <= (s.random() % 10000); - if (!above && clause.is_true() && clause.m_weight > 1) - clause.m_weight -= 1; - if (above && !clause.is_true()) - clause.m_weight += 1; - } - } - - // - // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) - // - rational solver::sls::dscore(var_t v, rational const& new_value) const { - auto const& vi = m_vars[v]; - rational score(0); - for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = *m_atoms[atm]; - auto const& cl = get_clause_info(ai.m_clause_idx); - // score += (dts(cl) - dts(cl, v, new_value)) * rational(cl.m_weight); - } - return score; - } - - int solver::sls::cm_score(var_t v, rational const& new_value) { - int score = 0; - auto& vi = m_vars[v]; - for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = *m_atoms[atm]; - auto const& clause = get_clause_info(ai.m_clause_idx); - rational dtt_old = dtt(ai.m_ineq); - rational dtt_new = dtt(ai.m_ineq, v, new_value); - if (!clause.is_true()) { - if (dtt_new == 0) - ++score; - } - else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) - continue; - else { - bool has_true = false; - for (auto lit : *clause.m_clause) { - if (!atom(lit)) - continue; - auto const& ai = *atom(lit); - rational d = dtt(ai.m_ineq, v, new_value); - has_true |= (d == 0); - } - if (!has_true) - --score; - } - } - return score; - } - - rational solver::sls::dts(unsigned cl) const { - rational d(1), d2; - bool first = true; - for (auto a : get_clause(cl)) { - auto const* ai = atom(a); - if (!ai) - continue; - d2 = dtt(ai->m_ineq); - if (first) - d = d2, first = false; - else - d = std::min(d, d2); - if (d == 0) - break; - } - return d; - } - - rational solver::sls::dts(unsigned cl, var_t v, rational const& new_value) const { - rational d(1), d2; - bool first = true; - for (auto lit : get_clause(cl)) { - auto const* ai = atom(lit); - if (!ai) - continue; - d2 = dtt(ai->m_ineq, v, new_value); - if (first) - d = d2, first = false; - else - d = std::min(d, d2); - if (d == 0) - break; - } - return d; - } - - void solver::sls::update(var_t v, rational const& new_value) { - auto& vi = m_vars[v]; - auto const& old_value = vi.m_value; - for (auto const& [coeff, atm] : vi.m_atoms) { - auto& ai = *m_atoms[atm]; - SASSERT(!ai.m_is_bool); - auto& clause = get_clause_info(ai.m_clause_idx); - rational dtt_old = dtt(ai.m_ineq); - ai.m_ineq.m_args_value += coeff * (new_value - old_value); - rational dtt_new = dtt(ai.m_ineq); - bool was_true = clause.is_true(); - auto& dts_value = dts(ai.m_clause_idx); - if (dtt_new < dts_value) { - if (was_true && dts_value > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { - for (auto lit : *clause.m_clause) { -#if false - TODO - if (is_true(lit)) { - dec_break(lit); - break; - } -#endif - } - } - dts_value = dtt_new; - if (!was_true && clause.is_true()) - unsat().remove(ai.m_clause_idx); - } - else if (dts_value == dtt_old && dtt_old < dtt_new) { - dts_value = dts(ai.m_clause_idx); - if (was_true && !clause.is_true()) - unsat().insert(ai.m_clause_idx); - if (was_true && clause.is_true() && dts_value > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { - for (auto lit : *clause.m_clause) { -#if false - TODO - if (is_true(lit)) { - inc_break(lit); - break; - } -#endif - } - } - } - SASSERT(dts_value >= 0); - } - vi.m_value = new_value; - } - -#if 0 - - - - - - - void solver::sls::add_clause(sat::clause* cl) { - unsigned clause_idx = m_clauses.size(); - m_clauses.push_back({ cl, 1, rational::zero() }); - clause& cls = m_clauses.back(); - cls.m_dts = dts(cls); - for (sat::literal lit : *cl) { - if (is_true(lit)) - cls.add(lit); - } - - for (auto a : arith) - m_atoms[a].m_clause_idx = clause_idx; - - if (!cl.is_true()) { - m_best_min_unsat++; - m_unsat.insert(clause_idx); - } - else if (cl.m_dts > 0 && cl.m_num_trues == 1) - inc_break(sat::to_literal(cl.m_trues)); - - } - - -#endif -} - diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 460bdedce81..7e099715674 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -31,8 +31,10 @@ namespace arith { void sls::operator()(bool_vector& phase) { - m_best_min_unsat = unsat().size(); unsigned num_steps = 0; + for (unsigned v = 0; v < s.s().num_vars(); ++v) + init_bool_var_assignment(v); + m_best_min_unsat = unsat().size(); while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) break; @@ -446,11 +448,11 @@ namespace arith { bool should_minus = false; sls::ineq_kind op; if (!lit.sign()) { - should_minus = b->get_bound_kind() == lp::GE; + should_minus = b->get_bound_kind() == lp_api::bound_kind::upper_t; op = sls::ineq_kind::LE; } else { - should_minus = b->get_bound_kind() == lp::LE; + should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t; if (s.is_int(b->get_var())) { bound -= 1; op = sls::ineq_kind::LE; @@ -464,7 +466,7 @@ namespace arith { auto& ineq = new_ineq(op, bound); add_args(ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); - set_literal(lit, ineq); + m_literals.set(lit.index(), &ineq); return; } @@ -478,34 +480,20 @@ namespace arith { auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); add_args(ineq, tu, u, rational::one()); add_args(ineq, tv, v, -rational::one()); - set_literal(lit, ineq); + m_literals.set(lit.index(), &ineq); return; } } - /** - * Associate literal with inequality and synchronize truth assignment based on arithmetic values. - */ - void sls::set_literal(sat::literal lit, ineq& ineq) { - m_literals.set(lit.index(), &ineq); - if (m_bool_search->get_value(lit.var())) { - if (dtt(ineq) != 0) - m_bool_search->flip(lit.var()); - } - else { - if (dtt(ineq) == 0) - m_bool_search->flip(lit.var()); - } + void sls::init_bool_var_assignment(sat::bool_var v) { + init_literal_assignment(literal(v, false)); + init_literal_assignment(literal(v, true)); } -#if 0 - - { - + void sls::init_literal_assignment(sat::literal lit) { + auto* ineq = m_literals.get(lit.index(), nullptr); + if (ineq && m_bool_search->get_value(lit.var()) != (dtt(*ineq) == 0)) + m_bool_search->flip(lit.var()); } } - -#endif -} - diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index dcb9356105e..16a8549f9dd 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -132,7 +132,8 @@ namespace arith { void add_bounds(sat::literal_vector& bounds); void add_args(ineq& ineq, lp::tv t, euf::theory_var v, rational sign); void init_literal(sat::literal lit); - void set_literal(sat::literal lit, ineq& ineq); + void init_bool_var_assignment(sat::bool_var v); + void init_literal_assignment(sat::literal lit); rational value(var_t v) const { return m_vars[v].m_value; } public: From 5e30323b1ac7626634e2d1f167296d9f828ed676 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 15:46:39 -0800 Subject: [PATCH 406/597] wip - bounded local search for arithmetic --- src/sat/sat_ddfw.cpp | 10 ++-- src/sat/sat_extension.h | 2 +- src/sat/sat_solver.cpp | 9 ++++ src/sat/smt/arith_sls.cpp | 93 ++++++++++++++++++++------------ src/sat/smt/arith_sls.h | 33 ++++++++++-- src/sat/smt/arith_solver.h | 2 +- src/sat/smt/euf_local_search.cpp | 35 ++++++------ src/sat/smt/euf_solver.h | 4 +- 8 files changed, 123 insertions(+), 65 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 98e3ce2bd0f..747ea49400d 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -148,7 +148,8 @@ namespace sat { m_use_list[lit.index()].pop_back(); m_alloc.del_clause(info.m_clause); m_clauses.pop_back(); - m_unsat.remove(m_clauses.size()); + if (m_unsat.contains(m_clauses.size())) + m_unsat.remove(m_clauses.size()); } void ddfw::add(solver const& s) { @@ -188,12 +189,11 @@ namespace sat { } void ddfw::remove_assumptions() { + if (m_assumptions.empty()) + return; for (unsigned i = 0; i < m_assumptions.size(); ++i) del(); - m_unsat_vars.reset(); - for (auto idx : m_unsat) - for (auto lit : get_clause(idx)) - m_unsat_vars.insert(lit.var()); + init(0, nullptr); } void ddfw::init(unsigned sz, literal const* assumptions) { diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index 3a1f363a368..ae99cae12be 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -126,7 +126,7 @@ namespace sat { virtual void add_assumptions(literal_set& ext_assumptions) {} virtual bool tracking_assumptions() { return false; } virtual bool enable_self_propagate() const { return false; } - virtual void local_search(bool_vector& phase) {} + virtual lbool local_search(bool_vector& phase) { return l_undef; } virtual bool extract_pb(std::function& card, std::function& pb) { diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 10aac6dcbd7..898c3a2a420 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1302,6 +1302,9 @@ namespace sat { return l_undef; } + // uncomment this to test bounded local search: + // bounded_local_search(); + log_stats(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; @@ -1360,6 +1363,12 @@ namespace sat { }; void solver::bounded_local_search() { + if (m_ext) { + verbose_stream() << "bounded local search\n"; + do_restart(true); + m_ext->local_search(m_best_phase); + return; + } literal_vector _lits; scoped_limits scoped_rl(rlimit()); m_local_search = alloc(ddfw); diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 7e099715674..79e6aec54d2 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -20,21 +20,24 @@ Module Name: namespace arith { - - /// - /// need to initialize ineqs (arithmetical atoms) - /// - - sls::sls(solver& s): s(s), m(s.m) {} - void sls::operator()(bool_vector& phase) { + void sls::reset() { + m_literals.reset(); + m_vars.reset(); + m_clauses.reset(); + m_terms.reset(); + } + + lbool sls::operator()(bool_vector& phase) { unsigned num_steps = 0; for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); m_best_min_unsat = unsat().size(); + verbose_stream() << "max arith steps " << m_max_arith_steps << "\n"; + //m_max_arith_steps = 10000; while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) break; @@ -47,24 +50,27 @@ namespace arith { save_best_values(); } } - IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); + log(); + return unsat().empty() ? l_true : l_undef; + } + + void sls::log() { + IF_VERBOSE(2, verbose_stream() << "(sls :flips " << m_stats.m_num_flips << " :unsat " << unsat().size() << ")\n"); } void sls::save_best_values() { // first compute assignment to terms - // then update non-basic variables in tableau, assuming a sat solution was found. -#if false - for (auto const& [t, v] : terms) { + // then update non-basic variables in tableau. + for (auto const& [t, v] : m_terms) { rational val; - lp::lar_term const& term = lp().get_term(t); + lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { - auto t2 = lp().column2tv(arg.column()); - auto w = lp().local_to_external(t2.id()); - val += arg.coeff() * local_search.value(w); + auto t2 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + val += arg.coeff() * value(w); } update(v, val); } -#endif for (unsigned v = 0; v < s.get_num_vars(); ++v) { if (s.is_bool(v)) @@ -87,6 +93,8 @@ namespace arith { void sls::set(sat::ddfw* d) { m_bool_search = d; + reset(); + m_literals.reserve(s.s().num_vars() * 2); add_vars(); m_clauses.resize(d->num_clauses()); for (unsigned i = 0; i < d->num_clauses(); ++i) @@ -151,12 +159,16 @@ namespace arith { bool sls::cm(ineq const& ineq, var_t v, rational& new_value) { SASSERT(!ineq.is_true()); auto delta = ineq.m_args_value - ineq.m_bound; + if (ineq.m_op == ineq_kind::NE || ineq.m_op == ineq_kind::LT) + delta--; for (auto const& [coeff, w] : ineq.m_args) { if (w == v) { + if (coeff > 0) new_value = value(v) - abs(ceil(delta / coeff)); else new_value = value(v) + abs(floor(delta / coeff)); + switch (ineq.m_op) { case ineq_kind::LE: SASSERT(delta + coeff * (new_value - value(v)) <= 0); @@ -189,9 +201,12 @@ namespace arith { auto const& clause = get_clause(cl); rational new_value; for (literal lit : clause) { + if (is_true(lit)) + continue; auto const* ineq = atom(lit); - if (!ineq || ineq->is_true()) + if (!ineq) continue; + SASSERT(!ineq->is_true()); for (auto const& [coeff, v] : ineq->m_args) { if (!cm(*ineq, v, new_value)) continue; @@ -201,8 +216,9 @@ namespace arith { unsigned num_unsat = unsat().size(); update(v, new_value); IF_VERBOSE(2, - verbose_stream() << "score " << v << " " << score << "\n" + verbose_stream() << "v" << v << " score " << score << " " << num_unsat << " -> " << unsat().size() << "\n"); + SASSERT(num_unsat > unsat().size()); return true; } } @@ -255,7 +271,8 @@ namespace arith { } /** - * redistribute weights of clauses. TODO - re-use ddfw weights instead. + * redistribute weights of clauses. + * TODO - re-use ddfw weights instead. */ void sls::paws() { for (unsigned cl = num_clauses(); cl-- > 0; ) { @@ -270,13 +287,15 @@ namespace arith { // // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) + // TODO - use cached dts instead of computed dts + // cached dts has to be updated when the score of literals are updated. // rational sls::dscore(var_t v, rational const& new_value) const { auto const& vi = m_vars[v]; rational score(0); for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); + score += (compute_dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); return score; } @@ -290,10 +309,11 @@ namespace arith { for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); if (!clause.is_true()) { + VERIFY(dtt_old != 0); if (dtt_new == 0) ++score; // false -> true } - else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) // true -> true ?? TODO + else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals continue; else if (all_of(*clause.m_clause, [&](auto lit) { return !atom(lit) || dtt(*atom(lit), v, new_value) > 0; })) // ?? TODO --score; @@ -302,7 +322,7 @@ namespace arith { return score; } - rational sls::dts(unsigned cl) const { + rational sls::compute_dts(unsigned cl) const { rational d(1), d2; bool first = true; for (auto a : get_clause(cl)) { @@ -346,14 +366,20 @@ namespace arith { rational dtt_old = dtt(ineq); ineq.m_args_value += coeff * (new_value - old_value); rational dtt_new = dtt(ineq); - SASSERT(!(dtt_new == 0 && dtt_new < dtt_old) || m_bool_search->get_value(lit.var()) == lit.sign()); - SASSERT(!(dtt_old == 0 && dtt_new > dtt_old) || m_bool_search->get_value(lit.var()) != lit.sign()); + if ((dtt_new == 0) == is_true(lit)) { + dtt(ineq) = dtt_new; + continue; + } + VERIFY((dtt_old == 0) == is_true(lit)); + VERIFY(!(dtt_new == 0 && dtt_new < dtt_old) || !is_true(lit)); + VERIFY(!(dtt_old == 0 && dtt_new > dtt_old) || is_true(lit)); if (dtt_new == 0 && dtt_new < dtt_old) // flip from false to true m_bool_search->flip(lit.var()); else if (dtt_old == 0 && dtt_old < dtt_new) // flip from true to false m_bool_search->flip(lit.var()); dtt(ineq) = dtt_new; - SASSERT((dtt_new == 0) == (m_bool_search->get_value(lit.var()) != lit.sign())); + + VERIFY((dtt_new == 0) == is_true(lit)); } vi.m_value = new_value; } @@ -422,18 +448,18 @@ namespace arith { } - void sls::add_args(ineq& ineq, lp::tv t, theory_var v, rational sign) { + void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, rational sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - ineq.m_args.push_back({ sign * arg.coeff(), w }); + add_arg(lit, ineq, sign * arg.coeff(), w); } } else - ineq.m_args.push_back({ sign, s.lp().local_to_external(t.id()) }); + add_arg(lit, ineq, sign, s.lp().local_to_external(t.id())); } @@ -465,7 +491,7 @@ namespace arith { bound.neg(); auto& ineq = new_ineq(op, bound); - add_args(ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); + add_args(lit, ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); m_literals.set(lit.index(), &ineq); return; } @@ -478,8 +504,8 @@ namespace arith { lp::tv tu = s.get_tv(u); lp::tv tv = s.get_tv(v); auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); - add_args(ineq, tu, u, rational::one()); - add_args(ineq, tv, v, -rational::one()); + add_args(lit, ineq, tu, u, rational::one()); + add_args(lit, ineq, tv, v, -rational::one()); m_literals.set(lit.index(), &ineq); return; } @@ -492,8 +518,9 @@ namespace arith { void sls::init_literal_assignment(sat::literal lit) { auto* ineq = m_literals.get(lit.index(), nullptr); - if (ineq && m_bool_search->get_value(lit.var()) != (dtt(*ineq) == 0)) - m_bool_search->flip(lit.var()); + + if (ineq && is_true(lit) != (dtt(*ineq) == 0)) + m_bool_search->flip(lit.var()); } } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 16a8549f9dd..9a4cfcd81cc 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -55,6 +55,7 @@ namespace arith { unsigned m_num_flips = 0; }; + public: // encode args <= bound, args = bound, args < bound struct ineq { vector> m_args; @@ -74,7 +75,23 @@ namespace arith { return m_args_value < m_bound; } } + std::ostream& display(std::ostream& out) const { + bool first = true; + for (auto const& [c, v] : m_args) + out << (first? "": " + ") << c << " * v" << v, first = false; + switch (m_op) { + case ineq_kind::LE: + return out << " <= " << m_bound << "(" << m_args_value << ")"; + case ineq_kind::EQ: + return out << " == " << m_bound << "(" << m_args_value << ")"; + case ineq_kind::NE: + return out << " != " << m_bound << "(" << m_args_value << ")"; + default: + return out << " < " << m_bound << "(" << m_args_value << ")"; + } + } }; + private: struct var_info { rational m_value; @@ -85,6 +102,7 @@ namespace arith { struct clause { unsigned m_weight = 1; + rational m_dts = rational::one(); }; solver& s; @@ -97,6 +115,8 @@ namespace arith { scoped_ptr_vector m_literals; vector m_vars; vector m_clauses; + svector> m_terms; + indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } unsigned num_clauses() const { return m_bool_search->num_clauses(); } @@ -104,12 +124,14 @@ namespace arith { sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } + bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); } + void reset(); ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } unsigned& get_weight(unsigned idx) { return m_clauses[idx].m_weight; } unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } bool flip(); - void log() {} + void log(); bool flip_unsat(); bool flip_clauses(); bool flip_dscore(); @@ -119,7 +141,7 @@ namespace arith { rational dtt(rational const& args, ineq const& ineq) const; rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; rational dts(unsigned cl, var_t v, rational const& new_value) const; - rational dts(unsigned cl) const; + rational compute_dts(unsigned cl) const; bool cm(ineq const& ineq, var_t v, rational& new_value); int cm_score(var_t v, rational const& new_value); void update(var_t v, rational const& new_value); @@ -130,7 +152,7 @@ namespace arith { sls::ineq& new_ineq(ineq_kind op, rational const& bound); void add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v); void add_bounds(sat::literal_vector& bounds); - void add_args(ineq& ineq, lp::tv t, euf::theory_var v, rational sign); + void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, rational sign); void init_literal(sat::literal lit); void init_bool_var_assignment(sat::bool_var v); void init_literal_assignment(sat::literal lit); @@ -138,11 +160,14 @@ namespace arith { rational value(var_t v) const { return m_vars[v].m_value; } public: sls(solver& s); - void operator ()(bool_vector& phase); + lbool operator ()(bool_vector& phase); void set_bounds_begin(); void set_bounds_end(unsigned num_literals); void set_bounds(euf::enode* n); void set(sat::ddfw* d); }; + inline std::ostream& operator<<(std::ostream& out, sls::ineq const& ineq) { + return ineq.display(out); + } } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 732e291b10f..a31ca844a3e 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -515,7 +515,7 @@ namespace arith { void set_bounds_begin() override { m_local_search.set_bounds_begin(); } void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } void set_bounds(enode* n) override { m_local_search.set_bounds(n); } - void local_search(bool_vector& phase) override { m_local_search(phase); } + lbool local_search(bool_vector& phase) override { return m_local_search(phase); } void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); } // bounds and equality propagation callbacks diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 873a64b7e58..fab3b7815b4 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -21,7 +21,7 @@ Module Name: namespace euf { - void solver::local_search(bool_vector& phase) { + lbool solver::local_search(bool_vector& phase) { scoped_limits scoped_rl(m.limit()); sat::ddfw bool_search; bool_search.reinit(s(), phase); @@ -36,7 +36,7 @@ namespace euf { for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { - setup_bounds(phase); + setup_bounds(bool_search, phase); // Non-boolean literals are assumptions to Boolean search literal_vector assumptions; @@ -44,6 +44,8 @@ namespace euf { if (!is_propositional(literal(v))) assumptions.push_back(literal(v, !bool_search.get_value(v))); + verbose_stream() << "assumptions " << assumptions.size() << "\n"; + bool_search.rlimit().push(m_max_bool_steps); lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); @@ -51,15 +53,15 @@ namespace euf { for (auto* th : m_solvers) th->local_search(phase); - // if is_sat break; + if (bool_search.unsat_set().empty()) + break; } - - auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) - phase[i] = mdl[i] == l_true; - + phase[i] = mdl[i] == l_true; + + return bool_search.unsat_set().empty() ? l_true : l_undef; } bool solver::is_propositional(sat::literal lit) { @@ -67,13 +69,13 @@ namespace euf { return !e || is_uninterp_const(e) || !m_egraph.find(e); } - void solver::setup_bounds(bool_vector const& phase) { + void solver::setup_bounds(sat::ddfw& bool_search, bool_vector const& phase) { unsigned num_literals = 0; unsigned num_bool = 0; for (auto* th : m_solvers) th->set_bounds_begin(); - auto init_literal = [&](sat::literal l) { + auto count_literal = [&](sat::literal l) { if (is_propositional(l)) { ++num_bool; return; @@ -86,16 +88,11 @@ namespace euf { } }; - auto is_true = [&](auto lit) { - return phase[lit.var()] == !lit.sign(); - }; - - for (auto* cp : s().clauses()) { - if (any_of(*cp, [&](auto lit) { return is_true(lit); })) - continue; - num_literals += cp->size(); - for (auto l : *cp) - init_literal(l); + for (auto cl : bool_search.unsat_set()) { + auto& c = *bool_search.get_clause_info(cl).m_clause; + num_literals += c.size(); + for (auto l : c) + count_literal(l); } m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index a19dbca5d9b..d623903296a 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -265,7 +265,7 @@ namespace euf { // local search unsigned m_max_bool_steps = 10; bool is_propositional(sat::literal lit); - void setup_bounds(bool_vector const& mdl); + void setup_bounds(sat::ddfw& bool_search, bool_vector const& mdl); // user propagator void check_for_user_propagator() { @@ -353,7 +353,7 @@ namespace euf { void add_assumptions(sat::literal_set& assumptions) override; bool tracking_assumptions() override; std::string reason_unknown() override { return m_reason_unknown; } - void local_search(bool_vector& phase) override; + lbool local_search(bool_vector& phase) override; void propagate(literal lit, ext_justification_idx idx); bool propagate(enode* a, enode* b, ext_justification_idx idx); From ede9e5ffc225f98a02cc27e35de8a99f6ca6c91c Mon Sep 17 00:00:00 2001 From: Walden Yan Date: Sat, 11 Feb 2023 18:48:29 -0500 Subject: [PATCH 407/597] [WIP] More TS Binding Features (#6412) * feat: basic quantfier support * feat: added isQuantifier * feat: expanded functions * wip: (lambda broken) * temp fix to LambdaImpl typing issue * feat: function type inference * formatting with prettier * fix: imported from invalid module * fix isBool bug and dumping to smtlib * substitution and model.updateValue * api to add custom func interps to model * fix: building * properly handling uint32 -> number conversion in z3 TS wrapper * added simplify * remame Add->Sum and Mul->Product * formatting --- .../js/examples/high-level/using_smtlib2.ts | 36 + src/api/js/examples/low-level/example-raw.ts | 1 + src/api/js/package-lock.json | 44 +- src/api/js/package.json | 9 +- src/api/js/scripts/make-ts-wrapper.ts | 12 +- src/api/js/scripts/parse-api.ts | 2 +- src/api/js/src/high-level/high-level.test.ts | 197 +++- src/api/js/src/high-level/high-level.ts | 927 +++++++++++++++--- src/api/js/src/high-level/types.ts | 483 ++++++--- 9 files changed, 1381 insertions(+), 330 deletions(-) create mode 100644 src/api/js/examples/high-level/using_smtlib2.ts diff --git a/src/api/js/examples/high-level/using_smtlib2.ts b/src/api/js/examples/high-level/using_smtlib2.ts new file mode 100644 index 00000000000..e9275b7bfbf --- /dev/null +++ b/src/api/js/examples/high-level/using_smtlib2.ts @@ -0,0 +1,36 @@ +// @ts-ignore we're not going to bother with types for this +import process from 'process'; +import { init } from '../../build/node'; +import assert from 'assert'; + +(async () => { + let { Context, em } = await init(); + let z3 = Context('main'); + + const x = z3.BitVec.const('x', 256); + const y = z3.BitVec.const('y', 256); + const z = z3.BitVec.const('z', 256); + const xPlusY = x.add(y); + const xPlusZ = x.add(z); + const expr = xPlusY.mul(xPlusZ); + + const to_check = expr.eq(z3.Const('test', expr.sort)); + + const solver = new z3.Solver(); + solver.add(to_check); + const cr = await solver.check(); + console.log(cr); + assert(cr === 'sat'); + + const model = solver.model(); + let modelStr = model.sexpr(); + modelStr = modelStr.replace(/\n/g, ' '); + console.log("Model: ", modelStr); + + const exprs = z3.ast_from_string(modelStr); + console.log(exprs); + +})().catch(e => { + console.error('error', e); + process.exit(1); +}); \ No newline at end of file diff --git a/src/api/js/examples/low-level/example-raw.ts b/src/api/js/examples/low-level/example-raw.ts index 6e34b4aba4b..2790f959449 100644 --- a/src/api/js/examples/low-level/example-raw.ts +++ b/src/api/js/examples/low-level/example-raw.ts @@ -1,3 +1,4 @@ +// @ts-ignore we're not going to bother with types for this import process from 'process'; import { init, Z3_error_code } from '../../build/node'; diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json index f6969a933c8..a46cae95101 100644 --- a/src/api/js/package-lock.json +++ b/src/api/js/package-lock.json @@ -4461,14 +4461,14 @@ "dev": true }, "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", + "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "vscode-textmate": "^6.0.0" } }, "side-channel": { @@ -4826,16 +4826,15 @@ "dev": true }, "typedoc": { - "version": "0.22.18", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.18.tgz", - "integrity": "sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA==", + "version": "0.23.16", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.16.tgz", + "integrity": "sha512-rumYsCeNRXlyuZVzefD7050n7ptL2uudsCJg50dY0v/stKniqIlRpvx/F/6expC0/Q6Dbab+g/JpZuB7Sw90FA==", "dev": true, "requires": { - "glob": "^8.0.3", "lunr": "^2.3.9", - "marked": "^4.0.16", + "marked": "^4.0.19", "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "shiki": "^0.11.1" }, "dependencies": { "brace-expansion": { @@ -4847,19 +4846,6 @@ "balanced-match": "^1.0.0" } }, - "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, "minimatch": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", @@ -4872,9 +4858,9 @@ } }, "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true }, "typical": { @@ -4945,9 +4931,9 @@ "dev": true }, "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", + "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", "dev": true }, "walker": { diff --git a/src/api/js/package.json b/src/api/js/package.json index 90a7bfa3de2..3e79ba8f4bc 100644 --- a/src/api/js/package.json +++ b/src/api/js/package.json @@ -1,5 +1,6 @@ { "name": "z3-solver", + "version": "0.1.0", "keywords": [ "Z3", "theorem", @@ -26,8 +27,8 @@ "build:ts:generate": "ts-node --transpileOnly scripts/make-ts-wrapper.ts src/low-level/wrapper.__GENERATED__.ts src/low-level/types.__GENERATED__.ts", "build:wasm": "ts-node --transpileOnly ./scripts/build-wasm.ts", "clean": "rimraf build 'src/**/*.__GENERATED__.*'", - "lint": "prettier -c '{,src/,scripts/,examples}*.{js,ts}'", - "format": "prettier --write '{,src/,scripts/}*.{js,ts}'", + "lint": "prettier -c '{./,src/,scripts/,examples/}**/*.{js,ts}'", + "format": "prettier --write '{./,src/,scripts/}**/*.{js,ts}'", "test": "jest", "docs": "typedoc", "check-engine": "check-engine" @@ -53,8 +54,8 @@ "ts-expect": "^1.3.0", "ts-jest": "^28.0.3", "ts-node": "^10.8.0", - "typedoc": "^0.22.17", - "typescript": "^4.5.4" + "typedoc": "^0.23.16", + "typescript": "^4.8.4" }, "license": "MIT", "dependencies": { diff --git a/src/api/js/scripts/make-ts-wrapper.ts b/src/api/js/scripts/make-ts-wrapper.ts index 58b2ce0ae07..56860946796 100644 --- a/src/api/js/scripts/make-ts-wrapper.ts +++ b/src/api/js/scripts/make-ts-wrapper.ts @@ -76,6 +76,7 @@ function makeTsWrapper() { } const isInParam = (p: FuncParam) => p.kind !== undefined && ['in', 'in_array'].includes(p.kind); + function wrapFunction(fn: Func) { if (CUSTOM_IMPLEMENTATIONS.includes(fn.name)) { return null; @@ -104,7 +105,7 @@ function makeTsWrapper() { let isAsync = asyncFuncs.includes(fn.name); let trivial = - !['string', 'boolean'].includes(fn.ret) && + !['string', 'boolean', 'unsigned'].includes(fn.ret) && !fn.nullableRet && outParams.length === 0 && !inParams.some(p => p.type === 'string' || p.isArray || p.nullable); @@ -234,6 +235,7 @@ function makeTsWrapper() { function setArg() { args[outParam.idx] = memIdx === 0 ? 'outAddress' : `outAddress + ${memIdx * 4}`; } + let read, type; if (outParam.type === 'string') { read = `Mod.UTF8ToString(getOutUint(${memIdx}))`; @@ -330,11 +332,15 @@ function makeTsWrapper() { if (ret === 0) { return null; } - `.trim(); + `.trim(); + } else if (fn.ret === 'unsigned') { + infix += ` + ret = (new Uint32Array([ret]))[0]; + `.trim(); } // prettier-ignore - let invocation = `Mod.ccall('${isAsync ? 'async_' : ''}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(', ')}])`; + let invocation = `Mod.ccall('${isAsync ? "async_" : ""}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(", ")}])`; if (isAsync) { invocation = `await Mod.async_call(() => ${invocation})`; diff --git a/src/api/js/scripts/parse-api.ts b/src/api/js/scripts/parse-api.ts index c3292583af1..a3aa81acdc3 100644 --- a/src/api/js/scripts/parse-api.ts +++ b/src/api/js/scripts/parse-api.ts @@ -45,7 +45,7 @@ const types = { __proto__: null, // these are function types I can't be bothered to parse - // NSB: They can be extracted automatically from z3_api.h thanks to the use + // NSB: They can be extracted automatically from z3_api.h thanks to the use // of a macro. Z3_error_handler: 'Z3_error_handler', Z3_push_eh: 'Z3_push_eh', diff --git a/src/api/js/src/high-level/high-level.test.ts b/src/api/js/src/high-level/high-level.test.ts index 70c11b875bb..d4207516982 100644 --- a/src/api/js/src/high-level/high-level.test.ts +++ b/src/api/js/src/high-level/high-level.test.ts @@ -1,8 +1,8 @@ import assert from 'assert'; import asyncToArray from 'iter-tools/methods/async-to-array'; import { init, killThreads } from '../jest'; -import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types'; -import { expectType } from "ts-expect"; +import { Arith, Bool, Model, Quantifier, Z3AssertionError, Z3HighLevel, AstVector } from './types'; +import { expectType } from 'ts-expect'; /** * Generate all possible solutions from given assumptions. @@ -58,6 +58,7 @@ async function* allSolutions(...assertions: Bool[]): async function prove(conjecture: Bool): Promise { const solver = new conjecture.ctx.Solver(); + solver.set('timeout', 1000); const { Not } = solver.ctx; solver.add(Not(conjecture)); expect(await solver.check()).toStrictEqual('unsat'); @@ -113,11 +114,11 @@ describe('high-level', () => { it('test loading a solver state from a string', async () => { const { Solver, Not, Int } = api.Context('main'); const solver = new Solver(); - solver.fromString("(declare-const x Int) (assert (and (< x 2) (> x 0)))") - expect(await solver.check()).toStrictEqual('sat') - const x = Int.const('x') - solver.add(Not(x.eq(1))) - expect(await solver.check()).toStrictEqual('unsat') + solver.fromString('(declare-const x Int) (assert (and (< x 2) (> x 0)))'); + expect(await solver.check()).toStrictEqual('sat'); + const x = Int.const('x'); + solver.add(Not(x.eq(1))); + expect(await solver.check()).toStrictEqual('unsat'); }); it('disproves x = y implies g(g(x)) = g(y)', async () => { @@ -393,14 +394,13 @@ describe('high-level', () => { }); describe('arrays', () => { - it('Example 1', async () => { const Z3 = api.Context('main'); const arr = Z3.Array.const('arr', Z3.Int.sort(), Z3.Int.sort()); const [idx, val] = Z3.Int.consts('idx val'); - const conjecture = (arr.store(idx, val).select(idx).eq(val)); + const conjecture = arr.store(idx, val).select(idx).eq(val); await prove(conjecture); }); @@ -428,7 +428,7 @@ describe('high-level', () => { // and is detected at compile time // @ts-expect-error const arr3 = Array.const('arr3', BitVec.sort(1)); - }) + }); it('can do simple proofs', async () => { const { BitVec, Array, isArray, isArraySort, isConstArray, Eq, Not } = api.Context('main'); @@ -447,13 +447,6 @@ describe('high-level', () => { await prove(Eq(arr2.select(0), FIVE_VAL)); await prove(Not(Eq(arr2.select(0), BitVec.val(6, 256)))); await prove(Eq(arr2.store(idx, val).select(idx), constArr.store(idx, val).select(idx))); - - // TODO: add in Quantifiers and better typing of arrays - // await prove( - // ForAll([idx], idx.add(1).ugt(idx).and(arr.select(idx.add(1)).ugt(arr.select(idx)))).implies( - // arr.select(0).ult(arr.select(1000)) - // ) - // ); }); it('Finds arrays that differ but that sum to the same', async () => { @@ -465,18 +458,16 @@ describe('high-level', () => { const arr1 = Array.const('arr', BitVec.sort(2), BitVec.sort(32)); const arr2 = Array.const('arr2', BitVec.sort(2), BitVec.sort(32)); - const same_sum = arr1.select(0) + const same_sum = arr1 + .select(0) .add(arr1.select(1)) .add(arr1.select(2)) .add(arr1.select(3)) - .eq( - arr2.select(0) - .add(arr2.select(1)) - .add(arr2.select(2)) - .add(arr2.select(3)) - ); - - const different = arr1.select(0).neq(arr2.select(0)) + .eq(arr2.select(0).add(arr2.select(1)).add(arr2.select(2)).add(arr2.select(3))); + + const different = arr1 + .select(0) + .neq(arr2.select(0)) .or(arr1.select(1).neq(arr2.select(1))) .or(arr1.select(2).neq(arr2.select(2))) .or(arr1.select(3).neq(arr2.select(3))); @@ -485,11 +476,105 @@ describe('high-level', () => { const arr1Vals = [0, 1, 2, 3].map(i => model.eval(arr1.select(i)).value()); const arr2Vals = [0, 1, 2, 3].map(i => model.eval(arr2.select(i)).value()); - expect((arr1Vals.reduce((a, b) => a + b, 0n) % mod) === arr2Vals.reduce((a, b) => a + b, 0n) % mod); + expect(arr1Vals.reduce((a, b) => a + b, 0n) % mod === arr2Vals.reduce((a, b) => a + b, 0n) % mod); for (let i = 0; i < 4; i++) { expect(arr1Vals[i] !== arr2Vals[i]); } }); + + it('Array type inference', async () => { + const z3 = api.Context('main'); + + const Z3_ADDR = z3.BitVec.const('Vault_addr', 160); + const Z3_GLOBAL_STORAGE = z3.Array.const( + 'global_storage', + z3.BitVec.sort(160), + z3.Array.sort(z3.BitVec.sort(160), z3.BitVec.sort(256)), + ); + const Z3_STORAGE = Z3_GLOBAL_STORAGE.select(Z3_ADDR); + + // We are so far unable to properly infer the type of Z3_STORAGE + // expectType< + // SMTArray<'main', [BitVecSort<160>], BitVecSort<256>> + // >(Z3_STORAGE); + }); + }); + + describe('quantifiers', () => { + it('Basic Universal', async () => { + const Z3 = api.Context('main'); + + const [x, y] = Z3.Int.consts('x y'); + + const conjecture = Z3.ForAll([x, y], x.neq(y).implies(x.lt(y).or(x.gt(y)))); + expect(Z3.isBool(conjecture)).toBeTruthy(); + expect(conjecture.var_name(0)).toBe('x'); + expect(conjecture.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + expect(conjecture.var_name(1)).toBe('y'); + expect(conjecture.var_sort(1).eqIdentity(Z3.Int.sort())).toBeTruthy(); + await prove(conjecture); + }); + + it('Basic Existential', async () => { + const Z3 = api.Context('main'); + + const [x, y, z] = Z3.Int.consts('x y z'); + + const quantifier = Z3.Exists([z], Z3.Not(z.lt(x)).and(Z3.Not(z.gt(y)))); + expect(Z3.isBool(quantifier)).toBeTruthy(); + expect(quantifier.var_name(0)).toBe('z'); + expect(quantifier.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + + const conjecture = Z3.Not(x.gt(y)).implies(quantifier); // Can be trivially discovered with z = x or x = y + await prove(conjecture); + }); + + it('Basic Lambda', async () => { + const Z3 = api.Context('main'); + + const [x, y] = Z3.Int.consts('x y z'); + const L = Z3.Lambda([x, y], x.add(y)); + expect(Z3.isArraySort(L.sort)).toBeTruthy(); + expect(Z3.isArray(L)).toBeFalsy(); + expect(L.var_name(0)).toBe('x'); + expect(L.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + expect(L.var_name(1)).toBe('y'); + expect(L.var_sort(1).eqIdentity(Z3.Int.sort())).toBeTruthy(); + + const conjecture = L.select(Z3.Int.val(2), Z3.Int.val(5)).eq(Z3.Int.val(7)); + await prove(conjecture); + }); + + it('Loading Quantifier Preserves Type', async () => { + const Z3 = api.Context('main'); + + const [x, y, z] = Z3.Int.consts('x y z'); + const quantifier = Z3.Exists([z], Z3.Not(z.lt(x)).and(Z3.Not(z.gt(y)))); + expect(Z3.isBool(quantifier)).toBeTruthy(); + + const solver = new Z3.Solver(); + solver.add(quantifier); + + const dumped_str = solver.toString(); + + const solver2 = new Z3.Solver(); + solver2.fromString(dumped_str); + const quantifier2 = solver2.assertions().get(0) as unknown as Quantifier; + expect(Z3.isBool(quantifier2)).toBeTruthy(); + expect(quantifier2.var_name(0)).toBe('z'); + }); + }); + + describe('uninterpreted functions', () => { + it('Type Inference', async () => { + const Z3 = api.Context('main'); + + const f = Z3.Function.declare('f', Z3.Int.sort(), Z3.Bool.sort()); + const input = Z3.Int.val(6); + const output = f.call(input); + expectType(output); + expect(output.sort.eqIdentity(Z3.Bool.sort())).toBeTruthy(); + }); }); describe('Solver', () => { @@ -543,10 +628,11 @@ describe('high-level', () => { describe('AstVector', () => { it('can use basic methods', async () => { - const { Solver, AstVector, Int } = api.Context('main'); + const Z3 = api.Context('main'); + const { Solver, Int } = Z3; const solver = new Solver(); - const vector = new AstVector(); + const vector = new Z3.AstVector(); for (let i = 0; i < 5; i++) { vector.push(Int.const(`int__${i}`)); } @@ -559,4 +645,57 @@ describe('high-level', () => { expect(await solver.check()).toStrictEqual('sat'); }); }); + + describe('Substitution', () => { + it('basic variable substitution', async () => { + const { Int, substitute } = api.Context('main'); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + const expr = x.add(y); + const subst = substitute(expr, [x, z]); + expect(subst.eqIdentity(z.add(y))).toBeTruthy(); + }); + + it('term substitution', async () => { + const { Int, substitute } = api.Context('main'); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + const expr = x.add(y).mul(Int.val(1).sub(x.add(y))); + const subst = substitute(expr, [x.add(y), z]); + expect(subst.eqIdentity(z.mul(Int.val(1).sub(z)))).toBeTruthy(); + }); + }); + + describe('Model', () => { + it('Assigning constants', async () => { + const { Int, Model } = api.Context('main'); + const m = new Model(); + + const [x, y] = Int.consts('x y'); + + m.updateValue(x, Int.val(6)); + m.updateValue(y, Int.val(12)); + + expect(m.eval(x.add(y)).eqIdentity(Int.val(18))).toBeTruthy(); + }); + + it('Creating Func Interpretations', async () => { + const { Int, Function, Model } = api.Context('main'); + const m = new Model(); + + const f = Function.declare('f', Int.sort(), Int.sort(), Int.sort()); + + const f_interp = m.addFuncInterp(f, 0); + f_interp.addEntry([Int.val(1), Int.val(2)], Int.val(3)); + f_interp.addEntry([Int.val(4), Int.val(5)], Int.val(6)); + + expect(m.eval(f.call(1, 2)).eqIdentity(Int.val(3))).toBeTruthy(); + expect(m.eval(f.call(4, 5)).eqIdentity(Int.val(6))).toBeTruthy(); + expect(m.eval(f.call(0, 0)).eqIdentity(Int.val(0))).toBeTruthy(); + }); + }); }); diff --git a/src/api/js/src/high-level/high-level.ts b/src/api/js/src/high-level/high-level.ts index 59a0b1e3623..80f89b104eb 100644 --- a/src/api/js/src/high-level/high-level.ts +++ b/src/api/js/src/high-level/high-level.ts @@ -31,13 +31,19 @@ import { Z3_symbol, Z3_symbol_kind, Z3_tactic, + Z3_pattern, + Z3_app, + Z3_params, + Z3_func_entry, } from '../low-level'; import { AnyAst, AnyExpr, AnySort, Arith, - ArithSort, ArrayIndexType, + ArithSort, + ArrayIndexType, + CoercibleToArrayIndexType, Ast, AstMap, AstMapCtor, @@ -48,11 +54,12 @@ import { BitVecSort, Bool, BoolSort, - CheckSatResult, CoercibleFromMap, + CheckSatResult, + CoercibleToMap, CoercibleRational, CoercibleToBitVec, CoercibleToExpr, - CoercibleToExprMap, + CoercibleFromMap, Context, ContextCtor, Expr, @@ -61,7 +68,10 @@ import { FuncInterp, IntNum, Model, + Pattern, Probe, + Quantifier, + BodyT, RatNum, SMTArray, SMTArraySort, @@ -71,6 +81,9 @@ import { Tactic, Z3Error, Z3HighLevel, + CoercibleToArith, + NonEmptySortArray, + FuncEntry, } from './types'; import { allSatisfy, assert, assertExhaustive } from './utils'; @@ -81,18 +94,19 @@ const asyncMutex = new Mutex(); function isCoercibleRational(obj: any): obj is CoercibleRational { // prettier-ignore const r = ( - (obj !== null && - (typeof obj === 'object' || typeof obj === 'function')) && - (obj.numerator !== null && - (typeof obj.numerator === 'number' || typeof obj.numerator === 'bigint')) && - (obj.denominator !== null && - (typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')) - ); - r && assert( - (typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) && - (typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)), - 'Fraction numerator and denominator must be integers', - ); + (obj !== null && + (typeof obj === 'object' || typeof obj === 'function')) && + (obj.numerator !== null && + (typeof obj.numerator === 'number' || typeof obj.numerator === 'bigint')) && + (obj.denominator !== null && + (typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')) + ); + r && + assert( + (typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) && + (typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)), + 'Fraction numerator and denominator must be integers', + ); return r; } @@ -152,9 +166,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { function createContext(name: Name, options?: Record): Context { const cfg = Z3.mk_config(); if (options != null) { - Object.entries(options).forEach( - ([key, value]) => check(Z3.set_param_value(cfg, key, value.toString())) - ); + Object.entries(options).forEach(([key, value]) => check(Z3.set_param_value(cfg, key, value.toString()))); } const contextPtr = Z3.mk_context_rc(cfg); Z3.set_ast_print_mode(contextPtr, Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT); @@ -171,7 +183,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - function check(val: T) { + function check(val: T): T { throwIfError(); return val; } @@ -199,6 +211,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function _toParams(key: string, value: any): Z3_params { + const params = Z3.mk_params(contextPtr); + Z3.params_inc_ref(contextPtr, params); + // If value is a boolean + if (typeof value === 'boolean') { + Z3.params_set_bool(contextPtr, params, _toSymbol(key), value); + } else if (typeof value === 'number') { + // If value is a uint + if (Number.isInteger(value)) { + check(Z3.params_set_uint(contextPtr, params, _toSymbol(key), value)); + } else { + // If value is a double + check(Z3.params_set_double(contextPtr, params, _toSymbol(key), value)); + } + } else if (typeof value === 'string') { + check(Z3.params_set_symbol(contextPtr, params, _toSymbol(key), _toSymbol(value))); + } + return params; + } + function _toAst(ast: Z3_ast): AnyAst { switch (check(Z3.get_ast_kind(contextPtr, ast))) { case Z3_ast_kind.Z3_SORT_AST: @@ -229,13 +261,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { function _toExpr(ast: Z3_ast): AnyExpr { const kind = check(Z3.get_ast_kind(contextPtr, ast)); if (kind === Z3_ast_kind.Z3_QUANTIFIER_AST) { - if (Z3.is_quantifier_forall(contextPtr, ast)) - return new BoolImpl(ast); - if (Z3.is_quantifier_exists(contextPtr, ast)) - return new BoolImpl(ast); - if (Z3.is_lambda(contextPtr, ast)) - return new ExprImpl(ast); - assert(false); + if (Z3.is_lambda(contextPtr, ast)) { + return new LambdaImpl(ast); + } + return new NonLambdaQuantifierImpl(ast); } const sortKind = check(Z3.get_sort_kind(contextPtr, Z3.get_sort(contextPtr, ast))); switch (sortKind) { @@ -325,6 +354,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return r; } + function isFuncInterp(obj: unknown): obj is FuncInterp { + const r = obj instanceof FuncInterpImpl; + r && _assertContext(obj); + return r; + } + function isApp(obj: unknown): boolean { if (!isExpr(obj)) { return false; @@ -352,7 +387,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } function isBool(obj: unknown): obj is Bool { - const r = obj instanceof BoolImpl; + const r = obj instanceof ExprImpl && obj.sort.kind() === Z3_sort_kind.Z3_BOOL_SORT; r && _assertContext(obj); return r; } @@ -389,6 +424,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return isAppOf(obj, Z3_decl_kind.Z3_OP_DISTINCT); } + function isQuantifier(obj: unknown): obj is Quantifier { + const r = obj instanceof QuantifierImpl; + r && _assertContext(obj); + return r; + } + function isArith(obj: unknown): obj is Arith { const r = obj instanceof ArithImpl; r && _assertContext(obj); @@ -531,8 +572,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel { // expression simplification // /////////////////////////////// - async function simplify(e: Expr) { - const result = await Z3.simplify(contextPtr, e.ast) + async function simplify(e: Expr): Promise> { + const result = await Z3.simplify(contextPtr, e.ast); return _toExpr(check(result)); } @@ -543,7 +584,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { declare: (name: string) => new SortImpl(Z3.mk_uninterpreted_sort(contextPtr, _toSymbol(name))), }; const Function = { - declare: (name: string, ...signature: FuncDeclSignature) => { + declare: [], RangeSort extends Sort>( + name: string, + ...signature: [...DomainSort, RangeSort] + ): FuncDecl => { const arity = signature.length - 1; const rng = signature[arity]; _assertContext(rng); @@ -552,9 +596,11 @@ export function createApi(Z3: Z3Core): Z3HighLevel { _assertContext(signature[i]); dom.push(signature[i].ptr); } - return new FuncDeclImpl(Z3.mk_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr)); + return new FuncDeclImpl(Z3.mk_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr)); }, - fresh: (...signature: FuncDeclSignature) => { + fresh: [], RangeSort extends Sort>( + ...signature: [...DomainSort, RangeSort] + ): FuncDecl => { const arity = signature.length - 1; const rng = signature[arity]; _assertContext(rng); @@ -563,7 +609,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { _assertContext(signature[i]); dom.push(signature[i].ptr); } - return new FuncDeclImpl(Z3.mk_fresh_func_decl(contextPtr, 'f', dom, rng.ptr)); + return new FuncDeclImpl(Z3.mk_fresh_func_decl(contextPtr, 'f', dom, rng.ptr)); }, }; const RecFunc = { @@ -701,7 +747,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { }, }; const Array = { - sort, ...AnySort[]], RangeSort extends AnySort>( + sort, RangeSort extends AnySort>( ...sig: [...DomainSort, RangeSort] ): SMTArraySort { const arity = sig.length - 1; @@ -711,16 +757,23 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new ArraySortImpl(Z3.mk_array_sort(contextPtr, d.ptr, r.ptr)); } const dom = sig.slice(0, arity); - return new ArraySortImpl(Z3.mk_array_sort_n(contextPtr, dom.map(s => s.ptr), r.ptr)); + return new ArraySortImpl( + Z3.mk_array_sort_n( + contextPtr, + dom.map(s => s.ptr), + r.ptr, + ), + ); }, - const, ...AnySort[]], RangeSort extends AnySort>( - name: string, ...sig: [...DomainSort, RangeSort] + const, RangeSort extends AnySort>( + name: string, + ...sig: [...DomainSort, RangeSort] ): SMTArray { return new ArrayImpl( - check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr)) + check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr)), ); }, - consts, ...AnySort[]], RangeSort extends AnySort>( + consts, RangeSort extends AnySort>( names: string | string[], ...sig: [...DomainSort, RangeSort] ): SMTArray[] { @@ -731,13 +784,11 @@ export function createApi(Z3: Z3Core): Z3HighLevel { }, K, RangeSort extends AnySort>( domain: DomainSort, - value: SortToExprMap + value: SortToExprMap, ): SMTArray { - return new ArrayImpl<[DomainSort], RangeSort>( - check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr)) - ); - } - } + return new ArrayImpl<[DomainSort], RangeSort>(check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr))); + }, + }; //////////////// // Operations // @@ -747,7 +798,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { condition: Bool | boolean, onTrue: OnTrueRef, onFalse: OnFalseRef, - ): CoercibleToExprMap; + ): CoercibleFromMap; function If( condition: Bool | Probe | boolean, onTrue: CoercibleToExpr | Tactic, @@ -812,6 +863,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new BoolImpl(check(Z3.mk_implies(contextPtr, a.ptr, b.ptr))); } + function Iff(a: Bool | boolean, b: Bool | boolean): Bool { + a = from(a) as Bool; + b = from(b) as Bool; + _assertContext(a, b); + return new BoolImpl(check(Z3.mk_iff(contextPtr, a.ptr, b.ptr))); + } + function Eq(a: CoercibleToExpr, b: CoercibleToExpr): Bool { a = from(a); b = from(b); @@ -897,6 +955,84 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function ForAll>( + quantifiers: ArrayIndexType, + body: Bool, + weight: number = 1, + ): NonLambdaQuantifierImpl { + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new NonLambdaQuantifierImpl( + check( + Z3.mk_quantifier_const_ex( + contextPtr, + true, + weight, + _toSymbol(''), + _toSymbol(''), + quantifiers.map(q => q.ptr as unknown as Z3_app), // The earlier check verifies these are all apps + [], + [], + body.ptr, + ), + ), + ); + } + + function Exists>( + quantifiers: ArrayIndexType, + body: Bool, + weight: number = 1, + ): NonLambdaQuantifierImpl { + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new NonLambdaQuantifierImpl( + check( + Z3.mk_quantifier_const_ex( + contextPtr, + false, + weight, + _toSymbol(''), + _toSymbol(''), + quantifiers.map(q => q.ptr as unknown as Z3_app), // The earlier check verifies these are all apps + [], + [], + body.ptr, + ), + ), + ); + } + + function Lambda, RangeSort extends Sort>( + quantifiers: ArrayIndexType, + expr: SortToExprMap, + ): LambdaImpl { + // TODO(walden): For some reason LambdaImpl leads to type issues + // and Typescript won't build. I'm not sure why since the types seem to all match + // up. For now, we just use any for the domain sort + + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new LambdaImpl( + check( + Z3.mk_lambda_const( + contextPtr, + quantifiers.map(q => q.ptr as unknown as Z3_app), + expr.ptr, + ), + ), + ); + } + function ToReal(expr: Arith | bigint): Arith { expr = from(expr) as Arith; _assertContext(expr); @@ -961,6 +1097,101 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new TacticImpl(check(Z3.tactic_cond(contextPtr, probe.ptr, onTrue.ptr, onFalse.ptr))); } + function LT(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_lt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function GT(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_gt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function LE(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_le(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function GE(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_ge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function ULT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvult(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function UGT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvugt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function ULE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvule(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function UGE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvuge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SLT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvslt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SGT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SLE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsle(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SGE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function Extract(hi: number, lo: number, val: BitVec): BitVec { + return new BitVecImpl(check(Z3.mk_extract(contextPtr, hi, lo, val.ast))); + } + + function Select, RangeSort extends Sort>( + array: SMTArray, + ...indices: CoercibleToArrayIndexType + ): SortToExprMap { + const args = indices.map((arg, i) => array.domain_n(i).cast(arg as any)); + if (args.length === 1) { + return _toExpr(check(Z3.mk_select(contextPtr, array.ast, args[0].ast))) as SortToExprMap; + } + const _args = args.map(arg => arg.ast); + return _toExpr(check(Z3.mk_select_n(contextPtr, array.ast, _args))) as SortToExprMap; + } + + function Store, RangeSort extends Sort>( + array: SMTArray, + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray { + const args = indicesAndValue.map((arg, i) => { + if (i === indicesAndValue.length - 1) { + return array.range().cast(arg as any) as SortToExprMap; + } + return array.domain_n(i).cast(arg as any); + }); + if (args.length <= 1) { + throw new Error('Array store requires both index and value arguments'); + } + if (args.length === 2) { + return _toExpr(check(Z3.mk_store(contextPtr, array.ast, args[0].ast, args[1].ast))) as SMTArray< + Name, + DomainSort, + RangeSort + >; + } + const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast); + return _toExpr(check(Z3.mk_store_n(contextPtr, array.ast, _idxs, args[args.length - 1].ast))) as SMTArray< + Name, + DomainSort, + RangeSort + >; + } + class AstImpl implements Ast { declare readonly __typename: Ast['__typename']; readonly ctx: Context; @@ -1023,6 +1254,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { cleanup.register(this, () => Z3.solver_dec_ref(contextPtr, myPtr)); } + set(key: string, value: any): void { + Z3.solver_set_params(contextPtr, this.ptr, _toParams(key, value)); + } + push() { Z3.solver_push(contextPtr, this.ptr); } @@ -1110,20 +1345,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.values(); } - * entries(): IterableIterator<[number, FuncDecl]> { + *entries(): IterableIterator<[number, FuncDecl]> { const length = this.length(); for (let i = 0; i < length; i++) { yield [i, this.get(i)]; } } - * keys(): IterableIterator { + *keys(): IterableIterator { for (const [key] of this.entries()) { yield key; } } - * values(): IterableIterator> { + *values(): IterableIterator> { for (const [, value] of this.entries()) { yield value; } @@ -1160,7 +1395,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { get(sort: Sort): AstVector>; get( i: number | FuncDecl | Expr | Sort, - to?: number + to?: number, ): FuncDecl | FuncInterp | Expr | AstVector> | FuncDecl[] { assert(to === undefined || typeof i === 'number'); if (typeof i === 'number') { @@ -1200,6 +1435,41 @@ export function createApi(Z3: Z3Core): Z3HighLevel { assert(false, 'Number, declaration or constant expected'); } + updateValue(decl: FuncDecl | Expr, a: Ast | FuncInterp): void { + _assertContext(decl); + _assertContext(a); + if (isExpr(decl)) { + decl = decl.decl(); + } + if (isFuncDecl(decl) && decl.arity() !== 0 && isFuncInterp(a)) { + const funcInterp = this.addFuncInterp(decl, a.elseValue() as Expr); + for (let i = 0; i < a.numEntries(); i++) { + const e = a.entry(i); + const n = e.numArgs(); + const args = global.Array(n).map((_, i) => e.argValue(i)); + funcInterp.addEntry(args, e.value()); + } + return; + } + if (!isFuncDecl(decl) || decl.arity() !== 0) { + throw new Z3Error('Expecting 0-ary function or constant expression'); + } + if (!isAst(a)) { + throw new Z3Error('Only func declarations can be assigned to func interpretations'); + } + check(Z3.add_const_interp(contextPtr, this.ptr, decl.ptr, a.ast)); + } + + addFuncInterp[] = Sort[], RangeSort extends Sort = Sort>( + decl: FuncDecl, + defaultValue: CoercibleToMap, Name>, + ): FuncInterp { + const fi = check( + Z3.add_func_interp(contextPtr, this.ptr, decl.ptr, decl.range().cast(defaultValue).ptr as Z3_ast), + ); + return new FuncInterpImpl(fi); + } + private getInterp(expr: FuncDecl | Expr): Expr | FuncInterp | null { assert(isFuncDecl(expr) || isConst(expr), 'Declaration expected'); if (isConst(expr)) { @@ -1228,6 +1498,30 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class FuncEntryImpl implements FuncEntry { + declare readonly __typename: FuncEntry['__typename']; + + readonly ctx: Context; + + constructor(readonly ptr: Z3_func_entry) { + this.ctx = ctx; + Z3.func_entry_inc_ref(contextPtr, ptr); + cleanup.register(this, () => Z3.func_entry_dec_ref(contextPtr, ptr)); + } + + numArgs() { + return check(Z3.func_entry_get_num_args(contextPtr, this.ptr)); + } + + argValue(i: number): Expr { + return _toExpr(check(Z3.func_entry_get_arg(contextPtr, this.ptr, i))); + } + + value(): Expr { + return _toExpr(check(Z3.func_entry_get_value(contextPtr, this.ptr))); + } + } + class FuncInterpImpl implements FuncInterp { declare readonly __typename: FuncInterp['__typename']; readonly ctx: Context; @@ -1237,6 +1531,33 @@ export function createApi(Z3: Z3Core): Z3HighLevel { Z3.func_interp_inc_ref(contextPtr, ptr); cleanup.register(this, () => Z3.func_interp_dec_ref(contextPtr, ptr)); } + + elseValue(): Expr { + return _toExpr(check(Z3.func_interp_get_else(contextPtr, this.ptr))); + } + + numEntries(): number { + return check(Z3.func_interp_get_num_entries(contextPtr, this.ptr)); + } + + arity(): number { + return check(Z3.func_interp_get_arity(contextPtr, this.ptr)); + } + + entry(i: number): FuncEntry { + return new FuncEntryImpl(check(Z3.func_interp_get_entry(contextPtr, this.ptr, i))); + } + + addEntry(args: Expr[], value: Expr): void { + const argsVec = new AstVectorImpl(); + for (const arg of args) { + argsVec.push(arg); + } + _assertContext(argsVec); + _assertContext(value); + assert(this.arity() === argsVec.length(), "Number of arguments in entry doesn't match function arity"); + check(Z3.func_interp_add_entry(contextPtr, this.ptr, argsVec.ptr, value.ptr as Z3_ast)); + } } class SortImpl extends AstImpl implements Sort { @@ -1275,10 +1596,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - class FuncDeclImpl extends AstImpl implements FuncDecl { + class FuncDeclImpl[], RangeSort extends Sort> + extends AstImpl + implements FuncDecl + { declare readonly __typename: FuncDecl['__typename']; - get ast() { + get ast(): Z3_ast { return Z3.func_decl_to_ast(contextPtr, this.ptr); } @@ -1290,20 +1614,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return Z3.get_arity(contextPtr, this.ptr); } - domain(i: number) { + domain(i: T): DomainSort[T] { assert(i < this.arity(), 'Index out of bounds'); return _toSort(Z3.get_domain(contextPtr, this.ptr, i)); } - range() { - return _toSort(Z3.get_range(contextPtr, this.ptr)); + range(): RangeSort { + return _toSort(Z3.get_range(contextPtr, this.ptr)) as RangeSort; } kind() { return Z3.get_decl_kind(contextPtr, this.ptr); } - params(): (number | string | Z3_symbol | Sort | Expr | FuncDecl)[] { + params(): (number | string | Sort | Expr | FuncDecl)[] { const n = Z3.get_decl_num_parameters(contextPtr, this.ptr); const result = []; for (let i = 0; i < n; i++) { @@ -1319,7 +1643,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { result.push(check(Z3.get_decl_rational_parameter(contextPtr, this.ptr, i))); break; case Z3_parameter_kind.Z3_PARAMETER_SYMBOL: - result.push(check(Z3.get_decl_symbol_parameter(contextPtr, this.ptr, i))); + result.push(_fromSymbol(check(Z3.get_decl_symbol_parameter(contextPtr, this.ptr, i)))); break; case Z3_parameter_kind.Z3_PARAMETER_SORT: result.push(new SortImpl(check(Z3.get_decl_sort_parameter(contextPtr, this.ptr, i)))); @@ -1337,21 +1661,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return result; } - call(...args: CoercibleToExpr[]) { + call(...args: CoercibleToArrayIndexType): SortToExprMap { assert(args.length === this.arity(), `Incorrect number of arguments to ${this}`); return _toExpr( - check(Z3.mk_app( - contextPtr, - this.ptr, - args.map((arg, i) => { - return this.domain(i).cast(arg).ast; - }), - )), - ); + check( + Z3.mk_app( + contextPtr, + this.ptr, + args.map((arg, i) => { + return this.domain(i).cast(arg as any).ast; + }), + ), + ), + ) as SortToExprMap; } } - class ExprImpl = AnySort> extends AstImpl implements Expr { + class ExprImpl = AnySort> + extends AstImpl + implements Expr + { declare readonly __typename: Expr['__typename']; get sort(): S { @@ -1364,13 +1693,19 @@ export function createApi(Z3: Z3Core): Z3HighLevel { neq(other: CoercibleToExpr): Bool { return new BoolImpl( - check(Z3.mk_distinct( - contextPtr, - [this, other].map(expr => from(expr).ast), - )), + check( + Z3.mk_distinct( + contextPtr, + [this, other].map(expr => from(expr).ast), + ), + ), ); } + name() { + return this.decl().name(); + } + params() { return this.decl().params(); } @@ -1404,6 +1739,16 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class PatternImpl implements Pattern { + declare readonly __typename: Pattern['__typename']; + readonly ctx: Context; + + constructor(readonly ptr: Z3_pattern) { + this.ctx = ctx; + // TODO: implement rest of Pattern + } + } + class BoolSortImpl extends SortImpl implements BoolSort { declare readonly __typename: BoolSort['__typename']; @@ -1425,7 +1770,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } class BoolImpl extends ExprImpl> implements Bool { - declare readonly __typename: Bool['__typename']; + declare readonly __typename: 'Bool' | 'NonLambdaQuantifier'; not(): Bool { return Not(this); @@ -1446,6 +1791,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { implies(other: Bool | boolean): Bool { return Implies(this, other); } + + iff(other: Bool | boolean): Bool { + return Iff(this, other); + } } class ProbeImpl implements Probe { @@ -1482,12 +1831,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { class ArithSortImpl extends SortImpl implements ArithSort { declare readonly __typename: ArithSort['__typename']; - cast(other: bigint | number): IntNum | RatNum; + cast(other: bigint | number | string): IntNum | RatNum; cast(other: CoercibleRational | RatNum): RatNum; cast(other: IntNum): IntNum; cast(other: Bool | Arith): Arith; cast(other: CoercibleToExpr): never; - cast(other: CoercibleToExpr): Arith | RatNum | IntNum { + cast(other: CoercibleToExpr | string): Arith | RatNum | IntNum { const sortTypeStr = isIntSort(this) ? 'IntSort' : 'RealSort'; if (isExpr(other)) { const otherS = other.sort; @@ -1519,51 +1868,164 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function Sum(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Sum( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Sum>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec add only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvadd(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_add(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Sub(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Sub( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Sub>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec sub only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvsub(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_sub(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Product(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Product( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Product>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec mul only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvmul(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_mul(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Div(arg0: Arith, arg1: CoercibleToArith): Arith; + function Div( + arg0: BitVec, + arg1: CoercibleToBitVec, + ): BitVec; + function Div>(arg0: T, arg1: CoercibleToMap): T { + if (arg0 instanceof BitVecImpl) { + return new BitVecImpl( + check(Z3.mk_bvsdiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_div(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast))) as unknown as T; + } + } + + function BUDiv( + arg0: BitVec, + arg1: CoercibleToBitVec, + ): BitVec { + return new BitVecImpl( + check(Z3.mk_bvudiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)), + ) as unknown as BitVec; + } + + function Neg(a: Arith): Arith; + function Neg(a: BitVec): BitVec; + function Neg>(a: T): T { + if (a instanceof BitVecImpl) { + return new BitVecImpl(check(Z3.mk_bvneg(contextPtr, a.ast))) as unknown as T; + } else { + assert(a instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_unary_minus(contextPtr, a.ast))) as unknown as T; + } + } + + function Mod(a: Arith, b: CoercibleToArith): Arith; + function Mod(a: BitVec, b: CoercibleToBitVec): BitVec; + function Mod>(a: T, b: CoercibleToMap): T { + if (a instanceof BitVecImpl) { + return new BitVecImpl(check(Z3.mk_bvsrem(contextPtr, a.ast, a.sort.cast(b).ast))) as unknown as T; + } else { + assert(a instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_mod(contextPtr, a.ast, a.sort.cast(b).ast))) as unknown as T; + } + } + class ArithImpl extends ExprImpl> implements Arith { declare readonly __typename: Arith['__typename']; - add(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_add(contextPtr, [this.ast, this.sort.cast(other).ast]))); + add(other: CoercibleToArith) { + return Sum(this, other); } - mul(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_mul(contextPtr, [this.ast, this.sort.cast(other).ast]))); + mul(other: CoercibleToArith) { + return Product(this, other); } - sub(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_sub(contextPtr, [this.ast, this.sort.cast(other).ast]))); + sub(other: CoercibleToArith) { + return Sub(this, other); } - pow(exponent: Arith | number | bigint | string | CoercibleRational) { + pow(exponent: CoercibleToArith) { return new ArithImpl(check(Z3.mk_power(contextPtr, this.ast, this.sort.cast(exponent).ast))); } - div(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_div(contextPtr, this.ast, this.sort.cast(other).ast))); + div(other: CoercibleToArith) { + return Div(this, other); } - mod(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_mod(contextPtr, this.ast, this.sort.cast(other).ast))); + mod(other: CoercibleToArith) { + return Mod(this, other); } neg() { - return new ArithImpl(check(Z3.mk_unary_minus(contextPtr, this.ast))); + return Neg(this); } - le(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_le(contextPtr, this.ast, this.sort.cast(other).ast))); + le(other: CoercibleToArith) { + return LE(this, other); } - lt(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_lt(contextPtr, this.ast, this.sort.cast(other).ast))); + lt(other: CoercibleToArith) { + return LT(this, other); } - gt(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_gt(contextPtr, this.ast, this.sort.cast(other).ast))); + gt(other: CoercibleToArith) { + return GT(this, other); } - ge(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_ge(contextPtr, this.ast, this.sort.cast(other).ast))); + ge(other: CoercibleToArith) { + return GE(this, other); } } @@ -1644,27 +2106,27 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } add(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvadd(contextPtr, this.ast, this.sort.cast(other).ast))); + return Sum(this, other); } mul(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvmul(contextPtr, this.ast, this.sort.cast(other).ast))); + return Product(this, other); } sub(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsub(contextPtr, this.ast, this.sort.cast(other).ast))); + return Sub(this, other); } sdiv(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsdiv(contextPtr, this.ast, this.sort.cast(other).ast))); + return Div(this, other); } udiv(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvudiv(contextPtr, this.ast, this.sort.cast(other).ast))); + return BUDiv(this, other); } smod(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsmod(contextPtr, this.ast, this.sort.cast(other).ast))); + return Mod(this, other); } urem(other: CoercibleToBitVec): BitVec { @@ -1676,7 +2138,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } neg(): BitVec { - return new BitVecImpl(check(Z3.mk_bvneg(contextPtr, this.ast))); + return Neg(this); } or(other: CoercibleToBitVec): BitVec { @@ -1723,8 +2185,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new BitVecImpl(check(Z3.mk_bvnot(contextPtr, this.ast))); } - extract(high: number, low: number): BitVec { - return new BitVecImpl(check(Z3.mk_extract(contextPtr, high, low, this.ast))); + extract(high: number, low: number): BitVec { + return Extract(high, low, this); } signExt(count: number): BitVec { @@ -1740,35 +2202,35 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } sle(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsle(contextPtr, this.ast, this.sort.cast(other).ast))); + return SLE(this, other); } ule(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvule(contextPtr, this.ast, this.sort.cast(other).ast))); + return ULE(this, other); } slt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvslt(contextPtr, this.ast, this.sort.cast(other).ast))); + return SLT(this, other); } ult(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvult(contextPtr, this.ast, this.sort.cast(other).ast))); + return ULT(this, other); } sge(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsge(contextPtr, this.ast, this.sort.cast(other).ast))); + return SGE(this, other); } uge(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvuge(contextPtr, this.ast, this.sort.cast(other).ast))); + return UGE(this, other); } sgt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, this.ast, this.sort.cast(other).ast))); + return SGT(this, other); } ugt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvugt(contextPtr, this.ast, this.sort.cast(other).ast))); + return UGT(this, other); } redAnd(): BitVec { @@ -1840,10 +2302,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - class ArraySortImpl, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = Sort> + class ArraySortImpl, RangeSort extends Sort> extends SortImpl - implements SMTArraySort { + implements SMTArraySort + { declare readonly __typename: SMTArraySort['__typename']; domain(): DomainSort[0] { @@ -1857,15 +2319,141 @@ export function createApi(Z3: Z3Core): Z3HighLevel { range(): RangeSort { return _toSort(check(Z3.get_array_sort_range(contextPtr, this.ptr))) as RangeSort; } + } + + class ArrayImpl, RangeSort extends Sort> + extends ExprImpl> + implements SMTArray + { + declare readonly __typename: 'Array' | 'Lambda'; + + domain(): DomainSort[0] { + return this.sort.domain(); + } + domain_n(i: T): DomainSort[T] { + return this.sort.domain_n(i); + } + + range(): RangeSort { + return this.sort.range(); + } + + select(...indices: CoercibleToArrayIndexType): SortToExprMap { + return Select(this, ...indices) as SortToExprMap; + } + + store( + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray { + return Store(this, ...indicesAndValue); + } } - class ArrayImpl< - DomainSort extends [AnySort, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = Sort - > extends ExprImpl> - implements SMTArray { - declare readonly __typename: SMTArray['__typename']; + class QuantifierImpl< + QVarSorts extends NonEmptySortArray, + QSort extends BoolSort | SMTArraySort, + > + extends ExprImpl + implements Quantifier + { + declare readonly __typename: Quantifier['__typename']; + + is_forall(): boolean { + return Z3.is_quantifier_forall(contextPtr, this.ast); + } + + is_exists(): boolean { + return Z3.is_quantifier_exists(contextPtr, this.ast); + } + + is_lambda(): boolean { + return Z3.is_lambda(contextPtr, this.ast); + } + + weight(): number { + return Z3.get_quantifier_weight(contextPtr, this.ast); + } + + num_patterns(): number { + return Z3.get_quantifier_num_patterns(contextPtr, this.ast); + } + + pattern(i: number): Pattern { + return new PatternImpl(check(Z3.get_quantifier_pattern_ast(contextPtr, this.ast, i))); + } + + num_no_patterns(): number { + return Z3.get_quantifier_num_no_patterns(contextPtr, this.ast); + } + + no_pattern(i: number): Expr { + return _toExpr(check(Z3.get_quantifier_no_pattern_ast(contextPtr, this.ast, i))); + } + + body(): BodyT { + return _toExpr(check(Z3.get_quantifier_body(contextPtr, this.ast))) as any; + } + + num_vars(): number { + return Z3.get_quantifier_num_bound(contextPtr, this.ast); + } + + var_name(i: number): string | number { + return _fromSymbol(Z3.get_quantifier_bound_name(contextPtr, this.ast, i)); + } + + var_sort(i: T): QVarSorts[T] { + return _toSort(check(Z3.get_quantifier_bound_sort(contextPtr, this.ast, i))); + } + + children(): [BodyT] { + return [this.body()]; + } + } + + class NonLambdaQuantifierImpl> + extends QuantifierImpl> + implements Quantifier>, Bool + { + declare readonly __typename: 'NonLambdaQuantifier'; + + not(): Bool { + return Not(this); + } + + and(other: Bool | boolean): Bool { + return And(this, other); + } + + or(other: Bool | boolean): Bool { + return Or(this, other); + } + + xor(other: Bool | boolean): Bool { + return Xor(this, other); + } + + implies(other: Bool | boolean): Bool { + return Implies(this, other); + } + + iff(other: Bool | boolean): Bool { + return Iff(this, other); + } + } + + // isBool will return false which is unlike the python API (but like the C API) + class LambdaImpl, RangeSort extends Sort> + extends QuantifierImpl> + implements + Quantifier>, + SMTArray + { + declare readonly __typename: 'Lambda'; domain(): DomainSort[0] { return this.sort.domain(); @@ -1879,35 +2467,17 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.sort.range(); } - select(...indices: ArrayIndexType): SortToExprMap { - const args = indices.map((arg, i) => this.domain_n(i).cast(arg as any)); - if (args.length === 1) { - return _toExpr(check(Z3.mk_select(contextPtr, this.ast, args[0].ast))) as SortToExprMap; - } - const _args = args.map(arg => arg.ast); - return _toExpr(check(Z3.mk_select_n(contextPtr, this.ast, _args))) as SortToExprMap; + select(...indices: CoercibleToArrayIndexType): SortToExprMap { + return Select(this, ...indices); } store( ...indicesAndValue: [ - ...ArrayIndexType, - CoercibleFromMap, Name> + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, ] ): SMTArray { - const args = indicesAndValue.map((arg, i) => { - if (i === indicesAndValue.length - 1) { - return this.range().cast(arg as CoercibleFromMap, Name>); - } - return this.domain_n(i).cast(arg as any); - }); - if (args.length <= 1) { - throw new Z3Error("Array store requires both index and value arguments"); - } - if (args.length === 2) { - return _toExpr(check(Z3.mk_store(contextPtr, this.ast, args[0].ast, args[1].ast))) as SMTArray; - } - const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast); - return _toExpr(check(Z3.mk_store_n(contextPtr, this.ast, _idxs, args[args.length - 1].ast))) as SMTArray; + return Store(this, ...indicesAndValue); } } @@ -1929,20 +2499,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.values(); } - * entries(): IterableIterator<[number, Item]> { + *entries(): IterableIterator<[number, Item]> { const length = this.length(); for (let i = 0; i < length; i++) { yield [i, this.get(i)]; } } - * keys(): IterableIterator { + *keys(): IterableIterator { for (let [key] of this.entries()) { yield key; } } - * values(): IterableIterator { + *values(): IterableIterator { for (let [, value] of this.entries()) { yield value; } @@ -2027,7 +2597,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return Z3.ast_map_size(contextPtr, this.ptr); } - * entries(): IterableIterator<[Key, Value]> { + *entries(): IterableIterator<[Key, Value]> { for (const key of this.keys()) { yield [key, this.get(key)]; } @@ -2037,7 +2607,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new AstVectorImpl(Z3.ast_map_keys(contextPtr, this.ptr)); } - * values(): IterableIterator { + *values(): IterableIterator { for (const [_, value] of this.entries()) { yield value; } @@ -2068,6 +2638,31 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function substitute(t: Expr, ...substitutions: [Expr, Expr][]): Expr { + _assertContext(t); + const from: Z3_ast[] = []; + const to: Z3_ast[] = []; + for (const [f, t] of substitutions) { + _assertContext(f); + _assertContext(t); + from.push(f.ast); + to.push(t.ast); + } + return _toExpr(check(Z3.substitute(contextPtr, t.ast, from, to))); + } + + function ast_from_string(s: string): Ast { + const sort_names: Z3_symbol[] = []; + const sorts: Z3_sort[] = []; + const decl_names: Z3_symbol[] = []; + const decls: Z3_func_decl[] = []; + const v = new AstVectorImpl(check(Z3.parse_smtlib2_string(contextPtr, s, sort_names, sorts, decl_names, decls))); + if (v.length() !== 1) { + throw new Error('Expected exactly one AST. Instead got ' + v.length() + ': ' + v.sexpr()); + } + return v.get(0); + } + const ctx: Context = { ptr: contextPtr, name, @@ -2089,6 +2684,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { isAst, isSort, isFuncDecl, + isFuncInterp, isApp, isConst, isExpr, @@ -2103,6 +2699,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { isNot, isEq, isDistinct, + isQuantifier, isArith, isArithSort, isInt, @@ -2147,11 +2744,15 @@ export function createApi(Z3: Z3Core): Z3HighLevel { FreshConst, Var, Implies, + Iff, Eq, Xor, Not, And, Or, + ForAll, + Exists, + Lambda, ToReal, ToInt, IsInt, @@ -2161,6 +2762,36 @@ export function createApi(Z3: Z3Core): Z3HighLevel { Int2BV, Concat, Cond, + LT, + GT, + LE, + GE, + ULT, + UGT, + ULE, + UGE, + SLT, + SGT, + SLE, + SGE, + Sum, + Sub, + Product, + Div, + BUDiv, + Neg, + Mod, + Select, + Store, + Extract, + + substitute, + simplify, + + ///////////// + // Loading // + ///////////// + ast_from_string, }; cleanup.register(ctx, () => Z3.del_context(contextPtr)); return ctx; diff --git a/src/api/js/src/high-level/types.ts b/src/api/js/src/high-level/types.ts index a6cb01e79d3..c4b56044208 100644 --- a/src/api/js/src/high-level/types.ts +++ b/src/api/js/src/high-level/types.ts @@ -5,13 +5,13 @@ import { Z3_context, Z3_decl_kind, Z3_func_decl, + Z3_func_entry, Z3_func_interp, Z3_model, Z3_probe, Z3_solver, Z3_sort, Z3_sort_kind, - Z3_symbol, Z3_tactic, } from '../low-level'; @@ -21,7 +21,7 @@ export type AnySort = | BoolSort | ArithSort | BitVecSort - | SMTArraySort, ...AnySort[]], AnySort>; + | SMTArraySort; /** @hidden */ export type AnyExpr = | Expr @@ -31,53 +31,64 @@ export type AnyExpr = | RatNum | BitVec | BitVecNum - | SMTArray, ...AnySort[]], AnySort>; + | SMTArray; /** @hidden */ export type AnyAst = AnyExpr | AnySort | FuncDecl; /** @hidden */ -export type SortToExprMap, Name extends string = 'main'> = - S extends BoolSort - ? Bool - : S extends ArithSort - ? Arith - : S extends BitVecSort - ? BitVec - : S extends SMTArraySort - ? SMTArray - : S extends Sort - ? Expr - : never; +export type SortToExprMap, Name extends string = 'main'> = S extends BoolSort + ? Bool + : S extends ArithSort + ? Arith + : S extends BitVecSort + ? BitVec + : S extends SMTArraySort + ? SMTArray + : S extends Sort + ? Expr + : never; /** @hidden */ -export type CoercibleToExprMap, Name extends string = 'main'> = - S extends bigint - ? ArithSort - : S extends number | CoercibleRational - ? RatNum - : S extends boolean - ? Bool - : S extends Expr - ? S - : never; +export type CoercibleFromMap, Name extends string = 'main'> = S extends bigint + ? Arith + : S extends number | CoercibleRational + ? RatNum + : S extends boolean + ? Bool + : S extends Expr + ? S + : never; /** @hidden */ -export type CoercibleFromMap, Name extends string = 'main'> = - S extends Bool - ? (boolean | Bool) - : S extends IntNum - ? (bigint | number | IntNum) - : S extends RatNum - ? (bigint | number | CoercibleRational | RatNum) - : S extends Arith - ? (bigint | number | CoercibleRational | Arith) - : S extends BitVec - ? (number | BitVec) - : S extends SMTArray - ? SMTArray - : S extends Expr - ? Expr - : never; +export type CoercibleToBitVec = + | bigint + | number + | BitVec; + +export type CoercibleRational = { numerator: bigint | number; denominator: bigint | number }; + +/** @hidden */ +export type CoercibleToExpr = number | bigint | boolean | CoercibleRational | Expr; + +/** @hidden */ +export type CoercibleToArith = number | string | bigint | CoercibleRational | Arith; + +/** @hidden */ +export type CoercibleToMap, Name extends string = 'main'> = T extends Bool + ? boolean | Bool + : T extends IntNum + ? bigint | number | IntNum + : T extends RatNum + ? bigint | number | CoercibleRational | RatNum + : T extends Arith + ? CoercibleToArith + : T extends BitVec + ? CoercibleToBitVec + : T extends SMTArray + ? SMTArray + : T extends Expr + ? Expr + : never; /** * Used to create a Real constant @@ -97,16 +108,10 @@ export type CoercibleFromMap, Name extends string = 'mai * @see {@link Context.from} * @category Global */ -export type CoercibleRational = { numerator: bigint | number; denominator: bigint | number }; -/** @hidden */ -export type CoercibleToExpr = number | bigint | boolean | CoercibleRational | Expr; - -export class Z3Error extends Error { -} +export class Z3Error extends Error {} -export class Z3AssertionError extends Z3Error { -} +export class Z3AssertionError extends Z3Error {} /** @category Global */ export type CheckSatResult = 'sat' | 'unsat' | 'unknown'; @@ -149,6 +154,9 @@ export interface Context { /** @category Functions */ isFuncDecl(obj: unknown): obj is FuncDecl; + /** @category Functions */ + isFuncInterp(obj: unknown): obj is FuncInterp; + /** @category Functions */ isApp(obj: unknown): boolean; @@ -191,6 +199,9 @@ export interface Context { /** @category Functions */ isDistinct(obj: unknown): boolean; + /** @category Functions */ + isQuantifier(obj: unknown): obj is Quantifier; + /** @category Functions */ isArith(obj: unknown): obj is Arith; @@ -225,10 +236,10 @@ export interface Context { isBitVecVal(obj: unknown): obj is BitVecNum; /** @category Functions */ - isArraySort(obj: unknown): obj is SMTArraySort, ...AnySort[]], AnySort>; + isArraySort(obj: unknown): obj is SMTArraySort; /** @category Functions */ - isArray(obj: unknown): obj is SMTArray, ...AnySort[]], AnySort>; + isArray(obj: unknown): obj is SMTArray; /** @category Functions */ isConstArray(obj: unknown): boolean; @@ -315,7 +326,11 @@ export interface Context { /** @category Classes */ readonly AstVector: new = AnyAst>() => AstVector; /** @category Classes */ - readonly AstMap: new = AnyAst, Value extends Ast = AnyAst>() => AstMap; + readonly AstMap: new = AnyAst, Value extends Ast = AnyAst>() => AstMap< + Name, + Key, + Value + >; /** @category Classes */ readonly Tactic: new (name: string) => Tactic; @@ -363,7 +378,7 @@ export interface Context { condition: Bool | boolean, onTrue: OnTrueRef, onFalse: OnFalseRef, - ): CoercibleToExprMap; + ): CoercibleFromMap; /** @category Operations */ Distinct(...args: CoercibleToExpr[]): Bool; @@ -371,6 +386,9 @@ export interface Context { /** @category Operations */ Implies(a: Bool | boolean, b: Bool | boolean): Bool; + /** @category Operations */ + Iff(a: Bool | boolean, b: Bool | boolean): Bool; + /** @category Operations */ Eq(a: CoercibleToExpr, b: CoercibleToExpr): Bool; @@ -407,6 +425,28 @@ export interface Context { /** @category Operations */ Or(...args: Probe[]): Probe; + // Quantifiers + + /** @category Operations */ + ForAll>( + quantifiers: ArrayIndexType, + body: Bool, + weight?: number, + ): Quantifier> & Bool; + + /** @category Operations */ + Exists>( + quantifiers: ArrayIndexType, + body: Bool, + weight?: number, + ): Quantifier> & Bool; + + /** @category Operations */ + Lambda, RangeSort extends Sort>( + quantifiers: ArrayIndexType, + expr: SortToExprMap, + ): Quantifier> & SMTArray; + // Arithmetic /** @category Operations */ ToReal(expr: Arith | bigint): Arith; @@ -437,7 +477,7 @@ export interface Context { * // a**(1/2) * ``` * @category Operations */ - Sqrt(a: Arith | number | bigint | string | CoercibleRational): Arith; + Sqrt(a: CoercibleToArith): Arith; /** * Returns a Z3 expression representing cubic root of a @@ -449,7 +489,7 @@ export interface Context { * // a**(1/3) * ``` * @category Operations */ - Cbrt(a: Arith | number | bigint | string | CoercibleRational): Arith; + Cbrt(a: CoercibleToArith): Arith; // Bit Vectors /** @category Operations */ @@ -462,7 +502,102 @@ export interface Context { Concat(...bitvecs: BitVec[]): BitVec; /** @category Operations */ - Cond(probe: Probe, onTrue: Tactic, onFalse: Tactic): Tactic + Cond(probe: Probe, onTrue: Tactic, onFalse: Tactic): Tactic; + + // Arith + + /** @category Operations */ + LT(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + GT(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + LE(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + GE(a: Arith, b: CoercibleToArith): Bool; + + // Bit Vectors + + /** @category Operations */ + ULT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + UGT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + ULE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + UGE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SLT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SGT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SGE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SLE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + Sum(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Sum(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Sub(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Sub(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Product(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Product(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Div(arg0: Arith, arg1: CoercibleToArith): Arith; + + Div(arg0: BitVec, arg1: CoercibleToBitVec): BitVec; + + BUDiv(arg0: BitVec, arg1: CoercibleToBitVec): BitVec; + + Neg(a: Arith): Arith; + + Neg(a: BitVec): BitVec; + + Mod(a: Arith, b: CoercibleToArith): Arith; + + Mod(a: BitVec, b: CoercibleToBitVec): BitVec; + + // Arrays + + /** @category Operations */ + Select, RangeSort extends Sort = Sort>( + array: SMTArray, + ...indices: CoercibleToArrayIndexType + ): SortToExprMap; + + /** @category Operations */ + Store, RangeSort extends Sort = Sort>( + array: SMTArray, + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray; + + /** @category Operations */ + Extract(hi: number, lo: number, val: BitVec): BitVec; + + /** @category Operations */ + ast_from_string(s: string): Ast; + + /** @category Operations */ + substitute(t: Expr, ...substitutions: [Expr, Expr][]): Expr; + + simplify(expr: Expr): Promise>; } export interface Ast { @@ -490,7 +625,7 @@ export interface Ast { /** @hidden */ export interface SolverCtor { - new(): Solver; + new (): Solver; } export interface Solver { @@ -500,10 +635,11 @@ export interface Solver { readonly ctx: Context; readonly ptr: Z3_solver; - /* TODO(ritave): Decide on how to discern between integer and float parameters set(key: string, value: any): void; - set(params: Record): void; - */ + + /* TODO(ritave): Decide on how to discern between integer and float parameters + set(params: Record): void; + */ push(): void; pop(num?: number): void; @@ -527,7 +663,7 @@ export interface Solver { /** @hidden */ export interface ModelCtor { - new(): Model; + new (): Model; } export interface Model extends Iterable> { @@ -566,6 +702,13 @@ export interface Model extends Iterable): Expr; get(sort: Sort): AstVector>; + + updateValue(decl: FuncDecl | Expr, a: Ast | FuncInterp): void; + + addFuncInterp[] = Sort[], RangeSort extends Sort = Sort>( + decl: FuncDecl, + defaultValue: CoercibleToMap, Name>, + ): FuncInterp; } /** @@ -608,6 +751,23 @@ export interface Sort extends Ast { name(): string | number; } +/** + * @category Functions + */ +export interface FuncEntry { + /** @hidden */ + readonly __typename: 'FuncEntry'; + + readonly ctx: Context; + readonly ptr: Z3_func_entry; + + numArgs(): number; + + argValue(i: number): Expr; + + value(): Expr; +} + /** * @category Functions */ @@ -617,6 +777,16 @@ export interface FuncInterp { readonly ctx: Context; readonly ptr: Z3_func_interp; + + elseValue(): Expr; + + numEntries(): number; + + arity(): number; + + entry(i: number): FuncEntry; + + addEntry(args: Expr[], value: Expr): void; } /** @hidden */ @@ -639,9 +809,14 @@ export interface FuncDeclCreation { * @param name Name of the function * @param signature The domains, and last parameter - the range of the function */ - declare(name: string, ...signature: FuncDeclSignature): FuncDecl; - - fresh(...signature: FuncDeclSignature): FuncDecl; + declare[], RangeSort extends Sort>( + name: string, + ...signature: [...DomainSort, RangeSort] + ): FuncDecl; + + fresh[], RangeSort extends Sort>( + ...signature: [...DomainSort, RangeSort] + ): FuncDecl; } /** @@ -656,7 +831,11 @@ export interface RecFuncCreation { /** * @category Functions */ -export interface FuncDecl extends Ast { +export interface FuncDecl< + Name extends string = 'main', + DomainSort extends Sort[] = Sort[], + RangeSort extends Sort = Sort, +> extends Ast { /** @hidden */ readonly __typename: 'FuncDecl'; @@ -664,21 +843,26 @@ export interface FuncDecl extends Ast; + domain(i: T): DomainSort[T]; - range(): Sort; + range(): RangeSort; kind(): Z3_decl_kind; - params(): (number | string | Z3_symbol | Sort | Expr | FuncDecl)[]; + params(): (number | string | Sort | Expr | FuncDecl)[]; - call(...args: CoercibleToExpr[]): AnyExpr; + call(...args: CoercibleToArrayIndexType): SortToExprMap; } export interface Expr = AnySort, Ptr = unknown> extends Ast { /** @hidden */ - readonly __typename: 'Expr' | Bool['__typename'] | Arith['__typename'] | BitVec['__typename'] | SMTArray['__typename']; + readonly __typename: + | 'Expr' + | Bool['__typename'] + | Arith['__typename'] + | BitVec['__typename'] + | SMTArray['__typename']; get sort(): S; @@ -688,6 +872,8 @@ export interface Expr = AnySo params(): ReturnType['params']>; + name(): ReturnType['name']>; + decl(): FuncDecl; numArgs(): number; @@ -725,7 +911,7 @@ export interface BoolCreation { /** @category Booleans */ export interface Bool extends Expr, Z3_ast> { /** @hidden */ - readonly __typename: 'Bool'; + readonly __typename: 'Bool' | 'NonLambdaQuantifier'; not(): Bool; @@ -738,6 +924,13 @@ export interface Bool extends Expr | boolean): Bool; } +// TODO: properly implement pattern +/** @category Quantifiers */ +export interface Pattern { + /** @hidden */ + readonly __typename: 'Pattern'; +} + /** * A Sort that represents Integers or Real numbers * @category Arithmetic @@ -798,17 +991,17 @@ export interface Arith extends Expr | number | bigint | string): Arith; + add(other: CoercibleToArith): Arith; /** * Multiplies two numbers together */ - mul(other: Arith | number | bigint | string): Arith; + mul(other: CoercibleToArith): Arith; /** * Subtract second number from the first one */ - sub(other: Arith | number | bigint | string): Arith; + sub(other: CoercibleToArith): Arith; /** * Applies power to the number @@ -820,12 +1013,12 @@ export interface Arith extends Expr | number | bigint | string): Arith; + pow(exponent: CoercibleToArith): Arith; /** * Divides the number by the second one */ - div(other: Arith | number | bigint | string): Arith; + div(other: CoercibleToArith): Arith; /** * Returns a number modulo second one @@ -837,7 +1030,7 @@ export interface Arith extends Expr | number | bigint | string): Arith; + mod(other: CoercibleToArith): Arith; /** * Returns a negation of the number @@ -847,22 +1040,22 @@ export interface Arith extends Expr | number | bigint | string): Bool; + le(other: CoercibleToArith): Bool; /** * Returns whether the number is less than the second one (`<`) */ - lt(other: Arith | number | bigint | string): Bool; + lt(other: CoercibleToArith): Bool; /** * Returns whether the number is greater than the second one (`>`) */ - gt(other: Arith | number | bigint | string): Bool; + gt(other: CoercibleToArith): Bool; /** * Returns whether the number is greater or equal than the second one (`>=`) */ - ge(other: Arith | number | bigint | string): Bool; + ge(other: CoercibleToArith): Bool; } /** @@ -939,12 +1132,6 @@ export interface BitVecSort): Expr; } -/** @hidden */ -export type CoercibleToBitVec = - | bigint - | number - | BitVec; - /** @category Bit Vectors */ export interface BitVecCreation { sort(bits: Bits): BitVecSort; @@ -1213,10 +1400,11 @@ export interface BitVecNum, ...AnySort[]] = [Sort, ...Sort[]], +export interface SMTArraySort< + Name extends string = 'main', + DomainSort extends NonEmptySortArray = [Sort, ...Sort[]], RangeSort extends AnySort = AnySort, - > extends Sort { +> extends Sort { /** @hidden */ readonly __typename: 'ArraySort'; @@ -1236,36 +1424,47 @@ export interface SMTArraySort { - sort, ...AnySort[]], RangeSort extends AnySort>( + sort, RangeSort extends Sort>( ...sig: [...DomainSort, RangeSort] ): SMTArraySort; - const, ...AnySort[]], RangeSort extends AnySort>( - name: string, ...sig: [...DomainSort, RangeSort] + const, RangeSort extends Sort>( + name: string, + ...sig: [...DomainSort, RangeSort] ): SMTArray; - consts, ...AnySort[]], RangeSort extends AnySort>( + consts, RangeSort extends Sort>( names: string | string[], ...sig: [...DomainSort, RangeSort] ): SMTArray[]; K, RangeSort extends AnySort>( domain: DomainSort, - value: SortToExprMap + value: SortToExprMap, ): SMTArray; } -export type ArrayIndexType, ...AnySort[]] = [Sort, ...Sort[]]> = [...{ - [Index in keyof DomainSort]: DomainSort[Index] extends AnySort ? - CoercibleFromMap, Name> : - DomainSort[Index]; -}] +export type NonEmptySortArray = [Sort, ...Array>]; + +export type ArrayIndexType[]> = [ + ...{ + [Key in keyof DomainSort]: DomainSort[Key] extends AnySort + ? SortToExprMap + : DomainSort[Key]; + }, +]; + +export type CoercibleToArrayIndexType[]> = [ + ...{ + [Key in keyof DomainSort]: DomainSort[Key] extends AnySort + ? CoercibleToMap, Name> + : DomainSort[Key]; + }, +]; /** * Represents Array expression @@ -1274,13 +1473,13 @@ export type ArrayIndexType, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = AnySort> - extends Expr, Z3_ast> { - +export interface SMTArray< + Name extends string = 'main', + DomainSort extends NonEmptySortArray = [Sort, ...Sort[]], + RangeSort extends Sort = Sort, +> extends Expr, Z3_ast> { /** @hidden */ - readonly __typename: 'Array'; + readonly __typename: 'Array' | 'Lambda'; domain(): DomainSort[0]; @@ -1288,7 +1487,7 @@ export interface SMTArray): SortToExprMap; + select(...indices: CoercibleToArrayIndexType): SortToExprMap; /** * value should be coercible to RangeSort @@ -1297,11 +1496,60 @@ export interface SMTArray, - CoercibleFromMap, Name> + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, ] ): SMTArray; +} + +/** + * Defines the expression type of the body of a quantifier expression + * + * @category Quantifiers + */ +export type BodyT< + Name extends string = 'main', + QVarSorts extends NonEmptySortArray = [Sort, ...Sort[]], + QSort extends BoolSort | SMTArraySort = BoolSort | SMTArraySort, +> = QSort extends BoolSort + ? Bool + : QSort extends SMTArray + ? SortToExprMap + : never; + +/** @category Quantifiers */ +export interface Quantifier< + Name extends string = 'main', + QVarSorts extends NonEmptySortArray = [Sort, ...Sort[]], + QSort extends BoolSort | SMTArraySort = BoolSort | SMTArraySort, +> extends Expr { + readonly __typename: 'NonLambdaQuantifier' | 'Lambda'; + + is_forall(): boolean; + + is_exists(): boolean; + + is_lambda(): boolean; + + weight(): number; + + num_patterns(): number; + + pattern(i: number): Pattern; + + num_no_patterns(): number; + + no_pattern(i: number): Expr; + + body(): BodyT; + + num_vars(): number; + + var_name(i: number): string | number; + + var_sort(i: T): QVarSorts[T]; + children(): [BodyT]; } export interface Probe { @@ -1314,7 +1562,7 @@ export interface Probe { /** @hidden */ export interface TacticCtor { - new(name: string): Tactic; + new (name: string): Tactic; } export interface Tactic { @@ -1327,7 +1575,7 @@ export interface Tactic { /** @hidden */ export interface AstVectorCtor { - new = AnyAst>(): AstVector; + new = AnyAst>(): AstVector; } /** @@ -1378,7 +1626,7 @@ export interface AstVector /** @hidden */ export interface AstMapCtor { - new = AnyAst, Value extends Ast = AnyAst>(): AstMap; + new = AnyAst, Value extends Ast = AnyAst>(): AstMap; } /** @@ -1402,8 +1650,11 @@ export interface AstMapCtor { * // 0 * ``` */ -export interface AstMap = AnyAst, Value extends Ast = AnyAst> - extends Iterable<[Key, Value]> { +export interface AstMap< + Name extends string = 'main', + Key extends Ast = AnyAst, + Value extends Ast = AnyAst, +> extends Iterable<[Key, Value]> { /** @hidden */ readonly __typename: 'AstMap'; From cac5052685eedb9dca304b623616fc9b8659bf58 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 13:43:44 -0800 Subject: [PATCH 408/597] fixes related to #6577 - enforce elim-and in bool-rewriter when invoking hoisting. - make cnf tactic more resilient to non-normalized input. - enable eliminate predicates on ground formulas --- src/ast/rewriter/bool_rewriter.h | 5 ++- src/ast/rewriter/hoist_rewriter.cpp | 22 +++++++++-- src/ast/rewriter/hoist_rewriter.h | 7 ++++ src/ast/simplifiers/eliminate_predicates.cpp | 2 - src/tactic/core/tseitin_cnf_tactic.cpp | 41 +++++++++++++++++++- 5 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 8f2221a8cee..48894908452 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -81,7 +81,10 @@ 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_hoist(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); + m_hoist.set(*this); + } 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); } diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 40ad4604a6b..b86f1eb47b6 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -17,16 +17,30 @@ Module Name: #include "ast/rewriter/hoist_rewriter.h" +#include "ast/rewriter/bool_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" - hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): m(m), m_args1(m), m_args2(m), m_subst(m) { updt_params(p); } +expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { + if (m_rewriter) + return m_rewriter->mk_and(args); + else + return ::mk_and(args); +} + +expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { + if (m_rewriter) + return m_rewriter->mk_or(args); + else + return ::mk_or(args); +} + br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { if (num_args < 2) return BR_FAILED; @@ -152,13 +166,13 @@ expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsi for (expr* e : m_args1) if (!preds.contains(e)) fmls.push_back(e); - args.push_back(::mk_and(fmls)); + args.push_back(mk_and(fmls)); } fmls.reset(); - fmls.push_back(::mk_or(args)); + fmls.push_back(mk_or(args)); for (auto* p : preds) fmls.push_back(p); - result = ::mk_and(fmls); + result = mk_and(fmls); return result; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index cc83bfa5622..72d44d0bce1 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -25,8 +25,11 @@ Module Name: #include "util/union_find.h" #include "util/obj_hashtable.h" +class bool_rewriter; + class hoist_rewriter { ast_manager & m; + bool_rewriter* m_rewriter = nullptr; expr_ref_vector m_args1, m_args2; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; @@ -39,6 +42,8 @@ class hoist_rewriter { expr_mark m_mark; bool is_and(expr* e, expr_ref_vector* args); + expr_ref mk_and(expr_ref_vector const& args); + expr_ref mk_or(expr_ref_vector const& args); bool is_var(expr* e) { return m_expr2var.contains(e); } expr* mk_expr(unsigned v) { return m_var2expr[v]; } @@ -48,6 +53,7 @@ class hoist_rewriter { expr_ref hoist_predicates(obj_hashtable const& p, unsigned num_args, expr* const* args); + public: hoist_rewriter(ast_manager & m, params_ref const & p = params_ref()); family_id get_fid() const { return m.get_basic_family_id(); } @@ -56,6 +62,7 @@ class hoist_rewriter { 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); + void set(bool_rewriter& r) { m_rewriter = &r; } }; struct hoist_rewriter_cfg : public default_rewriter_cfg { diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 6c1eccea00c..05f347f25e7 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -1008,8 +1008,6 @@ void eliminate_predicates::reset() { void eliminate_predicates::reduce() { - if (!m_fmls.has_quantifiers()) - return; reset(); init_clauses(); find_definitions(); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index bd2f58b442e..ec03849bc7a 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -141,6 +141,7 @@ class tseitin_cnf_tactic : public tactic { sign = !sign; goto start; case OP_OR: + case OP_AND: l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); @@ -187,6 +188,7 @@ class tseitin_cnf_tactic : public tactic { goto start; } case OP_OR: + case OP_AND: visited = false; push_frame(to_app(n)); return; @@ -197,7 +199,6 @@ class tseitin_cnf_tactic : public tactic { push_frame(to_app(n)); } return; - case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: @@ -617,6 +618,43 @@ class tseitin_cnf_tactic : public tactic { } return DONE; } + + mres match_and(app * t, bool first, bool root) { + if (!m.is_and(t)) + return NO; + if (first) { + bool visited = true; + for (expr* a : *t) + visit(a, visited); + if (!visited) + return CONT; + } + expr_ref_buffer lits(m); + expr_ref l(m), nl(m); + app_ref k(m), nk(m); + if (root) { + for (expr* arg : *t) { + get_lit(arg, false, l); + expr* lits[1] = { l }; + mk_clause(1, lits); + } + } + else { + k = mk_fresh(); + nk = m.mk_not(k); + cache_result(t, k); + + for (expr* arg : *t) { + get_lit(arg, false, l); + mk_clause(nk, l); + inv(l, nl); + lits.push_back(nl); + } + lits.push_back(k); + mk_clause(lits.size(), lits.data()); + } + return DONE; + } mres match_or(app * t, bool first, bool root) { if (!m.is_or(t)) @@ -778,6 +816,7 @@ class tseitin_cnf_tactic : public tactic { fr.m_first = false; TRY(match_or_3and); TRY(match_or); + TRY(match_and); TRY(match_iff3); // TRY(match_iff_or); TRY(match_iff); From 102eee77dcd955a0b938d198444312d46c3e0a34 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 20:12:01 -0800 Subject: [PATCH 409/597] patch regressions Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/hoist_rewriter.cpp | 2 +- src/sat/smt/arith_sls.cpp | 127 ++++++++++++------------- src/sat/smt/arith_sls.h | 42 ++++---- src/tactic/core/tseitin_cnf_tactic.cpp | 5 +- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index b86f1eb47b6..2329fd527d1 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -35,7 +35,7 @@ expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { } expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { - if (m_rewriter) + if (false && m_rewriter) return m_rewriter->mk_or(args); else return ::mk_or(args); diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 79e6aec54d2..d7a85cdb1f8 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -62,12 +62,12 @@ namespace arith { // first compute assignment to terms // then update non-basic variables in tableau. for (auto const& [t, v] : m_terms) { - rational val; + int64_t val; lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - val += arg.coeff() * value(w); + val += to_numeral(arg.coeff()) * value(w); } update(v, val); } @@ -77,15 +77,18 @@ namespace arith { continue; if (!s.lp().external_is_used(v)) continue; - rational old_value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); - rational new_value = value(v); + int64_t old_value = 0; + if (s.is_registered_var(v)) + old_value = to_numeral(s.get_ivalue(v).x); + int64_t new_value = value(v); if (old_value == new_value) continue; s.ensure_column(v); lp::column_index vj = s.lp().to_column_index(v); SASSERT(!vj.is_null()); if (!s.lp().is_base(vj.index())) { - lp::impq val(new_value); + rational new_value_(new_value, rational::i64()); + lp::impq val(new_value_, rational::zero()); s.lp().set_value_for_nbasic_column(vj.index(), val); } } @@ -120,31 +123,31 @@ namespace arith { } // distance to true - rational sls::dtt(rational const& args, ineq const& ineq) const { + int64_t sls::dtt(int64_t args, ineq const& ineq) const { switch (ineq.m_op) { case ineq_kind::LE: if (args <= ineq.m_bound) - return rational::zero(); + return 0; return args - ineq.m_bound; case ineq_kind::EQ: if (args == ineq.m_bound) - return rational::zero(); - return rational::one(); + return 0; + return 1; case ineq_kind::NE: if (args == ineq.m_bound) - return rational::one(); - return rational::zero(); + return 1; + return 0; case ineq_kind::LT: if (args < ineq.m_bound) - return rational::zero(); + return 0; return args - ineq.m_bound + 1; default: UNREACHABLE(); - return rational::zero(); + return 0; } } - rational sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { + int64_t sls::dtt(ineq const& ineq, var_t v, int64_t new_value) const { auto new_args_value = ineq.m_args_value; for (auto const& [coeff, w] : ineq.m_args) { if (w == v) { @@ -156,18 +159,18 @@ namespace arith { } // critical move - bool sls::cm(ineq const& ineq, var_t v, rational& new_value) { + bool sls::cm(ineq const& ineq, var_t v, int64_t& new_value) { SASSERT(!ineq.is_true()); - auto delta = ineq.m_args_value - ineq.m_bound; + int64_t delta = ineq.m_args_value - ineq.m_bound; if (ineq.m_op == ineq_kind::NE || ineq.m_op == ineq_kind::LT) delta--; for (auto const& [coeff, w] : ineq.m_args) { if (w == v) { if (coeff > 0) - new_value = value(v) - abs(ceil(delta / coeff)); + new_value = value(v) - abs((delta + coeff - 1)/ coeff); else - new_value = value(v) + abs(floor(delta / coeff)); + new_value = value(v) + abs(delta) / -coeff; switch (ineq.m_op) { case ineq_kind::LE: @@ -199,7 +202,7 @@ namespace arith { bool sls::flip(unsigned cl) { auto const& clause = get_clause(cl); - rational new_value; + int64_t new_value; for (literal lit : clause) { if (is_true(lit)) continue; @@ -246,7 +249,7 @@ namespace arith { bool sls::flip_dscore(unsigned cl) { auto const& clause = get_clause(cl); - rational new_value, min_value, min_score(-1); + int64_t new_value, min_value, min_score(-1); var_t min_var = UINT_MAX; for (auto lit : clause) { auto const* ineq = atom(lit); @@ -254,7 +257,7 @@ namespace arith { continue; for (auto const& [coeff, v] : ineq->m_args) { if (cm(*ineq, v, new_value)) { - rational score = dscore(v, new_value); + int64_t score = dscore(v, new_value); if (UINT_MAX == min_var || score < min_score) { min_var = v; min_value = new_value; @@ -290,22 +293,22 @@ namespace arith { // TODO - use cached dts instead of computed dts // cached dts has to be updated when the score of literals are updated. // - rational sls::dscore(var_t v, rational const& new_value) const { + int64_t sls::dscore(var_t v, int64_t new_value) const { auto const& vi = m_vars[v]; - rational score(0); + int64_t score(0); for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); + score += (compute_dts(cl) - dts(cl, v, new_value)) * int64_t(get_weight(cl)); return score; } - int sls::cm_score(var_t v, rational const& new_value) { + int sls::cm_score(var_t v, int64_t new_value) { int score = 0; auto& vi = m_vars[v]; for (auto const& [coeff, lit] : vi.m_literals) { auto const& ineq = *atom(lit); - rational dtt_old = dtt(ineq); - rational dtt_new = dtt(ineq, v, new_value); + int64_t dtt_old = dtt(ineq); + int64_t dtt_new = dtt(ineq, v, new_value); for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); if (!clause.is_true()) { @@ -322,8 +325,8 @@ namespace arith { return score; } - rational sls::compute_dts(unsigned cl) const { - rational d(1), d2; + int64_t sls::compute_dts(unsigned cl) const { + int64_t d(1), d2; bool first = true; for (auto a : get_clause(cl)) { auto const* ineq = atom(a); @@ -340,8 +343,8 @@ namespace arith { return d; } - rational sls::dts(unsigned cl, var_t v, rational const& new_value) const { - rational d(1), d2; + int64_t sls::dts(unsigned cl, var_t v, int64_t new_value) const { + int64_t d(1), d2; bool first = true; for (auto lit : get_clause(cl)) { auto const* ineq = atom(lit); @@ -358,28 +361,17 @@ namespace arith { return d; } - void sls::update(var_t v, rational const& new_value) { + void sls::update(var_t v, int64_t new_value) { auto& vi = m_vars[v]; auto const& old_value = vi.m_value; for (auto const& [coeff, lit] : vi.m_literals) { auto& ineq = *atom(lit); - rational dtt_old = dtt(ineq); ineq.m_args_value += coeff * (new_value - old_value); - rational dtt_new = dtt(ineq); - if ((dtt_new == 0) == is_true(lit)) { - dtt(ineq) = dtt_new; - continue; - } - VERIFY((dtt_old == 0) == is_true(lit)); - VERIFY(!(dtt_new == 0 && dtt_new < dtt_old) || !is_true(lit)); - VERIFY(!(dtt_old == 0 && dtt_new > dtt_old) || is_true(lit)); - if (dtt_new == 0 && dtt_new < dtt_old) // flip from false to true - m_bool_search->flip(lit.var()); - else if (dtt_old == 0 && dtt_old < dtt_new) // flip from true to false - m_bool_search->flip(lit.var()); - dtt(ineq) = dtt_new; - - VERIFY((dtt_new == 0) == is_true(lit)); + int64_t dtt_new = dtt(ineq); + if ((dtt_new == 0) != is_true(lit)) + m_bool_search->flip(lit.var()); + + SASSERT((dtt_new == 0) == is_true(lit)); } vi.m_value = new_value; } @@ -387,21 +379,20 @@ namespace arith { void sls::add_vars() { SASSERT(m_vars.empty()); for (unsigned v = 0; v < s.get_num_vars(); ++v) { - rational value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); - value = s.is_int(v) ? ceil(value) : value; + int64_t value = s.is_registered_var(v) ? to_numeral(s.get_ivalue(v).x) : 0; auto k = s.is_int(v) ? sls::var_kind::INT : sls::var_kind::REAL; m_vars.push_back({ value, value, k, {} }); } } - sls::ineq& sls::new_ineq(ineq_kind op, rational const& bound) { + sls::ineq& sls::new_ineq(ineq_kind op, int64_t const& bound) { auto* i = alloc(ineq); i->m_bound = bound; i->m_op = op; return *i; } - void sls::add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v) { + void sls::add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v) { ineq.m_args.push_back({ c, v }); ineq.m_args_value += c * value(v); m_vars[v].m_literals.push_back({ c, lit }); @@ -426,36 +417,42 @@ namespace arith { bool has_hi = s.lp().has_upper_bound(vi.index(), ci, hi, is_strict_hi); if (has_lo && has_hi && lo == hi) { - auto& ineq = new_ineq(sls::ineq_kind::EQ, lo); + auto& ineq = new_ineq(sls::ineq_kind::EQ, to_numeral(lo)); sat::literal lit(bvars++); - add_arg(lit, ineq, rational::one(), v); + add_arg(lit, ineq, 1, v); add_ineq(lit, ineq); continue; } if (has_lo) { - auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, -lo); + auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(-lo)); sat::literal lit(bvars++); - add_arg(lit, ineq, -rational::one(), v); + add_arg(lit, ineq, -1, v); add_ineq(lit, ineq); } if (has_hi) { - auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, hi); + auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(hi)); sat::literal lit(bvars++); - add_arg(lit, ineq, rational::one(), v); + add_arg(lit, ineq, 1, v); add_ineq(lit, ineq); } } } + int64_t sls::to_numeral(rational const& r) { + if (r.is_int64()) + return r.get_int64(); + return 0; + } + - void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, rational sign) { + void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - add_arg(lit, ineq, sign * arg.coeff(), w); + add_arg(lit, ineq, sign * to_numeral(arg.coeff()), w); } } else @@ -489,9 +486,9 @@ namespace arith { } if (should_minus) bound.neg(); - auto& ineq = new_ineq(op, bound); + auto& ineq = new_ineq(op, to_numeral(bound)); - add_args(lit, ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); + add_args(lit, ineq, t, b->get_var(), should_minus ? -1 : 1); m_literals.set(lit.index(), &ineq); return; } @@ -503,9 +500,9 @@ namespace arith { theory_var v = s.get_th_var(r); lp::tv tu = s.get_tv(u); lp::tv tv = s.get_tv(v); - auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); - add_args(lit, ineq, tu, u, rational::one()); - add_args(lit, ineq, tv, v, -rational::one()); + auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, 0); + add_args(lit, ineq, tu, u, 1); + add_args(lit, ineq, tv, v, -1); m_literals.set(lit.index(), &ineq); return; } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 9a4cfcd81cc..9682376bef0 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -58,10 +58,10 @@ namespace arith { public: // encode args <= bound, args = bound, args < bound struct ineq { - vector> m_args; + vector> m_args; ineq_kind m_op = ineq_kind::LE; - rational m_bound; - rational m_args_value; + int64_t m_bound; + int64_t m_args_value; bool is_true() const { switch (m_op) { @@ -94,15 +94,15 @@ namespace arith { private: struct var_info { - rational m_value; - rational m_best_value; + int64_t m_value; + int64_t m_best_value; var_kind m_kind = var_kind::INT; - vector> m_literals; + vector> m_literals; }; struct clause { unsigned m_weight = 1; - rational m_dts = rational::one(); + int64_t m_dts = 1; }; solver& s; @@ -137,27 +137,29 @@ namespace arith { bool flip_dscore(); bool flip_dscore(unsigned cl); bool flip(unsigned cl); - rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } - rational dtt(rational const& args, ineq const& ineq) const; - rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; - rational dts(unsigned cl, var_t v, rational const& new_value) const; - rational compute_dts(unsigned cl) const; - bool cm(ineq const& ineq, var_t v, rational& new_value); - int cm_score(var_t v, rational const& new_value); - void update(var_t v, rational const& new_value); + int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } + int64_t dtt(int64_t args, ineq const& ineq) const; + int64_t dtt(ineq const& ineq, var_t v, int64_t new_value) const; + int64_t dts(unsigned cl, var_t v, int64_t new_value) const; + int64_t compute_dts(unsigned cl) const; + bool cm(ineq const& ineq, var_t v, int64_t& new_value); + int cm_score(var_t v, int64_t new_value); + void update(var_t v, int64_t new_value); void paws(); - rational dscore(var_t v, rational const& new_value) const; + int64_t dscore(var_t v, int64_t new_value) const; void save_best_values(); void add_vars(); - sls::ineq& new_ineq(ineq_kind op, rational const& bound); - void add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v); + sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); + void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); void add_bounds(sat::literal_vector& bounds); - void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, rational sign); + void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); void init_literal(sat::literal lit); void init_bool_var_assignment(sat::bool_var v); void init_literal_assignment(sat::literal lit); - rational value(var_t v) const { return m_vars[v].m_value; } + int64_t value(var_t v) const { return m_vars[v].m_value; } + int64_t to_numeral(rational const& r); + public: sls(solver& s); lbool operator ()(bool_vector& phase); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index ec03849bc7a..411b8aa6edb 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -141,7 +141,7 @@ class tseitin_cnf_tactic : public tactic { sign = !sign; goto start; case OP_OR: - case OP_AND: + // case OP_AND: l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); @@ -188,7 +188,7 @@ class tseitin_cnf_tactic : public tactic { goto start; } case OP_OR: - case OP_AND: + // case OP_AND: visited = false; push_frame(to_app(n)); return; @@ -202,6 +202,7 @@ class tseitin_cnf_tactic : public tactic { case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: + case OP_AND: throw_op_not_handled(); default: return; From bb81bc5452f9c93a1044544b8b4d6034b22177ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 20:21:53 -0800 Subject: [PATCH 410/597] fix #6580 Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_sls.cpp | 2 +- src/smt/theory_lra.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index d7a85cdb1f8..7afabe2474e 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -62,7 +62,7 @@ namespace arith { // first compute assignment to terms // then update non-basic variables in tableau. for (auto const& [t, v] : m_terms) { - int64_t val; + int64_t val = 0; lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c5b706fded3..d26ea516ac9 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1596,8 +1596,12 @@ class theory_lra::imp { final_check_status eval_power(expr* e) { expr* x, * y; + rational r; VERIFY(a.is_power(e, x, y)); - + if (a.is_numeral(x, r) && r == 0 && a.is_numeral(y, r) && r == 0) + return FC_DONE; + if (!m_nla) + return FC_GIVEUP; switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y), m_nla_lemma_vector)) { case l_true: return FC_DONE; From 7956cf120147b4b8cdfb74851196d1026c8c57ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 20:55:44 -0800 Subject: [PATCH 411/597] annotate arith_sls --- src/sat/sat_ddfw.h | 1 + src/sat/smt/arith_sls.cpp | 13 +++++++++++++ src/sat/smt/euf_local_search.cpp | 7 ++----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 9971033e3f6..ee1c73b6e96 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -52,6 +52,7 @@ namespace sat { p(p), i(lit.index()) {} unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; } unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; } + unsigned size() const { return p.m_use_list_index[i + 1] - p.m_use_list_index[i]; } }; protected: diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 7afabe2474e..809295cca34 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -90,6 +90,7 @@ namespace arith { rational new_value_(new_value, rational::i64()); lp::impq val(new_value_, rational::zero()); s.lp().set_value_for_nbasic_column(vj.index(), val); + // TODO - figure out why this leads to unsound (unsat). } } } @@ -147,6 +148,11 @@ namespace arith { } } + // + // dtt is high overhead. It walks ineq.m_args + // m_vars[w].m_value can be computed outside and shared among calls + // different data-structures for storing coefficients + // int64_t sls::dtt(ineq const& ineq, var_t v, int64_t new_value) const { auto new_args_value = ineq.m_args_value; for (auto const& [coeff, w] : ineq.m_args) { @@ -302,6 +308,12 @@ namespace arith { return score; } + // + // cm_score is costly. It involves several cache misses. + // Note that + // - m_bool_search->get_use_list(lit).size() is "often" 1 or 2 + // - dtt_old can be saved + // int sls::cm_score(var_t v, int64_t new_value) { int score = 0; auto& vi = m_vars[v]; @@ -309,6 +321,7 @@ namespace arith { auto const& ineq = *atom(lit); int64_t dtt_old = dtt(ineq); int64_t dtt_new = dtt(ineq, v, new_value); + for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); if (!clause.is_true()) { diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index fab3b7815b4..5357c083723 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -81,11 +81,8 @@ namespace euf { return; } euf::enode* n = m_egraph.find(m_bool_var2expr.get(l.var(), nullptr)); - for (auto const& thv : enode_th_vars(n)) { - auto* th = m_id2solver.get(thv.get_id(), nullptr); - if (th) - th->set_bounds(n); - } + for (auto* s : m_solvers) + s->set_bounds(n); }; for (auto cl : bool_search.unsat_set()) { From 52804b5c8f1c67bf6b699d17bb33b2424e2832d0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Feb 2023 08:29:32 -0800 Subject: [PATCH 412/597] save on dtt Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_sls.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 809295cca34..34d4ac84e74 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -317,10 +317,12 @@ namespace arith { int sls::cm_score(var_t v, int64_t new_value) { int score = 0; auto& vi = m_vars[v]; + int64_t old_value = vi.m_value; for (auto const& [coeff, lit] : vi.m_literals) { auto const& ineq = *atom(lit); int64_t dtt_old = dtt(ineq); - int64_t dtt_new = dtt(ineq, v, new_value); + int64_t delta = coeff * (new_value - old_value); + int64_t dtt_new = dtt(ineq.m_args_value + delta, ineq); for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); @@ -331,7 +333,7 @@ namespace arith { } else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals continue; - else if (all_of(*clause.m_clause, [&](auto lit) { return !atom(lit) || dtt(*atom(lit), v, new_value) > 0; })) // ?? TODO + else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || lit == lit2 || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO --score; } } From 2b7701299371a72c66aa2bab739dca45db9a19bb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Feb 2023 08:36:12 -0800 Subject: [PATCH 413/597] fix build --- src/sat/smt/arith_sls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 34d4ac84e74..8183fe1bad5 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -333,7 +333,7 @@ namespace arith { } else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals continue; - else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || lit == lit2 || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO + else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO --score; } } From 3dc91de531dd835b723b5d572f5024a491a966cf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Feb 2023 13:20:55 -0800 Subject: [PATCH 414/597] fix #6582 --- src/math/lp/nla_core.cpp | 7 +++++++ src/math/lp/nla_core.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 386ad296d3b..4d1cc6edb65 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1745,6 +1745,13 @@ bool core::influences_nl_var(lpvar j) const { return false; } +void core::set_use_nra_model(bool m) { + if (m != m_use_nra_model) { + trail().push(value_trail(m_use_nra_model)); + m_use_nra_model = m; + } +} + void core::collect_statistics(::statistics & st) { st.update("arith-nla-explanations", m_stats.m_nla_explanations); st.update("arith-nla-lemmas", m_stats.m_nla_lemmas); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 016ff6e9dec..938bcbe8367 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -418,7 +418,7 @@ class core { bool var_is_big(lpvar) const; bool has_real(const factorization&) const; bool has_real(const monic& m) const; - void set_use_nra_model(bool m) { m_use_nra_model = m; } + void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } void collect_statistics(::statistics&); private: From 9ce5fe707d5d6444843d1647c969f18ccb614d74 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 11:09:11 -0800 Subject: [PATCH 415/597] track assumptions when parsing into a solver. This enables solver.from_file/solver.from_string to support assumptions/cores #6587 Signed-off-by: Nikolaj Bjorner --- src/api/api_parsers.cpp | 10 +++++++--- src/api/api_solver.cpp | 7 +++++-- src/cmd_context/cmd_context.cpp | 11 ++++------- src/cmd_context/cmd_context.h | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 899750ef7b5..94a955d4bf8 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -128,7 +128,8 @@ extern "C" { static Z3_ast_vector Z3_parser_context_parse_stream(Z3_context c, scoped_ptr& ctx, bool owned, std::istream& is) { Z3_TRY; - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + ast_manager& m = mk_c(c)->m(); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); std::stringstream errstrm; ctx->set_regular_stream(errstrm); @@ -147,8 +148,11 @@ extern "C" { SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str()); return of_ast_vector(v); } - for (expr* e : ctx->tracked_assertions()) - v->m_ast_vector.push_back(e); + for (auto const& [asr, an] : ctx->tracked_assertions()) + if (an) + v->m_ast_vector.push_back(m.mk_implies(an, asr)); + else + v->m_ast_vector.push_back(asr); ctx->reset_tracked_assertions(); return of_ast_vector(v); Z3_CATCH_RETURN(nullptr); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index ca39329bbe4..2c19d0d9eeb 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -314,8 +314,11 @@ extern "C" { bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); - for (expr* e : ctx->tracked_assertions()) - to_solver(s)->assert_expr(e); + for (auto const& [asr, an] : ctx->tracked_assertions()) + if (an) + to_solver(s)->assert_expr(asr, an); + else + to_solver(s)->assert_expr(asr); ctx->reset_tracked_assertions(); to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); auto* ctx_s = ctx->get_solver(); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 89733a7eacb..1d3476b3d72 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -2201,21 +2201,18 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) { } -expr_ref_vector cmd_context::tracked_assertions() { - expr_ref_vector result(m()); +vector> cmd_context::tracked_assertions() { + vector> result; if (assertion_names().size() == assertions().size()) { for (unsigned i = 0; i < assertions().size(); ++i) { expr* an = assertion_names()[i]; expr* asr = assertions()[i]; - if (an) - result.push_back(m().mk_implies(an, asr)); - else - result.push_back(asr); + result.push_back({ asr, an }); } } else { for (expr * e : assertions()) - result.push_back(e); + result.push_back({ e, nullptr}); } return result; } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index f5a2477943b..b034a9ffc07 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -523,7 +523,7 @@ class cmd_context : public progress_callback, public tactic_manager, public ast_ ptr_vector const& assertions() const { return m_assertions; } ptr_vector const& assertion_names() const { return m_assertion_names; } - expr_ref_vector tracked_assertions(); + vector> tracked_assertions(); void reset_tracked_assertions(); /** From 44fcf60a72342e0580e92ed70a4d2f528a50b520 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 15:06:21 -0800 Subject: [PATCH 416/597] wip experiments with sls --- src/sat/smt/arith_sls.cpp | 19 +++++++++++++++---- src/sat/smt/arith_sls.h | 1 + src/sat/smt/euf_local_search.cpp | 14 +++++++++++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 8183fe1bad5..6cecb4dbb92 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -35,9 +35,8 @@ namespace arith { unsigned num_steps = 0; for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); - m_best_min_unsat = unsat().size(); verbose_stream() << "max arith steps " << m_max_arith_steps << "\n"; - //m_max_arith_steps = 10000; + m_max_arith_steps = std::max(1000u, m_max_arith_steps); while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) break; @@ -50,6 +49,7 @@ namespace arith { save_best_values(); } } + store_best_values(); log(); return unsat().empty() ? l_true : l_undef; } @@ -59,6 +59,11 @@ namespace arith { } void sls::save_best_values() { + for (unsigned v = 0; v < s.get_num_vars(); ++v) + m_vars[v].m_best_value = m_vars[v].m_value; + } + + void sls::store_best_values() { // first compute assignment to terms // then update non-basic variables in tableau. for (auto const& [t, v] : m_terms) { @@ -80,7 +85,7 @@ namespace arith { int64_t old_value = 0; if (s.is_registered_var(v)) old_value = to_numeral(s.get_ivalue(v).x); - int64_t new_value = value(v); + int64_t new_value = m_vars[v].m_best_value; if (old_value == new_value) continue; s.ensure_column(v); @@ -104,6 +109,9 @@ namespace arith { for (unsigned i = 0; i < d->num_clauses(); ++i) for (sat::literal lit : *d->get_clause_info(i).m_clause) init_literal(lit); + for (unsigned v = 0; v < s.s().num_vars(); ++v) + init_bool_var_assignment(v); + m_best_min_unsat = std::numeric_limits::max(); } void sls::set_bounds_begin() { @@ -115,7 +123,7 @@ namespace arith { } void sls::set_bounds_end(unsigned num_literals) { - m_max_arith_steps = (m_config.L * m_max_arith_steps) / num_literals; + m_max_arith_steps = (m_config.L * m_max_arith_steps); // / num_literals; } bool sls::flip() { @@ -323,6 +331,9 @@ namespace arith { int64_t dtt_old = dtt(ineq); int64_t delta = coeff * (new_value - old_value); int64_t dtt_new = dtt(ineq.m_args_value + delta, ineq); + + if (dtt_old == dtt_new) + continue; for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 9682376bef0..706fff1ddfa 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -148,6 +148,7 @@ namespace arith { void paws(); int64_t dscore(var_t v, int64_t new_value) const; void save_best_values(); + void store_best_values(); void add_vars(); sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 5357c083723..0572afc3922 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -40,9 +40,11 @@ namespace euf { // Non-boolean literals are assumptions to Boolean search literal_vector assumptions; +#if 0 for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) assumptions.push_back(literal(v, !bool_search.get_value(v))); +#endif verbose_stream() << "assumptions " << assumptions.size() << "\n"; @@ -51,6 +53,14 @@ namespace euf { lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); bool_search.rlimit().pop(); +#if 0 + // restore state to optimal model + auto const& mdl = bool_search.get_model(); + for (unsigned i = 0; i < mdl.size(); ++i) + if ((mdl[i] == l_true) != bool_search.get_value(i)) + bool_search.flip(i); +#endif + for (auto* th : m_solvers) th->local_search(phase); @@ -92,7 +102,9 @@ namespace euf { count_literal(l); } - m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; + m_max_bool_steps = (m_ls_config.L * num_bool); // / num_literals; + m_max_bool_steps = std::max(10000u, m_max_bool_steps); + verbose_stream() << "num literals " << num_literals << " num bool " << num_bool << " max bool steps " << m_max_bool_steps << "\n"; for (auto* th : m_solvers) th->set_bounds_end(num_literals); From a976b781a04e26abf763b2f8029c98a7b0a294b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 15:33:17 -0800 Subject: [PATCH 417/597] fix #6585 --- src/math/lp/emonics.cpp | 2 +- src/math/lp/emonics.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index 21bfe679234..bcdb81dd8f4 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -68,8 +68,8 @@ void emonics::pop(unsigned n) { TRACE("nla_solver_mons", tout << "pop: " << n << "\n";); SASSERT(invariant()); for (unsigned i = 0; i < n; ++i) { - m_u_f_stack.pop_scope(1); m_ve.pop(1); + m_u_f_stack.pop_scope(1); } SASSERT(invariant()); SASSERT(monics_are_canonized()); diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index d26d96ad66f..e4f4f484861 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -81,8 +81,8 @@ class emonics { } }; - union_find m_u_f; trail_stack m_u_f_stack; + union_find m_u_f; mutable svector m_find_key; // the key used when looking for a monic with the specific variables var_eqs& m_ve; mutable vector m_monics; // set of monics @@ -125,8 +125,8 @@ class emonics { other calls to push/pop to the var_eqs should take place. */ emonics(var_eqs& ve): - m_u_f(*this), m_u_f_stack(), + m_u_f(*this), m_ve(ve), m_visited(0), m_cg_hash(*this), From c2fe76569fdb118101e8131f982cc5bfd7cc4438 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 17:48:02 -0800 Subject: [PATCH 418/597] remove dependency on bool-rewriter in hoist rewriter deal with regression reported in https://github.com/Z3Prover/z3/commit/cac5052685eedb9dca304b623616fc9b8659bf58#commitcomment-100606067 and unit tests doc.cpp --- src/ast/rewriter/bool_rewriter.cpp | 1 + src/ast/rewriter/bool_rewriter.h | 1 - src/ast/rewriter/hoist_rewriter.cpp | 25 ++++++++++++++++--------- src/ast/rewriter/hoist_rewriter.h | 4 ++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 392b2e681c7..378c794cd13 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -32,6 +32,7 @@ void bool_rewriter::updt_params(params_ref const & _p) { m_blast_distinct = p.blast_distinct(); m_blast_distinct_threshold = p.blast_distinct_threshold(); m_ite_extra_rules = p.ite_extra_rules(); + m_hoist.set_elim_and(m_elim_and); } void bool_rewriter::get_param_descrs(param_descrs & r) { diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 48894908452..e02bf86d38f 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -83,7 +83,6 @@ class bool_rewriter { public: 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); - m_hoist.set(*this); } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 2329fd527d1..d3f5af2b573 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -28,17 +28,23 @@ hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): } expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { - if (m_rewriter) - return m_rewriter->mk_and(args); + if (m_elim_and) { + expr_ref_vector negs(m); + for (expr* a : args) + if (m.is_false(a)) + return expr_ref(m.mk_false(), m); + else if (m.is_true(a)) + continue; + else + negs.push_back(::mk_not(m, a)); + return expr_ref(::mk_not(m, mk_or(negs)), m); + } else return ::mk_and(args); } expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { - if (false && m_rewriter) - return m_rewriter->mk_or(args); - else - return ::mk_or(args); + return ::mk_or(args); } br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { @@ -159,11 +165,11 @@ 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_vector args(m), args1(m), fmls(m); for (unsigned i = 0; i < num_args; ++i) { - VERIFY(is_and(es[i], &m_args1)); + VERIFY(is_and(es[i], &args1)); fmls.reset(); - for (expr* e : m_args1) + for (expr* e : args1) if (!preds.contains(e)) fmls.push_back(e); args.push_back(mk_and(fmls)); @@ -199,6 +205,7 @@ bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { args->reset(); for (expr* arg : *to_app(e)) args->push_back(::mk_not(m, arg)); + TRACE("hoist", tout << args << " " << * args << "\n"); } return true; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index 72d44d0bce1..ae7dff3bc51 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -29,7 +29,6 @@ class bool_rewriter; class hoist_rewriter { ast_manager & m; - bool_rewriter* m_rewriter = nullptr; expr_ref_vector m_args1, m_args2; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; @@ -40,6 +39,7 @@ class hoist_rewriter { obj_map m_expr2var; ptr_vector m_var2expr; expr_mark m_mark; + bool m_elim_and = false; bool is_and(expr* e, expr_ref_vector* args); expr_ref mk_and(expr_ref_vector const& args); @@ -62,7 +62,7 @@ class hoist_rewriter { 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); - void set(bool_rewriter& r) { m_rewriter = &r; } + void set_elim_and(bool b) { m_elim_and = b; } }; struct hoist_rewriter_cfg : public default_rewriter_cfg { From 8ce0c56ff5580e199c7c940b49419e792582786b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 08:36:01 -0800 Subject: [PATCH 419/597] fix #6590 --- src/math/lp/nla_powers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index 3af31a48816..96284f26a77 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -81,6 +81,8 @@ namespace nla { lbool powers::check(lpvar r, lpvar x, lpvar y, vector& lemmas) { if (x == null_lpvar || y == null_lpvar || r == null_lpvar) return l_undef; + if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(r)) + return l_undef; core& c = m_core; if (c.use_nra_model()) From 4f20b8e2ba92de6d78fcbe0806f70a07dd63fdaf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 08:36:10 -0800 Subject: [PATCH 420/597] wip - local search --- src/sat/sat_ddfw.cpp | 101 +++++++++++++++++++++++++++++-------------- src/sat/sat_ddfw.h | 49 ++++++++++++++++++--- 2 files changed, 112 insertions(+), 38 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 747ea49400d..478e793e33e 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -9,7 +9,7 @@ DDFW Local search module for clauses - Author: + Author: Nikolaj Bjorner, Marijn Heule 2019-4-23 @@ -33,25 +33,43 @@ namespace sat { ddfw::~ddfw() { - for (auto& ci : m_clauses) { - m_alloc.del_clause(ci.m_clause); - } + for (auto& ci : m_clauses) + m_alloc.del_clause(ci.m_clause); } - lbool ddfw::check(unsigned sz, literal const* assumptions, parallel* p) { init(sz, assumptions); flet _p(m_par, p); + if (m_plugin) + check_with_plugin(); + else + check_without_plugin(); + remove_assumptions(); + log(); + return m_min_sz == 0 ? l_true : l_undef; + } + + void ddfw::check_without_plugin() { while (m_limit.inc() && m_min_sz > 0) { if (should_reinit_weights()) do_reinit_weights(); - else if (do_flip()) ; + else if (do_flip()); else if (should_restart()) do_restart(); else if (should_parallel_sync()) do_parallel_sync(); - else shift_weights(); + else shift_weights(); } - remove_assumptions(); - log(); - return m_min_sz == 0 ? l_true : l_undef; + } + + void ddfw::check_with_plugin() { + m_plugin->init_search(); + while (m_limit.inc() && m_min_sz > 0) { + if (should_reinit_weights()) do_reinit_weights(); + else if (do_flip()); + else if (do_literal_flip()); + else if (should_restart()) do_restart(), m_plugin->on_restart(); + else if (should_parallel_sync()) do_parallel_sync(); + else shift_weights(), m_plugin->on_rescale(); + } + m_plugin->finish_search(); } void ddfw::log() { @@ -77,55 +95,72 @@ namespace sat { m_last_flips = m_flips; } + template bool ddfw::do_flip() { - bool_var v = pick_var(); + bool_var v = pick_var(); + return apply_flip(v); + } + + template + bool ddfw::apply_flip(bool_var v) { + if (v == null_bool_var) + return false; if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { - flip(v); - if (m_unsat.size() <= m_min_sz) save_best_values(); + if (uses_plugin) + m_plugin->flip(v); + else + flip(v); + if (m_unsat.size() <= m_min_sz) + save_best_values(); return true; } return false; } + template bool_var ddfw::pick_var() { double sum_pos = 0; unsigned n = 1; + double r; bool_var v0 = null_bool_var; for (bool_var v : m_unsat_vars) { - double r = reward(v); - if (r > 0.0) { - sum_pos += score(r); - } - else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0) { - v0 = v; - } + r = uses_plugin ? plugin_reward(v) : reward(v); + if (r > 0.0) + sum_pos += score(r); + else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0) + v0 = v; } if (sum_pos > 0) { double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos; for (bool_var v : m_unsat_vars) { - double r = reward(v); + r = uses_plugin ? plugin_reward(v) : reward(v); if (r > 0) { lim_pos -= score(r); - if (lim_pos <= 0) { - return v; - } + if (lim_pos <= 0) + return v; } } } - if (v0 != null_bool_var) { + if (v0 != null_bool_var) return v0; - } + if (m_unsat_vars.empty()) + return 0; return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size())); } - /** - * TBD: map reward value to a score, possibly through an exponential function, such as - * exp(-tau/r), where tau > 0 - */ - double ddfw::mk_score(double r) { - return r; + template + bool ddfw::do_literal_flip() { + return apply_flip(pick_literal_var()); } + /* + * Pick a random false literal from a satisfied clause such that + * the literal has zero break count and positive reward. + */ + template + bool_var ddfw::pick_literal_var() { + return null_bool_var; + } void ddfw::add(unsigned n, literal const* c) { clause* cls = m_alloc.mk_clause(n, c, false); @@ -409,6 +444,8 @@ namespace sat { for (unsigned i = 0; i < num_vars(); ++i) m_model[i] = to_lbool(value(i)); save_priorities(); + if (m_plugin) + m_plugin->on_save_model(); } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ee1c73b6e96..5d56c3adcf7 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -31,6 +31,17 @@ namespace sat { class solver; class parallel; + class local_search_plugin { + public: + virtual void init_search() = 0; + virtual void finish_search() = 0; + virtual void flip(bool_var v) = 0; + virtual double reward(bool_var v) = 0; + virtual void on_rescale() = 0; + virtual void on_save_model() = 0; + virtual void on_restart() = 0; + }; + class ddfw : public i_local_search { public: struct clause_info { @@ -83,6 +94,7 @@ namespace sat { double m_reward = 0; unsigned m_make_count = 0; int m_bias = 0; + bool m_external = false; ema m_reward_avg = 1e-5; }; @@ -113,14 +125,15 @@ namespace sat { stopwatch m_stopwatch; parallel* m_par; - - + scoped_ptr< local_search_plugin> m_plugin; void flatten_use_list(); - double mk_score(double r); - - inline double score(double r) { return r; } // TBD: { for (unsigned sz = m_scores.size(); sz <= r; ++sz) m_scores.push_back(mk_score(sz)); return m_scores[r]; } + /** + * TBD: map reward value to a score, possibly through an exponential function, such as + * exp(-tau/r), where tau > 0 + */ + inline double score(double r) { return r; } inline unsigned num_vars() const { return m_vars.size(); } @@ -134,6 +147,12 @@ namespace sat { inline double reward(bool_var v) const { return m_vars[v].m_reward; } + inline double plugin_reward(bool_var v) const { return m_plugin->reward(v); } + + void set_external(bool_var v) { m_vars[v].m_external = true; } + + inline bool is_external(bool_var v) const { return m_vars[v].m_external; } + inline int& bias(bool_var v) { return m_vars[v].m_bias; } unsigned value_hash() const; @@ -164,9 +183,25 @@ namespace sat { inline void dec_reward(literal lit, double w) { reward(lit.var()) -= w; } + void check_with_plugin(); + void check_without_plugin(); + // flip activity + template bool do_flip(); - bool_var pick_var(); + + template + bool_var pick_var(); + + template + bool apply_flip(bool_var v); + + template + bool do_literal_flip(); + + template + bool_var pick_literal_var(); + void save_best_values(); void save_model(); void save_priorities(); @@ -215,6 +250,8 @@ namespace sat { ~ddfw() override; + void set(local_search_plugin* p) { m_plugin = p; } + lbool check(unsigned sz, literal const* assumptions, parallel* p) override; void updt_params(params_ref const& p) override; From a1f73d3805dfa2f32a8d30b36662ab02bb0ebfc8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 08:48:37 -0800 Subject: [PATCH 421/597] wip - local search - fix build --- src/sat/sat_ddfw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 5d56c3adcf7..a681d75f53b 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -33,6 +33,7 @@ namespace sat { class local_search_plugin { public: + virtual ~local_search_plugin() {} virtual void init_search() = 0; virtual void finish_search() = 0; virtual void flip(bool_var v) = 0; From c1ecc49021c53072d65aba54c2f9cc9ca8d8a284 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 13:32:30 -0800 Subject: [PATCH 422/597] wip - local search - move to plugin model --- src/sat/sat_ddfw.h | 9 +- src/sat/sat_solver/sat_smt_setup.h | 90 ++++++++++++++++++++ src/sat/smt/arith_sls.cpp | 127 ++++++++++++++++++++++++----- src/sat/smt/arith_sls.h | 21 ++++- src/sat/smt/euf_local_search.cpp | 37 +-------- 5 files changed, 225 insertions(+), 59 deletions(-) create mode 100644 src/sat/sat_solver/sat_smt_setup.h diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index a681d75f53b..fc03f8ba2f5 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -27,6 +27,10 @@ #include "sat/sat_clause.h" #include "sat/sat_types.h" +namespace arith { + class sls; +} + namespace sat { class solver; class parallel; @@ -44,6 +48,7 @@ namespace sat { }; class ddfw : public i_local_search { + friend class arith::sls; public: struct clause_info { clause_info(clause* cl, double init_weight): m_weight(init_weight), m_clause(cl) {} @@ -126,7 +131,7 @@ namespace sat { stopwatch m_stopwatch; parallel* m_par; - scoped_ptr< local_search_plugin> m_plugin; + local_search_plugin* m_plugin = nullptr; void flatten_use_list(); @@ -148,7 +153,7 @@ namespace sat { inline double reward(bool_var v) const { return m_vars[v].m_reward; } - inline double plugin_reward(bool_var v) const { return m_plugin->reward(v); } + inline double plugin_reward(bool_var v) const { return is_external(v) ? m_plugin->reward(v) : reward(v); } void set_external(bool_var v) { m_vars[v].m_external = true; } diff --git a/src/sat/sat_solver/sat_smt_setup.h b/src/sat/sat_solver/sat_smt_setup.h new file mode 100644 index 00000000000..c78eb72c855 --- /dev/null +++ b/src/sat/sat_solver/sat_smt_setup.h @@ -0,0 +1,90 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + sat_smt_setup.h + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-17 + +--*/ +#pragma once + +#include "ast/ast.h" +#include "smt/params/smt_params.h" +#include "sat/sat_config.h" +#include "ast/simplifiers/dependent_expr_state.h" + +struct static_features; + +namespace sat_smt { + + void setup_sat_config(smt_params const& p, sat::config& config); + + class setup { + ast_manager& m; + dependent_expr_state& m_st; + smt_params& m_params; + symbol m_logic; + bool m_already_configured = false; + + void setup_auto_config(); + void setup_default(); + // + // setup_() methods do not depend on static features of the formula. So, they are safe to use + // even in an incremental setting. + // + // setup_(static_features & st) can only be used if the logical context will perform a single + // check. + // + void setup_QF_DT(); + void setup_QF_UF(); + void setup_QF_UF(static_features const & st); + void setup_QF_RDL(); + void setup_QF_RDL(static_features & st); + void setup_QF_IDL(); + void setup_QF_IDL(static_features & st); + void setup_QF_UFIDL(); + void setup_QF_UFIDL(static_features & st); + void setup_QF_LRA(); + void setup_QF_LRA(static_features const & st); + void setup_QF_LIA(); + void setup_QF_LIRA(static_features const& st); + void setup_QF_LIA(static_features const & st); + void setup_QF_UFLIA(); + void setup_QF_UFLIA(static_features & st); + void setup_QF_UFLRA(); + void setup_QF_BV(); + void setup_QF_AUFBV(); + void setup_QF_AX(); + void setup_QF_AX(static_features const & st); + void setup_QF_AUFLIA(); + void setup_QF_AUFLIA(static_features const & st); + void setup_QF_FP(); + void setup_QF_FPBV(); + void setup_QF_S(); + void setup_LRA(); + void setup_CSP(); + void setup_AUFLIA(bool simple_array = true); + void setup_AUFLIA(static_features const & st); + void setup_AUFLIRA(bool simple_array = true); + void setup_UFNIA(); + void setup_UFLRA(); + void setup_AUFLIAp(); + void setup_AUFNIRA(); + void setup_QF_BVRE(); + void setup_unknown(); + void setup_unknown(static_features & st); + + public: + setup(ast_manager& m, dependent_expr_state& st, smt_params & params); + void setk_already_configured() { m_already_configured = true; } + bool already_configured() const { return m_already_configured; } + symbol const & get_logic() const { return m_logic; } + void operator()(); + }; +}; + + diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 6cecb4dbb92..3d4522e76d2 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2020 Microsoft Corporation +Copyright (c) 2023 Microsoft Corporation Module Name: @@ -112,6 +112,8 @@ namespace arith { for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); m_best_min_unsat = std::numeric_limits::max(); + + d->set(this); } void sls::set_bounds_begin() { @@ -209,14 +211,13 @@ namespace arith { unsigned start = s.random(); unsigned sz = unsat().size(); for (unsigned i = sz; i-- > 0; ) - if (flip(unsat().elem_at((i + start) % sz))) + if (flip_clause(unsat().elem_at((i + start) % sz))) return true; return false; } - bool sls::flip(unsigned cl) { + bool sls::flip_clause(unsigned cl) { auto const& clause = get_clause(cl); - int64_t new_value; for (literal lit : clause) { if (is_true(lit)) continue; @@ -224,20 +225,32 @@ namespace arith { if (!ineq) continue; SASSERT(!ineq->is_true()); - for (auto const& [coeff, v] : ineq->m_args) { - if (!cm(*ineq, v, new_value)) - continue; - int score = cm_score(v, new_value); - if (score <= 0) - continue; - unsigned num_unsat = unsat().size(); - update(v, new_value); - IF_VERBOSE(2, - verbose_stream() << "v" << v << " score " << score << " " - << num_unsat << " -> " << unsat().size() << "\n"); - SASSERT(num_unsat > unsat().size()); + if (flip(*ineq)) return true; - } + + } + return false; + } + + // flip on the first positive score + // it could be changed to flip on maximal positive score + // or flip on maximal non-negative score + // or flip on first non-negative score + bool sls::flip(ineq const& ineq) { + int64_t new_value; + for (auto const& [coeff, v] : ineq.m_args) { + if (!cm(ineq, v, new_value)) + continue; + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = unsat().size(); + update(v, new_value); + IF_VERBOSE(2, + verbose_stream() << "v" << v << " score " << score << " " + << num_unsat << " -> " << unsat().size() << "\n"); + SASSERT(num_unsat > unsat().size()); + return true; } return false; } @@ -246,7 +259,7 @@ namespace arith { unsigned start = s.random(); unsigned sz = m_bool_search->num_clauses(); for (unsigned i = sz; i-- > 0; ) - if (flip((i + start) % sz)) + if (flip_clause((i + start) % sz)) return true; return false; } @@ -541,9 +554,85 @@ namespace arith { void sls::init_literal_assignment(sat::literal lit) { auto* ineq = m_literals.get(lit.index(), nullptr); - if (ineq && is_true(lit) != (dtt(*ineq) == 0)) m_bool_search->flip(lit.var()); } + + void sls::init_search() { + on_restart(); + } + + void sls::finish_search() { + store_best_values(); + } + + void sls::flip(sat::bool_var v) { + sat::literal lit(v, m_bool_search->get_value(v)); + SASSERT(!is_true(lit)); + auto const* ineq = atom(lit); + if (!ineq) + IF_VERBOSE(0, verbose_stream() << "no inequality for variable " << v << "\n"); + if (!ineq) + return; + IF_VERBOSE(1, verbose_stream() << "flip " << lit << "\n"); + SASSERT(!ineq->is_true()); + flip(*ineq); + } + + double sls::reward(sat::bool_var v) { + if (m_dscore_mode) + return dscore_reward(v); + else + return dtt_reward(v); + } + + double sls::dtt_reward(sat::bool_var v) { + sat::literal litv(v, m_bool_search->get_value(v)); + auto const* ineq = atom(litv); + if (!ineq) + return 0; + int64_t new_value; + double result = 0; + for (auto const & [coeff, x] : ineq->m_args) { + if (!cm(*ineq, x, new_value)) + continue; + for (auto const [coeff, lit] : m_vars[x].m_literals) { + auto dtt_old = dtt(*atom(lit)); + auto dtt_new = dtt(*atom(lit), x, new_value); + if ((dtt_new == 0) != (dtt_old == 0)) + result += m_bool_search->reward(lit.var()); + } + } + return result; + } + + double sls::dscore_reward(sat::bool_var x) { + m_dscore_mode = false; + sat::literal litv(x, m_bool_search->get_value(x)); + auto const* ineq = atom(litv); + if (!ineq) + return 0; + SASSERT(!ineq->is_true()); + int64_t new_value; + double result = 0; + for (auto const& [coeff, v] : ineq->m_args) + if (cm(*ineq, v, new_value)) + result += dscore(v, new_value); + return result; + } + + // switch to dscore mode + void sls::on_rescale() { + m_dscore_mode = true; + } + + void sls::on_save_model() { + save_best_values(); + } + + void sls::on_restart() { + for (unsigned v = 0; v < s.s().num_vars(); ++v) + init_bool_var_assignment(v); + } } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 706fff1ddfa..e781e966686 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -37,7 +37,7 @@ namespace arith { class solver; // local search portion for arithmetic - class sls { + class sls : public sat::local_search_plugin { enum class ineq_kind { EQ, LE, LT, NE }; enum class var_kind { INT, REAL }; typedef unsigned var_t; @@ -78,7 +78,7 @@ namespace arith { std::ostream& display(std::ostream& out) const { bool first = true; for (auto const& [c, v] : m_args) - out << (first? "": " + ") << c << " * v" << v, first = false; + out << (first ? "" : " + ") << c << " * v" << v, first = false; switch (m_op) { case ineq_kind::LE: return out << " <= " << m_bound << "(" << m_args_value << ")"; @@ -97,7 +97,7 @@ namespace arith { int64_t m_value; int64_t m_best_value; var_kind m_kind = var_kind::INT; - vector> m_literals; + svector> m_literals; }; struct clause { @@ -116,6 +116,7 @@ namespace arith { vector m_vars; vector m_clauses; svector> m_terms; + bool m_dscore_mode = false; indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } @@ -136,7 +137,8 @@ namespace arith { bool flip_clauses(); bool flip_dscore(); bool flip_dscore(unsigned cl); - bool flip(unsigned cl); + bool flip_clause(unsigned cl); + bool flip(ineq const& ineq); int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } int64_t dtt(int64_t args, ineq const& ineq) const; int64_t dtt(ineq const& ineq, var_t v, int64_t new_value) const; @@ -145,6 +147,8 @@ namespace arith { bool cm(ineq const& ineq, var_t v, int64_t& new_value); int cm_score(var_t v, int64_t new_value); void update(var_t v, int64_t new_value); + double dscore_reward(sat::bool_var v); + double dtt_reward(sat::bool_var v); void paws(); int64_t dscore(var_t v, int64_t new_value) const; void save_best_values(); @@ -163,11 +167,20 @@ namespace arith { public: sls(solver& s); + ~sls() override {} lbool operator ()(bool_vector& phase); void set_bounds_begin(); void set_bounds_end(unsigned num_literals); void set_bounds(euf::enode* n); void set(sat::ddfw* d); + + void init_search() override; + void finish_search() override; + void flip(sat::bool_var v) override; + double reward(sat::bool_var v) override; + void on_rescale() override; + void on_save_model() override; + void on_restart() override; }; inline std::ostream& operator<<(std::ostream& out, sls::ineq const& ineq) { diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 0572afc3922..801a9215039 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -29,44 +29,13 @@ namespace euf { bool_search.set_seed(rand()); scoped_rl.push_child(&(bool_search.rlimit())); - unsigned max_rounds = 30; - for (auto* th : m_solvers) th->set_bool_search(&bool_search); - for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { - - setup_bounds(bool_search, phase); - - // Non-boolean literals are assumptions to Boolean search - literal_vector assumptions; -#if 0 - for (unsigned v = 0; v < phase.size(); ++v) - if (!is_propositional(literal(v))) - assumptions.push_back(literal(v, !bool_search.get_value(v))); -#endif - - verbose_stream() << "assumptions " << assumptions.size() << "\n"; + bool_search.rlimit().push(m_max_bool_steps); + lbool r = bool_search.check(0, nullptr, nullptr); + bool_search.rlimit().pop(); - bool_search.rlimit().push(m_max_bool_steps); - - lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); - bool_search.rlimit().pop(); - -#if 0 - // restore state to optimal model - auto const& mdl = bool_search.get_model(); - for (unsigned i = 0; i < mdl.size(); ++i) - if ((mdl[i] == l_true) != bool_search.get_value(i)) - bool_search.flip(i); -#endif - - for (auto* th : m_solvers) - th->local_search(phase); - - if (bool_search.unsat_set().empty()) - break; - } auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) phase[i] = mdl[i] == l_true; From 7c08e53e944f806af7db74cc512a197c4d76c7f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 15:11:44 -0800 Subject: [PATCH 423/597] fixes for #6590 --- src/math/lp/nla_powers.cpp | 18 +++++++++++++----- src/smt/theory_lra.cpp | 8 ++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index 96284f26a77..f389aad93c7 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -79,6 +79,7 @@ am().set(rval, am_value(r)); namespace nla { lbool powers::check(lpvar r, lpvar x, lpvar y, vector& lemmas) { + TRACE("nla", tout << r << " == " << x << "^" << y << "\n"); if (x == null_lpvar || y == null_lpvar || r == null_lpvar) return l_undef; if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(r)) @@ -145,17 +146,24 @@ namespace nla { auto r2val = power(xval, yval.get_unsigned()); if (rval == r2val) return l_true; - if (xval > 0 && r2val < rval) { - SASSERT(yval > 0); + if (c.random() % 2 == 0) { + new_lemma lemma(c, "x == x0, y == y0 => r = x0^y0"); + lemma |= ineq(x, llc::NE, xval); + lemma |= ineq(y, llc::NE, yval); + lemma |= ineq(r, llc::EQ, r2val); + return l_false; + } + if (yval > 0 && r2val > rval) { new_lemma lemma(c, "x >= x0 > 0, y >= y0 > 0 => r >= x0^y0"); lemma |= ineq(x, llc::LT, xval); lemma |= ineq(y, llc::LT, yval); lemma |= ineq(r, llc::GE, r2val); return l_false; } - if (xval > 0 && r2val < rval) { - new_lemma lemma(c, "x >= x0 > 0, y <= y0 => r <= x0^y0"); - lemma |= ineq(x, llc::LT, xval); + if (r2val < rval) { + new_lemma lemma(c, "0 < x <= x0, y <= y0 => r <= x0^y0"); + lemma |= ineq(x, llc::LE, rational::zero()); + lemma |= ineq(x, llc::GT, xval); lemma |= ineq(y, llc::GT, yval); lemma |= ineq(r, llc::LE, r2val); return l_false; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d26ea516ac9..d61910ff20e 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -814,12 +814,13 @@ class theory_lra::imp { } lpvar get_lpvar(expr* e) { - return get_lpvar(get_enode(e)); + theory_var v = mk_var(e); + m_solver->register_existing_terms(); + return register_theory_var_in_lar_solver(v); } lpvar get_lpvar(enode* n) { - ensure_column(n); - return n ? get_lpvar(n->get_th_var(get_id())) : lp::null_lpvar; + return get_lpvar(n->get_expr()); } lpvar get_lpvar(theory_var v) const { @@ -1674,7 +1675,6 @@ class theory_lra::imp { for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; - st = FC_DONE; switch (eval_unsupported(e)) { case FC_CONTINUE: st = FC_CONTINUE; From 554a9e8efe71f9bc0aa19b61d8e2cefda7b03a37 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Feb 2023 08:53:08 -0800 Subject: [PATCH 424/597] fix #6346 --- src/util/zstring.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 570510458dd..7d2b4296a54 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -214,7 +214,8 @@ int zstring::indexofu(zstring const& other, unsigned offset) const { } int zstring::last_indexof(zstring const& other) const { - if (other.length() == 0) return length(); + if (length() == 0 && other.length() == 0) return 0; + if (other.length() == 0) return -1; if (other.length() > length()) return -1; for (unsigned last = length() - other.length() + 1; last-- > 0; ) { bool suffix = true; From ac068888e79c61c037bfdfc58f23b14d853ad55b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Feb 2023 08:59:55 -0800 Subject: [PATCH 425/597] add trichotomy for sequence comparison. #6586 --- src/ast/rewriter/seq_axioms.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index c7dde763f78..14f34f1f66a 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -928,7 +928,6 @@ namespace seq { e1 < e2 => prefix(e1, e2) or e1 = xcy e1 < e2 => prefix(e1, e2) or c < d e1 < e2 => prefix(e1, e2) or e2 = xdz - e1 < e2 => e1 != e2 !(e1 < e2) => prefix(e2, e1) or e2 = xdz !(e1 < e2) => prefix(e2, e1) or d < c !(e1 < e2) => prefix(e2, e1) or e1 = xcy @@ -938,6 +937,7 @@ namespace seq { e1 < e2 or e1 = e2 or e2 < e1 !(e1 = e2) or !(e2 < e1) !(e1 < e2) or !(e2 < e1) + */ void axioms::lt_axiom(expr* n) { expr* _e1 = nullptr, *_e2 = nullptr; @@ -948,6 +948,7 @@ namespace seq { sort* char_sort = nullptr; VERIFY(seq.is_seq(s, char_sort)); expr_ref lt = expr_ref(n, m); + expr_ref gt = expr_ref(seq.str.mk_lex_lt(e2, e1), m); expr_ref x = m_sk.mk("str.<.x", e1, e2); expr_ref y = m_sk.mk("str.<.y", e1, e2); expr_ref z = m_sk.mk("str.<.z", e1, e2); @@ -969,6 +970,7 @@ namespace seq { add_clause(lt, pref21, ltdc); add_clause(lt, pref21, e2xdz); add_clause(~eq, ~lt); + add_clause(eq, lt, gt); } /** From bd10ddf6aea3808968bbea9f30ad532844d410bf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Feb 2023 09:17:11 -0800 Subject: [PATCH 426/597] wip - local search - use dispatch model from bool local search instead of separate phases. --- src/sat/sat_ddfw.cpp | 24 ++++- src/sat/sat_ddfw.h | 2 +- src/sat/sat_solver.cpp | 3 - src/sat/smt/arith_sls.cpp | 174 +------------------------------ src/sat/smt/arith_sls.h | 25 +---- src/sat/smt/arith_solver.h | 4 - src/sat/smt/euf_local_search.cpp | 40 +------ src/sat/smt/euf_solver.h | 7 +- 8 files changed, 31 insertions(+), 248 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 478e793e33e..64589dc6e3f 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -61,7 +61,8 @@ namespace sat { void ddfw::check_with_plugin() { m_plugin->init_search(); - while (m_limit.inc() && m_min_sz > 0) { + m_steps_since_progress = 0; + while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) { if (should_reinit_weights()) do_reinit_weights(); else if (do_flip()); else if (do_literal_flip()); @@ -106,7 +107,7 @@ namespace sat { if (v == null_bool_var) return false; if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { - if (uses_plugin) + if (uses_plugin && is_external(v)) m_plugin->flip(v); else flip(v); @@ -159,6 +160,24 @@ namespace sat { */ template bool_var ddfw::pick_literal_var() { +#if false + unsigned sz = m_clauses.size(); + unsigned start = rand(); + for (unsigned i = 0; i < 100; ++i) { + unsigned cl = (i + start) % sz; + if (m_unsat.contains(cl)) + continue; + for (auto lit : *m_clauses[cl].m_clause) { + if (is_true(lit)) + continue; + double r = uses_plugin ? plugin_reward(lit.var()) : reward(lit.var()); + if (r < 0) + continue; + //verbose_stream() << "false " << r << " " << lit << "\n"; + return lit.var(); + } + } +#endif return null_bool_var; } @@ -453,6 +472,7 @@ namespace sat { if (m_unsat.empty()) save_model(); else if (m_unsat.size() < m_min_sz) { + m_steps_since_progress = 0; if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11) save_model(); } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index fc03f8ba2f5..65491011142 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -126,7 +126,7 @@ namespace sat { unsigned m_restart_count = 0, m_reinit_count = 0, m_parsync_count = 0; uint64_t m_restart_next = 0, m_reinit_next = 0, m_parsync_next = 0; uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0; - unsigned m_min_sz = 0; + unsigned m_min_sz = 0, m_steps_since_progress = 0; hashtable> m_models; stopwatch m_stopwatch; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 898c3a2a420..c570843b54c 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1302,9 +1302,6 @@ namespace sat { return l_undef; } - // uncomment this to test bounded local search: - // bounded_local_search(); - log_stats(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 3d4522e76d2..54141e635a3 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -26,34 +26,9 @@ namespace arith { void sls::reset() { m_literals.reset(); m_vars.reset(); - m_clauses.reset(); m_terms.reset(); } - lbool sls::operator()(bool_vector& phase) { - - unsigned num_steps = 0; - for (unsigned v = 0; v < s.s().num_vars(); ++v) - init_bool_var_assignment(v); - verbose_stream() << "max arith steps " << m_max_arith_steps << "\n"; - m_max_arith_steps = std::max(1000u, m_max_arith_steps); - while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { - if (!flip()) - break; - ++m_stats.m_num_flips; - ++num_steps; - unsigned num_unsat = unsat().size(); - if (num_unsat < m_best_min_unsat) { - m_best_min_unsat = num_unsat; - num_steps = 0; - save_best_values(); - } - } - store_best_values(); - log(); - return unsat().empty() ? l_true : l_undef; - } - void sls::log() { IF_VERBOSE(2, verbose_stream() << "(sls :flips " << m_stats.m_num_flips << " :unsat " << unsat().size() << ")\n"); } @@ -105,7 +80,6 @@ namespace arith { reset(); m_literals.reserve(s.s().num_vars() * 2); add_vars(); - m_clauses.resize(d->num_clauses()); for (unsigned i = 0; i < d->num_clauses(); ++i) for (sat::literal lit : *d->get_clause_info(i).m_clause) init_literal(lit); @@ -116,22 +90,6 @@ namespace arith { d->set(this); } - void sls::set_bounds_begin() { - m_max_arith_steps = 0; - } - - void sls::set_bounds(enode* n) { - ++m_max_arith_steps; - } - - void sls::set_bounds_end(unsigned num_literals) { - m_max_arith_steps = (m_config.L * m_max_arith_steps); // / num_literals; - } - - bool sls::flip() { - log(); - return flip_unsat() || flip_clauses() || flip_dscore(); - } // distance to true int64_t sls::dtt(int64_t args, ineq const& ineq) const { @@ -207,31 +165,6 @@ namespace arith { return false; } - bool sls::flip_unsat() { - unsigned start = s.random(); - unsigned sz = unsat().size(); - for (unsigned i = sz; i-- > 0; ) - if (flip_clause(unsat().elem_at((i + start) % sz))) - return true; - return false; - } - - bool sls::flip_clause(unsigned cl) { - auto const& clause = get_clause(cl); - for (literal lit : clause) { - if (is_true(lit)) - continue; - auto const* ineq = atom(lit); - if (!ineq) - continue; - SASSERT(!ineq->is_true()); - if (flip(*ineq)) - return true; - - } - return false; - } - // flip on the first positive score // it could be changed to flip on maximal positive score // or flip on maximal non-negative score @@ -255,77 +188,17 @@ namespace arith { return false; } - bool sls::flip_clauses() { - unsigned start = s.random(); - unsigned sz = m_bool_search->num_clauses(); - for (unsigned i = sz; i-- > 0; ) - if (flip_clause((i + start) % sz)) - return true; - return false; - } - - bool sls::flip_dscore() { - paws(); - unsigned start = s.random(); - unsigned sz = unsat().size(); - for (unsigned i = sz; i-- > 0; ) - if (flip_dscore(unsat().elem_at((i + start) % sz))) - return true; - return false; - } - - bool sls::flip_dscore(unsigned cl) { - auto const& clause = get_clause(cl); - int64_t new_value, min_value, min_score(-1); - var_t min_var = UINT_MAX; - for (auto lit : clause) { - auto const* ineq = atom(lit); - if (!ineq || ineq->is_true()) - continue; - for (auto const& [coeff, v] : ineq->m_args) { - if (cm(*ineq, v, new_value)) { - int64_t score = dscore(v, new_value); - if (UINT_MAX == min_var || score < min_score) { - min_var = v; - min_value = new_value; - min_score = score; - } - } - } - } - if (min_var != UINT_MAX) { - update(min_var, min_value); - return true; - } - return false; - } - - /** - * redistribute weights of clauses. - * TODO - re-use ddfw weights instead. - */ - void sls::paws() { - for (unsigned cl = num_clauses(); cl-- > 0; ) { - auto& clause = get_clause_info(cl); - bool above = 10000 * m_config.sp <= (s.random() % 10000); - if (!above && clause.is_true() && get_weight(cl) > 1) - get_weight(cl) -= 1; - if (above && !clause.is_true()) - get_weight(cl) += 1; - } - } - // // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) // TODO - use cached dts instead of computed dts // cached dts has to be updated when the score of literals are updated. // - int64_t sls::dscore(var_t v, int64_t new_value) const { + double sls::dscore(var_t v, int64_t new_value) const { auto const& vi = m_vars[v]; - int64_t score(0); + double score = 0; for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * int64_t(get_weight(cl)); + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); return score; } @@ -437,46 +310,6 @@ namespace arith { m_vars[v].m_literals.push_back({ c, lit }); } - void sls::add_bounds(sat::literal_vector& bounds) { - unsigned bvars = s.s().num_vars(); - auto add_ineq = [&](sat::literal lit, ineq& i) { - m_literals.set(lit.index(), &i); - bounds.push_back(lit); - }; - for (unsigned v = 0; v < s.get_num_vars(); ++v) { - rational lo, hi; - bool is_strict_lo = false, is_strict_hi = false; - lp::constraint_index ci; - if (!s.is_registered_var(v)) - continue; - lp::column_index vi = s.lp().to_column_index(v); - if (vi.is_null()) - continue; - bool has_lo = s.lp().has_lower_bound(vi.index(), ci, lo, is_strict_lo); - bool has_hi = s.lp().has_upper_bound(vi.index(), ci, hi, is_strict_hi); - - if (has_lo && has_hi && lo == hi) { - auto& ineq = new_ineq(sls::ineq_kind::EQ, to_numeral(lo)); - sat::literal lit(bvars++); - add_arg(lit, ineq, 1, v); - add_ineq(lit, ineq); - continue; - } - if (has_lo) { - auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(-lo)); - sat::literal lit(bvars++); - add_arg(lit, ineq, -1, v); - add_ineq(lit, ineq); - } - if (has_hi) { - auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(hi)); - sat::literal lit(bvars++); - add_arg(lit, ineq, 1, v); - add_ineq(lit, ineq); - } - } - } - int64_t sls::to_numeral(rational const& r) { if (r.is_int64()) return r.get_int64(); @@ -635,4 +468,3 @@ namespace arith { init_bool_var_assignment(v); } } - diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index e781e966686..5496be63747 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -100,11 +100,6 @@ namespace arith { svector> m_literals; }; - struct clause { - unsigned m_weight = 1; - int64_t m_dts = 1; - }; - solver& s; ast_manager& m; sat::ddfw* m_bool_search = nullptr; @@ -114,7 +109,6 @@ namespace arith { config m_config; scoped_ptr_vector m_literals; vector m_vars; - vector m_clauses; svector> m_terms; bool m_dscore_mode = false; @@ -129,15 +123,9 @@ namespace arith { void reset(); ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } - unsigned& get_weight(unsigned idx) { return m_clauses[idx].m_weight; } - unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } - bool flip(); + void log(); - bool flip_unsat(); - bool flip_clauses(); - bool flip_dscore(); - bool flip_dscore(unsigned cl); - bool flip_clause(unsigned cl); + bool flip(ineq const& ineq); int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } int64_t dtt(int64_t args, ineq const& ineq) const; @@ -149,14 +137,12 @@ namespace arith { void update(var_t v, int64_t new_value); double dscore_reward(sat::bool_var v); double dtt_reward(sat::bool_var v); - void paws(); - int64_t dscore(var_t v, int64_t new_value) const; + double dscore(var_t v, int64_t new_value) const; void save_best_values(); void store_best_values(); void add_vars(); sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); - void add_bounds(sat::literal_vector& bounds); void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); void init_literal(sat::literal lit); void init_bool_var_assignment(sat::bool_var v); @@ -168,12 +154,7 @@ namespace arith { public: sls(solver& s); ~sls() override {} - lbool operator ()(bool_vector& phase); - void set_bounds_begin(); - void set_bounds_end(unsigned num_literals); - void set_bounds(euf::enode* n); void set(sat::ddfw* d); - void init_search() override; void finish_search() override; void flip(sat::bool_var v) override; diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index a31ca844a3e..9ae671c162b 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -512,10 +512,6 @@ namespace arith { bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); } bool has_unhandled() const override { return m_not_handled != nullptr; } - void set_bounds_begin() override { m_local_search.set_bounds_begin(); } - void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } - void set_bounds(enode* n) override { m_local_search.set_bounds(n); } - lbool local_search(bool_vector& phase) override { return m_local_search(phase); } void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); } // bounds and equality propagation callbacks diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 801a9215039..d22a65fb871 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -32,9 +32,7 @@ namespace euf { for (auto* th : m_solvers) th->set_bool_search(&bool_search); - bool_search.rlimit().push(m_max_bool_steps); - lbool r = bool_search.check(0, nullptr, nullptr); - bool_search.rlimit().pop(); + lbool r = bool_search.check(0, nullptr, nullptr); auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) @@ -42,40 +40,4 @@ namespace euf { return bool_search.unsat_set().empty() ? l_true : l_undef; } - - bool solver::is_propositional(sat::literal lit) { - expr* e = m_bool_var2expr.get(lit.var(), nullptr); - return !e || is_uninterp_const(e) || !m_egraph.find(e); - } - - void solver::setup_bounds(sat::ddfw& bool_search, bool_vector const& phase) { - unsigned num_literals = 0; - unsigned num_bool = 0; - for (auto* th : m_solvers) - th->set_bounds_begin(); - - auto count_literal = [&](sat::literal l) { - if (is_propositional(l)) { - ++num_bool; - return; - } - euf::enode* n = m_egraph.find(m_bool_var2expr.get(l.var(), nullptr)); - for (auto* s : m_solvers) - s->set_bounds(n); - }; - - for (auto cl : bool_search.unsat_set()) { - auto& c = *bool_search.get_clause_info(cl).m_clause; - num_literals += c.size(); - for (auto l : c) - count_literal(l); - } - - m_max_bool_steps = (m_ls_config.L * num_bool); // / num_literals; - m_max_bool_steps = std::max(10000u, m_max_bool_steps); - verbose_stream() << "num literals " << num_literals << " num bool " << num_bool << " max bool steps " << m_max_bool_steps << "\n"; - - for (auto* th : m_solvers) - th->set_bounds_end(num_literals); - } } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index d623903296a..e51b68b0911 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -260,12 +260,7 @@ namespace euf { constraint& mk_constraint(constraint*& c, constraint::kind_t k); constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); } constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); } - constraint& lit_constraint(enode* n); - - // local search - unsigned m_max_bool_steps = 10; - bool is_propositional(sat::literal lit); - void setup_bounds(sat::ddfw& bool_search, bool_vector const& mdl); + constraint& lit_constraint(enode* n); // user propagator void check_for_user_propagator() { From 828ff98c77eb5437178b7b8aff902394e29e0445 Mon Sep 17 00:00:00 2001 From: Ding Fei Date: Sat, 18 Feb 2023 01:26:45 +0800 Subject: [PATCH 427/597] fix tpl instantiation issue for mingw (#6597) --- src/ast/rewriter/rewriter.h | 2 +- src/ast/rewriter/rewriter_def.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index a04c96f4f06..cd89bb2f9b3 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -347,7 +347,7 @@ class rewriter_tpl : public rewriter_core { Config & cfg() { return m_cfg; } Config const & cfg() const { return m_cfg; } - ~rewriter_tpl() override; + ~rewriter_tpl() override {}; void reset(); void cleanup(); diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index e9b9c316a95..44bed09c6b2 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -640,10 +640,6 @@ rewriter_tpl::rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg m_pr2(m) { } -template -rewriter_tpl::~rewriter_tpl() { -} - template void rewriter_tpl::reset() { m_cfg.reset(); From f66a082de9e51205b42a910a0640a73172aa5eeb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:10:58 -0800 Subject: [PATCH 428/597] fix #6595 --- .gitignore | 1 + src/ast/rewriter/bv_bounds_base.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 936f977aaa5..b0c19a43260 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ ocamlz3 # Directories with generated code and documentation release/* build/* +trace/* build-dist/* dist/* src/out/* diff --git a/src/ast/rewriter/bv_bounds_base.h b/src/ast/rewriter/bv_bounds_base.h index 840cf6e3ee8..8542e5921f9 100644 --- a/src/ast/rewriter/bv_bounds_base.h +++ b/src/ast/rewriter/bv_bounds_base.h @@ -185,7 +185,7 @@ namespace bv { m_args.push_back(arg); continue; } - if (!m_bv.is_extract(arg) && m_bound.find(arg, b)) { + if (!m_bv.is_extract(arg) && m_bound.find(arg, b) && b.lo() <= b.hi()) { unsigned num_bits = b.hi().get_num_bits(); unsigned bv_size = m_bv.get_bv_size(arg); if (0 < num_bits && num_bits < bv_size) { @@ -202,6 +202,7 @@ namespace bv { if (simplified) { result = m.mk_app(to_app(t)->get_decl(), m_args); + TRACE("bv", tout << mk_pp(t, m) << " -> " << result << "\n"); return true; } From c5e33b79b580ddf228c508549fc93bdbd855a0d6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:11:42 -0800 Subject: [PATCH 429/597] wip - arith sls overhaul to tier inequalities with Boolean variables instead of literals --- src/sat/sat_ddfw.cpp | 23 +- src/sat/sat_ddfw.h | 4 +- src/sat/sat_solver.cpp | 18 +- src/sat/smt/arith_sls.cpp | 450 ++++++++++++++++++++----------- src/sat/smt/arith_sls.h | 32 +-- src/sat/smt/arith_solver.cpp | 8 +- src/sat/smt/euf_local_search.cpp | 7 + 7 files changed, 352 insertions(+), 190 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 64589dc6e3f..329d783f4db 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -98,15 +98,17 @@ namespace sat { template bool ddfw::do_flip() { - bool_var v = pick_var(); - return apply_flip(v); + double reward = 0; + bool_var v = pick_var(reward); + return apply_flip(v, reward); } template - bool ddfw::apply_flip(bool_var v) { - if (v == null_bool_var) + bool ddfw::apply_flip(bool_var v, double reward) { + if (v == null_bool_var) return false; - if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { + + if (reward > 0 || (reward == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { if (uses_plugin && is_external(v)) m_plugin->flip(v); else @@ -119,10 +121,9 @@ namespace sat { } template - bool_var ddfw::pick_var() { + bool_var ddfw::pick_var(double& r) { double sum_pos = 0; unsigned n = 1; - double r; bool_var v0 = null_bool_var; for (bool_var v : m_unsat_vars) { r = uses_plugin ? plugin_reward(v) : reward(v); @@ -142,16 +143,18 @@ namespace sat { } } } + r = 0; if (v0 != null_bool_var) return v0; if (m_unsat_vars.empty()) - return 0; + return null_bool_var; return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size())); } template bool ddfw::do_literal_flip() { - return apply_flip(pick_literal_var()); + double reward = 1; + return apply_flip(pick_literal_var(), reward); } /* @@ -414,7 +417,7 @@ namespace sat { bool ddfw::should_restart() { return m_flips >= m_restart_next; } - + void ddfw::do_restart() { reinit_values(); init_clause_data(); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 65491011142..8c4f9287fcf 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -197,10 +197,10 @@ namespace sat { bool do_flip(); template - bool_var pick_var(); + bool_var pick_var(double& reward); template - bool apply_flip(bool_var v); + bool apply_flip(bool_var v, double reward); template bool do_literal_flip(); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index c570843b54c..5ac0fbcf3af 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1363,7 +1363,13 @@ namespace sat { if (m_ext) { verbose_stream() << "bounded local search\n"; do_restart(true); - m_ext->local_search(m_best_phase); + lbool r = m_ext->local_search(m_best_phase); + verbose_stream() << r << "\n"; + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + } return; } literal_vector _lits; @@ -1728,6 +1734,8 @@ namespace sat { push(); m_stats.m_decision++; + CTRACE("sat", m_best_phase[next] != guess(next), tout << "phase " << phase << " " << m_best_phase[next] << " " << guess(next) << "\n"); + if (phase == l_undef) phase = guess(next) ? l_true: l_false; @@ -1738,12 +1746,12 @@ namespace sat { m_case_split_queue.unassign_var_eh(next); next_lit = literal(next, false); } - + if (phase == l_undef) is_pos = guess(next); else is_pos = phase == l_true; - + if (!is_pos) next_lit.neg(); @@ -2966,7 +2974,7 @@ namespace sat { } bool solver::should_rephase() { - return m_conflicts_since_init > m_rephase_lim; + return m_conflicts_since_init > 5 && m_conflicts_since_init > m_rephase_lim; } void solver::do_rephase() { @@ -3015,7 +3023,7 @@ namespace sat { UNREACHABLE(); break; } - m_rephase_inc += m_config.m_rephase_base; + m_rephase_inc = m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; } diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 54141e635a3..aeddd319bcc 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -24,7 +24,7 @@ namespace arith { s(s), m(s.m) {} void sls::reset() { - m_literals.reset(); + m_bool_vars.reset(); m_vars.reset(); m_terms.reset(); } @@ -36,6 +36,21 @@ namespace arith { void sls::save_best_values() { for (unsigned v = 0; v < s.get_num_vars(); ++v) m_vars[v].m_best_value = m_vars[v].m_value; + + auto check_bool_var = [&](sat::bool_var bv) { + auto const* ineq = atom(bv); + if (!ineq) + return; + sat::literal lit(bv, !m_bool_search->get_value(bv)); + int64_t d = dtt(lit.sign(), *ineq); + // verbose_stream() << "check " << lit << " " << *ineq << "\n"; + if (is_true(lit) != (d == 0)) { + verbose_stream() << lit << " " << *ineq << "\n"; + } + VERIFY(is_true(lit) == (d == 0)); + }; + for (unsigned v = 0; v < s.get_num_vars(); ++v) + check_bool_var(v); } void sls::store_best_values() { @@ -47,7 +62,7 @@ namespace arith { for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - val += to_numeral(arg.coeff()) * value(w); + val += to_numeral(arg.coeff()) * m_vars[w].m_best_value; } update(v, val); } @@ -55,14 +70,12 @@ namespace arith { for (unsigned v = 0; v < s.get_num_vars(); ++v) { if (s.is_bool(v)) continue; - if (!s.lp().external_is_used(v)) - continue; + if (!s.lp().external_is_used(v)) + continue; int64_t old_value = 0; if (s.is_registered_var(v)) old_value = to_numeral(s.get_ivalue(v).x); int64_t new_value = m_vars[v].m_best_value; - if (old_value == new_value) - continue; s.ensure_column(v); lp::column_index vj = s.lp().to_column_index(v); SASSERT(!vj.is_null()); @@ -73,40 +86,98 @@ namespace arith { // TODO - figure out why this leads to unsound (unsat). } } + + lbool r = s.make_feasible(); + VERIFY (!unsat().empty() || r == l_true); + if (unsat().empty()) { + s.m_num_conflicts = s.get_config().m_arith_propagation_threshold; + } + verbose_stream() << "has changed " << s.m_solver->has_changed_columns() << "\n"; + + auto check_bool_var = [&](sat::bool_var bv) { + auto* ineq = m_bool_vars.get(bv, nullptr); + if (!ineq) + return; + api_bound* b = nullptr; + s.m_bool_var2bound.find(bv, b); + if (!b) + return; + auto bound = b->get_value(); + theory_var v = b->get_var(); + if (s.get_phase(bv) == m_bool_search->get_model()[bv]) + return; + switch (b->get_bound_kind()) { + case lp_api::lower_t: + verbose_stream() << bv << " " << bound << " <= " << s.get_value(v) << "\n"; + break; + case lp_api::upper_t: + verbose_stream() << bv << " " << bound << " >= " << s.get_value(v) << "\n"; + break; + } + int64_t value = 0; + for (auto const& [coeff, v] : ineq->m_args) { + value += coeff * m_vars[v].m_best_value; + } + ineq->m_args_value = value; + verbose_stream() << *ineq << " dtt " << dtt(false, *ineq) << " phase " << s.get_phase(bv) << " model " << m_bool_search->get_model()[bv] << "\n"; + }; + + if (unsat().empty()) { + for (bool_var v = 0; v < s.s().num_vars(); ++v) + check_bool_var(v); + } } void sls::set(sat::ddfw* d) { m_bool_search = d; reset(); - m_literals.reserve(s.s().num_vars() * 2); + m_bool_vars.reserve(s.s().num_vars()); add_vars(); for (unsigned i = 0; i < d->num_clauses(); ++i) for (sat::literal lit : *d->get_clause_info(i).m_clause) - init_literal(lit); + init_bool_var(lit.var()); for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); - m_best_min_unsat = std::numeric_limits::max(); d->set(this); } - // distance to true - int64_t sls::dtt(int64_t args, ineq const& ineq) const { + int64_t sls::dtt(bool sign, int64_t args, ineq const& ineq) const { switch (ineq.m_op) { case ineq_kind::LE: + if (sign) { + if (args <= ineq.m_bound) + return ineq.m_bound - args + 1; + return 0; + } if (args <= ineq.m_bound) return 0; return args - ineq.m_bound; case ineq_kind::EQ: + if (sign) { + if (args == ineq.m_bound) + return 1; + return 0; + } if (args == ineq.m_bound) return 0; return 1; case ineq_kind::NE: + if (sign) { + if (args == ineq.m_bound) + return 0; + return 1; + } if (args == ineq.m_bound) return 1; return 0; case ineq_kind::LT: + if (sign) { + if (args < ineq.m_bound) + return ineq.m_bound - args; + return 0; + } if (args < ineq.m_bound) return 0; return args - ineq.m_bound + 1; @@ -121,45 +192,95 @@ namespace arith { // m_vars[w].m_value can be computed outside and shared among calls // different data-structures for storing coefficients // - int64_t sls::dtt(ineq const& ineq, var_t v, int64_t new_value) const { - auto new_args_value = ineq.m_args_value; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { - new_args_value += coeff * (new_value - m_vars[w].m_value); - break; - } - } - return dtt(new_args_value, ineq); + int64_t sls::dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const { + for (auto const& [coeff, w] : ineq.m_args) + if (w == v) + return dtt(sign, ineq.m_args_value + coeff * (new_value - m_vars[v].m_value), ineq); + return 1; + } + + int64_t sls::dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const { + return dtt(sign, ineq.m_args_value + coeff * (new_value - old_value), ineq); } - // critical move - bool sls::cm(ineq const& ineq, var_t v, int64_t& new_value) { - SASSERT(!ineq.is_true()); - int64_t delta = ineq.m_args_value - ineq.m_bound; - if (ineq.m_op == ineq_kind::NE || ineq.m_op == ineq_kind::LT) - delta--; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { + bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value) { + for (auto const& [coeff, w] : ineq.m_args) + if (w == v) + return cm(sign, ineq, v, coeff, new_value); + return false; + } - if (coeff > 0) - new_value = value(v) - abs((delta + coeff - 1)/ coeff); + bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { + VERIFY(ineq.is_true() == sign); + verbose_stream() << "cm " << ineq << " for " << v << " sign " << sign << "\n"; + auto bound = ineq.m_bound; + auto argsv = ineq.m_args_value; + bool solved = false; + int64_t delta = argsv - bound; + if (sign) { + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(argsv <= bound); + SASSERT(delta <= 0); + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) > bound); + return true; + case ineq_kind::LT: + SASSERT(argsv <= ineq.m_bound); + SASSERT(delta <= 0); + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) >= bound); + return true; + case ineq_kind::EQ: + if (delta >= 0) + delta++; else - new_value = value(v) + abs(delta) / -coeff; - - switch (ineq.m_op) { - case ineq_kind::LE: - SASSERT(delta + coeff * (new_value - value(v)) <= 0); - return true; - case ineq_kind::EQ: - return delta + coeff * (new_value - value(v)) == 0; - case ineq_kind::NE: - return delta + coeff * (new_value - value(v)) != 0; - case ineq_kind::LT: - return delta + coeff * (new_value - value(v)) < 0; - default: - UNREACHABLE(); - break; - } + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) != bound); + return true; + case ineq_kind::NE: + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + solved = argsv + coeff * (new_value - value(v)) == bound; + if (!solved) verbose_stream() << "did not solve disequality " << ineq << " for " << v << "\n"; + return solved; + default: + UNREACHABLE(); + break; + } + } + else { + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(argsv > ineq.m_bound); + SASSERT(delta > 0); + new_value = value(v) - (delta + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) <= bound); + return true; + case ineq_kind::LT: + SASSERT(argsv >= ineq.m_bound); + SASSERT(delta >= 0); + ++delta; + new_value = value(v) - (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) < bound); + return true; + case ineq_kind::NE: + if (delta >= 0) + delta++; + else + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) != bound); + return true; + case ineq_kind::EQ: + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + solved = argsv + coeff * (new_value - value(v)) == bound; + if (!solved) verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n"; + return solved; + default: + UNREACHABLE(); + break; } } return false; @@ -169,23 +290,19 @@ namespace arith { // it could be changed to flip on maximal positive score // or flip on maximal non-negative score // or flip on first non-negative score - bool sls::flip(ineq const& ineq) { + bool sls::flip(bool sign, ineq const& ineq) { int64_t new_value; - for (auto const& [coeff, v] : ineq.m_args) { - if (!cm(ineq, v, new_value)) - continue; - int score = cm_score(v, new_value); - if (score <= 0) - continue; - unsigned num_unsat = unsat().size(); - update(v, new_value); - IF_VERBOSE(2, - verbose_stream() << "v" << v << " score " << score << " " - << num_unsat << " -> " << unsat().size() << "\n"); - SASSERT(num_unsat > unsat().size()); - return true; + auto v = ineq.m_var_to_flip; + if (v == UINT_MAX) { + verbose_stream() << "no var to flip\n"; + return false; } - return false; + if (!cm(sign, ineq, v, new_value)) { + verbose_stream() << "no critical move for " << v << "\n"; + return false; + } + update(v, new_value); + return true; } // @@ -195,10 +312,13 @@ namespace arith { // double sls::dscore(var_t v, int64_t new_value) const { auto const& vi = m_vars[v]; + verbose_stream() << "dscore " << v << "\n"; double score = 0; +#if 0 for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); +#endif return score; } @@ -212,25 +332,28 @@ namespace arith { int score = 0; auto& vi = m_vars[v]; int64_t old_value = vi.m_value; - for (auto const& [coeff, lit] : vi.m_literals) { - auto const& ineq = *atom(lit); - int64_t dtt_old = dtt(ineq); - int64_t delta = coeff * (new_value - old_value); - int64_t dtt_new = dtt(ineq.m_args_value + delta, ineq); - - if (dtt_old == dtt_new) + for (auto const& [coeff, bv] : vi.m_bool_vars) { + auto const& ineq = *atom(bv); + bool sign = !m_bool_search->value(bv); + int64_t dtt_old = dtt(sign, ineq); + int64_t dtt_new = dtt(sign, ineq, coeff, old_value, new_value); + if ((dtt_old == 0) == (dtt_new == 0)) continue; - + sat::literal lit(bv, sign); + if (dtt_old == 0) + // flip from true to false + lit.neg(); + + // lit flips form false to true: for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); - if (!clause.is_true()) { - VERIFY(dtt_old != 0); - if (dtt_new == 0) - ++score; // false -> true - } - else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals - continue; - else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO + if (!clause.is_true()) + ++score; + } + // ignore the situation where clause contains multiple literals using v + for (auto cl : m_bool_search->get_use_list(~lit)) { + auto const& clause = get_clause_info(cl); + if (clause.m_num_trues == 1) --score; } } @@ -241,10 +364,10 @@ namespace arith { int64_t d(1), d2; bool first = true; for (auto a : get_clause(cl)) { - auto const* ineq = atom(a); + auto const* ineq = atom(a.var()); if (!ineq) continue; - d2 = dtt(*ineq); + d2 = dtt(a.sign(), *ineq); if (first) d = d2, first = false; else @@ -259,10 +382,10 @@ namespace arith { int64_t d(1), d2; bool first = true; for (auto lit : get_clause(cl)) { - auto const* ineq = atom(lit); + auto const* ineq = atom(lit.var()); if (!ineq) continue; - d2 = dtt(*ineq, v, new_value); + d2 = dtt(lit.sign(), *ineq, v, new_value); if (first) d = d2, first = false; else @@ -275,15 +398,17 @@ namespace arith { void sls::update(var_t v, int64_t new_value) { auto& vi = m_vars[v]; - auto const& old_value = vi.m_value; - for (auto const& [coeff, lit] : vi.m_literals) { - auto& ineq = *atom(lit); + auto old_value = vi.m_value; + for (auto const& [coeff, bv] : vi.m_bool_vars) { + auto& ineq = *atom(bv); + bool sign = !m_bool_search->value(bv); + sat::literal lit(bv, sign); + SASSERT(is_true(lit)); ineq.m_args_value += coeff * (new_value - old_value); - int64_t dtt_new = dtt(ineq); - if ((dtt_new == 0) != is_true(lit)) - m_bool_search->flip(lit.var()); - - SASSERT((dtt_new == 0) == is_true(lit)); + int64_t dtt_new = dtt(sign, ineq); + if (dtt_new != 0) + m_bool_search->flip(bv); + SASSERT(dtt(!m_bool_search->value(bv), ineq) == 0); } vi.m_value = new_value; } @@ -304,10 +429,10 @@ namespace arith { return *i; } - void sls::add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v) { + void sls::add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v) { ineq.m_args.push_back({ c, v }); ineq.m_args_value += c * value(v); - m_vars[v].m_literals.push_back({ c, lit }); + m_vars[v].m_bool_vars.push_back({ c, bv}); } int64_t sls::to_numeral(rational const& r) { @@ -316,79 +441,63 @@ namespace arith { return 0; } - - void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { + void sls::add_args(sat::bool_var bv, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - add_arg(lit, ineq, sign * to_numeral(arg.coeff()), w); + add_arg(bv, ineq, sign * to_numeral(arg.coeff()), w); } } else - add_arg(lit, ineq, sign, s.lp().local_to_external(t.id())); + add_arg(bv, ineq, sign, s.lp().local_to_external(t.id())); } - - void sls::init_literal(sat::literal lit) { - if (m_literals.get(lit.index(), nullptr)) + void sls::init_bool_var(sat::bool_var bv) { + if (m_bool_vars.get(bv, nullptr)) return; api_bound* b = nullptr; - s.m_bool_var2bound.find(lit.var(), b); + s.m_bool_var2bound.find(bv, b); if (b) { auto t = b->tv(); rational bound = b->get_value(); bool should_minus = false; sls::ineq_kind op; - if (!lit.sign()) { - should_minus = b->get_bound_kind() == lp_api::bound_kind::upper_t; - op = sls::ineq_kind::LE; - } - else { - should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t; - if (s.is_int(b->get_var())) { - bound -= 1; - op = sls::ineq_kind::LE; - } - else - op = sls::ineq_kind::LT; - - } + should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t; + op = sls::ineq_kind::LE; if (should_minus) bound.neg(); + auto& ineq = new_ineq(op, to_numeral(bound)); - add_args(lit, ineq, t, b->get_var(), should_minus ? -1 : 1); - m_literals.set(lit.index(), &ineq); + add_args(bv, ineq, t, b->get_var(), should_minus ? -1 : 1); + m_bool_vars.set(bv, &ineq); + m_bool_search->set_external(bv); return; } - expr* e = s.bool_var2expr(lit.var()); + expr* e = s.bool_var2expr(bv); expr* l = nullptr, * r = nullptr; if (e && m.is_eq(e, l, r) && s.a.is_int_real(l)) { theory_var u = s.get_th_var(l); theory_var v = s.get_th_var(r); lp::tv tu = s.get_tv(u); lp::tv tv = s.get_tv(v); - auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, 0); - add_args(lit, ineq, tu, u, 1); - add_args(lit, ineq, tv, v, -1); - m_literals.set(lit.index(), &ineq); + auto& ineq = new_ineq(sls::ineq_kind::EQ, 0); + add_args(bv, ineq, tu, u, 1); + add_args(bv, ineq, tv, v, -1); + m_bool_vars.set(bv, &ineq); + m_bool_search->set_external(bv); return; } } void sls::init_bool_var_assignment(sat::bool_var v) { - init_literal_assignment(literal(v, false)); - init_literal_assignment(literal(v, true)); - } - - void sls::init_literal_assignment(sat::literal lit) { - auto* ineq = m_literals.get(lit.index(), nullptr); - if (ineq && is_true(lit) != (dtt(*ineq) == 0)) - m_bool_search->flip(lit.var()); + auto* ineq = m_bool_vars.get(v, nullptr); + if (ineq && is_true(sat::literal(v, false)) != (dtt(false, *ineq) == 0)) + m_bool_search->flip(v); } void sls::init_search() { @@ -402,14 +511,13 @@ namespace arith { void sls::flip(sat::bool_var v) { sat::literal lit(v, m_bool_search->get_value(v)); SASSERT(!is_true(lit)); - auto const* ineq = atom(lit); + auto const* ineq = atom(v); if (!ineq) IF_VERBOSE(0, verbose_stream() << "no inequality for variable " << v << "\n"); if (!ineq) return; - IF_VERBOSE(1, verbose_stream() << "flip " << lit << "\n"); - SASSERT(!ineq->is_true()); - flip(*ineq); + SASSERT(ineq->is_true() == lit.sign()); + flip(!lit.sign(), *ineq); } double sls::reward(sat::bool_var v) { @@ -419,39 +527,56 @@ namespace arith { return dtt_reward(v); } - double sls::dtt_reward(sat::bool_var v) { - sat::literal litv(v, m_bool_search->get_value(v)); - auto const* ineq = atom(litv); + double sls::dtt_reward(sat::bool_var bv0) { + bool sign0 = !m_bool_search->get_value(bv0); + auto* ineq = atom(bv0); if (!ineq) - return 0; - int64_t new_value; + return -1; + int64_t new_value; double result = 0; + double max_result = -1; + theory_var max_var = 0; for (auto const & [coeff, x] : ineq->m_args) { - if (!cm(*ineq, x, new_value)) + if (!cm(!sign0, *ineq, x, coeff, new_value)) continue; - for (auto const [coeff, lit] : m_vars[x].m_literals) { - auto dtt_old = dtt(*atom(lit)); - auto dtt_new = dtt(*atom(lit), x, new_value); + double result = 0; + auto old_value = m_vars[x].m_value; + for (auto const [coeff, bv] : m_vars[x].m_bool_vars) { + bool sign = !m_bool_search->value(bv); + auto dtt_old = dtt(sign, *atom(bv)); + auto dtt_new = dtt(sign, *atom(bv), coeff, old_value, new_value); if ((dtt_new == 0) != (dtt_old == 0)) - result += m_bool_search->reward(lit.var()); + result += m_bool_search->reward(bv); + } + if (result > max_result) { + max_result = result; + ineq->m_var_to_flip = x; } } - return result; + return max_result; } - double sls::dscore_reward(sat::bool_var x) { + double sls::dscore_reward(sat::bool_var bv) { m_dscore_mode = false; - sat::literal litv(x, m_bool_search->get_value(x)); - auto const* ineq = atom(litv); + bool sign = !m_bool_search->get_value(bv); + sat::literal litv(bv, sign); + auto* ineq = atom(bv); if (!ineq) return 0; - SASSERT(!ineq->is_true()); + SASSERT(ineq->is_true() == sign); int64_t new_value; - double result = 0; - for (auto const& [coeff, v] : ineq->m_args) - if (cm(*ineq, v, new_value)) - result += dscore(v, new_value); - return result; + + for (auto const& [coeff, v] : ineq->m_args) { + double result = 0; + if (cm(sign, *ineq, v, coeff, new_value)) + result = dscore(v, new_value); + // just pick first positive, or pick a max? + if (result > 0) { + ineq->m_var_to_flip = v; + return result; + } + } + return 0; } // switch to dscore mode @@ -466,5 +591,24 @@ namespace arith { void sls::on_restart() { for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); + + verbose_stream() << "on-restart\n"; + auto check_bool_var = [&](sat::bool_var bv) { + auto const* ineq = atom(bv); + if (!ineq) + return; + bool sign = !m_bool_search->get_value(bv); + int64_t d = dtt(sign, *ineq); + sat::literal lit(bv, sign); + // verbose_stream() << "check " << lit << " " << *ineq << "\n"; + if (is_true(lit) != (d == 0)) { + verbose_stream() << "restart " << bv << " " << *ineq << "\n"; + } + VERIFY(is_true(lit) == (d == 0)); + }; + for (unsigned v = 0; v < s.get_num_vars(); ++v) + check_bool_var(v); + + verbose_stream() << "on-restart-done\n"; } } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 5496be63747..3c9daaa5127 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -60,8 +60,9 @@ namespace arith { struct ineq { vector> m_args; ineq_kind m_op = ineq_kind::LE; - int64_t m_bound; - int64_t m_args_value; + int64_t m_bound; + int64_t m_args_value; + unsigned m_var_to_flip = UINT_MAX; bool is_true() const { switch (m_op) { @@ -97,17 +98,15 @@ namespace arith { int64_t m_value; int64_t m_best_value; var_kind m_kind = var_kind::INT; - svector> m_literals; + svector> m_bool_vars; }; solver& s; ast_manager& m; sat::ddfw* m_bool_search = nullptr; - unsigned m_max_arith_steps = 0; - unsigned m_best_min_unsat = UINT_MAX; stats m_stats; config m_config; - scoped_ptr_vector m_literals; + scoped_ptr_vector m_bool_vars; vector m_vars; svector> m_terms; bool m_dscore_mode = false; @@ -122,17 +121,19 @@ namespace arith { bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); } void reset(); - ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } + ineq* atom(sat::bool_var bv) const { return m_bool_vars[bv]; } void log(); - bool flip(ineq const& ineq); - int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } - int64_t dtt(int64_t args, ineq const& ineq) const; - int64_t dtt(ineq const& ineq, var_t v, int64_t new_value) const; + bool flip(bool sign, ineq const& ineq); + int64_t dtt(bool sign, ineq const& ineq) const { return dtt(sign, ineq.m_args_value, ineq); } + int64_t dtt(bool sign, int64_t args_value, ineq const& ineq) const; + int64_t dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const; + int64_t dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const; int64_t dts(unsigned cl, var_t v, int64_t new_value) const; int64_t compute_dts(unsigned cl) const; - bool cm(ineq const& ineq, var_t v, int64_t& new_value); + bool cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value); + bool cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value); int cm_score(var_t v, int64_t new_value); void update(var_t v, int64_t new_value); double dscore_reward(sat::bool_var v); @@ -142,11 +143,10 @@ namespace arith { void store_best_values(); void add_vars(); sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); - void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); - void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); - void init_literal(sat::literal lit); + void add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v); + void add_args(sat::bool_var bv, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); + void init_bool_var(sat::bool_var v); void init_bool_var_assignment(sat::bool_var v); - void init_literal_assignment(sat::literal lit); int64_t value(var_t v) const { return m_vars[v].m_value; } int64_t to_numeral(rational const& r); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index fd507ed15c9..bd5dd315fe7 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -101,8 +101,7 @@ namespace arith { return false; switch (lbl) { - case l_false: - TRACE("arith", tout << "propagation conflict\n";); + case l_false: get_infeasibility_explanation_and_set_conflict(); break; case l_true: @@ -382,9 +381,9 @@ namespace arith { void solver::assert_bound(bool is_true, api_bound& b) { - TRACE("arith", tout << b << "\n";); lp::constraint_index ci = b.get_constraint(is_true); lp().activate(ci); + TRACE("arith", tout << b << " " << is_infeasible() << "\n";); if (is_infeasible()) return; lp::lconstraint_kind k = bound2constraint_kind(b.is_int(), b.get_bound_kind(), is_true); @@ -1066,6 +1065,7 @@ namespace arith { TRACE("pcs", tout << lp().constraints();); auto status = lp().find_feasible_solution(); TRACE("arith_verbose", display(tout);); + TRACE("arith", tout << status << "\n"); switch (status) { case lp::lp_status::INFEASIBLE: return l_false; @@ -1202,7 +1202,7 @@ namespace arith { TRACE("arith", tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n"; - for (literal c : m_core) tout << literal2expr(c) << "\n"; + for (literal c : m_core) tout << c << ": " << literal2expr(c) << "\n"; for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";); if (is_conflict) { diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index d22a65fb871..1c83b3a691e 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -38,6 +38,13 @@ namespace euf { for (unsigned i = 0; i < mdl.size(); ++i) phase[i] = mdl[i] == l_true; + if (bool_search.unsat_set().empty()) { + enable_trace("arith"); + enable_trace("sat"); + enable_trace("euf"); + TRACE("sat", s().display(tout)); + } + return bool_search.unsat_set().empty() ? l_true : l_undef; } } From daeaed1e82748c5b404d514bfc418a6ce69c21ee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:13:40 -0800 Subject: [PATCH 430/597] revert debug only changes to sat-solver --- src/sat/sat_solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 5ac0fbcf3af..56330d68db4 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -2974,7 +2974,7 @@ namespace sat { } bool solver::should_rephase() { - return m_conflicts_since_init > 5 && m_conflicts_since_init > m_rephase_lim; + return m_conflicts_since_init > m_rephase_lim; } void solver::do_rephase() { @@ -3023,7 +3023,7 @@ namespace sat { UNREACHABLE(); break; } - m_rephase_inc = m_config.m_rephase_base; + m_rephase_inc += m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; } From 6092bf534c38ffc9573e4f8475f418f31faacad9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:18:02 -0800 Subject: [PATCH 431/597] fix #6599 --- src/sat/sat_prob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/sat_prob.h b/src/sat/sat_prob.h index d8d58d0911a..69175815412 100644 --- a/src/sat/sat_prob.h +++ b/src/sat/sat_prob.h @@ -140,7 +140,7 @@ namespace sat { model const& get_model() const override { return m_model; } - double get_priority(bool_var v) const { return 0; } + double get_priority(bool_var v) const override { return 0; } std::ostream& display(std::ostream& out) const; From c0f80f92ba5f84671dfb56334959202ccc761f78 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 17:53:37 -0800 Subject: [PATCH 432/597] deal with compiler warnings (unused variables etc) --- src/ast/rewriter/bv_rewriter.cpp | 2 +- src/sat/sat_ddfw.cpp | 3 +-- src/sat/smt/arith_sls.cpp | 6 +----- src/sat/smt/euf_local_search.cpp | 2 +- src/smt/theory_pb.cpp | 2 +- src/test/api.cpp | 1 + src/test/bit_blaster.cpp | 10 +++------- src/test/interval.cpp | 11 +++++------ 8 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 1c374dcb84b..5f76d3fddb2 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -1554,7 +1554,7 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re if (eq_args) { if (m.is_ite(new_args.back(), x, y, z)) { ptr_buffer args1, args2; - for (expr* arg : new_args) + for (unsigned i = 0; i < new_args.size(); ++i) 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; diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 329d783f4db..7ab05804ae9 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -583,8 +583,7 @@ namespace sat { void ddfw::shift_weights() { ++m_shifts; for (unsigned to_idx : m_unsat) { - auto& cf = m_clauses[to_idx]; - SASSERT(!cf.is_true()); + SASSERT(!m_clauses[to_idx].is_true()); unsigned from_idx = select_max_same_sign(to_idx); if (from_idx == UINT_MAX || disregard_neighbor()) from_idx = select_random_true_clause(); diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index aeddd319bcc..26358dc177a 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -72,9 +72,6 @@ namespace arith { continue; if (!s.lp().external_is_used(v)) continue; - int64_t old_value = 0; - if (s.is_registered_var(v)) - old_value = to_numeral(s.get_ivalue(v).x); int64_t new_value = m_vars[v].m_best_value; s.ensure_column(v); lp::column_index vj = s.lp().to_column_index(v); @@ -535,13 +532,12 @@ namespace arith { int64_t new_value; double result = 0; double max_result = -1; - theory_var max_var = 0; for (auto const & [coeff, x] : ineq->m_args) { if (!cm(!sign0, *ineq, x, coeff, new_value)) continue; double result = 0; auto old_value = m_vars[x].m_value; - for (auto const [coeff, bv] : m_vars[x].m_bool_vars) { + for (auto const& [coeff, bv] : m_vars[x].m_bool_vars) { bool sign = !m_bool_search->value(bv); auto dtt_old = dtt(sign, *atom(bv)); auto dtt_new = dtt(sign, *atom(bv), coeff, old_value, new_value); diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 1c83b3a691e..ca450e513e3 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -32,7 +32,7 @@ namespace euf { for (auto* th : m_solvers) th->set_bool_search(&bool_search); - lbool r = bool_search.check(0, nullptr, nullptr); + bool_search.check(0, nullptr, nullptr); auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index db076ef8cac..718d5c65ab8 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -1604,7 +1604,7 @@ namespace smt { std::cout << B << "\n"; } #endif - SASSERT(is_sat != l_true); + VERIFY(is_sat != l_true); return true; } diff --git a/src/test/api.cpp b/src/test/api.cpp index ccbbef6ea34..ea95c1264e0 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -100,6 +100,7 @@ static void test_mk_distinct() { Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; Z3_ast d = Z3_mk_distinct(ctx, 2, args); + VERIFY(d); ENSURE(cb_called); Z3_del_config(cfg); Z3_del_context(ctx); diff --git a/src/test/bit_blaster.cpp b/src/test/bit_blaster.cpp index 8c4cd090337..fa623f76762 100644 --- a/src/test/bit_blaster.cpp +++ b/src/test/bit_blaster.cpp @@ -28,13 +28,9 @@ void mk_bits(ast_manager & m, char const * prefix, unsigned sz, expr_ref_vector sort_ref b(m); b = m.mk_bool_sort(); for (unsigned i = 0; i < sz; ++i) { - char buffer[128]; -#ifdef _WINDOWS - sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%s%d.smt", prefix, i); -#else - sprintf(buffer, "%s%d.smt", prefix, i); -#endif - r.push_back(m.mk_const(symbol(buffer), b)); + std::stringstream ous; + ous << prefix << i << ".smt2"; + r.push_back(m.mk_const(symbol(ous.str()), b)); } } diff --git a/src/test/interval.cpp b/src/test/interval.cpp index f289871deab..64f6cb88c1b 100644 --- a/src/test/interval.cpp +++ b/src/test/interval.cpp @@ -24,6 +24,7 @@ Revision History: #include "util/debug.h" #include "util/rlimit.h" #include +#include template class interval_manager; typedef im_default_config::interval interval; @@ -200,15 +201,13 @@ static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { #define BUFFER_SZ 256 static int g_problem_id = 0; static char g_buffer[BUFFER_SZ]; +static std::stringstream ous; char const * get_next_file_name() { -#ifdef _WINDOWS - sprintf_s(g_buffer, BUFFER_SZ, "interval_lemma_%d.smt2", g_problem_id); -#else - sprintf(g_buffer, "interval_lemma_%d.smt2", g_problem_id); -#endif + ous.clear(); + ous << "interval_lemma_" << g_problem_id << ".smt2"; g_problem_id++; - return g_buffer; + return ous.str().c_str(); } static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, From cb814732603b584511b49a759fe5c99d3627bdb7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 17:54:26 -0800 Subject: [PATCH 433/597] add destructive equality resolution to the main simplifier. --- src/ast/rewriter/der.cpp | 5 +++-- src/ast/rewriter/th_rewriter.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index fd79529f9f6..0e28cf6f6fa 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -197,9 +197,10 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { m_pos2var.reserve(num_args, -1); - // Find all disequalities + // Find all equalities/disequalities for (unsigned i = 0; i < num_args; i++) { - is_eq = is_forall(q) ? is_var_diseq(literals.get(i), num_decls, v, t) : is_var_eq(literals.get(i), num_decls, v, t); + expr* arg = literals.get(i); + is_eq = is_forall(q) ? is_var_diseq(arg, num_decls, v, t) : is_var_eq(arg, num_decls, v, t); if (is_eq) { unsigned idx = v->get_idx(); if (m_map.get(idx, nullptr) == nullptr) { diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index c6ab22636d4..c57d3aabc2e 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -31,6 +31,7 @@ Module Name: #include "ast/rewriter/seq_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" +#include "ast/rewriter/der.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -54,6 +55,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { recfun_rewriter m_rec_rw; arith_util m_a_util; bv_util m_bv_util; + der m_der; expr_safe_replace m_rep; expr_ref_vector m_pinned; // substitution support @@ -819,6 +821,15 @@ struct th_rewriter_cfg : public default_rewriter_cfg { result_pr = m().mk_transitivity(p1, p2); } + if (is_quantifier(result)) { + proof_ref p2(m()); + expr_ref r(m()); + m_der(to_quantifier(result), r, p2); + if (m().proofs_enabled() && result.get() != r.get()) + result_pr = m().mk_transitivity(result_pr, p2); + result = r; + } + TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;); SASSERT(old_q->get_sort() == result->get_sort()); @@ -839,6 +850,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_rec_rw(m), m_a_util(m), m_bv_util(m), + m_der(m), m_rep(m), m_pinned(m), m_used_dependencies(m) { From a6eed9f00c1a070e7c97871d2d2f9319b6e82480 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 18:43:20 -0800 Subject: [PATCH 434/597] Update api.cpp fix test --- src/test/api.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/api.cpp b/src/test/api.cpp index ea95c1264e0..560dd11218f 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -88,7 +88,7 @@ void test_bvneg() { static bool cb_called = false; static void my_cb(Z3_context, Z3_error_code) { - cb_called = true; + cb_called = true; } static void test_mk_distinct() { @@ -100,8 +100,8 @@ static void test_mk_distinct() { Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; Z3_ast d = Z3_mk_distinct(ctx, 2, args); - VERIFY(d); ENSURE(cb_called); + VERIFY(!d); Z3_del_config(cfg); Z3_del_context(ctx); From 6352340478e605711348a1abc8d2fdba23e9474d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 09:59:33 -0800 Subject: [PATCH 435/597] update do logging --- src/smt/mam.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 44a43504180..3804b72284e 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -3958,7 +3958,7 @@ namespace { void relevant_eh(enode * n, bool lazy) override { TRACE("trigger_bug", tout << "relevant_eh:\n" << mk_ismt2_pp(n->get_expr(), m) << "\n"; tout << "mam: " << this << "\n";); - TRACE("mam", tout << "relevant_eh: #" << n->get_owner_id() << "\n";); + TRACE("mam", tout << "relevant_eh: #" << enode_pp(n, m_context) << "\n";); if (n->has_lbl_hash()) update_lbls(n, n->get_lbl_hash()); From 9b6ac45e022d53b263d5507500ec72b76a5ccb13 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 10:03:38 -0800 Subject: [PATCH 436/597] compile warnings Signed-off-by: Nikolaj Bjorner --- src/test/interval.cpp | 8 +++----- src/test/no_overflow.cpp | 4 ++-- src/test/qe_arith.cpp | 3 +++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/test/interval.cpp b/src/test/interval.cpp index 64f6cb88c1b..289265949d9 100644 --- a/src/test/interval.cpp +++ b/src/test/interval.cpp @@ -200,14 +200,12 @@ static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { #define BUFFER_SZ 256 static int g_problem_id = 0; -static char g_buffer[BUFFER_SZ]; -static std::stringstream ous; -char const * get_next_file_name() { - ous.clear(); +std::string get_next_file_name() { + std::stringstream ous; ous << "interval_lemma_" << g_problem_id << ".smt2"; g_problem_id++; - return ous.str().c_str(); + return ous.str(); } static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, diff --git a/src/test/no_overflow.cpp b/src/test/no_overflow.cpp index dd826bad8b1..c7124a5ce8d 100644 --- a/src/test/no_overflow.cpp +++ b/src/test/no_overflow.cpp @@ -529,8 +529,8 @@ void test_div(unsigned bvsize) { Z3_del_context(ctx); } -typedef Z3_ast (Z3_API *NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed); -typedef Z3_ast (Z3_API *ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); +typedef Z3_ast (*NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed); +typedef Z3_ast (*ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); typedef enum { OVFL_FUNC, UDFL_FUNC } overflow_type; diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index 859d7f2e5bb..2e170979a1b 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -377,6 +377,9 @@ static void add_random_ineq( case opt::t_mod: NOT_IMPLEMENTED_YET(); break; + default: + NOT_IMPLEMENTED_YET(); + break; } fmls.push_back(fml); mbo.add_constraint(vars, rational(coeff), rel); From bc6037464d4f63e7ff8721c36f9b3d7709cf924c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 10:08:31 -0800 Subject: [PATCH 437/597] clean up build warnings Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_convex_closure.cpp | 16 +++++++++------- src/sat/smt/arith_sls.cpp | 5 ++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_convex_closure.cpp b/src/muz/spacer/spacer_convex_closure.cpp index 47682164301..e313c972bed 100644 --- a/src/muz/spacer/spacer_convex_closure.cpp +++ b/src/muz/spacer/spacer_convex_closure.cpp @@ -22,21 +22,23 @@ Module Name: #include "ast/rewriter/th_rewriter.h" namespace { + +#ifdef Z3DEBUG bool is_int_matrix(const spacer::spacer_matrix &matrix) { - rational val; - for (unsigned i = 0, rows = matrix.num_rows(); i < rows; i++) { + for (unsigned i = 0, rows = matrix.num_rows(); i < rows; i++) for (unsigned j = 0, cols = matrix.num_cols(); j < cols; j++) - if (!matrix.get(i, j).is_int()) return false; - } + if (!matrix.get(i, j).is_int()) + return false; return true; } bool is_sorted(const vector &data) { - for (unsigned i = 0; i < data.size() - 1; i++) { - if (!(data[i] >= data[i + 1])) return false; - } + for (unsigned i = 0; i < data.size() - 1; i++) + if (!(data[i] >= data[i + 1])) + return false; return true; } +#endif /// Check whether all elements of \p data are congruent modulo \p m bool is_congruent_mod(const vector &data, const rational &m) { diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 26358dc177a..42594f04327 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -308,10 +308,10 @@ namespace arith { // cached dts has to be updated when the score of literals are updated. // double sls::dscore(var_t v, int64_t new_value) const { - auto const& vi = m_vars[v]; - verbose_stream() << "dscore " << v << "\n"; double score = 0; #if 0 + auto const& vi = m_vars[v]; + verbose_stream() << "dscore " << v << "\n"; for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); @@ -530,7 +530,6 @@ namespace arith { if (!ineq) return -1; int64_t new_value; - double result = 0; double max_result = -1; for (auto const & [coeff, x] : ineq->m_args) { if (!cm(!sign0, *ineq, x, coeff, new_value)) From 6454e7fa3f541029868519c04acd16433ee2b33b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 11:03:04 -0800 Subject: [PATCH 438/597] apply rewriting if result of destructive equality resolution is simplified --- src/ast/rewriter/th_rewriter.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index c57d3aabc2e..93b867c4595 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -821,17 +821,28 @@ struct th_rewriter_cfg : public default_rewriter_cfg { result_pr = m().mk_transitivity(p1, p2); } + TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;); + + proof_ref p2(m()); + expr_ref r(m()); + + bool der_change = false; if (is_quantifier(result)) { - proof_ref p2(m()); - expr_ref r(m()); m_der(to_quantifier(result), r, p2); + der_change = result.get() != r.get(); + if (m().proofs_enabled() && der_change) + result_pr = m().mk_transitivity(result_pr, p2); + result = r; + } + + if (der_change) { + th_rewriter rw(m()); + rw(result, r, p2); if (m().proofs_enabled() && result.get() != r.get()) result_pr = m().mk_transitivity(result_pr, p2); result = r; } - TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;); - SASSERT(old_q->get_sort() == result->get_sort()); return true; } From 0758c930868959937b09d94568ea26bc6297a196 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 11:09:52 -0800 Subject: [PATCH 439/597] fix #6591 - add check for lambdas similar to as-array in context of quantifiers. MBQI is not a decision procedure for this combination and can then incorrectly conclude satisfiabiltiy. Scenario The formula contains assertions - bv = (map or (lambda ..) t) - forall y (not (select bv (pair s y))) Since bv is extensionally equal to a term that depends on a lambda, MBQI cannot just take the current finite approximation of bv when checking the quantifier for satisfiability. --- src/smt/theory_array_full.cpp | 8 ++++++++ src/smt/theory_array_full.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 120e124189c..a4876ab4d81 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -252,6 +252,8 @@ namespace smt { else if (m.is_lambda_def(n->get_decl())) { instantiate_default_lambda_def_axiom(n); d->m_lambdas.push_back(n); + m_lambdas.push_back(n); + ctx.push_trail(push_back_vector(m_lambdas)); } return r; } @@ -830,6 +832,12 @@ namespace smt { return true; } } + for (enode* n : m_lambdas) + for (enode* p : n->get_parents()) + if (!ctx.is_beta_redex(p, n)) { + TRACE("array", tout << "not a beta redex " << enode_pp(p, ctx) << "\n"); + return true; + } return false; } diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 210426e10d2..98e53627c90 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -86,6 +86,7 @@ namespace smt { bool has_unitary_domain(app* array_term); std::pair mk_epsilon(sort* s); enode_vector m_as_array; + enode_vector m_lambdas; bool has_non_beta_as_array(); bool instantiate_select_const_axiom(enode* select, enode* cnst); From 755b517001cf3f6926fd448a8e568e32efde0689 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 14:02:37 -0800 Subject: [PATCH 440/597] fix #6600 ensure that semantics of last-indexof(t,"") = len(t) --- src/ast/rewriter/seq_axioms.cpp | 4 ++-- src/util/zstring.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index 14f34f1f66a..e08ead08b71 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -513,8 +513,8 @@ namespace seq { !contains(t, s) => i = -1 |t| = 0 => |s| = 0 or i = -1 - |t| = 0 & |s| = 0 => i = 0 |t| != 0 & contains(t, s) => t = xsy & i = len(x) + |s| = 0 => i = len(t) |s| = 0 or s = s_head*s_tail |s| = 0 or !contains(s_tail*y, s) @@ -540,7 +540,7 @@ namespace seq { add_clause(cnt, i_eq_m1); add_clause(~t_eq_empty, s_eq_empty, i_eq_m1); - add_clause(~t_eq_empty, ~s_eq_empty, i_eq_0); + add_clause(~s_eq_empty, mk_eq(i, mk_len(t))); add_clause(t_eq_empty, ~cnt, mk_seq_eq(t, xsy)); add_clause(t_eq_empty, ~cnt, mk_eq(i, mk_len(x))); add_clause(s_eq_empty, mk_eq(s, mk_concat(s_head, s_tail))); diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 7d2b4296a54..570510458dd 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -214,8 +214,7 @@ int zstring::indexofu(zstring const& other, unsigned offset) const { } int zstring::last_indexof(zstring const& other) const { - if (length() == 0 && other.length() == 0) return 0; - if (other.length() == 0) return -1; + if (other.length() == 0) return length(); if (other.length() > length()) return -1; for (unsigned last = length() - other.length() + 1; last-- > 0; ) { bool suffix = true; From 4aa05b2b5759a1049464e9ff453c9b448d82b835 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Feb 2023 12:16:43 -0800 Subject: [PATCH 441/597] remove limiting error mode #6600 --- src/parsers/smt2/smt2parser.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 9f61d48ca84..7a1fd640a13 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -2640,8 +2640,6 @@ namespace smt2 { check_lparen_next("invalid get-value command, '(' expected"); while (!curr_is_rparen()) { parse_expr(); - if (!is_ground(expr_stack().back())) - throw cmd_exception("invalid get-value term, term must be ground and must not contain quantifiers"); m_cached_strings.push_back(m_scanner.cached_str(cache_it, m_cache_end)); cache_it = m_cache_end; } @@ -2680,7 +2678,7 @@ namespace smt2 { m_ctx.regular_stream() << "\n "; m_ctx.regular_stream() << "(" << m_cached_strings[i] << " "; m_ctx.display(m_ctx.regular_stream(), v); - m_ctx.regular_stream() << ")"; + m_ctx.regular_stream() << ")"; } m_ctx.regular_stream() << ")" << std::endl; expr_stack().shrink(spos); From 146f0eae0687c3ee5e7030deff64e5ea6c2774e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Feb 2023 12:17:14 -0800 Subject: [PATCH 442/597] wip - arith local search --- src/sat/sat_ddfw.cpp | 2 +- src/sat/sat_solver.cpp | 6 ++++ src/sat/smt/arith_sls.cpp | 74 ++++++++++++++++++++++----------------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 7ab05804ae9..5d80e5af4d1 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -62,7 +62,7 @@ namespace sat { void ddfw::check_with_plugin() { m_plugin->init_search(); m_steps_since_progress = 0; - while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) { + while (m_min_sz > 0 && m_steps_since_progress++ <= 150000) { if (should_reinit_weights()) do_reinit_weights(); else if (do_flip()); else if (do_literal_flip()); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 56330d68db4..d8d68d26208 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1302,6 +1302,12 @@ namespace sat { return l_undef; } + if (false && m_config.m_phase == PS_LOCAL_SEARCH && m_ext) { + IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n"); + bounded_local_search(); + exit(0); + } + log_stats(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 42594f04327..b32358a3c25 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -207,41 +207,59 @@ namespace arith { return false; } - bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { - VERIFY(ineq.is_true() == sign); - verbose_stream() << "cm " << ineq << " for " << v << " sign " << sign << "\n"; + bool sls::cm(bool new_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { + SASSERT(ineq.is_true() == new_sign); + VERIFY(ineq.is_true() == new_sign); auto bound = ineq.m_bound; auto argsv = ineq.m_args_value; bool solved = false; int64_t delta = argsv - bound; - if (sign) { + auto make_eq = [&]() { + SASSERT(delta != 0); + if (delta < 0) + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + else + new_value = value(v) - (delta + abs(coeff) - 1) / coeff; + solved = argsv + coeff * (new_value - value(v)) == bound; + if (!solved && abs(coeff) == 1) { + verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n"; + verbose_stream() << new_value << " " << value(v) << " delta " << delta << " lhs " << (argsv + coeff * (new_value - value(v))) << " bound " << bound << "\n"; + UNREACHABLE(); + } + return solved; + }; + + auto make_diseq = [&]() { + if (delta >= 0) + delta++; + else + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) != bound); + return true; + }; + + if (new_sign) { switch (ineq.m_op) { case ineq_kind::LE: + // args <= bound -> args > bound SASSERT(argsv <= bound); SASSERT(delta <= 0); - delta--; + --delta; new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; VERIFY(argsv + coeff * (new_value - value(v)) > bound); return true; case ineq_kind::LT: + // args < bound -> args >= bound SASSERT(argsv <= ineq.m_bound); SASSERT(delta <= 0); new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; VERIFY(argsv + coeff * (new_value - value(v)) >= bound); return true; case ineq_kind::EQ: - if (delta >= 0) - delta++; - else - delta--; - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - VERIFY(argsv + coeff * (new_value - value(v)) != bound); - return true; + return make_diseq(); case ineq_kind::NE: - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - solved = argsv + coeff * (new_value - value(v)) == bound; - if (!solved) verbose_stream() << "did not solve disequality " << ineq << " for " << v << "\n"; - return solved; + return make_eq(); default: UNREACHABLE(); break; @@ -263,18 +281,9 @@ namespace arith { VERIFY(argsv + coeff * (new_value - value(v)) < bound); return true; case ineq_kind::NE: - if (delta >= 0) - delta++; - else - delta--; - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - VERIFY(argsv + coeff * (new_value - value(v)) != bound); - return true; + return make_diseq(); case ineq_kind::EQ: - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - solved = argsv + coeff * (new_value - value(v)) == bound; - if (!solved) verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n"; - return solved; + return make_eq(); default: UNREACHABLE(); break; @@ -291,10 +300,10 @@ namespace arith { int64_t new_value; auto v = ineq.m_var_to_flip; if (v == UINT_MAX) { - verbose_stream() << "no var to flip\n"; + // verbose_stream() << "no var to flip\n"; return false; } - if (!cm(sign, ineq, v, new_value)) { + if (!cm(!sign, ineq, v, new_value)) { verbose_stream() << "no critical move for " << v << "\n"; return false; } @@ -308,6 +317,7 @@ namespace arith { // cached dts has to be updated when the score of literals are updated. // double sls::dscore(var_t v, int64_t new_value) const { + verbose_stream() << "dscore\n"; double score = 0; #if 0 auto const& vi = m_vars[v]; @@ -558,12 +568,12 @@ namespace arith { auto* ineq = atom(bv); if (!ineq) return 0; - SASSERT(ineq->is_true() == sign); + SASSERT(ineq->is_true() != sign); int64_t new_value; for (auto const& [coeff, v] : ineq->m_args) { double result = 0; - if (cm(sign, *ineq, v, coeff, new_value)) + if (cm(!sign, *ineq, v, coeff, new_value)) result = dscore(v, new_value); // just pick first positive, or pick a max? if (result > 0) { @@ -576,7 +586,7 @@ namespace arith { // switch to dscore mode void sls::on_rescale() { - m_dscore_mode = true; + // m_dscore_mode = true; } void sls::on_save_model() { From 828fff96849f5f102e8217b4882650c20f31520b Mon Sep 17 00:00:00 2001 From: hgvk94 Date: Wed, 22 Feb 2023 18:28:33 -0500 Subject: [PATCH 443/597] fix #6543. don't assume order on bindings --- src/muz/spacer/spacer_global_generalizer.cpp | 24 ++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/muz/spacer/spacer_global_generalizer.cpp b/src/muz/spacer/spacer_global_generalizer.cpp index 8c0d7aa6fbc..55bc4eec791 100644 --- a/src/muz/spacer/spacer_global_generalizer.cpp +++ b/src/muz/spacer/spacer_global_generalizer.cpp @@ -170,14 +170,14 @@ void lemma_global_generalizer::subsumer::mk_col_names(const lemma_cluster &lc) { m_col_names.reserve(sub.get_num_bindings()); for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) { - // get var id (sub is in reverse order) - sub.get_binding(sz - 1 - j, v, r); + sub.get_binding(j, v, r); auto *sort = r.get_expr()->get_sort(); - - if (!m_col_names.get(j) || m_col_names.get(j)->get_sort() != sort) { + auto i = v.first; + SASSERT(0 <= i && i < sz); + if (!m_col_names.get(i) || m_col_names.get(i)->get_sort() != sort) { // create a fresh skolem constant for the jth variable // reuse variables if they are already here and have matching sort - m_col_names[j] = m.mk_fresh_const("mrg_cvx!!", sort); + m_col_names[i] = m.mk_fresh_const("mrg_cvx!!", sort); } } @@ -210,10 +210,13 @@ void lemma_global_generalizer::subsumer::setup_cvx_closure( is_first = false; } + unsigned i; for (unsigned j = 0; j < n_vars; j++) { - sub.get_binding(n_vars - 1 - j, v, r); + sub.get_binding(j, v, r); + i = v.first; + SASSERT(0 <= i && i < n_vars); if (is_numeral(r.get_expr(), num)) { - m_col_lcm[j] = lcm(m_col_lcm.get(j), abs(denominator(num))); + m_col_lcm[i] = lcm(m_col_lcm.get(i), abs(denominator(num))); } } } @@ -229,14 +232,17 @@ void lemma_global_generalizer::subsumer::setup_cvx_closure( cc.set_col_var(j, mk_rat_mul(m_col_lcm.get(j), m_col_names.get(j))); vector row; + unsigned i; for (const auto &lemma : lemmas) { row.reset(); + row.reserve(n_vars); const substitution &sub = lemma.get_sub(); for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) { - sub.get_binding(sz - 1 - j, v, r); + sub.get_binding(j, v, r); + i = v.first; VERIFY(is_numeral(r.get_expr(), num)); - row.push_back(m_col_lcm.get(j) * num); + row[i] = m_col_lcm.get(i) * num; } // -- add normalized row to convex closure From 6e7d80633da849da13633ca000a05e1573ad509a Mon Sep 17 00:00:00 2001 From: Julian Parsert Date: Tue, 28 Feb 2023 19:44:21 +0000 Subject: [PATCH 444/597] Documentation on how to add z3 to CMake project using FetchContent and documentation to recdef function. (#6613) * Added overloaded versions of context::recfun in the c++ api that allow for the declaration of recursive functions where the domain is given by a z3::sort_vector instead of an arity and sort* * added documentation to recdef function * added a section in the README-CMake.md that explains how z3 can be added to a CMake project as a dependency --------- Co-authored-by: Julian Parsert --- README-CMake.md | 31 +++++++++++++++++++++++++++++++ src/api/c++/z3++.h | 8 +++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/README-CMake.md b/README-CMake.md index 7b7381107f2..5845a52c372 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -90,6 +90,37 @@ CFLAGS="-m32" CXXFLAGS="-m32" CC=gcc CXX=g++ cmake ../ Note like with the ``CC`` and ``CXX`` flags this must be done on the very first invocation to CMake in the build directory. +### Adding Z3 as a dependency to a CMAKE Project + +CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) allows +the fetching and populating of an external project. This is useful when a certain version +of z3 is required that may not match with the system version. With the following code in the +cmake file of your project, z3 version 4.12.1 is downloaded to the build directory and the +cmake targets are added to the project: + +``` +FetchContent_Declare(z3 + GIT_REPOSITORY https://github.com/Z3Prover/z3 + GIT_TAG z3-4.12.1 +) +FetchContent_MakeAvailable(z3) +``` + +The header files can be added to the included directories as follows: + +``` +include_directories( ${z3_SOURCE_DIR}/src/api ) +``` + +Finally, the z3 library can be linked to a `yourTarget` using + +``` +target_link_libraries(yourTarget libz3) +``` +Note that this is `libz3` not `z3` (`libz3` refers to the library target from `src/CMakeLists.txt`). + + + ### Ninja [Ninja](https://ninja-build.org/) is a simple build system that is built for speed. diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index f2c0f3f243e..52d5e657394 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -366,7 +366,13 @@ namespace z3 { func_decl recfun(char const * name, sort const & domain, sort const & range); func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); - void recdef(func_decl, expr_vector const& args, expr const& body); + /** + * \brief add function definition body to declaration decl. decl needs to be declared using context::. + * @param decl + * @param args + * @param body + */ + void recdef(func_decl decl, expr_vector const& args, expr const& body); func_decl user_propagate_function(symbol const& name, sort_vector const& domain, sort const& range); /** From 1a9990a92f5986a7ed8baf8eca3f211b1e482558 Mon Sep 17 00:00:00 2001 From: Kevin Phoenix Date: Tue, 28 Feb 2023 12:46:10 -0700 Subject: [PATCH 445/597] Use sys.getdefaultencoding() instead of sys.stdout.encoding (#6612) --- doc/mk_params_doc.py | 4 ++-- doc/mk_tactic_doc.py | 2 +- scripts/mk_util.py | 2 +- scripts/update_api.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/mk_params_doc.py b/doc/mk_params_doc.py index de527641649..021cab3c35e 100644 --- a/doc/mk_params_doc.py +++ b/doc/mk_params_doc.py @@ -37,7 +37,7 @@ def help(ous): out = subprocess.Popen([z3_exe, "-pm"],stdout=subprocess.PIPE).communicate()[0] modules = ["global"] if out != None: - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) module_re = re.compile(r"\[module\] (.*)\,") lines = out.split("\n") for line in lines: @@ -48,7 +48,7 @@ def help(ous): out = subprocess.Popen([z3_exe, "-pmmd:%s" % module],stdout=subprocess.PIPE).communicate()[0] if out == None: continue - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) out = out.replace("\r","") ous.write(out) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index a22201e2712..804df2f7e19 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -28,7 +28,7 @@ def extract_params(ous, tac): out = subprocess.Popen([z3_exe, f"-tacticsmd:{tac}"], stdout=subprocess.PIPE).communicate()[0] if not out: return - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) if is_ws(out): return ous.write("### Parameters\n\n") diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 635b8cec1e9..805aea19df3 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -122,7 +122,7 @@ def getenv(name, default): def check_output(cmd): out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] if out != None: - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() if enc != None: return out.decode(enc).rstrip('\r\n') else: return out.rstrip('\r\n') else: diff --git a/scripts/update_api.py b/scripts/update_api.py index 62b961d67aa..a3d92a7e94b 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1836,14 +1836,14 @@ def _to_pystr(s): else: def _str_to_bytes(s): if isinstance(s, str): - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() return s.encode(enc if enc != None else 'latin-1') else: return s def _to_pystr(s): if s != None: - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() return s.decode(enc if enc != None else 'latin-1') else: return "" From 5974a2dc58221e1037919512bc83880e86c3596e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:35:54 -0800 Subject: [PATCH 446/597] remove m_b from lar_core_solver the column vector is pure overhead for the way the lar solver uses lp. Some other solver modules use column vectors b and integrate with the lp_core_solver_base. The interaction model should be reviewed. Unused solvers should be removed to make it easier to maintain this code. --- src/math/lp/lar_core_solver.h | 7 +- src/math/lp/lar_core_solver_def.h | 30 +++----- src/math/lp/lar_solver.cpp | 10 +-- src/math/lp/lp_core_solver_base.cpp | 10 ++- src/math/lp/lp_core_solver_base.h | 11 +-- src/math/lp/lp_core_solver_base_def.h | 104 +++++++++----------------- src/math/lp/lp_dual_core_solver.h | 2 +- src/math/lp/lp_primal_core_solver.h | 4 +- 8 files changed, 66 insertions(+), 112 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index bcd33966f90..8a6c64ef00f 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -66,6 +66,7 @@ class lar_core_solver { ); lp_settings & settings() { return m_r_solver.m_settings;} + const lp_settings & settings() const { return m_r_solver.m_settings;} int get_infeasible_sum_sign() const { return m_infeasible_sum_sign; } @@ -340,6 +341,7 @@ class lar_core_solver { switch (m_column_types[j]) { case column_type::free_column: lp_assert(false); // unreachable + break; case column_type::upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; @@ -365,7 +367,7 @@ class lar_core_solver { } } - lp_assert(is_zero_vector(s.m_b)); + // lp_assert(is_zero_vector(s.m_b)); s.solve_Ax_eq_b(); } @@ -463,7 +465,8 @@ class lar_core_solver { m_d_nbasis = m_r_nbasis; delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; - } else { + } + else { prepare_solver_x_with_signature_tableau(signature); m_r_solver.start_tracing_basis_changes(); m_r_solver.find_feasible_solution(); diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 939a0511461..75fff64fd71 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -46,14 +46,10 @@ lar_core_solver::lar_core_solver( column_names) { } - - void lar_core_solver::calculate_pivot_row(unsigned i) { m_r_solver.calculate_pivot_row(i); } - - void lar_core_solver::prefix_r() { if (!m_r_solver.m_settings.use_tableau()) { m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n()); @@ -67,7 +63,7 @@ void lar_core_solver::prefix_r() { init_column_row_nz_for_r_solver(); } - m_r_solver.m_b.resize(m_r_solver.m_m()); + // m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n()); @@ -78,7 +74,7 @@ void lar_core_solver::prefix_r() { } void lar_core_solver::prefix_d() { - m_d_solver.m_b.resize(m_d_solver.m_m()); + // m_d_solver.m_b.resize(m_d_solver.m_m()); m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n()); m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); m_d_solver.m_costs.resize(m_d_solver.m_n()); @@ -100,9 +96,8 @@ void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); - for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) { - m_infeasible_linear_combination.push_back(std::make_pair(rc.coeff(), rc.var())); - } + for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) + m_infeasible_linear_combination.push_back(std::make_pair(rc.coeff(), rc.var())); } void lar_core_solver::fill_not_improvable_zero_sum() { @@ -115,26 +110,23 @@ void lar_core_solver::fill_not_improvable_zero_sum() { m_infeasible_linear_combination.clear(); for (auto j : m_r_solver.m_basis) { const mpq & cost_j = m_r_solver.m_costs[j]; - if (!numeric_traits::is_zero(cost_j)) { - m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); - } + if (!numeric_traits::is_zero(cost_j)) + m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); } // m_costs are expressed by m_d ( additional costs), substructing the latter gives 0 for (unsigned j = 0; j < m_r_solver.m_n(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; const mpq & d_j = m_r_solver.m_d[j]; - if (!numeric_traits::is_zero(d_j)) { - m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); - } + if (!numeric_traits::is_zero(d_j)) + m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); } } unsigned lar_core_solver::get_number_of_non_ints() const { unsigned n = 0; - for (auto & x : m_r_solver.m_x) { - if (x.is_int() == false) - n++; - } + for (auto & x : m_r_solver.m_x) + if (!x.is_int()) + n++; return n; } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 46ed0b5a93b..86ecb02b964 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -8,8 +8,8 @@ namespace lp { - ////////////////// methods //////////////////////////////// static_matrix& lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A; } + static_matrix const& lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A; } lp_settings& lar_solver::settings() { return m_settings; } @@ -18,7 +18,6 @@ namespace lp { statistics& lar_solver::stats() { return m_settings.stats(); } - void lar_solver::updt_params(params_ref const& _p) { smt_params_helper p(_p); set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows()); @@ -42,7 +41,6 @@ namespace lp { } lar_solver::~lar_solver() { - for (auto t : m_terms) delete t; } @@ -1406,7 +1404,6 @@ namespace lp { A_r().m_rows.pop_back(); A_r().m_columns.pop_back(); CASSERT("check_static_matrix", A_r().is_correct()); - slv.m_b.pop_back(); } void lar_solver::remove_last_column_from_A() { @@ -1716,8 +1713,6 @@ namespace lp { m_terms.push_back(t); } - - // terms bool lar_solver::all_vars_are_registered(const vector>& coeffs) { for (const auto& p : coeffs) { @@ -1787,8 +1782,7 @@ namespace lp { if (use_tableau()) { A_r().fill_last_row_with_pivoting(*term, j, - m_mpq_lar_core_solver.m_r_solver.m_basis_heading); - m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); + m_mpq_lar_core_solver.m_r_solver.m_basis_heading); } else { fill_last_row_of_A_r(A_r(), term); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 83da68d9d3e..c8b1692d272 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -36,8 +36,8 @@ template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base::lp_core_solver_base( - lp::static_matrix&, vector&, - vector&, + lp::static_matrix&, // vector&, + vector&, vector &, vector &, vector&, vector&, @@ -80,7 +80,9 @@ template void lp::lp_core_solver_base >::calc template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); template void lp::lp_core_solver_base >::init_reduced_costs_for_one_iteration(); -template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, vector >&, vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, +template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, + // vector >&, + vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); @@ -91,7 +93,7 @@ template bool lp::lp_core_solver_base >::upda template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, - vector&, + //vector&, vector&, vector &, vector &, vector&, diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 5cde4485d00..b7010aa54a8 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -67,7 +67,7 @@ class lp_core_solver_base { indexed_vector m_pivot_row_of_B_1; // the pivot row of the reverse of B indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A - vector & m_b; // the right side + // vector const & m_b; // the right side vector & m_basis; vector& m_nbasis; vector& m_basis_heading; @@ -118,7 +118,7 @@ class lp_core_solver_base { unsigned m_n() const { return m_A.column_count(); } // the number of columns in the matrix m_A lp_core_solver_base(static_matrix & A, - vector & b, // the right side vector + //vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, @@ -213,7 +213,6 @@ class lp_core_solver_base { return !below_bound(x, bound) && !above_bound(x, bound); } - bool need_to_pivot_to_basis_tableau() const { unsigned m = m_A.row_count(); for (unsigned i = 0; i < m; i++) { @@ -284,7 +283,6 @@ class lp_core_solver_base { return below_bound(m_x[p], m_upper_bounds[p]); } - bool x_above_upper_bound(unsigned p) const { return above_bound(m_x[p], m_upper_bounds[p]); } @@ -310,7 +308,6 @@ class lp_core_solver_base { bool d_is_not_positive(unsigned j) const; - bool time_is_over(); void rs_minus_Anx(vector & rs); @@ -351,8 +348,6 @@ class lp_core_solver_base { std::string column_name(unsigned column) const; - void copy_right_side(vector & rs); - void add_delta_to_xB(vector & del); void find_error_in_BxB(vector& rs); @@ -366,8 +361,6 @@ class lp_core_solver_base { ret = snap_column_to_bound(j) || ret; return ret; } - - bool snap_column_to_bound(unsigned j) { switch (m_column_types[j]) { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index f85de111128..4d29234a815 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -28,7 +28,7 @@ namespace lp { template lp_core_solver_base:: lp_core_solver_base(static_matrix & A, - vector & b, // the right side vector + // vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, @@ -47,7 +47,7 @@ lp_core_solver_base(static_matrix & A, m_pivot_row_of_B_1(A.row_count()), m_pivot_row(A.column_count()), m_A(A), - m_b(b), + // m_b(b), m_basis(basis), m_nbasis(nbasis), m_basis_heading(heading), @@ -220,7 +220,7 @@ A_mult_x_is_off() const { lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) { for (unsigned i = 0; i < m_m(); i++) { - X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); + X delta = /*m_b[i] */- m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { return true; } @@ -230,8 +230,8 @@ A_mult_x_is_off() const { T feps = convert_struct::convert(m_settings.refactor_tolerance); X one = convert_struct::convert(1.0); for (unsigned i = 0; i < m_m(); i++) { - X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); - X eps = feps * (one + T(0.1) * abs(m_b[i])); + X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); + auto eps = feps /* * (one + T(0.1) * abs(m_b[i])) */; if (delta > eps) { #if 0 @@ -263,8 +263,8 @@ A_mult_x_is_off_on_index(const vector & index) const { T feps = convert_struct::convert(m_settings.refactor_tolerance); X one = convert_struct::convert(1.0); for (unsigned i : index) { - X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); - X eps = feps * (one + T(0.1) * abs(m_b[i])); + X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); + auto eps = feps /* *(one + T(0.1) * abs(m_b[i])) */; if (delta > eps) { #if 0 @@ -400,7 +400,8 @@ column_is_dual_feasible(unsigned j) const { case column_type::lower_bound: return x_is_at_lower_bound(j) && d_is_not_negative(j); case column_type::upper_bound: - lp_assert(false); // impossible case + UNREACHABLE(); + break; case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: @@ -441,7 +442,7 @@ template void lp_core_solver_base:: rs_minus_Anx(vector & rs) { unsigned row = m_m(); while (row--) { - auto &rsv = rs[row] = m_b[row]; + auto& rsv = rs[row] = zero_of_type(); //m_b[row]; for (auto & it : m_A.m_rows[row]) { unsigned j = it.var(); if (m_basis_heading[j] < 0) { @@ -454,8 +455,7 @@ rs_minus_Anx(vector & rs) { template bool lp_core_solver_base:: find_x_by_solving() { solve_Ax_eq_b(); - bool ret= !A_mult_x_is_off(); - return ret; + return !A_mult_x_is_off(); } template bool lp_core_solver_base::column_is_feasible(unsigned j) const { @@ -463,28 +463,12 @@ template bool lp_core_solver_base::column_is_feas switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: - if (this->above_bound(x, this->m_upper_bounds[j])) { - return false; - } else if (this->below_bound(x, this->m_lower_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->above_bound(x, this->m_upper_bounds[j]) && + !this->below_bound(x, this->m_lower_bounds[j]); case column_type::lower_bound: - if (this->below_bound(x, this->m_lower_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->below_bound(x, this->m_lower_bounds[j]); case column_type::upper_bound: - if (this->above_bound(x, this->m_upper_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->above_bound(x, this->m_upper_bounds[j]); case column_type::free_column: return true; break; @@ -598,7 +582,7 @@ divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { if (is_zero(coeff)) return false; - this->m_b[pivot_row] /= coeff; + // this->m_b[pivot_row] /= coeff; for (unsigned j = 0; j < size; j++) { auto & c = row[j]; if (c.var() != pivot_col) { @@ -662,59 +646,52 @@ basis_has_no_doubles() const { template bool lp_core_solver_base:: non_basis_has_no_doubles() const { std::set bm; - for (auto j : m_nbasis) { - bm.insert(j); - } + for (auto j : m_nbasis) + bm.insert(j); return bm.size() == m_nbasis.size(); } template bool lp_core_solver_base:: basis_is_correctly_represented_in_heading() const { - for (unsigned i = 0; i < m_m(); i++) { + for (unsigned i = 0; i < m_m(); i++) if (m_basis_heading[m_basis[i]] != static_cast(i)) - return false; - } + return false; return true; } template bool lp_core_solver_base:: non_basis_is_correctly_represented_in_heading() const { - for (unsigned i = 0; i < m_nbasis.size(); i++) { + for (unsigned i = 0; i < m_nbasis.size(); i++) if (m_basis_heading[m_nbasis[i]] != - static_cast(i) - 1) return false; - } - for (unsigned j = 0; j < m_A.column_count(); j++) { - if (m_basis_heading[j] >= 0) { + + for (unsigned j = 0; j < m_A.column_count(); j++) + if (m_basis_heading[j] >= 0) lp_assert(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); - } - } + return true; } template bool lp_core_solver_base:: basis_heading_is_correct() const { - if ( m_A.column_count() > 10 ) { // for the performance reason + if ( m_A.column_count() > 10 ) // for the performance reason return true; - } + lp_assert(m_basis_heading.size() == m_A.column_count()); lp_assert(m_basis.size() == m_A.row_count()); lp_assert(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller - if (!basis_has_no_doubles()) { - return false; - } - if (!non_basis_has_no_doubles()) { + if (!basis_has_no_doubles()) return false; - } - - if (!basis_is_correctly_represented_in_heading()) { + + if (!non_basis_has_no_doubles()) return false; - } + + if (!basis_is_correctly_represented_in_heading()) + return false; - if (!non_basis_is_correctly_represented_in_heading()) { + if (!non_basis_is_correctly_represented_in_heading()) return false; - } - - + return true; } @@ -781,14 +758,6 @@ column_name(unsigned column) const { return m_column_names.get_variable_name(column); } -template void lp_core_solver_base:: -copy_right_side(vector & rs) { - unsigned i = m_m(); - while (i --) { - rs[i] = m_b[i]; - } -} - template void lp_core_solver_base:: add_delta_to_xB(vector & del) { unsigned i = m_m(); @@ -819,7 +788,8 @@ solve_Ax_eq_b() { rs_minus_Anx(rs); m_factorization->solve_By(rs); copy_rs_to_xB(rs); - } else { + } + else { vector rs(m_m()); rs_minus_Anx(rs); vector rrs = rs; // another copy of rs diff --git a/src/math/lp/lp_dual_core_solver.h b/src/math/lp/lp_dual_core_solver.h index f4aa4b44d02..804879c3da2 100644 --- a/src/math/lp/lp_dual_core_solver.h +++ b/src/math/lp/lp_dual_core_solver.h @@ -61,7 +61,7 @@ class lp_dual_core_solver:public lp_core_solver_base { lp_settings & settings, const column_namer & column_names): lp_core_solver_base(A, - b, + // b, basis, nbasis, heading, diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 3abf9dbc055..4b6163df810 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -948,7 +948,7 @@ class lp_primal_core_solver:public lp_core_solver_base { const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): - lp_core_solver_base(A, b, + lp_core_solver_base(A, // b, basis, nbasis, heading, @@ -983,7 +983,7 @@ class lp_primal_core_solver:public lp_core_solver_base { const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): - lp_core_solver_base(A, b, + lp_core_solver_base(A, // b, basis, nbasis, heading, From 76aad689c65383f207684510afda9ca5b54c999a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:38:36 -0800 Subject: [PATCH 447/597] Update smt_context_pp.cpp print units in statistics --- src/smt/smt_context_pp.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 40e789204f8..a6088fdf7df 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -704,14 +704,20 @@ namespace smt { for (clause* cp : m_lemmas) if (cp->get_num_literals() == 2) ++bin_lemmas; + auto num_units = [&]() { + if (m_scopes.empty()) + return m_assigned_literals.size(); + else + return m_scopes[0].m_assigned_literals_lim; + }; std::stringstream strm; strm << "(smt.stats " << std::setw(4) << m_stats.m_num_restarts << " " << std::setw(6) << m_stats.m_num_conflicts << " " << std::setw(6) << m_stats.m_num_decisions << " " << std::setw(6) << m_stats.m_num_propagations << " " - << std::setw(5) << (m_aux_clauses.size() + bin_clauses) << "/" << bin_clauses << " " - << std::setw(5) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " "; + << std::setw(5) << (m_aux_clauses.size() + bin_clauses) << "/" << bin_clauses << "/" << num_units() + << std::setw(7) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " "; strm << std::setw(5) << m_stats.m_num_simplifications << " " << std::setw(4) << m_stats.m_num_del_clauses << " " << std::setw(7) << mem_stat() << ")\n"; @@ -739,8 +745,8 @@ namespace smt { m_last_position_log = m_stats.m_num_restarts; // restarts decisions clauses simplifications memory // conflicts propagations lemmas deletions - int adjust[9] = { -3, -3, -3, -3, -3, -3, -4, -4, -1 }; - char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin ", ":lemmas ", ":simplify ", ":deletions", ":memory" }; + int adjust[9] = { -3, -3, -3, -3, -3, -4, -4, -4, -1 }; + char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin/units ", ":lemmas ", ":simplify ", ":deletions", ":memory" }; std::stringstream l1, l2; l1 << "(smt.stats "; From 79d47eb3029e01635ac12d71bd47a06bfc67e330 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:39:00 -0800 Subject: [PATCH 448/597] add preprocessor parameter whether to use bound simplifier --- src/smt/params/preprocessor_params.cpp | 2 ++ src/smt/params/preprocessor_params.h | 1 + src/smt/params/smt_params_helper.pyg | 1 + src/solver/solver_preprocess.cpp | 2 ++ 4 files changed, 6 insertions(+) diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 180242f858a..b6b80a34e33 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -30,6 +30,7 @@ void preprocessor_params::updt_local_params(params_ref const & _p) { m_elim_unconstrained = p.elim_unconstrained(); m_solve_eqs = p.solve_eqs(); m_ng_lift_ite = static_cast(p.q_lift_ite()); + m_bound_simplifier = p.bound_simplifier(); } void preprocessor_params::updt_params(params_ref const & p) { @@ -63,4 +64,5 @@ void preprocessor_params::display(std::ostream & out) const { DISPLAY_PARAM(m_max_bv_sharing); DISPLAY_PARAM(m_pre_simplifier); DISPLAY_PARAM(m_nlquant_elim); + DISPLAY_PARAM(m_bound_simplifier); } diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h index 55a55980b2b..d53cbbe8f65 100644 --- a/src/smt/params/preprocessor_params.h +++ b/src/smt/params/preprocessor_params.h @@ -49,6 +49,7 @@ struct preprocessor_params : public pattern_inference_params, bool m_max_bv_sharing = true; bool m_pre_simplifier = true; bool m_nlquant_elim = false; + bool m_bound_simplifier = true; public: preprocessor_params(params_ref const & p = params_ref()): diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 86a0371f24e..4b4a453c8a3 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -21,6 +21,7 @@ def_module_params(module_name='smt', ('elim_unconstrained', BOOL, True, 'pre-processing: eliminate unconstrained subterms'), ('solve_eqs', BOOL, True, 'pre-processing: solve equalities'), ('propagate_values', BOOL, True, 'pre-processing: propagate values'), + ('bound_simplifier', BOOL, True, 'apply bounds simplification during pre-processing'), ('pull_nested_quantifiers', BOOL, False, 'pre-processing: pull nested quantifiers'), ('refine_inj_axioms', BOOL, True, 'pre-processing: refine injectivity axioms'), ('candidate_models', BOOL, False, 'create candidate models even when quantifier or theory reasoning is incomplete'), diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp index 38b0584b52b..7fd9d1dba3c 100644 --- a/src/solver/solver_preprocess.cpp +++ b/src/solver/solver_preprocess.cpp @@ -39,6 +39,7 @@ Module Name: #include "ast/simplifiers/push_ite.h" #include "ast/simplifiers/elim_term_ite.h" #include "ast/simplifiers/flatten_clauses.h" +#include "ast/simplifiers/bound_simplifier.h" #include "ast/simplifiers/cnf_nnf.h" #include "smt/params/smt_params.h" #include "solver/solver_preprocess.h" @@ -59,6 +60,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); if (smtp.m_bv_size_reduce) s.add_simplifier(alloc(bv::slice, m, st)); if (smtp.m_distribute_forall) s.add_simplifier(alloc(distribute_forall_simplifier, m, p, st)); + if (smtp.m_bound_simplifier) s.add_simplifier(alloc(bound_simplifier, m, p, st)); if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); From e87fa1c299b9a24503eba580281d154a41ff46d1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:39:30 -0800 Subject: [PATCH 449/597] remove stale file --- src/tactic/arith/bound_simplifier_tactic.h | 42 ---------------------- 1 file changed, 42 deletions(-) delete mode 100644 src/tactic/arith/bound_simplifier_tactic.h diff --git a/src/tactic/arith/bound_simplifier_tactic.h b/src/tactic/arith/bound_simplifier_tactic.h deleted file mode 100644 index b61a100048d..00000000000 --- a/src/tactic/arith/bound_simplifier_tactic.h +++ /dev/null @@ -1,42 +0,0 @@ -/*++ -Copyright (c) 2023 Microsoft Corporation - -Module Name: - - bound_simplifier_tactic.h - -Author: - - Nikolaj Bjorner (nbjorner) 2023-01-22 - -Tactic Documentation: - -## Tactic bound-simplifier - -### Short Description - -Tactic for simplifying arithmetical expressions modulo bounds - -### Long Description - -The tactic is used to eliminate occurrences of modulus expressions when it is known that terms are within the bounds -of the modulus. - - ---*/ -#pragma once - -#include "util/params.h" -#include "tactic/tactic.h" -#include "tactic/dependent_expr_state_tactic.h" -#include "ast/simplifiers/bound_simplifier.h" - -inline tactic* mk_bound_simplifier_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, - [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }); -} - -/* - ADD_TACTIC("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "mk_bound_simplifier_tactic(m, p)") - ADD_SIMPLIFIER("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "alloc(bound_simplifier, m, p, s)") -*/ From 25d45a3500cac1291198b00da0f83b147ef08914 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:40:00 -0800 Subject: [PATCH 450/597] fixes and tests for arith-sls --- src/sat/sat_ddfw.cpp | 28 +++--- src/sat/sat_ddfw.h | 5 +- src/sat/sat_solver.cpp | 48 ++++------- src/sat/sat_solver.h | 19 +--- src/sat/sat_types.h | 34 ++++++++ src/sat/smt/arith_sls.cpp | 177 ++++++++++++++++++++++---------------- src/sat/smt/arith_sls.h | 7 +- 7 files changed, 182 insertions(+), 136 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 5d80e5af4d1..ca274be51ca 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -62,13 +62,16 @@ namespace sat { void ddfw::check_with_plugin() { m_plugin->init_search(); m_steps_since_progress = 0; - while (m_min_sz > 0 && m_steps_since_progress++ <= 150000) { + unsigned steps = 0; + while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) { if (should_reinit_weights()) do_reinit_weights(); + else if (steps % 5000 == 0) shift_weights(), m_plugin->on_rescale(); + else if (should_restart()) do_restart(), m_plugin->on_restart(); else if (do_flip()); else if (do_literal_flip()); - else if (should_restart()) do_restart(), m_plugin->on_restart(); else if (should_parallel_sync()) do_parallel_sync(); else shift_weights(), m_plugin->on_rescale(); + ++steps; } m_plugin->finish_search(); } @@ -135,7 +138,7 @@ namespace sat { if (sum_pos > 0) { double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos; for (bool_var v : m_unsat_vars) { - r = uses_plugin ? plugin_reward(v) : reward(v); + r = uses_plugin && is_external(v) ? m_vars[v].m_last_reward : reward(v); if (r > 0) { lim_pos -= score(r); if (lim_pos <= 0) @@ -472,9 +475,7 @@ namespace sat { void ddfw::save_best_values() { - if (m_unsat.empty()) - save_model(); - else if (m_unsat.size() < m_min_sz) { + if (m_unsat.size() < m_min_sz) { m_steps_since_progress = 0; if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11) save_model(); @@ -489,13 +490,20 @@ namespace sat { } } } + unsigned h = value_hash(); + unsigned occs = 0; + bool contains = m_models.find(h, occs); if (!m_models.contains(h)) { - for (unsigned v = 0; v < num_vars(); ++v) + for (unsigned v = 0; v < num_vars(); ++v) bias(v) += value(v) ? 1 : -1; - m_models.insert(h); - if (m_models.size() > m_config.m_max_num_models) - m_models.erase(*m_models.begin()); + if (m_models.size() > m_config.m_max_num_models) + m_models.erase(m_models.begin()->m_key); + } + m_models.insert(h, occs + 1); + if (occs > 100) { + m_restart_next = m_flips; + m_models.erase(h); } m_min_sz = m_unsat.size(); } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 8c4f9287fcf..9883652859c 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -98,6 +98,7 @@ namespace sat { var_info() {} bool m_value = false; double m_reward = 0; + double m_last_reward = 0; unsigned m_make_count = 0; int m_bias = 0; bool m_external = false; @@ -127,7 +128,7 @@ namespace sat { uint64_t m_restart_next = 0, m_reinit_next = 0, m_parsync_next = 0; uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0; unsigned m_min_sz = 0, m_steps_since_progress = 0; - hashtable> m_models; + u_map m_models; stopwatch m_stopwatch; parallel* m_par; @@ -153,7 +154,7 @@ namespace sat { inline double reward(bool_var v) const { return m_vars[v].m_reward; } - inline double plugin_reward(bool_var v) const { return is_external(v) ? m_plugin->reward(v) : reward(v); } + inline double plugin_reward(bool_var v) { return is_external(v) ? (m_vars[v].m_last_reward = m_plugin->reward(v)) : reward(v); } void set_external(bool_var v) { m_vars[v].m_external = true; } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index d8d68d26208..5c1ed6daeab 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -40,26 +40,6 @@ Revision History: namespace sat { - /** - * Special cases of kissat style general backoff calculation. - * The version here calculates - * limit := value*log(C)^2*n*log(n) - * (effort calculation in kissat is based on ticks not clauses) - * - * respectively - * limit := conflicts + value*log(C)^2*n*log(n) - */ - void backoff::delta_effort(solver& s) { - count++; - unsigned d = value * count * log2(count + 1); - unsigned cl = log2(s.num_clauses() + 2); - limit = cl * cl * d; - } - - void backoff::delta_conflicts(solver& s) { - delta_effort(s); - limit += s.m_conflicts_since_init; - } solver::solver(params_ref const & p, reslimit& l): solver_core(l), @@ -1302,10 +1282,9 @@ namespace sat { return l_undef; } - if (false && m_config.m_phase == PS_LOCAL_SEARCH && m_ext) { - IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n"); + if (m_config.m_phase == PS_LOCAL_SEARCH && m_ext) { bounded_local_search(); - exit(0); + // exit(0); } log_stats(); @@ -1367,7 +1346,7 @@ namespace sat { void solver::bounded_local_search() { if (m_ext) { - verbose_stream() << "bounded local search\n"; + IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n"); do_restart(true); lbool r = m_ext->local_search(m_best_phase); verbose_stream() << r << "\n"; @@ -1388,8 +1367,8 @@ namespace sat { m_local_search->set_seed(m_rand()); scoped_rl.push_child(&(m_local_search->rlimit())); - m_backoffs.m_local_search.delta_effort(*this); - m_local_search->rlimit().push(m_backoffs.m_local_search.limit); + m_local_search_lim.inc(num_clauses()); + m_local_search->rlimit().push(m_local_search_lim.limit); m_local_search->reinit(*this, m_best_phase); lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); @@ -1977,11 +1956,13 @@ namespace sat { m_search_sat_conflicts = m_config.m_search_sat_conflicts; m_search_next_toggle = m_search_unsat_conflicts; m_best_phase_size = 0; + + m_reorder.lo = m_config.m_reorder_base; + m_rephase.base = m_config.m_rephase_base; m_rephase_lim = 0; m_rephase_inc = 0; - m_reorder_lim = m_config.m_reorder_base; - m_backoffs.m_local_search.value = 500; - m_reorder_inc = 0; + m_local_search_lim.base = 500; + m_conflicts_since_restart = 0; m_force_conflict_analysis = false; m_restart_threshold = m_config.m_restart_initial; @@ -2981,6 +2962,7 @@ namespace sat { bool solver::should_rephase() { return m_conflicts_since_init > m_rephase_lim; +// return m_rephase.should_apply(m_conflicts_since_init); } void solver::do_rephase() { @@ -2994,7 +2976,7 @@ namespace sat { case PS_FROZEN: break; case PS_BASIC_CACHING: - switch (m_rephase_lim % 4) { + switch (m_rephase.count % 4) { case 0: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; @@ -3031,10 +3013,11 @@ namespace sat { } m_rephase_inc += m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; + m_rephase.inc(m_conflicts_since_init, num_clauses()); } bool solver::should_reorder() { - return m_conflicts_since_init > m_reorder_lim; + return m_reorder.should_apply(m_conflicts_since_init); } void solver::do_reorder() { @@ -3078,8 +3061,7 @@ namespace sat { update_activity(v, m_rand(10)/10.0); } #endif - m_reorder_inc += m_config.m_reorder_base; - m_reorder_lim += m_reorder_inc; + m_reorder.inc(m_conflicts_since_init, num_clauses()); } void solver::updt_phase_counters() { diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 703b36dd069..3a437855e9d 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -87,23 +87,10 @@ namespace sat { struct no_drat_params : public params_ref { no_drat_params() { set_bool("drat.disable", true); } }; - - struct backoff { - unsigned value = 1; - unsigned lo = 0; - unsigned hi = 0; - unsigned limit = 0; - unsigned count = 0; - void delta_effort(solver& s); - void delta_conflicts(solver& s); - }; class solver : public solver_core { public: struct abort_solver {}; - struct backoffs { - backoff m_local_search; - }; protected: enum search_state { s_sat, s_unsat }; @@ -172,11 +159,11 @@ namespace sat { unsigned m_search_next_toggle; unsigned m_phase_counter; unsigned m_best_phase_size; - backoffs m_backoffs; + backoff m_local_search_lim; unsigned m_rephase_lim; unsigned m_rephase_inc; - unsigned m_reorder_lim; - unsigned m_reorder_inc; + backoff m_rephase; + backoff m_reorder; var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index d5d457cb0f8..427b6fb70f9 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -136,6 +136,40 @@ namespace sat { std::ostream& operator<<(std::ostream& out, sat::status const& st); std::ostream& operator<<(std::ostream& out, sat::status_pp const& p); + /** + * Special cases of kissat style general backoff calculation. + * The version here calculates + * limit := value*log(C)^2*n*log(n) + * (effort calculation in kissat is based on ticks not clauses) + * + * respectively + * limit := conflicts + value*log(C)^2*n*log(n) + */ + struct backoff { + unsigned base = 1; + unsigned lo = 0; + unsigned hi = UINT_MAX; + unsigned limit = 0; + unsigned count = 0; + + bool should_apply(unsigned n) const { + return limit <= n && lo <= n && n <= hi; + } + + void inc(unsigned num_clauses) { + count++; + unsigned d = base * count * log2(count + 1); + unsigned cl = log2(num_clauses + 2); + limit = cl * cl * d; + } + + void inc(unsigned num_conflicts, unsigned num_clauses) { + inc(num_clauses); + limit += num_conflicts; + } + + }; + }; diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index b32358a3c25..4fe153289de 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -29,42 +29,49 @@ namespace arith { m_terms.reset(); } - void sls::log() { - IF_VERBOSE(2, verbose_stream() << "(sls :flips " << m_stats.m_num_flips << " :unsat " << unsat().size() << ")\n"); - } - void sls::save_best_values() { for (unsigned v = 0; v < s.get_num_vars(); ++v) m_vars[v].m_best_value = m_vars[v].m_value; - - auto check_bool_var = [&](sat::bool_var bv) { - auto const* ineq = atom(bv); - if (!ineq) - return; - sat::literal lit(bv, !m_bool_search->get_value(bv)); - int64_t d = dtt(lit.sign(), *ineq); - // verbose_stream() << "check " << lit << " " << *ineq << "\n"; - if (is_true(lit) != (d == 0)) { - verbose_stream() << lit << " " << *ineq << "\n"; + check_ineqs(); + if (unsat().size() == 1) { + auto idx = *unsat().begin(); + verbose_stream() << idx << "\n"; + auto const& c = *m_bool_search->m_clauses[idx].m_clause; + verbose_stream() << c << "\n"; + for (auto lit : c) { + bool_var bv = lit.var(); + ineq* i = atom(bv); + if (i) + verbose_stream() << lit << ": " << *i << "\n"; } - VERIFY(is_true(lit) == (d == 0)); - }; - for (unsigned v = 0; v < s.get_num_vars(); ++v) - check_bool_var(v); + verbose_stream() << "\n"; + } } void sls::store_best_values() { // first compute assignment to terms // then update non-basic variables in tableau. - for (auto const& [t, v] : m_terms) { + + if (!unsat().empty()) + return; + + for (auto const& [t,v] : m_terms) { int64_t val = 0; lp::lar_term const& term = s.lp().get_term(t); - for (lp::lar_term::ival arg : term) { + for (lp::lar_term::ival const& arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); val += to_numeral(arg.coeff()) * m_vars[w].m_best_value; } - update(v, val); + if (v == 52) { + verbose_stream() << "update v" << v << " := " << val << "\n"; + for (lp::lar_term::ival const& arg : term) { + auto t2 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + verbose_stream() << "v" << w << " := " << m_vars[w].m_best_value << " * " << to_numeral(arg.coeff()) << "\n"; + } + } + m_vars[v].m_best_value = val; } for (unsigned v = 0; v < s.get_num_vars(); ++v) { @@ -80,16 +87,15 @@ namespace arith { rational new_value_(new_value, rational::i64()); lp::impq val(new_value_, rational::zero()); s.lp().set_value_for_nbasic_column(vj.index(), val); - // TODO - figure out why this leads to unsound (unsat). } } lbool r = s.make_feasible(); VERIFY (!unsat().empty() || r == l_true); - if (unsat().empty()) { +#if 0 + if (unsat().empty()) s.m_num_conflicts = s.get_config().m_arith_propagation_threshold; - } - verbose_stream() << "has changed " << s.m_solver->has_changed_columns() << "\n"; +#endif auto check_bool_var = [&](sat::bool_var bv) { auto* ineq = m_bool_vars.get(bv, nullptr); @@ -105,10 +111,10 @@ namespace arith { return; switch (b->get_bound_kind()) { case lp_api::lower_t: - verbose_stream() << bv << " " << bound << " <= " << s.get_value(v) << "\n"; + verbose_stream() << "v" << v << " " << bound << " <= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n"; break; case lp_api::upper_t: - verbose_stream() << bv << " " << bound << " >= " << s.get_value(v) << "\n"; + verbose_stream() << "v" << v << " " << bound << " >= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n"; break; } int64_t value = 0; @@ -117,6 +123,12 @@ namespace arith { } ineq->m_args_value = value; verbose_stream() << *ineq << " dtt " << dtt(false, *ineq) << " phase " << s.get_phase(bv) << " model " << m_bool_search->get_model()[bv] << "\n"; + for (auto const& [coeff, v] : ineq->m_args) + verbose_stream() << "v" << v << " := " << m_vars[v].m_best_value << "\n"; + s.display(verbose_stream()); + display(verbose_stream()); + UNREACHABLE(); + exit(0); }; if (unsat().empty()) { @@ -200,16 +212,16 @@ namespace arith { return dtt(sign, ineq.m_args_value + coeff * (new_value - old_value), ineq); } - bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value) { + bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t& new_value) { for (auto const& [coeff, w] : ineq.m_args) if (w == v) - return cm(sign, ineq, v, coeff, new_value); + return cm(old_sign, ineq, v, coeff, new_value); return false; } - bool sls::cm(bool new_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { - SASSERT(ineq.is_true() == new_sign); - VERIFY(ineq.is_true() == new_sign); + bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { + SASSERT(ineq.is_true() != old_sign); + VERIFY(ineq.is_true() != old_sign); auto bound = ineq.m_bound; auto argsv = ineq.m_args_value; bool solved = false; @@ -239,7 +251,7 @@ namespace arith { return true; }; - if (new_sign) { + if (!old_sign) { switch (ineq.m_op) { case ineq_kind::LE: // args <= bound -> args > bound @@ -300,10 +312,10 @@ namespace arith { int64_t new_value; auto v = ineq.m_var_to_flip; if (v == UINT_MAX) { - // verbose_stream() << "no var to flip\n"; + IF_VERBOSE(1, verbose_stream() << "no var to flip\n"); return false; } - if (!cm(!sign, ineq, v, new_value)) { + if (!cm(sign, ineq, v, new_value)) { verbose_stream() << "no critical move for " << v << "\n"; return false; } @@ -316,16 +328,16 @@ namespace arith { // TODO - use cached dts instead of computed dts // cached dts has to be updated when the score of literals are updated. // - double sls::dscore(var_t v, int64_t new_value) const { - verbose_stream() << "dscore\n"; + double sls::dscore(var_t v, int64_t new_value) const { double score = 0; -#if 0 auto const& vi = m_vars[v]; - verbose_stream() << "dscore " << v << "\n"; - for (auto const& [coeff, lit] : vi.m_literals) - for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); -#endif + for (auto const& [coeff, bv] : vi.m_bool_vars) { + sat::literal lit(bv, false); + for (auto cl : m_bool_search->get_use_list(lit)) + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); + for (auto cl : m_bool_search->get_use_list(~lit)) + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); + } return score; } @@ -341,12 +353,12 @@ namespace arith { int64_t old_value = vi.m_value; for (auto const& [coeff, bv] : vi.m_bool_vars) { auto const& ineq = *atom(bv); - bool sign = !m_bool_search->value(bv); - int64_t dtt_old = dtt(sign, ineq); - int64_t dtt_new = dtt(sign, ineq, coeff, old_value, new_value); + bool old_sign = sign(bv); + int64_t dtt_old = dtt(old_sign, ineq); + int64_t dtt_new = dtt(old_sign, ineq, coeff, old_value, new_value); if ((dtt_old == 0) == (dtt_new == 0)) continue; - sat::literal lit(bv, sign); + sat::literal lit(bv, old_sign); if (dtt_old == 0) // flip from true to false lit.neg(); @@ -408,14 +420,14 @@ namespace arith { auto old_value = vi.m_value; for (auto const& [coeff, bv] : vi.m_bool_vars) { auto& ineq = *atom(bv); - bool sign = !m_bool_search->value(bv); - sat::literal lit(bv, sign); + bool old_sign = sign(bv); + sat::literal lit(bv, old_sign); SASSERT(is_true(lit)); ineq.m_args_value += coeff * (new_value - old_value); - int64_t dtt_new = dtt(sign, ineq); + int64_t dtt_new = dtt(old_sign, ineq); if (dtt_new != 0) m_bool_search->flip(bv); - SASSERT(dtt(!m_bool_search->value(bv), ineq) == 0); + SASSERT(dtt(sign(bv), ineq) == 0); } vi.m_value = new_value; } @@ -451,7 +463,7 @@ namespace arith { void sls::add_args(sat::bool_var bv, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); - + m_terms.push_back({t,v}); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); @@ -479,6 +491,7 @@ namespace arith { auto& ineq = new_ineq(op, to_numeral(bound)); + add_args(bv, ineq, t, b->get_var(), should_minus ? -1 : 1); m_bool_vars.set(bv, &ineq); m_bool_search->set_external(bv); @@ -516,7 +529,7 @@ namespace arith { } void sls::flip(sat::bool_var v) { - sat::literal lit(v, m_bool_search->get_value(v)); + sat::literal lit(v, !sign(v)); SASSERT(!is_true(lit)); auto const* ineq = atom(v); if (!ineq) @@ -524,7 +537,7 @@ namespace arith { if (!ineq) return; SASSERT(ineq->is_true() == lit.sign()); - flip(!lit.sign(), *ineq); + flip(sign(v), *ineq); } double sls::reward(sat::bool_var v) { @@ -535,21 +548,23 @@ namespace arith { } double sls::dtt_reward(sat::bool_var bv0) { - bool sign0 = !m_bool_search->get_value(bv0); + bool sign0 = sign(bv0); auto* ineq = atom(bv0); if (!ineq) return -1; int64_t new_value; double max_result = -1; for (auto const & [coeff, x] : ineq->m_args) { - if (!cm(!sign0, *ineq, x, coeff, new_value)) + if (!cm(sign0, *ineq, x, coeff, new_value)) continue; double result = 0; auto old_value = m_vars[x].m_value; for (auto const& [coeff, bv] : m_vars[x].m_bool_vars) { - bool sign = !m_bool_search->value(bv); - auto dtt_old = dtt(sign, *atom(bv)); - auto dtt_new = dtt(sign, *atom(bv), coeff, old_value, new_value); + result += m_bool_search->reward(bv); + continue; + bool old_sign = sign(bv); + auto dtt_old = dtt(old_sign, *atom(bv)); + auto dtt_new = dtt(old_sign, *atom(bv), coeff, old_value, new_value); if ((dtt_new == 0) != (dtt_old == 0)) result += m_bool_search->reward(bv); } @@ -563,17 +578,17 @@ namespace arith { double sls::dscore_reward(sat::bool_var bv) { m_dscore_mode = false; - bool sign = !m_bool_search->get_value(bv); - sat::literal litv(bv, sign); + bool old_sign = sign(bv); + sat::literal litv(bv, old_sign); auto* ineq = atom(bv); if (!ineq) return 0; - SASSERT(ineq->is_true() != sign); + SASSERT(ineq->is_true() != old_sign); int64_t new_value; for (auto const& [coeff, v] : ineq->m_args) { double result = 0; - if (cm(!sign, *ineq, v, coeff, new_value)) + if (cm(old_sign, *ineq, v, coeff, new_value)) result = dscore(v, new_value); // just pick first positive, or pick a max? if (result > 0) { @@ -586,7 +601,7 @@ namespace arith { // switch to dscore mode void sls::on_rescale() { - // m_dscore_mode = true; + m_dscore_mode = true; } void sls::on_save_model() { @@ -597,23 +612,39 @@ namespace arith { for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); - verbose_stream() << "on-restart\n"; + check_ineqs(); + } + + void sls::check_ineqs() { + auto check_bool_var = [&](sat::bool_var bv) { auto const* ineq = atom(bv); if (!ineq) return; - bool sign = !m_bool_search->get_value(bv); - int64_t d = dtt(sign, *ineq); - sat::literal lit(bv, sign); - // verbose_stream() << "check " << lit << " " << *ineq << "\n"; + int64_t d = dtt(sign(bv), *ineq); + sat::literal lit(bv, sign(bv)); if (is_true(lit) != (d == 0)) { - verbose_stream() << "restart " << bv << " " << *ineq << "\n"; + verbose_stream() << "invalid assignment " << bv << " " << *ineq << "\n"; } VERIFY(is_true(lit) == (d == 0)); }; - for (unsigned v = 0; v < s.get_num_vars(); ++v) + for (unsigned v = 0; v < s.get_num_vars(); ++v) check_bool_var(v); - - verbose_stream() << "on-restart-done\n"; } + + std::ostream& sls::display(std::ostream& out) const { + for (bool_var bv = 0; bv < s.s().num_vars(); ++bv) { + auto const* ineq = atom(bv); + if (!ineq) + continue; + out << bv << " " << *ineq << "\n"; + } + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + if (s.is_bool(v)) + continue; + out << "v" << v << " := " << m_vars[v].m_value << " " << m_vars[v].m_best_value << "\n"; + } + return out; + } + } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 3c9daaa5127..af3a462340b 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -119,12 +119,11 @@ namespace arith { sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); } + bool sign(sat::bool_var v) const { return !m_bool_search->get_value(v); } void reset(); ineq* atom(sat::bool_var bv) const { return m_bool_vars[bv]; } - void log(); - bool flip(bool sign, ineq const& ineq); int64_t dtt(bool sign, ineq const& ineq) const { return dtt(sign, ineq.m_args_value, ineq); } int64_t dtt(bool sign, int64_t args_value, ineq const& ineq) const; @@ -151,6 +150,10 @@ namespace arith { int64_t value(var_t v) const { return m_vars[v].m_value; } int64_t to_numeral(rational const& r); + void check_ineqs(); + + std::ostream& display(std::ostream& out) const; + public: sls(solver& s); ~sls() override {} From 027770930e251b72d8c6a4db6e77aab5d73a553d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 17:03:15 -0800 Subject: [PATCH 451/597] fix bug in quasi macro identification: require quantifiers --- src/ast/simplifiers/eliminate_predicates.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 05f347f25e7..b2cc5e25dfa 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -205,6 +205,7 @@ void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause cons // forall vars . f(args) = if eqs then body else f'(args) f1 = m.mk_fresh_func_decl(f->get_name(), symbol::null, sorts.size(), sorts.data(), f->get_range()); + lhs = m.mk_app(f, args); rhs = m.mk_ite(mk_and(eqs), body, m.mk_app(f1, args)); insert_macro(lhs, rhs, cl.m_dep); @@ -572,7 +573,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { !occurs(x->get_decl(), y); }; - if (cl.is_unit() && m.is_eq(cl.atom(0), x, y)) { + if (cl.is_unit() && m.is_eq(cl.atom(0), x, y) && !cl.m_bound.empty()) { if (!cl.sign(0) && can_be_qdef(x, y)) { insert_quasi_macro(to_app(x), y, cl); return; @@ -590,7 +591,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { return; } } - if (cl.is_unit()) { + if (cl.is_unit() && !cl.m_bound.empty()) { expr* body = cl.sign(0) ? m.mk_false() : m.mk_true(); expr* x = cl.atom(0); if (can_be_qdef(x, body)) { From 46d37b6e307abba2e782e18d907dc2024ebe68d9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 17:30:07 -0800 Subject: [PATCH 452/597] fix #6615 make rewriting exception safe (for cancelation). The state during restart in smt_context is not exception safe. --- src/ast/rewriter/seq_axioms.cpp | 2 +- src/ast/rewriter/th_rewriter.cpp | 34 +++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index e08ead08b71..4d7da4d7f99 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -1237,7 +1237,7 @@ namespace seq { seq.str.is_string(x)) { expr_ref len(n, m); m_rewrite(len); - SASSERT(n != len); + SASSERT(m.limit().is_canceled() || n != len); add_clause(mk_eq(len, n)); } else { diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 93b867c4595..d75a31a66f2 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -967,17 +967,41 @@ void th_rewriter::reset() { } void th_rewriter::operator()(expr_ref & term) { - expr_ref result(term.get_manager()); - m_imp->operator()(term, result); - term = std::move(result); + expr_ref result(term.get_manager()); + try { + m_imp->operator()(term, result); + term = std::move(result); + } + catch (...) { + if (!term.get_manager().inc()) + return; + throw; + } } void th_rewriter::operator()(expr * t, expr_ref & result) { - m_imp->operator()(t, result); + try { + m_imp->operator()(t, result); + } + catch (...) { + result = t; + if (!result.get_manager().inc()) + return; + throw; + } } void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { - m_imp->operator()(t, result, result_pr); + try { + m_imp->operator()(t, result, result_pr); + } + catch (...) { + result = t; + result_pr = nullptr; + if (!result.get_manager().inc()) + return; + throw; + } } expr_ref th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings) { From acd2eaa39070057af515a43d992de96e988dc64c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 20:39:39 -0800 Subject: [PATCH 453/597] add (disabled) code path to enable nested conjunctions for experiments with disabling flat-and-or dependency --- src/ast/rewriter/hoist_rewriter.cpp | 32 +++++++++++++++++++++++++++-- src/ast/rewriter/hoist_rewriter.h | 2 +- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index d3f5af2b573..d5b2042a2d0 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -23,7 +23,7 @@ Module Name: #include "ast/ast_ll_pp.h" hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): - m(m), m_args1(m), m_args2(m), m_subst(m) { + m(m), m_args1(m), m_args2(m), m_refs(m), m_subst(m) { updt_params(p); } @@ -185,7 +185,7 @@ expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsi br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { switch (f->get_decl_kind()) { - case OP_OR: + case OP_OR: return mk_or(num_args, args, result); default: return BR_FAILED; @@ -193,6 +193,33 @@ 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 0 + if (!args) + return m.is_and(e) || (m.is_not(e, e) && m.is_or(e)); + expr_fast_mark1 visited; + args->reset(); + args->push_back(e); + m_refs.reset(); + for (unsigned i = 0; i < args->size(); ++i) { + e = args->get(i); + if (visited.is_marked(e)) + goto drop; + m_refs.push_back(e); + visited.mark(e, true); + if (m.is_and(e)) + args->append(to_app(e)->get_num_args(), to_app(e)->get_args()); + else if (m.is_not(e, e) && m.is_or(e)) + for (expr* arg : *to_app(e)) + args->push_back(::mk_not(m, arg)); + else + continue; + drop: + (*args)[i] = args->back(); + args->pop_back(); + --i; + } + return args->size() > 1; +#else if (m.is_and(e)) { if (args) { args->reset(); @@ -209,6 +236,7 @@ bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { } return true; } +#endif return false; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index ae7dff3bc51..b64325584a5 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -29,7 +29,7 @@ class bool_rewriter; class hoist_rewriter { ast_manager & m; - expr_ref_vector m_args1, m_args2; + expr_ref_vector m_args1, m_args2, m_refs; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; ptr_vector m_es; From 94b79eefea222f1250e3f569bbd003fb08be0c10 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 20:40:22 -0800 Subject: [PATCH 454/597] add back max_occs parameter dependency to solve-eqs --- src/ast/simplifiers/solve_eqs.cpp | 42 +++++++++++++++++++++++++++++++ src/ast/simplifiers/solve_eqs.h | 7 +++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 65fe8374a8a..c59ae1e49d8 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -262,6 +262,48 @@ namespace euf { } } + void solve_eqs::collect_num_occs(expr * t, expr_fast_mark1 & visited) { + ptr_buffer stack; + + auto visit = [&](expr* arg) { + if (is_uninterp_const(arg)) { + m_num_occs.insert_if_not_there(arg, 0)++; + } + if (!visited.is_marked(arg) && is_app(arg)) { + visited.mark(arg, true); + stack.push_back(to_app(arg)); + } + }; + + visit(t); + + while (!stack.empty()) { + app * t = stack.back(); + stack.pop_back(); + for (expr* arg : *t) + visit(arg); + } + } + + void solve_eqs::collect_num_occs() { + if (m_config.m_max_occs == UINT_MAX) + return; // no need to compute num occs + m_num_occs.reset(); + expr_fast_mark1 visited; + for (unsigned i : indices()) + collect_num_occs(m_fmls[i].fml(), visited); + } + + // Check if the number of occurrences of t is below the specified threshold :solve-eqs-max-occs + bool solve_eqs::check_occs(expr * t) const { + if (m_config.m_max_occs == UINT_MAX) + return true; + unsigned num = 0; + m_num_occs.find(t, num); + TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m_manager) << " num_occs: " << num << " max: " << m_max_occs << "\n";); + return num <= m_config.m_max_occs; + } + void solve_eqs::save_subst(vector const& old_fmls) { if (!m_subst->empty()) m_fmls.model_trail().push(m_subst.detach(), old_fmls); diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index c8fbe3a40c0..dde42dd94b4 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -56,10 +56,12 @@ namespace euf { expr_mark m_unsafe_vars; // expressions that cannot be replaced ptr_vector m_todo; expr_mark m_visited; + obj_map m_num_occs; + 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()]; } - bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e); } + bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e) && check_occs(e); } void get_eqs(dep_eq_vector& eqs); void filter_unsafe_vars(); void extract_subst(); @@ -67,6 +69,9 @@ namespace euf { void normalize(); void apply_subst(vector& old_fmls); void save_subst(vector const& old_fmls); + void collect_num_occs(expr * t, expr_fast_mark1 & visited); + void collect_num_occs(); + bool check_occs(expr* t) const; public: From fd97be0e3e3f20270ff5968414c184b9894d45c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 21:03:27 -0800 Subject: [PATCH 455/597] move sat.smt.proof.check_rup into solver.proof.check_rup #6616 --- src/params/solver_params.pyg | 1 + src/sat/sat_config.cpp | 1 - src/sat/sat_config.h | 1 - src/sat/sat_params.pyg | 3 +-- src/sat/smt/euf_proof_checker.cpp | 6 +++--- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/params/solver_params.pyg b/src/params/solver_params.pyg index 1c2be32134e..0912b4c7fdb 100644 --- a/src/params/solver_params.pyg +++ b/src/params/solver_params.pyg @@ -10,6 +10,7 @@ def_module_params('solver', ('axioms2files', BOOL, False, 'print negated theory axioms to separate files during search'), ('proof.log', SYMBOL, '', 'log clause proof trail into a file'), ('proof.check', BOOL, True, 'check proof logs'), + ('proof.check_rup', BOOL, True, 'check proof RUP inference in proof logs'), ('proof.save', BOOL, False, 'save proof log into a proof object that can be extracted using (get-proof)'), ('proof.trim', BOOL, False, 'trim and save proof into a proof object that an be extracted using (get-proof)'), )) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 0a9e803a959..73516f66d12 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -200,7 +200,6 @@ namespace sat { m_drat_check_sat = p.drat_check_sat(); m_drat_file = p.drat_file(); m_smt_proof_check = p.smt_proof_check(); - m_smt_proof_check_rup = p.smt_proof_check_rup(); m_drat_disable = p.drat_disable(); m_drat = !m_drat_disable && p.threads() == 1 && diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index f8c0775b110..a47f041b028 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -180,7 +180,6 @@ namespace sat { bool m_drat_binary; symbol m_drat_file; bool m_smt_proof_check; - bool m_smt_proof_check_rup; bool m_drat_check_unsat; bool m_drat_check_sat; bool m_drat_activity; diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 6aedf1c89e9..d40d606d1e1 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -48,8 +48,7 @@ def_module_params('sat', ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), ('drat.disable', BOOL, False, 'override anything that enables DRAT'), ('smt', BOOL, False, 'use the SAT solver based incremental SMT core'), - ('smt.proof.check', BOOL, False, 'check SMT proof while it is created'), - ('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'), + ('smt.proof.check', BOOL, False, 'check proofs on the fly during SMT search'), ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), ('drat.binary', BOOL, False, 'use Binary DRAT output format'), ('drat.check_unsat', BOOL, False, 'build up internal proof and check'), diff --git a/src/sat/smt/euf_proof_checker.cpp b/src/sat/smt/euf_proof_checker.cpp index 2d4f67cd237..a538b2a804f 100644 --- a/src/sat/smt/euf_proof_checker.cpp +++ b/src/sat/smt/euf_proof_checker.cpp @@ -28,7 +28,7 @@ Module Name: #include "sat/smt/bv_theory_checker.h" #include "sat/smt/distinct_theory_checker.h" #include "sat/smt/tseitin_theory_checker.h" - +#include "params/solver_params.hpp" namespace euf { @@ -388,8 +388,8 @@ namespace euf { m_sat_solver.updt_params(m_params); m_drat.updt_config(); m_rup = symbol("rup"); - sat_params sp(m_params); - m_check_rup = sp.smt_proof_check_rup(); + solver_params sp(m_params); + m_check_rup = sp.proof_check_rup(); } void smt_proof_checker::ensure_solver() { From aa75ba8a6b213e3bb4e2293893a061864cb95fb1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 21:03:41 -0800 Subject: [PATCH 456/597] remove parenthesis --- src/ast/simplifiers/solve_eqs.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index c59ae1e49d8..160b16b3b7b 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -266,9 +266,8 @@ namespace euf { ptr_buffer stack; auto visit = [&](expr* arg) { - if (is_uninterp_const(arg)) { - m_num_occs.insert_if_not_there(arg, 0)++; - } + if (is_uninterp_const(arg)) + m_num_occs.insert_if_not_there(arg, 0)++; if (!visited.is_marked(arg) && is_app(arg)) { visited.mark(arg, true); stack.push_back(to_app(arg)); From b82d177276ae7ff87691c695b086c5b50cc878bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 3 Mar 2023 11:26:13 -0800 Subject: [PATCH 457/597] fix build Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_eqs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 160b16b3b7b..fbc6fbb02db 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -299,7 +299,7 @@ namespace euf { return true; unsigned num = 0; m_num_occs.find(t, num); - TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m_manager) << " num_occs: " << num << " max: " << m_max_occs << "\n";); + TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m) << " num_occs: " << num << " max: " << m_config.m_max_occs << "\n";); return num <= m_config.m_max_occs; } From 55d45e0c0c2075ebca1b597c8230716a0c26a93a Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Fri, 3 Mar 2023 15:32:23 -0500 Subject: [PATCH 458/597] bug fix. Prevent resetting gg stats #6062 (#6618) --- src/muz/spacer/spacer_context.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 4c5c13e3428..38b1c714981 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -114,18 +114,10 @@ void pob::inherit(pob const &p) { m_desired_level = std::max(m_desired_level, p.m_desired_level); m_open = p.m_open; m_use_farkas = p.m_use_farkas; - - m_is_conjecture = p.m_is_conjecture; - m_enable_local_gen = p.m_enable_local_gen; - m_enable_concretize = p.m_enable_concretize; - m_is_subsume = p.m_is_subsume; - m_enable_expand_bnd_gen = p.m_enable_expand_bnd_gen; - m_weakness = p.m_weakness; m_derivation = nullptr; - m_gas = p.m_gas; } void pob::close () { From f986ac6a75b22ea0b7bafe9c5ddaed6cde048007 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 14:50:10 -0800 Subject: [PATCH 459/597] remove mps_reader --- src/math/lp/mps_reader.h | 891 ------------------------------------ src/shell/CMakeLists.txt | 1 - src/shell/lp_frontend.cpp | 109 ----- src/shell/lp_frontend.h | 7 - src/shell/main.cpp | 10 +- src/test/lp/lp.cpp | 924 +++----------------------------------- src/test/lp/smt_reader.h | 2 - 7 files changed, 69 insertions(+), 1875 deletions(-) delete mode 100644 src/math/lp/mps_reader.h delete mode 100644 src/shell/lp_frontend.cpp delete mode 100644 src/shell/lp_frontend.h diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h deleted file mode 100644 index 8093954b11e..00000000000 --- a/src/math/lp/mps_reader.h +++ /dev/null @@ -1,891 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -// reads an MPS file representing a Mixed Integer Program -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" -#include "math/lp/lar_solver.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -namespace lp { -inline bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -inline size_t number_of_whites(const std::string & s) { - size_t i = 0; - for(;i < s.size(); i++) - if (!my_white_space(s[i])) return i; - return i; -} -inline size_t number_of_whites_from_end(const std::string & s) { - size_t ret = 0; - for(int i = static_cast(s.size()) - 1;i >= 0; i--) - if (my_white_space(s[i])) ret++;else break; - - return ret; -} - - - // trim from start -inline std::string <rim(std::string &s) { - s.erase(0, number_of_whites(s)); - return s; -} - - - - - // trim from end -inline std::string &rtrim(std::string &s) { - // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - s.erase(s.end() - number_of_whites_from_end(s), s.end()); - return s; -} - // trim from both ends -inline std::string &trim(std::string &s) { - return ltrim(rtrim(s)); -} - -inline std::string trim(std::string const &r) { - std::string s = r; - return ltrim(rtrim(s)); -} - - -inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { - vector results; - size_t prev = 0; - size_t next = 0; - while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { - if (keep_empty || (next - prev != 0)) { - results.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - if (prev < source.size()) { - results.push_back(source.substr(prev)); - } - return results; -} - -inline vector split_and_trim(const std::string &line) { - auto split = string_split(line, " \t", false); - vector ret; - for (auto s : split) { - ret.push_back(trim(s)); - } - return ret; -} - -template -class mps_reader { - enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; - struct bound { - T m_low; - T m_upper; - bool m_low_is_set; - bool m_upper_is_set; - bool m_value_is_fixed; - T m_fixed_value; - bool m_free; - // constructor - bound() : m_low(numeric_traits::zero()), - m_low_is_set(true), - m_upper_is_set(false), - m_value_is_fixed(false), - m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable - }; - - struct column { - std::string m_name; - bound * m_bound; - unsigned m_index; - column(const std::string &name, unsigned index): m_name(name), - m_bound(nullptr), - m_index(index) { - } - }; - - struct row { - row_type m_type; - std::string m_name; - std::unordered_map m_row_columns; - unsigned m_index; - T m_right_side; - T m_range; - row(row_type type, const std::string &name, unsigned index) : - m_type(type), - m_name(name), - m_index(index), - m_right_side(zero_of_type()), - m_range(zero_of_type()) - { - } - }; - - bool m_is_OK; - std::string m_file_name; - std::unordered_map m_rows; - std::unordered_map m_columns; - std::unordered_map m_names_to_var_index; - std::string m_line; - std::string m_name; - std::string m_cost_row_name; - std::ifstream m_file_stream; - // needed to adjust the index row - unsigned m_cost_line_count; - unsigned m_line_number; - std::ostream * m_message_stream; - - void set_m_ok_to_false() { - *m_message_stream << "setting m_is_OK to false" << std::endl; - m_is_OK = false; - } - - std::string get_string_from_position(unsigned offset) { - unsigned i = offset; - for (; i < m_line.size(); i++){ - if (m_line[i] == ' ') - break; - } - lp_assert(m_line.size() >= offset); - lp_assert(m_line.size() >> i); - lp_assert(i >= offset); - return m_line.substr(offset, i - offset); - } - - void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ - if (b == nullptr) { - solver->set_lower_bound(col, numeric_traits::zero()); - return; - } - - if (b->m_free) { - return; - } - if (b->m_low_is_set) { - solver->set_lower_bound(col, b->m_low); - } - if (b->m_upper_is_set) { - solver->set_upper_bound(col, b->m_upper); - } - - if (b->m_value_is_fixed) { - solver->set_fixed_value(col, b->m_fixed_value); - } - } - - bool all_white_space() { - for (unsigned i = 0; i < m_line.size(); i++) { - char c = m_line[i]; - if (c != ' ' && c != '\t') { - return false; - } - } - return true; - } - - void read_line() { - while (m_is_OK) { - if (!getline(m_file_stream, m_line)) { - m_line_number++; - set_m_ok_to_false(); - *m_message_stream << "cannot read from file" << std::endl; - } - m_line_number++; - if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) - break; - } - } - - void read_name() { - do { - read_line(); - if (m_line.find("NAME") != 0) { - continue; - } - m_line = m_line.substr(4); - m_name = trim(m_line); - break; - } while (m_is_OK); - } - - void read_rows() { - // look for start of the rows - read_line(); - do { - if (static_cast(m_line.find("ROWS")) >= 0) { - break; - } - } while (m_is_OK); - do { - read_line(); - if (m_line.find("COLUMNS") == 0) { - break; - } - add_row(); - } while (m_is_OK); - } - - void read_column_by_columns(const std::string & column_name, std::string column_data) { - // uph, let us try to work with columns - if (column_data.size() >= 22) { - std::string ss = column_data.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - *m_message_stream << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); - if (column_data.size() > 24) { - column_data = column_data.substr(25); - if (column_data.size() >= 22) { - read_column_by_columns(column_name, column_data); - } - } - } - } else { - fail: - set_m_ok_to_false(); - *m_message_stream << "cannot understand this line\n" - "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void read_column(const std::string & column_name, const std::string & column_data){ - auto tokens = split_and_trim(column_data); - for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { - auto row_name = tokens[i]; - if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here - auto t = m_rows.find(row_name); - if (t == m_rows.end()) { - read_column_by_columns(column_name, column_data); - return; - } - row *r = t->second; - r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); - } - } - - void read_columns(){ - std::string column_name; - do { - read_line(); - if (m_line.find("RHS") == 0) { - break; - } - if (m_line.size() < 22) { - (*m_message_stream) << "line is too short for a column" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string column_name_tmp = trim(m_line.substr(4, 8)); - if (!column_name_tmp.empty()) { - column_name = column_name_tmp; - } - auto col_it = m_columns.find(column_name); - mps_reader::column * col; - if (col_it == m_columns.end()) { - col = new mps_reader::column(column_name, static_cast(m_columns.size())); - m_columns[column_name] = col; - // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; - } else { - col = col_it->second; - } - read_column(column_name, m_line.substr(14)); - } while (m_is_OK); - } - - void read_rhs() { - do { - read_line(); - if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { - break; - } - fill_rhs(); - } while (m_is_OK); - } - - - void fill_rhs_by_columns(std::string rhsides) { - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - fill_rhs_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void fill_rhs() { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string rhsides = m_line.substr(14); - vector splitted_line = split_and_trim(rhsides); - - for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { - auto t = m_rows.find(splitted_line[i]); - if (t == m_rows.end()) { - fill_rhs_by_columns(rhsides); - return; - } - row * row = t->second; - row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); - } - } - - void read_bounds() { - if (m_line.find("BOUNDS") != 0) { - return; - } - - do { - read_line(); - if (m_line[0] != ' ') { - break; - } - create_or_update_bound(); - } while (m_is_OK); - } - - void read_ranges() { - if (m_line.find("RANGES") != 0) { - return; - } - do { - read_line(); - auto sl = split_and_trim(m_line); - if (sl.size() < 2) { - break; - } - read_range(sl); - } while (m_is_OK); - } - - - void read_bound_by_columns(const std::string & colstr) { - if (colstr.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (colstr.size() >= 22) { - std::string ss = colstr.substr(0, 8); - std::string column_name = trim(ss); - auto t = m_columns.find(column_name); - - if (t == m_columns.end()) { - (*m_message_stream) << "cannot find " << column_name << std::endl; - goto fail; - } else { - vector bound_string; - bound_string.push_back(column_name); - if (colstr.size() > 14) { - bound_string.push_back(colstr.substr(14)); - } - mps_reader::column * col = t->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void update_bound(bound * b, vector bound_string) { - /* - UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. - */ - - std::string bound_type = get_string_from_position(1); - if (bound_type == "BV") { - b->m_upper_is_set = true; - b->m_upper = 1; - return; - } - - if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - b->m_upper_is_set = true; - b->m_upper= numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "LO" || bound_type == "LI") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_low_is_set = true; - b->m_low = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "FR") { - b->m_free = true; - } else if (bound_type == "FX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_value_is_fixed = true; - b->m_fixed_value = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "PL") { - b->m_low_is_set = true; - b->m_low = 0; - } else if (bound_type == "MI") { - b->m_upper_is_set = true; - b->m_upper = 0; - } else { - (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - } - - void create_or_update_bound() { - const unsigned name_offset = 14; - lp_assert(m_line.size() >= 14); - vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); - - if (bound_string.empty()) { - set_m_ok_to_false(); - (*m_message_stream) << "error at line " << m_line_number << std::endl; - throw m_line; - } - - std::string name = bound_string[0]; - auto it = m_columns.find(name); - if (it == m_columns.end()){ - read_bound_by_columns(m_line.substr(14)); - return; - } - mps_reader::column * col = it->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - - - - void read_range_by_columns(std::string rhsides) { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_range = numeric_traits::from_string(rhsides.substr(8)); - maybe_modify_current_row_and_add_row_for_range(row); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - read_range_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - - void read_range(vector & splitted_line){ - for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { - auto it = m_rows.find(splitted_line[i]); - if (it == m_rows.end()) { - read_range_by_columns(m_line.substr(14)); - return; - } - row * row = it->second; - row->m_range = numeric_traits::from_string(splitted_line[i + 1]); - maybe_modify_current_row_and_add_row_for_range(row); - } - } - - void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - std::string row_name = row_with_range->m_name + "_range"; - row * other_bound_range_row; - switch (row_with_range->m_type) { - case row_type::Greater_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); - break; - case row_type::Less_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); - break; - case row_type::Equal: - if (row_with_range->m_range > 0) { - row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - } else { // row->m_range < 0; - row_with_range->m_type = row_type::Less_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - } - other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; - break; - default: - (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - - for (auto s : row_with_range->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - other_bound_range_row->m_row_columns[s.first] = s.second; - } - } - - void add_row() { - if (m_line.length() < 2) { - return; - } - - m_line = trim(m_line); - char c = m_line[0]; - m_line = m_line.substr(1); - m_line = trim(m_line); - add_row(c); - } - - void add_row(char c) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - switch (c) { - case 'E': - m_rows[m_line] = new row(row_type::Equal, m_line, index); - break; - case 'L': - m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); - break; - case 'G': - m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); - break; - case 'N': - m_rows[m_line] = new row(row_type::Cost, m_line, index); - m_cost_row_name = m_line; - m_cost_line_count++; - break; - } - } - unsigned range_count() { - unsigned ret = 0; - for (auto s : m_rows) { - if (s.second->m_range != 0) { - ret++; - } - } - return ret; - } - - /* - If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: - sense interval - G [rhs, rhs + |range|] - L [rhs - |range|, rhs] - E [rhs, rhs + |range|] if range > 0, - [rhs - |range|, rhs] if range < 0 - where |range| is range's absolute value. - */ - - lp_relation get_relation_from_row(row_type rt) { - switch (rt) { - case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; - case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; - case mps_reader::Equal: return lp_relation::Equal; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned solver_row_count() { - return m_rows.size() - m_cost_line_count + range_count(); - } - - void fill_solver_on_row(row * row, lp_solver *solver) { - if (row->m_name != m_cost_row_name) { - solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); - for (auto s : row->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); - } - } else { - set_solver_cost(row, solver); - } - } - - T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } - - void fill_solver_on_rows(lp_solver * solver) { - for (auto row_it : m_rows) { - fill_solver_on_row(row_it.second, solver); - } - } - - - void fill_solver_on_columns(lp_solver * solver){ - for (auto s : m_columns) { - mps_reader::column * col = s.second; - unsigned index = col->m_index; - set_boundary_for_column(index, col->m_bound, solver); - // optional call - solver->give_symbolic_name_to_column(col->m_name, col->m_index); - } - } - - void fill_solver(lp_solver *solver) { - fill_solver_on_rows(solver); - fill_solver_on_columns(solver); - } - - void set_solver_cost(row * row, lp_solver *solver) { - for (auto s : row->m_row_columns) { - std::string name = s.first; - lp_assert(m_columns.find(name) != m_columns.end()); - mps_reader::column * col = m_columns[name]; - solver->set_cost_for_column(col->m_index, s.second); - } - } - -public: - - void set_message_stream(std::ostream * o) { - lp_assert(o != nullptr); - m_message_stream = o; - } - vector column_names() { - vector v; - for (auto s : m_columns) { - v.push_back(s.first); - } - return v; - } - - ~mps_reader() { - for (auto s : m_rows) { - delete s.second; - } - for (auto s : m_columns) { - auto col = s.second; - delete col->m_bound; - delete col; - } - } - - mps_reader(const std::string & file_name): - m_is_OK(true), - m_file_name(file_name), - m_file_stream(file_name), - m_cost_line_count(0), - m_line_number(0), - m_message_stream(& std::cout) {} - void read() { - if (!m_file_stream.is_open()){ - set_m_ok_to_false(); - return; - } - - read_name(); - read_rows(); - read_columns(); - read_rhs(); - if (m_line.find("BOUNDS") == 0) { - read_bounds(); - read_ranges(); - } else if (m_line.find("RANGES") == 0) { - read_ranges(); - read_bounds(); - } - } - - bool is_ok() { - return m_is_OK; - } - - lp_solver * create_solver(bool dual) { - lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); - fill_solver(solver); - return solver; - } - - lconstraint_kind get_lar_relation_from_row(row_type rt) { - switch (rt) { - case Less_or_equal: return LE; - case Greater_or_equal: return GE; - case Equal: return EQ; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned get_var_index(std::string s) { - auto it = m_names_to_var_index.find(s); - if (it != m_names_to_var_index.end()) - return it->second; - unsigned ret = static_cast(m_names_to_var_index.size()); - m_names_to_var_index[s] = ret; - return ret; - } - - void fill_lar_solver_on_row(row * row, lar_solver *solver, int row_index) { - if (row->m_name != m_cost_row_name) { - auto kind = get_lar_relation_from_row(row->m_type); - vector> ls; - for (auto s : row->m_row_columns) { - var_index i = solver->add_var(get_var_index(s.first), false); - ls.push_back(std::make_pair(s.second, i)); - } - unsigned j = solver->add_term(ls, row_index); - solver->add_var_bound(j, kind, row->m_right_side); - } else { - // ignore the cost row - } - } - - - void fill_lar_solver_on_rows(lar_solver * solver) { - int row_index = 0; - for (auto row_it : m_rows) { - fill_lar_solver_on_row(row_it.second, solver, row_index++); - } - } - - void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, GE, b->m_low); - } - - void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_upper); - } - - void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_fixed_value); - solver->add_var_bound(i, GE, b->m_fixed_value); - } - - void fill_lar_solver_on_columns(lar_solver * solver) { - for (auto s : m_columns) { - mps_reader::column * col = s.second; - solver->add_var(col->m_index, false); - auto b = col->m_bound; - if (b == nullptr) return; - - if (b->m_free) continue; - - if (b->m_low_is_set) { - create_low_constraint_for_var(col, b, solver); - } - if (b->m_upper_is_set) { - create_upper_constraint_for_var(col, b, solver); - } - if (b->m_value_is_fixed) { - create_equality_contraint_for_var(col, b, solver); - } - } - } - - - void fill_lar_solver(lar_solver * solver) { - fill_lar_solver_on_columns(solver); - fill_lar_solver_on_rows(solver); - } - - lar_solver * create_lar_solver() { - lar_solver * solver = new lar_solver(); - fill_lar_solver(solver); - return solver; - } -}; -} diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index d9b74f162f9..c0e9c85050f 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -28,7 +28,6 @@ add_executable(shell opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp - lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp deleted file mode 100644 index 8d6425533c8..00000000000 --- a/src/shell/lp_frontend.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/*++ -Copyright (c) 2016 Microsoft Corporation - -Author: - - Lev Nachmanson 2016-10-27 - ---*/ - -#include "math/lp/lp_settings.h" -#include "math/lp/mps_reader.h" -#include "util/timeout.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "util/rlimit.h" -#include "util/gparams.h" -#include "util/mutex.h" -#include -#include -#include "smt/params/smt_params_helper.hpp" - -namespace { -static mutex *display_stats_mux = new mutex; - -static lp::lp_solver* g_solver = nullptr; - -static void display_statistics() { - lock_guard lock(*display_stats_mux); - if (g_solver && g_solver->settings().print_statistics) { - // TBD display relevant information about statistics - } -} - -static void STD_CALL on_ctrl_c(int) { - signal (SIGINT, SIG_DFL); - display_statistics(); - raise(SIGINT); -} - -static void on_timeout() { - display_statistics(); - _Exit(0); -} - -struct front_end_resource_limit : public lp::lp_resource_limit { - reslimit& m_reslim; - - front_end_resource_limit(reslimit& lim): - m_reslim(lim) - {} - - bool get_cancel_flag() override { return !m_reslim.inc(); } -}; - -void run_solver(smt_params_helper & params, char const * mps_file_name) { - - reslimit rlim; - unsigned timeout = gparams::get_ref().get_uint("timeout", 0); - unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); - front_end_resource_limit lp_limit(rlim); - - scoped_rlimit _rlimit(rlim, rlimit); - cancel_eh eh(rlim); - scoped_timer timer(timeout, &eh); - - std::string fn(mps_file_name); - lp::mps_reader reader(fn); - reader.set_message_stream(&std::cout); // can be redirected - reader.read(); - if (!reader.is_ok()) { - std::cerr << "cannot process " << mps_file_name << std::endl; - return; - } - lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver - solver->settings().set_resource_limit(lp_limit); - g_solver = solver; - if (params.arith_min()) { - solver->flip_costs(); - } - solver->settings().set_message_ostream(&std::cout); - solver->settings().report_frequency = params.arith_rep_freq(); - solver->settings().print_statistics = params.arith_print_stats(); - solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); - - solver->find_maximal_solution(); - - *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp::lp_status::OPTIMAL) { - if (params.arith_min()) { - solver->flip_costs(); - } - solver->print_model(std::cout); - } - - display_statistics(); - g_solver = nullptr; - delete solver; -} -} - -unsigned read_mps_file(char const * mps_file_name) { - signal(SIGINT, on_ctrl_c); - register_on_timeout_proc(on_timeout); - smt_params_helper p; - param_descrs r; - p.collect_param_descrs(r); - run_solver(p, mps_file_name); - return 0; -} diff --git a/src/shell/lp_frontend.h b/src/shell/lp_frontend.h deleted file mode 100644 index b24be811f18..00000000000 --- a/src/shell/lp_frontend.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - Copyright (c) 2013 Microsoft Corporation. All rights reserved. - - Author: Lev Nachmanson -*/ -#pragma once -unsigned read_mps_file(char const * mps_file_name); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 4c26d91d968..26325d3d0a2 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -37,14 +37,13 @@ Revision History: #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" -#include "shell/lp_frontend.h" #include "shell/drat_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_DRAT } input_kind; static char const * g_input_file = nullptr; static char const * g_drat_input_file = nullptr; @@ -377,10 +376,6 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } - else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || - strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { - g_input_kind = IN_MPS; - } } } switch (g_input_kind) { @@ -406,9 +401,6 @@ int STD_CALL main(int argc, char ** argv) { case IN_Z3_LOG: replay_z3_log(g_input_file); break; - case IN_MPS: - return_value = read_mps_file(g_input_file); - break; case IN_DRAT: return_value = read_drat(g_drat_input_file); break; diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c82cdd0a4b4..984c7b4bb0c 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -34,7 +34,6 @@ #include #include "math/lp/lp_utils.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/mps_reader.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" @@ -59,6 +58,71 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" + +bool my_white_space(const char & a) { + return a == ' ' || a == '\t'; +} +size_t number_of_whites(const std::string & s) { + size_t i = 0; + for(;i < s.size(); i++) + if (!my_white_space(s[i])) return i; + return i; +} +size_t number_of_whites_from_end(const std::string & s) { + size_t ret = 0; + for(int i = static_cast(s.size()) - 1;i >= 0; i--) + if (my_white_space(s[i])) ret++;else break; + + return ret; +} + + +std::string <rim(std::string &s) { + s.erase(0, number_of_whites(s)); + return s; +} + + + + + // trim from end +inline std::string &rtrim(std::string &s) { + // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.end() - number_of_whites_from_end(s), s.end()); + return s; +} + // trim from both ends +inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} + + +vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { + vector results; + size_t prev = 0; + size_t next = 0; + while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { + if (keep_empty || (next - prev != 0)) { + results.push_back(source.substr(prev, next - prev)); + } + prev = next + 1; + } + if (prev < source.size()) { + results.push_back(source.substr(prev)); + } + return results; +} + +vector split_and_trim(const std::string &line) { + auto split = string_split(line, " \t", false); + vector ret; + for (auto s : split) { + ret.push_back(trim(s)); + } + return ret; +} + + namespace nla { void test_horner(); void test_monics(); @@ -1408,156 +1472,12 @@ void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args bool values_are_one_percent_close(double a, double b); -void print_x(mps_reader & reader, lp_solver * solver) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; -} -void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { - for (const auto & name : reader.column_names()) { - double a = solver->get_column_value_by_name(name); - double b = solver0->get_column_value_by_name(name); - if (!values_are_one_percent_close(a, b)) { - std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; - } - } -} -void solve_mps_double(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return; - } - - lp_solver * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - if (dual) { - std::cout << "solving for dual" << std::endl; - } - solver->find_maximal_solution(); - sw.stop(); - double span = sw.get_seconds(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - print_x(reader, solver); - } - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost << std::endl; - } - std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; - if (compare_with_primal) { - auto * primal_solver = reader.create_solver(false); - setup_solver(time_limit, look_for_min, args_parser, primal_solver); - primal_solver->find_maximal_solution(); - if (solver->get_status() != primal_solver->get_status()) { - std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; - } else { - if (solver->get_status() == lp_status::OPTIMAL) { - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - double primal_cost = primal_solver->get_current_cost(); - if (look_for_min) { - primal_cost = -primal_cost; - } - std::cout << "primal cost = " << primal_cost << std::endl; - if (!values_are_one_percent_close(cost, primal_cost)) { - compare_solutions(reader, primal_solver, solver); - print_x(reader, primal_solver); - std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; - lp_assert(false); - } - } - } - delete primal_solver; - } - delete solver; -} - -void solve_mps_rational(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - solver->find_maximal_solution(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - - if (solver->get_status() == lp_status::OPTIMAL) { - // for (auto name: reader.column_names()) { - // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - // } - lp::mpq cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost.get_double() << std::endl; - } - std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition -void solve_mps(std::string file_name, bool look_for_min, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { - if (!solve_for_rational) { - std::cout << "solving " << file_name << std::endl; - solve_mps_double(file_name, look_for_min, time_limit, dual, compare_with_primal, args_parser); - } - else { - std::cout << "solving " << file_name << " in rationals " << std::endl; - solve_mps_rational(file_name, look_for_min, time_limit, dual, args_parser); - } -} -void solve_mps(std::string file_name, argument_parser & args_parser) { - bool look_for_min = args_parser.option_is_used("--min"); - unsigned time_limit; - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - solve_mps(file_name, look_for_min, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); -} - -void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { - std::cout << "solving " << file_name << std::endl; - - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; - } - } - std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void test_upair_queue() { int n = 10; @@ -1626,53 +1546,6 @@ void test_binary_priority_queue() { std::cout << " done" << std::endl; } -bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); - return solver->solution_is_feasible(solution); - } - return false; -} - - -void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { - std::cout << "solving " << file_name << std::endl; - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (status != solver->get_status()){ - std::cout << "status should be " << lp_status_to_string(status) << std::endl; - lp_assert(status == solver->get_status()); - throw "status is wrong"; - } - if (solver->get_status() == lp_status::OPTIMAL) { - std::cout << "cost = " << solver->get_current_cost() << std::endl; - if (solution != nullptr) { - for (auto it : *solution) { - if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { - std::cout << "expected:" << it.first << "=" << - it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; - } - lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); - } - } - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; - } - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} int get_random_rows() { return 5 + my_random() % 2; @@ -1896,140 +1769,9 @@ void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // std::cout << "fn = " << fn << std::endl; } -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); - -void solve_some_mps(argument_parser & args_parser) { - unsigned max_iters = UINT_MAX, time_limit = UINT_MAX; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0; - unsigned failures = 0; - unsigned inconclusives = 0; - std::set minimums; - vector file_names; - fill_file_names(file_names, minimums); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); - if (compare_with_glpk) { - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - test_out_dir(out_dir); - for (auto& a : file_names) { - try { - std::string file_dir; - std::string file_name; - find_dir_and_file_name(a, file_dir, file_name); - process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - return; - } - if (!solve_for_rational) { - solve_mps(file_names[6], false, time_limit, false, dual, compare_with_primal, args_parser); - solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) - std::unordered_map sol; - sol["X1"] = 0; - sol["X2"] = 6; - sol["X3"] = 0; - sol["X4"] = 15; - sol["X5"] = 2; - sol["X6"] = 1; - sol["X7"] = 1; - sol["X8"] = 0; - solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); - solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); - sol.clear(); - sol["X1"] = 25.0/14.0; - // sol["X2"] = 0; - // sol["X3"] = 0; - // sol["X4"] = 0; - // sol["X5"] = 0; - // sol["X6"] = 0; - // sol["X7"] = 9.0/14.0; - solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) - solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) - solve_mps(file_names[8], false, time_limit, false, dual, compare_with_primal, args_parser); - // return; - for (auto& s : file_names) { - try { - solve_mps(s, minimums.find(s) != minimums.end(), time_limit, false, dual, compare_with_primal, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } else { - // unsigned i = 0; - for (auto& s : file_names) { - // if (i++ > 9) return; - try { - solve_mps_in_rational(s, dual, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } -} -#endif - -void solve_rational() { - lp_primal_simplex solver; - solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); - solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); - - // setting the cost - int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; - std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; - for (unsigned i = 0; i < 8; i++) { - solver.set_cost_for_column(i, lp::mpq(cost[i])); - solver.give_symbolic_name_to_column(var_names[i], i); - } - - int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); - } - - int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); - } - - int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; - for (unsigned i = 0; i < 8; i++) { - solver.set_lower_bound(i, lp::mpq(0)); - solver.set_upper_bound(i, lp::mpq(bounds[i])); - } - - std::unordered_map expected_sol; - expected_sol["x1"] = lp::mpq(0); - expected_sol["x2"] = lp::mpq(6); - expected_sol["x3"] = lp::mpq(0); - expected_sol["x4"] = lp::mpq(15); - expected_sol["x5"] = lp::mpq(2); - expected_sol["x6"] = lp::mpq(1); - expected_sol["x7"] = lp::mpq(1); - expected_sol["x8"] = lp::mpq(0); - solver.find_maximal_solution(); - lp_assert(solver.get_status() == lp_status::OPTIMAL); -#ifdef Z3DEBUG - for (const auto & it : expected_sol) { - (void)it; - lp_assert(it.second == solver.get_column_value_by_name(it.first)); - } #endif -} + std::string read_line(bool & end, std::ifstream & file) { @@ -2047,49 +1789,6 @@ bool contains(std::string const & s, char const * pattern) { } -std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { - std::ifstream file(file_name); - if (!file.is_open()){ - std::cerr << "cannot open " << file_name << std::endl; - return nullptr; - } - std::string s; - bool end; - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - if (contains(s, "Column name")){ - break; - } - } while (true); - - read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - - auto ret = new std::unordered_map(); - - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - auto split = string_split(s, " \t", false); - if (split.empty()) { - return ret; - } - - lp_assert(split.size() > 3); - (*ret)[split[1]] = atof(split[3].c_str()); - } while (true); -} - void test_init_U() { @@ -2189,7 +1888,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); - parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); @@ -2360,237 +2058,9 @@ std::string get_status(std::string file_name) { throw 0; } -// returns true if the costs should be compared too -bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { - std::string glpk_status = get_status(glpk_out_file_name); - std::string lp_tst_status = get_status(lp_out_file_name); - if (glpk_status != lp_tst_status) { - if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { - successes++; - return false; - } else { - std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; - std::cout << " but lp_tst status is " << lp_tst_status << std::endl; - failures++; - return false; - } - } - return lp_tst_status == "OPTIMAL"; -} -double get_glpk_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - while (getline(f, str)) { - if (str.find("Objective") != std::string::npos) { - vector tokens = split_and_trim(str); - if (tokens.size() != 5) { - std::cout << "unexpected Objective std::string " << str << std::endl; - throw 0; - } - return atof(tokens[3].c_str()); - } - } - std::cout << "cannot find the Objective line in " << file_name << std::endl; - throw 0; -} -double get_lp_tst_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - std::string cost_string; - while (getline(f, str)) { - if (str.find("cost") != std::string::npos) { - cost_string = str; - } - } - if (cost_string.empty()) { - std::cout << "cannot find the cost line in " << file_name << std::endl; - throw 0; - } - - vector tokens = split_and_trim(cost_string); - if (tokens.size() != 3) { - std::cout << "unexpected cost string " << cost_string << std::endl; - throw 0; - } - return atof(tokens[2].c_str()); -} - -bool values_are_one_percent_close(double a, double b) { - double maxval = std::max(fabs(a), fabs(b)); - if (maxval < 0.000001) { - return true; - } - - double one_percent = maxval / 100; - return fabs(a - b) <= one_percent; -} - -// returns true if both are optimal -void compare_costs(std::string glpk_out_file_name, - std::string lp_out_file_name, - unsigned & successes, - unsigned & failures) { - double a = get_glpk_cost(glpk_out_file_name); - double b = get_lp_tst_cost(lp_out_file_name); - - if (values_are_one_percent_close(a, b)) { - successes++; - } else { - failures++; - std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; - } -} - - - -void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { -#ifdef CHECK_GLPK_SOLUTION - std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); - if (solution_is_feasible(lp_file_name, *solution_table)) { - std::cout << "glpk solution is feasible" << std::endl; - } else { - std::cout << "glpk solution is infeasible" << std::endl; - } - delete solution_table; -#endif - if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { - compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); - } -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser); - -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { - bool use_mpq = args_parser.option_is_used("--mpq"); - bool minimize = args_parser.option_is_used("--min"); - std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); - - std::string input_file_name = test_dir + "/" + test_file_name; - if (input_file_name[input_file_name.size() - 1] == '~') { - // std::cout << "ignoring " << input_file_name << std::endl; - return; - } - std::cout <<"processing " << input_file_name << std::endl; - - std::ofstream out(full_lp_tst_out_name); - if (!out.is_open()) { - std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; - throw 0; - } - std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer - std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! - bool dual = args_parser.option_is_used("--dual"); - try { - if (args_parser.option_is_used("--lar")) - test_lar_on_file(input_file_name, args_parser); - else - solve_mps(input_file_name, minimize, time_limit, use_mpq, dual, false, args_parser); - } - catch(...) { - std::cout << "catching the failure" << std::endl; - failures++; - std::cout.rdbuf(coutbuf); // reset to standard output again - return; - } - std::cout.rdbuf(coutbuf); // reset to standard output again - - if (args_parser.option_is_used("--compare_with_glpk")) { - std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); - int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); - if (glpk_exit_code != 0) { - std::cout << "glpk failed" << std::endl; - inconclusives++; - } else { - compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); - } - } -} -/* - int my_readdir(DIR *dirp, struct dirent * - #ifndef LEAN_WINDOWS - entry - #endif - , struct dirent **result) { - #ifdef LEAN_WINDOWS - *result = readdir(dirp); // NOLINT - return *result != nullptr? 0 : 1; - #else - return readdir_r(dirp, entry, result); - #endif - } -*/ -/* - vector> get_file_list_of_dir(std::string test_file_dir) { - DIR *dir; - if ((dir = opendir(test_file_dir.c_str())) == nullptr) { - std::cout << "Cannot open directory " << test_file_dir << std::endl; - throw 0; - } - vector> ret; - struct dirent entry; - struct dirent* result; - int return_code; - for (return_code = my_readdir(dir, &entry, &result); - #ifndef LEAN_WINDOWS - result != nullptr && - #endif - return_code == 0; - return_code = my_readdir(dir, &entry, &result)) { - DIR *tmp_dp = opendir(result->d_name); - struct stat file_record; - if (tmp_dp == nullptr) { - std::string s = test_file_dir+ "/" + result->d_name; - int stat_ret = stat(s.c_str(), & file_record); - if (stat_ret!= -1) { - ret.push_back(make_pair(result->d_name, file_record.st_size)); - } else { - perror("stat"); - exit(1); - } - } else { - closedir(tmp_dp); - } - } - closedir(dir); - return ret; - } -*/ -/* - struct file_size_comp { - unordered_map& m_file_sizes; - file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} - int operator()(std::string a, std::string b) { - std::cout << m_file_sizes.size() << std::endl; - std::cout << a << std::endl; - std::cout << b << std::endl; - - auto ls = m_file_sizes.find(a); - std::cout << "fa" << std::endl; - auto rs = m_file_sizes.find(b); - std::cout << "fb" << std::endl; - if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { - std::cout << "fc " << std::endl; - int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); - std::cout << "calc r " << std::endl; - return r; - } else { - std::cout << "sc " << std::endl; - return 0; - } - } - }; - -*/ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; @@ -2598,121 +2068,11 @@ struct sort_pred { }; -void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { - /* - std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - DIR *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - return; - } - closedir(out_dir_p); - vector> files = get_file_list_of_dir(test_file_dir); - std::sort(files.begin(), files.end(), sort_pred()); - unsigned max_iters, time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0, failures = 0, inconclusives = 0; - for (auto & t : files) { - process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - */ -} -std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { - std::unordered_map ret; - for (const auto & it : reader.column_names()) { - ret[it] = lps->get_column_value_by_name(it); - } - return ret; -} -void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { - std::string maxng = args_parser.get_option_value("--maxng"); - if (!maxng.empty()) { - solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); - } - if (args_parser.option_is_used("-pd")){ - solver->settings().presolve_with_double_solver_for_lar = true; - } - - if (args_parser.option_is_used("--compare_with_primal")){ - if (reader == nullptr) { - std::cout << "cannot compare with primal, the reader is null " << std::endl; - return; - } - auto * lps = reader->create_solver(false); - lps->find_maximal_solution(); - std::unordered_map sol = get_solution_map(lps, *reader); - std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; - return; - } - stopwatch sw; - sw.start(); - lp_status status = solver->solve(); - std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; - if (solver->get_status() == lp_status::INFEASIBLE) { - explanation evidence; - solver->get_infeasibility_explanation(evidence); - } - if (args_parser.option_is_used("--randomize_lar")) { - if (solver->get_status() != lp_status::OPTIMAL) { - std::cout << "cannot check randomize on an infeazible problem" << std::endl; - return; - } - std::cout << "checking randomize" << std::endl; - vector all_vars; - for (unsigned j = 0; j < solver->number_of_vars(); j++) - all_vars.push_back(j); - - unsigned m = all_vars.size(); - if (m > 100) - m = 100; - - var_index *vars = new var_index[m]; - for (unsigned i = 0; i < m; i++) - vars[i]=all_vars[i]; - - solver->random_update(m, vars); - delete []vars; - } -} -lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { - if (args_parser.option_is_used("--smt")) { - smt_reader reader(file_name); - reader.read(); - if (!reader.is_ok()){ - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); - } - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser) { - lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); - mps_reader reader(file_name); - mps_reader * mps_reader = nullptr; - reader.read(); - if (reader.is_ok()) { - mps_reader = & reader; - run_lar_solver(args_parser, solver, mps_reader); - } - delete solver; -} vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); @@ -2733,23 +2093,6 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } -void test_lar_solver(argument_parser & args_parser) { - - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - test_lar_on_file(file_name, args_parser); - return; - } - - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - test_lar_on_file(fn, args_parser); - return; - } - - std::cout << "give option --file or --filelist to test_lar_solver\n"; -} void test_numeric_pair() { numeric_pair a; @@ -3934,22 +3277,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("--test_mpq")) { - test_rationals(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np")) { - test_rationals_no_numeric_pairs(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np_plus")) { - test_rationals_no_numeric_pairs_plus(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_int_set")) { test_int_set(); @@ -3967,29 +3294,8 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } -#ifdef Z3DEBUG - if (args_parser.option_is_used("--test_swaps")) { - square_sparse_matrix m(10, 0); - fill_matrix(m); - test_swap_rows_with_permutation(m); - test_swap_cols_with_permutation(m); - return finalize(0); - } -#endif - if (args_parser.option_is_used("--test_perm")) { - test_permutations(); - return finalize(0); - } - if (args_parser.option_is_used("--test_file_directory")) { - test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); - return finalize(0); - } - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - solve_mps(fn, args_parser); - return finalize(0); - } + + if (args_parser.option_is_used("-tbq")) { test_binary_priority_queue(); @@ -3997,100 +3303,6 @@ void test_lp_local(int argn, char**argv) { return finalize(ret); } -#ifdef Z3DEBUG - lp_settings settings; - update_settings(args_parser, settings); - if (args_parser.option_is_used("--test_lu")) { - test_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_small_lu")) { - test_small_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--lar")){ - std::cout <<"calling test_lar_solver" << std::endl; - test_lar_solver(args_parser); - return finalize(0); - } - - - - if (args_parser.option_is_used("--test_larger_lu")) { - test_larger_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_larger_lu_with_holes")) { - test_larger_lu_with_holes(settings); - ret = 0; - return finalize(ret); - } -#endif - if (args_parser.option_is_used("--eti")) { - test_evidence_for_total_inf_simple(args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--maximize_term")) { - test_maximize_term(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_lp_0")) { - test_lp_0(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--smap")) { - test_stacked(); - ret = 0; - return finalize(ret); - } - if (args_parser.option_is_used("--term")) { - test_term(); - ret = 0; - return finalize(ret); - } - unsigned time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - bool dual = args_parser.option_is_used("--dual"); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - solve_mps(file_name, args_parser.option_is_used("--min"), time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--solve_some_mps")) { -#ifndef _WINDOWS - solve_some_mps(args_parser); -#endif - ret = 0; - return finalize(ret); - } - // lp::ccc = 0; - return finalize(0); - test_init_U(); - test_replace_column(); -#ifdef Z3DEBUG - square_sparse_matrix_with_permutations_test(); - test_dense_matrix(); - test_swap_operations(); - test_permutations(); - test_pivot_like_swaps_and_pivot(); -#endif - tst1(); - std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 2ab0c1ea69a..ee41a418aef 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -20,7 +20,6 @@ Revision History: #pragma once -// reads an MPS file representing a Mixed Integer Program #include #include #include @@ -31,7 +30,6 @@ Revision History: #include #include #include -#include "math/lp/mps_reader.h" #include "math/lp/ul_pair.h" #include "math/lp/lar_constraints.h" #include From cd24c9973945b1338784c43c1b4dc5d2e760534f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:26:06 -0800 Subject: [PATCH 460/597] remove a lp_primal_simplex.cpp from CMakeLists --- src/math/lp/CMakeLists.txt | 1 - src/test/lp/lp.cpp | 33 --------------------------------- 2 files changed, 34 deletions(-) diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 9f0fae6bcca..30724fcf729 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -22,7 +22,6 @@ z3_add_component(lp lp_dual_core_solver.cpp lp_dual_simplex.cpp lp_primal_core_solver.cpp - lp_primal_simplex.cpp lp_settings.cpp lp_solver.cpp lu.cpp diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 984c7b4bb0c..5193c6167c5 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1459,16 +1459,6 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { } } -template -void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { - if (time_limit > 0) - solver->set_time_limit(time_limit); - - if (look_for_min) - solver->flip_costs(); - - update_settings(args_parser, solver->settings()); -} bool values_are_one_percent_close(double a, double b); @@ -1572,29 +1562,6 @@ void add_random_cost(lp_primal_simplex * solver, int cols) { } } -lp_primal_simplex * generate_random_solver() { - int rows = get_random_rows(); - int cols = get_random_columns(); - auto * solver = new lp_primal_simplex(); - for (int i = 0; i < rows; i++) { - add_random_row(solver, cols, i); - } - add_random_cost(solver, cols); - return solver; -} - - - -void random_test_on_i(unsigned i) { - if (i % 1000 == 0) { - std::cout << "."; - } - srand(i); - auto *solver = generate_random_solver(); - solver->find_maximal_solution(); - // std::cout << lp_status_to_string(solver->get_status()) << std::endl; - delete solver; -} void random_test() { for (unsigned i = 0; i < std::numeric_limits::max(); i++) { From 8db2f1409b2c1a17402ac8c3911b83308f252d86 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:27:57 -0800 Subject: [PATCH 461/597] lp_dual_simplex.cpp removed from CMakeLists.txt --- src/math/lp/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 30724fcf729..41813b10434 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,7 +20,6 @@ z3_add_component(lp lar_core_solver.cpp lp_core_solver_base.cpp lp_dual_core_solver.cpp - lp_dual_simplex.cpp lp_primal_core_solver.cpp lp_settings.cpp lp_solver.cpp From a44772424c75a2b5b9219cbdb6799c46602df9f4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:30:15 -0800 Subject: [PATCH 462/597] more removals --- src/test/lp/lp.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 5193c6167c5..9e042519482 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1563,17 +1563,6 @@ void add_random_cost(lp_primal_simplex * solver, int cols) { } -void random_test() { - for (unsigned i = 0; i < std::numeric_limits::max(); i++) { - try { - random_test_on_i(i); - } - catch (const char * error) { - std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; - break; - } - } -} #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { From 2ec09944d726b2bba610423692e2696c729a3b68 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:32:44 -0800 Subject: [PATCH 463/597] removals --- src/math/lp/CMakeLists.txt | 1 - src/test/lp/lp.cpp | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 41813b10434..5719de44fae 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -22,7 +22,6 @@ z3_add_component(lp lp_dual_core_solver.cpp lp_primal_core_solver.cpp lp_settings.cpp - lp_solver.cpp lu.cpp lp_utils.cpp matrix.cpp diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 9e042519482..c2b297e6b50 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1549,21 +1549,6 @@ int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } -void add_random_row(lp_primal_simplex * solver, int cols, int row) { - solver->add_constraint(lp_relation::Greater_or_equal, 1, row); - for (int i = 0; i < cols; i++) { - solver->set_row_column_coefficient(row, i, get_random_int()); - } -} - -void add_random_cost(lp_primal_simplex * solver, int cols) { - for (int i = 0; i < cols; i++) { - solver->set_cost_for_column(i, get_random_int()); - } -} - - - #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); From d2e8297d4142f9b58c9bdd16293dae2bdb72af37 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:38:47 -0800 Subject: [PATCH 464/597] remove includes of lp_dual_simplex --- src/math/lp/static_matrix.cpp | 1 - src/sat/smt/arith_sls.h | 1 - src/smt/theory_lra.cpp | 1 - src/test/lp/smt_reader.h | 1 - 4 files changed, 4 deletions(-) diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 571e9b1d06d..cde96277b5e 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,7 +24,6 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_dual_core_solver.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index af3a462340b..f5044187195 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -21,7 +21,6 @@ Module Name: #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d61910ff20e..4180f9ec344 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -21,7 +21,6 @@ #include "util/stopwatch.h" #include "math/lp/lp_solver.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index ee41a418aef..6060370a810 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -24,7 +24,6 @@ Revision History: #include #include #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lar_solver.h" #include #include From 8989e10e7141d52d9696b5c1a39100e16fb0e6e1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:41:30 -0800 Subject: [PATCH 465/597] rm lp_dual_simplex --- src/math/lp/lp_dual_simplex.cpp | 24 -- src/math/lp/lp_dual_simplex.h | 93 -------- src/math/lp/lp_dual_simplex_def.h | 376 ------------------------------ src/sat/smt/arith_solver.h | 1 - 4 files changed, 494 deletions(-) delete mode 100644 src/math/lp/lp_dual_simplex.cpp delete mode 100644 src/math/lp/lp_dual_simplex.h delete mode 100644 src/math/lp/lp_dual_simplex_def.h diff --git a/src/math/lp/lp_dual_simplex.cpp b/src/math/lp/lp_dual_simplex.cpp deleted file mode 100644 index aaf612f5696..00000000000 --- a/src/math/lp/lp_dual_simplex.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_dual_simplex_def.h" -template lp::mpq lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); -template double lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_dual_simplex.h b/src/math/lp/lp_dual_simplex.h deleted file mode 100644 index 75ef87492f2..00000000000 --- a/src/math/lp/lp_dual_simplex.h +++ /dev/null @@ -1,93 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_dual_core_solver.h" -namespace lp { - -template -class lp_dual_simplex: public lp_solver { - lp_dual_core_solver * m_core_solver; - vector m_b_copy; - vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver - vector m_column_types_of_core_solver; - vector m_column_types_of_logicals; - vector m_can_enter_basis; -public: - ~lp_dual_simplex() override { - delete m_core_solver; - } - - lp_dual_simplex() : m_core_solver(nullptr) {} - - - void decide_on_status_after_stage1(); - - void fix_logical_for_stage2(unsigned j); - - void fix_structural_for_stage2(unsigned j); - - void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - - void restore_right_sides(); - - void solve_for_stage2(); - - void fill_x_with_zeros(); - - void stage1(); - - void stage2(); - - void fill_first_stage_solver_fields(); - - column_type get_column_type(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); - - void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); - - void set_type_for_logical(unsigned j, column_type col_type) { - this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; - } - - void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial); - - void augment_matrix_A_and_fill_x_and_allocate_some_fields(); - - - - void copy_m_b_aside_and_set_it_to_zeros(); - - void find_maximal_solution() override; - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; -}; -} diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h deleted file mode 100644 index 8af9d87c104..00000000000 --- a/src/math/lp/lp_dual_simplex_def.h +++ /dev/null @@ -1,376 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "math/lp/lp_dual_simplex.h" -namespace lp{ - -template void lp_dual_simplex::decide_on_status_after_stage1() { - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - this->m_status = lp_status::FEASIBLE; - } else { - this->m_status = lp_status::UNBOUNDED; - } - break; - case lp_status::DUAL_UNBOUNDED: - lp_unreachable(); - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { - lp_assert(j >= this->number_of_core_structurals()); - switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::fixed; - m_can_enter_basis[j] = false; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - switch (ci->get_column_type()) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - case column_type::upper_bound: - lp_unreachable(); - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::boxed; - m_can_enter_basis[j] = true; - break; - case column_type::free_column: - m_can_enter_basis[j] = true; - m_column_types_of_core_solver[j] = column_type::free_column; - break; - default: - lp_unreachable(); - } - // T cost_was = this->m_costs[j]; - this->set_scaled_cost(j); -} - -template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { - fix_logical_for_stage2(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fix_structural_for_stage2(j); - } -} - -template void lp_dual_simplex::restore_right_sides() { - unsigned i = this->m_A->row_count(); - while (i--) { - this->m_b[i] = m_b_copy[i]; - } -} - -template void lp_dual_simplex::solve_for_stage2() { - m_core_solver->restore_non_basis(); - m_core_solver->solve_yB(m_core_solver->m_y); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - m_core_solver->set_status(lp_status::FEASIBLE); - m_core_solver->solve(); - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - this->m_status = lp_status::OPTIMAL; - break; - case lp_status::DUAL_UNBOUNDED: - this->m_status = lp_status::INFEASIBLE; - break; - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } - this->m_second_stage_iterations = m_core_solver->total_iterations(); - this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); -} - -template void lp_dual_simplex::fill_x_with_zeros() { - unsigned j = this->m_A->column_count(); - while (j--) { - this->m_x[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::stage1() { - lp_assert(m_core_solver == nullptr); - this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - m_core_solver = new lp_dual_core_solver( - *this->m_A, - m_can_enter_basis, - this->m_b, // the right side vector - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types_of_core_solver, - this->m_lower_bounds, - this->m_upper_bounds, - this->m_settings, - *this); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - // skipping stage 1 - m_core_solver->set_status(lp_status::OPTIMAL); - m_core_solver->set_total_iterations(0); - } else { - m_core_solver->solve(); - } - decide_on_status_after_stage1(); - this->m_first_stage_iterations = m_core_solver->total_iterations(); -} - -template void lp_dual_simplex::stage2() { - unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - restore_right_sides(); - solve_for_stage2(); -} - -template void lp_dual_simplex::fill_first_stage_solver_fields() { - unsigned slack_var = this->number_of_core_structurals(); - unsigned artificial = this->number_of_core_structurals() + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); - } - fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); -} - -template column_type lp_dual_simplex::get_column_type(unsigned j) { - lp_assert(j < this->m_A->column_count()); - if (j >= this->number_of_core_structurals()) { - return m_column_types_of_logicals[j - this->number_of_core_structurals()]; - } - return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { - // see 4.7 in the dissertation of Achim Koberstein - lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != - this->m_core_solver_columns_to_external_columns.end()); - - T free_bound = T(1e4); // see 4.8 - unsigned jj = this->m_core_solver_columns_to_external_columns[j]; - lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = this->m_map_from_var_index_to_column_info[jj]; - switch (ci->get_column_type()) { - case column_type::upper_bound: { - std::stringstream s; - s << "unexpected bound type " << j << " " - << column_type_to_string(get_column_type(j)); - throw_exception(s.str()); - break; - } - case column_type::lower_bound: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - break; - } - case column_type::free_column: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_upper_bounds[j] = free_bound; - this->m_lower_bounds[j] = -free_bound; - break; - } - case column_type::boxed: - m_can_enter_basis[j] = false; - this->m_costs[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? - break; - default: - lp_unreachable(); - } - m_column_types_of_core_solver[j] = column_type::boxed; -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { - this->m_costs[j] = 0; - lp_assert(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { - m_column_types_of_core_solver[j] = column_type::boxed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - } else { - m_column_types_of_core_solver[j] = column_type::fixed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { // go over logicals here - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); - } -} - -template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - switch (constraint.m_relation) { - case Equal: // no slack variable here - set_type_for_logical(artificial, column_type::fixed); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - (*this->m_A)(row, artificial) = numeric_traits::one(); - artificial++; - break; - - case Greater_or_equal: - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - if (rs > 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put a slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = numeric_traits::one(); - if (rs < 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - } -} - -template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { - this->count_slacks_and_artificials(); - this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); - unsigned n = this->m_A->column_count(); - this->m_column_types_of_core_solver.resize(n); - m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); - this->m_costs.resize(n); - this->m_upper_bounds.resize(n); - this->m_lower_bounds.resize(n); - m_can_enter_basis.resize(n); - this->m_basis.resize(this->m_A->row_count()); -} - - - -template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { - for (unsigned i = 0; i < this->m_b.size(); i++) { - m_b_copy.push_back(this->m_b[i]); - this->m_b[i] = numeric_traits::zero(); // preparing for the first stage - } -} - -template void lp_dual_simplex::find_maximal_solution(){ - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->flip_costs(); // do it for now, todo ( remove the flipping) - - this->cleanup(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->fill_matrix_A_and_init_right_side(); - this->fill_m_b(); - this->scale(); - augment_matrix_A_and_fill_x_and_allocate_some_fields(); - fill_first_stage_solver_fields(); - copy_m_b_aside_and_set_it_to_zeros(); - stage1(); - if (this->m_status == lp_status::FEASIBLE) { - stage2(); - } -} - - -template T lp_dual_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return -ret; // we flip costs for now -} -} diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 9ae671c162b..eac73c719d5 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -21,7 +21,6 @@ Module Name: #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" From 2dd30fa350a78c9ef7419f8ae8ffde2cc6090000 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:44:50 -0800 Subject: [PATCH 466/597] rm lp_primal_simplex --- src/math/lp/lp_primal_simplex.cpp | 35 --- src/math/lp/lp_primal_simplex.h | 106 -------- src/math/lp/lp_primal_simplex_def.h | 367 ---------------------------- src/sat/smt/arith_sls.h | 1 - src/sat/smt/arith_solver.h | 1 - src/smt/theory_lra.cpp | 1 - src/test/lp/lp.cpp | 1 - src/test/lp/smt_reader.h | 1 - 8 files changed, 513 deletions(-) delete mode 100644 src/math/lp/lp_primal_simplex.cpp delete mode 100644 src/math/lp/lp_primal_simplex.h delete mode 100644 src/math/lp/lp_primal_simplex_def.h diff --git a/src/math/lp/lp_primal_simplex.cpp b/src/math/lp/lp_primal_simplex.cpp deleted file mode 100644 index 634f529009b..00000000000 --- a/src/math/lp/lp_primal_simplex.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_primal_simplex_def.h" -template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template double lp::lp_primal_simplex::get_current_cost() const; -template double lp::lp_primal_simplex::get_column_value(unsigned int) const; -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::mpq lp::lp_primal_simplex::get_current_cost() const; -template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; -template void lp::lp_primal_simplex::find_maximal_solution(); -template void lp::lp_primal_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_primal_simplex.h b/src/math/lp/lp_primal_simplex.h deleted file mode 100644 index 77e12d0888e..00000000000 --- a/src/math/lp/lp_primal_simplex.h +++ /dev/null @@ -1,106 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include -#include -#include -#include "math/lp/lp_utils.h" -#include "math/lp/column_info.h" -#include "math/lp/lp_primal_core_solver.h" -#include "math/lp/lp_solver.h" -namespace lp { -template -class lp_primal_simplex: public lp_solver { - lp_primal_core_solver * m_core_solver; - vector m_lower_bounds; -private: - unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } - - void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); - - void init_buffer(unsigned k, vector & r); - - void refactor(); - - void set_scaled_costs(); -public: - lp_primal_simplex(): m_core_solver(nullptr) {} - - column_info * get_or_create_column_info(unsigned column); - - void set_status(lp_status status) { - this->m_status = status; - } - - lp_status get_status() { - return this->m_status; - } - - void fill_acceptable_values_for_x(); - - - void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); - - void fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial); - - - - - void set_core_solver_bounds(); - - void find_maximal_solution() override; - - void fill_A_x_and_basis_for_stage_one_total_inf(); - - void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); - - void solve_with_total_inf(); - - - ~lp_primal_simplex() override; - - bool bounds_hold(std::unordered_map const & solution); - - T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraints_hold(std::unordered_map const & solution); - - - T * get_array_from_map(std::unordered_map const & solution); - - bool solution_is_feasible(std::unordered_map const & solution) { - return bounds_hold(solution) && row_constraints_hold(solution); - } - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; - - -}; -} diff --git a/src/math/lp/lp_primal_simplex_def.h b/src/math/lp/lp_primal_simplex_def.h deleted file mode 100644 index 7ffe819b207..00000000000 --- a/src/math/lp/lp_primal_simplex_def.h +++ /dev/null @@ -1,367 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "util/vector.h" -#include "math/lp/lp_primal_simplex.h" - -namespace lp { -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { - unsigned slack_var = original_number_of_columns; - unsigned artificial = original_number_of_columns + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); - } -} - -template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { - for (unsigned i = 0; i < k; i++) { - r[i] = 0; - } - r[k] = 1; - for (unsigned i = this->row_count() -1; i > k; i--) { - r[i] = 0; - } -} - -template void lp_primal_simplex::refactor() { - m_core_solver->init_lu(); - if (m_core_solver->factorization()->get_status() != LU_status::OK) { - throw_exception("cannot refactor"); - } -} - -template void lp_primal_simplex::set_scaled_costs() { - unsigned j = this->number_of_core_structurals(); - while (j-- > 0) { - this->set_scaled_cost(j); - } -} - -template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { - auto it = this->m_columns.find(column); - return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; -} - -template void lp_primal_simplex::fill_acceptable_values_for_x() { - for (auto t : this->m_core_solver_columns_to_external_columns) { - this->m_x[t.first] = numeric_traits::zero(); - } -} - - -template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { - bound_is_set[i] = true; - bounds[i] = numeric_traits::zero(); -} - -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row >= 0 && row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - T artificial_cost = - numeric_traits::one(); - switch (constraint.m_relation) { - case Equal: // no slack variable here - this->m_column_types[artificial] = column_type::lower_bound; - this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero - this->m_basis[row] = artificial; - if (rs >= 0) { - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_x[artificial] = rs; - } else { - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_x[artificial] = - rs; - } - artificial++; - break; - - case Greater_or_equal: - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - - if (rs > 0) { - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - // adding one artificial - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_basis[row] = artificial; - this->m_x[artificial] = rs; - artificial++; - } else { - // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = - rs; - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = numeric_traits::one(); - - if (rs < 0) { - // adding one artificial - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_x[artificial] = - rs; - this->m_basis[row] = artificial++; - } else { - // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = rs; - } - slack_var++; - break; - } -} - - - - - -template void lp_primal_simplex::set_core_solver_bounds() { - unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; - this->m_column_types.resize(total_vars); - this->m_upper_bounds.resize(total_vars); - for (auto cit : this->m_map_from_var_index_to_column_info) { - column_info * ci = cit.second; - unsigned j = ci->get_column_index(); - if (!is_valid(j)) - continue; // the variable is not mapped to a column - switch (this->m_column_types[j] = ci->get_column_type()){ - case column_type::fixed: - this->m_upper_bounds[j] = numeric_traits::zero(); - break; - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - break; - - default: break; // do nothing - } - } -} - - -template void lp_primal_simplex::find_maximal_solution() { - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->cleanup(); - this->fill_matrix_A_and_init_right_side(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->m_x.resize(this->m_A->column_count()); - this->fill_m_b(); - this->scale(); - fill_acceptable_values_for_x(); - this->count_slacks_and_artificials(); - set_core_solver_bounds(); - solve_with_total_inf(); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { - for (unsigned row = 0; row < this->row_count(); row++) - fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { - lp_assert(row < this->row_count()); - auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); - lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); - unsigned ext_row = ext_row_it->second; - auto constr_it = this->m_constraints.find(ext_row); - lp_assert(constr_it != this->m_constraints.end()); - auto & constraint = constr_it->second; - unsigned j = this->m_A->column_count(); // j is a slack variable - this->m_A->add_column(); - // we need to bring the program to the form Ax = b - this->m_basis[row] = j; - switch (constraint.m_relation) { - case Equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::fixed; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - - case Greater_or_equal: - this->m_x[j] = - this->m_b[row]; - (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = zero_of_type(); - break; - case Less_or_equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - default: - lp_unreachable(); - } -} - -template void lp_primal_simplex::solve_with_total_inf() { - int total_vars = this->m_A->column_count() + this->row_count(); - if (total_vars == 0) { - this->m_status = lp_status::OPTIMAL; - return; - } - m_lower_bounds.clear(); - m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero - this->m_x.resize(total_vars, numeric_traits::zero()); - this->m_basis.resize(this->row_count()); - this->m_costs.clear(); - this->m_costs.resize(total_vars, zero_of_type()); - fill_A_x_and_basis_for_stage_one_total_inf(); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - set_scaled_costs(); - - m_core_solver = new lp_primal_core_solver(*this->m_A, - this->m_b, - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types, - m_lower_bounds, - this->m_upper_bounds, - this->m_settings, *this); - m_core_solver->solve(); - this->set_status(m_core_solver->get_status()); - this->m_total_iterations = m_core_solver->total_iterations(); -} - - -template lp_primal_simplex::~lp_primal_simplex() { - delete m_core_solver; -} - -template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { - for (auto it : this->m_map_from_var_index_to_column_info) { - auto sol_it = solution.find(it.second->get_name()); - if (sol_it == solution.end()) { - std::stringstream s; - s << "cannot find column " << it.first << " in solution"; - throw_exception(s.str() ); - } - - if (!it.second->bounds_hold(sol_it->second)) { - it.second->bounds_hold(sol_it->second); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { - auto it = this->m_A_values.find(i); - if (it == this->m_A_values.end()) { - std::stringstream s; - s << "cannot find row " << i; - throw_exception(s.str() ); - } - T ret = numeric_traits::zero(); - for (auto & pair : it->second) { - auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); - lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = cit->second; - auto sol_it = solution.find(ci->get_name()); - lp_assert(sol_it != solution.end()); - T column_val = sol_it->second; - if (out != nullptr) { - (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; - } - ret += pair.second * column_val; - } - if (out != nullptr) { - (*out) << " = " << ret << std::endl; - } - return ret; -} - -template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { - T row_val = get_row_value(i, solution, out); - auto & constraint = this->m_constraints[i]; - T rs = constraint.m_rs; - bool print = out != nullptr; - switch (constraint.m_relation) { - case Equal: - if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { - if (print) { - (*out) << "should be = " << rs << std::endl; - } - return false; - } - return true; - case Greater_or_equal: - if (numeric_traits::get_double(row_val - rs) < -0.00001) { - if (print) { - (*out) << "should be >= " << rs << std::endl; - } - return false; - } - return true;; - - case Less_or_equal: - if (numeric_traits::get_double(row_val - rs) > 0.00001) { - if (print) { - (*out) << "should be <= " << rs << std::endl; - } - return false; - } - return true;; - } - lp_unreachable(); - return false; // it is unreachable -} - -template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { - for (auto it : this->m_A_values) { - if (!row_constraint_holds(it.first, solution, nullptr)) { - row_constraint_holds(it.first, solution, nullptr); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return ret; -} -} diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index f5044187195..b0b4fb48bb3 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -20,7 +20,6 @@ Module Name: #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index eac73c719d5..3152485ec2e 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -20,7 +20,6 @@ Module Name: #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 4180f9ec344..a654366a2ca 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -20,7 +20,6 @@ --*/ #include "util/stopwatch.h" #include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c2b297e6b50..78abf1a6f4d 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -33,7 +33,6 @@ #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_primal_simplex.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 6060370a810..75edb23b987 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -23,7 +23,6 @@ Revision History: #include #include #include -#include "math/lp/lp_primal_simplex.h" #include "math/lp/lar_solver.h" #include #include From 5e4bca3d26d5f86edce6baf1331d6e4d72c00ae0 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:58:25 -0800 Subject: [PATCH 467/597] small removals --- src/sat/smt/arith_sls.h | 1 - src/test/lp/test_file_reader.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index b0b4fb48bb3..09a56c84efd 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -19,7 +19,6 @@ Module Name: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/test_file_reader.h b/src/test/lp/test_file_reader.h index 8f461ea1c26..36b27374023 100644 --- a/src/test/lp/test_file_reader.h +++ b/src/test/lp/test_file_reader.h @@ -27,7 +27,6 @@ Revision History: #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" namespace lp { From ff1dc0424ca73b70c314f710737213ea30a3fefe Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 16:32:49 -0800 Subject: [PATCH 468/597] rm lp_solver --- src/math/lp/indexed_value.h | 11 - src/math/lp/lar_core_solver.h | 30 +- src/math/lp/lp_primal_core_solver.h | 1 - src/math/lp/lp_solver.cpp | 55 --- src/math/lp/lp_solver.h | 260 ------------- src/math/lp/lp_solver_def.h | 571 ---------------------------- src/sat/smt/arith_solver.h | 2 +- src/smt/theory_lra.cpp | 1 - 8 files changed, 2 insertions(+), 929 deletions(-) delete mode 100644 src/math/lp/lp_solver.cpp delete mode 100644 src/math/lp/lp_solver.h delete mode 100644 src/math/lp/lp_solver_def.h diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index c4837647099..92d8f2adf1f 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -43,15 +43,4 @@ class indexed_value { m_value = val; } }; -#ifdef Z3DEBUG -template -bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - const X & v = w[i]; - if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) - return false; - } - return true; -} -#endif } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 8a6c64ef00f..de8fe68adb5 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -651,35 +651,7 @@ class lar_core_solver { } } - void scale_problem_for_doubles( - static_matrix& A, - vector & lower_bounds, - vector & upper_bounds) { - vector column_scale_vector; - vector right_side_vector(A.column_count()); - settings().reps_in_scaler = 5; - scaler scaler(right_side_vector, - A, - settings().scaling_minimum, - settings().scaling_maximum, - column_scale_vector, - settings()); - if (! scaler.scale()) { - // the scale did not succeed, unscaling - A.clear(); - create_double_matrix(A); - } else { - for (unsigned j = 0; j < A.column_count(); j++) { - if (m_r_solver.column_has_upper_bound(j)) { - upper_bounds[j] /= column_scale_vector[j]; - } - if (m_r_solver.column_has_lower_bound(j)) { - lower_bounds[j] /= column_scale_vector[j]; - } - } - } - - } + // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 4b6163df810..a60395ab014 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -30,7 +30,6 @@ Revision History: #include #include #include "math/lp/lu.h" -#include "math/lp/lp_solver.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" diff --git a/src/math/lp/lp_solver.cpp b/src/math/lp/lp_solver.cpp deleted file mode 100644 index fc95140982b..00000000000 --- a/src/math/lp/lp_solver.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "math/lp/lp_solver_def.h" -template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template double lp::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/math/lp/lp_solver.h b/src/math/lp/lp_solver.h deleted file mode 100644 index ab16a686fd6..00000000000 --- a/src/math/lp/lp_solver.h +++ /dev/null @@ -1,260 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/column_info.h" -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include "math/lp/scaler.h" -#include "math/lp/bound_analyzer_on_row.h" -namespace lp { -enum lp_relation { - Less_or_equal, - Equal, - Greater_or_equal -}; - -template -struct lp_constraint { - X m_rs; // right side of the constraint - lp_relation m_relation; - lp_constraint() {} // empty constructor - lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} -}; - - -template -class lp_solver : public column_namer { - column_info * get_or_create_column_info(unsigned column); - -protected: - T get_column_cost_value(unsigned j, column_info * ci) const; -public: - unsigned m_total_iterations; - static_matrix* m_A; // this is the matrix of constraints - vector m_b; // the right side vector - unsigned m_first_stage_iterations; - unsigned m_second_stage_iterations; - std::unordered_map> m_constraints; - std::unordered_map*> m_map_from_var_index_to_column_info; - std::unordered_map > m_A_values; - std::unordered_map m_names_to_columns; // don't have to use it - std::unordered_map m_external_rows_to_core_solver_rows; - std::unordered_map m_core_solver_rows_to_external_rows; - std::unordered_map m_core_solver_columns_to_external_columns; - vector m_column_scale; - std::unordered_map m_name_map; - unsigned m_artificials; - unsigned m_slacks; - vector m_column_types; - vector m_costs; - vector m_x; - vector m_upper_bounds; - vector m_basis; - vector m_nbasis; - vector m_heading; - - - lp_status m_status; - - lp_settings m_settings; - lp_solver(): - m_A(nullptr), // this is the matrix of constraints - m_first_stage_iterations (0), - m_second_stage_iterations (0), - m_artificials (0), - m_slacks (0), - m_status(lp_status::UNKNOWN) - {} - - unsigned row_count() const { return this->m_A->row_count(); } - - void add_constraint(lp_relation relation, T right_side, unsigned row_index); - - void set_cost_for_column(unsigned column, T column_cost) { - get_or_create_column_info(column)->set_cost(column_cost); - } - std::string get_variable_name(unsigned j) const override; - - void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { - m_A_values[row][column] = val; - } - // returns the current cost - virtual T get_current_cost() const = 0; - // do not have to call it - void give_symbolic_name_to_column(std::string name, unsigned column); - - virtual T get_column_value(unsigned column) const = 0; - - T get_column_value_by_name(std::string name) const; - - // returns -1 if not found - virtual int get_column_index_by_name(std::string name) const; - - void set_lower_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_lower_bound(bound); - } - - void set_upper_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_upper_bound(bound); - } - - void unset_lower_bound(unsigned i) { - get_or_create_column_info(i)->unset_lower_bound(); - } - - void unset_upper_bound(unsigned i) { - get_or_create_column_info(i)->unset_upper_bound(); - } - - void set_fixed_value(unsigned i, T val) { - column_info *ci = get_or_create_column_info(i); - ci->set_fixed_value(val); - } - - void unset_fixed_value(unsigned i) { - get_or_create_column_info(i)->unset_fixed(); - } - - lp_status get_status() const { - return m_status; - } - - void set_status(lp_status st) { - m_status = st; - } - - - ~lp_solver() override; - - void flip_costs(); - - virtual void find_maximal_solution() = 0; - void set_time_limit(unsigned time_limit_in_seconds) { - m_settings.time_limit = time_limit_in_seconds; - } - - -protected: - bool problem_is_empty(); - - void scale(); - - - void print_rows_scale_stats(std::ostream & out); - - void print_columns_scale_stats(std::ostream & out); - - void print_row_scale_stats(unsigned i, std::ostream & out); - - void print_column_scale_stats(unsigned j, std::ostream & out); - - void print_scale_stats(std::ostream & out); - - void get_max_abs_in_row(std::unordered_map & row_map); - - void pin_vars_down_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, - numeric_traits::one()); - } - - void pin_vars_up_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, numeric_traits::one()); - } - - void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); - - bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); - - bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); - - bool row_is_zero(std::unordered_map & row); - - bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); - - // analyse possible max and min values that are derived from var boundaries - // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. - // Then we know what values of the variables are. For each positive coeff of the row it has to be - // the low boundary of the var and for a negative - the upper. - - // this routing also pins the variables to the boundaries - bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); - - void remove_fixed_or_zero_columns(); - - void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); - - unsigned try_to_remove_some_rows(); - - void cleanup(); - - void map_external_rows_to_core_solver_rows(); - - void map_external_columns_to_core_solver_columns(); - - unsigned number_of_core_structurals() { - return static_cast(m_core_solver_columns_to_external_columns.size()); - } - - void restore_column_scales_to_one() { - for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); - } - - void unscale(); - - void fill_A_from_A_values(); - - void fill_matrix_A_and_init_right_side(); - - void count_slacks_and_artificials(); - - void count_slacks_and_artificials_for_row(unsigned i); - - T lower_bound_shift_for_row(unsigned i); - - void fill_m_b(); - - T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; - void set_scaled_cost(unsigned j); - void print_statistics_on_A(std::ostream & out) { - out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; - } - -public: - lp_settings & settings() { return m_settings;} - void print_model(std::ostream & s) const { - s << "objective = " << get_current_cost() << std::endl; - s << "column values\n"; - for (auto & it : m_names_to_columns) { - s << it.first << " = " << get_column_value(it.second) << std::endl; - } - } -}; -} diff --git a/src/math/lp/lp_solver_def.h b/src/math/lp/lp_solver_def.h deleted file mode 100644 index 191832a2487..00000000000 --- a/src/math/lp/lp_solver_def.h +++ /dev/null @@ -1,571 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_solver.h" -namespace lp { -template column_info * lp_solver::get_or_create_column_info(unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; -} - -template -std::string lp_solver::get_variable_name(unsigned j) const { // j here is the core solver index - if (!m_settings.print_external_var_name()) - return std::string("j")+T_to_string(j); - auto it = this->m_core_solver_columns_to_external_columns.find(j); - if (it == this->m_core_solver_columns_to_external_columns.end()) - return std::string("x")+T_to_string(j); - unsigned external_j = it->second; - auto t = this->m_map_from_var_index_to_column_info.find(external_j); - if (t == this->m_map_from_var_index_to_column_info.end()) { - return std::string("x") +T_to_string(external_j); - } - return t->second->get_name(); -} - -template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { - if (ci->is_fixed()) { - return ci->get_cost() * ci->get_fixed_value(); - } - return ci->get_cost() * get_column_value(j); -} -template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { - lp_assert(m_constraints.find(row_index) == m_constraints.end()); - lp_constraint cs(right_side, relation); - m_constraints[row_index] = cs; -} - -template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - column_info *ci; - if (it == m_map_from_var_index_to_column_info.end()){ - m_map_from_var_index_to_column_info[column] = ci = new column_info; - } else { - ci = it->second; - } - ci->set_name(name); - m_names_to_columns[name] = column; -} - - -template T lp_solver::get_column_value_by_name(std::string name) const { - auto it = m_names_to_columns.find(name); - if (it == m_names_to_columns.end()) { - std::stringstream s; - s << "get_column_value_by_name " << name; - throw_exception(s.str()); - } - return get_column_value(it -> second); -} - -// returns -1 if not found -template int lp_solver::get_column_index_by_name(std::string name) const { - auto t = m_names_to_columns.find(name); - if (t == m_names_to_columns.end()) { - return -1; - } - return t->second; -} - - -template lp_solver::~lp_solver(){ - delete m_A; - for (auto t : m_map_from_var_index_to_column_info) { - delete t.second; - } -} - -template void lp_solver::flip_costs() { - for (auto t : m_map_from_var_index_to_column_info) { - column_info *ci = t.second; - ci->set_cost(-ci->get_cost()); - } -} - -template bool lp_solver::problem_is_empty() { - for (auto & c : m_A_values) - if (!c.second.empty()) - return false; - return true; -} - -template void lp_solver::scale() { - if (numeric_traits::precise() || m_settings.use_scaling == false) { - m_column_scale.clear(); - m_column_scale.resize(m_A->column_count(), one_of_type()); - return; - } - - T smin = T(m_settings.scaling_minimum); - T smax = T(m_settings.scaling_maximum); - - scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); - if (!scaler.scale()) { - unscale(); - } -} - - -template void lp_solver::print_rows_scale_stats(std::ostream & out) { - out << "rows max" << std::endl; - for (unsigned i = 0; i < m_A->row_count(); i++) { - print_row_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_columns_scale_stats(std::ostream & out) { - out << "columns max" << std::endl; - for (unsigned i = 0; i < m_A->column_count(); i++) { - print_column_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { - out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; - out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; -} - -template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { - out << "(" << m_A->get_min_abs_in_row(j) << " "; - out << m_A->get_max_abs_in_column(j) << ")"; -} - -template void lp_solver::print_scale_stats(std::ostream & out) { - print_rows_scale_stats(out); - print_columns_scale_stats(out); -} - -template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { - T ret = numeric_traits::zero(); - for (auto jp : row_map) { - T ac = numeric_traits::abs(jp->second); - if (ac > ret) { - ret = ac; - } - } - return ret; -} - -template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { - for (auto t : row) { - unsigned j = t.first; - column_info * ci = m_map_from_var_index_to_column_info[j]; - T a = t.second; - if (a * sign > numeric_traits::zero()) { - lp_assert(ci->upper_bound_is_set()); - ci->set_fixed_value(ci->get_upper_bound()); - } else { - lp_assert(ci->lower_bound_is_set()); - ci->set_fixed_value(ci->get_lower_bound()); - } - } -} - -template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a > numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a < numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::row_is_zero(std::unordered_map & row) { - for (auto & t : row) { - if (!is_zero(t.second)) - return false; - } - return true; -} - -template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (!is_zero(rs)) - m_status = lp_status::INFEASIBLE; - return true; - } - - T lower_bound; - bool lb = get_minimal_row_value(row, lower_bound); - if (lb) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_epsilon - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - T upper_bound; - bool ub = get_maximal_row_value(row, upper_bound); - if (ub) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs > zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - T upper_bound; - if (get_maximal_row_value(row, upper_bound)) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { - T lower_bound; - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs < zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - if (get_minimal_row_value(row, lower_bound)) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - return false; -} - -// analyse possible max and min values that are derived from var boundaries -// Let us say that the we have a "ge" constraint, and the min value is equal to the rs. -// Then we know what values of the variables are. For each positive coeff of the row it has to be -// the low boundary of the var and for a negative - the upper. - -// this routing also pins the variables to the boundaries -template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { - auto & constraint = m_constraints[row_index]; - switch (constraint.m_relation) { - case lp_relation::Equal: - return row_e_is_obsolete(row, row_index); - - case lp_relation::Greater_or_equal: - return row_ge_is_obsolete(row, row_index); - - case lp_relation::Less_or_equal: - return row_le_is_obsolete(row, row_index); - } - lp_unreachable(); - return false; // it is unreachable -} - -template void lp_solver::remove_fixed_or_zero_columns() { - for (auto & i_row : m_A_values) { - remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); - } -} - -template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { - auto & constraint = m_constraints[i]; - vector removed; - for (auto & col : row) { - unsigned j = col.first; - lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); - column_info * ci = m_map_from_var_index_to_column_info[j]; - if (ci->is_fixed()) { - removed.push_back(j); - T aj = col.second; - constraint.m_rs -= aj * ci->get_fixed_value(); - } else { - if (numeric_traits::is_zero(col.second)){ - removed.push_back(j); - } - } - } - - for (auto j : removed) { - row.erase(j); - } -} - -template unsigned lp_solver::try_to_remove_some_rows() { - vector rows_to_delete; - for (auto & t : m_A_values) { - if (row_is_obsolete(t.second, t.first)) { - rows_to_delete.push_back(t.first); - } - - if (m_status == lp_status::INFEASIBLE) { - return 0; - } - } - if (!rows_to_delete.empty()) { - for (unsigned k : rows_to_delete) { - m_A_values.erase(k); - } - } - remove_fixed_or_zero_columns(); - return static_cast(rows_to_delete.size()); -} - -template void lp_solver::cleanup() { - int n = 0; // number of deleted rows - int d; - while ((d = try_to_remove_some_rows()) > 0) - n += d; - - if (n == 1) { - LP_OUT(m_settings, "deleted one row" << std::endl); - } else if (n) { - LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); - } -} - -template void lp_solver::map_external_rows_to_core_solver_rows() { - unsigned size = 0; - for (auto & row : m_A_values) { - m_external_rows_to_core_solver_rows[row.first] = size; - m_core_solver_rows_to_external_rows[size] = row.first; - size++; - } -} - -template void lp_solver::map_external_columns_to_core_solver_columns() { - unsigned size = 0; - for (auto & row : m_A_values) { - for (auto & col : row.second) { - if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { - throw_exception("found fixed column"); - } - unsigned j = col.first; - auto column_info_it = m_map_from_var_index_to_column_info.find(j); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - - auto j_column = column_info_it->second->get_column_index(); - if (!is_valid(j_column)) { // j is a newcomer - m_map_from_var_index_to_column_info[j]->set_column_index(size); - m_core_solver_columns_to_external_columns[size++] = j; - } - } - } -} - -template void lp_solver::unscale() { - delete m_A; - m_A = nullptr; - fill_A_from_A_values(); - restore_column_scales_to_one(); - fill_m_b(); -} - -template void lp_solver::fill_A_from_A_values() { - m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); - for (auto & t : m_A_values) { - auto row_it = m_external_rows_to_core_solver_rows.find(t.first); - lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); - unsigned row = row_it->second; - for (auto k : t.second) { - auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - column_info *ci = column_info_it->second; - unsigned col = ci->get_column_index(); - lp_assert(is_valid(col)); - bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); - if (!col_is_flipped) { - (*m_A)(row, col) = k.second; - } else { - (*m_A)(row, col) = - k.second; - } - } - } -} - -template void lp_solver::fill_matrix_A_and_init_right_side() { - map_external_rows_to_core_solver_rows(); - map_external_columns_to_core_solver_columns(); - lp_assert(m_A == nullptr); - fill_A_from_A_values(); - m_b.resize(m_A->row_count()); -} - -template void lp_solver::count_slacks_and_artificials() { - for (int i = row_count() - 1; i >= 0; i--) { - count_slacks_and_artificials_for_row(i); - } -} - -template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; - switch (constraint.m_relation) { - case Equal: - m_artificials++; - break; - case Greater_or_equal: - m_slacks++; - if (this->m_b[i] > 0) { - m_artificials++; - } - break; - case Less_or_equal: - m_slacks++; - if (this->m_b[i] < 0) { - m_artificials++; - } - break; - } -} - -template T lp_solver::lower_bound_shift_for_row(unsigned i) { - T ret = numeric_traits::zero(); - - auto row = this->m_A_values.find(i); - if (row == this->m_A_values.end()) { - throw_exception("cannot find row"); - } - for (auto col : row->second) { - ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); - } - return ret; -} - -template void lp_solver::fill_m_b() { - for (int i = this->row_count() - 1; i >= 0; i--) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; - auto & constraint = this->m_constraints[external_i]; - this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); - } -} - -template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { - auto cit = this->m_map_from_var_index_to_column_info.find(column); - if (cit == this->m_map_from_var_index_to_column_info.end()) { - return numeric_traits::zero(); - } - - column_info * ci = cit->second; - - if (ci->is_fixed()) { - return ci->get_fixed_value(); - } - - unsigned cj = ci->get_column_index(); - if (cj != static_cast(-1)) { - T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; - if (ci->is_free()) { - return v; - } - if (!ci->is_flipped()) { - return v + ci->get_lower_bound(); - } - - // the flipped case when there is only upper bound - return -v + ci->get_upper_bound(); // - } - - return numeric_traits::zero(); // returns zero for out of boundary columns -} - -template void lp_solver::set_scaled_cost(unsigned j) { - // grab original costs but modify it with the column scales - lp_assert(j < this->m_column_scale.size()); - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - T cost = ci->get_cost(); - if (ci->is_flipped()){ - cost *= T(-1); - } - lp_assert(ci->is_fixed() == false); - this->m_costs[j] = cost * this->m_column_scale[j]; -} -} diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 3152485ec2e..68d5f802592 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -19,7 +19,7 @@ Module Name: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" + #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a654366a2ca..2aa988282d2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -19,7 +19,6 @@ --*/ #include "util/stopwatch.h" -#include "math/lp/lp_solver.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" From 92fe8c59688b89b165287e63cb560a43139e427e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 17:53:00 -0800 Subject: [PATCH 469/597] restore the previous state Signed-off-by: Lev Nachmanson --- src/math/lp/CMakeLists.txt | 3 + src/math/lp/indexed_value.h | 11 + src/math/lp/lar_core_solver.h | 30 +- src/math/lp/lp_dual_simplex.cpp | 24 + src/math/lp/lp_dual_simplex.h | 93 +++ src/math/lp/lp_dual_simplex_def.h | 376 +++++++++++ src/math/lp/lp_primal_core_solver.h | 1 + src/math/lp/lp_primal_simplex.cpp | 35 + src/math/lp/lp_primal_simplex.h | 106 +++ src/math/lp/lp_primal_simplex_def.h | 367 +++++++++++ src/math/lp/lp_solver.cpp | 55 ++ src/math/lp/lp_solver.h | 260 ++++++++ src/math/lp/lp_solver_def.h | 571 ++++++++++++++++ src/math/lp/mps_reader.h | 891 +++++++++++++++++++++++++ src/math/lp/static_matrix.cpp | 1 + src/sat/smt/arith_sls.h | 3 + src/sat/smt/arith_solver.h | 4 +- src/shell/CMakeLists.txt | 1 + src/shell/lp_frontend.cpp | 109 +++ src/shell/lp_frontend.h | 7 + src/shell/main.cpp | 10 +- src/smt/theory_lra.cpp | 3 + src/test/lp/lp.cpp | 984 ++++++++++++++++++++++++++-- src/test/lp/smt_reader.h | 4 + src/test/lp/test_file_reader.h | 1 + 25 files changed, 3879 insertions(+), 71 deletions(-) create mode 100644 src/math/lp/lp_dual_simplex.cpp create mode 100644 src/math/lp/lp_dual_simplex.h create mode 100644 src/math/lp/lp_dual_simplex_def.h create mode 100644 src/math/lp/lp_primal_simplex.cpp create mode 100644 src/math/lp/lp_primal_simplex.h create mode 100644 src/math/lp/lp_primal_simplex_def.h create mode 100644 src/math/lp/lp_solver.cpp create mode 100644 src/math/lp/lp_solver.h create mode 100644 src/math/lp/lp_solver_def.h create mode 100644 src/math/lp/mps_reader.h create mode 100644 src/shell/lp_frontend.cpp create mode 100644 src/shell/lp_frontend.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5719de44fae..9f0fae6bcca 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,8 +20,11 @@ z3_add_component(lp lar_core_solver.cpp lp_core_solver_base.cpp lp_dual_core_solver.cpp + lp_dual_simplex.cpp lp_primal_core_solver.cpp + lp_primal_simplex.cpp lp_settings.cpp + lp_solver.cpp lu.cpp lp_utils.cpp matrix.cpp diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index 92d8f2adf1f..c4837647099 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -43,4 +43,15 @@ class indexed_value { m_value = val; } }; +#ifdef Z3DEBUG +template +bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { + for (unsigned i : w.m_index) { + const X & v = w[i]; + if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) + return false; + } + return true; +} +#endif } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index de8fe68adb5..8a6c64ef00f 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -651,7 +651,35 @@ class lar_core_solver { } } - + void scale_problem_for_doubles( + static_matrix& A, + vector & lower_bounds, + vector & upper_bounds) { + vector column_scale_vector; + vector right_side_vector(A.column_count()); + settings().reps_in_scaler = 5; + scaler scaler(right_side_vector, + A, + settings().scaling_minimum, + settings().scaling_maximum, + column_scale_vector, + settings()); + if (! scaler.scale()) { + // the scale did not succeed, unscaling + A.clear(); + create_double_matrix(A); + } else { + for (unsigned j = 0; j < A.column_count(); j++) { + if (m_r_solver.column_has_upper_bound(j)) { + upper_bounds[j] /= column_scale_vector[j]; + } + if (m_r_solver.column_has_lower_bound(j)) { + lower_bounds[j] /= column_scale_vector[j]; + } + } + } + + } // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { diff --git a/src/math/lp/lp_dual_simplex.cpp b/src/math/lp/lp_dual_simplex.cpp new file mode 100644 index 00000000000..aaf612f5696 --- /dev/null +++ b/src/math/lp/lp_dual_simplex.cpp @@ -0,0 +1,24 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include "math/lp/lp_dual_simplex_def.h" +template lp::mpq lp::lp_dual_simplex::get_current_cost() const; +template void lp::lp_dual_simplex::find_maximal_solution(); +template double lp::lp_dual_simplex::get_current_cost() const; +template void lp::lp_dual_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_dual_simplex.h b/src/math/lp/lp_dual_simplex.h new file mode 100644 index 00000000000..75ef87492f2 --- /dev/null +++ b/src/math/lp/lp_dual_simplex.h @@ -0,0 +1,93 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/vector.h" +#include "math/lp/lp_utils.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_dual_core_solver.h" +namespace lp { + +template +class lp_dual_simplex: public lp_solver { + lp_dual_core_solver * m_core_solver; + vector m_b_copy; + vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver + vector m_column_types_of_core_solver; + vector m_column_types_of_logicals; + vector m_can_enter_basis; +public: + ~lp_dual_simplex() override { + delete m_core_solver; + } + + lp_dual_simplex() : m_core_solver(nullptr) {} + + + void decide_on_status_after_stage1(); + + void fix_logical_for_stage2(unsigned j); + + void fix_structural_for_stage2(unsigned j); + + void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); + + void restore_right_sides(); + + void solve_for_stage2(); + + void fill_x_with_zeros(); + + void stage1(); + + void stage2(); + + void fill_first_stage_solver_fields(); + + column_type get_column_type(unsigned j); + + void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); + + void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); + + void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); + + void set_type_for_logical(unsigned j, column_type col_type) { + this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; + } + + void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, + unsigned & slack_var, + unsigned & artificial); + + void augment_matrix_A_and_fill_x_and_allocate_some_fields(); + + + + void copy_m_b_aside_and_set_it_to_zeros(); + + void find_maximal_solution() override; + + T get_column_value(unsigned column) const override { + return this->get_column_value_with_core_solver(column, m_core_solver); + } + + T get_current_cost() const override; +}; +} diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h new file mode 100644 index 00000000000..8af9d87c104 --- /dev/null +++ b/src/math/lp/lp_dual_simplex_def.h @@ -0,0 +1,376 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once + +#include "math/lp/lp_dual_simplex.h" +namespace lp{ + +template void lp_dual_simplex::decide_on_status_after_stage1() { + switch (m_core_solver->get_status()) { + case lp_status::OPTIMAL: + if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { + this->m_status = lp_status::FEASIBLE; + } else { + this->m_status = lp_status::UNBOUNDED; + } + break; + case lp_status::DUAL_UNBOUNDED: + lp_unreachable(); + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; + break; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; + break; + default: + lp_unreachable(); + } +} + +template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { + lp_assert(j >= this->number_of_core_structurals()); + switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; + m_can_enter_basis[j] = true; + break; + case column_type::fixed: + this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::fixed; + m_can_enter_basis[j] = false; + break; + default: + lp_unreachable(); + } +} + +template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { + column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; + switch (ci->get_column_type()) { + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; + m_can_enter_basis[j] = true; + break; + case column_type::fixed: + case column_type::upper_bound: + lp_unreachable(); + case column_type::boxed: + this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::boxed; + m_can_enter_basis[j] = true; + break; + case column_type::free_column: + m_can_enter_basis[j] = true; + m_column_types_of_core_solver[j] = column_type::free_column; + break; + default: + lp_unreachable(); + } + // T cost_was = this->m_costs[j]; + this->set_scaled_cost(j); +} + +template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { + unsigned j = this->m_A->column_count(); + while (j-- > this->number_of_core_structurals()) { + fix_logical_for_stage2(j); + } + j = this->number_of_core_structurals(); + while (j--) { + fix_structural_for_stage2(j); + } +} + +template void lp_dual_simplex::restore_right_sides() { + unsigned i = this->m_A->row_count(); + while (i--) { + this->m_b[i] = m_b_copy[i]; + } +} + +template void lp_dual_simplex::solve_for_stage2() { + m_core_solver->restore_non_basis(); + m_core_solver->solve_yB(m_core_solver->m_y); + m_core_solver->fill_reduced_costs_from_m_y_by_rows(); + m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); + m_core_solver->set_status(lp_status::FEASIBLE); + m_core_solver->solve(); + switch (m_core_solver->get_status()) { + case lp_status::OPTIMAL: + this->m_status = lp_status::OPTIMAL; + break; + case lp_status::DUAL_UNBOUNDED: + this->m_status = lp_status::INFEASIBLE; + break; + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; + break; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; + break; + default: + lp_unreachable(); + } + this->m_second_stage_iterations = m_core_solver->total_iterations(); + this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); +} + +template void lp_dual_simplex::fill_x_with_zeros() { + unsigned j = this->m_A->column_count(); + while (j--) { + this->m_x[j] = numeric_traits::zero(); + } +} + +template void lp_dual_simplex::stage1() { + lp_assert(m_core_solver == nullptr); + this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); + if (this->m_settings.get_message_ostream() != nullptr) + this->print_statistics_on_A(*this->m_settings.get_message_ostream()); + m_core_solver = new lp_dual_core_solver( + *this->m_A, + m_can_enter_basis, + this->m_b, // the right side vector + this->m_x, + this->m_basis, + this->m_nbasis, + this->m_heading, + this->m_costs, + this->m_column_types_of_core_solver, + this->m_lower_bounds, + this->m_upper_bounds, + this->m_settings, + *this); + m_core_solver->fill_reduced_costs_from_m_y_by_rows(); + m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); + if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { + // skipping stage 1 + m_core_solver->set_status(lp_status::OPTIMAL); + m_core_solver->set_total_iterations(0); + } else { + m_core_solver->solve(); + } + decide_on_status_after_stage1(); + this->m_first_stage_iterations = m_core_solver->total_iterations(); +} + +template void lp_dual_simplex::stage2() { + unmark_boxed_and_fixed_columns_and_fix_structural_costs(); + restore_right_sides(); + solve_for_stage2(); +} + +template void lp_dual_simplex::fill_first_stage_solver_fields() { + unsigned slack_var = this->number_of_core_structurals(); + unsigned artificial = this->number_of_core_structurals() + this->m_slacks; + + for (unsigned row = 0; row < this->row_count(); row++) { + fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); + } + fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); +} + +template column_type lp_dual_simplex::get_column_type(unsigned j) { + lp_assert(j < this->m_A->column_count()); + if (j >= this->number_of_core_structurals()) { + return m_column_types_of_logicals[j - this->number_of_core_structurals()]; + } + return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); +} + +template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { + // see 4.7 in the dissertation of Achim Koberstein + lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != + this->m_core_solver_columns_to_external_columns.end()); + + T free_bound = T(1e4); // see 4.8 + unsigned jj = this->m_core_solver_columns_to_external_columns[j]; + lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); + column_info * ci = this->m_map_from_var_index_to_column_info[jj]; + switch (ci->get_column_type()) { + case column_type::upper_bound: { + std::stringstream s; + s << "unexpected bound type " << j << " " + << column_type_to_string(get_column_type(j)); + throw_exception(s.str()); + break; + } + case column_type::lower_bound: { + m_can_enter_basis[j] = true; + this->set_scaled_cost(j); + this->m_lower_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = numeric_traits::one(); + break; + } + case column_type::free_column: { + m_can_enter_basis[j] = true; + this->set_scaled_cost(j); + this->m_upper_bounds[j] = free_bound; + this->m_lower_bounds[j] = -free_bound; + break; + } + case column_type::boxed: + m_can_enter_basis[j] = false; + this->m_costs[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? + break; + default: + lp_unreachable(); + } + m_column_types_of_core_solver[j] = column_type::boxed; +} + +template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { + this->m_costs[j] = 0; + lp_assert(get_column_type(j) != column_type::upper_bound); + if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { + m_column_types_of_core_solver[j] = column_type::boxed; + this->m_lower_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = numeric_traits::one(); + } else { + m_column_types_of_core_solver[j] = column_type::fixed; + this->m_lower_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = numeric_traits::zero(); + } +} + +template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { + unsigned j = this->m_A->column_count(); + while (j-- > this->number_of_core_structurals()) { // go over logicals here + fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); + } + j = this->number_of_core_structurals(); + while (j--) { + fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); + } +} + +template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, + unsigned & slack_var, + unsigned & artificial) { + lp_assert(row < this->row_count()); + auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; + // we need to bring the program to the form Ax = b + T rs = this->m_b[row]; + switch (constraint.m_relation) { + case Equal: // no slack variable here + set_type_for_logical(artificial, column_type::fixed); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + (*this->m_A)(row, artificial) = numeric_traits::one(); + artificial++; + break; + + case Greater_or_equal: + set_type_for_logical(slack_var, column_type::lower_bound); + (*this->m_A)(row, slack_var) = - numeric_traits::one(); + if (rs > 0) { + // adding one artificial + set_type_for_logical(artificial, column_type::fixed); + (*this->m_A)(row, artificial) = numeric_traits::one(); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + artificial++; + } else { + // we can put a slack_var into the basis, and avoid adding an artificial variable + this->m_basis[row] = slack_var; + this->m_costs[slack_var] = numeric_traits::zero(); + } + slack_var++; + break; + case Less_or_equal: + // introduce a non-negative slack variable + set_type_for_logical(slack_var, column_type::lower_bound); + (*this->m_A)(row, slack_var) = numeric_traits::one(); + if (rs < 0) { + // adding one artificial + set_type_for_logical(artificial, column_type::fixed); + (*this->m_A)(row, artificial) = - numeric_traits::one(); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + artificial++; + } else { + // we can put slack_var into the basis, and avoid adding an artificial variable + this->m_basis[row] = slack_var; + this->m_costs[slack_var] = numeric_traits::zero(); + } + slack_var++; + break; + } +} + +template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { + this->count_slacks_and_artificials(); + this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); + unsigned n = this->m_A->column_count(); + this->m_column_types_of_core_solver.resize(n); + m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); + this->m_costs.resize(n); + this->m_upper_bounds.resize(n); + this->m_lower_bounds.resize(n); + m_can_enter_basis.resize(n); + this->m_basis.resize(this->m_A->row_count()); +} + + + +template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { + for (unsigned i = 0; i < this->m_b.size(); i++) { + m_b_copy.push_back(this->m_b[i]); + this->m_b[i] = numeric_traits::zero(); // preparing for the first stage + } +} + +template void lp_dual_simplex::find_maximal_solution(){ + if (this->problem_is_empty()) { + this->m_status = lp_status::EMPTY; + return; + } + + this->flip_costs(); // do it for now, todo ( remove the flipping) + + this->cleanup(); + if (this->m_status == lp_status::INFEASIBLE) { + return; + } + this->fill_matrix_A_and_init_right_side(); + this->fill_m_b(); + this->scale(); + augment_matrix_A_and_fill_x_and_allocate_some_fields(); + fill_first_stage_solver_fields(); + copy_m_b_aside_and_set_it_to_zeros(); + stage1(); + if (this->m_status == lp_status::FEASIBLE) { + stage2(); + } +} + + +template T lp_dual_simplex::get_current_cost() const { + T ret = numeric_traits::zero(); + for (auto it : this->m_map_from_var_index_to_column_info) { + ret += this->get_column_cost_value(it.first, it.second); + } + return -ret; // we flip costs for now +} +} diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index a60395ab014..4b6163df810 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -30,6 +30,7 @@ Revision History: #include #include #include "math/lp/lu.h" +#include "math/lp/lp_solver.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" diff --git a/src/math/lp/lp_primal_simplex.cpp b/src/math/lp/lp_primal_simplex.cpp new file mode 100644 index 00000000000..634f529009b --- /dev/null +++ b/src/math/lp/lp_primal_simplex.cpp @@ -0,0 +1,35 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include +#include +#include "util/vector.h" +#include +#include "math/lp/lp_primal_simplex_def.h" +template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); +template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); +template double lp::lp_primal_simplex::get_current_cost() const; +template double lp::lp_primal_simplex::get_column_value(unsigned int) const; +template lp::lp_primal_simplex::~lp_primal_simplex(); +template lp::lp_primal_simplex::~lp_primal_simplex(); +template lp::mpq lp::lp_primal_simplex::get_current_cost() const; +template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; +template void lp::lp_primal_simplex::find_maximal_solution(); +template void lp::lp_primal_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_primal_simplex.h b/src/math/lp/lp_primal_simplex.h new file mode 100644 index 00000000000..77e12d0888e --- /dev/null +++ b/src/math/lp/lp_primal_simplex.h @@ -0,0 +1,106 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/vector.h" +#include +#include +#include +#include "math/lp/lp_utils.h" +#include "math/lp/column_info.h" +#include "math/lp/lp_primal_core_solver.h" +#include "math/lp/lp_solver.h" +namespace lp { +template +class lp_primal_simplex: public lp_solver { + lp_primal_core_solver * m_core_solver; + vector m_lower_bounds; +private: + unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } + + void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); + + void init_buffer(unsigned k, vector & r); + + void refactor(); + + void set_scaled_costs(); +public: + lp_primal_simplex(): m_core_solver(nullptr) {} + + column_info * get_or_create_column_info(unsigned column); + + void set_status(lp_status status) { + this->m_status = status; + } + + lp_status get_status() { + return this->m_status; + } + + void fill_acceptable_values_for_x(); + + + void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); + + void fill_costs_and_x_for_first_stage_solver_for_row( + int row, + unsigned & slack_var, + unsigned & artificial); + + + + + void set_core_solver_bounds(); + + void find_maximal_solution() override; + + void fill_A_x_and_basis_for_stage_one_total_inf(); + + void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); + + void solve_with_total_inf(); + + + ~lp_primal_simplex() override; + + bool bounds_hold(std::unordered_map const & solution); + + T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); + + bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); + + bool row_constraints_hold(std::unordered_map const & solution); + + + T * get_array_from_map(std::unordered_map const & solution); + + bool solution_is_feasible(std::unordered_map const & solution) { + return bounds_hold(solution) && row_constraints_hold(solution); + } + + T get_column_value(unsigned column) const override { + return this->get_column_value_with_core_solver(column, m_core_solver); + } + + T get_current_cost() const override; + + +}; +} diff --git a/src/math/lp/lp_primal_simplex_def.h b/src/math/lp/lp_primal_simplex_def.h new file mode 100644 index 00000000000..7ffe819b207 --- /dev/null +++ b/src/math/lp/lp_primal_simplex_def.h @@ -0,0 +1,367 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once + +#include +#include "util/vector.h" +#include "math/lp/lp_primal_simplex.h" + +namespace lp { +template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { + unsigned slack_var = original_number_of_columns; + unsigned artificial = original_number_of_columns + this->m_slacks; + + for (unsigned row = 0; row < this->row_count(); row++) { + fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); + } +} + +template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { + for (unsigned i = 0; i < k; i++) { + r[i] = 0; + } + r[k] = 1; + for (unsigned i = this->row_count() -1; i > k; i--) { + r[i] = 0; + } +} + +template void lp_primal_simplex::refactor() { + m_core_solver->init_lu(); + if (m_core_solver->factorization()->get_status() != LU_status::OK) { + throw_exception("cannot refactor"); + } +} + +template void lp_primal_simplex::set_scaled_costs() { + unsigned j = this->number_of_core_structurals(); + while (j-- > 0) { + this->set_scaled_cost(j); + } +} + +template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { + auto it = this->m_columns.find(column); + return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; +} + +template void lp_primal_simplex::fill_acceptable_values_for_x() { + for (auto t : this->m_core_solver_columns_to_external_columns) { + this->m_x[t.first] = numeric_traits::zero(); + } +} + + +template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { + bound_is_set[i] = true; + bounds[i] = numeric_traits::zero(); +} + +template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( + int row, + unsigned & slack_var, + unsigned & artificial) { + lp_assert(row >= 0 && row < this->row_count()); + auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; + // we need to bring the program to the form Ax = b + T rs = this->m_b[row]; + T artificial_cost = - numeric_traits::one(); + switch (constraint.m_relation) { + case Equal: // no slack variable here + this->m_column_types[artificial] = column_type::lower_bound; + this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero + this->m_basis[row] = artificial; + if (rs >= 0) { + (*this->m_A)(row, artificial) = numeric_traits::one(); + this->m_x[artificial] = rs; + } else { + (*this->m_A)(row, artificial) = - numeric_traits::one(); + this->m_x[artificial] = - rs; + } + artificial++; + break; + + case Greater_or_equal: + this->m_column_types[slack_var] = column_type::lower_bound; + (*this->m_A)(row, slack_var) = - numeric_traits::one(); + + if (rs > 0) { + lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); + // adding one artificial + this->m_column_types[artificial] = column_type::lower_bound; + (*this->m_A)(row, artificial) = numeric_traits::one(); + this->m_costs[artificial] = artificial_cost; + this->m_basis[row] = artificial; + this->m_x[artificial] = rs; + artificial++; + } else { + // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable + this->m_basis[row] = slack_var; + this->m_x[slack_var] = - rs; + } + slack_var++; + break; + case Less_or_equal: + // introduce a non-negative slack variable + this->m_column_types[slack_var] = column_type::lower_bound; + (*this->m_A)(row, slack_var) = numeric_traits::one(); + + if (rs < 0) { + // adding one artificial + lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); + this->m_column_types[artificial] = column_type::lower_bound; + (*this->m_A)(row, artificial) = - numeric_traits::one(); + this->m_costs[artificial] = artificial_cost; + this->m_x[artificial] = - rs; + this->m_basis[row] = artificial++; + } else { + // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable + this->m_basis[row] = slack_var; + this->m_x[slack_var] = rs; + } + slack_var++; + break; + } +} + + + + + +template void lp_primal_simplex::set_core_solver_bounds() { + unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; + this->m_column_types.resize(total_vars); + this->m_upper_bounds.resize(total_vars); + for (auto cit : this->m_map_from_var_index_to_column_info) { + column_info * ci = cit.second; + unsigned j = ci->get_column_index(); + if (!is_valid(j)) + continue; // the variable is not mapped to a column + switch (this->m_column_types[j] = ci->get_column_type()){ + case column_type::fixed: + this->m_upper_bounds[j] = numeric_traits::zero(); + break; + case column_type::boxed: + this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; + break; + + default: break; // do nothing + } + } +} + + +template void lp_primal_simplex::find_maximal_solution() { + if (this->problem_is_empty()) { + this->m_status = lp_status::EMPTY; + return; + } + + this->cleanup(); + this->fill_matrix_A_and_init_right_side(); + if (this->m_status == lp_status::INFEASIBLE) { + return; + } + this->m_x.resize(this->m_A->column_count()); + this->fill_m_b(); + this->scale(); + fill_acceptable_values_for_x(); + this->count_slacks_and_artificials(); + set_core_solver_bounds(); + solve_with_total_inf(); +} + +template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { + for (unsigned row = 0; row < this->row_count(); row++) + fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); +} + +template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { + lp_assert(row < this->row_count()); + auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); + lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); + unsigned ext_row = ext_row_it->second; + auto constr_it = this->m_constraints.find(ext_row); + lp_assert(constr_it != this->m_constraints.end()); + auto & constraint = constr_it->second; + unsigned j = this->m_A->column_count(); // j is a slack variable + this->m_A->add_column(); + // we need to bring the program to the form Ax = b + this->m_basis[row] = j; + switch (constraint.m_relation) { + case Equal: + this->m_x[j] = this->m_b[row]; + (*this->m_A)(row, j) = numeric_traits::one(); + this->m_column_types[j] = column_type::fixed; + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); + break; + + case Greater_or_equal: + this->m_x[j] = - this->m_b[row]; + (*this->m_A)(row, j) = - numeric_traits::one(); + this->m_column_types[j] = column_type::lower_bound; + this->m_upper_bounds[j] = zero_of_type(); + break; + case Less_or_equal: + this->m_x[j] = this->m_b[row]; + (*this->m_A)(row, j) = numeric_traits::one(); + this->m_column_types[j] = column_type::lower_bound; + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); + break; + default: + lp_unreachable(); + } +} + +template void lp_primal_simplex::solve_with_total_inf() { + int total_vars = this->m_A->column_count() + this->row_count(); + if (total_vars == 0) { + this->m_status = lp_status::OPTIMAL; + return; + } + m_lower_bounds.clear(); + m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero + this->m_x.resize(total_vars, numeric_traits::zero()); + this->m_basis.resize(this->row_count()); + this->m_costs.clear(); + this->m_costs.resize(total_vars, zero_of_type()); + fill_A_x_and_basis_for_stage_one_total_inf(); + if (this->m_settings.get_message_ostream() != nullptr) + this->print_statistics_on_A(*this->m_settings.get_message_ostream()); + set_scaled_costs(); + + m_core_solver = new lp_primal_core_solver(*this->m_A, + this->m_b, + this->m_x, + this->m_basis, + this->m_nbasis, + this->m_heading, + this->m_costs, + this->m_column_types, + m_lower_bounds, + this->m_upper_bounds, + this->m_settings, *this); + m_core_solver->solve(); + this->set_status(m_core_solver->get_status()); + this->m_total_iterations = m_core_solver->total_iterations(); +} + + +template lp_primal_simplex::~lp_primal_simplex() { + delete m_core_solver; +} + +template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { + for (auto it : this->m_map_from_var_index_to_column_info) { + auto sol_it = solution.find(it.second->get_name()); + if (sol_it == solution.end()) { + std::stringstream s; + s << "cannot find column " << it.first << " in solution"; + throw_exception(s.str() ); + } + + if (!it.second->bounds_hold(sol_it->second)) { + it.second->bounds_hold(sol_it->second); + return false; + } + } + return true; +} + +template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { + auto it = this->m_A_values.find(i); + if (it == this->m_A_values.end()) { + std::stringstream s; + s << "cannot find row " << i; + throw_exception(s.str() ); + } + T ret = numeric_traits::zero(); + for (auto & pair : it->second) { + auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); + lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); + column_info * ci = cit->second; + auto sol_it = solution.find(ci->get_name()); + lp_assert(sol_it != solution.end()); + T column_val = sol_it->second; + if (out != nullptr) { + (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; + } + ret += pair.second * column_val; + } + if (out != nullptr) { + (*out) << " = " << ret << std::endl; + } + return ret; +} + +template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { + T row_val = get_row_value(i, solution, out); + auto & constraint = this->m_constraints[i]; + T rs = constraint.m_rs; + bool print = out != nullptr; + switch (constraint.m_relation) { + case Equal: + if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { + if (print) { + (*out) << "should be = " << rs << std::endl; + } + return false; + } + return true; + case Greater_or_equal: + if (numeric_traits::get_double(row_val - rs) < -0.00001) { + if (print) { + (*out) << "should be >= " << rs << std::endl; + } + return false; + } + return true;; + + case Less_or_equal: + if (numeric_traits::get_double(row_val - rs) > 0.00001) { + if (print) { + (*out) << "should be <= " << rs << std::endl; + } + return false; + } + return true;; + } + lp_unreachable(); + return false; // it is unreachable +} + +template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { + for (auto it : this->m_A_values) { + if (!row_constraint_holds(it.first, solution, nullptr)) { + row_constraint_holds(it.first, solution, nullptr); + return false; + } + } + return true; +} + +template T lp_primal_simplex::get_current_cost() const { + T ret = numeric_traits::zero(); + for (auto it : this->m_map_from_var_index_to_column_info) { + ret += this->get_column_cost_value(it.first, it.second); + } + return ret; +} +} diff --git a/src/math/lp/lp_solver.cpp b/src/math/lp/lp_solver.cpp new file mode 100644 index 00000000000..fc95140982b --- /dev/null +++ b/src/math/lp/lp_solver.cpp @@ -0,0 +1,55 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include "math/lp/lp_solver_def.h" +template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); +template void lp::lp_solver::cleanup(); +template void lp::lp_solver::count_slacks_and_artificials(); +template void lp::lp_solver::fill_m_b(); +template void lp::lp_solver::fill_matrix_A_and_init_right_side(); +template void lp::lp_solver::flip_costs(); +template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; +template int lp::lp_solver::get_column_index_by_name(std::string) const; +template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; +template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); +template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); +template void lp::lp_solver::print_statistics_on_A(std::ostream & out); +template bool lp::lp_solver::problem_is_empty(); +template void lp::lp_solver::scale(); +template void lp::lp_solver::set_scaled_cost(unsigned int); +template lp::lp_solver::~lp_solver(); +template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); +template void lp::lp_solver::cleanup(); +template void lp::lp_solver::count_slacks_and_artificials(); +template void lp::lp_solver::fill_m_b(); +template void lp::lp_solver::fill_matrix_A_and_init_right_side(); +template void lp::lp_solver::flip_costs(); +template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; +template int lp::lp_solver::get_column_index_by_name(std::string) const; +template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; +template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; +template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); +template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); +template void lp::lp_solver::print_statistics_on_A(std::ostream & out); +template bool lp::lp_solver::problem_is_empty(); +template void lp::lp_solver::scale(); +template void lp::lp_solver::set_scaled_cost(unsigned int); +template lp::lp_solver::~lp_solver(); +template double lp::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/math/lp/lp_solver.h b/src/math/lp/lp_solver.h new file mode 100644 index 00000000000..ab16a686fd6 --- /dev/null +++ b/src/math/lp/lp_solver.h @@ -0,0 +1,260 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +#include +#include +#include +#include "util/vector.h" +#include "math/lp/lp_settings.h" +#include "math/lp/column_info.h" +#include "math/lp/static_matrix.h" +#include "math/lp/lp_core_solver_base.h" +#include "math/lp/scaler.h" +#include "math/lp/bound_analyzer_on_row.h" +namespace lp { +enum lp_relation { + Less_or_equal, + Equal, + Greater_or_equal +}; + +template +struct lp_constraint { + X m_rs; // right side of the constraint + lp_relation m_relation; + lp_constraint() {} // empty constructor + lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} +}; + + +template +class lp_solver : public column_namer { + column_info * get_or_create_column_info(unsigned column); + +protected: + T get_column_cost_value(unsigned j, column_info * ci) const; +public: + unsigned m_total_iterations; + static_matrix* m_A; // this is the matrix of constraints + vector m_b; // the right side vector + unsigned m_first_stage_iterations; + unsigned m_second_stage_iterations; + std::unordered_map> m_constraints; + std::unordered_map*> m_map_from_var_index_to_column_info; + std::unordered_map > m_A_values; + std::unordered_map m_names_to_columns; // don't have to use it + std::unordered_map m_external_rows_to_core_solver_rows; + std::unordered_map m_core_solver_rows_to_external_rows; + std::unordered_map m_core_solver_columns_to_external_columns; + vector m_column_scale; + std::unordered_map m_name_map; + unsigned m_artificials; + unsigned m_slacks; + vector m_column_types; + vector m_costs; + vector m_x; + vector m_upper_bounds; + vector m_basis; + vector m_nbasis; + vector m_heading; + + + lp_status m_status; + + lp_settings m_settings; + lp_solver(): + m_A(nullptr), // this is the matrix of constraints + m_first_stage_iterations (0), + m_second_stage_iterations (0), + m_artificials (0), + m_slacks (0), + m_status(lp_status::UNKNOWN) + {} + + unsigned row_count() const { return this->m_A->row_count(); } + + void add_constraint(lp_relation relation, T right_side, unsigned row_index); + + void set_cost_for_column(unsigned column, T column_cost) { + get_or_create_column_info(column)->set_cost(column_cost); + } + std::string get_variable_name(unsigned j) const override; + + void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { + m_A_values[row][column] = val; + } + // returns the current cost + virtual T get_current_cost() const = 0; + // do not have to call it + void give_symbolic_name_to_column(std::string name, unsigned column); + + virtual T get_column_value(unsigned column) const = 0; + + T get_column_value_by_name(std::string name) const; + + // returns -1 if not found + virtual int get_column_index_by_name(std::string name) const; + + void set_lower_bound(unsigned i, T bound) { + column_info *ci = get_or_create_column_info(i); + ci->set_lower_bound(bound); + } + + void set_upper_bound(unsigned i, T bound) { + column_info *ci = get_or_create_column_info(i); + ci->set_upper_bound(bound); + } + + void unset_lower_bound(unsigned i) { + get_or_create_column_info(i)->unset_lower_bound(); + } + + void unset_upper_bound(unsigned i) { + get_or_create_column_info(i)->unset_upper_bound(); + } + + void set_fixed_value(unsigned i, T val) { + column_info *ci = get_or_create_column_info(i); + ci->set_fixed_value(val); + } + + void unset_fixed_value(unsigned i) { + get_or_create_column_info(i)->unset_fixed(); + } + + lp_status get_status() const { + return m_status; + } + + void set_status(lp_status st) { + m_status = st; + } + + + ~lp_solver() override; + + void flip_costs(); + + virtual void find_maximal_solution() = 0; + void set_time_limit(unsigned time_limit_in_seconds) { + m_settings.time_limit = time_limit_in_seconds; + } + + +protected: + bool problem_is_empty(); + + void scale(); + + + void print_rows_scale_stats(std::ostream & out); + + void print_columns_scale_stats(std::ostream & out); + + void print_row_scale_stats(unsigned i, std::ostream & out); + + void print_column_scale_stats(unsigned j, std::ostream & out); + + void print_scale_stats(std::ostream & out); + + void get_max_abs_in_row(std::unordered_map & row_map); + + void pin_vars_down_on_row(std::unordered_map & row) { + pin_vars_on_row_with_sign(row, - numeric_traits::one()); + } + + void pin_vars_up_on_row(std::unordered_map & row) { + pin_vars_on_row_with_sign(row, numeric_traits::one()); + } + + void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); + + bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); + + bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); + + bool row_is_zero(std::unordered_map & row); + + bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); + + bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); + + bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); + + // analyse possible max and min values that are derived from var boundaries + // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. + // Then we know what values of the variables are. For each positive coeff of the row it has to be + // the low boundary of the var and for a negative - the upper. + + // this routing also pins the variables to the boundaries + bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); + + void remove_fixed_or_zero_columns(); + + void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); + + unsigned try_to_remove_some_rows(); + + void cleanup(); + + void map_external_rows_to_core_solver_rows(); + + void map_external_columns_to_core_solver_columns(); + + unsigned number_of_core_structurals() { + return static_cast(m_core_solver_columns_to_external_columns.size()); + } + + void restore_column_scales_to_one() { + for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); + } + + void unscale(); + + void fill_A_from_A_values(); + + void fill_matrix_A_and_init_right_side(); + + void count_slacks_and_artificials(); + + void count_slacks_and_artificials_for_row(unsigned i); + + T lower_bound_shift_for_row(unsigned i); + + void fill_m_b(); + + T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; + void set_scaled_cost(unsigned j); + void print_statistics_on_A(std::ostream & out) { + out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; + } + +public: + lp_settings & settings() { return m_settings;} + void print_model(std::ostream & s) const { + s << "objective = " << get_current_cost() << std::endl; + s << "column values\n"; + for (auto & it : m_names_to_columns) { + s << it.first << " = " << get_column_value(it.second) << std::endl; + } + } +}; +} diff --git a/src/math/lp/lp_solver_def.h b/src/math/lp/lp_solver_def.h new file mode 100644 index 00000000000..191832a2487 --- /dev/null +++ b/src/math/lp/lp_solver_def.h @@ -0,0 +1,571 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once + +#include +#include +#include "util/vector.h" +#include "math/lp/lp_solver.h" +namespace lp { +template column_info * lp_solver::get_or_create_column_info(unsigned column) { + auto it = m_map_from_var_index_to_column_info.find(column); + return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; +} + +template +std::string lp_solver::get_variable_name(unsigned j) const { // j here is the core solver index + if (!m_settings.print_external_var_name()) + return std::string("j")+T_to_string(j); + auto it = this->m_core_solver_columns_to_external_columns.find(j); + if (it == this->m_core_solver_columns_to_external_columns.end()) + return std::string("x")+T_to_string(j); + unsigned external_j = it->second; + auto t = this->m_map_from_var_index_to_column_info.find(external_j); + if (t == this->m_map_from_var_index_to_column_info.end()) { + return std::string("x") +T_to_string(external_j); + } + return t->second->get_name(); +} + +template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { + if (ci->is_fixed()) { + return ci->get_cost() * ci->get_fixed_value(); + } + return ci->get_cost() * get_column_value(j); +} +template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { + lp_assert(m_constraints.find(row_index) == m_constraints.end()); + lp_constraint cs(right_side, relation); + m_constraints[row_index] = cs; +} + +template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { + auto it = m_map_from_var_index_to_column_info.find(column); + column_info *ci; + if (it == m_map_from_var_index_to_column_info.end()){ + m_map_from_var_index_to_column_info[column] = ci = new column_info; + } else { + ci = it->second; + } + ci->set_name(name); + m_names_to_columns[name] = column; +} + + +template T lp_solver::get_column_value_by_name(std::string name) const { + auto it = m_names_to_columns.find(name); + if (it == m_names_to_columns.end()) { + std::stringstream s; + s << "get_column_value_by_name " << name; + throw_exception(s.str()); + } + return get_column_value(it -> second); +} + +// returns -1 if not found +template int lp_solver::get_column_index_by_name(std::string name) const { + auto t = m_names_to_columns.find(name); + if (t == m_names_to_columns.end()) { + return -1; + } + return t->second; +} + + +template lp_solver::~lp_solver(){ + delete m_A; + for (auto t : m_map_from_var_index_to_column_info) { + delete t.second; + } +} + +template void lp_solver::flip_costs() { + for (auto t : m_map_from_var_index_to_column_info) { + column_info *ci = t.second; + ci->set_cost(-ci->get_cost()); + } +} + +template bool lp_solver::problem_is_empty() { + for (auto & c : m_A_values) + if (!c.second.empty()) + return false; + return true; +} + +template void lp_solver::scale() { + if (numeric_traits::precise() || m_settings.use_scaling == false) { + m_column_scale.clear(); + m_column_scale.resize(m_A->column_count(), one_of_type()); + return; + } + + T smin = T(m_settings.scaling_minimum); + T smax = T(m_settings.scaling_maximum); + + scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); + if (!scaler.scale()) { + unscale(); + } +} + + +template void lp_solver::print_rows_scale_stats(std::ostream & out) { + out << "rows max" << std::endl; + for (unsigned i = 0; i < m_A->row_count(); i++) { + print_row_scale_stats(i, out); + } + out << std::endl; +} + +template void lp_solver::print_columns_scale_stats(std::ostream & out) { + out << "columns max" << std::endl; + for (unsigned i = 0; i < m_A->column_count(); i++) { + print_column_scale_stats(i, out); + } + out << std::endl; +} + +template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { + out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; + out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; +} + +template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { + out << "(" << m_A->get_min_abs_in_row(j) << " "; + out << m_A->get_max_abs_in_column(j) << ")"; +} + +template void lp_solver::print_scale_stats(std::ostream & out) { + print_rows_scale_stats(out); + print_columns_scale_stats(out); +} + +template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { + T ret = numeric_traits::zero(); + for (auto jp : row_map) { + T ac = numeric_traits::abs(jp->second); + if (ac > ret) { + ret = ac; + } + } + return ret; +} + +template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { + for (auto t : row) { + unsigned j = t.first; + column_info * ci = m_map_from_var_index_to_column_info[j]; + T a = t.second; + if (a * sign > numeric_traits::zero()) { + lp_assert(ci->upper_bound_is_set()); + ci->set_fixed_value(ci->get_upper_bound()); + } else { + lp_assert(ci->lower_bound_is_set()); + ci->set_fixed_value(ci->get_lower_bound()); + } + } +} + +template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); + for (auto & t : row) { + T a = t.second; + column_info * ci = m_map_from_var_index_to_column_info[t.first]; + if (a > numeric_traits::zero()) { + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; + } else { + return false; + } + } else { + if (ci->upper_bound_is_set()) { + lower_bound += ci->get_upper_bound() * a; + } else { + return false; + } + } + } + return true; +} + +template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); + for (auto & t : row) { + T a = t.second; + column_info * ci = m_map_from_var_index_to_column_info[t.first]; + if (a < numeric_traits::zero()) { + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; + } else { + return false; + } + } else { + if (ci->upper_bound_is_set()) { + lower_bound += ci->get_upper_bound() * a; + } else { + return false; + } + } + } + return true; +} + +template bool lp_solver::row_is_zero(std::unordered_map & row) { + for (auto & t : row) { + if (!is_zero(t.second)) + return false; + } + return true; +} + +template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { + T rs = m_constraints[row_index].m_rs; + if (row_is_zero(row)) { + if (!is_zero(rs)) + m_status = lp_status::INFEASIBLE; + return true; + } + + T lower_bound; + bool lb = get_minimal_row_value(row, lower_bound); + if (lb) { + T diff = lower_bound - rs; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ + // lower_bound > rs + m_settings.refactor_epsilon + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_down_on_row(row); + return true; + } + } + + T upper_bound; + bool ub = get_maximal_row_value(row, upper_bound); + if (ub) { + T diff = rs - upper_bound; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { + // upper_bound < rs - m_settings.refactor_tolerance + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_up_on_row(row); + return true; + } + } + + return false; +} + +template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { + T rs = m_constraints[row_index].m_rs; + if (row_is_zero(row)) { + if (rs > zero_of_type()) + m_status = lp_status::INFEASIBLE; + return true; + } + + T upper_bound; + if (get_maximal_row_value(row, upper_bound)) { + T diff = rs - upper_bound; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { + // upper_bound < rs - m_settings.refactor_tolerance + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_up_on_row(row); + return true; + } + } + + return false; +} + +template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { + T lower_bound; + T rs = m_constraints[row_index].m_rs; + if (row_is_zero(row)) { + if (rs < zero_of_type()) + m_status = lp_status::INFEASIBLE; + return true; + } + + if (get_minimal_row_value(row, lower_bound)) { + T diff = lower_bound - rs; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ + // lower_bound > rs + m_settings.refactor_tolerance + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_down_on_row(row); + return true; + } + } + + return false; +} + +// analyse possible max and min values that are derived from var boundaries +// Let us say that the we have a "ge" constraint, and the min value is equal to the rs. +// Then we know what values of the variables are. For each positive coeff of the row it has to be +// the low boundary of the var and for a negative - the upper. + +// this routing also pins the variables to the boundaries +template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { + auto & constraint = m_constraints[row_index]; + switch (constraint.m_relation) { + case lp_relation::Equal: + return row_e_is_obsolete(row, row_index); + + case lp_relation::Greater_or_equal: + return row_ge_is_obsolete(row, row_index); + + case lp_relation::Less_or_equal: + return row_le_is_obsolete(row, row_index); + } + lp_unreachable(); + return false; // it is unreachable +} + +template void lp_solver::remove_fixed_or_zero_columns() { + for (auto & i_row : m_A_values) { + remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); + } +} + +template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { + auto & constraint = m_constraints[i]; + vector removed; + for (auto & col : row) { + unsigned j = col.first; + lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); + column_info * ci = m_map_from_var_index_to_column_info[j]; + if (ci->is_fixed()) { + removed.push_back(j); + T aj = col.second; + constraint.m_rs -= aj * ci->get_fixed_value(); + } else { + if (numeric_traits::is_zero(col.second)){ + removed.push_back(j); + } + } + } + + for (auto j : removed) { + row.erase(j); + } +} + +template unsigned lp_solver::try_to_remove_some_rows() { + vector rows_to_delete; + for (auto & t : m_A_values) { + if (row_is_obsolete(t.second, t.first)) { + rows_to_delete.push_back(t.first); + } + + if (m_status == lp_status::INFEASIBLE) { + return 0; + } + } + if (!rows_to_delete.empty()) { + for (unsigned k : rows_to_delete) { + m_A_values.erase(k); + } + } + remove_fixed_or_zero_columns(); + return static_cast(rows_to_delete.size()); +} + +template void lp_solver::cleanup() { + int n = 0; // number of deleted rows + int d; + while ((d = try_to_remove_some_rows()) > 0) + n += d; + + if (n == 1) { + LP_OUT(m_settings, "deleted one row" << std::endl); + } else if (n) { + LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); + } +} + +template void lp_solver::map_external_rows_to_core_solver_rows() { + unsigned size = 0; + for (auto & row : m_A_values) { + m_external_rows_to_core_solver_rows[row.first] = size; + m_core_solver_rows_to_external_rows[size] = row.first; + size++; + } +} + +template void lp_solver::map_external_columns_to_core_solver_columns() { + unsigned size = 0; + for (auto & row : m_A_values) { + for (auto & col : row.second) { + if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { + throw_exception("found fixed column"); + } + unsigned j = col.first; + auto column_info_it = m_map_from_var_index_to_column_info.find(j); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); + + auto j_column = column_info_it->second->get_column_index(); + if (!is_valid(j_column)) { // j is a newcomer + m_map_from_var_index_to_column_info[j]->set_column_index(size); + m_core_solver_columns_to_external_columns[size++] = j; + } + } + } +} + +template void lp_solver::unscale() { + delete m_A; + m_A = nullptr; + fill_A_from_A_values(); + restore_column_scales_to_one(); + fill_m_b(); +} + +template void lp_solver::fill_A_from_A_values() { + m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); + for (auto & t : m_A_values) { + auto row_it = m_external_rows_to_core_solver_rows.find(t.first); + lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); + unsigned row = row_it->second; + for (auto k : t.second) { + auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); + column_info *ci = column_info_it->second; + unsigned col = ci->get_column_index(); + lp_assert(is_valid(col)); + bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); + if (!col_is_flipped) { + (*m_A)(row, col) = k.second; + } else { + (*m_A)(row, col) = - k.second; + } + } + } +} + +template void lp_solver::fill_matrix_A_and_init_right_side() { + map_external_rows_to_core_solver_rows(); + map_external_columns_to_core_solver_columns(); + lp_assert(m_A == nullptr); + fill_A_from_A_values(); + m_b.resize(m_A->row_count()); +} + +template void lp_solver::count_slacks_and_artificials() { + for (int i = row_count() - 1; i >= 0; i--) { + count_slacks_and_artificials_for_row(i); + } +} + +template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { + lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; + switch (constraint.m_relation) { + case Equal: + m_artificials++; + break; + case Greater_or_equal: + m_slacks++; + if (this->m_b[i] > 0) { + m_artificials++; + } + break; + case Less_or_equal: + m_slacks++; + if (this->m_b[i] < 0) { + m_artificials++; + } + break; + } +} + +template T lp_solver::lower_bound_shift_for_row(unsigned i) { + T ret = numeric_traits::zero(); + + auto row = this->m_A_values.find(i); + if (row == this->m_A_values.end()) { + throw_exception("cannot find row"); + } + for (auto col : row->second) { + ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); + } + return ret; +} + +template void lp_solver::fill_m_b() { + for (int i = this->row_count() - 1; i >= 0; i--) { + lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; + auto & constraint = this->m_constraints[external_i]; + this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); + } +} + +template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { + auto cit = this->m_map_from_var_index_to_column_info.find(column); + if (cit == this->m_map_from_var_index_to_column_info.end()) { + return numeric_traits::zero(); + } + + column_info * ci = cit->second; + + if (ci->is_fixed()) { + return ci->get_fixed_value(); + } + + unsigned cj = ci->get_column_index(); + if (cj != static_cast(-1)) { + T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; + if (ci->is_free()) { + return v; + } + if (!ci->is_flipped()) { + return v + ci->get_lower_bound(); + } + + // the flipped case when there is only upper bound + return -v + ci->get_upper_bound(); // + } + + return numeric_traits::zero(); // returns zero for out of boundary columns +} + +template void lp_solver::set_scaled_cost(unsigned j) { + // grab original costs but modify it with the column scales + lp_assert(j < this->m_column_scale.size()); + column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; + T cost = ci->get_cost(); + if (ci->is_flipped()){ + cost *= T(-1); + } + lp_assert(ci->is_fixed() == false); + this->m_costs[j] = cost * this->m_column_scale[j]; +} +} diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h new file mode 100644 index 00000000000..8093954b11e --- /dev/null +++ b/src/math/lp/mps_reader.h @@ -0,0 +1,891 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once + +// reads an MPS file representing a Mixed Integer Program +#include +#include +#include +#include "util/vector.h" +#include +#include +#include +#include +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" +#include "math/lp/lar_solver.h" +#include "math/lp/lp_utils.h" +#include "math/lp/lp_solver.h" +namespace lp { +inline bool my_white_space(const char & a) { + return a == ' ' || a == '\t'; +} +inline size_t number_of_whites(const std::string & s) { + size_t i = 0; + for(;i < s.size(); i++) + if (!my_white_space(s[i])) return i; + return i; +} +inline size_t number_of_whites_from_end(const std::string & s) { + size_t ret = 0; + for(int i = static_cast(s.size()) - 1;i >= 0; i--) + if (my_white_space(s[i])) ret++;else break; + + return ret; +} + + + // trim from start +inline std::string <rim(std::string &s) { + s.erase(0, number_of_whites(s)); + return s; +} + + + + + // trim from end +inline std::string &rtrim(std::string &s) { + // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.end() - number_of_whites_from_end(s), s.end()); + return s; +} + // trim from both ends +inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} + +inline std::string trim(std::string const &r) { + std::string s = r; + return ltrim(rtrim(s)); +} + + +inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { + vector results; + size_t prev = 0; + size_t next = 0; + while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { + if (keep_empty || (next - prev != 0)) { + results.push_back(source.substr(prev, next - prev)); + } + prev = next + 1; + } + if (prev < source.size()) { + results.push_back(source.substr(prev)); + } + return results; +} + +inline vector split_and_trim(const std::string &line) { + auto split = string_split(line, " \t", false); + vector ret; + for (auto s : split) { + ret.push_back(trim(s)); + } + return ret; +} + +template +class mps_reader { + enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; + struct bound { + T m_low; + T m_upper; + bool m_low_is_set; + bool m_upper_is_set; + bool m_value_is_fixed; + T m_fixed_value; + bool m_free; + // constructor + bound() : m_low(numeric_traits::zero()), + m_low_is_set(true), + m_upper_is_set(false), + m_value_is_fixed(false), + m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable + }; + + struct column { + std::string m_name; + bound * m_bound; + unsigned m_index; + column(const std::string &name, unsigned index): m_name(name), + m_bound(nullptr), + m_index(index) { + } + }; + + struct row { + row_type m_type; + std::string m_name; + std::unordered_map m_row_columns; + unsigned m_index; + T m_right_side; + T m_range; + row(row_type type, const std::string &name, unsigned index) : + m_type(type), + m_name(name), + m_index(index), + m_right_side(zero_of_type()), + m_range(zero_of_type()) + { + } + }; + + bool m_is_OK; + std::string m_file_name; + std::unordered_map m_rows; + std::unordered_map m_columns; + std::unordered_map m_names_to_var_index; + std::string m_line; + std::string m_name; + std::string m_cost_row_name; + std::ifstream m_file_stream; + // needed to adjust the index row + unsigned m_cost_line_count; + unsigned m_line_number; + std::ostream * m_message_stream; + + void set_m_ok_to_false() { + *m_message_stream << "setting m_is_OK to false" << std::endl; + m_is_OK = false; + } + + std::string get_string_from_position(unsigned offset) { + unsigned i = offset; + for (; i < m_line.size(); i++){ + if (m_line[i] == ' ') + break; + } + lp_assert(m_line.size() >= offset); + lp_assert(m_line.size() >> i); + lp_assert(i >= offset); + return m_line.substr(offset, i - offset); + } + + void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ + if (b == nullptr) { + solver->set_lower_bound(col, numeric_traits::zero()); + return; + } + + if (b->m_free) { + return; + } + if (b->m_low_is_set) { + solver->set_lower_bound(col, b->m_low); + } + if (b->m_upper_is_set) { + solver->set_upper_bound(col, b->m_upper); + } + + if (b->m_value_is_fixed) { + solver->set_fixed_value(col, b->m_fixed_value); + } + } + + bool all_white_space() { + for (unsigned i = 0; i < m_line.size(); i++) { + char c = m_line[i]; + if (c != ' ' && c != '\t') { + return false; + } + } + return true; + } + + void read_line() { + while (m_is_OK) { + if (!getline(m_file_stream, m_line)) { + m_line_number++; + set_m_ok_to_false(); + *m_message_stream << "cannot read from file" << std::endl; + } + m_line_number++; + if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) + break; + } + } + + void read_name() { + do { + read_line(); + if (m_line.find("NAME") != 0) { + continue; + } + m_line = m_line.substr(4); + m_name = trim(m_line); + break; + } while (m_is_OK); + } + + void read_rows() { + // look for start of the rows + read_line(); + do { + if (static_cast(m_line.find("ROWS")) >= 0) { + break; + } + } while (m_is_OK); + do { + read_line(); + if (m_line.find("COLUMNS") == 0) { + break; + } + add_row(); + } while (m_is_OK); + } + + void read_column_by_columns(const std::string & column_name, std::string column_data) { + // uph, let us try to work with columns + if (column_data.size() >= 22) { + std::string ss = column_data.substr(0, 8); + std::string row_name = trim(ss); + auto t = m_rows.find(row_name); + + if (t == m_rows.end()) { + *m_message_stream << "cannot find " << row_name << std::endl; + goto fail; + } else { + row * row = t->second; + row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); + if (column_data.size() > 24) { + column_data = column_data.substr(25); + if (column_data.size() >= 22) { + read_column_by_columns(column_name, column_data); + } + } + } + } else { + fail: + set_m_ok_to_false(); + *m_message_stream << "cannot understand this line\n" + "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + void read_column(const std::string & column_name, const std::string & column_data){ + auto tokens = split_and_trim(column_data); + for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { + auto row_name = tokens[i]; + if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here + auto t = m_rows.find(row_name); + if (t == m_rows.end()) { + read_column_by_columns(column_name, column_data); + return; + } + row *r = t->second; + r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); + } + } + + void read_columns(){ + std::string column_name; + do { + read_line(); + if (m_line.find("RHS") == 0) { + break; + } + if (m_line.size() < 22) { + (*m_message_stream) << "line is too short for a column" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + std::string column_name_tmp = trim(m_line.substr(4, 8)); + if (!column_name_tmp.empty()) { + column_name = column_name_tmp; + } + auto col_it = m_columns.find(column_name); + mps_reader::column * col; + if (col_it == m_columns.end()) { + col = new mps_reader::column(column_name, static_cast(m_columns.size())); + m_columns[column_name] = col; + // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; + } else { + col = col_it->second; + } + read_column(column_name, m_line.substr(14)); + } while (m_is_OK); + } + + void read_rhs() { + do { + read_line(); + if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { + break; + } + fill_rhs(); + } while (m_is_OK); + } + + + void fill_rhs_by_columns(std::string rhsides) { + // uph, let us try to work with columns + if (rhsides.size() >= 22) { + std::string ss = rhsides.substr(0, 8); + std::string row_name = trim(ss); + auto t = m_rows.find(row_name); + + if (t == m_rows.end()) { + (*m_message_stream) << "cannot find " << row_name << std::endl; + goto fail; + } else { + row * row = t->second; + row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); + if (rhsides.size() > 24) { + rhsides = rhsides.substr(25); + if (rhsides.size() >= 22) { + fill_rhs_by_columns(rhsides); + } + } + } + } else { + fail: + set_m_ok_to_false(); + (*m_message_stream) << "cannot understand this line" << std::endl; + (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + void fill_rhs() { + if (m_line.size() < 14) { + (*m_message_stream) << "line is too short" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + std::string rhsides = m_line.substr(14); + vector splitted_line = split_and_trim(rhsides); + + for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { + auto t = m_rows.find(splitted_line[i]); + if (t == m_rows.end()) { + fill_rhs_by_columns(rhsides); + return; + } + row * row = t->second; + row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); + } + } + + void read_bounds() { + if (m_line.find("BOUNDS") != 0) { + return; + } + + do { + read_line(); + if (m_line[0] != ' ') { + break; + } + create_or_update_bound(); + } while (m_is_OK); + } + + void read_ranges() { + if (m_line.find("RANGES") != 0) { + return; + } + do { + read_line(); + auto sl = split_and_trim(m_line); + if (sl.size() < 2) { + break; + } + read_range(sl); + } while (m_is_OK); + } + + + void read_bound_by_columns(const std::string & colstr) { + if (colstr.size() < 14) { + (*m_message_stream) << "line is too short" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + // uph, let us try to work with columns + if (colstr.size() >= 22) { + std::string ss = colstr.substr(0, 8); + std::string column_name = trim(ss); + auto t = m_columns.find(column_name); + + if (t == m_columns.end()) { + (*m_message_stream) << "cannot find " << column_name << std::endl; + goto fail; + } else { + vector bound_string; + bound_string.push_back(column_name); + if (colstr.size() > 14) { + bound_string.push_back(colstr.substr(14)); + } + mps_reader::column * col = t->second; + bound * b = col->m_bound; + if (b == nullptr) { + col->m_bound = b = new bound(); + } + update_bound(b, bound_string); + } + } else { + fail: + set_m_ok_to_false(); + (*m_message_stream) << "cannot understand this line" << std::endl; + (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + void update_bound(bound * b, vector bound_string) { + /* + UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. + */ + + std::string bound_type = get_string_from_position(1); + if (bound_type == "BV") { + b->m_upper_is_set = true; + b->m_upper = 1; + return; + } + + if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { + if (bound_string.size() <= 1){ + set_m_ok_to_false(); + return; + } + b->m_upper_is_set = true; + b->m_upper= numeric_traits::from_string(bound_string[1]); + } else if (bound_type == "LO" || bound_type == "LI") { + if (bound_string.size() <= 1){ + set_m_ok_to_false(); + return; + } + + b->m_low_is_set = true; + b->m_low = numeric_traits::from_string(bound_string[1]); + } else if (bound_type == "FR") { + b->m_free = true; + } else if (bound_type == "FX") { + if (bound_string.size() <= 1){ + set_m_ok_to_false(); + return; + } + + b->m_value_is_fixed = true; + b->m_fixed_value = numeric_traits::from_string(bound_string[1]); + } else if (bound_type == "PL") { + b->m_low_is_set = true; + b->m_low = 0; + } else if (bound_type == "MI") { + b->m_upper_is_set = true; + b->m_upper = 0; + } else { + (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; + set_m_ok_to_false(); + throw; + } + } + + void create_or_update_bound() { + const unsigned name_offset = 14; + lp_assert(m_line.size() >= 14); + vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); + + if (bound_string.empty()) { + set_m_ok_to_false(); + (*m_message_stream) << "error at line " << m_line_number << std::endl; + throw m_line; + } + + std::string name = bound_string[0]; + auto it = m_columns.find(name); + if (it == m_columns.end()){ + read_bound_by_columns(m_line.substr(14)); + return; + } + mps_reader::column * col = it->second; + bound * b = col->m_bound; + if (b == nullptr) { + col->m_bound = b = new bound(); + } + update_bound(b, bound_string); + } + + + + void read_range_by_columns(std::string rhsides) { + if (m_line.size() < 14) { + (*m_message_stream) << "line is too short" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + // uph, let us try to work with columns + if (rhsides.size() >= 22) { + std::string ss = rhsides.substr(0, 8); + std::string row_name = trim(ss); + auto t = m_rows.find(row_name); + + if (t == m_rows.end()) { + (*m_message_stream) << "cannot find " << row_name << std::endl; + goto fail; + } else { + row * row = t->second; + row->m_range = numeric_traits::from_string(rhsides.substr(8)); + maybe_modify_current_row_and_add_row_for_range(row); + if (rhsides.size() > 24) { + rhsides = rhsides.substr(25); + if (rhsides.size() >= 22) { + read_range_by_columns(rhsides); + } + } + } + } else { + fail: + set_m_ok_to_false(); + (*m_message_stream) << "cannot understand this line" << std::endl; + (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + + void read_range(vector & splitted_line){ + for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { + auto it = m_rows.find(splitted_line[i]); + if (it == m_rows.end()) { + read_range_by_columns(m_line.substr(14)); + return; + } + row * row = it->second; + row->m_range = numeric_traits::from_string(splitted_line[i + 1]); + maybe_modify_current_row_and_add_row_for_range(row); + } + } + + void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { + unsigned index= static_cast(m_rows.size() - m_cost_line_count); + std::string row_name = row_with_range->m_name + "_range"; + row * other_bound_range_row; + switch (row_with_range->m_type) { + case row_type::Greater_or_equal: + m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); + other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); + break; + case row_type::Less_or_equal: + m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); + other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); + break; + case row_type::Equal: + if (row_with_range->m_range > 0) { + row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change + m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); + } else { // row->m_range < 0; + row_with_range->m_type = row_type::Less_or_equal; // the existing row type change + m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); + } + other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; + break; + default: + (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; + set_m_ok_to_false(); + throw; + } + + for (auto s : row_with_range->m_row_columns) { + lp_assert(m_columns.find(s.first) != m_columns.end()); + other_bound_range_row->m_row_columns[s.first] = s.second; + } + } + + void add_row() { + if (m_line.length() < 2) { + return; + } + + m_line = trim(m_line); + char c = m_line[0]; + m_line = m_line.substr(1); + m_line = trim(m_line); + add_row(c); + } + + void add_row(char c) { + unsigned index= static_cast(m_rows.size() - m_cost_line_count); + switch (c) { + case 'E': + m_rows[m_line] = new row(row_type::Equal, m_line, index); + break; + case 'L': + m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); + break; + case 'G': + m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); + break; + case 'N': + m_rows[m_line] = new row(row_type::Cost, m_line, index); + m_cost_row_name = m_line; + m_cost_line_count++; + break; + } + } + unsigned range_count() { + unsigned ret = 0; + for (auto s : m_rows) { + if (s.second->m_range != 0) { + ret++; + } + } + return ret; + } + + /* + If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: + sense interval + G [rhs, rhs + |range|] + L [rhs - |range|, rhs] + E [rhs, rhs + |range|] if range > 0, + [rhs - |range|, rhs] if range < 0 + where |range| is range's absolute value. + */ + + lp_relation get_relation_from_row(row_type rt) { + switch (rt) { + case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; + case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; + case mps_reader::Equal: return lp_relation::Equal; + default: + (*m_message_stream) << "Unexpected rt " << rt << std::endl; + set_m_ok_to_false(); + throw; + } + } + + unsigned solver_row_count() { + return m_rows.size() - m_cost_line_count + range_count(); + } + + void fill_solver_on_row(row * row, lp_solver *solver) { + if (row->m_name != m_cost_row_name) { + solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); + for (auto s : row->m_row_columns) { + lp_assert(m_columns.find(s.first) != m_columns.end()); + solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); + } + } else { + set_solver_cost(row, solver); + } + } + + T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } + + void fill_solver_on_rows(lp_solver * solver) { + for (auto row_it : m_rows) { + fill_solver_on_row(row_it.second, solver); + } + } + + + void fill_solver_on_columns(lp_solver * solver){ + for (auto s : m_columns) { + mps_reader::column * col = s.second; + unsigned index = col->m_index; + set_boundary_for_column(index, col->m_bound, solver); + // optional call + solver->give_symbolic_name_to_column(col->m_name, col->m_index); + } + } + + void fill_solver(lp_solver *solver) { + fill_solver_on_rows(solver); + fill_solver_on_columns(solver); + } + + void set_solver_cost(row * row, lp_solver *solver) { + for (auto s : row->m_row_columns) { + std::string name = s.first; + lp_assert(m_columns.find(name) != m_columns.end()); + mps_reader::column * col = m_columns[name]; + solver->set_cost_for_column(col->m_index, s.second); + } + } + +public: + + void set_message_stream(std::ostream * o) { + lp_assert(o != nullptr); + m_message_stream = o; + } + vector column_names() { + vector v; + for (auto s : m_columns) { + v.push_back(s.first); + } + return v; + } + + ~mps_reader() { + for (auto s : m_rows) { + delete s.second; + } + for (auto s : m_columns) { + auto col = s.second; + delete col->m_bound; + delete col; + } + } + + mps_reader(const std::string & file_name): + m_is_OK(true), + m_file_name(file_name), + m_file_stream(file_name), + m_cost_line_count(0), + m_line_number(0), + m_message_stream(& std::cout) {} + void read() { + if (!m_file_stream.is_open()){ + set_m_ok_to_false(); + return; + } + + read_name(); + read_rows(); + read_columns(); + read_rhs(); + if (m_line.find("BOUNDS") == 0) { + read_bounds(); + read_ranges(); + } else if (m_line.find("RANGES") == 0) { + read_ranges(); + read_bounds(); + } + } + + bool is_ok() { + return m_is_OK; + } + + lp_solver * create_solver(bool dual) { + lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); + fill_solver(solver); + return solver; + } + + lconstraint_kind get_lar_relation_from_row(row_type rt) { + switch (rt) { + case Less_or_equal: return LE; + case Greater_or_equal: return GE; + case Equal: return EQ; + default: + (*m_message_stream) << "Unexpected rt " << rt << std::endl; + set_m_ok_to_false(); + throw; + } + } + + unsigned get_var_index(std::string s) { + auto it = m_names_to_var_index.find(s); + if (it != m_names_to_var_index.end()) + return it->second; + unsigned ret = static_cast(m_names_to_var_index.size()); + m_names_to_var_index[s] = ret; + return ret; + } + + void fill_lar_solver_on_row(row * row, lar_solver *solver, int row_index) { + if (row->m_name != m_cost_row_name) { + auto kind = get_lar_relation_from_row(row->m_type); + vector> ls; + for (auto s : row->m_row_columns) { + var_index i = solver->add_var(get_var_index(s.first), false); + ls.push_back(std::make_pair(s.second, i)); + } + unsigned j = solver->add_term(ls, row_index); + solver->add_var_bound(j, kind, row->m_right_side); + } else { + // ignore the cost row + } + } + + + void fill_lar_solver_on_rows(lar_solver * solver) { + int row_index = 0; + for (auto row_it : m_rows) { + fill_lar_solver_on_row(row_it.second, solver, row_index++); + } + } + + void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { + var_index i = solver->add_var(col->m_index, false); + solver->add_var_bound(i, GE, b->m_low); + } + + void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { + var_index i = solver->add_var(col->m_index, false); + solver->add_var_bound(i, LE, b->m_upper); + } + + void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { + var_index i = solver->add_var(col->m_index, false); + solver->add_var_bound(i, LE, b->m_fixed_value); + solver->add_var_bound(i, GE, b->m_fixed_value); + } + + void fill_lar_solver_on_columns(lar_solver * solver) { + for (auto s : m_columns) { + mps_reader::column * col = s.second; + solver->add_var(col->m_index, false); + auto b = col->m_bound; + if (b == nullptr) return; + + if (b->m_free) continue; + + if (b->m_low_is_set) { + create_low_constraint_for_var(col, b, solver); + } + if (b->m_upper_is_set) { + create_upper_constraint_for_var(col, b, solver); + } + if (b->m_value_is_fixed) { + create_equality_contraint_for_var(col, b, solver); + } + } + } + + + void fill_lar_solver(lar_solver * solver) { + fill_lar_solver_on_columns(solver); + fill_lar_solver_on_rows(solver); + } + + lar_solver * create_lar_solver() { + lar_solver * solver = new lar_solver(); + fill_lar_solver(solver); + return solver; + } +}; +} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index cde96277b5e..571e9b1d06d 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,6 +24,7 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_dual_core_solver.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 09a56c84efd..af3a462340b 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -19,6 +19,9 @@ Module Name: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 68d5f802592..9ae671c162b 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -19,7 +19,9 @@ Module Name: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" - +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index c0e9c85050f..d9b74f162f9 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(shell opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp + lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp new file mode 100644 index 00000000000..8d6425533c8 --- /dev/null +++ b/src/shell/lp_frontend.cpp @@ -0,0 +1,109 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Author: + + Lev Nachmanson 2016-10-27 + +--*/ + +#include "math/lp/lp_settings.h" +#include "math/lp/mps_reader.h" +#include "util/timeout.h" +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" +#include "util/rlimit.h" +#include "util/gparams.h" +#include "util/mutex.h" +#include +#include +#include "smt/params/smt_params_helper.hpp" + +namespace { +static mutex *display_stats_mux = new mutex; + +static lp::lp_solver* g_solver = nullptr; + +static void display_statistics() { + lock_guard lock(*display_stats_mux); + if (g_solver && g_solver->settings().print_statistics) { + // TBD display relevant information about statistics + } +} + +static void STD_CALL on_ctrl_c(int) { + signal (SIGINT, SIG_DFL); + display_statistics(); + raise(SIGINT); +} + +static void on_timeout() { + display_statistics(); + _Exit(0); +} + +struct front_end_resource_limit : public lp::lp_resource_limit { + reslimit& m_reslim; + + front_end_resource_limit(reslimit& lim): + m_reslim(lim) + {} + + bool get_cancel_flag() override { return !m_reslim.inc(); } +}; + +void run_solver(smt_params_helper & params, char const * mps_file_name) { + + reslimit rlim; + unsigned timeout = gparams::get_ref().get_uint("timeout", 0); + unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); + front_end_resource_limit lp_limit(rlim); + + scoped_rlimit _rlimit(rlim, rlimit); + cancel_eh eh(rlim); + scoped_timer timer(timeout, &eh); + + std::string fn(mps_file_name); + lp::mps_reader reader(fn); + reader.set_message_stream(&std::cout); // can be redirected + reader.read(); + if (!reader.is_ok()) { + std::cerr << "cannot process " << mps_file_name << std::endl; + return; + } + lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver + solver->settings().set_resource_limit(lp_limit); + g_solver = solver; + if (params.arith_min()) { + solver->flip_costs(); + } + solver->settings().set_message_ostream(&std::cout); + solver->settings().report_frequency = params.arith_rep_freq(); + solver->settings().print_statistics = params.arith_print_stats(); + solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); + + solver->find_maximal_solution(); + + *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; + if (solver->get_status() == lp::lp_status::OPTIMAL) { + if (params.arith_min()) { + solver->flip_costs(); + } + solver->print_model(std::cout); + } + + display_statistics(); + g_solver = nullptr; + delete solver; +} +} + +unsigned read_mps_file(char const * mps_file_name) { + signal(SIGINT, on_ctrl_c); + register_on_timeout_proc(on_timeout); + smt_params_helper p; + param_descrs r; + p.collect_param_descrs(r); + run_solver(p, mps_file_name); + return 0; +} diff --git a/src/shell/lp_frontend.h b/src/shell/lp_frontend.h new file mode 100644 index 00000000000..b24be811f18 --- /dev/null +++ b/src/shell/lp_frontend.h @@ -0,0 +1,7 @@ +/* + Copyright (c) 2013 Microsoft Corporation. All rights reserved. + + Author: Lev Nachmanson +*/ +#pragma once +unsigned read_mps_file(char const * mps_file_name); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 26325d3d0a2..4c26d91d968 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -37,13 +37,14 @@ Revision History: #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" +#include "shell/lp_frontend.h" #include "shell/drat_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_DRAT } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; static char const * g_input_file = nullptr; static char const * g_drat_input_file = nullptr; @@ -376,6 +377,10 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } + else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || + strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { + g_input_kind = IN_MPS; + } } } switch (g_input_kind) { @@ -401,6 +406,9 @@ int STD_CALL main(int argc, char ** argv) { case IN_Z3_LOG: replay_z3_log(g_input_file); break; + case IN_MPS: + return_value = read_mps_file(g_input_file); + break; case IN_DRAT: return_value = read_drat(g_drat_input_file); break; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 2aa988282d2..d61910ff20e 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -19,6 +19,9 @@ --*/ #include "util/stopwatch.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 78abf1a6f4d..c82cdd0a4b4 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -33,6 +33,8 @@ #include #include #include "math/lp/lp_utils.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/mps_reader.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" @@ -57,71 +59,6 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" - -bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -size_t number_of_whites(const std::string & s) { - size_t i = 0; - for(;i < s.size(); i++) - if (!my_white_space(s[i])) return i; - return i; -} -size_t number_of_whites_from_end(const std::string & s) { - size_t ret = 0; - for(int i = static_cast(s.size()) - 1;i >= 0; i--) - if (my_white_space(s[i])) ret++;else break; - - return ret; -} - - -std::string <rim(std::string &s) { - s.erase(0, number_of_whites(s)); - return s; -} - - - - - // trim from end -inline std::string &rtrim(std::string &s) { - // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - s.erase(s.end() - number_of_whites_from_end(s), s.end()); - return s; -} - // trim from both ends -inline std::string &trim(std::string &s) { - return ltrim(rtrim(s)); -} - - -vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { - vector results; - size_t prev = 0; - size_t next = 0; - while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { - if (keep_empty || (next - prev != 0)) { - results.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - if (prev < source.size()) { - results.push_back(source.substr(prev)); - } - return results; -} - -vector split_and_trim(const std::string &line) { - auto split = string_split(line, " \t", false); - vector ret; - for (auto s : split) { - ret.push_back(trim(s)); - } - return ret; -} - - namespace nla { void test_horner(); void test_monics(); @@ -1458,15 +1395,169 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { } } +template +void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { + if (time_limit > 0) + solver->set_time_limit(time_limit); + + if (look_for_min) + solver->flip_costs(); + + update_settings(args_parser, solver->settings()); +} bool values_are_one_percent_close(double a, double b); +void print_x(mps_reader & reader, lp_solver * solver) { + for (const auto & name : reader.column_names()) { + std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; + } + std::cout << std::endl; +} +void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { + for (const auto & name : reader.column_names()) { + double a = solver->get_column_value_by_name(name); + double b = solver0->get_column_value_by_name(name); + if (!values_are_one_percent_close(a, b)) { + std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; + } + } +} +void solve_mps_double(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { + mps_reader reader(file_name); + reader.read(); + if (!reader.is_ok()) { + std::cout << "cannot process " << file_name << std::endl; + return; + } + + lp_solver * solver = reader.create_solver(dual); + setup_solver(time_limit, look_for_min, args_parser, solver); + stopwatch sw; + sw.start(); + if (dual) { + std::cout << "solving for dual" << std::endl; + } + solver->find_maximal_solution(); + sw.stop(); + double span = sw.get_seconds(); + std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; + if (solver->get_status() == lp_status::OPTIMAL) { + if (reader.column_names().size() < 20) { + print_x(reader, solver); + } + double cost = solver->get_current_cost(); + if (look_for_min) { + cost = -cost; + } + std::cout << "cost = " << cost << std::endl; + } + std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; + if (compare_with_primal) { + auto * primal_solver = reader.create_solver(false); + setup_solver(time_limit, look_for_min, args_parser, primal_solver); + primal_solver->find_maximal_solution(); + if (solver->get_status() != primal_solver->get_status()) { + std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; + } else { + if (solver->get_status() == lp_status::OPTIMAL) { + double cost = solver->get_current_cost(); + if (look_for_min) { + cost = -cost; + } + double primal_cost = primal_solver->get_current_cost(); + if (look_for_min) { + primal_cost = -primal_cost; + } + std::cout << "primal cost = " << primal_cost << std::endl; + if (!values_are_one_percent_close(cost, primal_cost)) { + compare_solutions(reader, primal_solver, solver); + print_x(reader, primal_solver); + std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; + lp_assert(false); + } + } + } + delete primal_solver; + } + delete solver; +} + +void solve_mps_rational(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, argument_parser & args_parser) { + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + auto * solver = reader.create_solver(dual); + setup_solver(time_limit, look_for_min, args_parser, solver); + stopwatch sw; + sw.start(); + solver->find_maximal_solution(); + std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; + + if (solver->get_status() == lp_status::OPTIMAL) { + // for (auto name: reader.column_names()) { + // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; + // } + lp::mpq cost = solver->get_current_cost(); + if (look_for_min) { + cost = -cost; + } + std::cout << "cost = " << cost.get_double() << std::endl; + } + std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; + delete solver; + } else { + std::cout << "cannot process " << file_name << std::endl; + } +} void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition +void solve_mps(std::string file_name, bool look_for_min, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { + if (!solve_for_rational) { + std::cout << "solving " << file_name << std::endl; + solve_mps_double(file_name, look_for_min, time_limit, dual, compare_with_primal, args_parser); + } + else { + std::cout << "solving " << file_name << " in rationals " << std::endl; + solve_mps_rational(file_name, look_for_min, time_limit, dual, args_parser); + } +} +void solve_mps(std::string file_name, argument_parser & args_parser) { + bool look_for_min = args_parser.option_is_used("--min"); + unsigned time_limit; + bool solve_for_rational = args_parser.option_is_used("--mpq"); + bool dual = args_parser.option_is_used("--dual"); + bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + solve_mps(file_name, look_for_min, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); +} + +void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { + std::cout << "solving " << file_name << std::endl; + + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + auto * solver = reader.create_solver(dual); + solver->find_maximal_solution(); + std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; + if (solver->get_status() == lp_status::OPTIMAL) { + if (reader.column_names().size() < 20) { + for (const auto & name : reader.column_names()) { + std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; + } + } + std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; + } + delete solver; + } else { + std::cout << "cannot process " << file_name << std::endl; + } +} void test_upair_queue() { int n = 10; @@ -1535,6 +1626,53 @@ void test_binary_priority_queue() { std::cout << " done" << std::endl; } +bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); + return solver->solution_is_feasible(solution); + } + return false; +} + + +void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { + std::cout << "solving " << file_name << std::endl; + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + auto * solver = reader.create_solver(dual); + solver->find_maximal_solution(); + std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; + if (status != solver->get_status()){ + std::cout << "status should be " << lp_status_to_string(status) << std::endl; + lp_assert(status == solver->get_status()); + throw "status is wrong"; + } + if (solver->get_status() == lp_status::OPTIMAL) { + std::cout << "cost = " << solver->get_current_cost() << std::endl; + if (solution != nullptr) { + for (auto it : *solution) { + if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { + std::cout << "expected:" << it.first << "=" << + it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; + } + lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); + } + } + if (reader.column_names().size() < 20) { + for (const auto & name : reader.column_names()) { + std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; + } + std::cout << std::endl; + } + } + delete solver; + } else { + std::cout << "cannot process " << file_name << std::endl; + } +} int get_random_rows() { return 5 + my_random() % 2; @@ -1548,6 +1686,55 @@ int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } +void add_random_row(lp_primal_simplex * solver, int cols, int row) { + solver->add_constraint(lp_relation::Greater_or_equal, 1, row); + for (int i = 0; i < cols; i++) { + solver->set_row_column_coefficient(row, i, get_random_int()); + } +} + +void add_random_cost(lp_primal_simplex * solver, int cols) { + for (int i = 0; i < cols; i++) { + solver->set_cost_for_column(i, get_random_int()); + } +} + +lp_primal_simplex * generate_random_solver() { + int rows = get_random_rows(); + int cols = get_random_columns(); + auto * solver = new lp_primal_simplex(); + for (int i = 0; i < rows; i++) { + add_random_row(solver, cols, i); + } + add_random_cost(solver, cols); + return solver; +} + + + +void random_test_on_i(unsigned i) { + if (i % 1000 == 0) { + std::cout << "."; + } + srand(i); + auto *solver = generate_random_solver(); + solver->find_maximal_solution(); + // std::cout << lp_status_to_string(solver->get_status()) << std::endl; + delete solver; +} + +void random_test() { + for (unsigned i = 0; i < std::numeric_limits::max(); i++) { + try { + random_test_on_i(i); + } + catch (const char * error) { + std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; + break; + } + } +} + #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); @@ -1709,9 +1896,140 @@ void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // std::cout << "fn = " << fn << std::endl; } - +void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); + +void solve_some_mps(argument_parser & args_parser) { + unsigned max_iters = UINT_MAX, time_limit = UINT_MAX; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + unsigned successes = 0; + unsigned failures = 0; + unsigned inconclusives = 0; + std::set minimums; + vector file_names; + fill_file_names(file_names, minimums); + bool solve_for_rational = args_parser.option_is_used("--mpq"); + bool dual = args_parser.option_is_used("--dual"); + bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); + bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); + if (compare_with_glpk) { + std::string out_dir = args_parser.get_option_value("--out_dir"); + if (out_dir.size() == 0) { + out_dir = "/tmp/test"; + } + test_out_dir(out_dir); + for (auto& a : file_names) { + try { + std::string file_dir; + std::string file_name; + find_dir_and_file_name(a, file_dir, file_name); + process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); + } + catch(const char *s){ + std::cout<< "exception: "<< s << std::endl; + } + } + std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; + return; + } + if (!solve_for_rational) { + solve_mps(file_names[6], false, time_limit, false, dual, compare_with_primal, args_parser); + solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) + std::unordered_map sol; + sol["X1"] = 0; + sol["X2"] = 6; + sol["X3"] = 0; + sol["X4"] = 15; + sol["X5"] = 2; + sol["X6"] = 1; + sol["X7"] = 1; + sol["X8"] = 0; + solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); + solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); + sol.clear(); + sol["X1"] = 25.0/14.0; + // sol["X2"] = 0; + // sol["X3"] = 0; + // sol["X4"] = 0; + // sol["X5"] = 0; + // sol["X6"] = 0; + // sol["X7"] = 9.0/14.0; + solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) + solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) + solve_mps(file_names[8], false, time_limit, false, dual, compare_with_primal, args_parser); + // return; + for (auto& s : file_names) { + try { + solve_mps(s, minimums.find(s) != minimums.end(), time_limit, false, dual, compare_with_primal, args_parser); + } + catch(const char *s){ + std::cout<< "exception: "<< s << std::endl; + } + } + } else { + // unsigned i = 0; + for (auto& s : file_names) { + // if (i++ > 9) return; + try { + solve_mps_in_rational(s, dual, args_parser); + } + catch(const char *s){ + std::cout<< "exception: "<< s << std::endl; + } + } + } +} #endif +void solve_rational() { + lp_primal_simplex solver; + solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); + solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); + + // setting the cost + int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; + std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; + + for (unsigned i = 0; i < 8; i++) { + solver.set_cost_for_column(i, lp::mpq(cost[i])); + solver.give_symbolic_name_to_column(var_names[i], i); + } + + int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; + for (unsigned i = 0; i < 8; i++) { + solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); + } + + int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; + for (unsigned i = 0; i < 8; i++) { + solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); + } + + int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; + for (unsigned i = 0; i < 8; i++) { + solver.set_lower_bound(i, lp::mpq(0)); + solver.set_upper_bound(i, lp::mpq(bounds[i])); + } + + std::unordered_map expected_sol; + expected_sol["x1"] = lp::mpq(0); + expected_sol["x2"] = lp::mpq(6); + expected_sol["x3"] = lp::mpq(0); + expected_sol["x4"] = lp::mpq(15); + expected_sol["x5"] = lp::mpq(2); + expected_sol["x6"] = lp::mpq(1); + expected_sol["x7"] = lp::mpq(1); + expected_sol["x8"] = lp::mpq(0); + solver.find_maximal_solution(); + lp_assert(solver.get_status() == lp_status::OPTIMAL); +#ifdef Z3DEBUG + for (const auto & it : expected_sol) { + (void)it; + lp_assert(it.second == solver.get_column_value_by_name(it.first)); + } +#endif +} std::string read_line(bool & end, std::ifstream & file) { @@ -1729,6 +2047,49 @@ bool contains(std::string const & s, char const * pattern) { } +std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { + std::ifstream file(file_name); + if (!file.is_open()){ + std::cerr << "cannot open " << file_name << std::endl; + return nullptr; + } + std::string s; + bool end; + do { + s = read_line(end, file); + if (end) { + std::cerr << "unexpected file end " << file_name << std::endl; + return nullptr; + } + if (contains(s, "Column name")){ + break; + } + } while (true); + + read_line(end, file); + if (end) { + std::cerr << "unexpected file end " << file_name << std::endl; + return nullptr; + } + + auto ret = new std::unordered_map(); + + do { + s = read_line(end, file); + if (end) { + std::cerr << "unexpected file end " << file_name << std::endl; + return nullptr; + } + auto split = string_split(s, " \t", false); + if (split.empty()) { + return ret; + } + + lp_assert(split.size() > 3); + (*ret)[split[1]] = atof(split[3].c_str()); + } while (true); +} + void test_init_U() { @@ -1828,6 +2189,7 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); + parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); @@ -1998,9 +2360,237 @@ std::string get_status(std::string file_name) { throw 0; } +// returns true if the costs should be compared too +bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { + std::string glpk_status = get_status(glpk_out_file_name); + std::string lp_tst_status = get_status(lp_out_file_name); + + if (glpk_status != lp_tst_status) { + if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { + successes++; + return false; + } else { + std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; + std::cout << " but lp_tst status is " << lp_tst_status << std::endl; + failures++; + return false; + } + } + return lp_tst_status == "OPTIMAL"; +} + +double get_glpk_cost(std::string file_name) { + std::ifstream f(file_name); + if (!f.is_open()) { + std::cout << "cannot open " << file_name << std::endl; + throw 0; + } + std::string str; + while (getline(f, str)) { + if (str.find("Objective") != std::string::npos) { + vector tokens = split_and_trim(str); + if (tokens.size() != 5) { + std::cout << "unexpected Objective std::string " << str << std::endl; + throw 0; + } + return atof(tokens[3].c_str()); + } + } + std::cout << "cannot find the Objective line in " << file_name << std::endl; + throw 0; +} + +double get_lp_tst_cost(std::string file_name) { + std::ifstream f(file_name); + if (!f.is_open()) { + std::cout << "cannot open " << file_name << std::endl; + throw 0; + } + std::string str; + std::string cost_string; + while (getline(f, str)) { + if (str.find("cost") != std::string::npos) { + cost_string = str; + } + } + if (cost_string.empty()) { + std::cout << "cannot find the cost line in " << file_name << std::endl; + throw 0; + } + + vector tokens = split_and_trim(cost_string); + if (tokens.size() != 3) { + std::cout << "unexpected cost string " << cost_string << std::endl; + throw 0; + } + return atof(tokens[2].c_str()); +} + +bool values_are_one_percent_close(double a, double b) { + double maxval = std::max(fabs(a), fabs(b)); + if (maxval < 0.000001) { + return true; + } + + double one_percent = maxval / 100; + return fabs(a - b) <= one_percent; +} + +// returns true if both are optimal +void compare_costs(std::string glpk_out_file_name, + std::string lp_out_file_name, + unsigned & successes, + unsigned & failures) { + double a = get_glpk_cost(glpk_out_file_name); + double b = get_lp_tst_cost(lp_out_file_name); + + if (values_are_one_percent_close(a, b)) { + successes++; + } else { + failures++; + std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; + } +} + +void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { +#ifdef CHECK_GLPK_SOLUTION + std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); + if (solution_is_feasible(lp_file_name, *solution_table)) { + std::cout << "glpk solution is feasible" << std::endl; + } else { + std::cout << "glpk solution is infeasible" << std::endl; + } + delete solution_table; +#endif + if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { + compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); + } +} +void test_lar_on_file(std::string file_name, argument_parser & args_parser); +void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { + bool use_mpq = args_parser.option_is_used("--mpq"); + bool minimize = args_parser.option_is_used("--min"); + std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); + + std::string input_file_name = test_dir + "/" + test_file_name; + if (input_file_name[input_file_name.size() - 1] == '~') { + // std::cout << "ignoring " << input_file_name << std::endl; + return; + } + std::cout <<"processing " << input_file_name << std::endl; + + std::ofstream out(full_lp_tst_out_name); + if (!out.is_open()) { + std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; + throw 0; + } + std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer + std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! + bool dual = args_parser.option_is_used("--dual"); + try { + if (args_parser.option_is_used("--lar")) + test_lar_on_file(input_file_name, args_parser); + else + solve_mps(input_file_name, minimize, time_limit, use_mpq, dual, false, args_parser); + } + catch(...) { + std::cout << "catching the failure" << std::endl; + failures++; + std::cout.rdbuf(coutbuf); // reset to standard output again + return; + } + std::cout.rdbuf(coutbuf); // reset to standard output again + + if (args_parser.option_is_used("--compare_with_glpk")) { + std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); + int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); + if (glpk_exit_code != 0) { + std::cout << "glpk failed" << std::endl; + inconclusives++; + } else { + compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); + } + } +} +/* + int my_readdir(DIR *dirp, struct dirent * + #ifndef LEAN_WINDOWS + entry + #endif + , struct dirent **result) { + #ifdef LEAN_WINDOWS + *result = readdir(dirp); // NOLINT + return *result != nullptr? 0 : 1; + #else + return readdir_r(dirp, entry, result); + #endif + } +*/ +/* + vector> get_file_list_of_dir(std::string test_file_dir) { + DIR *dir; + if ((dir = opendir(test_file_dir.c_str())) == nullptr) { + std::cout << "Cannot open directory " << test_file_dir << std::endl; + throw 0; + } + vector> ret; + struct dirent entry; + struct dirent* result; + int return_code; + for (return_code = my_readdir(dir, &entry, &result); + #ifndef LEAN_WINDOWS + result != nullptr && + #endif + return_code == 0; + return_code = my_readdir(dir, &entry, &result)) { + DIR *tmp_dp = opendir(result->d_name); + struct stat file_record; + if (tmp_dp == nullptr) { + std::string s = test_file_dir+ "/" + result->d_name; + int stat_ret = stat(s.c_str(), & file_record); + if (stat_ret!= -1) { + ret.push_back(make_pair(result->d_name, file_record.st_size)); + } else { + perror("stat"); + exit(1); + } + } else { + closedir(tmp_dp); + } + } + closedir(dir); + return ret; + } +*/ +/* + struct file_size_comp { + unordered_map& m_file_sizes; + file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} + int operator()(std::string a, std::string b) { + std::cout << m_file_sizes.size() << std::endl; + std::cout << a << std::endl; + std::cout << b << std::endl; + + auto ls = m_file_sizes.find(a); + std::cout << "fa" << std::endl; + auto rs = m_file_sizes.find(b); + std::cout << "fb" << std::endl; + if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { + std::cout << "fc " << std::endl; + int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); + std::cout << "calc r " << std::endl; + return r; + } else { + std::cout << "sc " << std::endl; + return 0; + } + } + }; + +*/ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; @@ -2008,11 +2598,121 @@ struct sort_pred { }; +void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { + /* + std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; + std::string out_dir = args_parser.get_option_value("--out_dir"); + if (out_dir.size() == 0) { + out_dir = "/tmp/test"; + } + DIR *out_dir_p = opendir(out_dir.c_str()); + if (out_dir_p == nullptr) { + std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; + return; + } + closedir(out_dir_p); + vector> files = get_file_list_of_dir(test_file_dir); + std::sort(files.begin(), files.end(), sort_pred()); + unsigned max_iters, time_limit; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + unsigned successes = 0, failures = 0, inconclusives = 0; + for (auto & t : files) { + process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); + } + std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; + */ +} +std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { + std::unordered_map ret; + for (const auto & it : reader.column_names()) { + ret[it] = lps->get_column_value_by_name(it); + } + return ret; +} +void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { + std::string maxng = args_parser.get_option_value("--maxng"); + if (!maxng.empty()) { + solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); + } + if (args_parser.option_is_used("-pd")){ + solver->settings().presolve_with_double_solver_for_lar = true; + } + + if (args_parser.option_is_used("--compare_with_primal")){ + if (reader == nullptr) { + std::cout << "cannot compare with primal, the reader is null " << std::endl; + return; + } + auto * lps = reader->create_solver(false); + lps->find_maximal_solution(); + std::unordered_map sol = get_solution_map(lps, *reader); + std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; + return; + } + stopwatch sw; + sw.start(); + lp_status status = solver->solve(); + std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; + if (solver->get_status() == lp_status::INFEASIBLE) { + explanation evidence; + solver->get_infeasibility_explanation(evidence); + } + if (args_parser.option_is_used("--randomize_lar")) { + if (solver->get_status() != lp_status::OPTIMAL) { + std::cout << "cannot check randomize on an infeazible problem" << std::endl; + return; + } + std::cout << "checking randomize" << std::endl; + vector all_vars; + for (unsigned j = 0; j < solver->number_of_vars(); j++) + all_vars.push_back(j); + + unsigned m = all_vars.size(); + if (m > 100) + m = 100; + + var_index *vars = new var_index[m]; + for (unsigned i = 0; i < m; i++) + vars[i]=all_vars[i]; + + solver->random_update(m, vars); + delete []vars; + } +} +lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { + if (args_parser.option_is_used("--smt")) { + smt_reader reader(file_name); + reader.read(); + if (!reader.is_ok()){ + std::cout << "cannot process " << file_name << std::endl; + return nullptr; + } + return reader.create_lar_solver(); + } + mps_reader reader(file_name); + reader.read(); + if (!reader.is_ok()) { + std::cout << "cannot process " << file_name << std::endl; + return nullptr; + } + return reader.create_lar_solver(); +} +void test_lar_on_file(std::string file_name, argument_parser & args_parser) { + lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); + mps_reader reader(file_name); + mps_reader * mps_reader = nullptr; + reader.read(); + if (reader.is_ok()) { + mps_reader = & reader; + run_lar_solver(args_parser, solver, mps_reader); + } + delete solver; +} vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); @@ -2033,6 +2733,23 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } +void test_lar_solver(argument_parser & args_parser) { + + std::string file_name = args_parser.get_option_value("--file"); + if (!file_name.empty()) { + test_lar_on_file(file_name, args_parser); + return; + } + + std::string file_list = args_parser.get_option_value("--filelist"); + if (!file_list.empty()) { + for (const std::string & fn : get_file_names_from_file_list(file_list)) + test_lar_on_file(fn, args_parser); + return; + } + + std::cout << "give option --file or --filelist to test_lar_solver\n"; +} void test_numeric_pair() { numeric_pair a; @@ -3217,6 +3934,22 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } + if (args_parser.option_is_used("--test_mpq")) { + test_rationals(); + return finalize(0); + } + + if (args_parser.option_is_used("--test_mpq_np")) { + test_rationals_no_numeric_pairs(); + return finalize(0); + } + + if (args_parser.option_is_used("--test_mpq_np_plus")) { + test_rationals_no_numeric_pairs_plus(); + return finalize(0); + } + + if (args_parser.option_is_used("--test_int_set")) { test_int_set(); @@ -3234,8 +3967,29 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - - +#ifdef Z3DEBUG + if (args_parser.option_is_used("--test_swaps")) { + square_sparse_matrix m(10, 0); + fill_matrix(m); + test_swap_rows_with_permutation(m); + test_swap_cols_with_permutation(m); + return finalize(0); + } +#endif + if (args_parser.option_is_used("--test_perm")) { + test_permutations(); + return finalize(0); + } + if (args_parser.option_is_used("--test_file_directory")) { + test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); + return finalize(0); + } + std::string file_list = args_parser.get_option_value("--filelist"); + if (!file_list.empty()) { + for (const std::string & fn : get_file_names_from_file_list(file_list)) + solve_mps(fn, args_parser); + return finalize(0); + } if (args_parser.option_is_used("-tbq")) { test_binary_priority_queue(); @@ -3243,6 +3997,100 @@ void test_lp_local(int argn, char**argv) { return finalize(ret); } +#ifdef Z3DEBUG + lp_settings settings; + update_settings(args_parser, settings); + if (args_parser.option_is_used("--test_lu")) { + test_lu(settings); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--test_small_lu")) { + test_small_lu(settings); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--lar")){ + std::cout <<"calling test_lar_solver" << std::endl; + test_lar_solver(args_parser); + return finalize(0); + } + + + + if (args_parser.option_is_used("--test_larger_lu")) { + test_larger_lu(settings); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--test_larger_lu_with_holes")) { + test_larger_lu_with_holes(settings); + ret = 0; + return finalize(ret); + } +#endif + if (args_parser.option_is_used("--eti")) { + test_evidence_for_total_inf_simple(args_parser); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--maximize_term")) { + test_maximize_term(); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--test_lp_0")) { + test_lp_0(); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--smap")) { + test_stacked(); + ret = 0; + return finalize(ret); + } + if (args_parser.option_is_used("--term")) { + test_term(); + ret = 0; + return finalize(ret); + } + unsigned time_limit; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + bool dual = args_parser.option_is_used("--dual"); + bool solve_for_rational = args_parser.option_is_used("--mpq"); + std::string file_name = args_parser.get_option_value("--file"); + if (!file_name.empty()) { + solve_mps(file_name, args_parser.option_is_used("--min"), time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--solve_some_mps")) { +#ifndef _WINDOWS + solve_some_mps(args_parser); +#endif + ret = 0; + return finalize(ret); + } + // lp::ccc = 0; + return finalize(0); + test_init_U(); + test_replace_column(); +#ifdef Z3DEBUG + square_sparse_matrix_with_permutations_test(); + test_dense_matrix(); + test_swap_operations(); + test_permutations(); + test_pivot_like_swaps_and_pivot(); +#endif + tst1(); + std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 75edb23b987..2ab0c1ea69a 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -20,14 +20,18 @@ Revision History: #pragma once +// reads an MPS file representing a Mixed Integer Program #include #include #include +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/lar_solver.h" #include #include #include #include +#include "math/lp/mps_reader.h" #include "math/lp/ul_pair.h" #include "math/lp/lar_constraints.h" #include diff --git a/src/test/lp/test_file_reader.h b/src/test/lp/test_file_reader.h index 36b27374023..8f461ea1c26 100644 --- a/src/test/lp/test_file_reader.h +++ b/src/test/lp/test_file_reader.h @@ -27,6 +27,7 @@ Revision History: #include #include #include "math/lp/lp_utils.h" +#include "math/lp/lp_solver.h" namespace lp { From b9a87e493babf0d545614610942fda0988d3e8c0 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 5 Mar 2023 19:08:41 +0000 Subject: [PATCH 470/597] minor code simplifications --- src/ast/rewriter/hoist_rewriter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index d5b2042a2d0..72a764bfa37 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -37,7 +37,7 @@ expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { continue; else negs.push_back(::mk_not(m, a)); - return expr_ref(::mk_not(m, mk_or(negs)), m); + return ::mk_not(mk_or(negs)); } else return ::mk_and(args); @@ -164,7 +164,6 @@ 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), args1(m), fmls(m); for (unsigned i = 0; i < num_args; ++i) { VERIFY(is_and(es[i], &args1)); @@ -178,8 +177,7 @@ expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsi fmls.push_back(mk_or(args)); for (auto* p : preds) fmls.push_back(p); - result = mk_and(fmls); - return result; + return mk_and(fmls); } From 42076a3c13e67f89e6ab577f1de3f67e870905fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Mar 2023 22:26:27 -0800 Subject: [PATCH 471/597] bug fixes to new core, elim_predicates and elim_unconstrained --- src/ast/simplifiers/elim_unconstrained.cpp | 12 ++++---- src/ast/simplifiers/elim_unconstrained.h | 4 ++- src/ast/simplifiers/eliminate_predicates.cpp | 31 ++++++++++---------- src/ast/simplifiers/eliminate_predicates.h | 3 +- src/model/model_core.cpp | 2 +- src/sat/smt/euf_model.cpp | 5 ++-- src/sat/smt/euf_solver.cpp | 9 ++++-- src/sat/smt/euf_solver.h | 2 +- src/sat/smt/q_mbi.cpp | 2 +- src/sat/tactic/goal2sat.cpp | 2 +- 10 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 41877202aae..3d2566193e5 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -52,9 +52,9 @@ monotonicity or reflexivity rules. #include "ast/simplifiers/elim_unconstrained.h" elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) : - dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m) { + dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m), m_args(m) { std::function is_var = [&](expr* e) { - return is_uninterp_const(e) && !m_fmls.frozen(e) && get_node(e).m_refcount <= 1; + return is_uninterp_const(e) && !m_fmls.frozen(e) && is_node(e) && get_node(e).m_refcount <= 1; }; m_inverter.set_is_var(is_var); } @@ -114,7 +114,7 @@ void elim_unconstrained::eliminate() { gc(e); invalidate_parents(e); freeze_rec(r); - + m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; get_node(e).m_proof = pr; @@ -291,7 +291,7 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { unsigned sz0 = todo.size(); if (is_app(t)) { for (expr* arg : *to_app(t)) - if (get_node(arg).m_dirty) + if (get_node(arg).m_dirty || !get_node(arg).m_term) todo.push_back(arg); if (todo.size() != sz0) continue; @@ -300,18 +300,20 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); n.m_term = m.mk_app(to_app(t)->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + m_args.shrink(sz); } else if (is_quantifier(t)) { expr* body = to_quantifier(t)->get_expr(); node& n2 = get_node(body); - if (n2.m_dirty) { + if (n2.m_dirty || !n2.m_term) { todo.push_back(body); continue; } n.m_term = m.update_quantifier(to_quantifier(t), n2.m_term); } m_trail.push_back(n.m_term); + m_root.setx(n.m_term->get_id(), n.m_term->get_id(), UINT_MAX); todo.pop_back(); n.m_dirty = false; } diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 19af099d0e2..0fdde5af2b5 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -46,13 +46,15 @@ class elim_unconstrained : public dependent_expr_simplifier { var_lt m_lt; heap m_heap; expr_ref_vector m_trail; - ptr_vector m_args; + expr_ref_vector m_args; stats m_stats; unsigned_vector m_root; bool m_created_compound = false; bool m_enable_proofs = false; bool is_var_lt(int v1, int v2) const; + bool is_node(unsigned n) const { return m_nodes.size() > n; } + bool is_node(expr* t) const { return is_node(t->get_id()); } node& get_node(unsigned n) { return m_nodes[n]; } node const& get_node(unsigned n) const { return m_nodes[n]; } node& get_node(expr* t) { return m_nodes[root(t)]; } diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index b2cc5e25dfa..0dfb5a87e83 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -174,7 +174,7 @@ bool eliminate_predicates::can_be_quasi_macro_head(expr* _head, unsigned num_bou // then replace (f x y z) by (if (= z (+ x y)) s (f' x y z)) // -void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause const& cl) { +void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause& cl) { expr_ref _body(body, m); uint_set indices; expr_ref_vector args(m), eqs(m); @@ -208,7 +208,7 @@ void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause cons lhs = m.mk_app(f, args); rhs = m.mk_ite(mk_and(eqs), body, m.mk_app(f1, args)); - insert_macro(lhs, rhs, cl.m_dep); + insert_macro(lhs, rhs, cl); } @@ -311,6 +311,12 @@ bool eliminate_predicates::is_macro_safe(expr* e) { return true; } +void eliminate_predicates::insert_macro(app* head, expr* def, clause& cl) { + insert_macro(head, def, cl.m_dep); + TRACE("elim_predicates", tout << "remove " << cl << "\n"); + cl.m_alive = false; +} + void eliminate_predicates::insert_macro(app* head, expr* def, expr_dependency* dep) { unsigned num = head->get_num_args(); ptr_buffer vars, subst_args; @@ -335,7 +341,7 @@ void eliminate_predicates::insert_macro(app* head, expr* def, expr_dependency* d auto* info = alloc(macro_def, _head, _def, _dep); m_macros.insert(head->get_decl(), info); m_fmls.model_trail().push(head->get_decl(), _def, _dep, {}); // augment with definition for head - m_is_macro.mark(head->get_decl(), true); + m_is_macro.mark(head->get_decl(), true); TRACE("elim_predicates", tout << "insert " << _head << " " << _def << "\n"); ++m_stats.m_num_macros; } @@ -368,26 +374,22 @@ void eliminate_predicates::try_find_macro(clause& cl) { // (= (f x) t) if (cl.is_unit() && !cl.sign(0) && m.is_eq(cl.atom(0), x, y)) { if (can_be_def(x, y)) { - insert_macro(to_app(x), y, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(x), y, cl); return; } if (can_be_def(y, x)) { - insert_macro(to_app(y), x, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(y), x, cl); return; } } // not (= (p x) t) -> (p x) = (not t) if (cl.is_unit() && cl.sign(0) && m.is_iff(cl.atom(0), x, y)) { if (can_be_def(x, y)) { - insert_macro(to_app(x), m.mk_not(y), cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(x), m.mk_not(y), cl); return; } if (can_be_def(y, x)) { - insert_macro(to_app(y), m.mk_not(x), cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(y), m.mk_not(x), cl); return; } } @@ -414,8 +416,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { m_fmls.model_trail().hide(fn); // hide definition of fn k = m.mk_app(fn, f->get_num_args(), f->get_args()); def = m.mk_ite(cond, t, k); - insert_macro(f, def, cl.m_dep); - cl.m_alive = false; + insert_macro(f, def, cl); fml = m.mk_not(m.mk_eq(k, t)); clause* new_cl = init_clause(fml, cl.m_dep, UINT_MAX); add_use_list(*new_cl); @@ -532,8 +533,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { expr_ref y1 = subtract(y, to_app(x), i); if (inv) y1 = uminus(y1); - insert_macro(to_app(arg), y1, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(arg), y1, cl); return true; } next: @@ -737,6 +737,7 @@ void eliminate_predicates::update_model(func_decl* p) { } rewrite(def); + TRACE("elim_predicates", tout << "insert " << p->get_name() << " " << def << "\n"); m_fmls.model_trail().push(p, def, dep, deleted); } diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index af0ede11938..013c7cf65cd 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -111,9 +111,10 @@ class eliminate_predicates : public dependent_expr_simplifier { bool try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); void try_resolve_definition(func_decl* p); void insert_macro(app* head, expr* def, expr_dependency* dep); + void insert_macro(app* head, expr* def, clause& cl); expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); bool can_be_macro_head(expr* head, unsigned num_bound); - void insert_quasi_macro(app* head, expr* body, clause const& cl); + void insert_quasi_macro(app* head, expr* body, clause& cl); bool can_be_quasi_macro_head(expr* head, unsigned num_bound); bool is_macro_safe(expr* e); void try_find_macro(clause& cl); diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index a23fc9800e7..222247c0721 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -81,7 +81,7 @@ void model_core::register_decl(func_decl * d, func_interp * fi) { } func_interp* model_core::update_func_interp(func_decl* d, func_interp* fi) { - TRACE("model", tout << "register " << d->get_name() << "\n";); + TRACE("model_verbose", tout << "register " << d->get_name() << "\n";); SASSERT(d->get_arity() > 0); SASSERT(&fi->m() == &m); diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index 0fd021d7045..b117ac1e32e 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -67,7 +67,7 @@ namespace euf { m_qmodel = mdl; } - void solver::update_model(model_ref& mdl) { + void solver::update_model(model_ref& mdl, bool validate) { TRACE("model", tout << "create model\n";); if (m_qmodel) { mdl = m_qmodel; @@ -87,7 +87,8 @@ namespace euf { for (auto* mb : m_solvers) mb->finalize_model(*mdl); TRACE("model", tout << "created model " << *mdl << "\n";); - validate_model(*mdl); + if (validate) + validate_model(*mdl); } bool solver::include_func_interp(func_decl* f) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 8efb433ffaf..f4e9c2c64ba 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -478,8 +478,13 @@ namespace euf { m_ackerman->cg_conflict_eh(a, b); switch (s().value(lit)) { case l_true: - if (n->merge_tf() && !m.is_value(n->get_root()->get_expr())) - m_egraph.merge(n, ante, to_ptr(lit)); + if (!n->merge_tf()) + break; + if (m.is_value(n->get_root()->get_expr())) + break; + if (!ante) + ante = mk_true(); + m_egraph.merge(n, ante, to_ptr(lit)); break; case l_undef: case l_false: diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index e51b68b0911..1a2cdf123f5 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -495,7 +495,7 @@ namespace euf { // model construction void save_model(model_ref& mdl); - void update_model(model_ref& mdl); + void update_model(model_ref& mdl, bool validate); obj_map const& values2root(); void model_updated(model_ref& mdl); expr* node2value(enode* n) const; diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 21842ec7699..c66f1b3a22c 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -635,7 +635,7 @@ namespace q { if (m_model) return; m_model = alloc(model, m); - ctx.update_model(m_model); + ctx.update_model(m_model, false); } void mbqi::init_solver() { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 865c5f15d72..c107a74c500 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -987,7 +987,7 @@ struct goal2sat::imp : public sat::sat_internalizer { void update_model(model_ref& mdl) { auto* ext = dynamic_cast(m_solver.get_extension()); if (ext) - ext->update_model(mdl); + ext->update_model(mdl, true); } void user_push() { From f7c9c9ef72fde0e00ef9409e6c24456effb8b930 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Mon, 6 Mar 2023 22:28:22 -0500 Subject: [PATCH 472/597] fix unsound slice criteria (#6625) * rename for readability * bug fix #6617. Don't slice op args that are values --- src/muz/transforms/dl_mk_slice.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index 25888cb6807..c13509224ff 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -467,15 +467,15 @@ namespace datalog { void mk_slice::solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars) { expr_ref_vector conjs = get_tail_conjs(r); for (expr * e : conjs) { - expr_ref r(m); + expr_ref rhs(m); unsigned v = 0; - if (is_eq(e, v, r) && is_output(v) && m_var_is_sliceable[v]) { + if (is_eq(e, v, rhs) && is_output(v) && m_var_is_sliceable[v]) { TRACE("dl", tout << "is_eq: " << mk_pp(e, m) << " " << (m_solved_vars[v].get()?"solved":"new") << "\n";); add_var(v); if (!m_solved_vars[v].get()) { TRACE("dl", tout << v << " is solved\n";); - add_free_vars(parameter_vars, r); - m_solved_vars[v] = r; + add_free_vars(parameter_vars, rhs); + m_solved_vars[v] = rhs; } else { TRACE("dl", tout << v << " is used\n";); @@ -666,10 +666,8 @@ namespace datalog { } else { SASSERT(m.is_value(arg)); - if (!is_output) { - TRACE("dl", tout << "input " << i << " in " << p->get_decl()->get_name() << "\n";); - bv.unset(i); - } + TRACE("dl", tout << i << " in " << p->get_decl()->get_name() << " is a value, unable to slice\n";); + bv.unset(i); } } } From ea16f6608cafada28378e2707a3ee40a6f2f5f41 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 11:30:59 -0800 Subject: [PATCH 473/597] before rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/CMakeLists.txt | 3 - src/math/lp/indexed_value.h | 11 - src/math/lp/lar_core_solver.h | 30 +- src/math/lp/lp_dual_simplex.cpp | 24 - src/math/lp/lp_dual_simplex.h | 93 --- src/math/lp/lp_dual_simplex_def.h | 376 ----------- src/math/lp/lp_primal_core_solver.h | 1 - src/math/lp/lp_primal_simplex.cpp | 35 - src/math/lp/lp_primal_simplex.h | 106 --- src/math/lp/lp_primal_simplex_def.h | 367 ----------- src/math/lp/lp_solver.cpp | 55 -- src/math/lp/lp_solver.h | 260 -------- src/math/lp/lp_solver_def.h | 571 ---------------- src/math/lp/mps_reader.h | 891 ------------------------- src/math/lp/static_matrix.cpp | 1 - src/sat/smt/arith_sls.h | 3 - src/sat/smt/arith_solver.h | 4 +- src/shell/CMakeLists.txt | 1 - src/shell/lp_frontend.cpp | 109 --- src/shell/lp_frontend.h | 7 - src/shell/main.cpp | 10 +- src/smt/theory_lra.cpp | 3 - src/test/lp/lp.cpp | 984 ++-------------------------- src/test/lp/smt_reader.h | 4 - src/test/lp/test_file_reader.h | 1 - 25 files changed, 71 insertions(+), 3879 deletions(-) delete mode 100644 src/math/lp/lp_dual_simplex.cpp delete mode 100644 src/math/lp/lp_dual_simplex.h delete mode 100644 src/math/lp/lp_dual_simplex_def.h delete mode 100644 src/math/lp/lp_primal_simplex.cpp delete mode 100644 src/math/lp/lp_primal_simplex.h delete mode 100644 src/math/lp/lp_primal_simplex_def.h delete mode 100644 src/math/lp/lp_solver.cpp delete mode 100644 src/math/lp/lp_solver.h delete mode 100644 src/math/lp/lp_solver_def.h delete mode 100644 src/math/lp/mps_reader.h delete mode 100644 src/shell/lp_frontend.cpp delete mode 100644 src/shell/lp_frontend.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 9f0fae6bcca..5719de44fae 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,11 +20,8 @@ z3_add_component(lp lar_core_solver.cpp lp_core_solver_base.cpp lp_dual_core_solver.cpp - lp_dual_simplex.cpp lp_primal_core_solver.cpp - lp_primal_simplex.cpp lp_settings.cpp - lp_solver.cpp lu.cpp lp_utils.cpp matrix.cpp diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index c4837647099..92d8f2adf1f 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -43,15 +43,4 @@ class indexed_value { m_value = val; } }; -#ifdef Z3DEBUG -template -bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - const X & v = w[i]; - if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) - return false; - } - return true; -} -#endif } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 8a6c64ef00f..de8fe68adb5 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -651,35 +651,7 @@ class lar_core_solver { } } - void scale_problem_for_doubles( - static_matrix& A, - vector & lower_bounds, - vector & upper_bounds) { - vector column_scale_vector; - vector right_side_vector(A.column_count()); - settings().reps_in_scaler = 5; - scaler scaler(right_side_vector, - A, - settings().scaling_minimum, - settings().scaling_maximum, - column_scale_vector, - settings()); - if (! scaler.scale()) { - // the scale did not succeed, unscaling - A.clear(); - create_double_matrix(A); - } else { - for (unsigned j = 0; j < A.column_count(); j++) { - if (m_r_solver.column_has_upper_bound(j)) { - upper_bounds[j] /= column_scale_vector[j]; - } - if (m_r_solver.column_has_lower_bound(j)) { - lower_bounds[j] /= column_scale_vector[j]; - } - } - } - - } + // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { diff --git a/src/math/lp/lp_dual_simplex.cpp b/src/math/lp/lp_dual_simplex.cpp deleted file mode 100644 index aaf612f5696..00000000000 --- a/src/math/lp/lp_dual_simplex.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_dual_simplex_def.h" -template lp::mpq lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); -template double lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_dual_simplex.h b/src/math/lp/lp_dual_simplex.h deleted file mode 100644 index 75ef87492f2..00000000000 --- a/src/math/lp/lp_dual_simplex.h +++ /dev/null @@ -1,93 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_dual_core_solver.h" -namespace lp { - -template -class lp_dual_simplex: public lp_solver { - lp_dual_core_solver * m_core_solver; - vector m_b_copy; - vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver - vector m_column_types_of_core_solver; - vector m_column_types_of_logicals; - vector m_can_enter_basis; -public: - ~lp_dual_simplex() override { - delete m_core_solver; - } - - lp_dual_simplex() : m_core_solver(nullptr) {} - - - void decide_on_status_after_stage1(); - - void fix_logical_for_stage2(unsigned j); - - void fix_structural_for_stage2(unsigned j); - - void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - - void restore_right_sides(); - - void solve_for_stage2(); - - void fill_x_with_zeros(); - - void stage1(); - - void stage2(); - - void fill_first_stage_solver_fields(); - - column_type get_column_type(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); - - void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); - - void set_type_for_logical(unsigned j, column_type col_type) { - this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; - } - - void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial); - - void augment_matrix_A_and_fill_x_and_allocate_some_fields(); - - - - void copy_m_b_aside_and_set_it_to_zeros(); - - void find_maximal_solution() override; - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; -}; -} diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h deleted file mode 100644 index 8af9d87c104..00000000000 --- a/src/math/lp/lp_dual_simplex_def.h +++ /dev/null @@ -1,376 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "math/lp/lp_dual_simplex.h" -namespace lp{ - -template void lp_dual_simplex::decide_on_status_after_stage1() { - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - this->m_status = lp_status::FEASIBLE; - } else { - this->m_status = lp_status::UNBOUNDED; - } - break; - case lp_status::DUAL_UNBOUNDED: - lp_unreachable(); - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { - lp_assert(j >= this->number_of_core_structurals()); - switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::fixed; - m_can_enter_basis[j] = false; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - switch (ci->get_column_type()) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - case column_type::upper_bound: - lp_unreachable(); - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::boxed; - m_can_enter_basis[j] = true; - break; - case column_type::free_column: - m_can_enter_basis[j] = true; - m_column_types_of_core_solver[j] = column_type::free_column; - break; - default: - lp_unreachable(); - } - // T cost_was = this->m_costs[j]; - this->set_scaled_cost(j); -} - -template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { - fix_logical_for_stage2(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fix_structural_for_stage2(j); - } -} - -template void lp_dual_simplex::restore_right_sides() { - unsigned i = this->m_A->row_count(); - while (i--) { - this->m_b[i] = m_b_copy[i]; - } -} - -template void lp_dual_simplex::solve_for_stage2() { - m_core_solver->restore_non_basis(); - m_core_solver->solve_yB(m_core_solver->m_y); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - m_core_solver->set_status(lp_status::FEASIBLE); - m_core_solver->solve(); - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - this->m_status = lp_status::OPTIMAL; - break; - case lp_status::DUAL_UNBOUNDED: - this->m_status = lp_status::INFEASIBLE; - break; - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } - this->m_second_stage_iterations = m_core_solver->total_iterations(); - this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); -} - -template void lp_dual_simplex::fill_x_with_zeros() { - unsigned j = this->m_A->column_count(); - while (j--) { - this->m_x[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::stage1() { - lp_assert(m_core_solver == nullptr); - this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - m_core_solver = new lp_dual_core_solver( - *this->m_A, - m_can_enter_basis, - this->m_b, // the right side vector - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types_of_core_solver, - this->m_lower_bounds, - this->m_upper_bounds, - this->m_settings, - *this); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - // skipping stage 1 - m_core_solver->set_status(lp_status::OPTIMAL); - m_core_solver->set_total_iterations(0); - } else { - m_core_solver->solve(); - } - decide_on_status_after_stage1(); - this->m_first_stage_iterations = m_core_solver->total_iterations(); -} - -template void lp_dual_simplex::stage2() { - unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - restore_right_sides(); - solve_for_stage2(); -} - -template void lp_dual_simplex::fill_first_stage_solver_fields() { - unsigned slack_var = this->number_of_core_structurals(); - unsigned artificial = this->number_of_core_structurals() + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); - } - fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); -} - -template column_type lp_dual_simplex::get_column_type(unsigned j) { - lp_assert(j < this->m_A->column_count()); - if (j >= this->number_of_core_structurals()) { - return m_column_types_of_logicals[j - this->number_of_core_structurals()]; - } - return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { - // see 4.7 in the dissertation of Achim Koberstein - lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != - this->m_core_solver_columns_to_external_columns.end()); - - T free_bound = T(1e4); // see 4.8 - unsigned jj = this->m_core_solver_columns_to_external_columns[j]; - lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = this->m_map_from_var_index_to_column_info[jj]; - switch (ci->get_column_type()) { - case column_type::upper_bound: { - std::stringstream s; - s << "unexpected bound type " << j << " " - << column_type_to_string(get_column_type(j)); - throw_exception(s.str()); - break; - } - case column_type::lower_bound: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - break; - } - case column_type::free_column: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_upper_bounds[j] = free_bound; - this->m_lower_bounds[j] = -free_bound; - break; - } - case column_type::boxed: - m_can_enter_basis[j] = false; - this->m_costs[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? - break; - default: - lp_unreachable(); - } - m_column_types_of_core_solver[j] = column_type::boxed; -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { - this->m_costs[j] = 0; - lp_assert(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { - m_column_types_of_core_solver[j] = column_type::boxed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - } else { - m_column_types_of_core_solver[j] = column_type::fixed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { // go over logicals here - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); - } -} - -template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - switch (constraint.m_relation) { - case Equal: // no slack variable here - set_type_for_logical(artificial, column_type::fixed); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - (*this->m_A)(row, artificial) = numeric_traits::one(); - artificial++; - break; - - case Greater_or_equal: - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - if (rs > 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put a slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = numeric_traits::one(); - if (rs < 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - } -} - -template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { - this->count_slacks_and_artificials(); - this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); - unsigned n = this->m_A->column_count(); - this->m_column_types_of_core_solver.resize(n); - m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); - this->m_costs.resize(n); - this->m_upper_bounds.resize(n); - this->m_lower_bounds.resize(n); - m_can_enter_basis.resize(n); - this->m_basis.resize(this->m_A->row_count()); -} - - - -template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { - for (unsigned i = 0; i < this->m_b.size(); i++) { - m_b_copy.push_back(this->m_b[i]); - this->m_b[i] = numeric_traits::zero(); // preparing for the first stage - } -} - -template void lp_dual_simplex::find_maximal_solution(){ - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->flip_costs(); // do it for now, todo ( remove the flipping) - - this->cleanup(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->fill_matrix_A_and_init_right_side(); - this->fill_m_b(); - this->scale(); - augment_matrix_A_and_fill_x_and_allocate_some_fields(); - fill_first_stage_solver_fields(); - copy_m_b_aside_and_set_it_to_zeros(); - stage1(); - if (this->m_status == lp_status::FEASIBLE) { - stage2(); - } -} - - -template T lp_dual_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return -ret; // we flip costs for now -} -} diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 4b6163df810..a60395ab014 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -30,7 +30,6 @@ Revision History: #include #include #include "math/lp/lu.h" -#include "math/lp/lp_solver.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" diff --git a/src/math/lp/lp_primal_simplex.cpp b/src/math/lp/lp_primal_simplex.cpp deleted file mode 100644 index 634f529009b..00000000000 --- a/src/math/lp/lp_primal_simplex.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_primal_simplex_def.h" -template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template double lp::lp_primal_simplex::get_current_cost() const; -template double lp::lp_primal_simplex::get_column_value(unsigned int) const; -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::mpq lp::lp_primal_simplex::get_current_cost() const; -template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; -template void lp::lp_primal_simplex::find_maximal_solution(); -template void lp::lp_primal_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_primal_simplex.h b/src/math/lp/lp_primal_simplex.h deleted file mode 100644 index 77e12d0888e..00000000000 --- a/src/math/lp/lp_primal_simplex.h +++ /dev/null @@ -1,106 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include -#include -#include -#include "math/lp/lp_utils.h" -#include "math/lp/column_info.h" -#include "math/lp/lp_primal_core_solver.h" -#include "math/lp/lp_solver.h" -namespace lp { -template -class lp_primal_simplex: public lp_solver { - lp_primal_core_solver * m_core_solver; - vector m_lower_bounds; -private: - unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } - - void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); - - void init_buffer(unsigned k, vector & r); - - void refactor(); - - void set_scaled_costs(); -public: - lp_primal_simplex(): m_core_solver(nullptr) {} - - column_info * get_or_create_column_info(unsigned column); - - void set_status(lp_status status) { - this->m_status = status; - } - - lp_status get_status() { - return this->m_status; - } - - void fill_acceptable_values_for_x(); - - - void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); - - void fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial); - - - - - void set_core_solver_bounds(); - - void find_maximal_solution() override; - - void fill_A_x_and_basis_for_stage_one_total_inf(); - - void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); - - void solve_with_total_inf(); - - - ~lp_primal_simplex() override; - - bool bounds_hold(std::unordered_map const & solution); - - T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraints_hold(std::unordered_map const & solution); - - - T * get_array_from_map(std::unordered_map const & solution); - - bool solution_is_feasible(std::unordered_map const & solution) { - return bounds_hold(solution) && row_constraints_hold(solution); - } - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; - - -}; -} diff --git a/src/math/lp/lp_primal_simplex_def.h b/src/math/lp/lp_primal_simplex_def.h deleted file mode 100644 index 7ffe819b207..00000000000 --- a/src/math/lp/lp_primal_simplex_def.h +++ /dev/null @@ -1,367 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "util/vector.h" -#include "math/lp/lp_primal_simplex.h" - -namespace lp { -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { - unsigned slack_var = original_number_of_columns; - unsigned artificial = original_number_of_columns + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); - } -} - -template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { - for (unsigned i = 0; i < k; i++) { - r[i] = 0; - } - r[k] = 1; - for (unsigned i = this->row_count() -1; i > k; i--) { - r[i] = 0; - } -} - -template void lp_primal_simplex::refactor() { - m_core_solver->init_lu(); - if (m_core_solver->factorization()->get_status() != LU_status::OK) { - throw_exception("cannot refactor"); - } -} - -template void lp_primal_simplex::set_scaled_costs() { - unsigned j = this->number_of_core_structurals(); - while (j-- > 0) { - this->set_scaled_cost(j); - } -} - -template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { - auto it = this->m_columns.find(column); - return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; -} - -template void lp_primal_simplex::fill_acceptable_values_for_x() { - for (auto t : this->m_core_solver_columns_to_external_columns) { - this->m_x[t.first] = numeric_traits::zero(); - } -} - - -template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { - bound_is_set[i] = true; - bounds[i] = numeric_traits::zero(); -} - -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row >= 0 && row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - T artificial_cost = - numeric_traits::one(); - switch (constraint.m_relation) { - case Equal: // no slack variable here - this->m_column_types[artificial] = column_type::lower_bound; - this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero - this->m_basis[row] = artificial; - if (rs >= 0) { - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_x[artificial] = rs; - } else { - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_x[artificial] = - rs; - } - artificial++; - break; - - case Greater_or_equal: - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - - if (rs > 0) { - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - // adding one artificial - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_basis[row] = artificial; - this->m_x[artificial] = rs; - artificial++; - } else { - // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = - rs; - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = numeric_traits::one(); - - if (rs < 0) { - // adding one artificial - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_x[artificial] = - rs; - this->m_basis[row] = artificial++; - } else { - // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = rs; - } - slack_var++; - break; - } -} - - - - - -template void lp_primal_simplex::set_core_solver_bounds() { - unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; - this->m_column_types.resize(total_vars); - this->m_upper_bounds.resize(total_vars); - for (auto cit : this->m_map_from_var_index_to_column_info) { - column_info * ci = cit.second; - unsigned j = ci->get_column_index(); - if (!is_valid(j)) - continue; // the variable is not mapped to a column - switch (this->m_column_types[j] = ci->get_column_type()){ - case column_type::fixed: - this->m_upper_bounds[j] = numeric_traits::zero(); - break; - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - break; - - default: break; // do nothing - } - } -} - - -template void lp_primal_simplex::find_maximal_solution() { - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->cleanup(); - this->fill_matrix_A_and_init_right_side(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->m_x.resize(this->m_A->column_count()); - this->fill_m_b(); - this->scale(); - fill_acceptable_values_for_x(); - this->count_slacks_and_artificials(); - set_core_solver_bounds(); - solve_with_total_inf(); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { - for (unsigned row = 0; row < this->row_count(); row++) - fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { - lp_assert(row < this->row_count()); - auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); - lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); - unsigned ext_row = ext_row_it->second; - auto constr_it = this->m_constraints.find(ext_row); - lp_assert(constr_it != this->m_constraints.end()); - auto & constraint = constr_it->second; - unsigned j = this->m_A->column_count(); // j is a slack variable - this->m_A->add_column(); - // we need to bring the program to the form Ax = b - this->m_basis[row] = j; - switch (constraint.m_relation) { - case Equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::fixed; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - - case Greater_or_equal: - this->m_x[j] = - this->m_b[row]; - (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = zero_of_type(); - break; - case Less_or_equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - default: - lp_unreachable(); - } -} - -template void lp_primal_simplex::solve_with_total_inf() { - int total_vars = this->m_A->column_count() + this->row_count(); - if (total_vars == 0) { - this->m_status = lp_status::OPTIMAL; - return; - } - m_lower_bounds.clear(); - m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero - this->m_x.resize(total_vars, numeric_traits::zero()); - this->m_basis.resize(this->row_count()); - this->m_costs.clear(); - this->m_costs.resize(total_vars, zero_of_type()); - fill_A_x_and_basis_for_stage_one_total_inf(); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - set_scaled_costs(); - - m_core_solver = new lp_primal_core_solver(*this->m_A, - this->m_b, - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types, - m_lower_bounds, - this->m_upper_bounds, - this->m_settings, *this); - m_core_solver->solve(); - this->set_status(m_core_solver->get_status()); - this->m_total_iterations = m_core_solver->total_iterations(); -} - - -template lp_primal_simplex::~lp_primal_simplex() { - delete m_core_solver; -} - -template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { - for (auto it : this->m_map_from_var_index_to_column_info) { - auto sol_it = solution.find(it.second->get_name()); - if (sol_it == solution.end()) { - std::stringstream s; - s << "cannot find column " << it.first << " in solution"; - throw_exception(s.str() ); - } - - if (!it.second->bounds_hold(sol_it->second)) { - it.second->bounds_hold(sol_it->second); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { - auto it = this->m_A_values.find(i); - if (it == this->m_A_values.end()) { - std::stringstream s; - s << "cannot find row " << i; - throw_exception(s.str() ); - } - T ret = numeric_traits::zero(); - for (auto & pair : it->second) { - auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); - lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = cit->second; - auto sol_it = solution.find(ci->get_name()); - lp_assert(sol_it != solution.end()); - T column_val = sol_it->second; - if (out != nullptr) { - (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; - } - ret += pair.second * column_val; - } - if (out != nullptr) { - (*out) << " = " << ret << std::endl; - } - return ret; -} - -template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { - T row_val = get_row_value(i, solution, out); - auto & constraint = this->m_constraints[i]; - T rs = constraint.m_rs; - bool print = out != nullptr; - switch (constraint.m_relation) { - case Equal: - if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { - if (print) { - (*out) << "should be = " << rs << std::endl; - } - return false; - } - return true; - case Greater_or_equal: - if (numeric_traits::get_double(row_val - rs) < -0.00001) { - if (print) { - (*out) << "should be >= " << rs << std::endl; - } - return false; - } - return true;; - - case Less_or_equal: - if (numeric_traits::get_double(row_val - rs) > 0.00001) { - if (print) { - (*out) << "should be <= " << rs << std::endl; - } - return false; - } - return true;; - } - lp_unreachable(); - return false; // it is unreachable -} - -template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { - for (auto it : this->m_A_values) { - if (!row_constraint_holds(it.first, solution, nullptr)) { - row_constraint_holds(it.first, solution, nullptr); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return ret; -} -} diff --git a/src/math/lp/lp_solver.cpp b/src/math/lp/lp_solver.cpp deleted file mode 100644 index fc95140982b..00000000000 --- a/src/math/lp/lp_solver.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "math/lp/lp_solver_def.h" -template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template double lp::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/math/lp/lp_solver.h b/src/math/lp/lp_solver.h deleted file mode 100644 index ab16a686fd6..00000000000 --- a/src/math/lp/lp_solver.h +++ /dev/null @@ -1,260 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/column_info.h" -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include "math/lp/scaler.h" -#include "math/lp/bound_analyzer_on_row.h" -namespace lp { -enum lp_relation { - Less_or_equal, - Equal, - Greater_or_equal -}; - -template -struct lp_constraint { - X m_rs; // right side of the constraint - lp_relation m_relation; - lp_constraint() {} // empty constructor - lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} -}; - - -template -class lp_solver : public column_namer { - column_info * get_or_create_column_info(unsigned column); - -protected: - T get_column_cost_value(unsigned j, column_info * ci) const; -public: - unsigned m_total_iterations; - static_matrix* m_A; // this is the matrix of constraints - vector m_b; // the right side vector - unsigned m_first_stage_iterations; - unsigned m_second_stage_iterations; - std::unordered_map> m_constraints; - std::unordered_map*> m_map_from_var_index_to_column_info; - std::unordered_map > m_A_values; - std::unordered_map m_names_to_columns; // don't have to use it - std::unordered_map m_external_rows_to_core_solver_rows; - std::unordered_map m_core_solver_rows_to_external_rows; - std::unordered_map m_core_solver_columns_to_external_columns; - vector m_column_scale; - std::unordered_map m_name_map; - unsigned m_artificials; - unsigned m_slacks; - vector m_column_types; - vector m_costs; - vector m_x; - vector m_upper_bounds; - vector m_basis; - vector m_nbasis; - vector m_heading; - - - lp_status m_status; - - lp_settings m_settings; - lp_solver(): - m_A(nullptr), // this is the matrix of constraints - m_first_stage_iterations (0), - m_second_stage_iterations (0), - m_artificials (0), - m_slacks (0), - m_status(lp_status::UNKNOWN) - {} - - unsigned row_count() const { return this->m_A->row_count(); } - - void add_constraint(lp_relation relation, T right_side, unsigned row_index); - - void set_cost_for_column(unsigned column, T column_cost) { - get_or_create_column_info(column)->set_cost(column_cost); - } - std::string get_variable_name(unsigned j) const override; - - void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { - m_A_values[row][column] = val; - } - // returns the current cost - virtual T get_current_cost() const = 0; - // do not have to call it - void give_symbolic_name_to_column(std::string name, unsigned column); - - virtual T get_column_value(unsigned column) const = 0; - - T get_column_value_by_name(std::string name) const; - - // returns -1 if not found - virtual int get_column_index_by_name(std::string name) const; - - void set_lower_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_lower_bound(bound); - } - - void set_upper_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_upper_bound(bound); - } - - void unset_lower_bound(unsigned i) { - get_or_create_column_info(i)->unset_lower_bound(); - } - - void unset_upper_bound(unsigned i) { - get_or_create_column_info(i)->unset_upper_bound(); - } - - void set_fixed_value(unsigned i, T val) { - column_info *ci = get_or_create_column_info(i); - ci->set_fixed_value(val); - } - - void unset_fixed_value(unsigned i) { - get_or_create_column_info(i)->unset_fixed(); - } - - lp_status get_status() const { - return m_status; - } - - void set_status(lp_status st) { - m_status = st; - } - - - ~lp_solver() override; - - void flip_costs(); - - virtual void find_maximal_solution() = 0; - void set_time_limit(unsigned time_limit_in_seconds) { - m_settings.time_limit = time_limit_in_seconds; - } - - -protected: - bool problem_is_empty(); - - void scale(); - - - void print_rows_scale_stats(std::ostream & out); - - void print_columns_scale_stats(std::ostream & out); - - void print_row_scale_stats(unsigned i, std::ostream & out); - - void print_column_scale_stats(unsigned j, std::ostream & out); - - void print_scale_stats(std::ostream & out); - - void get_max_abs_in_row(std::unordered_map & row_map); - - void pin_vars_down_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, - numeric_traits::one()); - } - - void pin_vars_up_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, numeric_traits::one()); - } - - void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); - - bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); - - bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); - - bool row_is_zero(std::unordered_map & row); - - bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); - - // analyse possible max and min values that are derived from var boundaries - // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. - // Then we know what values of the variables are. For each positive coeff of the row it has to be - // the low boundary of the var and for a negative - the upper. - - // this routing also pins the variables to the boundaries - bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); - - void remove_fixed_or_zero_columns(); - - void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); - - unsigned try_to_remove_some_rows(); - - void cleanup(); - - void map_external_rows_to_core_solver_rows(); - - void map_external_columns_to_core_solver_columns(); - - unsigned number_of_core_structurals() { - return static_cast(m_core_solver_columns_to_external_columns.size()); - } - - void restore_column_scales_to_one() { - for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); - } - - void unscale(); - - void fill_A_from_A_values(); - - void fill_matrix_A_and_init_right_side(); - - void count_slacks_and_artificials(); - - void count_slacks_and_artificials_for_row(unsigned i); - - T lower_bound_shift_for_row(unsigned i); - - void fill_m_b(); - - T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; - void set_scaled_cost(unsigned j); - void print_statistics_on_A(std::ostream & out) { - out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; - } - -public: - lp_settings & settings() { return m_settings;} - void print_model(std::ostream & s) const { - s << "objective = " << get_current_cost() << std::endl; - s << "column values\n"; - for (auto & it : m_names_to_columns) { - s << it.first << " = " << get_column_value(it.second) << std::endl; - } - } -}; -} diff --git a/src/math/lp/lp_solver_def.h b/src/math/lp/lp_solver_def.h deleted file mode 100644 index 191832a2487..00000000000 --- a/src/math/lp/lp_solver_def.h +++ /dev/null @@ -1,571 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_solver.h" -namespace lp { -template column_info * lp_solver::get_or_create_column_info(unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; -} - -template -std::string lp_solver::get_variable_name(unsigned j) const { // j here is the core solver index - if (!m_settings.print_external_var_name()) - return std::string("j")+T_to_string(j); - auto it = this->m_core_solver_columns_to_external_columns.find(j); - if (it == this->m_core_solver_columns_to_external_columns.end()) - return std::string("x")+T_to_string(j); - unsigned external_j = it->second; - auto t = this->m_map_from_var_index_to_column_info.find(external_j); - if (t == this->m_map_from_var_index_to_column_info.end()) { - return std::string("x") +T_to_string(external_j); - } - return t->second->get_name(); -} - -template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { - if (ci->is_fixed()) { - return ci->get_cost() * ci->get_fixed_value(); - } - return ci->get_cost() * get_column_value(j); -} -template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { - lp_assert(m_constraints.find(row_index) == m_constraints.end()); - lp_constraint cs(right_side, relation); - m_constraints[row_index] = cs; -} - -template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - column_info *ci; - if (it == m_map_from_var_index_to_column_info.end()){ - m_map_from_var_index_to_column_info[column] = ci = new column_info; - } else { - ci = it->second; - } - ci->set_name(name); - m_names_to_columns[name] = column; -} - - -template T lp_solver::get_column_value_by_name(std::string name) const { - auto it = m_names_to_columns.find(name); - if (it == m_names_to_columns.end()) { - std::stringstream s; - s << "get_column_value_by_name " << name; - throw_exception(s.str()); - } - return get_column_value(it -> second); -} - -// returns -1 if not found -template int lp_solver::get_column_index_by_name(std::string name) const { - auto t = m_names_to_columns.find(name); - if (t == m_names_to_columns.end()) { - return -1; - } - return t->second; -} - - -template lp_solver::~lp_solver(){ - delete m_A; - for (auto t : m_map_from_var_index_to_column_info) { - delete t.second; - } -} - -template void lp_solver::flip_costs() { - for (auto t : m_map_from_var_index_to_column_info) { - column_info *ci = t.second; - ci->set_cost(-ci->get_cost()); - } -} - -template bool lp_solver::problem_is_empty() { - for (auto & c : m_A_values) - if (!c.second.empty()) - return false; - return true; -} - -template void lp_solver::scale() { - if (numeric_traits::precise() || m_settings.use_scaling == false) { - m_column_scale.clear(); - m_column_scale.resize(m_A->column_count(), one_of_type()); - return; - } - - T smin = T(m_settings.scaling_minimum); - T smax = T(m_settings.scaling_maximum); - - scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); - if (!scaler.scale()) { - unscale(); - } -} - - -template void lp_solver::print_rows_scale_stats(std::ostream & out) { - out << "rows max" << std::endl; - for (unsigned i = 0; i < m_A->row_count(); i++) { - print_row_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_columns_scale_stats(std::ostream & out) { - out << "columns max" << std::endl; - for (unsigned i = 0; i < m_A->column_count(); i++) { - print_column_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { - out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; - out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; -} - -template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { - out << "(" << m_A->get_min_abs_in_row(j) << " "; - out << m_A->get_max_abs_in_column(j) << ")"; -} - -template void lp_solver::print_scale_stats(std::ostream & out) { - print_rows_scale_stats(out); - print_columns_scale_stats(out); -} - -template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { - T ret = numeric_traits::zero(); - for (auto jp : row_map) { - T ac = numeric_traits::abs(jp->second); - if (ac > ret) { - ret = ac; - } - } - return ret; -} - -template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { - for (auto t : row) { - unsigned j = t.first; - column_info * ci = m_map_from_var_index_to_column_info[j]; - T a = t.second; - if (a * sign > numeric_traits::zero()) { - lp_assert(ci->upper_bound_is_set()); - ci->set_fixed_value(ci->get_upper_bound()); - } else { - lp_assert(ci->lower_bound_is_set()); - ci->set_fixed_value(ci->get_lower_bound()); - } - } -} - -template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a > numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a < numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::row_is_zero(std::unordered_map & row) { - for (auto & t : row) { - if (!is_zero(t.second)) - return false; - } - return true; -} - -template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (!is_zero(rs)) - m_status = lp_status::INFEASIBLE; - return true; - } - - T lower_bound; - bool lb = get_minimal_row_value(row, lower_bound); - if (lb) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_epsilon - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - T upper_bound; - bool ub = get_maximal_row_value(row, upper_bound); - if (ub) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs > zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - T upper_bound; - if (get_maximal_row_value(row, upper_bound)) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { - T lower_bound; - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs < zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - if (get_minimal_row_value(row, lower_bound)) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - return false; -} - -// analyse possible max and min values that are derived from var boundaries -// Let us say that the we have a "ge" constraint, and the min value is equal to the rs. -// Then we know what values of the variables are. For each positive coeff of the row it has to be -// the low boundary of the var and for a negative - the upper. - -// this routing also pins the variables to the boundaries -template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { - auto & constraint = m_constraints[row_index]; - switch (constraint.m_relation) { - case lp_relation::Equal: - return row_e_is_obsolete(row, row_index); - - case lp_relation::Greater_or_equal: - return row_ge_is_obsolete(row, row_index); - - case lp_relation::Less_or_equal: - return row_le_is_obsolete(row, row_index); - } - lp_unreachable(); - return false; // it is unreachable -} - -template void lp_solver::remove_fixed_or_zero_columns() { - for (auto & i_row : m_A_values) { - remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); - } -} - -template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { - auto & constraint = m_constraints[i]; - vector removed; - for (auto & col : row) { - unsigned j = col.first; - lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); - column_info * ci = m_map_from_var_index_to_column_info[j]; - if (ci->is_fixed()) { - removed.push_back(j); - T aj = col.second; - constraint.m_rs -= aj * ci->get_fixed_value(); - } else { - if (numeric_traits::is_zero(col.second)){ - removed.push_back(j); - } - } - } - - for (auto j : removed) { - row.erase(j); - } -} - -template unsigned lp_solver::try_to_remove_some_rows() { - vector rows_to_delete; - for (auto & t : m_A_values) { - if (row_is_obsolete(t.second, t.first)) { - rows_to_delete.push_back(t.first); - } - - if (m_status == lp_status::INFEASIBLE) { - return 0; - } - } - if (!rows_to_delete.empty()) { - for (unsigned k : rows_to_delete) { - m_A_values.erase(k); - } - } - remove_fixed_or_zero_columns(); - return static_cast(rows_to_delete.size()); -} - -template void lp_solver::cleanup() { - int n = 0; // number of deleted rows - int d; - while ((d = try_to_remove_some_rows()) > 0) - n += d; - - if (n == 1) { - LP_OUT(m_settings, "deleted one row" << std::endl); - } else if (n) { - LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); - } -} - -template void lp_solver::map_external_rows_to_core_solver_rows() { - unsigned size = 0; - for (auto & row : m_A_values) { - m_external_rows_to_core_solver_rows[row.first] = size; - m_core_solver_rows_to_external_rows[size] = row.first; - size++; - } -} - -template void lp_solver::map_external_columns_to_core_solver_columns() { - unsigned size = 0; - for (auto & row : m_A_values) { - for (auto & col : row.second) { - if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { - throw_exception("found fixed column"); - } - unsigned j = col.first; - auto column_info_it = m_map_from_var_index_to_column_info.find(j); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - - auto j_column = column_info_it->second->get_column_index(); - if (!is_valid(j_column)) { // j is a newcomer - m_map_from_var_index_to_column_info[j]->set_column_index(size); - m_core_solver_columns_to_external_columns[size++] = j; - } - } - } -} - -template void lp_solver::unscale() { - delete m_A; - m_A = nullptr; - fill_A_from_A_values(); - restore_column_scales_to_one(); - fill_m_b(); -} - -template void lp_solver::fill_A_from_A_values() { - m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); - for (auto & t : m_A_values) { - auto row_it = m_external_rows_to_core_solver_rows.find(t.first); - lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); - unsigned row = row_it->second; - for (auto k : t.second) { - auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - column_info *ci = column_info_it->second; - unsigned col = ci->get_column_index(); - lp_assert(is_valid(col)); - bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); - if (!col_is_flipped) { - (*m_A)(row, col) = k.second; - } else { - (*m_A)(row, col) = - k.second; - } - } - } -} - -template void lp_solver::fill_matrix_A_and_init_right_side() { - map_external_rows_to_core_solver_rows(); - map_external_columns_to_core_solver_columns(); - lp_assert(m_A == nullptr); - fill_A_from_A_values(); - m_b.resize(m_A->row_count()); -} - -template void lp_solver::count_slacks_and_artificials() { - for (int i = row_count() - 1; i >= 0; i--) { - count_slacks_and_artificials_for_row(i); - } -} - -template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; - switch (constraint.m_relation) { - case Equal: - m_artificials++; - break; - case Greater_or_equal: - m_slacks++; - if (this->m_b[i] > 0) { - m_artificials++; - } - break; - case Less_or_equal: - m_slacks++; - if (this->m_b[i] < 0) { - m_artificials++; - } - break; - } -} - -template T lp_solver::lower_bound_shift_for_row(unsigned i) { - T ret = numeric_traits::zero(); - - auto row = this->m_A_values.find(i); - if (row == this->m_A_values.end()) { - throw_exception("cannot find row"); - } - for (auto col : row->second) { - ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); - } - return ret; -} - -template void lp_solver::fill_m_b() { - for (int i = this->row_count() - 1; i >= 0; i--) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; - auto & constraint = this->m_constraints[external_i]; - this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); - } -} - -template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { - auto cit = this->m_map_from_var_index_to_column_info.find(column); - if (cit == this->m_map_from_var_index_to_column_info.end()) { - return numeric_traits::zero(); - } - - column_info * ci = cit->second; - - if (ci->is_fixed()) { - return ci->get_fixed_value(); - } - - unsigned cj = ci->get_column_index(); - if (cj != static_cast(-1)) { - T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; - if (ci->is_free()) { - return v; - } - if (!ci->is_flipped()) { - return v + ci->get_lower_bound(); - } - - // the flipped case when there is only upper bound - return -v + ci->get_upper_bound(); // - } - - return numeric_traits::zero(); // returns zero for out of boundary columns -} - -template void lp_solver::set_scaled_cost(unsigned j) { - // grab original costs but modify it with the column scales - lp_assert(j < this->m_column_scale.size()); - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - T cost = ci->get_cost(); - if (ci->is_flipped()){ - cost *= T(-1); - } - lp_assert(ci->is_fixed() == false); - this->m_costs[j] = cost * this->m_column_scale[j]; -} -} diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h deleted file mode 100644 index 8093954b11e..00000000000 --- a/src/math/lp/mps_reader.h +++ /dev/null @@ -1,891 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -// reads an MPS file representing a Mixed Integer Program -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" -#include "math/lp/lar_solver.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -namespace lp { -inline bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -inline size_t number_of_whites(const std::string & s) { - size_t i = 0; - for(;i < s.size(); i++) - if (!my_white_space(s[i])) return i; - return i; -} -inline size_t number_of_whites_from_end(const std::string & s) { - size_t ret = 0; - for(int i = static_cast(s.size()) - 1;i >= 0; i--) - if (my_white_space(s[i])) ret++;else break; - - return ret; -} - - - // trim from start -inline std::string <rim(std::string &s) { - s.erase(0, number_of_whites(s)); - return s; -} - - - - - // trim from end -inline std::string &rtrim(std::string &s) { - // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - s.erase(s.end() - number_of_whites_from_end(s), s.end()); - return s; -} - // trim from both ends -inline std::string &trim(std::string &s) { - return ltrim(rtrim(s)); -} - -inline std::string trim(std::string const &r) { - std::string s = r; - return ltrim(rtrim(s)); -} - - -inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { - vector results; - size_t prev = 0; - size_t next = 0; - while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { - if (keep_empty || (next - prev != 0)) { - results.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - if (prev < source.size()) { - results.push_back(source.substr(prev)); - } - return results; -} - -inline vector split_and_trim(const std::string &line) { - auto split = string_split(line, " \t", false); - vector ret; - for (auto s : split) { - ret.push_back(trim(s)); - } - return ret; -} - -template -class mps_reader { - enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; - struct bound { - T m_low; - T m_upper; - bool m_low_is_set; - bool m_upper_is_set; - bool m_value_is_fixed; - T m_fixed_value; - bool m_free; - // constructor - bound() : m_low(numeric_traits::zero()), - m_low_is_set(true), - m_upper_is_set(false), - m_value_is_fixed(false), - m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable - }; - - struct column { - std::string m_name; - bound * m_bound; - unsigned m_index; - column(const std::string &name, unsigned index): m_name(name), - m_bound(nullptr), - m_index(index) { - } - }; - - struct row { - row_type m_type; - std::string m_name; - std::unordered_map m_row_columns; - unsigned m_index; - T m_right_side; - T m_range; - row(row_type type, const std::string &name, unsigned index) : - m_type(type), - m_name(name), - m_index(index), - m_right_side(zero_of_type()), - m_range(zero_of_type()) - { - } - }; - - bool m_is_OK; - std::string m_file_name; - std::unordered_map m_rows; - std::unordered_map m_columns; - std::unordered_map m_names_to_var_index; - std::string m_line; - std::string m_name; - std::string m_cost_row_name; - std::ifstream m_file_stream; - // needed to adjust the index row - unsigned m_cost_line_count; - unsigned m_line_number; - std::ostream * m_message_stream; - - void set_m_ok_to_false() { - *m_message_stream << "setting m_is_OK to false" << std::endl; - m_is_OK = false; - } - - std::string get_string_from_position(unsigned offset) { - unsigned i = offset; - for (; i < m_line.size(); i++){ - if (m_line[i] == ' ') - break; - } - lp_assert(m_line.size() >= offset); - lp_assert(m_line.size() >> i); - lp_assert(i >= offset); - return m_line.substr(offset, i - offset); - } - - void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ - if (b == nullptr) { - solver->set_lower_bound(col, numeric_traits::zero()); - return; - } - - if (b->m_free) { - return; - } - if (b->m_low_is_set) { - solver->set_lower_bound(col, b->m_low); - } - if (b->m_upper_is_set) { - solver->set_upper_bound(col, b->m_upper); - } - - if (b->m_value_is_fixed) { - solver->set_fixed_value(col, b->m_fixed_value); - } - } - - bool all_white_space() { - for (unsigned i = 0; i < m_line.size(); i++) { - char c = m_line[i]; - if (c != ' ' && c != '\t') { - return false; - } - } - return true; - } - - void read_line() { - while (m_is_OK) { - if (!getline(m_file_stream, m_line)) { - m_line_number++; - set_m_ok_to_false(); - *m_message_stream << "cannot read from file" << std::endl; - } - m_line_number++; - if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) - break; - } - } - - void read_name() { - do { - read_line(); - if (m_line.find("NAME") != 0) { - continue; - } - m_line = m_line.substr(4); - m_name = trim(m_line); - break; - } while (m_is_OK); - } - - void read_rows() { - // look for start of the rows - read_line(); - do { - if (static_cast(m_line.find("ROWS")) >= 0) { - break; - } - } while (m_is_OK); - do { - read_line(); - if (m_line.find("COLUMNS") == 0) { - break; - } - add_row(); - } while (m_is_OK); - } - - void read_column_by_columns(const std::string & column_name, std::string column_data) { - // uph, let us try to work with columns - if (column_data.size() >= 22) { - std::string ss = column_data.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - *m_message_stream << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); - if (column_data.size() > 24) { - column_data = column_data.substr(25); - if (column_data.size() >= 22) { - read_column_by_columns(column_name, column_data); - } - } - } - } else { - fail: - set_m_ok_to_false(); - *m_message_stream << "cannot understand this line\n" - "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void read_column(const std::string & column_name, const std::string & column_data){ - auto tokens = split_and_trim(column_data); - for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { - auto row_name = tokens[i]; - if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here - auto t = m_rows.find(row_name); - if (t == m_rows.end()) { - read_column_by_columns(column_name, column_data); - return; - } - row *r = t->second; - r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); - } - } - - void read_columns(){ - std::string column_name; - do { - read_line(); - if (m_line.find("RHS") == 0) { - break; - } - if (m_line.size() < 22) { - (*m_message_stream) << "line is too short for a column" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string column_name_tmp = trim(m_line.substr(4, 8)); - if (!column_name_tmp.empty()) { - column_name = column_name_tmp; - } - auto col_it = m_columns.find(column_name); - mps_reader::column * col; - if (col_it == m_columns.end()) { - col = new mps_reader::column(column_name, static_cast(m_columns.size())); - m_columns[column_name] = col; - // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; - } else { - col = col_it->second; - } - read_column(column_name, m_line.substr(14)); - } while (m_is_OK); - } - - void read_rhs() { - do { - read_line(); - if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { - break; - } - fill_rhs(); - } while (m_is_OK); - } - - - void fill_rhs_by_columns(std::string rhsides) { - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - fill_rhs_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void fill_rhs() { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string rhsides = m_line.substr(14); - vector splitted_line = split_and_trim(rhsides); - - for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { - auto t = m_rows.find(splitted_line[i]); - if (t == m_rows.end()) { - fill_rhs_by_columns(rhsides); - return; - } - row * row = t->second; - row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); - } - } - - void read_bounds() { - if (m_line.find("BOUNDS") != 0) { - return; - } - - do { - read_line(); - if (m_line[0] != ' ') { - break; - } - create_or_update_bound(); - } while (m_is_OK); - } - - void read_ranges() { - if (m_line.find("RANGES") != 0) { - return; - } - do { - read_line(); - auto sl = split_and_trim(m_line); - if (sl.size() < 2) { - break; - } - read_range(sl); - } while (m_is_OK); - } - - - void read_bound_by_columns(const std::string & colstr) { - if (colstr.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (colstr.size() >= 22) { - std::string ss = colstr.substr(0, 8); - std::string column_name = trim(ss); - auto t = m_columns.find(column_name); - - if (t == m_columns.end()) { - (*m_message_stream) << "cannot find " << column_name << std::endl; - goto fail; - } else { - vector bound_string; - bound_string.push_back(column_name); - if (colstr.size() > 14) { - bound_string.push_back(colstr.substr(14)); - } - mps_reader::column * col = t->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void update_bound(bound * b, vector bound_string) { - /* - UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. - */ - - std::string bound_type = get_string_from_position(1); - if (bound_type == "BV") { - b->m_upper_is_set = true; - b->m_upper = 1; - return; - } - - if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - b->m_upper_is_set = true; - b->m_upper= numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "LO" || bound_type == "LI") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_low_is_set = true; - b->m_low = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "FR") { - b->m_free = true; - } else if (bound_type == "FX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_value_is_fixed = true; - b->m_fixed_value = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "PL") { - b->m_low_is_set = true; - b->m_low = 0; - } else if (bound_type == "MI") { - b->m_upper_is_set = true; - b->m_upper = 0; - } else { - (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - } - - void create_or_update_bound() { - const unsigned name_offset = 14; - lp_assert(m_line.size() >= 14); - vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); - - if (bound_string.empty()) { - set_m_ok_to_false(); - (*m_message_stream) << "error at line " << m_line_number << std::endl; - throw m_line; - } - - std::string name = bound_string[0]; - auto it = m_columns.find(name); - if (it == m_columns.end()){ - read_bound_by_columns(m_line.substr(14)); - return; - } - mps_reader::column * col = it->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - - - - void read_range_by_columns(std::string rhsides) { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_range = numeric_traits::from_string(rhsides.substr(8)); - maybe_modify_current_row_and_add_row_for_range(row); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - read_range_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - - void read_range(vector & splitted_line){ - for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { - auto it = m_rows.find(splitted_line[i]); - if (it == m_rows.end()) { - read_range_by_columns(m_line.substr(14)); - return; - } - row * row = it->second; - row->m_range = numeric_traits::from_string(splitted_line[i + 1]); - maybe_modify_current_row_and_add_row_for_range(row); - } - } - - void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - std::string row_name = row_with_range->m_name + "_range"; - row * other_bound_range_row; - switch (row_with_range->m_type) { - case row_type::Greater_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); - break; - case row_type::Less_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); - break; - case row_type::Equal: - if (row_with_range->m_range > 0) { - row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - } else { // row->m_range < 0; - row_with_range->m_type = row_type::Less_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - } - other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; - break; - default: - (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - - for (auto s : row_with_range->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - other_bound_range_row->m_row_columns[s.first] = s.second; - } - } - - void add_row() { - if (m_line.length() < 2) { - return; - } - - m_line = trim(m_line); - char c = m_line[0]; - m_line = m_line.substr(1); - m_line = trim(m_line); - add_row(c); - } - - void add_row(char c) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - switch (c) { - case 'E': - m_rows[m_line] = new row(row_type::Equal, m_line, index); - break; - case 'L': - m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); - break; - case 'G': - m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); - break; - case 'N': - m_rows[m_line] = new row(row_type::Cost, m_line, index); - m_cost_row_name = m_line; - m_cost_line_count++; - break; - } - } - unsigned range_count() { - unsigned ret = 0; - for (auto s : m_rows) { - if (s.second->m_range != 0) { - ret++; - } - } - return ret; - } - - /* - If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: - sense interval - G [rhs, rhs + |range|] - L [rhs - |range|, rhs] - E [rhs, rhs + |range|] if range > 0, - [rhs - |range|, rhs] if range < 0 - where |range| is range's absolute value. - */ - - lp_relation get_relation_from_row(row_type rt) { - switch (rt) { - case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; - case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; - case mps_reader::Equal: return lp_relation::Equal; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned solver_row_count() { - return m_rows.size() - m_cost_line_count + range_count(); - } - - void fill_solver_on_row(row * row, lp_solver *solver) { - if (row->m_name != m_cost_row_name) { - solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); - for (auto s : row->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); - } - } else { - set_solver_cost(row, solver); - } - } - - T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } - - void fill_solver_on_rows(lp_solver * solver) { - for (auto row_it : m_rows) { - fill_solver_on_row(row_it.second, solver); - } - } - - - void fill_solver_on_columns(lp_solver * solver){ - for (auto s : m_columns) { - mps_reader::column * col = s.second; - unsigned index = col->m_index; - set_boundary_for_column(index, col->m_bound, solver); - // optional call - solver->give_symbolic_name_to_column(col->m_name, col->m_index); - } - } - - void fill_solver(lp_solver *solver) { - fill_solver_on_rows(solver); - fill_solver_on_columns(solver); - } - - void set_solver_cost(row * row, lp_solver *solver) { - for (auto s : row->m_row_columns) { - std::string name = s.first; - lp_assert(m_columns.find(name) != m_columns.end()); - mps_reader::column * col = m_columns[name]; - solver->set_cost_for_column(col->m_index, s.second); - } - } - -public: - - void set_message_stream(std::ostream * o) { - lp_assert(o != nullptr); - m_message_stream = o; - } - vector column_names() { - vector v; - for (auto s : m_columns) { - v.push_back(s.first); - } - return v; - } - - ~mps_reader() { - for (auto s : m_rows) { - delete s.second; - } - for (auto s : m_columns) { - auto col = s.second; - delete col->m_bound; - delete col; - } - } - - mps_reader(const std::string & file_name): - m_is_OK(true), - m_file_name(file_name), - m_file_stream(file_name), - m_cost_line_count(0), - m_line_number(0), - m_message_stream(& std::cout) {} - void read() { - if (!m_file_stream.is_open()){ - set_m_ok_to_false(); - return; - } - - read_name(); - read_rows(); - read_columns(); - read_rhs(); - if (m_line.find("BOUNDS") == 0) { - read_bounds(); - read_ranges(); - } else if (m_line.find("RANGES") == 0) { - read_ranges(); - read_bounds(); - } - } - - bool is_ok() { - return m_is_OK; - } - - lp_solver * create_solver(bool dual) { - lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); - fill_solver(solver); - return solver; - } - - lconstraint_kind get_lar_relation_from_row(row_type rt) { - switch (rt) { - case Less_or_equal: return LE; - case Greater_or_equal: return GE; - case Equal: return EQ; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned get_var_index(std::string s) { - auto it = m_names_to_var_index.find(s); - if (it != m_names_to_var_index.end()) - return it->second; - unsigned ret = static_cast(m_names_to_var_index.size()); - m_names_to_var_index[s] = ret; - return ret; - } - - void fill_lar_solver_on_row(row * row, lar_solver *solver, int row_index) { - if (row->m_name != m_cost_row_name) { - auto kind = get_lar_relation_from_row(row->m_type); - vector> ls; - for (auto s : row->m_row_columns) { - var_index i = solver->add_var(get_var_index(s.first), false); - ls.push_back(std::make_pair(s.second, i)); - } - unsigned j = solver->add_term(ls, row_index); - solver->add_var_bound(j, kind, row->m_right_side); - } else { - // ignore the cost row - } - } - - - void fill_lar_solver_on_rows(lar_solver * solver) { - int row_index = 0; - for (auto row_it : m_rows) { - fill_lar_solver_on_row(row_it.second, solver, row_index++); - } - } - - void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, GE, b->m_low); - } - - void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_upper); - } - - void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_fixed_value); - solver->add_var_bound(i, GE, b->m_fixed_value); - } - - void fill_lar_solver_on_columns(lar_solver * solver) { - for (auto s : m_columns) { - mps_reader::column * col = s.second; - solver->add_var(col->m_index, false); - auto b = col->m_bound; - if (b == nullptr) return; - - if (b->m_free) continue; - - if (b->m_low_is_set) { - create_low_constraint_for_var(col, b, solver); - } - if (b->m_upper_is_set) { - create_upper_constraint_for_var(col, b, solver); - } - if (b->m_value_is_fixed) { - create_equality_contraint_for_var(col, b, solver); - } - } - } - - - void fill_lar_solver(lar_solver * solver) { - fill_lar_solver_on_columns(solver); - fill_lar_solver_on_rows(solver); - } - - lar_solver * create_lar_solver() { - lar_solver * solver = new lar_solver(); - fill_lar_solver(solver); - return solver; - } -}; -} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 571e9b1d06d..cde96277b5e 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,7 +24,6 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_dual_core_solver.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index af3a462340b..09a56c84efd 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -19,9 +19,6 @@ Module Name: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 9ae671c162b..68d5f802592 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -19,9 +19,7 @@ Module Name: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" + #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index d9b74f162f9..c0e9c85050f 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -28,7 +28,6 @@ add_executable(shell opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp - lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp deleted file mode 100644 index 8d6425533c8..00000000000 --- a/src/shell/lp_frontend.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/*++ -Copyright (c) 2016 Microsoft Corporation - -Author: - - Lev Nachmanson 2016-10-27 - ---*/ - -#include "math/lp/lp_settings.h" -#include "math/lp/mps_reader.h" -#include "util/timeout.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "util/rlimit.h" -#include "util/gparams.h" -#include "util/mutex.h" -#include -#include -#include "smt/params/smt_params_helper.hpp" - -namespace { -static mutex *display_stats_mux = new mutex; - -static lp::lp_solver* g_solver = nullptr; - -static void display_statistics() { - lock_guard lock(*display_stats_mux); - if (g_solver && g_solver->settings().print_statistics) { - // TBD display relevant information about statistics - } -} - -static void STD_CALL on_ctrl_c(int) { - signal (SIGINT, SIG_DFL); - display_statistics(); - raise(SIGINT); -} - -static void on_timeout() { - display_statistics(); - _Exit(0); -} - -struct front_end_resource_limit : public lp::lp_resource_limit { - reslimit& m_reslim; - - front_end_resource_limit(reslimit& lim): - m_reslim(lim) - {} - - bool get_cancel_flag() override { return !m_reslim.inc(); } -}; - -void run_solver(smt_params_helper & params, char const * mps_file_name) { - - reslimit rlim; - unsigned timeout = gparams::get_ref().get_uint("timeout", 0); - unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); - front_end_resource_limit lp_limit(rlim); - - scoped_rlimit _rlimit(rlim, rlimit); - cancel_eh eh(rlim); - scoped_timer timer(timeout, &eh); - - std::string fn(mps_file_name); - lp::mps_reader reader(fn); - reader.set_message_stream(&std::cout); // can be redirected - reader.read(); - if (!reader.is_ok()) { - std::cerr << "cannot process " << mps_file_name << std::endl; - return; - } - lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver - solver->settings().set_resource_limit(lp_limit); - g_solver = solver; - if (params.arith_min()) { - solver->flip_costs(); - } - solver->settings().set_message_ostream(&std::cout); - solver->settings().report_frequency = params.arith_rep_freq(); - solver->settings().print_statistics = params.arith_print_stats(); - solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); - - solver->find_maximal_solution(); - - *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp::lp_status::OPTIMAL) { - if (params.arith_min()) { - solver->flip_costs(); - } - solver->print_model(std::cout); - } - - display_statistics(); - g_solver = nullptr; - delete solver; -} -} - -unsigned read_mps_file(char const * mps_file_name) { - signal(SIGINT, on_ctrl_c); - register_on_timeout_proc(on_timeout); - smt_params_helper p; - param_descrs r; - p.collect_param_descrs(r); - run_solver(p, mps_file_name); - return 0; -} diff --git a/src/shell/lp_frontend.h b/src/shell/lp_frontend.h deleted file mode 100644 index b24be811f18..00000000000 --- a/src/shell/lp_frontend.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - Copyright (c) 2013 Microsoft Corporation. All rights reserved. - - Author: Lev Nachmanson -*/ -#pragma once -unsigned read_mps_file(char const * mps_file_name); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 4c26d91d968..26325d3d0a2 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -37,14 +37,13 @@ Revision History: #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" -#include "shell/lp_frontend.h" #include "shell/drat_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_DRAT } input_kind; static char const * g_input_file = nullptr; static char const * g_drat_input_file = nullptr; @@ -377,10 +376,6 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } - else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || - strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { - g_input_kind = IN_MPS; - } } } switch (g_input_kind) { @@ -406,9 +401,6 @@ int STD_CALL main(int argc, char ** argv) { case IN_Z3_LOG: replay_z3_log(g_input_file); break; - case IN_MPS: - return_value = read_mps_file(g_input_file); - break; case IN_DRAT: return_value = read_drat(g_drat_input_file); break; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d61910ff20e..2aa988282d2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -19,9 +19,6 @@ --*/ #include "util/stopwatch.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c82cdd0a4b4..78abf1a6f4d 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -33,8 +33,6 @@ #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/mps_reader.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" @@ -59,6 +57,71 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" + +bool my_white_space(const char & a) { + return a == ' ' || a == '\t'; +} +size_t number_of_whites(const std::string & s) { + size_t i = 0; + for(;i < s.size(); i++) + if (!my_white_space(s[i])) return i; + return i; +} +size_t number_of_whites_from_end(const std::string & s) { + size_t ret = 0; + for(int i = static_cast(s.size()) - 1;i >= 0; i--) + if (my_white_space(s[i])) ret++;else break; + + return ret; +} + + +std::string <rim(std::string &s) { + s.erase(0, number_of_whites(s)); + return s; +} + + + + + // trim from end +inline std::string &rtrim(std::string &s) { + // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.end() - number_of_whites_from_end(s), s.end()); + return s; +} + // trim from both ends +inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} + + +vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { + vector results; + size_t prev = 0; + size_t next = 0; + while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { + if (keep_empty || (next - prev != 0)) { + results.push_back(source.substr(prev, next - prev)); + } + prev = next + 1; + } + if (prev < source.size()) { + results.push_back(source.substr(prev)); + } + return results; +} + +vector split_and_trim(const std::string &line) { + auto split = string_split(line, " \t", false); + vector ret; + for (auto s : split) { + ret.push_back(trim(s)); + } + return ret; +} + + namespace nla { void test_horner(); void test_monics(); @@ -1395,169 +1458,15 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { } } -template -void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { - if (time_limit > 0) - solver->set_time_limit(time_limit); - - if (look_for_min) - solver->flip_costs(); - - update_settings(args_parser, solver->settings()); -} bool values_are_one_percent_close(double a, double b); -void print_x(mps_reader & reader, lp_solver * solver) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; -} -void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { - for (const auto & name : reader.column_names()) { - double a = solver->get_column_value_by_name(name); - double b = solver0->get_column_value_by_name(name); - if (!values_are_one_percent_close(a, b)) { - std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; - } - } -} -void solve_mps_double(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return; - } - - lp_solver * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - if (dual) { - std::cout << "solving for dual" << std::endl; - } - solver->find_maximal_solution(); - sw.stop(); - double span = sw.get_seconds(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - print_x(reader, solver); - } - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost << std::endl; - } - std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; - if (compare_with_primal) { - auto * primal_solver = reader.create_solver(false); - setup_solver(time_limit, look_for_min, args_parser, primal_solver); - primal_solver->find_maximal_solution(); - if (solver->get_status() != primal_solver->get_status()) { - std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; - } else { - if (solver->get_status() == lp_status::OPTIMAL) { - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - double primal_cost = primal_solver->get_current_cost(); - if (look_for_min) { - primal_cost = -primal_cost; - } - std::cout << "primal cost = " << primal_cost << std::endl; - if (!values_are_one_percent_close(cost, primal_cost)) { - compare_solutions(reader, primal_solver, solver); - print_x(reader, primal_solver); - std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; - lp_assert(false); - } - } - } - delete primal_solver; - } - delete solver; -} - -void solve_mps_rational(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - solver->find_maximal_solution(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - - if (solver->get_status() == lp_status::OPTIMAL) { - // for (auto name: reader.column_names()) { - // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - // } - lp::mpq cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost.get_double() << std::endl; - } - std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition -void solve_mps(std::string file_name, bool look_for_min, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { - if (!solve_for_rational) { - std::cout << "solving " << file_name << std::endl; - solve_mps_double(file_name, look_for_min, time_limit, dual, compare_with_primal, args_parser); - } - else { - std::cout << "solving " << file_name << " in rationals " << std::endl; - solve_mps_rational(file_name, look_for_min, time_limit, dual, args_parser); - } -} -void solve_mps(std::string file_name, argument_parser & args_parser) { - bool look_for_min = args_parser.option_is_used("--min"); - unsigned time_limit; - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - solve_mps(file_name, look_for_min, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); -} - -void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { - std::cout << "solving " << file_name << std::endl; - - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; - } - } - std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void test_upair_queue() { int n = 10; @@ -1626,53 +1535,6 @@ void test_binary_priority_queue() { std::cout << " done" << std::endl; } -bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); - return solver->solution_is_feasible(solution); - } - return false; -} - - -void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { - std::cout << "solving " << file_name << std::endl; - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (status != solver->get_status()){ - std::cout << "status should be " << lp_status_to_string(status) << std::endl; - lp_assert(status == solver->get_status()); - throw "status is wrong"; - } - if (solver->get_status() == lp_status::OPTIMAL) { - std::cout << "cost = " << solver->get_current_cost() << std::endl; - if (solution != nullptr) { - for (auto it : *solution) { - if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { - std::cout << "expected:" << it.first << "=" << - it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; - } - lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); - } - } - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; - } - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} int get_random_rows() { return 5 + my_random() % 2; @@ -1686,55 +1548,6 @@ int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } -void add_random_row(lp_primal_simplex * solver, int cols, int row) { - solver->add_constraint(lp_relation::Greater_or_equal, 1, row); - for (int i = 0; i < cols; i++) { - solver->set_row_column_coefficient(row, i, get_random_int()); - } -} - -void add_random_cost(lp_primal_simplex * solver, int cols) { - for (int i = 0; i < cols; i++) { - solver->set_cost_for_column(i, get_random_int()); - } -} - -lp_primal_simplex * generate_random_solver() { - int rows = get_random_rows(); - int cols = get_random_columns(); - auto * solver = new lp_primal_simplex(); - for (int i = 0; i < rows; i++) { - add_random_row(solver, cols, i); - } - add_random_cost(solver, cols); - return solver; -} - - - -void random_test_on_i(unsigned i) { - if (i % 1000 == 0) { - std::cout << "."; - } - srand(i); - auto *solver = generate_random_solver(); - solver->find_maximal_solution(); - // std::cout << lp_status_to_string(solver->get_status()) << std::endl; - delete solver; -} - -void random_test() { - for (unsigned i = 0; i < std::numeric_limits::max(); i++) { - try { - random_test_on_i(i); - } - catch (const char * error) { - std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; - break; - } - } -} - #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); @@ -1896,140 +1709,9 @@ void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // std::cout << "fn = " << fn << std::endl; } -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); - -void solve_some_mps(argument_parser & args_parser) { - unsigned max_iters = UINT_MAX, time_limit = UINT_MAX; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0; - unsigned failures = 0; - unsigned inconclusives = 0; - std::set minimums; - vector file_names; - fill_file_names(file_names, minimums); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); - if (compare_with_glpk) { - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - test_out_dir(out_dir); - for (auto& a : file_names) { - try { - std::string file_dir; - std::string file_name; - find_dir_and_file_name(a, file_dir, file_name); - process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - return; - } - if (!solve_for_rational) { - solve_mps(file_names[6], false, time_limit, false, dual, compare_with_primal, args_parser); - solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) - std::unordered_map sol; - sol["X1"] = 0; - sol["X2"] = 6; - sol["X3"] = 0; - sol["X4"] = 15; - sol["X5"] = 2; - sol["X6"] = 1; - sol["X7"] = 1; - sol["X8"] = 0; - solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); - solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); - sol.clear(); - sol["X1"] = 25.0/14.0; - // sol["X2"] = 0; - // sol["X3"] = 0; - // sol["X4"] = 0; - // sol["X5"] = 0; - // sol["X6"] = 0; - // sol["X7"] = 9.0/14.0; - solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) - solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) - solve_mps(file_names[8], false, time_limit, false, dual, compare_with_primal, args_parser); - // return; - for (auto& s : file_names) { - try { - solve_mps(s, minimums.find(s) != minimums.end(), time_limit, false, dual, compare_with_primal, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } else { - // unsigned i = 0; - for (auto& s : file_names) { - // if (i++ > 9) return; - try { - solve_mps_in_rational(s, dual, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } -} -#endif - -void solve_rational() { - lp_primal_simplex solver; - solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); - solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); - - // setting the cost - int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; - std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; - - for (unsigned i = 0; i < 8; i++) { - solver.set_cost_for_column(i, lp::mpq(cost[i])); - solver.give_symbolic_name_to_column(var_names[i], i); - } - - int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); - } - - int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); - } - int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; - for (unsigned i = 0; i < 8; i++) { - solver.set_lower_bound(i, lp::mpq(0)); - solver.set_upper_bound(i, lp::mpq(bounds[i])); - } - - std::unordered_map expected_sol; - expected_sol["x1"] = lp::mpq(0); - expected_sol["x2"] = lp::mpq(6); - expected_sol["x3"] = lp::mpq(0); - expected_sol["x4"] = lp::mpq(15); - expected_sol["x5"] = lp::mpq(2); - expected_sol["x6"] = lp::mpq(1); - expected_sol["x7"] = lp::mpq(1); - expected_sol["x8"] = lp::mpq(0); - solver.find_maximal_solution(); - lp_assert(solver.get_status() == lp_status::OPTIMAL); -#ifdef Z3DEBUG - for (const auto & it : expected_sol) { - (void)it; - lp_assert(it.second == solver.get_column_value_by_name(it.first)); - } #endif -} + std::string read_line(bool & end, std::ifstream & file) { @@ -2047,49 +1729,6 @@ bool contains(std::string const & s, char const * pattern) { } -std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { - std::ifstream file(file_name); - if (!file.is_open()){ - std::cerr << "cannot open " << file_name << std::endl; - return nullptr; - } - std::string s; - bool end; - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - if (contains(s, "Column name")){ - break; - } - } while (true); - - read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - - auto ret = new std::unordered_map(); - - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - auto split = string_split(s, " \t", false); - if (split.empty()) { - return ret; - } - - lp_assert(split.size() > 3); - (*ret)[split[1]] = atof(split[3].c_str()); - } while (true); -} - void test_init_U() { @@ -2189,7 +1828,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); - parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); @@ -2360,237 +1998,9 @@ std::string get_status(std::string file_name) { throw 0; } -// returns true if the costs should be compared too -bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { - std::string glpk_status = get_status(glpk_out_file_name); - std::string lp_tst_status = get_status(lp_out_file_name); - - if (glpk_status != lp_tst_status) { - if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { - successes++; - return false; - } else { - std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; - std::cout << " but lp_tst status is " << lp_tst_status << std::endl; - failures++; - return false; - } - } - return lp_tst_status == "OPTIMAL"; -} - -double get_glpk_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - while (getline(f, str)) { - if (str.find("Objective") != std::string::npos) { - vector tokens = split_and_trim(str); - if (tokens.size() != 5) { - std::cout << "unexpected Objective std::string " << str << std::endl; - throw 0; - } - return atof(tokens[3].c_str()); - } - } - std::cout << "cannot find the Objective line in " << file_name << std::endl; - throw 0; -} - -double get_lp_tst_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - std::string cost_string; - while (getline(f, str)) { - if (str.find("cost") != std::string::npos) { - cost_string = str; - } - } - if (cost_string.empty()) { - std::cout << "cannot find the cost line in " << file_name << std::endl; - throw 0; - } - - vector tokens = split_and_trim(cost_string); - if (tokens.size() != 3) { - std::cout << "unexpected cost string " << cost_string << std::endl; - throw 0; - } - return atof(tokens[2].c_str()); -} - -bool values_are_one_percent_close(double a, double b) { - double maxval = std::max(fabs(a), fabs(b)); - if (maxval < 0.000001) { - return true; - } - - double one_percent = maxval / 100; - return fabs(a - b) <= one_percent; -} - -// returns true if both are optimal -void compare_costs(std::string glpk_out_file_name, - std::string lp_out_file_name, - unsigned & successes, - unsigned & failures) { - double a = get_glpk_cost(glpk_out_file_name); - double b = get_lp_tst_cost(lp_out_file_name); - - if (values_are_one_percent_close(a, b)) { - successes++; - } else { - failures++; - std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; - } -} - -void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { -#ifdef CHECK_GLPK_SOLUTION - std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); - if (solution_is_feasible(lp_file_name, *solution_table)) { - std::cout << "glpk solution is feasible" << std::endl; - } else { - std::cout << "glpk solution is infeasible" << std::endl; - } - delete solution_table; -#endif - if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { - compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); - } -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser); -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { - bool use_mpq = args_parser.option_is_used("--mpq"); - bool minimize = args_parser.option_is_used("--min"); - std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); - - std::string input_file_name = test_dir + "/" + test_file_name; - if (input_file_name[input_file_name.size() - 1] == '~') { - // std::cout << "ignoring " << input_file_name << std::endl; - return; - } - std::cout <<"processing " << input_file_name << std::endl; - - std::ofstream out(full_lp_tst_out_name); - if (!out.is_open()) { - std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; - throw 0; - } - std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer - std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! - bool dual = args_parser.option_is_used("--dual"); - try { - if (args_parser.option_is_used("--lar")) - test_lar_on_file(input_file_name, args_parser); - else - solve_mps(input_file_name, minimize, time_limit, use_mpq, dual, false, args_parser); - } - catch(...) { - std::cout << "catching the failure" << std::endl; - failures++; - std::cout.rdbuf(coutbuf); // reset to standard output again - return; - } - std::cout.rdbuf(coutbuf); // reset to standard output again - - if (args_parser.option_is_used("--compare_with_glpk")) { - std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); - int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); - if (glpk_exit_code != 0) { - std::cout << "glpk failed" << std::endl; - inconclusives++; - } else { - compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); - } - } -} -/* - int my_readdir(DIR *dirp, struct dirent * - #ifndef LEAN_WINDOWS - entry - #endif - , struct dirent **result) { - #ifdef LEAN_WINDOWS - *result = readdir(dirp); // NOLINT - return *result != nullptr? 0 : 1; - #else - return readdir_r(dirp, entry, result); - #endif - } -*/ -/* - vector> get_file_list_of_dir(std::string test_file_dir) { - DIR *dir; - if ((dir = opendir(test_file_dir.c_str())) == nullptr) { - std::cout << "Cannot open directory " << test_file_dir << std::endl; - throw 0; - } - vector> ret; - struct dirent entry; - struct dirent* result; - int return_code; - for (return_code = my_readdir(dir, &entry, &result); - #ifndef LEAN_WINDOWS - result != nullptr && - #endif - return_code == 0; - return_code = my_readdir(dir, &entry, &result)) { - DIR *tmp_dp = opendir(result->d_name); - struct stat file_record; - if (tmp_dp == nullptr) { - std::string s = test_file_dir+ "/" + result->d_name; - int stat_ret = stat(s.c_str(), & file_record); - if (stat_ret!= -1) { - ret.push_back(make_pair(result->d_name, file_record.st_size)); - } else { - perror("stat"); - exit(1); - } - } else { - closedir(tmp_dp); - } - } - closedir(dir); - return ret; - } -*/ -/* - struct file_size_comp { - unordered_map& m_file_sizes; - file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} - int operator()(std::string a, std::string b) { - std::cout << m_file_sizes.size() << std::endl; - std::cout << a << std::endl; - std::cout << b << std::endl; - - auto ls = m_file_sizes.find(a); - std::cout << "fa" << std::endl; - auto rs = m_file_sizes.find(b); - std::cout << "fb" << std::endl; - if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { - std::cout << "fc " << std::endl; - int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); - std::cout << "calc r " << std::endl; - return r; - } else { - std::cout << "sc " << std::endl; - return 0; - } - } - }; - -*/ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; @@ -2598,121 +2008,11 @@ struct sort_pred { }; -void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { - /* - std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - DIR *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - return; - } - closedir(out_dir_p); - vector> files = get_file_list_of_dir(test_file_dir); - std::sort(files.begin(), files.end(), sort_pred()); - unsigned max_iters, time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0, failures = 0, inconclusives = 0; - for (auto & t : files) { - process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - */ -} -std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { - std::unordered_map ret; - for (const auto & it : reader.column_names()) { - ret[it] = lps->get_column_value_by_name(it); - } - return ret; -} -void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { - std::string maxng = args_parser.get_option_value("--maxng"); - if (!maxng.empty()) { - solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); - } - if (args_parser.option_is_used("-pd")){ - solver->settings().presolve_with_double_solver_for_lar = true; - } - - if (args_parser.option_is_used("--compare_with_primal")){ - if (reader == nullptr) { - std::cout << "cannot compare with primal, the reader is null " << std::endl; - return; - } - auto * lps = reader->create_solver(false); - lps->find_maximal_solution(); - std::unordered_map sol = get_solution_map(lps, *reader); - std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; - return; - } - stopwatch sw; - sw.start(); - lp_status status = solver->solve(); - std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; - if (solver->get_status() == lp_status::INFEASIBLE) { - explanation evidence; - solver->get_infeasibility_explanation(evidence); - } - if (args_parser.option_is_used("--randomize_lar")) { - if (solver->get_status() != lp_status::OPTIMAL) { - std::cout << "cannot check randomize on an infeazible problem" << std::endl; - return; - } - std::cout << "checking randomize" << std::endl; - vector all_vars; - for (unsigned j = 0; j < solver->number_of_vars(); j++) - all_vars.push_back(j); - - unsigned m = all_vars.size(); - if (m > 100) - m = 100; - - var_index *vars = new var_index[m]; - for (unsigned i = 0; i < m; i++) - vars[i]=all_vars[i]; - - solver->random_update(m, vars); - delete []vars; - } -} -lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { - if (args_parser.option_is_used("--smt")) { - smt_reader reader(file_name); - reader.read(); - if (!reader.is_ok()){ - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); - } - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser) { - lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); - mps_reader reader(file_name); - mps_reader * mps_reader = nullptr; - reader.read(); - if (reader.is_ok()) { - mps_reader = & reader; - run_lar_solver(args_parser, solver, mps_reader); - } - delete solver; -} vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); @@ -2733,23 +2033,6 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } -void test_lar_solver(argument_parser & args_parser) { - - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - test_lar_on_file(file_name, args_parser); - return; - } - - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - test_lar_on_file(fn, args_parser); - return; - } - - std::cout << "give option --file or --filelist to test_lar_solver\n"; -} void test_numeric_pair() { numeric_pair a; @@ -3934,22 +3217,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("--test_mpq")) { - test_rationals(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np")) { - test_rationals_no_numeric_pairs(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np_plus")) { - test_rationals_no_numeric_pairs_plus(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_int_set")) { test_int_set(); @@ -3967,29 +3234,8 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } -#ifdef Z3DEBUG - if (args_parser.option_is_used("--test_swaps")) { - square_sparse_matrix m(10, 0); - fill_matrix(m); - test_swap_rows_with_permutation(m); - test_swap_cols_with_permutation(m); - return finalize(0); - } -#endif - if (args_parser.option_is_used("--test_perm")) { - test_permutations(); - return finalize(0); - } - if (args_parser.option_is_used("--test_file_directory")) { - test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); - return finalize(0); - } - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - solve_mps(fn, args_parser); - return finalize(0); - } + + if (args_parser.option_is_used("-tbq")) { test_binary_priority_queue(); @@ -3997,100 +3243,6 @@ void test_lp_local(int argn, char**argv) { return finalize(ret); } -#ifdef Z3DEBUG - lp_settings settings; - update_settings(args_parser, settings); - if (args_parser.option_is_used("--test_lu")) { - test_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_small_lu")) { - test_small_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--lar")){ - std::cout <<"calling test_lar_solver" << std::endl; - test_lar_solver(args_parser); - return finalize(0); - } - - - - if (args_parser.option_is_used("--test_larger_lu")) { - test_larger_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_larger_lu_with_holes")) { - test_larger_lu_with_holes(settings); - ret = 0; - return finalize(ret); - } -#endif - if (args_parser.option_is_used("--eti")) { - test_evidence_for_total_inf_simple(args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--maximize_term")) { - test_maximize_term(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_lp_0")) { - test_lp_0(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--smap")) { - test_stacked(); - ret = 0; - return finalize(ret); - } - if (args_parser.option_is_used("--term")) { - test_term(); - ret = 0; - return finalize(ret); - } - unsigned time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - bool dual = args_parser.option_is_used("--dual"); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - solve_mps(file_name, args_parser.option_is_used("--min"), time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--solve_some_mps")) { -#ifndef _WINDOWS - solve_some_mps(args_parser); -#endif - ret = 0; - return finalize(ret); - } - // lp::ccc = 0; - return finalize(0); - test_init_U(); - test_replace_column(); -#ifdef Z3DEBUG - square_sparse_matrix_with_permutations_test(); - test_dense_matrix(); - test_swap_operations(); - test_permutations(); - test_pivot_like_swaps_and_pivot(); -#endif - tst1(); - std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 2ab0c1ea69a..75edb23b987 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -20,18 +20,14 @@ Revision History: #pragma once -// reads an MPS file representing a Mixed Integer Program #include #include #include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lar_solver.h" #include #include #include #include -#include "math/lp/mps_reader.h" #include "math/lp/ul_pair.h" #include "math/lp/lar_constraints.h" #include diff --git a/src/test/lp/test_file_reader.h b/src/test/lp/test_file_reader.h index 8f461ea1c26..36b27374023 100644 --- a/src/test/lp/test_file_reader.h +++ b/src/test/lp/test_file_reader.h @@ -27,7 +27,6 @@ Revision History: #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" namespace lp { From 97c1ba4641138b3990cb93a8aa10c3a1ea6d3f4e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 11:56:23 -0800 Subject: [PATCH 474/597] rm get_column_in_lu_mode --- src/math/lp/lar_solver.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index f13231610bd..20556deab75 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -267,13 +267,7 @@ class lar_solver : public column_namer { void shrink_explanation_to_minimum(vector> & explanation) const; inline bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } bool model_is_int_feasible() const; - inline - indexed_vector & get_column_in_lu_mode(unsigned j) { - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - return m_column_buffer; - } + bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; inline lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } void catch_up_in_updating_int_solver(); From a38be432642598b2afcaccb050d0f29652d1ea4e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 12:15:48 -0800 Subject: [PATCH 475/597] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 2 +- src/math/lp/lar_solver.cpp | 19 ++++---------- src/math/lp/lp_core_solver_base_def.h | 20 ++------------- src/math/lp/lp_settings.h | 8 +++--- src/test/lp/lp.cpp | 36 --------------------------- 5 files changed, 11 insertions(+), 74 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index de8fe68adb5..b6c97513919 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -229,7 +229,7 @@ class lar_core_solver { } bool need_to_presolve_with_double_solver() const { - return settings().simplex_strategy() == simplex_strategy_enum::lu; + return false; } template diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 86ecb02b964..c2a332ad31a 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -45,7 +45,7 @@ namespace lp { delete t; } - bool lar_solver::use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + bool lar_solver::use_lu() const { return false; } bool lar_solver::sizes_are_correct() const { lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); @@ -478,10 +478,7 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); return ret; - case simplex_strategy_enum::lu: - lp_assert(false); // not implemented - return false; - + default: lp_unreachable(); // wrong mode } @@ -1999,20 +1996,14 @@ namespace lp { void lar_solver::decide_on_strategy_and_adjust_initial_state() { lp_assert(strategy_is_undecided()); - if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { - m_settings.set_simplex_strategy(simplex_strategy_enum::lu); - } - else { - m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? - } + + m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? + adjust_initial_state(); } void lar_solver::adjust_initial_state() { switch (m_settings.simplex_strategy()) { - case simplex_strategy_enum::lu: - adjust_initial_state_for_lu(); - break; case simplex_strategy_enum::tableau_rows: adjust_initial_state_for_tableau_rows(); break; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 4d29234a815..40e0a527d41 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -894,27 +894,11 @@ template bool lp_core_solver_base::pivot_column_g lp_assert(m_basis_heading[j] < 0); lp_assert(m_basis_heading[j_basic] >= 0); unsigned row_index = m_basis_heading[j_basic]; - if (m_settings.m_simplex_strategy == simplex_strategy_enum::lu) { - if (m_factorization->need_to_refactor()) { - init_lu(); - } - else { - m_factorization->prepare_entering(j, w); // to init vector w - m_factorization->replace_column(zero_of_type(), w, row_index); - } - if (m_factorization->get_status() != LU_status::OK) { - init_lu(); - return false; - } - else { - change_basis(j, j_basic); - } - } - else { // the tableau case + // the tableau case if (pivot_column_tableau(j, row_index)) change_basis(j, j_basic); else return false; - } + return true; } diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 2245f6f4ed1..790354e5586 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -55,8 +55,7 @@ inline std::ostream& operator<<(std::ostream& out, column_type const& t) { enum class simplex_strategy_enum { undecided = 3, tableau_rows = 0, - tableau_costs = 1, - lu = 2 + tableau_costs = 1 }; std::string column_type_to_string(column_type t); @@ -341,12 +340,11 @@ struct lp_settings { } bool use_lu() const { - return m_simplex_strategy == simplex_strategy_enum::lu; + return false; } bool use_tableau() const { - return m_simplex_strategy == simplex_strategy_enum::tableau_rows || - m_simplex_strategy == simplex_strategy_enum::tableau_costs; + return true; } bool use_tableau_rows() const { diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 78abf1a6f4d..fe589835d48 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1422,42 +1422,6 @@ bool get_double_from_args_parser(const char * option, argument_parser & args_par } -void update_settings(argument_parser & args_parser, lp_settings& settings) { - unsigned n; - settings.m_simplex_strategy = simplex_strategy_enum::lu; - if (get_int_from_args_parser("--rep_frq", args_parser, n)) - settings.report_frequency = n; - else - settings.report_frequency = args_parser.option_is_used("--mpq")? 80: 1000; - - settings.print_statistics = true; - - if (get_int_from_args_parser("--percent_for_enter", args_parser, n)) - settings.percent_of_entering_to_check = n; - if (get_int_from_args_parser("--partial_pivot", args_parser, n)) { - std::cout << "setting partial pivot constant to " << n << std::endl; - settings.c_partial_pivoting = n; - } - if (get_int_from_args_parser("--density", args_parser, n)) { - double density = static_cast(n) / 100.0; - std::cout << "setting density to " << density << std::endl; - settings.density_threshold = density; - } - if (get_int_from_args_parser("--maxng", args_parser, n)) - settings.max_number_of_iterations_with_no_improvements = n; - double d; - if (get_double_from_args_parser("--harris_toler", args_parser, d)) { - std::cout << "setting harris_feasibility_tolerance to " << d << std::endl; - settings.harris_feasibility_tolerance = d; - } - if (get_int_from_args_parser("--random_seed", args_parser, n)) { - settings.set_random_seed(n); - } - if (get_int_from_args_parser("--simplex_strategy", args_parser, n)) { - settings.set_simplex_strategy(static_cast(n)); - } -} - bool values_are_one_percent_close(double a, double b); From 527f0d124280c59641177087b9c5dd58d9af4c9d Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 12:34:15 -0800 Subject: [PATCH 476/597] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 13 +++---------- src/math/lp/lar_solver.h | 1 - src/math/lp/lp_core_solver_base_def.h | 3 +-- src/math/lp/lp_settings.h | 5 +---- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index c2a332ad31a..3339b55a5b7 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -45,8 +45,7 @@ namespace lp { delete t; } - bool lar_solver::use_lu() const { return false; } - + bool lar_solver::sizes_are_correct() const { lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); @@ -1622,8 +1621,7 @@ namespace lp { m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); add_new_var_to_core_fields_for_mpq(false); // false for not adding a row - if (use_lu()) - add_new_var_to_core_fields_for_doubles(false); + } void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { @@ -1785,8 +1783,6 @@ namespace lp { fill_last_row_of_A_r(A_r(), term); } m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); - if (use_lu()) - fill_last_row_of_A_d(A_d(), term); for (lar_term::ival c : *term) { unsigned j = c.column(); while (m_usage_in_terms.size() <= j) { @@ -1798,15 +1794,12 @@ namespace lp { } void lar_solver::add_basic_var_to_core_fields() { - bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); - lp_assert(!use_lu || A_r().column_count() == A_d().column_count()); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); m_incorrect_columns.increase_size_by_one(); m_rows_with_changed_bounds.increase_size_by_one(); add_new_var_to_core_fields_for_mpq(true); - if (use_lu) - add_new_var_to_core_fields_for_doubles(true); + } bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 20556deab75..c1deb2fb1c8 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -166,7 +166,6 @@ class lar_solver : public column_namer { void adjust_initial_state_for_lu(); void adjust_initial_state_for_tableau_rows(); void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); - bool use_lu() const; bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 40e0a527d41..93d5f430239 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -82,8 +82,7 @@ allocate_basis_heading() { // the rest of initialization will be handled by the template void lp_core_solver_base:: init() { allocate_basis_heading(); - if (m_settings.use_lu()) - init_factorization(m_factorization, m_A, m_basis, m_settings); + } // i is the pivot row, and j is the pivot column diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 790354e5586..d6a78564d8f 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -339,10 +339,7 @@ struct lp_settings { m_simplex_strategy = s; } - bool use_lu() const { - return false; - } - + bool use_tableau() const { return true; } From 25f103db1abc5d76412f85ddeb74d24bcb453f5e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 14:58:49 -0800 Subject: [PATCH 477/597] rm_lp Signed-off-by: Lev Nachmanson --- src/math/lp/core_solver_pretty_printer.h | 1 - src/math/lp/core_solver_pretty_printer_def.h | 67 +---- src/math/lp/int_solver.cpp | 1 - src/math/lp/lar_core_solver.h | 68 +---- src/math/lp/lar_core_solver_def.h | 37 +-- src/math/lp/lar_solver.cpp | 57 ++-- src/math/lp/lar_solver.h | 22 +- src/math/lp/lp_core_solver_base.cpp | 14 +- src/math/lp/lp_core_solver_base.h | 12 +- src/math/lp/lp_core_solver_base_def.h | 68 +---- src/math/lp/lp_dual_core_solver_def.h | 22 +- src/math/lp/lp_primal_core_solver.h | 3 +- src/math/lp/lp_primal_core_solver_def.h | 244 +----------------- .../lp/lp_primal_core_solver_tableau_def.h | 34 +-- src/math/lp/lp_settings.h | 6 +- src/math/lp/lu.cpp | 3 - src/math/lp/lu.h | 2 - src/math/lp/lu_def.h | 15 +- src/test/lp/lp.cpp | 30 +-- 19 files changed, 65 insertions(+), 641 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer.h b/src/math/lp/core_solver_pretty_printer.h index 3c0563c32bb..353212dcd1a 100644 --- a/src/math/lp/core_solver_pretty_printer.h +++ b/src/math/lp/core_solver_pretty_printer.h @@ -110,7 +110,6 @@ class core_solver_pretty_printer { return T_to_string(m_exact_column_norms[col]); } - void print_exact_norms(); void print_approx_norms(); diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 23417b691b6..6971061832b 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -59,22 +59,13 @@ core_solver_pretty_printer::core_solver_pretty_printer(const lp_core_solve } template void core_solver_pretty_printer::init_costs() { - if (!m_core_solver.use_tableau()) { - vector local_y(m_core_solver.m_m()); - m_core_solver.solve_yB(local_y); - for (unsigned i = 0; i < ncols(); i++) { - if (m_core_solver.m_basis_heading[i] < 0) { - T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i); - set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i)); - } - } - } else { + for (unsigned i = 0; i < ncols(); i++) { if (m_core_solver.m_basis_heading[i] < 0) { set_coeff(m_costs, m_cost_signs, i, m_core_solver.m_d[i], m_core_solver.column_name(i)); } } - } + } template core_solver_pretty_printer::~core_solver_pretty_printer() { @@ -97,7 +88,7 @@ template T core_solver_pretty_printer::current_co } template void core_solver_pretty_printer::init_m_A_and_signs() { - if (numeric_traits::precise() && m_core_solver.m_settings.use_tableau()) { + if (numeric_traits::precise() ) { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); for (const auto & c : m_core_solver.m_A.m_columns[column]){ @@ -125,23 +116,7 @@ template void core_solver_pretty_printer::init_m_ m_rs[row] += t[row] * m_core_solver.m_x[column]; } } - } else { - for (unsigned column = 0; column < ncols(); column++) { - m_core_solver.solve_Bd(column, m_ed_buff, m_w_buff); // puts the result into m_core_solver.m_ed - string name = m_core_solver.column_name(column); - for (unsigned row = 0; row < nrows(); row ++) { - set_coeff( - m_A[row], - m_signs[row], - column, - m_ed_buff[row], - name); - m_rs[row] += m_ed_buff[row] * m_core_solver.m_x[column]; - } - if (!m_core_solver.use_tableau()) - m_exact_column_norms.push_back(current_column_norm() + T(1)); // a conversion missing 1 -> T - } - } + } } template void core_solver_pretty_printer::init_column_widths() { @@ -190,11 +165,7 @@ template unsigned core_solver_pretty_printer:: ge w = cellw; } } - if (!m_core_solver.use_tableau()) { - w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size()); - if (!m_core_solver.m_column_norms.empty()) - w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size()); - } + return w; } @@ -315,41 +286,15 @@ template void core_solver_pretty_printer::print_u m_out << std::endl; } -template void core_solver_pretty_printer::print_exact_norms() { - if (m_core_solver.use_tableau()) return; - int blanks = m_title_width + 1 - static_cast(m_exact_norm_title.size()); - m_out << m_exact_norm_title; - print_blanks_local(blanks, m_out); - for (unsigned i = 0; i < ncols(); i++) { - string s = get_exact_column_norm_string(i); - int blanks = m_column_widths[i] - static_cast(s.size()); - print_blanks_local(blanks, m_out); - m_out << s << " "; - } - m_out << std::endl; -} template void core_solver_pretty_printer::print_approx_norms() { - if (m_core_solver.use_tableau()) return; - int blanks = m_title_width + 1 - static_cast(m_approx_norm_title.size()); - m_out << m_approx_norm_title; - print_blanks_local(blanks, m_out); - for (unsigned i = 0; i < ncols(); i++) { - string s = T_to_string(m_core_solver.m_column_norms[i]); - int blanks = m_column_widths[i] - static_cast(s.size()); - print_blanks_local(blanks, m_out); - m_out << s << " "; - } - m_out << std::endl; + return; } template void core_solver_pretty_printer::print() { for (unsigned i = 0; i < nrows(); i++) { print_row(i); } - print_exact_norms(); - if (!m_core_solver.m_column_norms.empty()) - print_approx_norms(); m_out << std::endl; if (m_core_solver.inf_set().size()) { m_out << "inf columns: "; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 3aaf8f29eb7..e338e222a27 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -344,7 +344,6 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq set_upper(u, inf_u, upper_bound(j) - xj); - lp_assert(settings().use_tableau()); const auto & A = lra.A_r(); TRACE("random_update", tout << "m = " << m << "\n";); diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index b6c97513919..9678edd6bed 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -80,8 +80,7 @@ class lar_core_solver { column_type get_column_type(unsigned j) { return m_column_types[j];} - void calculate_pivot_row(unsigned i); - + void print_pivot_row(std::ostream & out, unsigned row_index) const { for (unsigned j : m_r_solver.m_pivot_row.m_index) { if (numeric_traits::is_pos(m_r_solver.m_pivot_row.m_data[j])) @@ -138,18 +137,11 @@ class lar_core_solver { void fill_not_improvable_zero_sum(); void pop_basis(unsigned k) { - if (!settings().use_tableau()) { - m_r_pushed_basis.pop(k); - m_r_basis = m_r_pushed_basis(); - m_r_solver.init_basis_heading_and_non_basic_columns_vector(); - m_d_pushed_basis.pop(k); - m_d_basis = m_d_pushed_basis(); - m_d_solver.init_basis_heading_and_non_basic_columns_vector(); - } else { + m_d_basis = m_r_basis; m_d_nbasis = m_r_nbasis; m_d_heading = m_r_heading; - } + } void push() { @@ -160,19 +152,11 @@ class lar_core_solver { m_stacked_simplex_strategy.push(); m_column_types.push(); // rational - if (!settings().use_tableau()) - m_r_A.push(); m_r_lower_bounds.push(); m_r_upper_bounds.push(); - if (!settings().use_tableau()) { - push_vector(m_r_pushed_basis, m_r_basis); - push_vector(m_r_columns_nz, m_r_solver.m_columns_nz); - push_vector(m_r_rows_nz, m_r_solver.m_rows_nz); - } m_d_A.push(); - if (!settings().use_tableau()) - push_vector(m_d_pushed_basis, m_d_basis); + } template @@ -202,8 +186,6 @@ class lar_core_solver { void pop(unsigned k) { // rationals - if (!settings().use_tableau()) - m_r_A.pop(k); m_r_lower_bounds.pop(k); m_r_upper_bounds.pop(k); m_column_types.pop(k); @@ -213,8 +195,7 @@ class lar_core_solver { m_r_x.resize(m_r_A.column_count()); m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); - if(!settings().use_tableau()) - pop_markowitz_counts(k); + m_d_A.pop(k); // doubles delete m_d_solver.m_factorization; @@ -454,7 +435,6 @@ class lar_core_solver { void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { r_basis_is_OK(); - lp_assert(settings().use_tableau()); bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); if (!r) { // it is the case where m_d_solver gives a degenerated basis @@ -553,8 +533,7 @@ class lar_core_solver { bool r_basis_is_OK() const { #ifdef Z3DEBUG - if (!m_r_solver.m_settings.use_tableau()) - return true; + for (unsigned j : m_r_solver.m_basis) { lp_assert(m_r_solver.m_A.m_columns[j].size() == 1); } @@ -568,40 +547,7 @@ class lar_core_solver { return true; } - void solve_on_signature(const lar_solution_signature & signature, const vector & changes_of_basis) { - SASSERT(!settings().use_tableau()); - if (m_r_solver.m_factorization == nullptr) { - for (unsigned j = 0; j < changes_of_basis.size(); j+=2) { - unsigned entering = changes_of_basis[j]; - unsigned leaving = changes_of_basis[j + 1]; - m_r_solver.change_basis_unconditionally(entering, leaving); - } - init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings()); - } else { - catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver); - } - - if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back - catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); - m_r_solver.find_feasible_solution(); - m_d_basis = m_r_basis; - m_d_heading = m_r_heading; - m_d_nbasis = m_r_nbasis; - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; - } else { - prepare_solver_x_with_signature(signature, m_r_solver); - m_r_solver.start_tracing_basis_changes(); - m_r_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return; - m_r_solver.stop_tracing_basis_changes(); - // and now catch up in the double solver - lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); - catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); - } - } - + void create_double_matrix(static_matrix & A) { for (unsigned i = 0; i < m_r_A.row_count(); i++) { auto & row = m_r_A.m_rows[i]; diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 75fff64fd71..182029e3e98 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -46,23 +46,9 @@ lar_core_solver::lar_core_solver( column_names) { } -void lar_core_solver::calculate_pivot_row(unsigned i) { - m_r_solver.calculate_pivot_row(i); -} void lar_core_solver::prefix_r() { - if (!m_r_solver.m_settings.use_tableau()) { - m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n()); - m_r_solver.m_ed.resize(m_r_solver.m_m()); - m_r_solver.m_pivot_row.resize(m_r_solver.m_n()); - m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); - m_r_solver.m_w.resize(m_r_solver.m_m()); - m_r_solver.m_y.resize(m_r_solver.m_m()); - m_r_solver.m_rows_nz.resize(m_r_solver.m_m(), 0); - m_r_solver.m_columns_nz.resize(m_r_solver.m_n(), 0); - init_column_row_nz_for_r_solver(); - } - + // m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) @@ -142,7 +128,7 @@ void lar_core_solver::solve() { } ++settings().stats().m_need_to_solve_inf; CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); - lp_assert((!settings().use_tableau()) || r_basis_is_OK()); + lp_assert( r_basis_is_OK()); if (need_to_presolve_with_double_solver()) { TRACE("lar_solver", tout << "presolving\n";); prefix_d(); @@ -152,26 +138,17 @@ void lar_core_solver::solve() { m_r_solver.set_status(lp_status::TIME_EXHAUSTED); return; } - if (settings().use_tableau()) - solve_on_signature_tableau(solution_signature, changes_of_basis); - else - solve_on_signature(solution_signature, changes_of_basis); - - lp_assert(!settings().use_tableau() || r_basis_is_OK()); + solve_on_signature_tableau(solution_signature, changes_of_basis); + + lp_assert( r_basis_is_OK()); } else { - if (!settings().use_tableau()) { - TRACE("lar_solver", tout << "no tablau\n";); - bool snapped = m_r_solver.snap_non_basic_x_to_bound(); - lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); - if (snapped) - m_r_solver.solve_Ax_eq_b(); - } + if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? m_r_solver.find_feasible_solution(); else { m_r_solver.solve(); } - lp_assert(!settings().use_tableau() || r_basis_is_OK()); + lp_assert(r_basis_is_OK()); } switch (m_r_solver.get_status()) { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 3339b55a5b7..2e9541525d4 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -261,8 +261,7 @@ namespace lp { m_crossed_bounds_column.pop(k); unsigned n = m_columns_to_ul_pairs.peek_size(k); m_var_register.shrink(n); - if (m_settings.use_tableau()) - pop_tableau(); + pop_tableau(); lp_assert(A_r().column_count() == n); TRACE("lar_solver_details", for (unsigned j = 0; j < n; j++) { @@ -284,7 +283,7 @@ namespace lp { clean_popped_elements(m, m_rows_with_changed_bounds); clean_inf_set_of_r_solver_after_pop(); lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + ( m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau())); m_constraints.pop(k); @@ -299,7 +298,7 @@ namespace lp { m_simplex_strategy.pop(k); m_settings.set_simplex_strategy(m_simplex_strategy); lp_assert(sizes_are_correct()); - lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_usage_in_terms.pop(k); set_status(lp_status::UNKNOWN); } @@ -630,15 +629,7 @@ namespace lp { } void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { - if (A_r().row_count() != m_column_buffer.data_size()) - m_column_buffer.resize(A_r().row_count()); - else - m_column_buffer.clear(); - lp_assert(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); - - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) - insert_row_with_changed_bounds(i); + lp_assert(false); } @@ -648,8 +639,7 @@ namespace lp { insert_row_with_changed_bounds(rc.var()); } - bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } - + bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } @@ -692,7 +682,7 @@ namespace lp { } void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair& delta) { - if (use_tableau()) { + { for (const auto& c : A_r().m_columns[j]) { unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; if (tableau_with_costs()) { @@ -705,15 +695,7 @@ namespace lp { } } - else { - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -m_column_buffer[i] * delta); - } - } + } void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { @@ -742,10 +724,8 @@ namespace lp { return; } - if (use_tableau()) - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); - else - detect_rows_of_bound_change_column_for_nbasic_column(j); + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); + } void lar_solver::detect_rows_with_changed_bounds() { @@ -773,8 +753,6 @@ namespace lp { void lar_solver::solve_with_core_solver() { - if (!use_tableau()) - add_last_rows_to_lu(m_mpq_lar_core_solver.m_r_solver); if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); } @@ -782,10 +760,7 @@ namespace lp { if (costs_are_used()) { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); } - if (use_tableau()) - update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); - else - update_x_and_inf_costs_for_columns_with_changed_bounds(); + update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); m_mpq_lar_core_solver.solve(); set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); lp_assert(((stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold()); @@ -1753,7 +1728,7 @@ namespace lp { SASSERT(m_terms.size() == m_term_register.size()); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = tv::mask_term(adjusted_term_index); - if (use_tableau() && !coeffs.empty()) { + if ( !coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); if (m_settings.bound_propagation()) insert_row_with_changed_bounds(A_r().row_count() - 1); @@ -1774,14 +1749,12 @@ namespace lp { ul_pair ul(true); // to mark this column as associated_with_row m_columns_to_ul_pairs.push_back(ul); add_basic_var_to_core_fields(); - if (use_tableau()) { - A_r().fill_last_row_with_pivoting(*term, + + A_r().fill_last_row_with_pivoting(*term, j, m_mpq_lar_core_solver.m_r_solver.m_basis_heading); - } - else { - fill_last_row_of_A_r(A_r(), term); - } + + m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); for (lar_term::ival c : *term) { unsigned j = c.column(); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index c1deb2fb1c8..51365936819 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -177,7 +177,6 @@ class lar_solver : public column_namer { if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation || row_has_a_big_num(row_index)) return; - lp_assert(use_tableau()); bound_analyzer_on_row, lp_bound_propagator>::analyze_row(A_r().m_rows[row_index], null_ci, @@ -190,7 +189,6 @@ class lar_solver : public column_namer { void substitute_basis_var_in_terms_for_row(unsigned i); template void calculate_implied_bounds_for_row(unsigned i, lp_bound_propagator & bp) { - SASSERT(use_tableau()); analyze_new_bounds_on_row_tableau(i, bp); } static void clean_popped_elements(unsigned n, u_set& set); @@ -210,7 +208,6 @@ class lar_solver : public column_namer { vector> &left_side) const; void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); - bool use_tableau() const; bool use_tableau_costs() const; void detect_rows_of_column_with_bound_change(unsigned j); void adjust_x_of_column(unsigned j); @@ -376,7 +373,6 @@ class lar_solver : public column_namer { void mark_rows_for_bound_prop(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { - SASSERT(use_tableau()); for (unsigned i : m_rows_with_changed_bounds) { calculate_implied_bounds_for_row(i, bp); if (settings().get_cancel_flag()) @@ -429,8 +425,8 @@ class lar_solver : public column_namer { void change_basic_columns_dependend_on_a_given_nb_column_report(unsigned j, const numeric_pair & delta, const ChangeReport& after) { - if (use_tableau()) { - for (const auto & c : A_r().m_columns[j]) { + + for (const auto & c : A_r().m_columns[j]) { unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; if (tableau_with_costs()) { m_basic_columns_with_changed_cost.insert(bj); @@ -440,20 +436,8 @@ class lar_solver : public column_namer { TRACE("change_x_del", tout << "changed basis column " << bj << ", it is " << ( m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj)? "feas":"inf") << std::endl;); - - - } - } else { - NOT_IMPLEMENTED_YET(); - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -m_column_buffer[i] * delta); } - } - } + } template void set_value_for_nbasic_column_report(unsigned j, diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index c8b1692d272..5dc8fb9e2c2 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -26,7 +26,6 @@ Revision History: template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const; -template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); @@ -52,16 +51,12 @@ template void lp::lp_core_solver_base::set_non_basic_x_to_correc template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_Bd(unsigned int); -template void lp::lp_core_solver_base::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; -template void lp::lp_core_solver_base>::solve_Bd(unsigned int, indexed_vector&); template void lp::lp_core_solver_base::solve_yB(vector&) const; template bool lp::lp_core_solver_base::update_basis_and_x(int, int, double const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; -template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); @@ -71,11 +66,9 @@ template bool lp::lp_core_solver_base::print_statistics_with_i template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_Bd(unsigned int); template void lp::lp_core_solver_base::solve_yB(vector&) const; template bool lp::lp_core_solver_base::update_basis_and_x(int, int, lp::mpq const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); -template void lp::lp_core_solver_base >::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); @@ -88,7 +81,7 @@ template lp::lp_core_solver_base >::lp_core_s template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); template void lp::lp_core_solver_base >::solve_Ax_eq_b(); -template void lp::lp_core_solver_base >::solve_Bd(unsigned int); + template bool lp::lp_core_solver_base >::update_basis_and_x(int, int, lp::numeric_pair const&); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( @@ -145,8 +138,7 @@ template bool lp::lp_core_solver_base::inf_set_is_correct() co template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; -template void lp::lp_core_solver_base >::calculate_pivot_row(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int, lp::numeric_pair const&); -template void lp::lp_core_solver_base::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; -template void lp::lp_core_solver_base >::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; + + diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index b7010aa54a8..631f687816c 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -156,11 +156,6 @@ class lp_core_solver_base { void solve_yB(vector & y) const; - void solve_Bd(unsigned entering, indexed_vector & d_buff, indexed_vector& w_buff) const; - - void solve_Bd(unsigned entering); - - void solve_Bd(unsigned entering, indexed_vector & column); void pretty_print(std::ostream & out); @@ -184,9 +179,7 @@ class lp_core_solver_base { bool A_mult_x_is_off() const; bool A_mult_x_is_off_on_index(const vector & index) const; - // from page 182 of Istvan Maros's book - void calculate_pivot_row_of_B_1(unsigned pivot_row); - + void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); void add_delta_to_entering(unsigned entering, const X & delta); @@ -670,7 +663,6 @@ class lp_core_solver_base { m_settings.simplex_strategy(); } - bool use_tableau() const { return m_settings.use_tableau(); } template static void swap(vector &v, unsigned i, unsigned j) { @@ -760,7 +752,7 @@ class lp_core_solver_base { return m_iters_with_no_cost_growing; } - void calculate_pivot_row(unsigned i); + unsigned get_base_column_in_row(unsigned row_index) const { return m_basis[row_index]; } diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 93d5f430239..0e834243050 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -131,35 +131,9 @@ solve_yB(vector & y) const { // m_index_of_ed.push_back(i); // } // } -template void lp_core_solver_base::solve_Bd(unsigned entering, indexed_vector & column) { - lp_assert(!m_settings.use_tableau()); - if (m_factorization == nullptr) { - init_factorization(m_factorization, m_A, m_basis, m_settings); - } - m_factorization->solve_Bd_faster(entering, column); -} -template void lp_core_solver_base::solve_Bd(unsigned , indexed_vector& , indexed_vector &) const { - NOT_IMPLEMENTED_YET(); -} -template void lp_core_solver_base:: -solve_Bd(unsigned entering) { - lp_assert(m_ed.is_OK()); - m_factorization->solve_Bd(entering, m_ed, m_w); - if (this->precise()) - m_columns_nz[entering] = m_ed.m_index.size(); - lp_assert(m_ed.is_OK()); - lp_assert(m_w.is_OK()); -#ifdef Z3DEBUG - // auto B = get_B(*m_factorization, m_basis); - // vector a(m_m()); - // m_A.copy_column_to_vector(entering, a); - // vector cd(m_ed.m_data); - // B.apply_from_left(cd, m_settings); - // lp_assert(vectors_are_equal(cd , a)); -#endif -} + template void lp_core_solver_base:: pretty_print(std::ostream & out) { @@ -279,17 +253,6 @@ A_mult_x_is_off_on_index(const vector & index) const { return false; } -// from page 182 of Istvan Maros's book -template void lp_core_solver_base:: -calculate_pivot_row_of_B_1(unsigned pivot_row) { - lp_assert(! use_tableau()); - lp_assert(m_pivot_row_of_B_1.is_OK()); - m_pivot_row_of_B_1.clear(); - m_pivot_row_of_B_1.set_value(numeric_traits::one(), pivot_row); - lp_assert(m_pivot_row_of_B_1.is_OK()); - m_factorization->solve_yB_with_error_check_indexed(m_pivot_row_of_B_1, m_basis_heading, m_basis, m_settings); - lp_assert(m_pivot_row_of_B_1.is_OK()); -} template void lp_core_solver_base:: @@ -316,13 +279,7 @@ calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row) { template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { m_x[entering] += delta; - if (!use_tableau()) - for (unsigned i : m_ed.m_index) { - if (!numeric_traits::precise()) - m_copy_of_xB[i] = m_x[m_basis[i]]; - m_x[m_basis[i]] -= delta * m_ed[i]; - } - else + for (const auto & c : m_A.m_columns[entering]) { unsigned i = c.var(); m_x[m_basis[i]] -= delta * m_A.get_val(c); @@ -1000,26 +957,5 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) } } -template -void lp_core_solver_base::calculate_pivot_row(unsigned i) { - lp_assert(!use_tableau()); - lp_assert(m_pivot_row.is_OK()); - m_pivot_row_of_B_1.clear(); - m_pivot_row_of_B_1.resize(m_m()); - m_pivot_row.clear(); - m_pivot_row.resize(m_n()); - if (m_settings.use_tableau()) { - unsigned basic_j = m_basis[i]; - for (auto & c : m_A.m_rows[i]) { - if (c.var() != basic_j) - m_pivot_row.set_value(c.coeff(), c.var()); - } - return; - } - - calculate_pivot_row_of_B_1(i); - calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); -} - } diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h index b42d644af34..df70e64f1bc 100644 --- a/src/math/lp/lp_dual_core_solver_def.h +++ b/src/math/lp/lp_dual_core_solver_def.h @@ -210,26 +210,8 @@ template void lp_dual_core_solver::DSE_FTran() { } template bool lp_dual_core_solver::advance_on_known_p() { - if (done()) { - return true; - } - this->calculate_pivot_row_of_B_1(m_r); - this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(m_r); - if (!ratio_test()) { - return true; - } - calculate_beta_r_precisely(); - this->solve_Bd(m_q); // FTRAN - int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); - if (!pivot_compare_result){;} - else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - lp_unreachable(); // not implemented yet - } else { - lp_assert(pivot_compare_result == 1); - this->init_lu(); - } - DSE_FTran(); - return basis_change_and_update(); + + return false; } template int lp_dual_core_solver::define_sign_of_alpha_r() { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index a60395ab014..dc6cb2900e8 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -468,7 +468,6 @@ class lp_primal_core_solver:public lp_core_solver_base { } void update_basis_and_x_tableau_rows(int entering, int leaving, X const & tt) { - lp_assert(this->use_tableau()); lp_assert(entering != leaving); update_x_tableau_rows(entering, leaving, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); @@ -804,7 +803,7 @@ class lp_primal_core_solver:public lp_core_solver_base { return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); } - void init_reduced_costs(); + bool lower_bounds_are_set() const override { return true; } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 3818b589a5a..0a58b0fdf69 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -33,21 +33,14 @@ namespace lp { template void lp_primal_core_solver::sort_non_basis_rational() { lp_assert(numeric_traits::precise()); - if (this->m_settings.use_tableau()) { + std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); if (ca == 0 && cb != 0) return false; return ca < cb; }); - } else { - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { - unsigned ca = this->m_columns_nz[a]; - unsigned cb = this->m_columns_nz[b]; - if (ca == 0 && cb != 0) return false; - return ca < cb; - });} - + m_non_basis_list.clear(); // reinit m_basis_heading for (unsigned j = 0; j < this->m_nbasis.size(); j++) { @@ -644,25 +637,7 @@ template void lp_primal_core_solver::backup_an } template void lp_primal_core_solver::init_run() { - this->m_basis_sort_counter = 0; // to initiate the sort of the basis - // this->set_total_iterations(0); - this->iters_with_no_cost_growing() = 0; - init_inf_set(); - if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) - return; - this->set_using_infeas_costs(false); - if (this->m_settings.backup_costs) - backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = numeric_traits::precise()? zero_of_type(): T(1)/T(10000000); - m_breakpoint_indices_queue.resize(this->m_n()); - init_reduced_costs(); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } else { - if (this->m_columns_nz.size() != this->m_n()) - init_column_row_non_zeroes(); - } + } @@ -676,166 +651,20 @@ template void lp_primal_core_solver::calc_work template void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { - CASSERT("A_off", !this->A_mult_x_is_off() ); - this->add_delta_to_entering(entering, t * m_sign_of_entering_delta); - if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) { - this->init_lu(); - if (!this->find_x_by_solving()) { - this->restore_x(entering, t * m_sign_of_entering_delta); - this->iters_with_no_cost_growing()++; - LP_OUT(this->m_settings, "failing in advance_on_entering_equal_leaving for entering = " << entering << std::endl); - return; - } - } - if (this->using_infeas_costs()) { - lp_assert(is_zero(this->m_costs[entering])); - init_infeasibility_costs_for_changed_basis_only(); - } - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - return; - if (need_to_switch_costs() ||!this->current_x_is_feasible()) { - init_reduced_costs(); - } - this->iters_with_no_cost_growing() = 0; } template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { - lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); - lp_assert(this->using_infeas_costs() || t >= zero_of_type()); - lp_assert(leaving >= 0 && entering >= 0); - lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes - if (entering == leaving) { - advance_on_entering_equal_leaving(entering, t); - return; - } - unsigned pivot_row = this->m_basis_heading[leaving]; - this->calculate_pivot_row_of_B_1(pivot_row); - this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(pivot_row); - - int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving); - if (!pivot_compare_result){;} - else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - this->set_status(lp_status::UNSTABLE); - this->iters_with_no_cost_growing()++; - return; - } else { - lp_assert(pivot_compare_result == 1); - this->init_lu(); - if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::UNSTABLE); - this->iters_with_no_cost_growing()++; - return; - } - } - if (!numeric_traits::precise()) - calc_working_vector_beta_for_column_norms(); - if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search) { - if (m_sign_of_entering_delta == -1) - t = -t; - } - if (!this->update_basis_and_x(entering, leaving, t)) { - if (this->get_status() == lp_status::FLOATING_POINT_ERROR) - return; - if (this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - init_reduced_costs(); - return; - } - - if (!is_zero(t)) { - this->iters_with_no_cost_growing() = 0; - init_infeasibility_after_update_x_if_inf(leaving); - } - - if (this->current_x_is_feasible()) { - this->set_status(lp_status::FEASIBLE); - if (this->m_look_for_feasible_solution_only) - return; - } - if (numeric_traits::precise() == false) - update_or_init_column_norms(entering, leaving); - - - if (need_to_switch_costs()) { - init_reduced_costs(); - } else { - update_reduced_costs_from_pivot_row(entering, leaving); - } - lp_assert(!need_to_switch_costs()); - std::list::iterator it = m_non_basis_list.end(); - it--; - * it = static_cast(leaving); + } template void lp_primal_core_solver::advance_on_entering_precise(int entering) { - lp_assert(numeric_traits::precise()); - lp_assert(entering > -1); - this->solve_Bd(entering); - X t; - int leaving = find_leaving_and_t_precise(entering, t); - if (leaving == -1) { - TRACE("lar_solver", tout << "non-leaving\n";); - this->set_status(lp_status::UNBOUNDED); - return; - } - advance_on_entering_and_leaving(entering, leaving, t); + lp_assert(false); } template void lp_primal_core_solver::advance_on_entering(int entering) { - if (numeric_traits::precise()) { - advance_on_entering_precise(entering); - return; - } - lp_assert(entering > -1); - this->solve_Bd(entering); - int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); - if (refresh_result) { - if (this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - - this->init_lu(); - init_reduced_costs(); - if (refresh_result == 2) { - this->iters_with_no_cost_growing()++; - return; - } - } - X t; - int leaving = find_leaving_and_t(entering, t); - if (leaving == -1){ - if (!this->current_x_is_feasible()) { - lp_assert(!numeric_traits::precise()); // we cannot have unbounded with inf costs - - // if (m_look_for_feasible_solution_only) { - // this->m_status = INFEASIBLE; - // return; - // } - - - if (this->get_status() == lp_status::UNSTABLE) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - init_infeasibility_costs(); - this->set_status(lp_status::UNSTABLE); - - return; - } - if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) { - this->set_status(lp_status::UNBOUNDED); - } else { - this->set_status(lp_status::TENTATIVE_UNBOUNDED); - } - TRACE("lar_solver", tout << this->get_status() << "\n";); - return; - } - advance_on_entering_and_leaving(entering, leaving, t); + lp_assert(false); } template void lp_primal_core_solver::push_forward_offset_in_non_basis(unsigned & offset_in_nb) { @@ -867,7 +696,7 @@ template void lp_primal_core_solver::print_column // returns the number of iterations template unsigned lp_primal_core_solver::solve() { TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); - if (numeric_traits::precise() && this->m_settings.use_tableau()) + if (numeric_traits::precise()) return solve_with_tableau(); init_run(); @@ -893,56 +722,19 @@ template unsigned lp_primal_core_solver::solve() case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; - if (!numeric_traits::precise()) { - if(this->m_look_for_feasible_solution_only) - break; - this->init_lu(); + { // precise case - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status (lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } else { // precise case - if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time! - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } } break; case lp_status::TENTATIVE_UNBOUNDED: - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - - init_reduced_costs(); + lp_assert(false); break; case lp_status::UNBOUNDED: - if (this->current_x_is_infeasible()) { - init_reduced_costs(); - this->set_status(lp_status::UNKNOWN); - } + lp_assert(false); break; case lp_status::UNSTABLE: - lp_assert(! (numeric_traits::precise())); - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); + lp_assert(false); break; default: @@ -1292,20 +1084,6 @@ template void lp_primal_core_solver::print_breakp print_bound_info_and_x(b->m_j, out); } -template -void lp_primal_core_solver::init_reduced_costs() { - lp_assert(!this->use_tableau()); - if (this->current_x_is_infeasible() && !this->using_infeas_costs()) { - init_infeasibility_costs(); - } else if (this->current_x_is_feasible() && this->using_infeas_costs()) { - if (this->m_look_for_feasible_solution_only) - return; - this->m_costs = m_costs_backup; - this->set_using_infeas_costs(false); - } - - this->init_reduced_costs_for_one_iteration(); -} template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { if (b->m_j == entering) { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 46297a63e14..fa25694adc7 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -126,22 +126,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; - if (!numeric_traits::precise()) { - if(this->m_look_for_feasible_solution_only) - break; - this->init_lu(); - - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } else { // precise case + { // precise case if ((!this->infeasibility_costs_are_correct())) { init_reduced_costs_tableau(); // forcing recalc if (choose_entering_column_tableau() == -1) { @@ -153,13 +138,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } break; case lp_status::TENTATIVE_UNBOUNDED: - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - - init_reduced_costs(); + lp_assert(false); break; case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { @@ -169,13 +148,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { break; case lp_status::UNSTABLE: - lp_assert(! (numeric_traits::precise())); - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); + lp_assert(false); break; default: @@ -348,7 +321,6 @@ template void lp_primal_core_solver::init_run_tab template bool lp_primal_core_solver:: update_basis_and_x_tableau(int entering, int leaving, X const & tt) { - lp_assert(this->use_tableau()); lp_assert(entering != leaving); update_x_tableau(entering, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index d6a78564d8f..360ef99bf1d 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -339,11 +339,7 @@ struct lp_settings { m_simplex_strategy = s; } - - bool use_tableau() const { - return true; - } - + bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } diff --git a/src/math/lp/lu.cpp b/src/math/lp/lu.cpp index 6c9bcc5f65f..313fa990151 100644 --- a/src/math/lp/lu.cpp +++ b/src/math/lp/lu.cpp @@ -28,13 +28,10 @@ template double dot_product(vector const&, vector>::lu(static_matrix const&, vector&, lp_settings&); template void lu>::push_matrix_to_tail(tail_matrix*); template void lu>::replace_column(double, indexed_vector&, unsigned); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template mpq dot_product(vector const&, vector const&); template void init_factorization> diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index aca59065d4b..191018100f8 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -171,8 +171,6 @@ class lu { void print_matrix_compact(std::ostream & f); void print(indexed_vector & w, const vector& basis); - void solve_Bd(unsigned a_column, vector & d, indexed_vector & w); - void solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w); void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit void solve_yB(vector& y); diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h index 80c9cdf0ed4..059a430124c 100644 --- a/src/math/lp/lu_def.h +++ b/src/math/lp/lu_def.h @@ -117,7 +117,7 @@ lu::lu(const M& A, m_failure(false), m_row_eta_work_vector(A.row_count()), m_refactor_counter(0) { - lp_assert(!(numeric_traits::precise() && settings.use_tableau())); + lp_assert(!(numeric_traits::precise() )); #ifdef Z3DEBUG debug_test_of_basis(A, basis); #endif @@ -256,19 +256,6 @@ void lu< M>::print(indexed_vector & w, const vector& basis) { print_indexed_vector(w, f); f.close(); } -template -void lu< M>::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { - init_vector_w(a_column, w); - - if (w.m_index.size() * ratio_of_index_size_to_all_size() < d.m_data.size()) { // this const might need some tuning - d = w; - solve_By_for_T_indexed_only(d, m_settings); - } else { - d.m_data = w.m_data; - d.m_index.clear(); - solve_By_when_y_is_ready_for_T(d.m_data, d.m_index); - } -} template void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index fe589835d48..189bd604b92 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -2095,35 +2095,7 @@ void read_indexed_vector(indexed_vector & v, std::ifstream & f) { } void check_lu_from_file(std::string lufile_name) { - std::ifstream f(lufile_name); - if (!f.is_open()) { - std::cout << "cannot open file " << lufile_name << std::endl; - } - unsigned m, n; - get_matrix_dimensions(f, m, n); - std::cout << "init matrix " << m << " by " << n << std::endl; - static_matrix A(m, n); - read_rows(A, f); - vector basis; - read_basis(basis, f); - indexed_vector v(m); - // read_indexed_vector(v, f); - f.close(); - vector basis_heading; - lp_settings settings; - vector non_basic_columns; - lu> lsuhl(A, basis, settings); - indexed_vector d(A.row_count()); - unsigned entering = 26; - lsuhl.solve_Bd(entering, d, v); -#ifdef Z3DEBUG - auto B = get_B(lsuhl, basis); - vector a(m); - A.copy_column_to_vector(entering, a); - indexed_vector cd(d); - B.apply_from_left(cd.m_data, settings); - lp_assert(vectors_are_equal(cd.m_data , a)); -#endif + lp_assert(false); } void test_square_dense_submatrix() { From c251151d6631bfaf11f9d8eae6cb20979d96ffc0 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 15:15:08 -0800 Subject: [PATCH 478/597] rm_lu --- src/math/lp/lar_solver.cpp | 19 +----- src/math/lp/lp_primal_core_solver.h | 2 +- src/math/lp/lu.h | 59 ++---------------- src/test/lp/lp.cpp | 92 +---------------------------- 4 files changed, 9 insertions(+), 163 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 2e9541525d4..d9961eaa8ab 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -795,24 +795,7 @@ namespace lp { template void lar_solver::add_last_rows_to_lu(lp_primal_core_solver& s) { - auto& f = s.m_factorization; - if (f != nullptr) { - auto columns_to_replace = f->get_set_of_columns_to_replace_for_add_last_rows(s.m_basis_heading); - if (f->m_refactor_counter + columns_to_replace.size() >= 200 || f->has_dense_submatrix()) { - delete f; - f = nullptr; - } - else { - f->add_last_rows_to_B(s.m_basis_heading, columns_to_replace); - } - } - if (f == nullptr) { - init_factorization(f, s.m_A, s.m_basis, m_settings); - if (f->get_status() != LU_status::OK) { - delete f; - f = nullptr; - } - } + lp_assert(false); } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index dc6cb2900e8..5332b4001da 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -418,7 +418,7 @@ class lp_primal_core_solver:public lp_core_solver_base { // returns the number of iterations unsigned solve(); - lu> * factorization() {return this->m_factorization;} + lu> * factorization() {return nullptr;} void delete_factorization(); diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index 191018100f8..bcbb850434d 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -305,66 +305,19 @@ class lu { void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element); void prepare_entering(unsigned entering, indexed_vector & w) { - init_vector_w(entering, w); + lp_assert(false); } - bool need_to_refactor() { return m_refactor_counter >= 200; } + bool need_to_refactor() { lp_assert(false); + return m_refactor_counter >= 200; } void adjust_dimension_with_matrix_A() { - lp_assert(m_A.row_count() >= m_dim); - m_dim = m_A.row_count(); - m_U.resize(m_dim); - m_Q.resize(m_dim); - m_R.resize(m_dim); - m_row_eta_work_vector.resize(m_dim); + lp_assert(false); } - std::unordered_set get_set_of_columns_to_replace_for_add_last_rows(const vector & heading) const { - std::unordered_set columns_to_replace; - unsigned m = m_A.row_count(); - unsigned m_prev = m_U.dimension(); - - lp_assert(m_A.column_count() == heading.size()); - - for (unsigned i = m_prev; i < m; i++) { - for (const row_cell & c : m_A.m_rows[i]) { - int h = heading[c.var()]; - if (h < 0) { - continue; - } - columns_to_replace.insert(c.var()); - } - } - return columns_to_replace; - } - void add_last_rows_to_B(const vector & heading, const std::unordered_set & columns_to_replace) { - unsigned m = m_A.row_count(); - lp_assert(m_A.column_count() == heading.size()); - adjust_dimension_with_matrix_A(); - m_w_for_extension.resize(m); - // At this moment the LU is correct - // for B extended by only by ones at the diagonal in the lower right corner - - for (unsigned j :columns_to_replace) { - lp_assert(heading[j] >= 0); - replace_column_with_only_change_at_last_rows(j, heading[j]); - if (get_status() == LU_status::Degenerated) - break; - } - } - // column j is a basis column, and there is a change in the last rows - void replace_column_with_only_change_at_last_rows(unsigned j, unsigned column_to_change_in_U) { - init_vector_w(j, m_w_for_extension); - replace_column(zero_of_type(), m_w_for_extension, column_to_change_in_U); - } - - bool has_dense_submatrix() const { - for (auto m : m_tail) - if (m->is_dense()) - return true; - return false; - } + + }; // end of lu diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 189bd604b92..ceca92b4ecc 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -551,90 +551,7 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, #ifdef Z3DEBUG void test_small_lu(lp_settings & settings) { - std::cout << " test_small_lu" << std::endl; - static_matrix m(3, 6); - vector basis(3); - basis[0] = 0; - basis[1] = 1; - basis[2] = 3; - - m(0, 0) = 1; m(0, 2)= 3.9; m(2, 3) = 11; m(0, 5) = -3; - m(1, 1) = 4; m(1, 4) = 7; - m(2, 0) = 1.8; m(2, 2) = 5; m(2, 4) = 2; m(2, 5) = 8; - -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - lp_assert(l.is_correct(basis)); - indexed_vector w(m.row_count()); - std::cout << "entering 2, leaving 0" << std::endl; - l.prepare_entering(2, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(2, 0, basis, non_basic_columns, heading); - // #ifdef Z3DEBUG - // std::cout << "we were factoring " << std::endl; - // print_matrix(get_B(l)); - // #endif - lp_assert(l.is_correct(basis)); - std::cout << "entering 4, leaving 3" << std::endl; - l.prepare_entering(4, w); // to init vector w - l.replace_column(0, w, heading[3]); - change_basis(4, 3, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - - std::cout << "entering 5, leaving 1" << std::endl; - l.prepare_entering(5, w); // to init vector w - l.replace_column(0, w, heading[1]); - change_basis(5, 1, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - std::cout << "entering 3, leaving 2" << std::endl; - l.prepare_entering(3, w); // to init vector w - l.replace_column(0, w, heading[2]); - change_basis(3, 2, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - - m.add_row(); - m.add_column(); - m.add_row(); - m.add_column(); - for (unsigned i = 0; i < m.column_count(); i++) { - m(3, i) = i; - m(4, i) = i * i; // to make the rows linearly independent - } - unsigned j = m.column_count() ; - basis.push_back(j-2); - heading.push_back(basis.size() - 1); - basis.push_back(j-1); - heading.push_back(basis.size() - 1); - auto columns_to_replace = l.get_set_of_columns_to_replace_for_add_last_rows(heading); - l.add_last_rows_to_B(heading, columns_to_replace); - lp_assert(l.is_correct(basis)); } #endif @@ -827,10 +744,7 @@ void test_larger_lu(lp_settings& settings) { void test_lu(lp_settings & settings) { - test_small_lu(settings); - test_larger_lu(settings); - test_larger_lu_with_holes(settings); - test_larger_lu_exp(settings); + } #endif @@ -1785,10 +1699,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_after_string_with_help("--time_limit", "time limit in seconds"); parser.add_option_with_help_string("--mpq", "solve for rational numbers"); parser.add_option_with_after_string_with_help("--simplex_strategy", "sets simplex strategy for rational number"); - parser.add_option_with_help_string("--test_lu", "test the work of the factorization"); - parser.add_option_with_help_string("--test_small_lu", "test the work of the factorization on a smallish matrix"); - parser.add_option_with_help_string("--test_larger_lu", "test the work of the factorization"); - parser.add_option_with_help_string("--test_larger_lu_with_holes", "test the work of the factorization"); parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); From 9a7c99da332fae795dc52ea82e4ccba000e2b778 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 15:26:55 -0800 Subject: [PATCH 479/597] rm lu --- src/math/lp/lu.h | 10 +- src/math/lp/lu_def.h | 235 +++---------------------------------------- src/test/lp/lp.cpp | 136 ------------------------- 3 files changed, 16 insertions(+), 365 deletions(-) diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index bcbb850434d..b3e2f0c2221 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -266,13 +266,6 @@ class lu { bool is_correct(); -#ifdef Z3DEBUG - dense_matrix tail_product(); - dense_matrix get_left_side(const vector& basis); - dense_matrix get_left_side(); - - dense_matrix get_right_side(); -#endif // needed for debugging purposes void copy_w(T *buffer, indexed_vector & w); @@ -296,8 +289,7 @@ class lu { void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump); // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last // row at the same time - row_eta_matrix *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking); - + void replace_column(T pivot_elem, indexed_vector & w, unsigned leaving_column_of_U); void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump); diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h index 059a430124c..d7c0c0c3a07 100644 --- a/src/math/lp/lu_def.h +++ b/src/math/lp/lu_def.h @@ -626,161 +626,41 @@ void lu::process_column(int j) { } template bool lu::is_correct(const vector& basis) { -#ifdef Z3DEBUG - if (get_status() != LU_status::OK) { - return false; - } - dense_matrix left_side = get_left_side(basis); - dense_matrix right_side = get_right_side(); - return left_side == right_side; -#else - return true; -#endif +return true; } template bool lu::is_correct() { -#ifdef Z3DEBUG - if (get_status() != LU_status::OK) { - return false; - } - dense_matrix left_side = get_left_side(); - dense_matrix right_side = get_right_side(); - return left_side == right_side; -#else return true; -#endif } -#ifdef Z3DEBUG -template -dense_matrix lu::tail_product() { - lp_assert(tail_size() > 0); - dense_matrix left_side = permutation_matrix(m_dim); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_left_side(const vector& basis) { - dense_matrix left_side = get_B(*this, basis); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_left_side() { - dense_matrix left_side = get_B(*this); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_right_side() { - auto ret = U() * R(); - ret = Q() * ret; - return ret; -} -#endif - // needed for debugging purposes template void lu::copy_w(T *buffer, indexed_vector & w) { - unsigned i = m_dim; - while (i--) { - buffer[i] = w[i]; - } + } // needed for debugging purposes template void lu::restore_w(T *buffer, indexed_vector & w) { - unsigned i = m_dim; - while (i--) { - w[i] = buffer[i]; - } + } template bool lu::all_columns_and_rows_are_active() { - unsigned i = m_dim; - while (i--) { - lp_assert(m_U.col_is_active(i)); - lp_assert(m_U.row_is_active(i)); - } return true; } template bool lu::too_dense(unsigned j) const { - unsigned r = m_dim - j; - if (r < 5) - return false; - // if (j * 5 < m_dim * 4) // start looking for dense only at the bottom of the rows - // return false; - // return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j); - return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems(); + return false; } template void lu::pivot_in_dense_mode(unsigned i) { - int j = m_dense_LU->find_pivot_column_in_row(i); - if (j == -1) { - m_failure = true; - return; - } - if (i != static_cast(j)) { - swap_columns(i, j); - m_dense_LU->swap_columns(i, j); - } - m_dense_LU->pivot(i, m_settings); + } template void lu::create_initial_factorization(){ - m_U.prepare_for_factorization(); - unsigned j; - for (j = 0; j < m_dim; j++) { - process_column(j); - if (m_failure) { - set_status(LU_status::Degenerated); - return; - } - if (too_dense(j)) { - break; - } - } - if (j == m_dim) { - // TBD does not compile: lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // lp_assert(is_correct()); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - return; - } - j++; - m_dense_LU = new square_dense_submatrix(&m_U, j); - for (; j < m_dim; j++) { - pivot_in_dense_mode(j); - if (m_failure) { - set_status(LU_status::Degenerated); - return; - } - } - m_dense_LU->update_parent_matrix(m_settings); - lp_assert(m_dense_LU->is_L_matrix()); - m_dense_LU->conjugate_by_permutation(m_Q); - push_matrix_to_tail(m_dense_LU); - m_refactor_counter = 0; - // lp_assert(is_correct()); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + } template @@ -856,123 +736,38 @@ void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest } } } -// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last -// row at the same time -template -row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { - if (replaced_column == lowest_row_of_the_bump) return nullptr; - scan_last_row_to_work_vector(lowest_row_of_the_bump); - pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump); - if (numeric_traits::precise() == false && !is_zero(pivot_elem_for_checking)) { - T denom = std::max(T(1), abs(pivot_elem_for_checking)); - if ( - !m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) { - set_status(LU_status::Degenerated); - // LP_OUT(m_settings, "diagonal element is off" << std::endl); - return nullptr; - } - } -#ifdef Z3DEBUG - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); -#else - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); -#endif - - for (auto j : m_row_eta_work_vector.m_index) { - if (j < lowest_row_of_the_bump) { - auto & v = m_row_eta_work_vector[j]; - if (!is_zero(v)) { - if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){ - ret->push_back(j, v); - } - v = numeric_traits::zero(); - } - } - } // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values - return ret; -} template void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ - m_refactor_counter++; - unsigned replaced_column = transform_U_to_V_by_replacing_column( w, leaving_column_of_U); - unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column); - m_r_wave.init(m_dim); - calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, m_r_wave); - auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem_for_checking); - - if (get_status() == LU_status::Degenerated) { - m_row_eta_work_vector.clear_all(); - return; - } - m_Q.multiply_by_permutation_from_right(m_r_wave); - m_R.multiply_by_permutation_reverse_from_left(m_r_wave); - if (row_eta != nullptr) { - row_eta->conjugate_by_permutation(m_Q); - push_matrix_to_tail(row_eta); - } - calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // lp_assert(w.is_OK() && m_row_eta_work_vector.is_OK()); + lp_assert(false); } template void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ - T diagonal_elem; - if (replaced_column < lowest_row_of_the_bump) { - diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump]; - // lp_assert(m_row_eta_work_vector.is_OK()); - m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings); - } else { - diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently - } - if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) { - set_status(LU_status::Degenerated); - return; - } - - calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + lp_assert(false);// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } template void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { - auto l = new one_elem_on_diag(lowest_row_of_the_bump, diagonal_element); -#ifdef Z3DEBUG - l->set_number_of_columns(m_dim); -#endif - push_matrix_to_tail(l); - m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings); - l->conjugate_by_permutation(m_Q); + lp_assert(false); } template void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { - if (factorization != nullptr) - delete factorization; - factorization = new lu(m_A, m_basis, m_settings); - // if (factorization->get_status() != LU_status::OK) - // LP_OUT(m_settings, "failing in init_factorization" << std::endl); + lp_assert(false); } #ifdef Z3DEBUG template dense_matrix get_B(lu& f, const vector& basis) { - lp_assert(basis.size() == f.dimension()); - lp_assert(basis.size() == f.m_U.dimension()); - dense_matrix B(f.dimension(), f.dimension()); - for (unsigned i = 0; i < f.dimension(); i++) - for (unsigned j = 0; j < f.dimension(); j++) - B.set_elem(i, j, f.B_(i, j, basis)); - + lp_assert(false); + + dense_matrix B(0, 0); return B; } template dense_matrix get_B(lu& f) { - dense_matrix B(f.dimension(), f.dimension()); - for (unsigned i = 0; i < f.dimension(); i++) - for (unsigned j = 0; j < f.dimension(); j++) - B.set_elem(i, j, f.m_A[i][j]); - + lp_assert(false); + dense_matrix B(0,0); return B; } #endif diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index ceca92b4ecc..1a2ee2338b6 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -611,142 +611,6 @@ void fill_larger_square_sparse_matrix(static_matrix & m){ int perm_id = 0; -#ifdef Z3DEBUG -void test_larger_lu_exp(lp_settings & settings) { - std::cout << " test_larger_lu_exp" << std::endl; - static_matrix m(6, 12); - vector basis(6); - basis[0] = 1; - basis[1] = 3; - basis[2] = 0; - basis[3] = 4; - basis[4] = 5; - basis[5] = 6; - - - fill_larger_square_sparse_matrix_exp(m); - // print_matrix(m); - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - lp_assert(left_side == right_side); - int leaving = 3; - int entering = 8; - for (unsigned i = 0; i < m.row_count(); i++) { - std::cout << static_cast(m(i, entering)) << std::endl; - } - - indexed_vector w(m.row_count()); - - l.prepare_entering(entering, w); - l.replace_column(0, w, heading[leaving]); - change_basis(entering, leaving, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); - - l.prepare_entering(11, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(11, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - -void test_larger_lu_with_holes(lp_settings & settings) { - std::cout << " test_larger_lu_with_holes" << std::endl; - static_matrix m(8, 9); - vector basis(8); - for (unsigned i = 0; i < m.row_count(); i++) { - basis[i] = i; - } - m(0, 0) = 1; m(0, 1) = 2; m(0, 2) = 3; m(0, 3) = 4; m(0, 4) = 5; m(0, 8) = 99; - /* */ m(1, 1) =- 6; m(1, 2) = 7; m(1, 3) = 8; m(1, 4) = 9; - /* */ m(2, 2) = 10; - /* */ m(3, 2) = 11; m(3, 3) = -12; - /* */ m(4, 2) = 13; m(4, 3) = 14; m(4, 4) = 15; - // the rest of the matrix is denser - m(5, 4) = 28; m(5, 5) = -18; m(5, 6) = 19; m(5, 7) = 25; - /* */ m(6, 5) = 20; m(6, 6) = -21; - /* */ m(7, 5) = 22; m(7, 6) = 23; m(7, 7) = 24; m(7, 8) = 88; - print_matrix(m, std::cout); - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - std::cout << "printing factorization" << std::endl; - for (int i = l.tail_size() - 1; i >=0; i--) { - auto lp = l.get_lp_matrix(i); - lp->set_number_of_columns(m.row_count()); - lp->set_number_of_rows(m.row_count()); - print_matrix( *lp, std::cout); - } - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - if (!(left_side == right_side)) { - std::cout << "different sides" << std::endl; - } - - indexed_vector w(m.row_count()); - l.prepare_entering(8, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(8, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - - -void test_larger_lu(lp_settings& settings) { - std::cout << " test_larger_lu" << std::endl; - static_matrix m(6, 12); - vector basis(6); - basis[0] = 1; - basis[1] = 3; - basis[2] = 0; - basis[3] = 4; - basis[4] = 5; - basis[5] = 6; - - - fill_larger_square_sparse_matrix(m); - print_matrix(m, std::cout); - - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - auto l = lu> (m, basis, settings); - // std::cout << "printing factorization" << std::endl; - // for (int i = lu.tail_size() - 1; i >=0; i--) { - // auto lp = lu.get_lp_matrix(i); - // lp->set_number_of_columns(m.row_count()); - // lp->set_number_of_rows(m.row_count()); - // print_matrix(* lp); - // } - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - if (!(left_side == right_side)) { - std::cout << "left side" << std::endl; - print_matrix(&left_side, std::cout); - std::cout << "right side" << std::endl; - print_matrix(&right_side, std::cout); - - std::cout << "different sides" << std::endl; - std::cout << "initial factorization is incorrect" << std::endl; - exit(1); - } - indexed_vector w(m.row_count()); - l.prepare_entering(9, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(9, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - - -void test_lu(lp_settings & settings) { - -} -#endif From 5f03c93270b35e2cf43a3063bb6accd548ecf7d4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 16:02:46 -0800 Subject: [PATCH 480/597] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lp_core_solver_base.cpp | 3 -- src/math/lp/lp_core_solver_base.h | 3 +- src/math/lp/lp_core_solver_base_def.h | 60 ------------------------- src/math/lp/lp_dual_core_solver_def.h | 28 +----------- src/math/lp/lp_primal_core_solver.h | 4 +- src/math/lp/lp_primal_core_solver_def.h | 6 --- 6 files changed, 4 insertions(+), 100 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 5dc8fb9e2c2..9f6f2534f6c 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -52,7 +52,6 @@ template void lp::lp_core_solver_base::snap_xN_to_bounds_and_fre template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::solve_yB(vector&) const; -template bool lp::lp_core_solver_base::update_basis_and_x(int, int, double const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; @@ -67,7 +66,6 @@ template void lp::lp_core_solver_base::restore_x(unsigned int, template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::solve_yB(vector&) const; -template bool lp::lp_core_solver_base::update_basis_and_x(int, int, lp::mpq const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); @@ -82,7 +80,6 @@ template bool lp::lp_core_solver_base >::prin template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); template void lp::lp_core_solver_base >::solve_Ax_eq_b(); -template bool lp::lp_core_solver_base >::update_basis_and_x(int, int, lp::numeric_pair const&); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 631f687816c..dce805b62ee 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -307,8 +307,7 @@ class lp_core_solver_base { bool find_x_by_solving(); - bool update_basis_and_x(int entering, int leaving, X const & tt); - + bool basis_has_no_doubles() const; bool non_basis_has_no_doubles() const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 0e834243050..51b24128fef 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -457,66 +457,6 @@ template bool lp_core_solver_base::inf_set_is_cor return true; } -template bool lp_core_solver_base:: -update_basis_and_x(int entering, int leaving, X const & tt) { - - if (!is_zero(tt)) { - add_delta_to_entering(entering, tt); - if ((!numeric_traits::precise()) && A_mult_x_is_off_on_index(m_ed.m_index) && !find_x_by_solving()) { - init_factorization(m_factorization, m_A, m_basis, m_settings); - if (!find_x_by_solving()) { - restore_x(entering, tt); - if(A_mult_x_is_off()) { - m_status = lp_status::FLOATING_POINT_ERROR; - m_iters_with_no_cost_growing++; - return false; - } - - init_factorization(m_factorization, m_A, m_basis, m_settings); - m_iters_with_no_cost_growing++; - if (m_factorization->get_status() != LU_status::OK) { - std::stringstream s; - // s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations(); - m_status = lp_status::FLOATING_POINT_ERROR; - return false; - } - return false; - } - } - } - - bool refactor = m_factorization->need_to_refactor(); - if (!refactor) { - const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise - m_factorization->replace_column(pivot, m_w, m_basis_heading[leaving]); - if (m_factorization->get_status() == LU_status::OK) { - change_basis(entering, leaving); - return true; - } - } - // need to refactor == true - change_basis(entering, leaving); - init_lu(); - if (m_factorization->get_status() != LU_status::OK) { - if (m_look_for_feasible_solution_only && !precise()) { - m_status = lp_status::UNSTABLE; - delete m_factorization; - m_factorization = nullptr; - return false; - } - // LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl); - restore_x_and_refactor(entering, leaving, tt); - if (m_status == lp_status::FLOATING_POINT_ERROR) - return false; - CASSERT("A_off", !A_mult_x_is_off()); - m_iters_with_no_cost_growing++; - // LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl); - m_status = lp_status::UNSTABLE; - return false; - } - return true; -} - template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h index df70e64f1bc..fd8fc80718f 100644 --- a/src/math/lp/lp_dual_core_solver_def.h +++ b/src/math/lp/lp_dual_core_solver_def.h @@ -411,33 +411,7 @@ template void lp_dual_core_solver::init_betas_pre // step 7 of the algorithm from Progress template bool lp_dual_core_solver::basis_change_and_update() { - update_betas(); - update_d_and_xB(); - // m_theta_P = m_delta / this->m_ed[m_r]; - m_theta_P = m_delta / this->m_pivot_row[m_q]; - // xb_minus_delta_p_pivot_column(); - apply_flips(); - if (!this->update_basis_and_x(m_q, m_p, m_theta_P)) { - init_betas_precisely(); - return false; - } - - if (snap_runaway_nonbasic_column(m_p)) { - if (!this->find_x_by_solving()) { - revert_to_previous_basis(); - this->iters_with_no_cost_growing()++; - return false; - } - } - - if (!problem_is_dual_feasible()) { - // todo : shift the costs!!!! - revert_to_previous_basis(); - this->iters_with_no_cost_growing()++; - return false; - } - - lp_assert(d_is_correct()); + return true; } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 5332b4001da..abee0cc6efd 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -552,9 +552,9 @@ class lp_primal_core_solver:public lp_core_solver_base { void clear_breakpoints(); void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); - void advance_on_sorted_breakpoints(unsigned entering); + - void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); + void decide_on_status_when_cannot_find_entering() { lp_assert(!need_to_switch_costs()); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 0a58b0fdf69..7d522f9331d 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -855,12 +855,6 @@ template void lp_primal_core_solver::one_iteratio } } -template void lp_primal_core_solver::update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) { - if (entering != leaving) - this->update_basis_and_x(entering, leaving, delta); - else - this->update_x(entering, delta); -} template void lp_primal_core_solver::clear_breakpoints() { From 62bd3bd1e6f7145564dc444453918b34757e09cd Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 07:35:10 -0800 Subject: [PATCH 481/597] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 241 +----------------- src/math/lp/lar_core_solver_def.h | 28 +- src/math/lp/lp_core_solver_base.cpp | 11 - src/math/lp/lp_core_solver_base.h | 13 +- src/math/lp/lp_core_solver_base_def.h | 97 +------ src/math/lp/lp_dual_core_solver.h | 2 +- src/math/lp/lp_dual_core_solver_def.h | 14 +- src/math/lp/lp_primal_core_solver_def.h | 5 +- .../lp/lp_primal_core_solver_tableau_def.h | 9 - src/math/lp/lu.h | 3 +- src/math/lp/lu_def.h | 60 +---- 11 files changed, 22 insertions(+), 461 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 9678edd6bed..999eef13bc8 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -190,16 +190,12 @@ class lar_core_solver { m_r_upper_bounds.pop(k); m_column_types.pop(k); - delete m_r_solver.m_factorization; - m_r_solver.m_factorization = nullptr; m_r_x.resize(m_r_A.column_count()); m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); m_d_A.pop(k); // doubles - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; m_d_x.resize(m_d_A.column_count()); pop_basis(k); @@ -294,172 +290,13 @@ class lar_core_solver { unsigned jb = m_r_solver.m_basis[i]; m_r_solver.add_delta_to_x_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); } - CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); + } lp_assert(m_r_solver.inf_set_is_correct()); } - template - void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver & s) { - for (auto &t : signature) { - unsigned j = t.first; - lp_assert(m_r_heading[j] < 0); - auto pos_type = t.second; - switch (pos_type) { - case at_lower_bound: - s.m_x[j] = s.m_lower_bounds[j]; - break; - case at_fixed: - case at_upper_bound: - s.m_x[j] = s.m_upper_bounds[j]; - break; - case free_of_bounds: { - s.m_x[j] = zero_of_type(); - continue; - } - case not_at_bound: - switch (m_column_types[j]) { - case column_type::free_column: - lp_assert(false); // unreachable - break; - case column_type::upper_bound: - s.m_x[j] = s.m_upper_bounds[j]; - break; - case column_type::lower_bound: - s.m_x[j] = s.m_lower_bounds[j]; - break; - case column_type::boxed: - if (settings().random_next() % 2) { - s.m_x[j] = s.m_lower_bounds[j]; - } else { - s.m_x[j] = s.m_upper_bounds[j]; - } - break; - case column_type::fixed: - s.m_x[j] = s.m_lower_bounds[j]; - break; - default: - lp_assert(false); - } - break; - default: - lp_unreachable(); - } - } - - // lp_assert(is_zero_vector(s.m_b)); - s.solve_Ax_eq_b(); - } - - template - void catch_up_in_lu_in_reverse(const vector & trace_of_basis_change, lp_primal_core_solver & cs) { - // recover the previous working basis - for (unsigned i = trace_of_basis_change.size(); i > 0; i-= 2) { - unsigned entering = trace_of_basis_change[i-1]; - unsigned leaving = trace_of_basis_change[i-2]; - cs.change_basis_unconditionally(entering, leaving); - } - cs.init_lu(); - } - - //basis_heading is the basis heading of the solver owning trace_of_basis_change - // here we compact the trace as we go to avoid unnecessary column changes - template - void catch_up_in_lu(const vector & trace_of_basis_change, const vector & basis_heading, lp_primal_core_solver & cs) { - if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) { - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - cs.change_basis_unconditionally(entering, leaving); - } - if (cs.m_factorization != nullptr) { - delete cs.m_factorization; - cs.m_factorization = nullptr; - } - } else { - indexed_vector w(cs.m_A.row_count()); - // the queues of delayed indices - std::queue entr_q, leav_q; - auto * l = cs.m_factorization; - lp_assert(l->get_status() == LU_status::OK); - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0; - bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0; - if (!good_e && !good_l) continue; - if (good_e && !good_l) { - while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0) - leav_q.pop(); - if (!leav_q.empty()) { - leaving = leav_q.front(); - leav_q.pop(); - } else { - entr_q.push(entering); - continue; - } - } else if (!good_e && good_l) { - while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0) - entr_q.pop(); - if (!entr_q.empty()) { - entering = entr_q.front(); - entr_q.pop(); - } else { - leav_q.push(leaving); - continue; - } - } - lp_assert(cs.m_basis_heading[entering] < 0); - lp_assert(cs.m_basis_heading[leaving] >= 0); - if (l->get_status() == LU_status::OK) { - l->prepare_entering(entering, w); // to init vector w - l->replace_column(zero_of_type(), w, cs.m_basis_heading[leaving]); - } - cs.change_basis_unconditionally(entering, leaving); - } - if (l->get_status() != LU_status::OK) { - delete l; - cs.m_factorization = nullptr; - } - } - if (cs.m_factorization == nullptr) { - if (numeric_traits::precise()) - init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings()); - } - } - - bool no_r_lu() const { - return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated; - } - - void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { - r_basis_is_OK(); - bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); - - if (!r) { // it is the case where m_d_solver gives a degenerated basis - prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially - m_r_solver.find_feasible_solution(); - m_d_basis = m_r_basis; - m_d_heading = m_r_heading; - m_d_nbasis = m_r_nbasis; - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; - } - else { - prepare_solver_x_with_signature_tableau(signature); - m_r_solver.start_tracing_basis_changes(); - m_r_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return; - m_r_solver.stop_tracing_basis_changes(); - // and now catch up in the double solver - lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); - catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); - } - lp_assert(r_basis_is_OK()); - } - + bool adjust_x_of_column(unsigned j) { /* if (m_r_solver.m_basis_heading[j] >= 0) { @@ -478,58 +315,6 @@ class lar_core_solver { return true; } - - bool catch_up_in_lu_tableau(const vector & trace_of_basis_change, const vector & basis_heading) { - lp_assert(r_basis_is_OK()); - // the queues of delayed indices - std::queue entr_q, leav_q; - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0; - bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0; - if (!good_e && !good_l) continue; - if (good_e && !good_l) { - while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0) - leav_q.pop(); - if (!leav_q.empty()) { - leaving = leav_q.front(); - leav_q.pop(); - } else { - entr_q.push(entering); - continue; - } - } else if (!good_e && good_l) { - while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0) - entr_q.pop(); - if (!entr_q.empty()) { - entering = entr_q.front(); - entr_q.pop(); - } else { - leav_q.push(leaving); - continue; - } - } - lp_assert(m_r_solver.m_basis_heading[entering] < 0); - lp_assert(m_r_solver.m_basis_heading[leaving] >= 0); - m_r_solver.change_basis_unconditionally(entering, leaving); - if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { - // unroll the last step - m_r_solver.change_basis_unconditionally(leaving, entering); -#ifdef Z3DEBUG - bool t = -#endif - m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); -#ifdef Z3DEBUG - lp_assert(t); -#endif - return false; - } - } - lp_assert(r_basis_is_OK()); - return true; - } - bool r_basis_is_OK() const { #ifdef Z3DEBUG @@ -598,27 +383,7 @@ class lar_core_solver { } - // returns the trace of basis changes - vector find_solution_signature_with_doubles(lar_solution_signature & signature) { - if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { - vector ret; - return ret; - } - get_bounds_for_double_solver(); - - extract_signature_from_lp_core_solver(m_r_solver, signature); - prepare_solver_x_with_signature(signature, m_d_solver); - m_d_solver.start_tracing_basis_changes(); - m_d_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return vector(); - - m_d_solver.stop_tracing_basis_changes(); - extract_signature_from_lp_core_solver(m_d_solver, signature); - return m_d_solver.m_trace_of_basis_change_vector; - } - - + bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 182029e3e98..22dc23bd512 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -78,7 +78,6 @@ void lar_core_solver::prefix_d() { } void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { - CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); @@ -127,29 +126,16 @@ void lar_core_solver::solve() { return; } ++settings().stats().m_need_to_solve_inf; - CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); lp_assert( r_basis_is_OK()); - if (need_to_presolve_with_double_solver()) { - TRACE("lar_solver", tout << "presolving\n";); - prefix_d(); - lar_solution_signature solution_signature; - vector changes_of_basis = find_solution_signature_with_doubles(solution_signature); - if (m_d_solver.get_status() == lp_status::TIME_EXHAUSTED) { - m_r_solver.set_status(lp_status::TIME_EXHAUSTED); - return; - } - solve_on_signature_tableau(solution_signature, changes_of_basis); - - lp_assert( r_basis_is_OK()); - } else { + - if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? - m_r_solver.find_feasible_solution(); - else { - m_r_solver.solve(); - } - lp_assert(r_basis_is_OK()); + if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? + m_r_solver.find_feasible_solution(); + else { + m_r_solver.solve(); } + lp_assert(r_basis_is_OK()); + switch (m_r_solver.get_status()) { case lp_status::INFEASIBLE: diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 9f6f2534f6c..508a06d730e 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,8 +23,6 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template bool lp::lp_core_solver_base::A_mult_x_is_off() const; -template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const; template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; @@ -33,7 +31,6 @@ template bool lp::lp_core_solver_base::find_x_by_solving(); template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; -template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, // vector&, vector&, @@ -51,26 +48,20 @@ template void lp::lp_core_solver_base::set_non_basic_x_to_correc template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_yB(vector&) const; template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); -template bool lp::lp_core_solver_base::A_mult_x_is_off() const; -template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::find_x_by_solving(); -template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_yB(vector&) const; template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); -template void lp::lp_core_solver_base >::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, // vector >&, vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, @@ -106,7 +97,6 @@ template std::string lp::lp_core_solver_base template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); template void lp::lp_core_solver_base >::restore_state(lp::mpq*, lp::mpq*); template void lp::lp_core_solver_base >::save_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base >::solve_yB(vector&) const; template void lp::lp_core_solver_base::init_lu(); template void lp::lp_core_solver_base::init_lu(); template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; @@ -122,7 +112,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template void lp::lp_core_solver_base >::init_lu(); -template bool lp::lp_core_solver_base >::A_mult_x_is_off_on_index(vector const&) const; template bool lp::lp_core_solver_base >::find_x_by_solving(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index dce805b62ee..edff349d23d 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -154,9 +154,6 @@ class lp_core_solver_base { void fill_cb(vector & y) const; - void solve_yB(vector & y) const; - - void pretty_print(std::ostream & out); void save_state(T * w_buffer, T * d_buffer); @@ -175,10 +172,6 @@ class lp_core_solver_base { void copy_m_ed(T * buffer); void restore_m_ed(T * buffer); - - bool A_mult_x_is_off() const; - - bool A_mult_x_is_off_on_index(const vector & index) const; void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); @@ -317,8 +310,6 @@ class lp_core_solver_base { bool basis_heading_is_correct() const; - void restore_x_and_refactor(int entering, int leaving, X const & t); - void restore_x(unsigned entering, X const & t); void fill_reduced_costs_from_m_y_by_rows(); @@ -435,9 +426,7 @@ class lp_core_solver_base { void snap_xN_to_bounds_and_fill_xB(); void snap_xN_to_bounds_and_free_columns_to_zeroes(); - - void init_reduced_costs_for_one_iteration(); - + non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; void init_lu(); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 51b24128fef..ae04bd5eec6 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -116,11 +116,6 @@ fill_cb(vector & y) const { } } -template void lp_core_solver_base:: -solve_yB(vector & y) const { - fill_cb(y); // now y = cB, that is the projection of costs to basis - m_factorization->solve_yB_with_error_check(y, m_basis); -} // template void lp_core_solver_base:: // update_index_of_ed() { @@ -188,71 +183,6 @@ restore_m_ed(T * buffer) { } } -template bool lp_core_solver_base:: -A_mult_x_is_off() const { - lp_assert(m_x.size() == m_A.column_count()); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_m(); i++) { - X delta = /*m_b[i] */- m_A.dot_product_with_row(i, m_x); - if (delta != numeric_traits::zero()) { - return true; - } - } - return false; - } - T feps = convert_struct::convert(m_settings.refactor_tolerance); - X one = convert_struct::convert(1.0); - for (unsigned i = 0; i < m_m(); i++) { - X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); - auto eps = feps /* * (one + T(0.1) * abs(m_b[i])) */; - - if (delta > eps) { -#if 0 - LP_OUT(m_settings, "x is off (" - << "m_b[" << i << "] = " << m_b[i] << " " - << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' - << "delta = " << delta << ' ' - << "iters = " << total_iterations() << ")" << std::endl); -#endif - return true; - } - } - return false; -} -template bool lp_core_solver_base:: -A_mult_x_is_off_on_index(const vector & index) const { - lp_assert(m_x.size() == m_A.column_count()); - if (numeric_traits::precise()) return false; -#if RUN_A_MULT_X_IS_OFF_FOR_PRECESE - for (unsigned i : index) { - X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); - if (delta != numeric_traits::zero()) { - return true; - } - } - return false; -#endif - // todo(levnach) run on m_ed.m_index only !!!!! - T feps = convert_struct::convert(m_settings.refactor_tolerance); - X one = convert_struct::convert(1.0); - for (unsigned i : index) { - X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); - auto eps = feps /* *(one + T(0.1) * abs(m_b[i])) */; - - if (delta > eps) { -#if 0 - LP_OUT(m_settings, "x is off (" - << "m_b[" << i << "] = " << m_b[i] << " " - << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' - << "delta = " << delta << ' ' - << "iters = " << total_iterations() << ")" << std::endl); -#endif - return true; - } - } - return false; -} - template void lp_core_solver_base:: @@ -292,7 +222,7 @@ print_statistics(char const* str, X cost, std::ostream & out) { if (str!= nullptr) out << str << " "; out << "iterations = " << (total_iterations() - 1) << ", cost = " << T_to_string(cost) - << ", nonzeros = " << (m_factorization != nullptr? m_factorization->get_number_of_nonzeroes() : m_A.number_of_non_zeroes()) << std::endl; + << ", nonzeros = " << m_A.number_of_non_zeroes() << std::endl; } template bool lp_core_solver_base:: @@ -411,7 +341,7 @@ rs_minus_Anx(vector & rs) { template bool lp_core_solver_base:: find_x_by_solving() { solve_Ax_eq_b(); - return !A_mult_x_is_off(); + return true; } template bool lp_core_solver_base::column_is_feasible(unsigned j) const { @@ -591,24 +521,6 @@ template bool lp_core_solver_base:: return true; } -template void lp_core_solver_base:: -restore_x_and_refactor(int entering, int leaving, X const & t) { - this->restore_basis_change(entering, leaving); - restore_x(entering, t); - init_factorization(m_factorization, m_A, m_basis, m_settings); - if (m_factorization->get_status() == LU_status::Degenerated) { - LP_OUT(m_settings, "cannot refactor" << std::endl); - m_status = lp_status::FLOATING_POINT_ERROR; - return; - } - // solve_Ax_eq_b(); - if (A_mult_x_is_off()) { - LP_OUT(m_settings, "cannot restore solution" << std::endl); - m_status = lp_status::FLOATING_POINT_ERROR; - return; - } -} - template void lp_core_solver_base:: restore_x(unsigned entering, X const & t) { if (is_zero(t)) return; @@ -731,11 +643,6 @@ snap_xN_to_bounds_and_free_columns_to_zeroes() { solve_Ax_eq_b(); } -template void lp_core_solver_base:: -init_reduced_costs_for_one_iteration() { - solve_yB(m_y); - fill_reduced_costs_from_m_y_by_rows(); -} template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { diff --git a/src/math/lp/lp_dual_core_solver.h b/src/math/lp/lp_dual_core_solver.h index 804879c3da2..b78a2214701 100644 --- a/src/math/lp/lp_dual_core_solver.h +++ b/src/math/lp/lp_dual_core_solver.h @@ -76,7 +76,7 @@ class lp_dual_core_solver:public lp_core_solver_base { m_a_wave(this->m_m()), m_betas(this->m_m()) { m_harris_tolerance = numeric_traits::precise()? numeric_traits::zero() : T(this->m_settings.harris_feasibility_tolerance); - this->solve_yB(this->m_y); + lp_assert(false); this->init_basic_part_of_basis_heading(); fill_non_basis_with_only_able_to_enter_columns(); } diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h index fd8fc80718f..9c39fe195bf 100644 --- a/src/math/lp/lp_dual_core_solver_def.h +++ b/src/math/lp/lp_dual_core_solver_def.h @@ -74,8 +74,7 @@ template void lp_dual_core_solver::recalculate_xB } template void lp_dual_core_solver::recalculate_d() { - this->solve_yB(this->m_y); - this->fill_reduced_costs_from_m_y_by_rows(); +lp_assert(false) } template void lp_dual_core_solver::init_betas() { @@ -316,15 +315,8 @@ template void lp_dual_core_solver::restore_d() { } template bool lp_dual_core_solver::d_is_correct() { - this->solve_yB(this->m_y); - for (auto j : this->non_basis()) { - T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j); - if (numeric_traits::get_double(abs(d - this->m_d[j])) >= 0.001) { - LP_OUT(this->m_settings, "total_iterations = " << this->total_iterations() << std::endl - << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl); - return false; - } - } + + lp_assert(false); return true; } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7d522f9331d..19ffaa1eadc 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -705,10 +705,7 @@ template unsigned lp_primal_core_solver::solve() return 0; } - if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return 0; - } + do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf" : "feas"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index fa25694adc7..736b79db5ad 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -107,10 +107,6 @@ unsigned lp_primal_core_solver::solve_with_tableau() { return 0; } - if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return 0; - } do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); @@ -183,7 +179,6 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } template void lp_primal_core_solver::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { - CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(leaving >= 0 && entering >= 0); lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || @@ -200,7 +195,6 @@ template void lp_primal_core_solver::advance_on_en t = -t; } this->update_basis_and_x_tableau(entering, leaving, t); - CASSERT("A_off", this->A_mult_x_is_off() == false); this->iters_with_no_cost_growing() = 0; } else { this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); @@ -224,7 +218,6 @@ template void lp_primal_core_solver::advance_on_en template void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int entering, X & t) { - CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x_tableau(entering, t * m_sign_of_entering_delta); if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; @@ -296,7 +289,6 @@ template int lp_primal_core_solver::find_leaving_ return m_leaving_candidates[k]; } template void lp_primal_core_solver::init_run_tableau() { - CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(basis_columns_are_set_correctly()); this->m_basis_sort_counter = 0; // to initiate the sort of the basis // this->set_total_iterations(0); @@ -350,7 +342,6 @@ update_x_tableau(unsigned entering, const X& delta) { this->insert_column_into_inf_set(j); } } - CASSERT("A_off", this->A_mult_x_is_off() == false); } template void lp_primal_core_solver:: diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index b3e2f0c2221..18251e3ada5 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -173,8 +173,7 @@ class lu { void print(indexed_vector & w, const vector& basis); void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit - void solve_yB(vector& y); - + void solve_yB_indexed(indexed_vector& y); void add_delta_to_solution_indexed(indexed_vector& y); diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h index d7c0c0c3a07..ca34481cca8 100644 --- a/src/math/lp/lu_def.h +++ b/src/math/lp/lu_def.h @@ -263,20 +263,6 @@ void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts solve_By_for_T_indexed_only(d, m_settings); } -template -void lu< M>::solve_yB(vector& y) { - // first solve yU = cb*R(-1) - m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - m_U.solve_y_U(y); // got y*U=cb*R(-1) - m_Q.apply_reverse_from_right_to_T(y); // - for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { -#ifdef Z3DEBUG - (*e)->set_number_of_columns(m_dim); -#endif - (*e)->apply_from_right(y); - } -} - template void lu< M>::solve_yB_indexed(indexed_vector& y) { lp_assert(y.is_OK()); @@ -412,55 +398,15 @@ void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { - if (numeric_traits::precise()) { - if (y.m_index.size() * ratio_of_index_size_to_all_size() * 3 < m_A.column_count()) { - solve_yB_indexed(y); - } else { - solve_yB(y.m_data); - y.restore_index_and_clean_from_data(); - } - return; - } - lp_assert(m_y_copy.is_OK()); - lp_assert(y.is_OK()); - if (y.m_index.size() * ratio_of_index_size_to_all_size() < m_A.column_count()) { - m_y_copy = y; - solve_yB_indexed(y); - lp_assert(y.is_OK()); - if (y.m_index.size() * ratio_of_index_size_to_all_size() >= m_A.column_count()) { - find_error_of_yB(m_y_copy.m_data, y.m_data, basis); - solve_yB(m_y_copy.m_data); - add_delta_to_solution(m_y_copy.m_data, y.m_data); - y.restore_index_and_clean_from_data(); - m_y_copy.clear_all(); - } else { - find_error_of_yB_indexed(y, heading, settings); // this works with m_y_copy - solve_yB_indexed(m_y_copy); - add_delta_to_solution_indexed(y); - } - lp_assert(m_y_copy.is_OK()); - } else { - solve_yB_with_error_check(y.m_data, basis); - y.restore_index_and_clean_from_data(); - } -} + lp_assert(false); + } // solves y*B = y // y is the input template void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { - if (numeric_traits::precise()) { - solve_yB(y); - return; - } - auto & yc = m_y_copy.m_data; - yc =y; // copy y aside - solve_yB(y); - find_error_of_yB(yc, y, basis); - solve_yB(yc); - add_delta_to_solution(yc, y); - m_y_copy.clear_all(); + lp_assert(false); } template void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { From 1da4c018e458919f012f83057026f4f8913aae4f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 07:44:22 -0800 Subject: [PATCH 482/597] rm lu --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 3 - src/math/lp/lp_core_solver_base_def.h | 6 - src/math/lp/lp_dual_core_solver.cpp | 44 -- src/math/lp/lp_dual_core_solver.h | 212 -------- src/math/lp/lp_dual_core_solver_def.h | 699 -------------------------- src/math/lp/static_matrix.cpp | 1 - 8 files changed, 969 deletions(-) delete mode 100644 src/math/lp/lp_dual_core_solver.cpp delete mode 100644 src/math/lp/lp_dual_core_solver.h delete mode 100644 src/math/lp/lp_dual_core_solver_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5719de44fae..cc58c9610fc 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -19,7 +19,6 @@ z3_add_component(lp lar_solver.cpp lar_core_solver.cpp lp_core_solver_base.cpp - lp_dual_core_solver.cpp lp_primal_core_solver.cpp lp_settings.cpp lu.cpp diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 508a06d730e..0d9f4297419 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -27,7 +27,6 @@ template bool lp::lp_core_solver_base::basis_heading_is_correct( template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template bool lp::lp_core_solver_base::find_x_by_solving(); template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; @@ -53,7 +52,6 @@ template bool lp::lp_core_solver_base::basis_heading_is_correc template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template bool lp::lp_core_solver_base::find_x_by_solving(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); @@ -112,7 +110,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template void lp::lp_core_solver_base >::init_lu(); -template bool lp::lp_core_solver_base >::find_x_by_solving(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index edff349d23d..729792c1266 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -298,9 +298,6 @@ class lp_core_solver_base { void rs_minus_Anx(vector & rs); - bool find_x_by_solving(); - - bool basis_has_no_doubles() const; bool non_basis_has_no_doubles() const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index ae04bd5eec6..e470072e04c 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -338,12 +338,6 @@ rs_minus_Anx(vector & rs) { } } -template bool lp_core_solver_base:: -find_x_by_solving() { - solve_Ax_eq_b(); - return true; -} - template bool lp_core_solver_base::column_is_feasible(unsigned j) const { const X& x = this->m_x[j]; switch (this->m_column_types[j]) { diff --git a/src/math/lp/lp_dual_core_solver.cpp b/src/math/lp/lp_dual_core_solver.cpp deleted file mode 100644 index 8cf45f7c6cd..00000000000 --- a/src/math/lp/lp_dual_core_solver.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_dual_core_solver_def.h" -template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); -template void lp::lp_dual_core_solver::solve(); -template lp::lp_dual_core_solver::lp_dual_core_solver(lp::static_matrix&, vector&, - vector&, - vector&, - vector&, - vector &, - vector &, - vector&, - vector&, - vector&, - vector&, - lp::lp_settings&, const lp::column_namer&); -template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); -template void lp::lp_dual_core_solver::solve(); -template void lp::lp_dual_core_solver::restore_non_basis(); -template void lp::lp_dual_core_solver::restore_non_basis(); -template void lp::lp_dual_core_solver::revert_to_previous_basis(); -template void lp::lp_dual_core_solver::revert_to_previous_basis(); diff --git a/src/math/lp/lp_dual_core_solver.h b/src/math/lp/lp_dual_core_solver.h deleted file mode 100644 index b78a2214701..00000000000 --- a/src/math/lp/lp_dual_core_solver.h +++ /dev/null @@ -1,212 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include -#include -#include -#include -#include "util/vector.h" - -namespace lp { -template -class lp_dual_core_solver:public lp_core_solver_base { -public: - vector & m_can_enter_basis; - int m_r; // the row of the leaving column - int m_p; // leaving column; that is m_p = m_basis[m_r] - T m_delta; // the offset of the leaving basis variable - int m_sign_of_alpha_r; // see page 27 - T m_theta_D; - T m_theta_P; - int m_q; - // todo : replace by a vector later - std::set m_breakpoint_set; // it is F in "Progress in the dual simplex method ..." - std::set m_flipped_boxed; - std::set m_tight_set; // it is the set of all breakpoints that become tight when m_q becomes tight - vector m_a_wave; - vector m_betas; // m_betas[i] is approximately a square of the norm of the i-th row of the reverse of B - T m_harris_tolerance; - std::set m_forbidden_rows; - - lp_dual_core_solver(static_matrix & A, - vector & can_enter_basis, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - vector & column_type_array, - vector & lower_bound_values, - vector & upper_bound_values, - lp_settings & settings, - const column_namer & column_names): - lp_core_solver_base(A, - // b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - lower_bound_values, - upper_bound_values), - m_can_enter_basis(can_enter_basis), - m_a_wave(this->m_m()), - m_betas(this->m_m()) { - m_harris_tolerance = numeric_traits::precise()? numeric_traits::zero() : T(this->m_settings.harris_feasibility_tolerance); - lp_assert(false); - this->init_basic_part_of_basis_heading(); - fill_non_basis_with_only_able_to_enter_columns(); - } - - void init_a_wave_by_zeros(); - - void fill_non_basis_with_only_able_to_enter_columns() { - auto & nb = this->m_nbasis; - nb.reset(); - unsigned j = this->m_n(); - while (j--) { - if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue; - nb.push_back(j); - this->m_basis_heading[j] = - static_cast(nb.size()); - } - } - - void restore_non_basis(); - - bool update_basis(int entering, int leaving); - - void recalculate_xB_and_d(); - - void recalculate_d(); - - void init_betas(); - - void adjust_xb_for_changed_xn_and_init_betas(); - - void start_with_initial_basis_and_make_it_dual_feasible(); - - bool done(); - - T get_edge_steepness_for_lower_bound(unsigned p); - - T get_edge_steepness_for_upper_bound(unsigned p); - - T pricing_for_row(unsigned i); - - void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows); - - bool advance_on_known_p(); - - int define_sign_of_alpha_r(); - - bool can_be_breakpoint(unsigned j); - - void fill_breakpoint_set(); - - void DSE_FTran(); - T get_delta(); - - void restore_d(); - - bool d_is_correct(); - - void xb_minus_delta_p_pivot_column(); - - void update_betas(); - - void apply_flips(); - - void snap_xN_column_to_bounds(unsigned j); - - void snap_xN_to_bounds(); - - void init_beta_precisely(unsigned i); - - void init_betas_precisely(); - - // step 7 of the algorithm from Progress - bool basis_change_and_update(); - - void revert_to_previous_basis(); - - non_basic_column_value_position m_entering_boundary_position; - bool update_basis_and_x_local(int entering, int leaving, X const & tt); - void recover_leaving(); - - bool problem_is_dual_feasible() const; - - bool snap_runaway_nonbasic_column(unsigned); - - bool snap_runaway_nonbasic_columns(); - - unsigned get_number_of_rows_to_try_for_leaving(); - - void update_a_wave(const T & del, unsigned j) { - this->m_A.add_column_to_vector(del, j, & m_a_wave[0]); - } - - bool delta_keeps_the_sign(int initial_delta_sign, const T & delta); - - void set_status_to_tentative_dual_unbounded_or_dual_unbounded(); - - // it is positive if going from low bound to upper bound and negative if going from upper bound to low bound - T signed_span_of_boxed(unsigned j) { - return this->x_is_at_lower_bound(j)? this->bound_span(j): - this->bound_span(j); - } - - void add_tight_breakpoints_and_q_to_flipped_set(); - - T delta_lost_on_flips_of_tight_breakpoints(); - - bool tight_breakpoinst_are_all_boxed(); - - T calculate_harris_delta_on_breakpoint_set(); - - void fill_tight_set_on_harris_delta(const T & harris_delta ); - - void find_q_on_tight_set(); - - void find_q_and_tight_set(); - - void erase_tight_breakpoints_and_q_from_breakpoint_set(); - - bool ratio_test(); - - void process_flipped(); - void update_d_and_xB(); - - void calculate_beta_r_precisely(); - // see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" - - void update_xb_after_bound_flips(); - - void one_iteration(); - - void solve(); - - bool lower_bounds_are_set() const override { return true; } -}; -} diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h deleted file mode 100644 index 9c39fe195bf..00000000000 --- a/src/math/lp/lp_dual_core_solver_def.h +++ /dev/null @@ -1,699 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_dual_core_solver.h" - -namespace lp { - -template void lp_dual_core_solver::init_a_wave_by_zeros() { - unsigned j = this->m_m(); - while (j--) { - m_a_wave[j] = numeric_traits::zero(); - } -} - -template void lp_dual_core_solver::restore_non_basis() { - auto & nb = this->m_nbasis; - nb.reset(); - unsigned j = this->m_n(); - while (j--) { - if (this->m_basis_heading[j] >= 0 ) continue; - if (m_can_enter_basis[j]) { - lp_assert(std::find(nb.begin(), nb.end(), j) == nb.end()); - nb.push_back(j); - this->m_basis_heading[j] = - static_cast(nb.size()); - } - } -} - -template bool lp_dual_core_solver::update_basis(int entering, int leaving) { - // the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done - if (this->m_refactor_counter++ < 200) { - this->m_factorization->replace_column(this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w); - if (this->m_factorization->get_status() == LU_status::OK) { - this->m_factorization->change_basis(entering, leaving); - return true; - } - } - // need to refactor - this->m_factorization->change_basis(entering, leaving); - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings); - this->m_refactor_counter = 0; - if (this->m_factorization->get_status() != LU_status::OK) { - LP_OUT(this->m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->total_iterations() << std::endl); - this->m_iters_with_no_cost_growing++; - return false; - } - return true; -} - -template void lp_dual_core_solver::recalculate_xB_and_d() { - this->solve_Ax_eq_b(); - recalculate_d(); -} - -template void lp_dual_core_solver::recalculate_d() { -lp_assert(false) -} - -template void lp_dual_core_solver::init_betas() { - // todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation - // the current implementation is not good enough: todo - unsigned i = this->m_m(); - while (i--) { - m_betas[i] = 1; - } -} - -template void lp_dual_core_solver::adjust_xb_for_changed_xn_and_init_betas() { - this->solve_Ax_eq_b(); - init_betas(); -} - -template void lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible() { - this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29, - // however this version does not require that m_x is the solution of Ax = 0 beforehand - adjust_xb_for_changed_xn_and_init_betas(); -} - -template bool lp_dual_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL) { - return true; - } - - return false; // todo, need to be more cases -} - -template T lp_dual_core_solver::get_edge_steepness_for_lower_bound(unsigned p) { - lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_lower_bounds[p]; - del *= del; - return del / this->m_betas[this->m_basis_heading[p]]; -} - -template T lp_dual_core_solver::get_edge_steepness_for_upper_bound(unsigned p) { - lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_upper_bounds[p]; - del *= del; - return del / this->m_betas[this->m_basis_heading[p]]; -} - -template T lp_dual_core_solver::pricing_for_row(unsigned i) { - unsigned p = this->m_basis[i]; - switch (this->m_column_types[p]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_lower_bound(p); - return del; - } - if (this->x_above_upper_bound(p)) { - T del = get_edge_steepness_for_upper_bound(p); - return del; - } - return numeric_traits::zero(); - case column_type::lower_bound: - if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_lower_bound(p); - return del; - } - return numeric_traits::zero(); - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(p)) { - T del = get_edge_steepness_for_upper_bound(p); - return del; - } - return numeric_traits::zero(); - break; - case column_type::free_column: - lp_assert(numeric_traits::is_zero(this->m_d[p])); - return numeric_traits::zero(); - default: - lp_unreachable(); - } - lp_unreachable(); - return numeric_traits::zero(); -} - -template void lp_dual_core_solver::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) { - m_r = -1; - T steepest_edge_max = numeric_traits::zero(); - unsigned initial_offset_in_rows = offset_in_rows; - unsigned i = offset_in_rows; - unsigned rows_left = number_of_rows_to_try; - do { - if (m_forbidden_rows.find(i) != m_forbidden_rows.end()) { - if (++i == this->m_m()) { - i = 0; - } - continue; - } - T se = pricing_for_row(i); - if (se > steepest_edge_max) { - steepest_edge_max = se; - m_r = i; - if (rows_left > 0) { - rows_left--; - } - } - if (++i == this->m_m()) { - i = 0; - } - } while (i != initial_offset_in_rows && rows_left); - if (m_r == -1) { - if (this->get_status() != lp_status::UNSTABLE) { - this->set_status(lp_status::OPTIMAL); - } - } else { - m_p = this->m_basis[m_r]; - m_delta = get_delta(); - if (advance_on_known_p()){ - m_forbidden_rows.clear(); - return; - } - // failure in advance_on_known_p - if (this->get_status() == lp_status::FLOATING_POINT_ERROR) { - return; - } - this->set_status(lp_status::UNSTABLE); - m_forbidden_rows.insert(m_r); - } -} - - // this calculation is needed for the steepest edge update, - // it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle -template void lp_dual_core_solver::DSE_FTran() { // todo, see algorithm 7 from page 35 - this->m_factorization->solve_By_for_T_indexed_only(this->m_pivot_row_of_B_1, this->m_settings); -} - -template bool lp_dual_core_solver::advance_on_known_p() { - - return false; -} - -template int lp_dual_core_solver::define_sign_of_alpha_r() { - switch (this->m_column_types[m_p]) { - case column_type::boxed: - case column_type::fixed: - if (this->x_below_low_bound(m_p)) { - return -1; - } - if (this->x_above_upper_bound(m_p)) { - return 1; - } - lp_unreachable(); - case column_type::lower_bound: - if (this->x_below_low_bound(m_p)) { - return -1; - } - lp_unreachable(); - case column_type::upper_bound: - if (this->x_above_upper_bound(m_p)) { - return 1; - } - lp_unreachable(); - default: - lp_unreachable(); - } - lp_unreachable(); - return 0; -} - -template bool lp_dual_core_solver::can_be_breakpoint(unsigned j) { - if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; - switch (this->m_column_types[j]) { - case column_type::lower_bound: - lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_lower_bounds[j])); - return m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - case column_type::upper_bound: - lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); - return m_sign_of_alpha_r * this->m_pivot_row[j] < 0; - case column_type::boxed: - { - bool lower_bound = this->x_is_at_lower_bound(j); - bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - return lower_bound == grawing; - } - case column_type::fixed: // is always dual feasible so we ignore it - return false; - case column_type::free_column: - return true; - default: - return false; - } -} - -template void lp_dual_core_solver::fill_breakpoint_set() { - m_breakpoint_set.clear(); - for (unsigned j : this->non_basis()) { - if (can_be_breakpoint(j)) { - m_breakpoint_set.insert(j); - } - } -} - -// template void lp_dual_core_solver::FTran() { -// this->solve_Bd(m_q); -// } - -template T lp_dual_core_solver::get_delta() { - switch (this->m_column_types[m_p]) { - case column_type::boxed: - if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_lower_bounds[m_p]; - } - if (this->x_above_upper_bound(m_p)) { - return this->m_x[m_p] - this->m_upper_bounds[m_p]; - } - lp_unreachable(); - case column_type::lower_bound: - if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_lower_bounds[m_p]; - } - lp_unreachable(); - case column_type::upper_bound: - if (this->x_above_upper_bound(m_p)) { - return get_edge_steepness_for_upper_bound(m_p); - } - lp_unreachable(); - case column_type::fixed: - return this->m_x[m_p] - this->m_upper_bounds[m_p]; - default: - lp_unreachable(); - } - lp_unreachable(); - return zero_of_type(); -} - -template void lp_dual_core_solver::restore_d() { - this->m_d[m_p] = numeric_traits::zero(); - for (auto j : this->non_basis()) { - this->m_d[j] += m_theta_D * this->m_pivot_row[j]; - } -} - -template bool lp_dual_core_solver::d_is_correct() { - - lp_assert(false); - return true; -} - -template void lp_dual_core_solver::xb_minus_delta_p_pivot_column() { - unsigned i = this->m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i]; - } -} - -template void lp_dual_core_solver::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized - T one_over_arq = numeric_traits::one() / this->m_pivot_row[m_q]; - T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq); - T k = -2 * one_over_arq; - unsigned i = this->m_m(); - while (i--) { - if (static_cast(i) == m_r) continue; - T a = this->m_ed[i]; - m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]); - if (m_betas[i] < T(0.0001)) - m_betas[i] = T(0.0001); - } -} - -template void lp_dual_core_solver::apply_flips() { - for (unsigned j : m_flipped_boxed) { - lp_assert(this->x_is_at_bound(j)); - if (this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_upper_bounds[j]; - } else { - this->m_x[j] = this->m_lower_bounds[j]; - } - } -} - -template void lp_dual_core_solver::snap_xN_column_to_bounds(unsigned j) { - switch (this->m_column_type[j]) { - case column_type::fixed: - this->m_x[j] = this->m_lower_bounds[j]; - break; - case column_type::boxed: - if (this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - } else { - this->m_x[j] = this->m_upper_bounds[j]; - } - break; - case column_type::lower_bound: - this->m_x[j] = this->m_lower_bounds[j]; - break; - case column_type::upper_bound: - this->m_x[j] = this->m_upper_bounds[j]; - break; - case column_type::free_column: - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_core_solver::snap_xN_to_bounds() { - for (auto j : this->non_basis()) { - snap_xN_column_to_bounds(j); - } -} - -template void lp_dual_core_solver::init_beta_precisely(unsigned i) { - vector vec(this->m_m(), numeric_traits::zero()); - vec[i] = numeric_traits::one(); - this->m_factorization->solve_yB_with_error_check(vec, this->m_basis); - T beta = numeric_traits::zero(); - for (T & v : vec) { - beta += v * v; - } - this->m_betas[i] =beta; -} - -template void lp_dual_core_solver::init_betas_precisely() { - unsigned i = this->m_m(); - while (i--) { - init_beta_precisely(i); - } -} - -// step 7 of the algorithm from Progress -template bool lp_dual_core_solver::basis_change_and_update() { - - return true; -} - -template void lp_dual_core_solver::recover_leaving() { - switch (m_entering_boundary_position) { - case at_lower_bound: - case at_fixed: - this->m_x[m_q] = this->m_lower_bounds[m_q]; - break; - case at_upper_bound: - this->m_x[m_q] = this->m_upper_bounds[m_q]; - break; - case free_of_bounds: - this->m_x[m_q] = zero_of_type(); - default: - lp_unreachable(); - } -} - -template void lp_dual_core_solver::revert_to_previous_basis() { - LP_OUT(this->m_settings, "revert to previous basis on ( " << m_p << ", " << m_q << ")" << std::endl); - this->change_basis_unconditionally(m_p, m_q); - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); // complete failure - return; - } - recover_leaving(); - if (!this->find_x_by_solving()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - recalculate_xB_and_d(); - init_betas_precisely(); -} - -// returns true if the column has been snapped -template bool lp_dual_core_solver::snap_runaway_nonbasic_column(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::lower_bound: - if (!this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - break; - case column_type::boxed: - { - bool closer_to_lower_bound = abs(this->m_lower_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); - if (closer_to_lower_bound) { - if (!this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - } else { - if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - } - } - break; - case column_type::upper_bound: - if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_upper_bounds[j]; - return true; - } - break; - default: - break; - } - return false; -} - - -template bool lp_dual_core_solver::problem_is_dual_feasible() const { - for (unsigned j : this->non_basis()){ - if (!this->column_is_dual_feasible(j)) { - return false; - } - } - return true; -} - -template unsigned lp_dual_core_solver::get_number_of_rows_to_try_for_leaving() { - unsigned s = this->m_m(); - if (this->m_m() > 300) { - s = (unsigned)((s / 100.0) * this->m_settings.percent_of_entering_to_check); - } - return this->m_settings.random_next() % s + 1; -} - -template bool lp_dual_core_solver::delta_keeps_the_sign(int initial_delta_sign, const T & delta) { - if (numeric_traits::precise()) - return ((delta > numeric_traits::zero()) && (initial_delta_sign == 1)) || - ((delta < numeric_traits::zero()) && (initial_delta_sign == -1)); - - double del = numeric_traits::get_double(delta); - return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) || - ((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1)); -} - -template void lp_dual_core_solver::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { - if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { - this->set_status(lp_status::DUAL_UNBOUNDED); - } else { - this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED); - } -} - -template void lp_dual_core_solver::add_tight_breakpoints_and_q_to_flipped_set() { - m_flipped_boxed.insert(m_q); - for (auto j : m_tight_set) { - m_flipped_boxed.insert(j); - } -} - -template T lp_dual_core_solver::delta_lost_on_flips_of_tight_breakpoints() { - T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]); - for (auto j : m_tight_set) { - ret += abs(this->bound_span(j) * this->m_pivot_row[j]); - } - return ret; -} - -template bool lp_dual_core_solver::tight_breakpoinst_are_all_boxed() { - if (this->m_column_types[m_q] != column_type::boxed) return false; - for (auto j : m_tight_set) { - if (this->m_column_types[j] != column_type::boxed) return false; - } - return true; -} - -template T lp_dual_core_solver::calculate_harris_delta_on_breakpoint_set() { - bool first_time = true; - T ret = zero_of_type(); - lp_assert(m_breakpoint_set.size() > 0); - for (auto j : m_breakpoint_set) { - T t; - if (this->x_is_at_lower_bound(j)) { - t = abs((std::max(this->m_d[j], numeric_traits::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); - } else { - t = abs((std::min(this->m_d[j], numeric_traits::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); - } - if (first_time) { - ret = t; - first_time = false; - } else if (t < ret) { - ret = t; - } - } - return ret; -} - -template void lp_dual_core_solver::fill_tight_set_on_harris_delta(const T & harris_delta ){ - m_tight_set.clear(); - for (auto j : m_breakpoint_set) { - if (this->x_is_at_lower_bound(j)) { - if (abs(std::max(this->m_d[j], numeric_traits::zero()) / this->m_pivot_row[j]) <= harris_delta){ - m_tight_set.insert(j); - } - } else { - if (abs(std::min(this->m_d[j], numeric_traits::zero() ) / this->m_pivot_row[j]) <= harris_delta){ - m_tight_set.insert(j); - } - } - } -} - -template void lp_dual_core_solver::find_q_on_tight_set() { - m_q = -1; - T max_pivot; - for (auto j : m_tight_set) { - T r = abs(this->m_pivot_row[j]); - if (m_q != -1) { - if (r > max_pivot) { - max_pivot = r; - m_q = j; - } - } else { - max_pivot = r; - m_q = j; - } - } - m_tight_set.erase(m_q); - lp_assert(m_q != -1); -} - -template void lp_dual_core_solver::find_q_and_tight_set() { - T harris_del = calculate_harris_delta_on_breakpoint_set(); - fill_tight_set_on_harris_delta(harris_del); - find_q_on_tight_set(); - m_entering_boundary_position = this->get_non_basic_column_value_position(m_q); -} - -template void lp_dual_core_solver::erase_tight_breakpoints_and_q_from_breakpoint_set() { - m_breakpoint_set.erase(m_q); - for (auto j : m_tight_set) { - m_breakpoint_set.erase(j); - } -} - -template bool lp_dual_core_solver::ratio_test() { - m_sign_of_alpha_r = define_sign_of_alpha_r(); - fill_breakpoint_set(); - m_flipped_boxed.clear(); - int initial_delta_sign = m_delta >= numeric_traits::zero()? 1: -1; - do { - if (m_breakpoint_set.empty()) { - set_status_to_tentative_dual_unbounded_or_dual_unbounded(); - return false; - } - this->set_status(lp_status::FEASIBLE); - find_q_and_tight_set(); - if (!tight_breakpoinst_are_all_boxed()) break; - T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; - if (!delta_keeps_the_sign(initial_delta_sign, del)) break; - if (m_tight_set.size() + 1 == m_breakpoint_set.size()) { - break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis - } - // we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set - add_tight_breakpoints_and_q_to_flipped_set(); - m_delta = del; - erase_tight_breakpoints_and_q_from_breakpoint_set(); - } while (true); - m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q]; - return true; -} - -template void lp_dual_core_solver::process_flipped() { - init_a_wave_by_zeros(); - for (auto j : m_flipped_boxed) { - update_a_wave(signed_span_of_boxed(j), j); - } -} -template void lp_dual_core_solver::update_d_and_xB() { - for (auto j : this->non_basis()) { - this->m_d[j] -= m_theta_D * this->m_pivot_row[j]; - } - this->m_d[m_p] = - m_theta_D; - if (!m_flipped_boxed.empty()) { - process_flipped(); - update_xb_after_bound_flips(); - } -} - -template void lp_dual_core_solver::calculate_beta_r_precisely() { - T t = numeric_traits::zero(); - unsigned i = this->m_m(); - while (i--) { - T b = this->m_pivot_row_of_B_1[i]; - t += b * b; - } - m_betas[m_r] = t; -} -// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" - -template void lp_dual_core_solver::update_xb_after_bound_flips() { - this->m_factorization->solve_By(m_a_wave); - unsigned i = this->m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= m_a_wave[i]; - } -} - -template void lp_dual_core_solver::one_iteration() { - unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); - unsigned offset_in_rows = this->m_settings.random_next() % this->m_m(); - if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { - number_of_rows_to_try = this->m_m(); - } else { - this->set_status(lp_status::FEASIBLE); - } - pricing_loop(number_of_rows_to_try, offset_in_rows); - lp_assert(problem_is_dual_feasible()); -} - -template void lp_dual_core_solver::solve() { // see the page 35 - lp_assert(d_is_correct()); - lp_assert(problem_is_dual_feasible()); - lp_assert(this->basis_heading_is_correct()); - //this->set_total_iterations(0); - this->iters_with_no_cost_growing() = 0; - do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over("", *this->m_settings.get_message_ostream())){ - return; - } - one_iteration(); - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL && - this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements - ); -} -} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index cde96277b5e..93360b9140f 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -23,7 +23,6 @@ Revision History: #include #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/lp_dual_core_solver.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" From bfe73c01a6e3fbc309213832c051eeb093391b9e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 07:56:04 -0800 Subject: [PATCH 483/597] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 41 --------------------------- src/math/lp/lar_solver.cpp | 5 ---- src/math/lp/lar_solver.h | 1 - src/math/lp/lp_core_solver_base.cpp | 14 --------- src/math/lp/lp_core_solver_base.h | 10 +------ src/math/lp/lp_core_solver_base_def.h | 40 -------------------------- 6 files changed, 1 insertion(+), 110 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 999eef13bc8..9168862c651 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -274,47 +274,6 @@ class lar_core_solver { } - - void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { - lp_assert(m_r_solver.inf_set_is_correct()); - for (auto &t : signature) { - unsigned j = t.first; - if (m_r_heading[j] >= 0) - continue; - auto pos_type = t.second; - numeric_pair delta; - if (!update_xj_and_get_delta(j, pos_type, delta)) - continue; - for (const auto & cc : m_r_solver.m_A.m_columns[j]){ - unsigned i = cc.var(); - unsigned jb = m_r_solver.m_basis[i]; - m_r_solver.add_delta_to_x_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); - } - - } - lp_assert(m_r_solver.inf_set_is_correct()); - } - - - - bool adjust_x_of_column(unsigned j) { - /* - if (m_r_solver.m_basis_heading[j] >= 0) { - return false; - } - - if (m_r_solver.column_is_feasible(j)) { - return false; - } - - m_r_solver.snap_column_to_bound_tableau(j); - lp_assert(m_r_solver.column_is_feasible(j)); - m_r_solver.m_inf_set.erase(j); - */ - lp_assert(false); - return true; - } - bool r_basis_is_OK() const { #ifdef Z3DEBUG diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index d9961eaa8ab..48c575184ba 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -643,11 +643,6 @@ namespace lp { bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } - - void lar_solver::adjust_x_of_column(unsigned j) { - lp_assert(false); - } - bool lar_solver::row_is_correct(unsigned i) const { numeric_pair r = zero_of_type>(); for (const auto& c : A_r().m_rows[i]) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 51365936819..09612d905e4 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -210,7 +210,6 @@ class lar_solver : public column_namer { void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); bool use_tableau_costs() const; void detect_rows_of_column_with_bound_change(unsigned j); - void adjust_x_of_column(unsigned j); bool tableau_with_costs() const; bool costs_are_used() const; void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 0d9f4297419..90101e998f5 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -24,7 +24,6 @@ Revision History: #include #include "math/lp/lp_core_solver_base_def.h" template bool lp::lp_core_solver_base::basis_heading_is_correct() const; -template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; @@ -44,12 +43,9 @@ template bool lp::lp_core_solver_base::print_statistics_with_ite template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); -template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; -template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); @@ -57,7 +53,6 @@ template void lp::lp_core_solver_base::restore_x(unsigned int, template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); -template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, @@ -85,18 +80,10 @@ template lp::lp_core_solver_base::lp_core_solver_base( template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base::restore_state(double*, double*); -template void lp::lp_core_solver_base::save_state(double*, double*); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base::restore_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base::save_state(lp::mpq*, lp::mpq*); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base >::restore_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base >::save_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base::init_lu(); -template void lp::lp_core_solver_base::init_lu(); template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; @@ -109,7 +96,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); -template void lp::lp_core_solver_base >::init_lu(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 729792c1266..e54c884891b 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -156,10 +156,6 @@ class lp_core_solver_base { void pretty_print(std::ostream & out); - void save_state(T * w_buffer, T * d_buffer); - - void restore_state(T * w_buffer, T * d_buffer); - X get_cost() const { return dot_product(m_costs, m_x); } @@ -173,8 +169,6 @@ class lp_core_solver_base { void restore_m_ed(T * buffer); - void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); - void add_delta_to_entering(unsigned entering, const X & delta); const X & get_var_value(unsigned j) const { @@ -422,11 +416,9 @@ class lp_core_solver_base { void snap_non_basic_x_to_bound_and_free_to_zeroes(); void snap_xN_to_bounds_and_fill_xB(); - void snap_xN_to_bounds_and_free_columns_to_zeroes(); - + non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - void init_lu(); int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index e470072e04c..bc902e8b052 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -136,17 +136,6 @@ pretty_print(std::ostream & out) { pp.print(); } -template void lp_core_solver_base:: -save_state(T * w_buffer, T * d_buffer) { - copy_m_w(w_buffer); - copy_m_ed(d_buffer); -} - -template void lp_core_solver_base:: -restore_state(T * w_buffer, T * d_buffer) { - restore_m_w(w_buffer); - restore_m_ed(d_buffer); -} template void lp_core_solver_base:: copy_m_w(T * buffer) { @@ -185,26 +174,6 @@ restore_m_ed(T * buffer) { -template void lp_core_solver_base:: -calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row) { - m_pivot_row.clear(); - - for (unsigned i : m_pivot_row_of_B_1.m_index) { - const T & pi_1 = m_pivot_row_of_B_1[i]; - if (numeric_traits::is_zero(pi_1)) { - continue; - } - for (auto & c : m_A.m_rows[i]) { - unsigned j = c.var(); - if (m_basis_heading[j] < 0) { - m_pivot_row.add_value_at_index_with_drop_tolerance(j, c.coeff() * pi_1); - } - } - } - if (precise()) { - m_rows_nz[pivot_row] = m_pivot_row.m_index.size(); - } -} template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { @@ -631,11 +600,6 @@ snap_xN_to_bounds_and_fill_xB() { solve_Ax_eq_b(); } -template void lp_core_solver_base:: -snap_xN_to_bounds_and_free_columns_to_zeroes() { - snap_non_basic_x_to_bound_and_free_to_zeroes(); - solve_Ax_eq_b(); -} template non_basic_column_value_position lp_core_solver_base:: @@ -661,10 +625,6 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } -template void lp_core_solver_base::init_lu() { - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); -} - template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; const T & row_p = this->m_pivot_row[entering]; From 6132bf93f7b4adddc039b00da4ebe1f606e2b2e2 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:11:04 -0800 Subject: [PATCH 484/597] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lp_core_solver_base.cpp | 1 - src/math/lp/lp_core_solver_base.h | 7 ++-- src/math/lp/lp_core_solver_base_def.h | 46 +------------------------ src/math/lp/lp_primal_core_solver_def.h | 21 ++--------- 4 files changed, 6 insertions(+), 69 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 90101e998f5..b76976d371c 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -61,7 +61,6 @@ template lp::lp_core_solver_base >::lp_core_s const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); -template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); template void lp::lp_core_solver_base >::solve_Ax_eq_b(); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index e54c884891b..c9b42f28be0 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -74,7 +74,7 @@ class lp_core_solver_base { vector & m_x; // a feasible solution, the fist time set in the constructor vector & m_costs; lp_settings & m_settings; - lu> * m_factorization = nullptr; + vector m_y; // the buffer for yB = cb // a device that is able to solve Bx=c, xB=d, and change the basis const column_namer & m_column_names; @@ -134,7 +134,7 @@ class lp_core_solver_base { void init(); virtual ~lp_core_solver_base() { - delete m_factorization; + } vector & non_basis() { @@ -413,9 +413,6 @@ class lp_core_solver_base { } - void snap_non_basic_x_to_bound_and_free_to_zeroes(); - void snap_xN_to_bounds_and_fill_xB(); - non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index bc902e8b052..01aa9802e8c 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -554,52 +554,8 @@ find_error_in_BxB(vector& rs){ // recalculates the projection of x to B, such that Ax = b template void lp_core_solver_base:: solve_Ax_eq_b() { - if (numeric_traits::precise()) { - vector rs(m_m()); - rs_minus_Anx(rs); - m_factorization->solve_By(rs); - copy_rs_to_xB(rs); - } - else { - vector rs(m_m()); - rs_minus_Anx(rs); - vector rrs = rs; // another copy of rs - m_factorization->solve_By(rs); - copy_rs_to_xB(rs); - find_error_in_BxB(rrs); - m_factorization->solve_By(rrs); - add_delta_to_xB(rrs); - } -} - - - - -template void lp_core_solver_base:: -snap_non_basic_x_to_bound_and_free_to_zeroes() { - for (unsigned j : non_basis()) { - lp_assert(j < m_x.size()); - switch (m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - case column_type::lower_bound: - m_x[j] = m_lower_bounds[j]; - break; - case column_type::upper_bound: - m_x[j] = m_upper_bounds[j]; - break; - default: - m_x[j] = zero_of_type(); - break; - } - } + lp_assert(false); } -template void lp_core_solver_base:: -snap_xN_to_bounds_and_fill_xB() { - snap_non_basic_x_to_bound(); - solve_Ax_eq_b(); -} - template non_basic_column_value_position lp_core_solver_base:: diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 19ffaa1eadc..7764ce6d2d9 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -642,11 +642,7 @@ template void lp_primal_core_solver::init_run( template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ - lp_assert(numeric_traits::precise() == false); - lp_assert(this->m_ed.is_OK()); - lp_assert(m_beta.is_OK()); - m_beta = this->m_ed; - this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings); + lp_assert(false); } template @@ -758,10 +754,7 @@ template unsigned lp_primal_core_solver::solve() } template void lp_primal_core_solver::delete_factorization() { - if (this->m_factorization != nullptr) { - delete this->m_factorization; - this->m_factorization = nullptr; - } + lp_assert(false); } // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" @@ -776,15 +769,7 @@ template void lp_primal_core_solver::init_column_ // debug only template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - lp_assert(numeric_traits::precise() == false); - indexed_vector w(this->m_m()); - this->m_A.copy_column_to_vector(j, w); - vector d(this->m_m()); - this->m_factorization->solve_Bd_when_w_is_ready(d, w); - T ret = zero_of_type(); - for (auto v : d) - ret += v*v; - return ret+1; + lp_assert(false); } template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { From 377ceba6d58e436a05092b2f57ebe5f068cc78eb Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:33:53 -0800 Subject: [PATCH 485/597] rm lu --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lp_core_solver_base.h | 14 +- src/math/lp/lp_primal_core_solver.h | 4 - src/math/lp/lp_primal_core_solver_def.h | 13 +- .../lp/lp_primal_core_solver_tableau_def.h | 6 +- src/math/lp/lp_settings.h | 1 - src/math/lp/lp_settings_def.h | 2 - src/math/lp/lu.cpp | 81 -- src/math/lp/lu.h | 325 -------- src/math/lp/lu_def.h | 720 ------------------ src/math/lp/row_eta_matrix.cpp | 1 - src/math/lp/square_sparse_matrix.cpp | 1 - src/math/lp/square_sparse_matrix_def.h | 2 + src/smt/theory_lra.cpp | 2 +- 14 files changed, 22 insertions(+), 1151 deletions(-) delete mode 100644 src/math/lp/lu.cpp delete mode 100644 src/math/lp/lu.h delete mode 100644 src/math/lp/lu_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index cc58c9610fc..97073736717 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -21,7 +21,6 @@ z3_add_component(lp lp_core_solver_base.cpp lp_primal_core_solver.cpp lp_settings.cpp - lu.cpp lp_utils.cpp matrix.cpp mon_eq.cpp diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index c9b42f28be0..0e8a2abdf3a 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -25,11 +25,21 @@ Revision History: #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/numeric_pair.h" #include "math/lp/static_matrix.h" -#include "math/lp/lu.h" #include "math/lp/permutation_matrix.h" #include "math/lp/column_namer.h" +#include "math/lp/u_set.h" + namespace lp { +template +X dot_product(const vector & a, const vector & b) { + lp_assert(a.size() == b.size()); + auto r = zero_of_type(); + for (unsigned i = 0; i < a.size(); i++) { + r += a[i] * b[i]; + } + return r; +} template // X represents the type of the x variable and the bounds class lp_core_solver_base { @@ -156,6 +166,8 @@ class lp_core_solver_base { void pretty_print(std::ostream & out); + + X get_cost() const { return dot_product(m_costs, m_x); } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index abee0cc6efd..38a2aa5f5e0 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -29,7 +29,6 @@ Revision History: #include #include #include -#include "math/lp/lu.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" @@ -418,9 +417,6 @@ class lp_primal_core_solver:public lp_core_solver_base { // returns the number of iterations unsigned solve(); - lu> * factorization() {return nullptr;} - - void delete_factorization(); // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" void init_column_norms(); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7764ce6d2d9..3967b6e6698 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -26,6 +26,7 @@ Revision History: #include #include #include "math/lp/lp_primal_core_solver.h" +#include "math/lp/dense_matrix.h" namespace lp { // This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis @@ -733,8 +734,7 @@ template unsigned lp_primal_core_solver::solve() default: break; // do nothing } - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR - && + } while ( this->get_status() != lp_status::UNBOUNDED && this->get_status() != lp_status::OPTIMAL @@ -745,18 +745,13 @@ template unsigned lp_primal_core_solver::solve() && !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); - lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR - || + lp_assert( this->current_x_is_feasible() == false || this->calc_current_x_is_feasible_include_non_basis()); return this->total_iterations(); } -template void lp_primal_core_solver::delete_factorization() { - lp_assert(false); -} - // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" template void lp_primal_core_solver::init_column_norms() { lp_assert(numeric_traits::precise() == false); @@ -860,7 +855,7 @@ template void lp_primal_core_solver::fill_breakpo template bool lp_primal_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true; + if (this->get_status() == lp_status::OPTIMAL) return true; if (this->get_status() == lp_status::INFEASIBLE) { return true; } diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 736b79db5ad..e742b0cd01a 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -157,8 +157,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { this->set_status(lp_status::CANCELLED); break; // from the loop } - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR - && + } while ( this->get_status() != lp_status::UNBOUNDED && this->get_status() != lp_status::OPTIMAL @@ -168,8 +167,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) ); - lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR - || + lp_assert( this->get_status() == lp_status::CANCELLED || this->current_x_is_feasible() == false diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 360ef99bf1d..cb60eebc9c5 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -69,7 +69,6 @@ enum class lp_status { DUAL_UNBOUNDED, OPTIMAL, FEASIBLE, - FLOATING_POINT_ERROR, TIME_EXHAUSTED, EMPTY, UNSTABLE, diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index 58b37a19dcf..bb87109f6fb 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -45,7 +45,6 @@ const char* lp_status_to_string(lp_status status) { case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; case lp_status::OPTIMAL: return "OPTIMAL"; case lp_status::FEASIBLE: return "FEASIBLE"; - case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED"; case lp_status::EMPTY: return "EMPTY"; case lp_status::UNSTABLE: return "UNSTABLE"; @@ -62,7 +61,6 @@ lp_status lp_status_from_string(std::string status) { if (status == "UNBOUNDED") return lp_status::UNBOUNDED; if (status == "OPTIMAL") return lp_status::OPTIMAL; if (status == "FEASIBLE") return lp_status::FEASIBLE; - if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR; if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; lp_unreachable(); diff --git a/src/math/lp/lu.cpp b/src/math/lp/lu.cpp deleted file mode 100644 index 313fa990151..00000000000 --- a/src/math/lp/lu.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lu_def.h" -namespace lp { -template double dot_product(vector const&, vector const&); -template lu>::lu(static_matrix const&, vector&, lp_settings&); -template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::replace_column(double, indexed_vector&, unsigned); -template lu>::~lu(); -template void lu>::push_matrix_to_tail(tail_matrix*); -template lu>::~lu(); -template void lu>::push_matrix_to_tail(tail_matrix*); -template lu>::~lu(); -template mpq dot_product(vector const&, vector const&); -template void init_factorization> - (lu>*&, static_matrix&, vector&, lp_settings&); -template void init_factorization> - (lu>*&, static_matrix&, vector&, lp_settings&); -template void init_factorization>(lu >*&, static_matrix&, vector&, lp_settings&); -template void print_matrix>(square_sparse_matrix&, std::ostream & out); -template void print_matrix>(static_matrix&, std::ostream&); -template void print_matrix >(static_matrix&, std::ostream&); -template void print_matrix>(static_matrix&, std::ostream & out); -#ifdef Z3DEBUG -template bool lu>::is_correct(const vector& basis); -template bool lu>::is_correct( vector const &); -template dense_matrix get_B>(lu>&, const vector& basis); -template dense_matrix get_B>(lu>&, vector const&); - -#endif - -template bool lu>::pivot_the_row(int); // NOLINT -template void lu>::init_vector_w(unsigned int, indexed_vector&); -template void lu>::solve_By(vector&); -template void lu>::solve_By_when_y_is_ready_for_X(vector&); -template void lu>::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector const&, const vector & basis, const lp_settings&); -template void lu>::replace_column(mpq, indexed_vector&, unsigned); -template void lu>::solve_By(vector&); -template void lu>::solve_By_when_y_is_ready_for_X(vector&); -template void lu>::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lu >::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lu >::init_vector_w(unsigned int, indexed_vector&); -template void lu >::replace_column(mpq, indexed_vector&, unsigned); -template void lu >::solve_Bd_faster(unsigned int, indexed_vector&); -template void lu >::solve_By(vector&); -template void lu >::solve_By_when_y_is_ready_for_X(vector&); -template void lu >::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_By(indexed_vector&); -template void lu>::solve_By(indexed_vector&); -template void lu>::solve_yB_indexed(indexed_vector&); -template void lu >::solve_yB_indexed(indexed_vector&); -template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); -template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); -#ifdef Z3DEBUG -template void print_matrix>(tail_matrix&, std::ostream&); -#endif -} diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h deleted file mode 100644 index 18251e3ada5..00000000000 --- a/src/math/lp/lu.h +++ /dev/null @@ -1,325 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - for matrix B we have - t0*...*tn-1*B = Q*U*R - here ti are matrices corresponding to pivot operations, - including columns and rows swaps, - or a multiplication matrix row by a number - Q, R - permutations and U is an upper triangular matrix -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -#include "util/vector.h" -#include "util/debug.h" -#include -#include -#include "math/lp/square_sparse_matrix.h" -#include "math/lp/static_matrix.h" -#include -#include "math/lp/numeric_pair.h" -#include -#include -#include "math/lp/row_eta_matrix.h" -#include "math/lp/square_dense_submatrix.h" -#include "math/lp/dense_matrix.h" -namespace lp { -template // print the nr x nc submatrix at the top left corner -void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc); - -template -void print_matrix(M &m, std::ostream & out); - -template -X dot_product(const vector & a, const vector & b) { - lp_assert(a.size() == b.size()); - auto r = zero_of_type(); - for (unsigned i = 0; i < a.size(); i++) { - r += a[i] * b[i]; - } - return r; -} - - -template -class one_elem_on_diag: public tail_matrix { - unsigned m_i; - T m_val; -public: - one_elem_on_diag(unsigned i, T val) : m_i(i), m_val(val) { -#ifdef Z3DEBUG - m_one_over_val = numeric_traits::one() / m_val; -#endif - } - - bool is_dense() const override { return false; } - - one_elem_on_diag(const one_elem_on_diag & o); - -#ifdef Z3DEBUG - unsigned m_m; - unsigned m_n; - void set_number_of_rows(unsigned m) override { m_m = m; m_n = m; } - void set_number_of_columns(unsigned n) override { m_m = n; m_n = n; } - T m_one_over_val; - - T get_elem (unsigned i, unsigned j) const override; - - unsigned row_count() const override { return m_m; } // not defined } - unsigned column_count() const override { return m_m; } // not defined } -#endif - void apply_from_left(vector & w, lp_settings &) override { - w[m_i] /= m_val; - } - - void apply_from_right(vector & w) override { - w[m_i] /= m_val; - } - - void apply_from_right(indexed_vector & w) override { - if (is_zero(w.m_data[m_i])) - return; - auto & v = w.m_data[m_i] /= m_val; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - w.erase_from_index(m_i); - v = zero_of_type(); - } - } - - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; - - void conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_i = p.apply_reverse(m_i); - -#ifdef Z3DEBUG - // lp_assert(*this == deb); -#endif - } -}; // end of one_elem_on_diag - -enum class LU_status { OK, Degenerated}; - -// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c -// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -class lu { - LU_status m_status; -public: - typedef typename M::coefftype T; - typedef typename M::argtype X; - - // the fields - unsigned m_dim; - const M & m_A; - permutation_matrix m_Q; - permutation_matrix m_R; - permutation_matrix m_r_wave; - square_sparse_matrix m_U; - square_dense_submatrix* m_dense_LU; - - vector *> m_tail; - lp_settings & m_settings; - bool m_failure; - indexed_vector m_row_eta_work_vector; - indexed_vector m_w_for_extension; - indexed_vector m_y_copy; - indexed_vector m_ii; //to optimize the work with the m_index fields - unsigned m_refactor_counter; - // constructor - // if A is an m by n matrix then basis has length m and values in [0,n); the values are all different - // they represent the set of m columns - lu(const M & A, - vector& basis, - lp_settings & settings); - lu(const M & A, lp_settings&); - void debug_test_of_basis(const M & A, vector & basis); - void solve_Bd_when_w_is_ready(vector & d, indexed_vector& w ); - void solve_By(indexed_vector & y); - - void solve_By(vector & y); - - void solve_By_for_T_indexed_only(indexed_vector& y, const lp_settings &); - - template - void solve_By_when_y_is_ready(indexed_vector & y); - void solve_By_when_y_is_ready_for_X(vector & y); - void solve_By_when_y_is_ready_for_T(vector & y, vector & index); - void print_indexed_vector(indexed_vector & w, std::ofstream & f); - - void print_matrix_compact(std::ostream & f); - - void print(indexed_vector & w, const vector& basis); - void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit - - - void solve_yB_indexed(indexed_vector& y); - - void add_delta_to_solution_indexed(indexed_vector& y); - - void add_delta_to_solution(const vector& yc, vector& y); - - - void find_error_of_yB(vector& yc, const vector& y, - const vector& basis); - - void find_error_of_yB_indexed(const indexed_vector& y, - const vector& heading, const lp_settings& settings); - - - void solve_yB_with_error_check(vector & y, const vector& basis); - - void solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings &); - - void apply_Q_R_to_U(permutation_matrix & r_wave); - - - LU_status get_status() { return m_status; } - - void set_status(LU_status status) { - m_status = status; - } - - ~lu(); - - void init_vector_y(vector & y); - - void perform_transformations_on_w(indexed_vector& w); - - void init_vector_w(unsigned entering, indexed_vector & w); - void apply_lp_list_to_w(indexed_vector & w); - void apply_lp_list_to_y(vector& y); - - void swap_rows(int j, int k); - - void swap_columns(int j, int pivot_column); - - void push_matrix_to_tail(tail_matrix* tm) { - m_tail.push_back(tm); - } - - bool pivot_the_row(int row); - - eta_matrix * get_eta_matrix_for_pivot(unsigned j); - // we're processing the column j now - eta_matrix * get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U); - - // see page 407 of Chvatal - unsigned transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column_of_U); - -#ifdef Z3DEBUG - void check_vector_w(unsigned entering); - - void check_apply_matrix_to_vector(matrix *lp, T *w); - - void check_apply_lp_lists_to_w(T * w); - - // provide some access operators for testing - permutation_matrix & Q() { return m_Q; } - permutation_matrix & R() { return m_R; } - matrix & U() { return m_U; } - unsigned tail_size() { return m_tail.size(); } - - tail_matrix * get_lp_matrix(unsigned i) { - return m_tail[i]; - } - - T B_(unsigned i, unsigned j, const vector& basis) { - return m_A[i][basis[j]]; - } - - unsigned dimension() { return m_dim; } - -#endif - - - unsigned get_number_of_nonzeroes() { - return m_U.get_number_of_nonzeroes(); - } - - - void process_column(int j); - - bool is_correct(const vector& basis); - bool is_correct(); - - - - // needed for debugging purposes - void copy_w(T *buffer, indexed_vector & w); - - // needed for debugging purposes - void restore_w(T *buffer, indexed_vector & w); - bool all_columns_and_rows_are_active(); - - bool too_dense(unsigned j) const; - - void pivot_in_dense_mode(unsigned i); - - void create_initial_factorization(); - - void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave); - - void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump); - - bool diagonal_element_is_off(T /* diag_element */) { return false; } - - void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump); - // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last - // row at the same time - - void replace_column(T pivot_elem, indexed_vector & w, unsigned leaving_column_of_U); - - void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump); - - void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element); - - void prepare_entering(unsigned entering, indexed_vector & w) { - lp_assert(false); - } - bool need_to_refactor() { lp_assert(false); - return m_refactor_counter >= 200; } - - void adjust_dimension_with_matrix_A() { - lp_assert(false); - } - - - - - - -}; // end of lu - -template -void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings); - -#ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis); - -template -dense_matrix get_B(lu& f); -#endif -} diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h deleted file mode 100644 index ca34481cca8..00000000000 --- a/src/math/lp/lu_def.h +++ /dev/null @@ -1,720 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include -#include "util/vector.h" -#include -#include "util/debug.h" -#include "math/lp/lu.h" -namespace lp { -template // print the nr x nc submatrix at the top left corner -void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count() && j < nc; j++) { - A[i].push_back(T_to_string(static_cast(m(i, j)))); - } - } - - for (unsigned j = 0; j < m.column_count() && j < nc; j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} - -template -void print_matrix(M &m, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count(); i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(m[i][j])); - } - } - - for (unsigned j = 0; j < m.column_count(); j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} - -template -one_elem_on_diag::one_elem_on_diag(const one_elem_on_diag & o) { - m_i = o.m_i; - m_val = o.m_val; -#ifdef Z3DEBUG - m_m = m_n = o.m_m; - m_one_over_val = numeric_traits::one() / o.m_val; -#endif -} - -#ifdef Z3DEBUG -template -T one_elem_on_diag::get_elem(unsigned i, unsigned j) const { - if (i == j){ - if (j == m_i) { - return m_one_over_val; - } - return numeric_traits::one(); - } - - return numeric_traits::zero(); -} -#endif -template -void one_elem_on_diag::apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { - T & t = w[m_i]; - if (numeric_traits::is_zero(t)) { - return; - } - t /= m_val; - if (numeric_traits::precise()) return; - if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { - w.erase_from_index(m_i); - t = numeric_traits::zero(); - } -} - -// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c -// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -lu::lu(const M& A, - vector& basis, - lp_settings & settings): - m_status(LU_status::OK), - m_dim(A.row_count()), - m_A(A), - m_Q(m_dim), - m_R(m_dim), - m_r_wave(m_dim), - m_U(A, basis), // create the square matrix that eventually will be factorized - m_settings(settings), - m_failure(false), - m_row_eta_work_vector(A.row_count()), - m_refactor_counter(0) { - lp_assert(!(numeric_traits::precise() )); -#ifdef Z3DEBUG - debug_test_of_basis(A, basis); -#endif - ++m_settings.stats().m_num_factorizations; - create_initial_factorization(); -#ifdef Z3DEBUG - // lp_assert(check_correctness()); -#endif -} -template -lu::lu(const M& A, - lp_settings & settings): - m_status(LU_status::OK), - m_dim(A.row_count()), - m_A(A), - m_Q(m_dim), - m_R(m_dim), - m_r_wave(m_dim), - m_U(A), // create the square matrix that eventually will be factorized - m_settings(settings), - m_failure(false), - m_row_eta_work_vector(A.row_count()), - m_refactor_counter(0) { - lp_assert(A.row_count() == A.column_count()); - create_initial_factorization(); -#ifdef Z3DEBUG - lp_assert(is_correct()); -#endif -} -template -void lu::debug_test_of_basis( M const & A, vector & basis) { - std::set set; - for (unsigned i = 0; i < A.row_count(); i++) { - lp_assert(basis[i]< A.column_count()); - set.insert(basis[i]); - } - lp_assert(set.size() == A.row_count()); -} - -template -void lu::solve_By(indexed_vector & y) { - lp_assert(false); // not implemented - // init_vector_y(y); - // solve_By_when_y_is_ready(y); - } - - -template -void lu::solve_By(vector & y) { - init_vector_y(y); - solve_By_when_y_is_ready_for_X(y); -} - -template -void lu::solve_By_when_y_is_ready_for_X(vector & y) { - if (numeric_traits::precise()) { - m_U.solve_U_y(y); - m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal - return; - } - m_U.double_solve_U_y(y); - m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal - unsigned i = m_dim; - while (i--) { - if (is_zero(y[i])) continue; - if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ - y[i] = zero_of_type(); - } - } -} - -template -void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { - if (numeric_traits::precise()) { - m_U.solve_U_y(y); - m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal - unsigned j = m_dim; - while (j--) { - if (!is_zero(y[j])) - index.push_back(j); - } - return; - } - m_U.double_solve_U_y(y); - m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal - unsigned i = m_dim; - while (i--) { - if (is_zero(y[i])) continue; - if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ - y[i] = zero_of_type(); - } else { - index.push_back(i); - } - } -} - -template -void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { - if (numeric_traits::precise()) { - vector active_rows; - m_U.solve_U_y_indexed_only(y, settings, active_rows); - m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal - return; - } - m_U.double_solve_U_y(y, m_settings); - m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal -} - -template -void lu::print_matrix_compact(std::ostream & f) { - f << "matrix_start" << std::endl; - f << "nrows " << m_A.row_count() << std::endl; - f << "ncolumns " << m_A.column_count() << std::endl; - for (unsigned i = 0; i < m_A.row_count(); i++) { - auto & row = m_A.m_rows[i]; - f << "row " << i << std::endl; - for (auto & t : row) { - f << "column " << t.m_j << " value " << t.m_value << std::endl; - } - f << "row_end" << std::endl; - } - f << "matrix_end" << std::endl; -} -template -void lu< M>::print(indexed_vector & w, const vector& basis) { - std::string dump_file_name("/tmp/lu"); - remove(dump_file_name.c_str()); - std::ofstream f(dump_file_name); - if (!f.is_open()) { - LP_OUT(m_settings, "cannot open file " << dump_file_name << std::endl); - return; - } - LP_OUT(m_settings, "writing lu dump to " << dump_file_name << std::endl); - print_matrix_compact(f); - print_vector(basis, f); - print_indexed_vector(w, f); - f.close(); -} - -template -void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d - init_vector_w(a_column, d); - solve_By_for_T_indexed_only(d, m_settings); -} - -template -void lu< M>::solve_yB_indexed(indexed_vector& y) { - lp_assert(y.is_OK()); - // first solve yU = cb*R(-1) - m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - lp_assert(y.is_OK()); - m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1) - lp_assert(y.is_OK()); - m_Q.apply_reverse_from_right_to_T(y); - lp_assert(y.is_OK()); - for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { -#ifdef Z3DEBUG - (*e)->set_number_of_columns(m_dim); -#endif - (*e)->apply_from_right(y); - lp_assert(y.is_OK()); - } -} - -template -void lu< M>::add_delta_to_solution(const vector& yc, vector& y){ - unsigned i = static_cast(y.size()); - while (i--) - y[i]+=yc[i]; -} - -template -void lu< M>::add_delta_to_solution_indexed(indexed_vector& y) { - // the delta sits in m_y_copy, put result into y - lp_assert(y.is_OK()); - lp_assert(m_y_copy.is_OK()); - m_ii.clear(); - m_ii.resize(y.data_size()); - for (unsigned i : y.m_index) - m_ii.set_value(1, i); - for (unsigned i : m_y_copy.m_index) { - y.m_data[i] += m_y_copy[i]; - if (m_ii[i] == 0) - m_ii.set_value(1, i); - } - lp_assert(m_ii.is_OK()); - y.m_index.clear(); - - for (unsigned i : m_ii.m_index) { - T & v = y.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)) - y.m_index.push_back(i); - else if (!numeric_traits::is_zero(v)) - v = zero_of_type(); - } - - lp_assert(y.is_OK()); -} - -template -void lu< M>::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { - unsigned i = m_dim; - while (i--) { - yc[i] -= m_A.dot_product_with_column(y, m_basis[i]); - } -} - -template -void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { -#if 0 == 1 - // it is a non efficient version - indexed_vector yc = m_y_copy; - yc.m_index.clear(); - lp_assert(!numeric_traits::precise()); - { - - vector d_basis(y.m_data.size()); - for (unsigned j = 0; j < heading.size(); j++) { - if (heading[j] >= 0) { - d_basis[heading[j]] = j; - } - } - - - unsigned i = m_dim; - while (i--) { - T & v = yc.m_data[i] -= m_A.dot_product_with_column(y.m_data, d_basis[i]); - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) - v = zero_of_type(); - else - yc.m_index.push_back(i); - } - } -#endif - lp_assert(m_ii.is_OK()); - m_ii.clear(); - m_ii.resize(y.data_size()); - lp_assert(m_y_copy.is_OK()); - // put the error into m_y_copy - for (auto k : y.m_index) { - auto & row = m_A.m_rows[k]; - const T & y_k = y.m_data[k]; - for (auto & c : row) { - unsigned j = c.var(); - int hj = heading[j]; - if (hj < 0) continue; - if (m_ii.m_data[hj] == 0) - m_ii.set_value(1, hj); - m_y_copy.m_data[hj] -= c.coeff() * y_k; - } - } - // add the index of m_y_copy to m_ii - for (unsigned i : m_y_copy.m_index) { - if (m_ii.m_data[i] == 0) - m_ii.set_value(1, i); - } - - // there is no guarantee that m_y_copy is OK here, but its index - // is contained in m_ii index - m_y_copy.m_index.clear(); - // setup the index of m_y_copy - for (auto k : m_ii.m_index) { - T& v = m_y_copy.m_data[k]; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) - v = zero_of_type(); - else { - m_y_copy.set_value(v, k); - } - } - lp_assert(m_y_copy.is_OK()); - -} - - - - -// solves y*B = y -// y is the input -template -void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { - lp_assert(false); - } - - -// solves y*B = y -// y is the input -template -void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { - lp_assert(false); -} -template -void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { - m_U.multiply_from_right(r_wave); - m_U.multiply_from_left_with_reverse(r_wave); -} - - -// Solving yB = cb to find the entering variable, -// where cb is the cost vector projected to B. -// The result is stored in cb. - -// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering -// variable -template -lu< M>::~lu(){ - for (auto t : m_tail) { - delete t; - } -} -template -void lu< M>::init_vector_y(vector & y) { - apply_lp_list_to_y(y); - m_Q.apply_reverse_from_left_to_X(y); -} - -template -void lu< M>::perform_transformations_on_w(indexed_vector& w) { - apply_lp_list_to_w(w); - m_Q.apply_reverse_from_left(w); - // TBD does not compile: lp_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); -} - -// see Chvatal 24.3 -template -void lu< M>::init_vector_w(unsigned entering, indexed_vector & w) { - w.clear(); - m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column - perform_transformations_on_w(w); -} -template -void lu< M>::apply_lp_list_to_w(indexed_vector & w) { - for (unsigned i = 0; i < m_tail.size(); i++) { - m_tail[i]->apply_from_left_to_T(w, m_settings); - // TBD does not compile: lp_assert(check_vector_for_small_values(w, m_settings)); - } -} -template -void lu< M>::apply_lp_list_to_y(vector& y) { - for (unsigned i = 0; i < m_tail.size(); i++) { - m_tail[i]->apply_from_left(y, m_settings); - } -} -template -void lu< M>::swap_rows(int j, int k) { - if (j != k) { - m_Q.transpose_from_left(j, k); - m_U.swap_rows(j, k); - } -} - -template -void lu< M>::swap_columns(int j, int pivot_column) { - if (j == pivot_column) - return; - m_R.transpose_from_right(j, pivot_column); - m_U.swap_columns(j, pivot_column); -} -template -bool lu< M>::pivot_the_row(int row) { - eta_matrix * eta_matrix = get_eta_matrix_for_pivot(row); - if (get_status() != LU_status::OK) { - return false; - } - - if (eta_matrix == nullptr) { - m_U.shorten_active_matrix(row, nullptr); - return true; - } - if (!m_U.pivot_with_eta(row, eta_matrix, m_settings)) - return false; - eta_matrix->conjugate_by_permutation(m_Q); - push_matrix_to_tail(eta_matrix); - return true; -} -// we're processing the column j now -template -eta_matrix * lu< M>::get_eta_matrix_for_pivot(unsigned j) { - eta_matrix *ret; - if(!m_U.fill_eta_matrix(j, &ret)) { - set_status(LU_status::Degenerated); - } - return ret; -} -// we're processing the column j now -template -eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U) { - eta_matrix *ret; - copy_of_U.fill_eta_matrix(j, &ret); - return ret; -} - -// see page 407 of Chvatal -template -unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, - unsigned leaving_column) { - unsigned column_to_replace = m_R.apply_reverse(leaving_column); - m_U.replace_column(column_to_replace, w, m_settings); - return column_to_replace; -} - -#ifdef Z3DEBUG -template -void lu::check_vector_w(unsigned entering) { - T * w = new T[m_dim]; - m_A.copy_column_to_vector(entering, w); - check_apply_lp_lists_to_w(w); - delete [] w; -} -template -void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { - if (lp != nullptr) { - lp -> set_number_of_rows(m_dim); - lp -> set_number_of_columns(m_dim); - apply_to_vector(*lp, w); - } -} - -template -void lu::check_apply_lp_lists_to_w(T * w) { - for (unsigned i = 0; i < m_tail.size(); i++) { - check_apply_matrix_to_vector(m_tail[i], w); - } - permutation_matrix qr = m_Q.get_reverse(); - apply_to_vector(qr, w); - for (int i = m_dim - 1; i >= 0; i--) { - lp_assert(abs(w[i] - w[i]) < 0.0000001); - } -} - -#endif -template -void lu::process_column(int j) { - unsigned pi, pj; - bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j); - if (!success) { - // LP_OUT(m_settings, "get_pivot returned false: cannot find the pivot for column " << j << std::endl); - m_failure = true; - return; - } - - if (static_cast(pi) == -1) { - // LP_OUT(m_settings, "cannot find the pivot for column " << j << std::endl); - m_failure = true; - return; - } - swap_columns(j, pj); - swap_rows(j, pi); - if (!pivot_the_row(j)) { - // LP_OUT(m_settings, "pivot_the_row(" << j << ") failed" << std::endl); - m_failure = true; - } -} -template -bool lu::is_correct(const vector& basis) { -return true; -} - -template -bool lu::is_correct() { - return true; -} - - -// needed for debugging purposes -template -void lu::copy_w(T *buffer, indexed_vector & w) { - -} - -// needed for debugging purposes -template -void lu::restore_w(T *buffer, indexed_vector & w) { - -} -template -bool lu::all_columns_and_rows_are_active() { - return true; -} -template -bool lu::too_dense(unsigned j) const { - return false; -} -template -void lu::pivot_in_dense_mode(unsigned i) { - -} -template -void lu::create_initial_factorization(){ - -} - -template -void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { - if (bump_start > bump_end) { - set_status(LU_status::Degenerated); - return; - } - if (bump_start == bump_end) { - return; - } - - r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump - - for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) { - r_wave[i] = i - 1; - } - - m_U.multiply_from_right(r_wave); - m_U.multiply_from_left_with_reverse(r_wave); -} -template -void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { - vector> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump)); - for (auto & iv : last_row_vec) { - if (is_zero(iv.m_value)) continue; - lp_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); - unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index); - if (adjusted_col < lowest_row_of_the_bump) { - m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col); - } else { - m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix - } - } -} - -template -void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { - // we have the system right side at m_row_eta_work_vector now - // solve the system column wise - for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) { - T v = m_row_eta_work_vector[j]; - if (numeric_traits::is_zero(v)) continue; // this column does not contribute to the solution - unsigned aj = m_U.adjust_row(j); - vector> & row = m_U.get_row_values(aj); - for (auto & iv : row) { - unsigned col = m_U.adjust_column_inverse(iv.m_index); - lp_assert(col >= j || numeric_traits::is_zero(iv.m_value)); - if (col == j) continue; - if (numeric_traits::is_zero(iv.m_value)) { - continue; - } - // the -v is for solving the system ( to zero the last row), and +v is for pivoting - T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value; - lp_assert(numeric_traits::is_zero(delta) == false); - - - - // m_row_eta_work_vector.add_value_at_index_with_drop_tolerance(col, delta); - if (numeric_traits::is_zero(m_row_eta_work_vector[col])) { - if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){ - m_row_eta_work_vector.set_value(delta, col); - } - } else { - T t = (m_row_eta_work_vector[col] += delta); - if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){ - m_row_eta_work_vector[col] = numeric_traits::zero(); - auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col); - if (it != m_row_eta_work_vector.m_index.end()) - m_row_eta_work_vector.m_index.erase(it); - } - } - } - } -} - -template -void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ - lp_assert(false); -} -template -void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ - lp_assert(false);// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); -} - -template -void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { - lp_assert(false); -} - -template -void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { - lp_assert(false); -} - -#ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis) { - lp_assert(false); - - dense_matrix B(0, 0); - return B; -} -template -dense_matrix get_B(lu& f) { - lp_assert(false); - dense_matrix B(0,0); - return B; -} -#endif -} diff --git a/src/math/lp/row_eta_matrix.cpp b/src/math/lp/row_eta_matrix.cpp index 6fafb83ed6a..356f80b3c01 100644 --- a/src/math/lp/row_eta_matrix.cpp +++ b/src/math/lp/row_eta_matrix.cpp @@ -20,7 +20,6 @@ Revision History: #include #include "util/vector.h" #include "math/lp/row_eta_matrix_def.h" -#include "math/lp/lu.h" namespace lp { template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); template void row_eta_matrix >::conjugate_by_permutation(permutation_matrix >&); diff --git a/src/math/lp/square_sparse_matrix.cpp b/src/math/lp/square_sparse_matrix.cpp index 35d38e52944..3ec88f47d85 100644 --- a/src/math/lp/square_sparse_matrix.cpp +++ b/src/math/lp/square_sparse_matrix.cpp @@ -20,7 +20,6 @@ Revision History: #include #include "util/vector.h" #include "math/lp/lp_settings.h" -#include "math/lp/lu.h" #include "math/lp/square_sparse_matrix_def.h" #include "math/lp/dense_matrix.h" namespace lp { diff --git a/src/math/lp/square_sparse_matrix_def.h b/src/math/lp/square_sparse_matrix_def.h index 3533ba066b5..19263462717 100644 --- a/src/math/lp/square_sparse_matrix_def.h +++ b/src/math/lp/square_sparse_matrix_def.h @@ -21,6 +21,8 @@ Revision History: #include "util/vector.h" #include "math/lp/square_sparse_matrix.h" +#include "math/lp/dense_matrix.h" + #include #include namespace lp { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 2aa988282d2..3eda1ddff43 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3126,7 +3126,7 @@ class theory_lra::imp { return l_false; TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, - // FLOATING_POINT_ERROR, TIME_EXAUSTED, EMPTY, UNSTABLE + // TIME_EXAUSTED, EMPTY, UNSTABLE return l_undef; } From 73224adc48b6468d8aa48b082919fab1b90d7139 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:51:10 -0800 Subject: [PATCH 486/597] cleanup --- src/math/lp/lar_core_solver_def.h | 5 +- src/math/lp/lp_core_solver_base.h | 12 ---- src/math/lp/lp_core_solver_base_def.h | 34 +---------- src/math/lp/lp_primal_core_solver.cpp | 1 - src/math/lp/lp_primal_core_solver.h | 4 -- src/math/lp/lp_primal_core_solver_def.h | 58 ++----------------- .../lp/lp_primal_core_solver_tableau_def.h | 6 +- src/math/lp/lp_settings.h | 1 - 8 files changed, 9 insertions(+), 112 deletions(-) diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 22dc23bd512..fe99a202f71 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -51,8 +51,6 @@ void lar_core_solver::prefix_r() { // m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { - if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) - m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n()); m_r_solver.m_costs.resize(m_r_solver.m_n()); m_r_solver.m_d.resize(m_r_solver.m_n()); m_r_solver.set_using_infeas_costs(true); @@ -61,7 +59,6 @@ void lar_core_solver::prefix_r() { void lar_core_solver::prefix_d() { // m_d_solver.m_b.resize(m_d_solver.m_m()); - m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n()); m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); m_d_solver.m_costs.resize(m_d_solver.m_n()); m_d_solver.m_d.resize(m_d_solver.m_n()); @@ -91,7 +88,7 @@ void lar_core_solver::fill_not_improvable_zero_sum() { return; } // reusing the existing mechanism for row_feasibility_loop - m_infeasible_sum_sign = m_r_solver.m_settings.use_breakpoints_in_feasibility_search? -1 : 1; + m_infeasible_sum_sign = 1; m_infeasible_linear_combination.clear(); for (auto j : m_r_solver.m_basis) { const mpq & cost_j = m_r_solver.m_costs[j]; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 0e8a2abdf3a..5f723c1185f 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -159,15 +159,8 @@ class lp_core_solver_base { lp_status get_status() const{ return m_status; } - - void fill_cb(T * y) const; - - void fill_cb(vector & y) const; - void pretty_print(std::ostream & out); - - X get_cost() const { return dot_product(m_costs, m_x); } @@ -176,11 +169,6 @@ class lp_core_solver_base { void restore_m_w(T * buffer); - // needed for debugging - void copy_m_ed(T * buffer); - - void restore_m_ed(T * buffer); - void add_delta_to_entering(unsigned entering, const X & delta); const X & get_var_value(unsigned j) const { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 01aa9802e8c..8b87728e090 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -101,20 +101,7 @@ pivot_to_reduced_costs_tableau(unsigned i, unsigned j) { } -template void lp_core_solver_base:: -fill_cb(T * y) const { - for (unsigned i = 0; i < m_m(); i++) { - y[i] = m_costs[m_basis[i]]; - } -} - -template void lp_core_solver_base:: -fill_cb(vector & y) const { - for (unsigned i = 0; i < m_m(); i++) { - y[i] = m_costs[m_basis[i]]; - } -} // template void lp_core_solver_base:: @@ -155,25 +142,6 @@ restore_m_w(T * buffer) { } } -// needed for debugging -template void lp_core_solver_base:: -copy_m_ed(T * buffer) { - unsigned i = m_m(); - while (i --) { - buffer[i] = m_ed[i]; - } -} - -template void lp_core_solver_base:: -restore_m_ed(T * buffer) { - unsigned i = m_m(); - while (i --) { - m_ed[i] = buffer[i]; - } -} - - - template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { @@ -682,7 +650,7 @@ lp_core_solver_base::infeasibility_costs_are_correct() const { template bool lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) const { - T r = (!this->m_settings.use_breakpoints_in_feasibility_search)? -one_of_type(): one_of_type(); + T r = -one_of_type(); switch (this->m_column_types[j]) { case column_type::fixed: diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index 8a4359806a3..f4597da762c 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -34,7 +34,6 @@ template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver::solve_with_tableau(); template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); -template void lp::lp_primal_core_solver::clear_breakpoints(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, double const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 38a2aa5f5e0..0a182ad9175 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -47,8 +47,6 @@ class lp_primal_core_solver:public lp_core_solver_base { unsigned m_column_norm_update_counter; T m_enter_price_eps; int m_sign_of_entering_delta; - vector> m_breakpoints; - binary_heap_priority_queue m_breakpoint_indices_queue; indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; @@ -545,8 +543,6 @@ class lp_primal_core_solver:public lp_core_solver_base { void try_add_breakpoint_in_row(unsigned i); - void clear_breakpoints(); - void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 3967b6e6698..5d4a662cff7 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -112,8 +112,6 @@ template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { if (numeric_traits::precise()) return column_is_benefitial_for_entering_basis_precise(j); - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; switch (this->m_column_types[j]) { case column_type::fixed: break; @@ -146,8 +144,6 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsign template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { lp_assert (numeric_traits::precise()); - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { @@ -219,8 +215,6 @@ int lp_primal_core_solver::choose_entering_column_presize(unsigned number_ return -1; unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = -m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; @@ -259,8 +253,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef if (entering_iter != m_non_basis_list.end()) { unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = - m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; @@ -268,28 +260,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef return -1; } -template int lp_primal_core_solver::advance_on_sorted_breakpoints(unsigned entering, X &t) { - T slope_at_entering = this->m_d[entering]; - breakpoint * last_bp = nullptr; - lp_assert(m_breakpoint_indices_queue.is_empty()==false); - while (m_breakpoint_indices_queue.is_empty() == false) { - unsigned bi = m_breakpoint_indices_queue.dequeue(); - breakpoint *b = &m_breakpoints[bi]; - change_slope_on_breakpoint(entering, b, slope_at_entering); - last_bp = b; - if (slope_at_entering * m_sign_of_entering_delta > - m_epsilon_of_reduced_cost) { // the slope started to increase infeasibility - break; - } else { - if ((numeric_traits::precise() == false) || ( numeric_traits::is_zero(slope_at_entering) && this->m_settings.random_next() % 2 == 0)) { - // it is not cost beneficial to advance the delta more, so just break to increase the randomness - break; - } - } - } - lp_assert (last_bp != nullptr); - t = last_bp->m_delta; - return last_bp->m_j; -} template int @@ -406,8 +376,6 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { } template int lp_primal_core_solver::find_leaving_and_t_precise(unsigned entering, X & t) { - if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) - return find_leaving_and_t_with_breakpoints(entering, t); bool unlimited = true; unsigned steps = this->m_ed.m_index.size(); unsigned k = this->m_settings.random_next() % steps; @@ -472,8 +440,6 @@ template int lp_primal_core_solver::find_leaving_ template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { - if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) - return find_leaving_and_t_with_breakpoints(entering, t); X theta = zero_of_type(); bool unlimited = get_harris_theta(theta); lp_assert(unlimited || theta >= zero_of_type()); @@ -584,7 +550,7 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e this->m_d[j] -= dq * this->m_pivot_row[j]; } this->m_d[leaving] = -dq; - if (this->current_x_is_infeasible() && !this->m_settings.use_breakpoints_in_feasibility_search) { + if (this->current_x_is_infeasible()) { this->m_d[leaving] -= this->m_costs[leaving]; this->m_costs[leaving] = zero_of_type(); } @@ -833,14 +799,7 @@ template void lp_primal_core_solver::one_iteratio } - -template void lp_primal_core_solver::clear_breakpoints() { - m_breakpoints.clear(); - m_breakpoint_indices_queue.clear(); -} - template void lp_primal_core_solver::fill_breakpoints_array(unsigned entering) { - clear_breakpoints(); for (unsigned i : this->m_ed.m_index) try_add_breakpoint_in_row(i); @@ -924,9 +883,8 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const break; } - if (!this->m_settings.use_breakpoints_in_feasibility_search) { - ret = - ret; - } + ret = - ret; + return ret; } @@ -982,9 +940,8 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { } else { this->insert_column_into_inf_set(j); } - if (!this->m_settings.use_breakpoints_in_feasibility_search) { - this->m_costs[j] = - this->m_costs[j]; - } + this->m_costs[j] = - this->m_costs[j]; + } @@ -1009,10 +966,7 @@ template void lp_primal_core_solver::print_column } } -template void lp_primal_core_solver::add_breakpoint(unsigned j, X delta, breakpoint_type type) { - m_breakpoints.push_back(breakpoint(j, delta, type)); - m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta)); -} + // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index e742b0cd01a..b63c54bfd10 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -88,8 +88,6 @@ template int lp_primal_core_solver::choose_enteri return -1; unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = -m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; @@ -188,7 +186,7 @@ template void lp_primal_core_solver::advance_on_en return; } if (!is_zero(t)) { - if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search ) { + if (this->current_x_is_feasible() ) { if (m_sign_of_entering_delta == -1) t = -t; } @@ -297,8 +295,6 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - if (this->m_settings.use_breakpoints_in_feasibility_search) - m_breakpoint_indices_queue.resize(this->m_n()); if (!numeric_traits::precise()) { this->m_column_norm_update_counter = 0; init_column_norms(); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index cb60eebc9c5..38270230e7e 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -223,7 +223,6 @@ struct lp_settings { unsigned column_norms_update_frequency { 12000 }; bool scale_with_ratio { true }; double density_threshold { 0.7 }; - bool use_breakpoints_in_feasibility_search { false }; unsigned max_row_length_for_bound_propagation { 300 }; bool backup_costs { true }; unsigned column_number_threshold_for_using_lu_in_lar_solver { 4000 }; From 6201eda05531dc641e37c2f8812580441eaf5d37 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:59:32 -0800 Subject: [PATCH 487/597] rm breakpoints --- src/math/lp/breakpoint.h | 35 ------ src/math/lp/lar_core_solver.h | 6 - src/math/lp/lp_primal_core_solver.h | 66 +--------- src/math/lp/lp_primal_core_solver_def.h | 161 ------------------------ 4 files changed, 1 insertion(+), 267 deletions(-) delete mode 100644 src/math/lp/breakpoint.h diff --git a/src/math/lp/breakpoint.h b/src/math/lp/breakpoint.h deleted file mode 100644 index 40fab293f88..00000000000 --- a/src/math/lp/breakpoint.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -namespace lp { -enum breakpoint_type { - low_break, upper_break, fixed_break -}; -template -struct breakpoint { - unsigned m_j; // the basic column - breakpoint_type m_type; - X m_delta; - breakpoint(){} - breakpoint(unsigned j, X delta, breakpoint_type type):m_j(j), m_type(type), m_delta(delta) {} -}; -} diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 9168862c651..d9379cb9d3f 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -13,7 +13,6 @@ Copyright (c) 2017 Microsoft Corporation #include #include "math/lp/indexed_vector.h" #include "math/lp/binary_heap_priority_queue.h" -#include "math/lp/breakpoint.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" #include "math/lp/lar_solution_signature.h" @@ -96,11 +95,6 @@ class lar_core_solver { m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } - - void advance_on_sorted_breakpoints(unsigned entering); - - void change_slope_on_breakpoint(unsigned entering, breakpoint> * b, mpq & slope_at_entering); - bool row_is_infeasible(unsigned row); bool row_is_evidence(unsigned row); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 0a182ad9175..2c7360712d6 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -32,7 +32,6 @@ Revision History: #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/breakpoint.h" #include "math/lp/binary_heap_priority_queue.h" #include "math/lp/u_set.h" namespace lp { @@ -64,47 +63,7 @@ class lp_primal_core_solver:public lp_core_solver_base { int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); int choose_entering_column_tableau(); int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); - int find_leaving_and_t_with_breakpoints(unsigned entering, X & t); - // int find_inf_row() { - // // mimicing CLP : todo : use a heap - // int j = -1; - // for (unsigned k : this->m_inf_set.m_index) { - // if (k < static_cast(j)) - // j = static_cast(k); - // } - // if (j == -1) - // return -1; - // return this->m_basis_heading[j]; - // #if 0 - // vector choices; - // unsigned len = 100000000; - // for (unsigned j : this->m_inf_set.m_index) { - // int i = this->m_basis_heading[j]; - // lp_assert(i >= 0); - // unsigned row_len = this->m_A.m_rows[i].size(); - // if (row_len < len) { - // choices.clear(); - // choices.push_back(i); - // len = row_len; - // if (m_settings.random_next() % 10) break; - // } else if (row_len == len) { - // choices.push_back(i); - // if (m_settings.random_next() % 10) break; - // } - // } - - // if (choices.size() == 0) - // return -1; - - // if (choices.size() == 1) - // return choices[0]; - - // unsigned k = this->m_settings.random_next() % choices.size(); - // return choices[k]; - // #endif - // } - - + bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos // we have xbj = -aj * xj @@ -538,16 +497,7 @@ class lp_primal_core_solver:public lp_core_solver_base { if (this->current_x_is_feasible()) this->set_status(lp_status::OPTIMAL); } - - void fill_breakpoints_array(unsigned entering); - - void try_add_breakpoint_in_row(unsigned i); - - void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); - - - void decide_on_status_when_cannot_find_entering() { lp_assert(!need_to_switch_costs()); this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); @@ -772,10 +722,6 @@ class lp_primal_core_solver:public lp_core_solver_base { bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; - - bool column_is_benefitial_for_entering_on_breakpoints(unsigned j) const; - - bool can_enter_basis(unsigned j); bool done(); void init_infeasibility_costs(); @@ -785,26 +731,16 @@ class lp_primal_core_solver:public lp_core_solver_base { void init_infeasibility_costs_for_changed_basis_only(); void print_column(unsigned j, std::ostream & out); - void add_breakpoint(unsigned j, X delta, breakpoint_type type); - // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column - void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value); template bool same_sign_with_entering_delta(const L & a) { return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); } - bool lower_bounds_are_set() const override { return true; } - int advance_on_sorted_breakpoints(unsigned entering, X & t); - - std::string break_type_to_string(breakpoint_type type); - - void print_breakpoint(const breakpoint * b, std::ostream & out); - void print_bound_info_and_x(unsigned j, std::ostream & out); void init_infeasibility_after_update_x_if_inf(unsigned leaving) { diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 5d4a662cff7..7a027f8d38c 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -75,39 +75,6 @@ void lp_primal_core_solver::sort_non_basis() { } } -template -bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoints(unsigned j) const { - bool ret; - const T & d = this->m_d[j]; - switch (this->m_column_types[j]) { - case column_type::lower_bound: - lp_assert(this->x_is_at_lower_bound(j)); - ret = d < -m_epsilon_of_reduced_cost; - break; - case column_type::upper_bound: - lp_assert(this->x_is_at_upper_bound(j)); - ret = d > m_epsilon_of_reduced_cost; - break; - case column_type::fixed: - ret = false; - break; - case column_type::boxed: - { - bool lower_bound = this->x_is_at_lower_bound(j); - lp_assert(lower_bound || this->x_is_at_upper_bound(j)); - ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost); - } - break; - case column_type::free_column: - ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost; - break; - default: - lp_unreachable(); - ret = false; - break; - } - return ret; -} template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { if (numeric_traits::precise()) @@ -261,14 +228,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef } - -template int -lp_primal_core_solver::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){ - lp_assert(this->precise() == false); - fill_breakpoints_array(entering); - return advance_on_sorted_breakpoints(entering, t); -} - template bool lp_primal_core_solver::get_harris_theta(X & theta) { lp_assert(this->m_ed.is_OK()); bool unlimited = true; @@ -799,19 +758,6 @@ template void lp_primal_core_solver::one_iteratio } -template void lp_primal_core_solver::fill_breakpoints_array(unsigned entering) { - for (unsigned i : this->m_ed.m_index) - try_add_breakpoint_in_row(i); - - if (this->m_column_types[entering] == column_type::boxed) { - if (m_sign_of_entering_delta < 0) - add_breakpoint(entering, - this->bound_span(entering), low_break); - else - add_breakpoint(entering, this->bound_span(entering), upper_break); - } -} - - template bool lp_primal_core_solver::done() { if (this->get_status() == lp_status::OPTIMAL) return true; @@ -893,9 +839,6 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const template void lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { - // If j is a breakpoint column, then we set the cost zero. - // When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function - // set zero cost for each non-basis column if (this->m_basis_heading[j] < 0) { this->m_costs[j] = numeric_traits::zero(); this->remove_column_from_inf_set(j); @@ -966,110 +909,6 @@ template void lp_primal_core_solver::print_column } } - - -// j is the basic column, x is the value at x[j] -// d is the coefficient before m_entering in the row with j as the basis column -template void lp_primal_core_solver::try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) { - X diff = x - break_value; - if (is_zero(diff)) { - switch (break_type) { - case low_break: - if (!same_sign_with_entering_delta(d)) - return; // no breakpoint - break; - case upper_break: - if (same_sign_with_entering_delta(d)) - return; // no breakpoint - break; - default: break; - } - add_breakpoint(j, zero_of_type(), break_type); - return; - } - auto delta_j = diff / d; - if (same_sign_with_entering_delta(delta_j)) - add_breakpoint(j, delta_j, break_type); -} - -template std::string lp_primal_core_solver::break_type_to_string(breakpoint_type type) { - switch (type){ - case low_break: return "low_break"; - case upper_break: return "upper_break"; - case fixed_break: return "fixed_break"; - default: - lp_assert(false); - break; - } - return "type is not found"; -} - -template void lp_primal_core_solver::print_breakpoint(const breakpoint * b, std::ostream & out) { - out << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl; - print_bound_info_and_x(b->m_j, out); -} - - -template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { - if (b->m_j == entering) { - lp_assert(b->m_type != fixed_break && (!is_zero(b->m_delta))); - slope_at_entering += m_sign_of_entering_delta; - return; - } - - lp_assert(this->m_basis_heading[b->m_j] >= 0); - unsigned i_row = this->m_basis_heading[b->m_j]; - const T & d = - this->m_ed[i_row]; - if (numeric_traits::is_zero(d)) return; - - T delta = m_sign_of_entering_delta * abs(d); - switch (b->m_type) { - case fixed_break: - if (is_zero(b->m_delta)) { - slope_at_entering += delta; - } else { - slope_at_entering += 2 * delta; - } - break; - case low_break: - case upper_break: - slope_at_entering += delta; - break; - default: - lp_assert(false); - } -} - - -template void lp_primal_core_solver::try_add_breakpoint_in_row(unsigned i) { - lp_assert(i < this->m_m()); - const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row - if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x - unsigned j = this->m_basis[i]; - const X & x = this->m_x[j]; - switch (this->m_column_types[j]) { - case column_type::fixed: - try_add_breakpoint(j, x, d, fixed_break, this->m_lower_bounds[j]); - break; - case column_type::boxed: - try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); - try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); - break; - case column_type::lower_bound: - try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); - break; - case column_type::upper_bound: - try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); - break; - case column_type::free_column: - break; - default: - lp_assert(false); - break; - } -} - - template void lp_primal_core_solver::print_bound_info_and_x(unsigned j, std::ostream & out) { out << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_types[j]) << std::endl; out << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl; From a4189186cc5b3b9786ae8415f5faaa9874502097 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 10:23:52 -0800 Subject: [PATCH 488/597] rm dealing with doubles --- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 1 - src/math/lp/lp_core_solver_base_def.h | 17 --- src/math/lp/lp_primal_core_solver.h | 137 +++--------------- src/math/lp/lp_primal_core_solver_def.h | 103 +------------ .../lp/lp_primal_core_solver_tableau_def.h | 5 +- 6 files changed, 21 insertions(+), 245 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index b76976d371c..e87dc5dd2f6 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -83,9 +83,6 @@ template std::string lp::lp_core_solver_base::column_name(unsi template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 5f723c1185f..fff50090591 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -416,7 +416,6 @@ class lp_core_solver_base { non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 8b87728e090..c5910f4274c 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -549,23 +549,6 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } -template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { - const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; - const T & row_p = this->m_pivot_row[entering]; - if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero - // the pivots have to have the same sign - if (column_p < 0) { - if (row_p > 0) - return 2; - } else { // column_p > 0 - if (row_p < 0) - return 2; - } - T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); - if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) - return 1; - return 0; -} template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 2c7360712d6..568eefa9829 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -43,20 +43,16 @@ class lp_primal_core_solver:public lp_core_solver_base { public: // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise - unsigned m_column_norm_update_counter; T m_enter_price_eps; int m_sign_of_entering_delta; - indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; - T m_converted_harris_eps; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; u_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; - // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); std::list m_non_basis_list; void sort_non_basis(); void sort_non_basis_rational(); @@ -277,14 +273,9 @@ class lp_primal_core_solver:public lp_core_solver_base { return convert_struct::convert(std::numeric_limits::max()); } - bool get_harris_theta(X & theta); - - void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } - void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } - int find_leaving_on_harris_theta(X const & harris_theta, X & t); + bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t(unsigned entering, X & t); int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); @@ -318,10 +309,7 @@ class lp_primal_core_solver:public lp_core_solver_base { lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; - - X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; - } - + void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); vector m_lower_bounds_dummy; // needed for the base class only @@ -514,10 +502,7 @@ class lp_primal_core_solver:public lp_core_solver_base { // } void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0); - const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); - limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); - if (theta < zero_of_type()) theta = zero_of_type(); + lp_assert(false); } bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { @@ -531,16 +516,7 @@ class lp_primal_core_solver:public lp_core_solver_base { theta = zero_of_type(); unlimited = false; } - } else { - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - } + } return true; } @@ -555,16 +531,7 @@ class lp_primal_core_solver:public lp_core_solver_base { theta = zero_of_type(); unlimited = false; } - } else { - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - } + } return true; } @@ -576,82 +543,32 @@ class lp_primal_core_solver:public lp_core_solver_base { limit_theta((bound - x) / m, theta, unlimited); } } - else { - // x gets larger - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } - } + } void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - lp_assert(m < 0); - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } + lp_assert(false); + } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & lbound = this->m_lower_bounds[j]; - - if (this->below_bound(x, lbound)) { - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta((lbound - x + eps) / m, theta, unlimited); - } else { - const X & ubound = this->m_upper_bounds[j]; - if (this->below_bound(x, ubound)){ - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x + eps) / m, theta, unlimited); - } else if (!this->above_bound(x, ubound)) { - theta = zero_of_type(); - unlimited = false; - } - } + lp_assert(false); + } void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & ubound = this->m_upper_bounds[j]; - if (this->above_bound(x, ubound)) { - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x - eps) / m, theta, unlimited); - } else { - const X & lbound = this->m_lower_bounds[j]; - if (this->above_bound(x, lbound)){ - const X& eps = harris_eps_for_bound(lbound); - limit_theta((lbound - x - eps) / m, theta, unlimited); - } else if (!this->below_bound(x, lbound)) { - theta = zero_of_type(); - unlimited = false; - } - } + lp_assert(false); + } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0); - const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { - limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - unlimited = false; - } - } + lp_assert(false); + } void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - } + lp_assert(false); + } // j is a basic column or the entering, in any case x[j] has to stay feasible. @@ -722,14 +639,12 @@ class lp_primal_core_solver:public lp_core_solver_base { bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; - bool can_enter_basis(unsigned j); bool done(); void init_infeasibility_costs(); void init_infeasibility_cost_for_column(unsigned j); T get_infeasibility_cost_for_column(unsigned j) const; - void init_infeasibility_costs_for_changed_basis_only(); - + void print_column(unsigned j, std::ostream & out); // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column @@ -743,13 +658,6 @@ class lp_primal_core_solver:public lp_core_solver_base { void print_bound_info_and_x(unsigned j, std::ostream & out); - void init_infeasibility_after_update_x_if_inf(unsigned leaving) { - if (this->using_infeas_costs()) { - init_infeasibility_costs_for_changed_basis_only(); - this->m_costs[leaving] = zero_of_type(); - this->remove_column_from_inf_set(leaving); - } - } void init_inf_set() { this->clear_inf_set(); @@ -885,15 +793,10 @@ class lp_primal_core_solver:public lp_core_solver_base { column_type_array, lower_bound_values, upper_bound_values), - m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - if (!(numeric_traits::precise())) { - m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - } else { - m_converted_harris_eps = zero_of_type(); - } + this->set_status(lp_status::UNKNOWN); } @@ -919,9 +822,7 @@ class lp_primal_core_solver:public lp_core_solver_base { column_names, column_type_array, m_lower_bounds_dummy, - upper_bound_values), - m_beta(A.row_count()), - m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { + upper_bound_values) { lp_assert(initial_x_is_correct()); m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7a027f8d38c..22bd4db52e1 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -228,54 +228,8 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef } -template bool lp_primal_core_solver::get_harris_theta(X & theta) { - lp_assert(this->m_ed.is_OK()); - bool unlimited = true; - for (unsigned i : this->m_ed.m_index) { - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; - limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); - if (!unlimited && is_zero(theta)) break; - } - return unlimited; -} -template int lp_primal_core_solver:: -find_leaving_on_harris_theta(X const & harris_theta, X & t) { - int leaving = -1; - T pivot_abs_max = zero_of_type(); - // we know already that there is no bound flip on entering - // we also know that harris_theta is limited, so we will find a leaving - zero_harris_eps(); - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { - if (++k == steps) - k = 0; - continue; - } - X ratio; - unsigned j = this->m_basis[i]; - bool unlimited = true; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); - if ((!unlimited) && ratio <= harris_theta) { - if (leaving == -1 || abs(ed) > pivot_abs_max) { - t = ratio; - leaving = j; - pivot_abs_max = abs(ed); - } - } - if (++k == steps) k = 0; - } while (k != initial_k); - if (!this->precise()) - restore_harris_eps(); - return leaving; -} - template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, @@ -398,17 +352,6 @@ template int lp_primal_core_solver::find_leaving_ } -template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { - X theta = zero_of_type(); - bool unlimited = get_harris_theta(theta); - lp_assert(unlimited || theta >= zero_of_type()); - if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; - if (unlimited) - return -1; - return find_leaving_on_harris_theta(theta, t); -} - - // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) @@ -687,47 +630,10 @@ template void lp_primal_core_solver::init_column_ } } -// debug only -template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - lp_assert(false); -} -template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); - if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { - m_column_norm_update_counter = 0; - init_column_norms(); - } else { - m_column_norm_update_counter++; - update_column_norms(entering, leaving); - } -} -// following Swietanowski - A new steepest ... -template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - T pivot = this->m_pivot_row[entering]; - T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; - if (!numeric_traits::precise()) { - if (g_ent < T(0.000001)) - g_ent = T(0.000001); - } - this->m_column_norms[leaving] = g_ent; - for (unsigned j : this->m_pivot_row.m_index) { - if (j == leaving) - continue; - const T & t = this->m_pivot_row[j]; - T s = this->m_A.dot_product_with_column(m_beta.m_data, j); - T k = -2 / pivot; - T tp = t/pivot; - if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column - this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 - 1 + tp * tp); - } - } -} +// following Swietanowski - A new steepest ... template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { T r = numeric_traits::one(); @@ -771,13 +677,6 @@ template bool lp_primal_core_solver::done() { return false; } -template -void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { - for (unsigned i : this->m_ed.m_index) - init_infeasibility_cost_for_column(this->m_basis[i]); - this->set_using_infeas_costs(true); -} - template void lp_primal_core_solver::init_infeasibility_costs() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index b63c54bfd10..a63a6be17ad 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -295,10 +295,7 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } + if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); From d00fcc87c9a822d3a995f527e5bca69c400f60b0 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 10:56:44 -0800 Subject: [PATCH 489/597] Revert "rm dealing with doubles" This reverts commit 547254abe786c80231ca78bcd245e6ddb5a15c47. --- src/math/lp/lp_core_solver_base.cpp | 3 + src/math/lp/lp_core_solver_base.h | 1 + src/math/lp/lp_core_solver_base_def.h | 17 +++ src/math/lp/lp_primal_core_solver.h | 137 +++++++++++++++--- src/math/lp/lp_primal_core_solver_def.h | 103 ++++++++++++- .../lp/lp_primal_core_solver_tableau_def.h | 5 +- 6 files changed, 245 insertions(+), 21 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index e87dc5dd2f6..b76976d371c 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -83,6 +83,9 @@ template std::string lp::lp_core_solver_base::column_name(unsi template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); +template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; +template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; +template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index fff50090591..5f723c1185f 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -416,6 +416,7 @@ class lp_core_solver_base { non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; + int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index c5910f4274c..8b87728e090 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -549,6 +549,23 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } +template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { + const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; + const T & row_p = this->m_pivot_row[entering]; + if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero + // the pivots have to have the same sign + if (column_p < 0) { + if (row_p > 0) + return 2; + } else { // column_p > 0 + if (row_p < 0) + return 2; + } + T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); + if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) + return 1; + return 0; +} template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 568eefa9829..2c7360712d6 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -43,16 +43,20 @@ class lp_primal_core_solver:public lp_core_solver_base { public: // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise + unsigned m_column_norm_update_counter; T m_enter_price_eps; int m_sign_of_entering_delta; + indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; + T m_converted_harris_eps; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; u_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; + // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); std::list m_non_basis_list; void sort_non_basis(); void sort_non_basis_rational(); @@ -273,9 +277,14 @@ class lp_primal_core_solver:public lp_core_solver_base { return convert_struct::convert(std::numeric_limits::max()); } - + bool get_harris_theta(X & theta); + + void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } + void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } + int find_leaving_on_harris_theta(X const & harris_theta, X & t); bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); + int find_leaving_and_t(unsigned entering, X & t); int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); @@ -309,7 +318,10 @@ class lp_primal_core_solver:public lp_core_solver_base { lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; - + + X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; + } + void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); vector m_lower_bounds_dummy; // needed for the base class only @@ -502,7 +514,10 @@ class lp_primal_core_solver:public lp_core_solver_base { // } void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); + lp_assert(m < 0); + const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); + limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); + if (theta < zero_of_type()) theta = zero_of_type(); } bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { @@ -516,7 +531,16 @@ class lp_primal_core_solver:public lp_core_solver_base { theta = zero_of_type(); unlimited = false; } - } + } else { + const X& eps = harris_eps_for_bound(bound); + if (this->below_bound(x, bound)) return false; + if (this->above_bound(x, bound)) { + limit_theta((bound - x - eps) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + } return true; } @@ -531,7 +555,16 @@ class lp_primal_core_solver:public lp_core_solver_base { theta = zero_of_type(); unlimited = false; } - } + } else { + const X& eps = harris_eps_for_bound(bound); + if (this->above_bound(x, bound)) return false; + if (this->below_bound(x, bound)) { + limit_theta((bound - x + eps) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + } return true; } @@ -543,32 +576,82 @@ class lp_primal_core_solver:public lp_core_solver_base { limit_theta((bound - x) / m, theta, unlimited); } } - + else { + // x gets larger + lp_assert(m > 0); + const X& eps = harris_eps_for_bound(bound); + if (this->below_bound(x, bound)) { + limit_theta((bound - x + eps) / m, theta, unlimited); + } + } } void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - lp_assert(false); - + lp_assert(m < 0); + const X& eps = harris_eps_for_bound(bound); + if (this->above_bound(x, bound)) { + limit_theta((bound - x - eps) / m, theta, unlimited); + } } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); - + // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); + const X & x = this->m_x[j]; + const X & lbound = this->m_lower_bounds[j]; + + if (this->below_bound(x, lbound)) { + const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); + limit_theta((lbound - x + eps) / m, theta, unlimited); + } else { + const X & ubound = this->m_upper_bounds[j]; + if (this->below_bound(x, ubound)){ + const X& eps = harris_eps_for_bound(ubound); + limit_theta((ubound - x + eps) / m, theta, unlimited); + } else if (!this->above_bound(x, ubound)) { + theta = zero_of_type(); + unlimited = false; + } + } } void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); - + // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); + const X & x = this->m_x[j]; + const X & ubound = this->m_upper_bounds[j]; + if (this->above_bound(x, ubound)) { + const X& eps = harris_eps_for_bound(ubound); + limit_theta((ubound - x - eps) / m, theta, unlimited); + } else { + const X & lbound = this->m_lower_bounds[j]; + if (this->above_bound(x, lbound)){ + const X& eps = harris_eps_for_bound(lbound); + limit_theta((lbound - x - eps) / m, theta, unlimited); + } else if (!this->below_bound(x, lbound)) { + theta = zero_of_type(); + unlimited = false; + } + } } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); - + lp_assert(m > 0); + const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); + if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { + limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + if (theta < zero_of_type()) { + theta = zero_of_type(); + unlimited = false; + } + } } void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - lp_assert(false); - + lp_assert(m > 0); + const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); + limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + if (theta < zero_of_type()) { + theta = zero_of_type(); + } } // j is a basic column or the entering, in any case x[j] has to stay feasible. @@ -639,12 +722,14 @@ class lp_primal_core_solver:public lp_core_solver_base { bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; + bool can_enter_basis(unsigned j); bool done(); void init_infeasibility_costs(); void init_infeasibility_cost_for_column(unsigned j); T get_infeasibility_cost_for_column(unsigned j) const; - + void init_infeasibility_costs_for_changed_basis_only(); + void print_column(unsigned j, std::ostream & out); // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column @@ -658,6 +743,13 @@ class lp_primal_core_solver:public lp_core_solver_base { void print_bound_info_and_x(unsigned j, std::ostream & out); + void init_infeasibility_after_update_x_if_inf(unsigned leaving) { + if (this->using_infeas_costs()) { + init_infeasibility_costs_for_changed_basis_only(); + this->m_costs[leaving] = zero_of_type(); + this->remove_column_from_inf_set(leaving); + } + } void init_inf_set() { this->clear_inf_set(); @@ -793,10 +885,15 @@ class lp_primal_core_solver:public lp_core_solver_base { column_type_array, lower_bound_values, upper_bound_values), + m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - + if (!(numeric_traits::precise())) { + m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); + } else { + m_converted_harris_eps = zero_of_type(); + } this->set_status(lp_status::UNKNOWN); } @@ -822,7 +919,9 @@ class lp_primal_core_solver:public lp_core_solver_base { column_names, column_type_array, m_lower_bounds_dummy, - upper_bound_values) { + upper_bound_values), + m_beta(A.row_count()), + m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { lp_assert(initial_x_is_correct()); m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 22bd4db52e1..7a027f8d38c 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -228,8 +228,54 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef } +template bool lp_primal_core_solver::get_harris_theta(X & theta) { + lp_assert(this->m_ed.is_OK()); + bool unlimited = true; + for (unsigned i : this->m_ed.m_index) { + if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; + limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); + if (!unlimited && is_zero(theta)) break; + } + return unlimited; +} +template int lp_primal_core_solver:: +find_leaving_on_harris_theta(X const & harris_theta, X & t) { + int leaving = -1; + T pivot_abs_max = zero_of_type(); + // we know already that there is no bound flip on entering + // we also know that harris_theta is limited, so we will find a leaving + zero_harris_eps(); + unsigned steps = this->m_ed.m_index.size(); + unsigned k = this->m_settings.random_next() % steps; + unsigned initial_k = k; + do { + unsigned i = this->m_ed.m_index[k]; + const T & ed = this->m_ed[i]; + if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { + if (++k == steps) + k = 0; + continue; + } + X ratio; + unsigned j = this->m_basis[i]; + bool unlimited = true; + limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); + if ((!unlimited) && ratio <= harris_theta) { + if (leaving == -1 || abs(ed) > pivot_abs_max) { + t = ratio; + leaving = j; + pivot_abs_max = abs(ed); + } + } + if (++k == steps) k = 0; + } while (k != initial_k); + if (!this->precise()) + restore_harris_eps(); + return leaving; +} + template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, @@ -352,6 +398,17 @@ template int lp_primal_core_solver::find_leaving_ } +template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { + X theta = zero_of_type(); + bool unlimited = get_harris_theta(theta); + lp_assert(unlimited || theta >= zero_of_type()); + if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; + if (unlimited) + return -1; + return find_leaving_on_harris_theta(theta, t); +} + + // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) @@ -630,10 +687,47 @@ template void lp_primal_core_solver::init_column_ } } +// debug only +template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { + lp_assert(false); +} - +template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { + lp_assert(numeric_traits::precise() == false); + lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); + if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { + m_column_norm_update_counter = 0; + init_column_norms(); + } else { + m_column_norm_update_counter++; + update_column_norms(entering, leaving); + } +} // following Swietanowski - A new steepest ... +template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { + lp_assert(numeric_traits::precise() == false); + T pivot = this->m_pivot_row[entering]; + T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; + if (!numeric_traits::precise()) { + if (g_ent < T(0.000001)) + g_ent = T(0.000001); + } + this->m_column_norms[leaving] = g_ent; + + for (unsigned j : this->m_pivot_row.m_index) { + if (j == leaving) + continue; + const T & t = this->m_pivot_row[j]; + T s = this->m_A.dot_product_with_column(m_beta.m_data, j); + T k = -2 / pivot; + T tp = t/pivot; + if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column + this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 + 1 + tp * tp); + } + } +} template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { T r = numeric_traits::one(); @@ -677,6 +771,13 @@ template bool lp_primal_core_solver::done() { return false; } +template +void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { + for (unsigned i : this->m_ed.m_index) + init_infeasibility_cost_for_column(this->m_basis[i]); + this->set_using_infeas_costs(true); +} + template void lp_primal_core_solver::init_infeasibility_costs() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index a63a6be17ad..b63c54bfd10 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -295,7 +295,10 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - + if (!numeric_traits::precise()) { + this->m_column_norm_update_counter = 0; + init_column_norms(); + } if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); From 2e9dc3d090ba3b91c3996cb9742d2b9696a08ddb Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 11:01:39 -0800 Subject: [PATCH 490/597] rm lu Signed-off-by: Lev Nachmanson --- src/test/lp/lp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 1a2ee2338b6..c386cf002d9 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -49,7 +49,6 @@ #include "math/lp/matrix.h" #include "math/lp/hnf.h" #include "math/lp/square_sparse_matrix_def.h" -#include "math/lp/lu_def.h" #include "math/lp/general_matrix.h" #include "math/lp/lp_bound_propagator.h" #include "math/lp/nla_solver.h" From e04e726f45c375c9b4efd0c621812e5782ec71d2 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 11:38:45 -0800 Subject: [PATCH 491/597] rm lu --- src/math/lp/lar_core_solver.h | 9 +-------- src/math/lp/lar_core_solver_def.h | 29 +---------------------------- src/math/lp/lar_solver.cpp | 4 ---- 3 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index d9379cb9d3f..d40572b9e24 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -56,8 +56,6 @@ class lar_core_solver { lp_primal_core_solver> m_r_solver; // solver in rational numbers - - lp_primal_core_solver m_d_solver; // solver in doubles lar_core_solver( lp_settings & settings, @@ -140,7 +138,6 @@ class lar_core_solver { void push() { lp_assert(m_r_solver.basis_heading_is_correct()); - lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); lp_assert(m_column_types.size() == m_r_A.column_count()); m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy.push(); @@ -196,13 +193,9 @@ class lar_core_solver { m_stacked_simplex_strategy.pop(k); settings().set_simplex_strategy(m_stacked_simplex_strategy); lp_assert(m_r_solver.basis_heading_is_correct()); - lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); - } - - bool need_to_presolve_with_double_solver() const { - return false; } + template bool is_zero_vector(const vector & b) { for (const L & m: b) diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index fe99a202f71..419345f0ca0 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -31,18 +31,6 @@ lar_core_solver::lar_core_solver( m_r_lower_bounds(), m_r_upper_bounds(), settings, - column_names), - m_d_solver(m_d_A, - m_d_right_sides_dummy, - m_d_x, - m_d_basis, - m_d_nbasis, - m_d_heading, - m_d_costs_dummy, - m_column_types(), - m_d_lower_bounds, - m_d_upper_bounds, - settings, column_names) { } @@ -57,22 +45,7 @@ void lar_core_solver::prefix_r() { } } -void lar_core_solver::prefix_d() { - // m_d_solver.m_b.resize(m_d_solver.m_m()); - m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); - m_d_solver.m_costs.resize(m_d_solver.m_n()); - m_d_solver.m_d.resize(m_d_solver.m_n()); - m_d_solver.m_ed.resize(m_d_solver.m_m()); - m_d_solver.m_pivot_row.resize(m_d_solver.m_n()); - m_d_solver.m_pivot_row_of_B_1.resize(m_d_solver.m_m()); - m_d_solver.m_w.resize(m_d_solver.m_m()); - m_d_solver.m_y.resize(m_d_solver.m_m()); - m_d_solver.m_steepest_edge_coefficients.resize(m_d_solver.m_n()); - m_d_solver.m_column_norms.clear(); - m_d_solver.m_column_norms.resize(m_d_solver.m_n(), 2); - m_d_solver.clear_inf_set(); - m_d_solver.resize_inf_set(m_d_solver.m_n()); -} + void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 48c575184ba..f890b27fa43 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -47,7 +47,6 @@ namespace lp { bool lar_solver::sizes_are_correct() const { - lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); @@ -748,9 +747,6 @@ namespace lp { void lar_solver::solve_with_core_solver() { - if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { - add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); - } m_mpq_lar_core_solver.prefix_r(); if (costs_are_used()) { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); From 6eedbd4f35d6cee4c2c50ebc0cca88135ac22a38 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 13:40:21 -0800 Subject: [PATCH 492/597] rm lu --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/square_dense_submatrix.cpp | 48 -- src/math/lp/square_dense_submatrix.h | 225 --------- src/math/lp/square_dense_submatrix_def.h | 370 -------------- src/test/lp/lp.cpp | 592 +---------------------- 5 files changed, 4 insertions(+), 1232 deletions(-) delete mode 100644 src/math/lp/square_dense_submatrix.cpp delete mode 100644 src/math/lp/square_dense_submatrix.h delete mode 100644 src/math/lp/square_dense_submatrix_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 97073736717..5d21bba70ba 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -42,7 +42,6 @@ z3_add_component(lp random_updater.cpp row_eta_matrix.cpp scaler.cpp - square_dense_submatrix.cpp square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES diff --git a/src/math/lp/square_dense_submatrix.cpp b/src/math/lp/square_dense_submatrix.cpp deleted file mode 100644 index 4d9fcec131e..00000000000 --- a/src/math/lp/square_dense_submatrix.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/square_dense_submatrix_def.h" -template void lp::square_dense_submatrix::init(lp::square_sparse_matrix*, unsigned int); -template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); -template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix::is_L_matrix() const; -template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); -template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); -template lp::square_dense_submatrix >::square_dense_submatrix(lp::square_sparse_matrix >*, unsigned int); -template void lp::square_dense_submatrix >::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix >::is_L_matrix() const; -template void lp::square_dense_submatrix >::conjugate_by_permutation(lp::permutation_matrix >&); -template int lp::square_dense_submatrix >::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix >::pivot(unsigned int, lp::lp_settings&); -#ifdef Z3DEBUG -template double lp::square_dense_submatrix::get_elem(unsigned int, unsigned int) const; -#endif -template void lp::square_dense_submatrix::apply_from_right(vector&); - -template void lp::square_dense_submatrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::square_dense_submatrix::apply_from_left_to_vector(vector&); -template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); -template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix::is_L_matrix() const; -template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); -template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); diff --git a/src/math/lp/square_dense_submatrix.h b/src/math/lp/square_dense_submatrix.h deleted file mode 100644 index 308f9eadc63..00000000000 --- a/src/math/lp/square_dense_submatrix.h +++ /dev/null @@ -1,225 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/permutation_matrix.h" -#include -#include "math/lp/static_matrix.h" -#include -#include -#include -#include -#include -#include "math/lp/indexed_value.h" -#include "math/lp/indexed_vector.h" -#include -#include "math/lp/lp_settings.h" -#include "math/lp/eta_matrix.h" -#include "math/lp/binary_heap_upair_queue.h" -#include "math/lp/square_sparse_matrix.h" -namespace lp { -template -class square_dense_submatrix : public tail_matrix { - // the submatrix uses the permutations of the parent matrix to access the elements - struct ref { - unsigned m_i_offset; - square_dense_submatrix & m_s; - ref(unsigned i, square_dense_submatrix & s) : - m_i_offset((i - s.m_index_start) * s.m_dim), m_s(s){} - T & operator[] (unsigned j) { - lp_assert(j >= m_s.m_index_start); - return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; - } - const T & operator[] (unsigned j) const { - lp_assert(j >= m_s.m_index_start); - return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; - } - }; -public: - unsigned m_index_start; - unsigned m_dim; - vector m_v; - square_sparse_matrix * m_parent; - permutation_matrix m_row_permutation; - indexed_vector m_work_vector; -public: - permutation_matrix m_column_permutation; - bool is_active() const { return m_parent != nullptr; } - - square_dense_submatrix() {} - - square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start); - - void init(square_sparse_matrix *parent_matrix, unsigned index_start); - - bool is_dense() const override { return true; } - - ref operator[] (unsigned i) { - lp_assert(i >= m_index_start); - lp_assert(i < m_parent->dimension()); - return ref(i, *this); - } - - int find_pivot_column_in_row(unsigned i) const; - - void swap_columns(unsigned i, unsigned j) { - if (i != j) - m_column_permutation.transpose_from_left(i, j); - } - - unsigned adjust_column(unsigned col) const{ - if (col >= m_column_permutation.size()) - return col; - return m_column_permutation.apply_reverse(col); - } - - unsigned adjust_column_inverse(unsigned col) const{ - if (col >= m_column_permutation.size()) - return col; - return m_column_permutation[col]; - } - unsigned adjust_row(unsigned row) const{ - if (row >= m_row_permutation.size()) - return row; - return m_row_permutation[row]; - } - - unsigned adjust_row_inverse(unsigned row) const{ - if (row >= m_row_permutation.size()) - return row; - return m_row_permutation.apply_reverse(row); - } - - void pivot(unsigned i, lp_settings & settings); - - void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings);; - - void divide_row_by_pivot(unsigned i); - - void update_parent_matrix(lp_settings & settings); - - void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings); - - void push_new_elements_to_parent_matrix(lp_settings & settings); - - template - L row_by_vector_product(unsigned i, const vector & v); - - template - L column_by_vector_product(unsigned j, const vector & v); - - template - L row_by_indexed_vector_product(unsigned i, const indexed_vector & v); - - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings); - - template - void apply_from_left_to_vector(vector & w); - - bool is_L_matrix() const; - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local(w, settings); - } - - - - void apply_from_right(indexed_vector & w) override { -#if 1==0 - indexed_vector wcopy = w; - apply_from_right(wcopy.m_data); - wcopy.m_index.clear(); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (!is_zero(wcopy.m_data[i])) - wcopy.m_index.push_back(i); - } - } else { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - T & v = wcopy.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)){ - wcopy.m_index.push_back(i); - } else { - v = zero_of_type(); - } - } - } - lp_assert(wcopy.is_OK()); - apply_from_right(w.m_data); - w.m_index.clear(); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (!is_zero(w.m_data[i])) - w.m_index.push_back(i); - } - } else { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - T & v = w.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)){ - w.m_index.push_back(i); - } else { - v = zero_of_type(); - } - } - } -#else - lp_assert(w.is_OK()); - lp_assert(m_work_vector.is_OK()); - m_work_vector.resize(w.data_size()); - m_work_vector.clear(); - lp_assert(m_work_vector.is_OK()); - unsigned end = m_index_start + m_dim; - for (unsigned k : w.m_index) { - // find j such that k = adjust_row_inverse(j) - unsigned j = adjust_row(k); - if (j < m_index_start || j >= end) { - m_work_vector.set_value(w[k], adjust_column_inverse(j)); - } else { // j >= m_index_start and j < end - unsigned offset = (j - m_index_start) * m_dim; // this is the row start - const T& wv = w[k]; - for (unsigned col = m_index_start; col < end; col++, offset ++) { - unsigned adj_col = adjust_column_inverse(col); - m_work_vector.add_value_at_index(adj_col, m_v[offset] * wv); - } - } - } - m_work_vector.clean_up(); - lp_assert(m_work_vector.is_OK()); - w = m_work_vector; -#endif - } - void apply_from_left(vector & w, lp_settings & /*settings*/) override { - apply_from_left_to_vector(w);// , settings); - } - - void apply_from_right(vector & w) override; - -#ifdef Z3DEBUG - T get_elem (unsigned i, unsigned j) const override; - unsigned row_count() const override { return m_parent->row_count();} - unsigned column_count() const override { return row_count();} - void set_number_of_rows(unsigned) override {} - void set_number_of_columns(unsigned) override {} -#endif - void conjugate_by_permutation(permutation_matrix & q); -}; -} diff --git a/src/math/lp/square_dense_submatrix_def.h b/src/math/lp/square_dense_submatrix_def.h deleted file mode 100644 index 3a9006b4d9e..00000000000 --- a/src/math/lp/square_dense_submatrix_def.h +++ /dev/null @@ -1,370 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/square_dense_submatrix.h" -namespace lp { -template -square_dense_submatrix::square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start) : - m_index_start(index_start), - m_dim(parent_matrix->dimension() - index_start), - m_v(m_dim * m_dim), - m_parent(parent_matrix), - m_row_permutation(m_parent->dimension()), - m_column_permutation(m_parent->dimension()) { - int row_offset = - static_cast(m_index_start); - for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { - unsigned row = parent_matrix->adjust_row(i); - for (auto & iv : parent_matrix->get_row_values(row)) { - unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - lp_assert(j>= m_index_start); - m_v[row_offset + j] = iv.m_value; - } - row_offset += m_dim; - } -} - -template void square_dense_submatrix::init(square_sparse_matrix *parent_matrix, unsigned index_start) { - m_index_start = index_start; - m_dim = parent_matrix->dimension() - index_start; - m_v.resize(m_dim * m_dim); - m_parent = parent_matrix; - m_column_permutation.init(m_parent->dimension()); - for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { - unsigned row = parent_matrix->adjust_row(i); - for (auto & iv : parent_matrix->get_row_values(row)) { - unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - (*this)[i][j] = iv.m_value; - } - } -} - -template int square_dense_submatrix::find_pivot_column_in_row(unsigned i) const { - int j = -1; - T max = zero_of_type(); - lp_assert(i >= m_index_start); - unsigned row_start = (i - m_index_start) * m_dim; - for (unsigned k = i; k < m_parent->dimension(); k++) { - unsigned col = adjust_column(k); // this is where the column is in the row - unsigned offs = row_start + col - m_index_start; - T t = abs(m_v[offs]); - if (t > max) { - j = k; - max = t; - } - } - return j; -} - -template void square_dense_submatrix::pivot(unsigned i, lp_settings & settings) { - divide_row_by_pivot(i); - for (unsigned k = i + 1; k < m_parent->dimension(); k++) - pivot_row_to_row(i, k, settings); -} - -template void square_dense_submatrix::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) { - lp_assert(i < row); - unsigned pj = adjust_column(i); // the pivot column - unsigned pjd = pj - m_index_start; - unsigned pivot_row_offset = (i-m_index_start)*m_dim; - T pivot = m_v[pivot_row_offset + pjd]; - unsigned row_offset= (row-m_index_start)*m_dim; - T m = m_v[row_offset + pjd]; - lp_assert(!is_zero(pivot)); - m_v[row_offset + pjd] = -m * pivot; // creating L matrix - for (unsigned j = m_index_start; j < m_parent->dimension(); j++) { - if (j == pj) { - pivot_row_offset++; - row_offset++; - continue; - } - auto t = m_v[row_offset] - m_v[pivot_row_offset] * m; - if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { - m_v[row_offset] = zero_of_type(); - } else { - m_v[row_offset] = t; - } - row_offset++; pivot_row_offset++; - // at the same time we pivot the L too - } -} - -template void square_dense_submatrix::divide_row_by_pivot(unsigned i) { - unsigned pj = adjust_column(i); // the pivot column - unsigned irow_offset = (i - m_index_start) * m_dim; - T pivot = m_v[irow_offset + pj - m_index_start]; - lp_assert(!is_zero(pivot)); - for (unsigned k = m_index_start; k < m_parent->dimension(); k++) { - if (k == pj){ - m_v[irow_offset++] = one_of_type() / pivot; // creating the L matrix diagonal - continue; - } - m_v[irow_offset++] /= pivot; - } -} - -template void square_dense_submatrix::update_parent_matrix(lp_settings & settings) { - for (unsigned i = m_index_start; i < m_parent->dimension(); i++) - update_existing_or_delete_in_parent_matrix_for_row(i, settings); - push_new_elements_to_parent_matrix(settings); - for (unsigned i = m_index_start; i < m_parent->dimension(); i++) - m_parent->set_max_in_row(m_parent->adjust_row(i)); -} - -template void square_dense_submatrix::update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) { - bool diag_updated = false; - unsigned ai = m_parent->adjust_row(i); - auto & row_vals = m_parent->get_row_values(ai); - for (unsigned k = 0; k < row_vals.size(); k++) { - auto & iv = row_vals[k]; - unsigned j = m_parent->adjust_column_inverse(iv.m_index); - if (j < i) { - m_parent->remove_element(row_vals, iv); - k--; - } else if (i == j) { - m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type()); - diag_updated = true; - } else { // j > i - T & v = (*this)[i][j]; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - m_parent->remove_element(row_vals, iv); - k--; - } else { - m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v); - v = zero_of_type(); // only new elements are left above the diagonal - } - } - } - if (!diag_updated) { - unsigned aj = m_parent->adjust_column(i); - m_parent->add_new_element(ai, aj, one_of_type()); - } -} - -template void square_dense_submatrix::push_new_elements_to_parent_matrix(lp_settings & settings) { - for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) { - unsigned ai = m_parent->adjust_row(i); - for (unsigned j = i + 1; j < m_parent->dimension(); j++) { - T & v = (*this)[i][j]; - if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) { - unsigned aj = m_parent->adjust_column(j); - m_parent->add_new_element(ai, aj, v); - } - v = zero_of_type(); // leave only L elements now - } - } -} -template -template -L square_dense_submatrix::row_by_vector_product(unsigned i, const vector & v) { - lp_assert(i >= m_index_start); - - unsigned row_in_subm = i - m_index_start; - unsigned row_offset = row_in_subm * m_dim; - L r = zero_of_type(); - for (unsigned j = 0; j < m_dim; j++) - r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; - return r; -} - -template -template -L square_dense_submatrix::column_by_vector_product(unsigned j, const vector & v) { - lp_assert(j >= m_index_start); - - unsigned offset = j - m_index_start; - L r = zero_of_type(); - for (unsigned i = 0; i < m_dim; i++, offset += m_dim) - r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)]; - return r; -} -template -template -L square_dense_submatrix::row_by_indexed_vector_product(unsigned i, const indexed_vector & v) { - lp_assert(i >= m_index_start); - - unsigned row_in_subm = i - m_index_start; - unsigned row_offset = row_in_subm * m_dim; - L r = zero_of_type(); - for (unsigned j = 0; j < m_dim; j++) - r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; - return r; -} -template -template -void square_dense_submatrix::apply_from_left_local(indexed_vector & w, lp_settings & settings) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // vector deb_w(w.m_data.size()); - // for (unsigned i = 0; i < w.m_data.size(); i++) - // deb_w[i] = w[i]; - - // deb.apply_from_left(deb_w); -#endif // use indexed vector here - -#ifndef DO_NOT_USE_INDEX - vector t(m_parent->dimension(), zero_of_type()); - for (auto k : w.m_index) { - unsigned j = adjust_column(k); // k-th element will contribute only to column j - if (j < m_index_start || j >= this->m_index_start + this->m_dim) { // it is a unit matrix outside - t[adjust_row_inverse(j)] = w[k]; - } else { - const L & v = w[k]; - for (unsigned i = 0; i < m_dim; i++) { - unsigned row = adjust_row_inverse(m_index_start + i); - unsigned offs = i * m_dim + j - m_index_start; - t[row] += m_v[offs] * v; - } - } - } - w.m_index.clear(); - for (unsigned i = 0; i < m_parent->dimension(); i++) { - const L & v = t[i]; - if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){ - w.m_index.push_back(i); - w.m_data[i] = v; - } else { - w.m_data[i] = zero_of_type(); - } - } -#else - vector t(m_parent->dimension()); - for (unsigned i = 0; i < m_index_start; i++) { - t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; - } - for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ - t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - w.set_value(t[i], i); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - const L & v = t[i]; - if (!is_zero(v)) - w.m_index.push_back(i); - w.m_data[i] = v; - } -#endif -#ifdef Z3DEBUG - // cout << "w final" << endl; - // print_vector(w.m_data); - // lp_assert(vectors_are_equal(deb_w, w.m_data)); - // lp_assert(w.is_OK()); -#endif -} - -template -template -void square_dense_submatrix::apply_from_left_to_vector(vector & w) { - // lp_settings & settings) { - // dense_matrix deb(*this); - // vector deb_w(w); - // deb.apply_from_left_to_X(deb_w, settings); - // // cout << "deb" << endl; - // // print_matrix(deb); - // // cout << "w" << endl; - // // print_vector(w.m_data); - // // cout << "deb_w" << endl; - // // print_vector(deb_w); - vector t(m_parent->dimension()); - for (unsigned i = 0; i < m_index_start; i++) { - t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; - } - for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ - t[adjust_row_inverse(i)] = row_by_vector_product(i, w); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - w[i] = t[i]; - } -#ifdef Z3DEBUG - // cout << "w final" << endl; - // print_vector(w.m_data); - // lp_assert(vectors_are_equal(deb_w, w)); -#endif -} - -template bool square_dense_submatrix::is_L_matrix() const { -#ifdef Z3DEBUG - lp_assert(m_row_permutation.is_identity()); - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (i < m_index_start) { - lp_assert(m_column_permutation[i] == i); - continue; - } - unsigned row_offs = (i-m_index_start)*m_dim; - for (unsigned k = 0; k < m_dim; k++) { - unsigned j = m_index_start + k; - unsigned jex = adjust_column_inverse(j); - if (jex > i) { - lp_assert(is_zero(m_v[row_offs + k])); - } else if (jex == i) { - lp_assert(!is_zero(m_v[row_offs + k])); - } - } - } -#endif - return true; -} - -template void square_dense_submatrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // vector deb_w(w); - // deb.apply_from_right(deb_w); -#endif - vector t(w.size()); - - for (unsigned j = 0; j < m_index_start; j++) { - t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; - } - unsigned end = m_index_start + m_dim; - for (unsigned j = end; j < m_parent->dimension(); j++) { - t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; - } - for (unsigned j = m_index_start; j < end; j++) { - t[adjust_column_inverse(j)] = column_by_vector_product(j, w); - } - w = t; -#ifdef Z3DEBUG - // lp_assert(vector_are_equal(deb_w, w)); -#endif -} - - - - -#ifdef Z3DEBUG - -template T square_dense_submatrix::get_elem (unsigned i, unsigned j) const { - i = adjust_row(i); - j = adjust_column(j); - if (i < m_index_start || j < m_index_start) - return i == j? one_of_type() : zero_of_type(); - unsigned offs = (i - m_index_start)* m_dim + j - m_index_start; - return m_v[offs]; -} - -#endif -template void square_dense_submatrix::conjugate_by_permutation(permutation_matrix & q) { - m_row_permutation.multiply_by_permutation_from_left(q); - m_column_permutation.multiply_by_reverse_from_right(q); -} -} diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c386cf002d9..2e8faeaedc5 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -48,7 +48,6 @@ #include "test/lp/gomory_test.h" #include "math/lp/matrix.h" #include "math/lp/hnf.h" -#include "math/lp/square_sparse_matrix_def.h" #include "math/lp/general_matrix.h" #include "math/lp/lp_bound_propagator.h" #include "math/lp/nla_solver.h" @@ -56,6 +55,8 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" +#include "math/lp/static_matrix.h" +#include "math/lp/dense_matrix.h" bool my_white_space(const char & a) { return a == ' ' || a == '\t'; @@ -388,122 +389,6 @@ struct simple_column_namer:public column_namer }; -template -void test_matrix(square_sparse_matrix & a) { - auto m = a.dimension(); - - // copy a to b in the reversed order - square_sparse_matrix b(m, m); - std::cout << "copy b to a"<< std::endl; - for (int row = m - 1; row >= 0; row--) - for (int col = m - 1; col >= 0; col --) { - b(row, col) = (T const&) a(row, col); - } - - - std::cout << "zeroing b in the reverse order"<< std::endl; - for (int row = m - 1; row >= 0; row--) - for (int col = m - 1; col >= 0; col --) - b.set(row, col, T(0)); - - - - for (unsigned row = 0; row < m; row ++) - for (unsigned col = 0; col < m; col ++) - a.set(row, col, T(0)); - - - unsigned i = my_random() % m; - unsigned j = my_random() % m; - - auto t = T(1); - - a.set(i, j, t); - - lp_assert(a.get(i, j) == t); - - unsigned j1; - if (j < m - 1) { - j1 = m - 1; - a.set(i, j1, T(2)); - } -} - -void tst1() { - std::cout << "testing the minimal matrix with 1 row and 1 column" << std::endl; - square_sparse_matrix m0(1, 1); - m0.set(0, 0, 1); - // print_matrix(m0); - m0.set(0, 0, 0); - // print_matrix(m0); - test_matrix(m0); - - unsigned rows = 2; - square_sparse_matrix m(rows, rows); - std::cout << "setting m(0,1)=" << std::endl; - - m.set(0, 1, 11); - m.set(0, 0, 12); - - // print_matrix(m); - - test_matrix(m); - - square_sparse_matrix m1(2, 2); - m1.set(0, 0, 2); - m1.set(1, 0, 3); - // print_matrix(m1); - std::cout << " zeroing matrix 2 by 2" << std::endl; - m1.set(0, 0, 0); - m1.set(1, 0, 0); - // print_matrix(m1); - - test_matrix(m1); - - - std::cout << "printing zero matrix 3 by 1" << std::endl; - square_sparse_matrix m2(3, 3); - // print_matrix(m2); - - m2.set(0, 0, 1); - m2.set(2, 0, 2); - std::cout << "printing matrix 3 by 1 with a gap" << std::endl; - // print_matrix(m2); - - test_matrix(m2); - - square_sparse_matrix m10by9(10, 10); - m10by9.set(0, 1, 1); - - m10by9(0, 1) = 4; - - double test = m10by9(0, 1); - - std::cout << "got " << test << std::endl; - - - m10by9.set(0, 8, 8); - m10by9.set(3, 4, 7); - m10by9.set(3, 2, 5); - m10by9.set(3, 8, 99); - m10by9.set(3, 2, 6); - m10by9.set(1, 8, 9); - m10by9.set(4, 0, 40); - m10by9.set(0, 0, 10); - - std::cout << "printing matrix 10 by 9" << std::endl; - // print_matrix(m10by9); - - - test_matrix(m10by9); - std::cout <<"zeroing m10by9\n"; -#ifdef Z3DEBUG - for (unsigned int i = 0; i < m10by9.dimension(); i++) - for (unsigned int j = 0; j < m10by9.column_count(); j++) - m10by9.set(i, j, 0); -#endif - // print_matrix(m10by9); -} vector allocate_basis_heading(unsigned count) { // the rest of initialization will be handled by lu_QR vector basis_heading(count, -1); @@ -555,12 +440,6 @@ void test_small_lu(lp_settings & settings) { #endif -void fill_long_row(square_sparse_matrix &m, int i) { - int n = m.dimension(); - for (int j = 0; j < n; j ++) { - m (i, (j + i) % n) = j * j; - } -} void fill_long_row(static_matrix &m, int i) { int n = m.column_count(); @@ -570,13 +449,6 @@ void fill_long_row(static_matrix &m, int i) { } -void fill_long_row_exp(square_sparse_matrix &m, int i) { - int n = m.dimension(); - - for (int j = 0; j < n; j ++) { - m(i, j) = my_random() % 20; - } -} void fill_long_row_exp(static_matrix &m, int i) { int n = m.column_count(); @@ -586,26 +458,9 @@ void fill_long_row_exp(static_matrix &m, int i) { } } -void fill_larger_square_sparse_matrix_exp(square_sparse_matrix & m){ - for ( unsigned i = 0; i < m.dimension(); i++ ) - fill_long_row_exp(m, i); -} - -void fill_larger_square_sparse_matrix_exp(static_matrix & m){ - for ( unsigned i = 0; i < m.row_count(); i++ ) - fill_long_row_exp(m, i); -} -void fill_larger_square_sparse_matrix(square_sparse_matrix & m){ - for ( unsigned i = 0; i < m.dimension(); i++ ) - fill_long_row(m, i); -} -void fill_larger_square_sparse_matrix(static_matrix & m){ - for ( unsigned i = 0; i < m.row_count(); i++ ) - fill_long_row(m, i); -} int perm_id = 0; @@ -616,11 +471,6 @@ int perm_id = 0; -void init_b(vector & b, square_sparse_matrix & m, vector& x) { - for (unsigned i = 0; i < m.dimension(); i++) { - b.push_back(m.dot_product_with_row(i, x)); - } -} void init_b(vector & b, static_matrix & m, vector & x) { for (unsigned i = 0; i < m.row_count(); i++) { @@ -671,7 +521,7 @@ void test_lp_1() { m(2, 0) = 2; m(2, 1) = -1; m(2, 2) = 2; m(2, 5) = 1; m(3, 0) = 2; m(3, 1) = 3; m(3, 2) = -1; m(3, 6) = 1; #ifdef Z3DEBUG - print_matrix(m, std::cout); + //print_matrix(m, std::cout); #endif vector x_star(7); x_star[0] = 0; x_star[1] = 0; x_star[2] = 0; @@ -724,213 +574,9 @@ void test_lp_primal_core_solver() { } -#ifdef Z3DEBUG -template -void test_swap_rows_with_permutation(square_sparse_matrix& m){ - std::cout << "testing swaps" << std::endl; - unsigned dim = m.row_count(); - dense_matrix original(&m); - permutation_matrix q(dim); - print_matrix(m, std::cout); - lp_assert(original == q * m); - for (int i = 0; i < 100; i++) { - unsigned row1 = my_random() % dim; - unsigned row2 = my_random() % dim; - if (row1 == row2) continue; - std::cout << "swap " << row1 << " " << row2 << std::endl; - m.swap_rows(row1, row2); - q.transpose_from_left(row1, row2); - lp_assert(original == q * m); - print_matrix(m, std::cout); - std::cout << std::endl; - } -} -#endif -template -void fill_matrix(square_sparse_matrix& m); // forward definition -#ifdef Z3DEBUG -template -void test_swap_cols_with_permutation(square_sparse_matrix& m){ - std::cout << "testing swaps" << std::endl; - unsigned dim = m.row_count(); - dense_matrix original(&m); - permutation_matrix q(dim); - print_matrix(m, std::cout); - lp_assert(original == q * m); - for (int i = 0; i < 100; i++) { - unsigned row1 = my_random() % dim; - unsigned row2 = my_random() % dim; - if (row1 == row2) continue; - std::cout << "swap " << row1 << " " << row2 << std::endl; - m.swap_rows(row1, row2); - q.transpose_from_right(row1, row2); - lp_assert(original == q * m); - print_matrix(m, std::cout); - std::cout << std::endl; - } -} - - -template -void test_swap_rows(square_sparse_matrix& m, unsigned i0, unsigned i1){ - std::cout << "test_swap_rows(" << i0 << "," << i1 << ")" << std::endl; - square_sparse_matrix mcopy(m.dimension(), 0); - for (unsigned i = 0; i < m.dimension(); i++) - for (unsigned j = 0; j < m.dimension(); j++) { - mcopy(i, j)= m(i, j); - } - std::cout << "swapping rows "<< i0 << "," << i1 << std::endl; - m.swap_rows(i0, i1); - - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(i0, j) == m(i1, j)); - lp_assert(mcopy(i1, j) == m(i0, j)); - } -} -template -void test_swap_columns(square_sparse_matrix& m, unsigned i0, unsigned i1){ - std::cout << "test_swap_columns(" << i0 << "," << i1 << ")" << std::endl; - square_sparse_matrix mcopy(m.dimension(), 0); // the second argument does not matter - for (unsigned i = 0; i < m.dimension(); i++) - for (unsigned j = 0; j < m.dimension(); j++) { - mcopy(i, j)= m(i, j); - } - m.swap_columns(i0, i1); - - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(j, i0) == m(j, i1)); - lp_assert(mcopy(j, i1) == m(j, i0)); - } - - for (unsigned i = 0; i < m.dimension(); i++) { - if (i == i0 || i == i1) - continue; - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(j, i)== m(j, i)); - } - } -} -#endif - -template -void fill_matrix(square_sparse_matrix& m){ - int v = 0; - for (int i = m.dimension() - 1; i >= 0; i--) { - for (int j = m.dimension() - 1; j >=0; j--){ - m(i, j) = v++; - } - } -} - -void test_pivot_like_swaps_and_pivot(){ - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - // pivot at 2,7 - m.swap_columns(0, 7); - // print_matrix(m); - m.swap_rows(2, 0); - // print_matrix(m); - for (unsigned i = 1; i < m.dimension(); i++) { - m(i, 0) = 0; - } - // print_matrix(m); - - // say pivot at 3,4 - m.swap_columns(1, 4); - // print_matrix(m); - m.swap_rows(1, 3); - // print_matrix(m); - - vector row; - double alpha = 2.33; - unsigned pivot_row = 1; - unsigned target_row = 2; - unsigned pivot_row_0 = 3; - double beta = 3.1; - m(target_row, 3) = 0; - m(target_row, 5) = 0; - m(pivot_row, 6) = 0; -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - - for (unsigned j = 0; j < m.dimension(); j++) { - row.push_back(m(target_row, j) + alpha * m(pivot_row, j) + beta * m(pivot_row_0, j)); - } - - for (auto & t : row) { - std::cout << t << ","; - } - std::cout << std::endl; - lp_settings settings; - m.pivot_row_to_row(pivot_row, alpha, target_row, settings); - m.pivot_row_to_row(pivot_row_0, beta, target_row, settings); - // print_matrix(m); - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(abs(row[j] - m(target_row, j)) < 0.00000001); - } -} - -#ifdef Z3DEBUG -void test_swap_rows() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - test_swap_rows(m, 3, 5); - - test_swap_rows(m, 1, 3); - - - test_swap_rows(m, 1, 3); - - test_swap_rows(m, 1, 7); - - test_swap_rows(m, 3, 7); - test_swap_rows(m, 0, 7); - m(0, 4) = 1; - // print_matrix(m); - test_swap_rows(m, 0, 7); - // go over some corner cases - square_sparse_matrix m0(2, 2); - test_swap_rows(m0, 0, 1); - m0(0, 0) = 3; - test_swap_rows(m0, 0, 1); - m0(1, 0) = 3; - test_swap_rows(m0, 0, 1); - - - square_sparse_matrix m1(10, 10); - test_swap_rows(m1, 0, 1); - m1(0, 0) = 3; - test_swap_rows(m1, 0, 1); - m1(1, 0) = 3; - m1(0, 3) = 5; - m1(1, 3) = 4; - m1(1, 8) = 8; - m1(1, 9) = 8; - - test_swap_rows(m1, 0, 1); - - square_sparse_matrix m2(3, 3); - test_swap_rows(m2, 0, 1); - m2(0, 0) = 3; - test_swap_rows(m2, 0, 1); - m2(2, 0) = 3; - test_swap_rows(m2, 0, 2); -} - -void fill_uniformly(square_sparse_matrix & m, unsigned dim) { - int v = 0; - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - m(i, j) = v++; - } - } -} void fill_uniformly(dense_matrix & m, unsigned dim) { int v = 0; @@ -941,146 +587,8 @@ void fill_uniformly(dense_matrix & m, unsigned dim) { } } -void square_sparse_matrix_with_permutations_test() { - unsigned dim = 4; - square_sparse_matrix m(dim, dim); - fill_uniformly(m, dim); - dense_matrix dm(dim, dim); - fill_uniformly(dm, dim); - dense_matrix dm0(dim, dim); - fill_uniformly(dm0, dim); - permutation_matrix q0(dim); - q0[0] = 1; - q0[1] = 0; - q0[2] = 3; - q0[3] = 2; - permutation_matrix q1(dim); - q1[0] = 1; - q1[1] = 2; - q1[2] = 3; - q1[3] = 0; - permutation_matrix p0(dim); - p0[0] = 1; - p0[1] = 0; - p0[2] = 3; - p0[3] = 2; - permutation_matrix p1(dim); - p1[0] = 1; - p1[1] = 2; - p1[2] = 3; - p1[3] = 0; - - m.multiply_from_left(q0); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[i], j)); - } - } - - auto q0_dm = q0 * dm; - lp_assert(m == q0_dm); - - m.multiply_from_left(q1); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], j)); - } - } - - - auto q1_q0_dm = q1 * q0_dm; - - lp_assert(m == q1_q0_dm); - - m.multiply_from_right(p0); - - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); - } - } - - auto q1_q0_dm_p0 = q1_q0_dm * p0; - - lp_assert(m == q1_q0_dm_p0); - - m.multiply_from_right(p1); - - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); - } - } - - auto q1_q0_dm_p0_p1 = q1_q0_dm_p0 * p1; - lp_assert(m == q1_q0_dm_p0_p1); - - m.multiply_from_right(p1); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); - } - } - auto q1_q0_dm_p0_p1_p1 = q1_q0_dm_p0_p1 * p1; - - lp_assert(m == q1_q0_dm_p0_p1_p1); -} - -void test_swap_columns() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - - test_swap_columns(m, 3, 5); - - test_swap_columns(m, 1, 3); - - test_swap_columns(m, 1, 3); - - // print_matrix(m); - test_swap_columns(m, 1, 7); - - test_swap_columns(m, 3, 7); - - test_swap_columns(m, 0, 7); - - test_swap_columns(m, 0, 7); - - // go over some corner cases - square_sparse_matrix m0(2, 2); - test_swap_columns(m0, 0, 1); - m0(0, 0) = 3; - test_swap_columns(m0, 0, 1); - m0(0, 1) = 3; - test_swap_columns(m0, 0, 1); - - - square_sparse_matrix m1(10, 10); - test_swap_columns(m1, 0, 1); - m1(0, 0) = 3; - test_swap_columns(m1, 0, 1); - m1(0, 1) = 3; - m1(3, 0) = 5; - m1(3, 1) = 4; - m1(8, 1) = 8; - m1(9, 1) = 8; - - test_swap_columns(m1, 0, 1); - - square_sparse_matrix m2(3, 3); - test_swap_columns(m2, 0, 1); - m2(0, 0) = 3; - test_swap_columns(m2, 0, 1); - m2(0, 2) = 3; - test_swap_columns(m2, 0, 2); -} - -void test_swap_operations() { - test_swap_rows(); - test_swap_columns(); -} void test_dense_matrix() { dense_matrix d(3, 2); @@ -1110,7 +618,7 @@ void test_dense_matrix() { p2.set_elem(1, 0, 1); auto c2 = d * p2; } -#endif + @@ -1471,59 +979,6 @@ bool contains(std::string const & s, char const * pattern) { - -void test_init_U() { - static_matrix m(3, 7); - m(0, 0) = 10; m(0, 1) = 11; m(0, 2) = 12; m(0, 3) = 13; m(0, 4) = 14; - m(1, 0) = 20; m(1, 1) = 21; m(1, 2) = 22; m(1, 3) = 23; m(1, 5) = 24; - m(2, 0) = 30; m(2, 1) = 31; m(2, 2) = 32; m(2, 3) = 33; m(2, 6) = 34; -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - vector basis(3); - basis[0] = 1; - basis[1] = 2; - basis[2] = 4; - - square_sparse_matrix u(m, basis); - - for (unsigned i = 0; i < 3; i++) { - for (unsigned j = 0; j < 3; j ++) { - lp_assert(m(i, basis[j]) == u(i, j)); - } - } - - // print_matrix(m); - // print_matrix(u); -} - -void test_replace_column() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - m.swap_columns(0, 7); - m.swap_columns(6, 3); - m.swap_rows(2, 0); - for (unsigned i = 1; i < m.dimension(); i++) { - m(i, 0) = 0; - } - - indexed_vector w(m.dimension()); - for (unsigned i = 0; i < m.dimension(); i++) { - w.set_value(i % 3, i); - } - - lp_settings settings; - - for (unsigned column_to_replace = 0; column_to_replace < m.dimension(); column_to_replace ++) { - m.replace_column(column_to_replace, w, settings); - for (unsigned i = 0; i < m.dimension(); i++) { - lp_assert(abs(w[i] - m(i, column_to_replace)) < 0.00000001); - } - } -} - - - void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-monics", "test emonics"); parser.add_option_with_help_string("-nex_order", "test nex order"); @@ -1544,8 +999,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); - parser.add_option_with_help_string("--test_swaps", "test row swaps with a permutation"); - parser.add_option_with_help_string("--test_perm", "test permutations"); parser.add_option_with_after_string_with_help("--checklu", "the file name for lu checking"); parser.add_option_with_after_string_with_help("--partial_pivot", "the partial pivot constant, a number somewhere between 10 and 100"); parser.add_option_with_after_string_with_help("--percent_for_enter", "which percent of columns check for entering column"); @@ -1871,38 +1324,6 @@ void check_lu_from_file(std::string lufile_name) { lp_assert(false); } -void test_square_dense_submatrix() { - std::cout << "testing square_dense_submatrix" << std::endl; - unsigned parent_dim = 7; - square_sparse_matrix parent(parent_dim, 0); - fill_matrix(parent); - unsigned index_start = 3; - square_dense_submatrix d; - d.init(&parent, index_start); - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - d[i][j] = i*3+j*2; -#ifdef Z3DEBUG - unsigned dim = parent_dim - index_start; - dense_matrix m(dim, dim); - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - m[i-index_start][j-index_start] = d[i][j]; - print_matrix(&m, std::cout); -#endif - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - d[i][j] = d[j][i]; -#ifdef Z3DEBUG - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - m[i-index_start][j-index_start] = d[i][j]; - - print_matrix(&m, std::cout); - std::cout << std::endl; -#endif -} - void print_st(lp_status status) { @@ -2958,8 +2379,3 @@ void test_lp_local(int argn, char**argv) { void tst_lp(char ** argv, int argc, int& i) { lp::test_lp_local(argc - 2, argv + 2); } -#ifdef Z3DEBUG -namespace lp { -template void print_matrix(general_matrix&, std::ostream&); -} -#endif From 178135486c5fb8f20bd433604208ffa54a79f4fd Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 13:56:04 -0800 Subject: [PATCH 493/597] rm scaler --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lar_solver.h | 1 - src/math/lp/scaler.cpp | 22 --- src/math/lp/scaler.h | 94 ------------ src/math/lp/scaler_def.h | 270 ---------------------------------- src/math/lp/static_matrix.cpp | 1 - 6 files changed, 389 deletions(-) delete mode 100644 src/math/lp/scaler.cpp delete mode 100644 src/math/lp/scaler.h delete mode 100644 src/math/lp/scaler_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5d21bba70ba..5ccc023ab4c 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -41,7 +41,6 @@ z3_add_component(lp permutation_matrix.cpp random_updater.cpp row_eta_matrix.cpp - scaler.cpp square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 09612d905e4..be255dd65d3 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -31,7 +31,6 @@ #include "math/lp/lar_constraints.h" #include "math/lp/lar_core_solver.h" #include "math/lp/numeric_pair.h" -#include "math/lp/scaler.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/random_updater.h" #include "util/stacked_value.h" diff --git a/src/math/lp/scaler.cpp b/src/math/lp/scaler.cpp deleted file mode 100644 index 46330e2a1ee..00000000000 --- a/src/math/lp/scaler.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/scaler_def.h" -template bool lp::scaler::scale(); -template bool lp::scaler::scale(); diff --git a/src/math/lp/scaler.h b/src/math/lp/scaler.h deleted file mode 100644 index dd21b5c2895..00000000000 --- a/src/math/lp/scaler.h +++ /dev/null @@ -1,94 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include -#include -#include /* printf, fopen */ -#include /* exit, EXIT_FAILURE */ -#include "math/lp/lp_utils.h" -#include "math/lp/static_matrix.h" -namespace lp { -// for scaling an LP -template -class scaler { - vector & m_b; // right side - static_matrix &m_A; // the constraint matrix - const T & m_scaling_minimum; - const T & m_scaling_maximum; - vector& m_column_scale; - lp_settings & m_settings; -public: - // constructor - scaler(vector & b, static_matrix &A, const T & scaling_minimum, const T & scaling_maximum, vector & column_scale, - lp_settings & settings): - m_b(b), - m_A(A), - m_scaling_minimum(scaling_minimum), - m_scaling_maximum(scaling_maximum), - m_column_scale(column_scale), - m_settings(settings) { - lp_assert(m_column_scale.size() == 0); - m_column_scale.resize(m_A.column_count(), numeric_traits::one()); - } - - T right_side_balance(); - - T get_balance() { return m_A.get_balance(); } - - T A_min() const; - - T A_max() const; - - T get_A_ratio() const; - - T get_max_ratio_on_rows() const; - - T get_max_ratio_on_columns() const; - - void scale_rows_with_geometric_mean(); - - void scale_columns_with_geometric_mean(); - - void scale_once_for_ratio(); - - bool scale_with_ratio(); - - void bring_row_maximums_to_one(); - - void bring_column_maximums_to_one(); - - void bring_rows_and_columns_maximums_to_one(); - - bool scale_with_log_balance(); - // Returns true if and only if the scaling was successful. - // It is the caller responsibility to restore the matrix - bool scale(); - - void scale_rows(); - - void scale_row(unsigned i); - - void scale_column(unsigned i); - - void scale_columns(); -}; -} diff --git a/src/math/lp/scaler_def.h b/src/math/lp/scaler_def.h deleted file mode 100644 index 8604a67a131..00000000000 --- a/src/math/lp/scaler_def.h +++ /dev/null @@ -1,270 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "math/lp/scaler.h" -#include "math/lp/numeric_pair.h" -namespace lp { -// for scaling an LP -template T scaler::right_side_balance() { - T ret = zero_of_type(); - unsigned i = m_A.row_count(); - while (i--) { - T rs = abs(convert_struct::convert(m_b[i])); - if (!is_zero(rs)) { - numeric_traits::log(rs); - ret += rs * rs; - } - } - return ret; -} - -template T scaler::A_min() const { - T min = zero_of_type(); - for (unsigned i = 0; i < m_A.row_count(); i++) { - T t = m_A.get_min_abs_in_row(i); - min = i == 0 ? t : std::min(t, min); - } - return min; -} - -template T scaler::A_max() const { - T max = zero_of_type(); - for (unsigned i = 0; i < m_A.row_count(); i++) { - T t = m_A.get_max_abs_in_row(i); - max = i == 0? t : std::max(t, max); - } - return max; -} - -template T scaler::get_A_ratio() const { - T min = A_min(); - T max = A_max(); - lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); - T ratio = max / min; - return ratio; -} - -template T scaler::get_max_ratio_on_rows() const { - T ret = T(1); - unsigned i = m_A.row_count(); - while (i--) { - T den = m_A.get_min_abs_in_row(i); - lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); - T t = m_A.get_max_abs_in_row(i)/ den; - if (t > ret) - ret = t; - } - return ret; -} - -template T scaler::get_max_ratio_on_columns() const { - T ret = T(1); - unsigned i = m_A.column_count(); - while (i--) { - T den = m_A.get_min_abs_in_column(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(den)) - continue; // got a zero column - T t = m_A.get_max_abs_in_column(i)/den; - if (t > ret) - ret = t; - } - return ret; -} - -template void scaler::scale_rows_with_geometric_mean() { - unsigned i = m_A.row_count(); - while (i--) { - T max = m_A.get_max_abs_in_row(i); - T min = m_A.get_min_abs_in_row(i); - lp_assert(max > zero_of_type() && min > zero_of_type()); - if (is_zero(max) || is_zero(min)) - continue; - T gm = T(sqrt(numeric_traits::get_double(max*min))); - if (m_settings.is_eps_small_general(gm, 0.01)) { - continue; - } - m_A.multiply_row(i, one_of_type() / gm); - m_b[i] /= gm; - } -} - -template void scaler::scale_columns_with_geometric_mean() { - unsigned i = m_A.column_count(); - while (i--) { - T max = m_A.get_max_abs_in_column(i); - T min = m_A.get_min_abs_in_column(i); - T den = T(sqrt(numeric_traits::get_double(max*min))); - if (m_settings.is_eps_small_general(den, 0.01)) - continue; // got a zero column - T gm = T(1)/ den; - T cs = m_column_scale[i] * gm; - if (m_settings.is_eps_small_general(cs, 0.1)) - continue; - m_A.multiply_column(i, gm); - m_column_scale[i] = cs; - } -} - -template void scaler::scale_once_for_ratio() { - T max_ratio_on_rows = get_max_ratio_on_rows(); - T max_ratio_on_columns = get_max_ratio_on_columns(); - bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns; - // if max_ratio_on_columns is the largest then the rows are in worse shape than columns - if (scale_rows_first) { - scale_rows_with_geometric_mean(); - scale_columns_with_geometric_mean(); - } else { - scale_columns_with_geometric_mean(); - scale_rows_with_geometric_mean(); - } -} - -template bool scaler::scale_with_ratio() { - T ratio = get_A_ratio(); - // The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible - unsigned reps = m_settings.reps_in_scaler; - do { - scale_once_for_ratio(); - T new_r = get_A_ratio(); - if (new_r >= T(0.9) * ratio) - break; - } while (reps--); - - bring_rows_and_columns_maximums_to_one(); - return true; -} - -template void scaler::bring_row_maximums_to_one() { - unsigned i = m_A.row_count(); - while (i--) { - T t = m_A.get_max_abs_in_row(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(t)) continue; - m_A.multiply_row(i, one_of_type() / t); - m_b[i] /= t; - } -} - -template void scaler::bring_column_maximums_to_one() { - unsigned i = m_A.column_count(); - while (i--) { - T max = m_A.get_max_abs_in_column(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(max)) continue; - T t = T(1) / max; - m_A.multiply_column(i, t); - m_column_scale[i] *= t; - } -} - -template void scaler::bring_rows_and_columns_maximums_to_one() { - if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) { - bring_row_maximums_to_one(); - bring_column_maximums_to_one(); - } else { - bring_column_maximums_to_one(); - bring_row_maximums_to_one(); - } -} - -template bool scaler::scale_with_log_balance() { - T balance = get_balance(); - T balance_before_scaling = balance; - // todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed - for (int i = 0; i < 10; i++) { - scale_rows(); - scale_columns(); - T nb = get_balance(); - if (nb < T(0.9) * balance) { - balance = nb; - } else { - balance = nb; - break; - } - } - return balance <= balance_before_scaling; -} -// Returns true if and only if the scaling was successful. -// It is the caller responsibility to restore the matrix -template bool scaler::scale() { - if (numeric_traits::precise()) return true; - if (m_settings.scale_with_ratio) - return scale_with_ratio(); - return scale_with_log_balance(); -} - -template void scaler::scale_rows() { - for (unsigned i = 0; i < m_A.row_count(); i++) - scale_row(i); -} - -template void scaler::scale_row(unsigned i) { - T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct::convert(m_b[i]))); - T alpha = numeric_traits::one(); - if (numeric_traits::is_zero(row_max)) { - return; - } - if (row_max < m_scaling_minimum) { - do { - alpha *= T(2); - row_max *= T(2); - } while (row_max < m_scaling_minimum); - m_A.multiply_row(i, alpha); - m_b[i] *= alpha; - } else if (row_max > m_scaling_maximum) { - do { - alpha /= T(2); - row_max /= T(2); - } while (row_max > m_scaling_maximum); - m_A.multiply_row(i, alpha); - m_b[i] *= alpha; - } -} - -template void scaler::scale_column(unsigned i) { - T column_max = m_A.get_max_abs_in_column(i); - T alpha = numeric_traits::one(); - - if (numeric_traits::is_zero(column_max)){ - return; // the column has zeros only - } - if (column_max < m_scaling_minimum) { - do { - alpha *= T(2); - column_max *= T(2); - } while (column_max < m_scaling_minimum); - } else if (column_max > m_scaling_maximum) { - do { - alpha /= T(2); - column_max /= T(2); - } while (column_max > m_scaling_maximum); - } else { - return; - } - m_A.multiply_column(i, alpha); - m_column_scale[i] = alpha; -} - -template void scaler::scale_columns() { - for (unsigned i = 0; i < m_A.column_count(); i++) { - scale_column(i); - } -} -} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 93360b9140f..12d2f9f0df3 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,7 +24,6 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_primal_core_solver.h" -#include "math/lp/scaler.h" #include "math/lp/lar_solver.h" namespace lp { template void static_matrix::add_columns_at_the_end(unsigned int); From 0fb65dea3f68e16d9c9dd53f365d27cda71a3a57 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 14:17:04 -0800 Subject: [PATCH 494/597] rm square_sparse_matrix --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lar_solver.cpp | 7 - src/math/lp/lar_solver.h | 2 - src/math/lp/lp_primal_core_solver.h | 2 +- src/math/lp/square_sparse_matrix.cpp | 118 --- src/math/lp/square_sparse_matrix.h | 433 -------- src/math/lp/square_sparse_matrix_def.h | 1303 ------------------------ src/test/lp/lp.cpp | 4 +- 8 files changed, 3 insertions(+), 1867 deletions(-) delete mode 100644 src/math/lp/square_sparse_matrix.cpp delete mode 100644 src/math/lp/square_sparse_matrix.h delete mode 100644 src/math/lp/square_sparse_matrix_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5ccc023ab4c..ef363509fcb 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -41,7 +41,6 @@ z3_add_component(lp permutation_matrix.cpp random_updater.cpp row_eta_matrix.cpp - square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES util diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index f890b27fa43..9bc850848e5 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -783,13 +783,6 @@ namespace lp { return r; } - - template - void lar_solver::add_last_rows_to_lu(lp_primal_core_solver& s) { - lp_assert(false); - - } - bool lar_solver::x_is_correct() const { if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { return false; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index be255dd65d3..b925b5ef039 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -222,8 +222,6 @@ class lar_solver : public column_namer { void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); - template - void add_last_rows_to_lu(lp_primal_core_solver & s); bool x_is_correct() const; void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); template diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 2c7360712d6..10de624f55a 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -926,7 +926,7 @@ class lp_primal_core_solver:public lp_core_solver_base { m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); #ifdef Z3DEBUG - // check_correctness(); + lp_assert(false); #endif } diff --git a/src/math/lp/square_sparse_matrix.cpp b/src/math/lp/square_sparse_matrix.cpp deleted file mode 100644 index 3ec88f47d85..00000000000 --- a/src/math/lp/square_sparse_matrix.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/square_sparse_matrix_def.h" -#include "math/lp/dense_matrix.h" -namespace lp { -template double square_sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; -template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); -template void square_sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); -template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template const double & square_sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; -template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); -template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void square_sparse_matrix::prepare_for_factorization(); -template void square_sparse_matrix::remove_element(vector >&, indexed_value&); -template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix::set(unsigned int, unsigned int, double); -template void square_sparse_matrix::set_max_in_row(vector >&); -template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void square_sparse_matrix::solve_y_U(vector&) const; -template square_sparse_matrix::square_sparse_matrix(unsigned int, unsigned); -template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); -template void square_sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template mpq const & square_sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; -template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void square_sparse_matrix::prepare_for_factorization(); -template void square_sparse_matrix::remove_element(vector> &, indexed_value&); -template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix::set_max_in_row(vector>&); -template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void square_sparse_matrix::solve_y_U(vector&) const; -template void square_sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); -template void square_sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool square_sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); -template const mpq & square_sparse_matrix>::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix>::get_number_of_nonzeroes() const; -template bool square_sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix>::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); -template void square_sparse_matrix>::prepare_for_factorization(); -template void square_sparse_matrix>::remove_element(vector>&, indexed_value&); -template void square_sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix>::set_max_in_row(vector>&); -template bool square_sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); -template void square_sparse_matrix>::solve_y_U(vector&) const; -template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); -template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); -template void square_sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); -template void square_sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); -template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings&, vector &); -template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); -#ifdef Z3DEBUG -template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool square_sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -#endif - -template void square_sparse_matrix >::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); -template void square_sparse_matrix::solve_U_y(vector&); -template void square_sparse_matrix::double_solve_U_y(vector&); -template void square_sparse_matrix::solve_U_y(vector&); -template void square_sparse_matrix::double_solve_U_y(vector&); -template void square_sparse_matrix >::solve_U_y >(vector >&); -template void square_sparse_matrix >::double_solve_U_y >(vector >&); -template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template double square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template mpq square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix >::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template mpq square_sparse_matrix >::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix >::find_error_in_solution_U_y_indexed >(indexed_vector >&, indexed_vector >&, const vector &); -template numeric_pair square_sparse_matrix >::dot_product_with_row >(unsigned int, indexed_vector > const&) const; -template void square_sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); - -template void square_sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); - -template void square_sparse_matrix >::solve_U_y(vector&); -template void square_sparse_matrix >::double_solve_U_y(vector&); -template void square_sparse_matrix< mpq,numeric_pair< mpq> >::set(unsigned int,unsigned int, mpq); -template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings & ); -template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings &); -template void square_sparse_matrix >::solve_y_U_indexed(indexed_vector&, const lp_settings &); - -template square_sparse_matrix::square_sparse_matrix(static_matrix const&, vector&); -template square_sparse_matrix::square_sparse_matrix (static_matrix const&, vector&); -template square_sparse_matrix >::square_sparse_matrix(static_matrix > const&, vector&); -} -template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); -template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); diff --git a/src/math/lp/square_sparse_matrix.h b/src/math/lp/square_sparse_matrix.h deleted file mode 100644 index 5637af99f37..00000000000 --- a/src/math/lp/square_sparse_matrix.h +++ /dev/null @@ -1,433 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/permutation_matrix.h" -#include "math/lp/static_matrix.h" -#include -#include -#include -#include -#include -#include "math/lp/indexed_value.h" -#include "math/lp/indexed_vector.h" -#include -#include "math/lp/lp_settings.h" -#include "math/lp/eta_matrix.h" -#include "math/lp/binary_heap_upair_queue.h" -#include "math/lp/numeric_pair.h" -#include "math/lp/u_set.h" -namespace lp { -// it is a square matrix -template -class square_sparse_matrix - : public matrix -{ - struct col_header { - unsigned m_shortened_markovitz; - vector> m_values; // the actual column values - - col_header(): m_shortened_markovitz(0) {} - - void shorten_markovich_by_one() { - m_shortened_markovitz++; - } - - void zero_shortened_markovitz() { - m_shortened_markovitz = 0; - } - }; - - unsigned m_n_of_active_elems; - binary_heap_upair_queue m_pivot_queue; -public: - vector>> m_rows; - vector m_columns; - permutation_matrix m_row_permutation; - permutation_matrix m_column_permutation; - // m_work_pivot_vector[j] = offset of elementh of j-th column in the row we are pivoting to - // if the column is not present then m_work_pivot_vector[j] is -1 - vector m_work_pivot_vector; - vector m_processed; - unsigned get_n_of_active_elems() const { return m_n_of_active_elems; } - -#ifdef Z3DEBUG - // dense_matrix m_dense; -#endif - /* - the rule is: row i is mapped to m_row_permutation[i] and - column j is mapped to m_column_permutation.apply_reverse(j) - */ - - unsigned adjust_row(unsigned row) const{ - return m_row_permutation[row]; - } - - unsigned adjust_column(unsigned col) const{ - return m_column_permutation.apply_reverse(col); - } - - unsigned adjust_row_inverse(unsigned row) const{ - return m_row_permutation.apply_reverse(row); - } - - unsigned adjust_column_inverse(unsigned col) const{ - return m_column_permutation[col]; - } - - template - void copy_column_from_input(unsigned input_column, const M& A, unsigned j); - template - void copy_column_from_input_with_possible_zeros(const M& A, unsigned j); - - template - void copy_from_input(const M& A); - template - void copy_from_input_on_basis(const M& A, vector & basis); - -public: - - // constructors - template - square_sparse_matrix(const M &A, vector& basis); - - template - square_sparse_matrix(const M &A); - - square_sparse_matrix(unsigned dim, unsigned); // the second parameter is needed to distinguish this - // constructor from the one above - - - - class ref_matrix_element { - square_sparse_matrix & m_matrix; - unsigned m_row; - unsigned m_col; - public: - ref_matrix_element(square_sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} - ref_matrix_element & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } - ref_matrix_element & operator=(ref_matrix_element const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } - operator T () const { return m_matrix.get(m_row, m_col); } - }; - - class ref_row { - square_sparse_matrix & m_matrix; - unsigned m_row; - public: - ref_row(square_sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} - ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); } - }; - - void set_with_no_adjusting_for_row(unsigned row, unsigned col, T val); - void set_with_no_adjusting_for_col(unsigned row, unsigned col, T val); - - void set_with_no_adjusting(unsigned row, unsigned col, T val); - - void set(unsigned row, unsigned col, T val); - - T const & get_not_adjusted(unsigned row, unsigned col) const; - T const & get(unsigned row, unsigned col) const; - - ref_row operator[](unsigned row) { return ref_row(*this, row); } - - ref_matrix_element operator()(unsigned row, unsigned col) { return ref_matrix_element(*this, row, col); } - - T operator() (unsigned row, unsigned col) const { return get(row, col); } - - vector> & get_row_values(unsigned row) { - return m_rows[row]; - } - - vector> const & get_row_values(unsigned row) const { - return m_rows[row]; - } - - vector> & get_column_values(unsigned col) { - return m_columns[col].m_values; - } - - vector> const & get_column_values(unsigned col) const { - return m_columns[col].m_values; - } - - unsigned dimension() const {return static_cast(m_row_permutation.size());} - - unsigned row_count() const override {return dimension();} - unsigned column_count() const override {return dimension();} - - void init_row_headers(); - - void init_column_headers(); - - unsigned lowest_row_in_column(unsigned j); - - indexed_value & column_iv_other(indexed_value & iv) { - return m_rows[iv.m_index][iv.m_other]; - } - - indexed_value & row_iv_other(indexed_value & iv) { - return m_columns[iv.m_index].m_values[iv.m_other]; - } - - void remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset); - - void remove_element(vector> & row_chunk, indexed_value & row_el_iv); - - void put_max_index_to_0(vector> & row_vals, unsigned max_index); - - void set_max_in_row(unsigned row) { - set_max_in_row(m_rows[row]); - } - - - void set_max_in_row(vector> & row_vals); - - bool pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings); - - void scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column); - - // This method pivots row i to row i0 by muliplying row i by - // alpha and adding it to row i0. - // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, - // Returns false if the resulting row is all zeroes, and true otherwise - bool pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ); - - // set the max val as well - // returns false if the resulting row is all zeroes, and true otherwise - bool set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, - lp_settings & settings); - - - // set the max val as well - // returns false if the resulting row is all zeroes, and true otherwise - bool set_row_from_work_vector_and_clean_work_vector(unsigned i0); - - void remove_zero_elements_and_set_data_on_existing_elements(unsigned row); - - // work_vec here has not adjusted column indices - void remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings); - - void multiply_from_right(permutation_matrix& p) { - // m_dense = m_dense * p; - m_column_permutation.multiply_by_permutation_from_right(p); - // lp_assert(*this == m_dense); - } - - void multiply_from_left(permutation_matrix& p) { - // m_dense = p * m_dense; - m_row_permutation.multiply_by_permutation_from_left(p); - // lp_assert(*this == m_dense); - } - - void multiply_from_left_with_reverse(permutation_matrix& p) { - // m_dense = p * m_dense; - m_row_permutation.multiply_by_permutation_reverse_from_left(p); - // lp_assert(*this == m_dense); - } - - // adding delta columns at the end of the matrix - void add_columns_at_the_end(unsigned delta); - - void delete_column(int i); - - void swap_columns(unsigned a, unsigned b) { - m_column_permutation.transpose_from_left(a, b); - } - - void swap_rows(unsigned a, unsigned b) { - m_row_permutation.transpose_from_right(a, b); - // m_dense.swap_rows(a, b); - // lp_assert(*this == m_dense); - } - - void divide_row_by_constant(unsigned i, const T & t, lp_settings & settings); - - bool close(T a, T b) { - return // (numeric_traits::precise() && numeric_traits::is_zero(a - b)) - // || - fabs(numeric_traits::get_double(a - b)) < 0.0000001; - } - - // solving x * this = y, and putting the answer into y - // the matrix here has to be upper triangular - void solve_y_U(vector & y) const; - - // solving x * this = y, and putting the answer into y - // the matrix here has to be upper triangular - void solve_y_U_indexed(indexed_vector & y, const lp_settings &); - - // fills the indices for such that y[i] can be not a zero - // sort them so the smaller indices come first - void fill_reachable_indices(std::set & rset, T *y); - - template - void find_error_in_solution_U_y(vector& y_orig, vector & y); - - template - void find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows); - - template - void add_delta_to_solution(const vector& del, vector & y); - - template - void add_delta_to_solution(const indexed_vector& del, indexed_vector & y); - - template - void double_solve_U_y(indexed_vector& y, const lp_settings & settings); - - template - void double_solve_U_y(vector& y); - // solving this * x = y, and putting the answer into y - // the matrix here has to be upper triangular - template - void solve_U_y(vector & y); - // solving this * x = y, and putting the answer into y - // the matrix here has to be upper triangular - template - void solve_U_y_indexed_only(indexed_vector & y, const lp_settings&, vector & sorted_active_rows ); - - T get_elem(unsigned i, unsigned j) const override { return get(i, j); } - unsigned get_number_of_rows() const { return dimension(); } - unsigned get_number_of_columns() const { return dimension(); } - void set_number_of_rows(unsigned /*m*/) override { } - void set_number_of_columns(unsigned /*n*/) override { } - template - L dot_product_with_row (unsigned row, const vector & y) const; - - template - L dot_product_with_row (unsigned row, const indexed_vector & y) const; - - unsigned get_number_of_nonzeroes() const; - - bool get_non_zero_column_in_row(unsigned i, unsigned *j) const; - - void remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv); - - - // w contains the new column - // the old column inside of the matrix has not been changed yet - void remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w); - - void add_new_element(unsigned row, unsigned col, const T& val); - - // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed - // the old column inside of the matrix has not been changed yet - void add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings); - - void replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings); - - unsigned pivot_score(unsigned i, unsigned j); - - void enqueue_domain_into_pivot_queue(); - - void set_max_in_rows(); - - void zero_shortened_markovitz_numbers(); - - void prepare_for_factorization(); - - void recover_pivot_queue(vector & rejected_pivots); - - int elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting); - - bool remove_row_from_active_pivots_and_shorten_columns(unsigned row); - - void remove_pivot_column(unsigned row); - - void update_active_pivots(unsigned row); - - bool shorten_active_matrix(unsigned row, eta_matrix *eta_matrix); - - unsigned pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k); -#ifdef Z3DEBUG - bool can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k); - bool really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k); - void print_active_matrix(unsigned k, std::ostream & out); -#endif - bool pivot_queue_is_correct_for_row(unsigned i, unsigned k); - - bool pivot_queue_is_correct_after_pivoting(int k); - - bool get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k); - - bool elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting); - - unsigned number_of_non_zeroes_in_row(unsigned row) const { - return static_cast(m_rows[row].size()); - } - - unsigned number_of_non_zeroes_in_column(unsigned col) const { - return m_columns[col].m_values.size(); - } - - bool shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column); - - bool col_is_active(unsigned j, unsigned pivot) { - return adjust_column_inverse(j) > pivot; - } - - bool row_is_active(unsigned i, unsigned pivot) { - return adjust_row_inverse(i) > pivot; - } - - bool fill_eta_matrix(unsigned j, eta_matrix ** eta); -#ifdef Z3DEBUG - bool is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const; - - bool is_upper_triangular_until(unsigned k) const; - void check_column_vs_rows(unsigned col); - - void check_row_vs_columns(unsigned row); - - void check_rows_vs_columns(); - - void check_columns_vs_rows(); - - void check_matrix(); -#endif - void create_graph_G(const vector & active_rows, vector & sorted_active_rows); - void process_column_recursively(unsigned i, vector & sorted_rows); - void extend_and_sort_active_rows(const vector & active_rows, vector & sorted_active_rows); - void process_index_recursively_for_y_U(unsigned j, vector & sorted_rows); - void resize(unsigned new_dim) { - unsigned old_dim = dimension(); - lp_assert(new_dim >= old_dim); - for (unsigned j = old_dim; j < new_dim; j++) { - m_rows.push_back(vector>()); - m_columns.push_back(col_header()); - } - m_pivot_queue.resize(new_dim); - m_row_permutation.resize(new_dim); - m_column_permutation.resize(new_dim); - m_work_pivot_vector.resize(new_dim); - m_processed.resize(new_dim); - for (unsigned j = old_dim; j < new_dim; j++) { - add_new_element(j, j, numeric_traits::one()); - } - } -#ifdef Z3DEBUG -vector get_full_row(unsigned i) const; -#endif - unsigned pivot_queue_size() const { return m_pivot_queue.size(); } -}; -}; - - diff --git a/src/math/lp/square_sparse_matrix_def.h b/src/math/lp/square_sparse_matrix_def.h deleted file mode 100644 index 19263462717..00000000000 --- a/src/math/lp/square_sparse_matrix_def.h +++ /dev/null @@ -1,1303 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/square_sparse_matrix.h" -#include "math/lp/dense_matrix.h" - -#include -#include -namespace lp { -template -template -void square_sparse_matrix::copy_column_from_input(unsigned input_column, const M& A, unsigned j) { - vector> & new_column_vector = m_columns[j].m_values; - for (auto c : A.column(input_column)) { - unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[c.var()]; - unsigned row_offset = static_cast(row_vector.size()); - new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); - row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); - m_n_of_active_elems++; - } -} - -template -template -void square_sparse_matrix::copy_column_from_input_with_possible_zeros(const M& A, unsigned j) { - vector> & new_column_vector = m_columns[j].m_values; - for (auto c : A.column(j)) { - if (is_zero(c.coeff())) - continue; - unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[c.var()]; - unsigned row_offset = static_cast(row_vector.size()); - new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); - row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); - m_n_of_active_elems++; - } -} - -template -template -void square_sparse_matrix::copy_from_input_on_basis(const M & A, vector & basis) { - unsigned m = A.row_count(); - for (unsigned j = m; j-- > 0;) { - copy_column_from_input(basis[j], A, j); - } -} -template -template -void square_sparse_matrix::copy_from_input(const M & A) { - unsigned m = A.row_count(); - for (unsigned j = m; j-- > 0;) { - copy_column_from_input_with_possible_zeros(A, j); - } -} - -// constructor that copies columns of the basis from A -template -template -square_sparse_matrix::square_sparse_matrix(const M &A, vector & basis) : - m_n_of_active_elems(0), - m_pivot_queue(A.row_count()), - m_row_permutation(A.row_count()), - m_column_permutation(A.row_count()), - m_work_pivot_vector(A.row_count(), -1), - m_processed(A.row_count()) { - init_row_headers(); - init_column_headers(); - copy_from_input_on_basis(A, basis); -} - -template -template -square_sparse_matrix::square_sparse_matrix(const M &A) : - m_n_of_active_elems(0), - m_pivot_queue(A.row_count()), - m_row_permutation(A.row_count()), - m_column_permutation(A.row_count()), - m_work_pivot_vector(A.row_count(), -1), - m_processed(A.row_count()) { - init_row_headers(); - init_column_headers(); - copy_from_input(A); -} - - -template -void square_sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code - vector> & row_vec = m_rows[row]; - for (auto & iv : row_vec) { - if (iv.m_index == col) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - row_vec.push_back(indexed_value(val, col, -1)); -} - -template -void square_sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code - vector> & col_vec = m_columns[col].m_values; - for (auto & iv : col_vec) { - if (iv.m_index == row) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - col_vec.push_back(indexed_value(val, row, -1)); -} - - -template -void square_sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code - set_with_no_adjusting_for_row(row, col, val); - set_with_no_adjusting_for_col(row, col, val); -} - -template -void square_sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code - lp_assert(row < dimension() && col < dimension()); - // m_dense.set_elem(row, col, val); - row = adjust_row(row); - col = adjust_column(col); - set_with_no_adjusting(row, col, val); - // lp_assert(*this == m_dense); -} - -template -T const & square_sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { - for (indexed_value const & iv : m_rows[row]) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); -} - -template -T const & square_sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code - row = adjust_row(row); - auto & row_chunk = m_rows[row]; - col = adjust_column(col); - for (indexed_value const & iv : row_chunk) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); -} - -// constructor creating a zero matrix of dim*dim -template -square_sparse_matrix::square_sparse_matrix(unsigned dim, unsigned ) : - m_pivot_queue(dim), // dim will be the initial size of the queue - m_row_permutation(dim), - m_column_permutation(dim), - m_work_pivot_vector(dim, -1), - m_processed(dim) { - init_row_headers(); - init_column_headers(); - } - -template -void square_sparse_matrix::init_row_headers() { - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_rows.push_back(vector>()); - } -} - -template -void square_sparse_matrix::init_column_headers() { // we always have only square square_sparse_matrix - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_columns.push_back(col_header()); - } -} - -template -unsigned square_sparse_matrix::lowest_row_in_column(unsigned j) { - auto & mc = get_column_values(adjust_column(j)); - unsigned ret = 0; - for (auto & iv : mc) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > ret) { - ret = row; - } - } - return ret; -} - -template -void square_sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { - if (column_offset != column_vals.size() - 1) { - auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail - column_iv_other(column_iv).m_other = column_offset; - if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - } else if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - // do nothing - just decrease the sizes - column_vals.pop_back(); - row_vals.pop_back(); - m_n_of_active_elems--; // the value is correct only when refactoring -} - -template -void square_sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { - auto & column_chunk = get_column_values(row_el_iv.m_index); - indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; - remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); -} - -template -void square_sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { - if (max_index == 0) return; - indexed_value * max_iv = & row_vals[max_index]; - indexed_value * start_iv = & row_vals[0]; - // update the "other" columns elements which are bound to the start_iv and max_iv - m_columns[max_iv->m_index].m_values[max_iv->m_other].m_other = 0; - m_columns[start_iv->m_index].m_values[start_iv->m_other].m_other = max_index; - - // swap the elements - indexed_value t = * max_iv; - * max_iv = * start_iv; - * start_iv = t; -} - -template -void square_sparse_matrix::set_max_in_row(vector> & row_vals) { - if (row_vals.empty()) - return; - T max_val = abs(row_vals[0].m_value); - unsigned max_index = 0; - for (unsigned i = 1; i < row_vals.size(); i++) { - T iabs = abs(row_vals[i].m_value); - if (iabs > max_val) { - max_val = iabs; - max_index = i; - } - } - put_max_index_to_0(row_vals, max_index); -} - -template -bool square_sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { - const T& pivot = eta_matrix->get_diagonal_element(); - for (auto & it : eta_matrix->m_column_vector.m_data) { - if (!pivot_row_to_row(i, it.second, it.first, settings)) { - return false; - } - } - - divide_row_by_constant(i, pivot, settings); - if (!shorten_active_matrix(i, eta_matrix)) { - return false; - } - - return true; -} - -// returns the offset of the pivot column in the row -template -void square_sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { - auto & rvals = m_rows[row]; - unsigned size = rvals.size(); - for (unsigned j = 0; j < size; j++) { - auto & iv = rvals[j]; - if (iv.m_index != pivot_column) { - m_work_pivot_vector[iv.m_index] = j; - } else { - remove_element(rvals, iv); - j--; - size--; - } - } -} - -#ifdef Z3DEBUG -template -vector square_sparse_matrix::get_full_row(unsigned i) const { - vector r; - for (unsigned j = 0; j < column_count(); j++) - r.push_back(get(i, j)); - return r; -} -#endif - - - -// This method pivots row i to row i0 by muliplying row i by -// alpha and adding it to row i0. -// After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, -// Returns false if the resulting row is all zeroes, and true otherwise -template -bool square_sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { - lp_assert(i < dimension() && i0 < dimension()); - lp_assert(i != i0); - unsigned pivot_col = adjust_column(i); - i = adjust_row(i); - i0 = adjust_row(i0); - vector became_zeros; - // the offset of element of the pivot column in row i0 - scan_row_to_work_vector_and_remove_pivot_column(i0, pivot_col); - auto & i0_row_vals = m_rows[i0]; - // run over the pivot row and update row i0 - unsigned prev_size_i0 = i0_row_vals.size(); - for (const auto & iv : m_rows[i]) { - unsigned j = iv.m_index; - if (j == pivot_col) continue; - T alv = alpha * iv.m_value; - int j_offs = m_work_pivot_vector[j]; - if (j_offs == -1) { // it is a new element - if (!settings.abs_val_is_smaller_than_drop_tolerance(alv)) { - add_new_element(i0, j, alv); - } - } - else { - auto & row_el_iv = i0_row_vals[j_offs]; - row_el_iv.m_value += alv; - if (settings.abs_val_is_smaller_than_drop_tolerance(row_el_iv.m_value)) { - became_zeros.push_back(j_offs); - ensure_increasing(became_zeros); - } - else { - m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value); - } - } - } - - - // clean the work vector - for (unsigned k = 0; k < prev_size_i0; k++) { - m_work_pivot_vector[i0_row_vals[k].m_index] = -1; - } - - for (unsigned k = became_zeros.size(); k-- > 0; ) { - unsigned j = became_zeros[k]; - remove_element(i0_row_vals, i0_row_vals[j]); - if (i0_row_vals.empty()) - return false; - } - - if (numeric_traits::precise() == false) - set_max_in_row(i0_row_vals); - - return !i0_row_vals.empty(); -} - - - -// set the max val as well -// returns false if the resulting row is all zeroes, and true otherwise -template -bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, - lp_settings & settings) { - remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); - // all non-zero elements in m_work_pivot_vector are new - for (unsigned j : work_vec.m_index) { - if (numeric_traits::is_zero(work_vec[j])) { - continue; - } - lp_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); - add_new_element(i0, adjust_column(j), work_vec[j]); - work_vec[j] = numeric_traits::zero(); - } - work_vec.m_index.clear(); - auto & row_vals = m_rows[i0]; - if (row_vals.empty()) { - return false; - } - set_max_in_row(row_vals); // it helps to find larger pivots - return true; -} - - - -template -void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { - auto & row_vals = m_rows[row]; - for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - T & wj = m_work_pivot_vector[j]; - if (is_zero(wj)) { - remove_element(row_vals, row_el_iv); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(wj); - row_el_iv.set_value(wj); - wj = zero_of_type(); - } - } -} - -// work_vec here has not adjusted column indices -template -void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { - auto & row_vals = m_rows[row]; - for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - unsigned rj = adjust_column_inverse(j); - T val = work_vec[rj]; - if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { - remove_element(row_vals, row_el_iv); - lp_assert(numeric_traits::is_zero(val)); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); - work_vec[rj] = numeric_traits::zero(); - } - } -} - - -// adding delta columns at the end of the matrix -template -void square_sparse_matrix::add_columns_at_the_end(unsigned delta) { - for (unsigned i = 0; i < delta; i++) { - col_header col_head; - m_columns.push_back(col_head); - } - m_column_permutation.enlarge(delta); -} - -template -void square_sparse_matrix::delete_column(int i) { - lp_assert(i < dimension()); - for (auto cell = m_columns[i].m_head; cell != nullptr;) { - auto next_cell = cell->m_down; - kill_cell(cell); - cell = next_cell; - } -} - -template -void square_sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { - lp_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); - i = adjust_row(i); - for (auto & iv : m_rows[i]) { - T &v = iv.m_value; - v /= t; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)){ - v = numeric_traits::zero(); - } - m_columns[iv.m_index].m_values[iv.m_other].set_value(v); - } -} - - -// solving x * this = y, and putting the answer into y -// the matrix here has to be upper triangular -template -void square_sparse_matrix::solve_y_U(vector & y) const { // works by rows -#ifdef Z3DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - unsigned end = dimension(); - for (unsigned i = 0; i + 1 < end; i++) { - // all y[i] has correct values already - const T & yv = y[i]; - if (numeric_traits::is_zero(yv)) continue; - auto & mc = get_row_values(adjust_row(i)); - for (auto & c : mc) { - unsigned col = adjust_column_inverse(c.m_index); - if (col != i) { - y[col] -= c.m_value * yv; - } - } - } -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_right(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y, dimension())); - // delete [] clone_y; - // delete [] rs; -#endif -} - -// solving x * this = y, and putting the answer into y -// the matrix here has to be upper triangular -template -void square_sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { -#if 0 && Z3DEBUG - vector ycopy(y.m_data); - if (numeric_traits::precise() == false) - solve_y_U(ycopy); -#endif - vector sorted_active_columns; - extend_and_sort_active_rows(y.m_index, sorted_active_columns); - for (unsigned k = sorted_active_columns.size(); k-- > 0; ) { - unsigned j = sorted_active_columns[k]; - auto & yj = y[j]; - for (auto & c: m_columns[adjust_column(j)].m_values) { - unsigned i = adjust_row_inverse(c.m_index); - if (i == j) continue; - yj -= y[i] * c.m_value; - } - } - y.m_index.clear(); - for (auto j : sorted_active_columns) { - if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) - y.m_index.push_back(j); - else if (!numeric_traits::precise()) - y.m_data[j] = zero_of_type(); - } - - lp_assert(y.is_OK()); -#if 0 && Z3DEBUG - if (numeric_traits::precise() == false) - lp_assert(vectors_are_equal(ycopy, y.m_data)); -#endif -} - - -// fills the indices for such that y[i] can be not a zero -// sort them so the smaller indices come first -// void fill_reachable_indices(std::set & rset, T *y) { -// std::queue q; -// int m = dimension(); -// for (int i = m - 1; i >= 0; i--) { -// if (!numeric_traits::is_zero(y[i])){ -// for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { -// unsigned row = adjust_row_inverse(c->m_i); -// q.push(row); -// } -// } -// } -// while (!q.empty()) { -// unsigned i = q.front(); -// q.pop(); -// for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { -// unsigned row = adjust_row_inverse(c->m_i); -// if (rset.find(row) == rset.end()){ -// rset.insert(row); -// q.push(row); -// } -// } -// } -// } - -template -template -void square_sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { - unsigned i = dimension(); - while (i--) { - y_orig[i] -= dot_product_with_row(i, y); - } -} - -template -template -void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { - for (unsigned i: sorted_active_rows) - y_orig.add_value_at_index(i, -dot_product_with_row(i, y)); // cannot round up here!!! - // y_orig can contain very small values -} - - -template -template -void square_sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { - unsigned i = dimension(); - while (i--) { - y[i] += del[i]; - } -} -template -template -void square_sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { -// lp_assert(del.is_OK()); - // lp_assert(y.is_OK()); - for (auto i : del.m_index) { - y.add_value_at_index(i, del[i]); - } -} -template -template -void square_sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ - lp_assert(y.is_OK()); - indexed_vector y_orig(y); // copy y aside - vector active_rows; - solve_U_y_indexed_only(y, settings, active_rows); - lp_assert(y.is_OK()); - find_error_in_solution_U_y_indexed(y_orig, y, active_rows); - // y_orig contains the error now - if (y_orig.m_index.size() * ratio_of_index_size_to_all_size() < 32 * dimension()) { - active_rows.clear(); - solve_U_y_indexed_only(y_orig, settings, active_rows); - add_delta_to_solution(y_orig, y); - y.clean_up(); - } else { // the dense version - solve_U_y(y_orig.m_data); - add_delta_to_solution(y_orig.m_data, y.m_data); - y.restore_index_and_clean_from_data(); - } - lp_assert(y.is_OK()); -} -template -template -void square_sparse_matrix::double_solve_U_y(vector& y){ - vector y_orig(y); // copy y aside - solve_U_y(y); - find_error_in_solution_U_y(y_orig, y); - // y_orig contains the error now - solve_U_y(y_orig); - add_delta_to_solution(y_orig, y); -} - -// solving this * x = y, and putting the answer into y -// the matrix here has to be upper triangular -template -template -void square_sparse_matrix::solve_U_y(vector & y) { // it is a column wise version -#ifdef Z3DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - - for (unsigned j = dimension(); j--; ) { - const L & yj = y[j]; - if (is_zero(yj)) continue; - for (const auto & iv : m_columns[adjust_column(j)].m_values) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_left(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y, dimension())); -#endif -} -template -void square_sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { - lp_assert(m_processed[j] == false); - m_processed[j]=true; - auto & row = m_rows[adjust_row(j)]; - for (auto & c : row) { - unsigned i = adjust_column_inverse(c.m_index); - if (i == j) continue; - if (!m_processed[i]) { - process_index_recursively_for_y_U(i, sorted_active_rows); - } - } - sorted_active_rows.push_back(j); -} - -template -void square_sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { - lp_assert(m_processed[j] == false); - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i == j) continue; - if (!m_processed[i]) { - process_column_recursively(i, sorted_active_rows); - } - } - m_processed[j]=true; - sorted_active_rows.push_back(j); -} - - -template -void square_sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { - for (auto i : index_or_right_side) { - if (m_processed[i]) continue; - process_column_recursively(i, sorted_active_rows); - } - - for (auto i : sorted_active_rows) { - m_processed[i] = false; - } -} - - -template -void square_sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { - for (auto i : index_or_right_side) { - if (m_processed[i]) continue; - process_index_recursively_for_y_U(i, sorted_active_rows); - } - - for (auto i : sorted_active_rows) { - m_processed[i] = false; - } -} - - -template -template -void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version - create_graph_G(y.m_index, sorted_active_rows); - - for (auto k = sorted_active_rows.size(); k-- > 0;) { - unsigned j = sorted_active_rows[k]; - const L & yj = y[j]; - if (is_zero(yj)) continue; - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } - y.m_index.clear(); - for (auto j : sorted_active_rows) { - if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) - y.m_index.push_back(j); - else if (!numeric_traits::precise()) - y[j] = zero_of_type(); - } - - lp_assert(y.is_OK()); -#ifdef Z3DEBUG - // dense_matrix deb(this); - // vector clone_y(y.m_data); - // deb.apply_from_left(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y)); -#endif -} - -template -template -L square_sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { - L ret = zero_of_type(); - auto & mc = get_row_values(adjust_row(row)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - ret += c.m_value * y[col]; - } - return ret; -} - -template -template -L square_sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { - L ret = zero_of_type(); - auto & mc = get_row_values(adjust_row(row)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - ret += c.m_value * y[col]; - } - return ret; -} - - -template -unsigned square_sparse_matrix::get_number_of_nonzeroes() const { - unsigned ret = 0; - for (unsigned i = dimension(); i--; ) { - ret += number_of_non_zeroes_in_row(i); - } - return ret; -} - -template -bool square_sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { - // go over the i-th row - auto & mc = get_row_values(adjust_row(i)); - if (mc.size() > 0) { - *j = m_column_permutation[mc[0].m_index]; - return true; - } - return false; -} - -template -void square_sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { - auto & row_chunk = m_rows[col_el_iv.m_index]; - indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; - unsigned index_in_row = col_el_iv.m_other; - remove_element(row_chunk, col_el_iv.m_other, column_vals, row_el_iv.m_other); - if (index_in_row == 0) - set_max_in_row(row_chunk); -} - - -// w contains the new column -// the old column inside of the matrix has not been changed yet -template -void square_sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { - // -------------------------------- - // column_vals represents the old column - auto & column_vals = m_columns[column_to_replace].m_values; - for (unsigned k = static_cast(column_vals.size()); k-- > 0;) { - indexed_value & col_el_iv = column_vals[k]; - unsigned i = col_el_iv.m_index; - T &w_data_at_i = w[adjust_row_inverse(i)]; - if (numeric_traits::is_zero(w_data_at_i)) { - remove_element_that_is_not_in_w(column_vals, col_el_iv); - } else { - auto& row_chunk = m_rows[i]; - unsigned index_in_row = col_el_iv.m_other; - if (index_in_row == 0) { - bool look_for_max = abs(w_data_at_i) < abs(row_chunk[0].m_value); - row_chunk[0].set_value(col_el_iv.m_value = w_data_at_i); - if (look_for_max) - set_max_in_row(i); - } else { - row_chunk[index_in_row].set_value(col_el_iv.m_value = w_data_at_i); - if (abs(w_data_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, index_in_row); - } - w_data_at_i = numeric_traits::zero(); - } - } -} - -template -void square_sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { - auto & row_vals = m_rows[row]; - auto & col_vals = m_columns[col].m_values; - unsigned row_el_offs = static_cast(row_vals.size()); - unsigned col_el_offs = static_cast(col_vals.size()); - row_vals.push_back(indexed_value(val, col, col_el_offs)); - col_vals.push_back(indexed_value(val, row, row_el_offs)); - m_n_of_active_elems++; -} - -// w contains the "rest" of the new column; all common elements of w and the old column has been zeroed -// the old column inside of the matrix has not been changed yet -template -void square_sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - T w_at_i = w[i]; - if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_i)) { - unsigned ai = adjust_row(i); - add_new_element(ai, column_to_replace, w_at_i); - auto & row_chunk = m_rows[ai]; - lp_assert(row_chunk.size() > 0); - if (abs(w_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, static_cast(row_chunk.size()) - 1); - } - w[i] = numeric_traits::zero(); - } - w.m_index.clear(); -} - -template -void square_sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { - column_to_replace = adjust_column(column_to_replace); - remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); - add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); -} - -template -unsigned square_sparse_matrix::pivot_score(unsigned i, unsigned j) { - // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of - // new non zeroes we can obtain after the pivoting. - // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, - // which gives rnz(cnz-1). For example, is 0 for a column singleton, but not for - // a row singleton ( which is not a column singleton). - - auto col_header = m_columns[j]; - - return static_cast(get_row_values(i).size() * (col_header.m_values.size() - col_header.m_shortened_markovitz - 1)); -} - -template -void square_sparse_matrix::enqueue_domain_into_pivot_queue() { - lp_assert(m_pivot_queue.size() == 0); - for (unsigned i = 0; i < dimension(); i++) { - auto & rh = m_rows[i]; - unsigned rnz = static_cast(rh.size()); - for (auto iv : rh) { - unsigned j = iv.m_index; - m_pivot_queue.enqueue(i, j, rnz * (static_cast(m_columns[j].m_values.size()) - 1)); - } - } -} - -template -void square_sparse_matrix::set_max_in_rows() { - unsigned i = dimension(); - while (i--) - set_max_in_row(i); -} - - -template -void square_sparse_matrix::zero_shortened_markovitz_numbers() { - for (auto & ch : m_columns) - ch.zero_shortened_markovitz(); -} - -template -void square_sparse_matrix::prepare_for_factorization() { - zero_shortened_markovitz_numbers(); - set_max_in_rows(); - enqueue_domain_into_pivot_queue(); -} - -template -void square_sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { - for (auto p : rejected_pivots) { - m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); - } -} - -template -int square_sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { - auto & row_chunk = m_rows[i]; - - if (j == row_chunk[0].m_index) { - return 0; // the max element is at the head - } - T max = abs(row_chunk[0].m_value); - for (unsigned k = 1; k < row_chunk.size(); k++) { - auto &iv = row_chunk[k]; - if (iv.m_index == j) - return abs(iv.m_value) * c_partial_pivoting < max ? 1: 0; - } - return 2; // the element became zero but it still sits in the active pivots? -} - -template -bool square_sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { - unsigned arow = adjust_row(row); - for (auto & iv : m_rows[arow]) { - m_pivot_queue.remove(arow, iv.m_index); - m_n_of_active_elems--; // the value is correct only when refactoring - if (adjust_column_inverse(iv.m_index) <= row) - continue; // this column will be removed anyway - auto & col = m_columns[iv.m_index]; - - col.shorten_markovich_by_one(); - if (col.m_values.size() <= col.m_shortened_markovitz) - return false; // got a zero column - } - return true; -} - -template -void square_sparse_matrix::remove_pivot_column(unsigned row) { - unsigned acol = adjust_column(row); - for (const auto & iv : m_columns[acol].m_values) - if (adjust_row_inverse(iv.m_index) >= row) - m_pivot_queue.remove(iv.m_index, acol); -} - -template -void square_sparse_matrix::update_active_pivots(unsigned row) { - unsigned arow = adjust_row(row); - for (const auto & iv : m_rows[arow]) { - col_header & ch = m_columns[iv.m_index]; - int cols = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - lp_assert(cols >= 0); - for (const auto &ivc : ch.m_values) { - unsigned i = ivc.m_index; - if (adjust_row_inverse(i) <= row) continue; // the i is not an active row - m_pivot_queue.enqueue(i, iv.m_index, static_cast(m_rows[i].size())*cols); - } - } -} - -template -bool square_sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { - if (!remove_row_from_active_pivots_and_shorten_columns(row)) - return false; - remove_pivot_column(row); - // need to know the max priority of the queue here - update_active_pivots(row); - if (eta_matrix == nullptr) return true; - // it looks like double work, but the pivot scores have changed for all rows - // touched by eta_matrix - for (auto & it : eta_matrix->m_column_vector.m_data) { - unsigned row = adjust_row(it.first); - const auto & row_values = m_rows[row]; - unsigned rnz = static_cast(row_values.size()); - for (auto & iv : row_values) { - const col_header& ch = m_columns[iv.m_index]; - int cnz = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - lp_assert(cnz >= 0); - m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); - } - } - - return true; -} - -template -unsigned square_sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { - auto &cols = m_columns[j].m_values; - unsigned cnz = cols.size(); - for (auto & iv : cols) { - if (adjust_row_inverse(iv.m_index) < k) - cnz--; - } - lp_assert(cnz > 0); - return m_rows[i].m_values.size() * (cnz - 1); -} -#ifdef Z3DEBUG -template -bool square_sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { - unsigned arow = adjust_row(row); - auto & row_vals = m_rows[arow].m_values; - auto & begin_iv = row_vals[0]; - T row_max = abs(begin_iv.m_value); - lp_assert(adjust_column_inverse(begin_iv.m_index) >= k); - if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - for (unsigned jj = 1; jj < row_vals.size(); jj++) { - auto & iv = row_vals[jj]; - lp_assert(adjust_column_inverse(iv.m_index) >= k); - lp_assert(abs(iv.m_value) <= row_max); - if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; - if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - } - return false; -} - -template -bool square_sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { - unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); - for (unsigned ii = k; ii < dimension(); ii++) { - lp_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); - } - return true; -} -template -void square_sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { - out << "active matrix for k = " << k << std::endl; - if (k >= dimension()) { - out << "empty" << std::endl; - return; - } - unsigned dim = dimension() - k; - dense_matrix b(dim, dim); - for (unsigned i = 0; i < dim; i++) - for (unsigned j = 0; j < dim; j++ ) - b.set_elem(i, j, zero_of_type()); - for (int i = k; i < dimension(); i++) { - unsigned col = adjust_column(i); - for (auto &iv : get_column_values(col)) { - unsigned row = iv.m_index; - unsigned row_ex = this->adjust_row_inverse(row); - if (row_ex < k) continue; - auto v = this->get_not_adjusted(row, col); - b.set_elem(row_ex - k, i -k, v); - } - } - print_matrix(b, out); -} - -template -bool square_sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { - unsigned arow = adjust_row(i); - for (auto & iv : m_rows[arow].m_values) { - lp_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == - m_pivot_queue.get_priority(arow, iv.m_index)); - } - return true; -} - -template -bool square_sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { - for (unsigned i = k + 1; i < dimension(); i++ ) - lp_assert(pivot_queue_is_correct_for_row(i, k)); - lp_assert(m_pivot_queue.is_correct()); - return true; -} -#endif - -template -bool square_sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { - vector pivots_candidates_that_are_too_small; - while (!m_pivot_queue.is_empty()) { - m_pivot_queue.dequeue(i, j); - unsigned i_inv = adjust_row_inverse(i); - if (i_inv < k) continue; - unsigned j_inv = adjust_column_inverse(j); - if (j_inv < k) continue; - int _small = elem_is_too_small(i, j, c_partial_pivoting); - if (!_small) { -#ifdef Z3DEBUG - // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { - // print_active_matrix(k); - // lp_assert(false); - // } -#endif - recover_pivot_queue(pivots_candidates_that_are_too_small); - i = i_inv; - j = j_inv; - return true; - } - if (_small != 2) { // 2 means that the pair is not in the matrix - pivots_candidates_that_are_too_small.push_back(std::make_pair(i, j)); - } - } - recover_pivot_queue(pivots_candidates_that_are_too_small); - return false; -} - -template -bool square_sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { - if (&iv == &row_chunk[0]) { - return false; // the max element is at the head - } - T val = abs(iv.m_value); - T max = abs(row_chunk[0].m_value); - return val * c_partial_pivoting < max; -} - -template -bool square_sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { - vector> & row_chunk = get_row_values(i); - - for (indexed_value & iv : row_chunk) { - unsigned j = iv.m_index; - if (j == pivot_column) { - lp_assert(!col_is_active(j)); - continue; - } - m_columns[j].shorten_markovich_by_one(); - - if (m_columns[j].m_shortened_markovitz >= get_column_values(j).size()) { // got the zero column under the row! - return false; - } - } - return true; -} - -template -bool square_sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { - const vector> & col_chunk = get_column_values(adjust_column(j)); - bool is_unit = true; - for (const auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i > j) { - is_unit = false; - break; - } - if (i == j && iv.m_value != 1) { - is_unit = false; - break; - } - } - - if (is_unit) { - *eta = nullptr; - return true; - } - -#ifdef Z3DEBUG - *eta = new eta_matrix(j, dimension()); -#else - *eta = new eta_matrix(j); -#endif - for (const auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i < j) { - continue; - } - if (i > j) { - (*eta)->push_back(i, - iv.m_value); - } else { // i == j - if ( !(*eta)->set_diagonal_element(iv.m_value)) { - delete *eta; - *eta = nullptr; - return false; - } - - } - } - - (*eta)->divide_by_diagonal_element(); - return true; -} -#ifdef Z3DEBUG -template -bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { - for (unsigned i = 0; i < dimension(); i++) { - vector> const & row_chunk = get_row_values(i); - lp_assert(row_chunk.size()); - T const & max = abs(row_chunk[0].m_value); - unsigned ai = adjust_row_inverse(i); - for (auto & iv : row_chunk) { - lp_assert(abs(iv.m_value) <= max); - unsigned aj = adjust_column_inverse(iv.m_index); - if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) - return false; - if (aj == ai) { - if (iv.m_value != 1) { - return false; - } - } - if (settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value) && (!is_zero(iv.m_value))) - return false; - } - } - return true; -} - -template -bool square_sparse_matrix::is_upper_triangular_until(unsigned k) const { - for (unsigned j = 0; j < dimension() && j < k; j++) { - unsigned aj = adjust_column(j); - auto & col = get_column_values(aj); - for (auto & iv : col) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > j) - return false; - } - } - return true; -} - -template -void square_sparse_matrix::check_column_vs_rows(unsigned col) { - auto & mc = get_column_values(col); - for (indexed_value & column_iv : mc) { - indexed_value & row_iv = column_iv_other(column_iv); - if (row_iv.m_index != col) { - lp_assert(false); - } - - if (& row_iv_other(row_iv) != &column_iv) { - lp_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - lp_assert(false); - } - } -} - -template -void square_sparse_matrix::check_row_vs_columns(unsigned row) { - auto & mc = get_row_values(row); - for (indexed_value & row_iv : mc) { - indexed_value & column_iv = row_iv_other(row_iv); - - if (column_iv.m_index != row) { - lp_assert(false); - } - - if (& row_iv != & column_iv_other(column_iv)) { - lp_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - lp_assert(false); - } - } -} - -template -void square_sparse_matrix::check_rows_vs_columns() { - for (unsigned i = 0; i < dimension(); i++) { - check_row_vs_columns(i); - } -} - -template -void square_sparse_matrix::check_columns_vs_rows() { - for (unsigned i = 0; i < dimension(); i++) { - check_column_vs_rows(i); - } -} -template -void square_sparse_matrix::check_matrix() { - check_rows_vs_columns(); - check_columns_vs_rows(); -} -#endif -} - diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 2e8faeaedc5..b997b38f757 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -576,7 +576,7 @@ void test_lp_primal_core_solver() { - +#ifdef Z3DEBUG void fill_uniformly(dense_matrix & m, unsigned dim) { int v = 0; @@ -619,7 +619,7 @@ void test_dense_matrix() { auto c2 = d * p2; } - +#endif vector> vector_of_permutations() { From f33f8c265ee4ec52d37ec6c27c0e608fddaf6741 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 14:38:59 -0800 Subject: [PATCH 495/597] more cleanup --- src/math/lp/CMakeLists.txt | 2 - src/math/lp/eta_matrix.cpp | 43 ------- src/math/lp/eta_matrix.h | 98 ---------------- src/math/lp/eta_matrix_def.h | 151 ------------------------- src/math/lp/row_eta_matrix.cpp | 47 -------- src/math/lp/row_eta_matrix.h | 89 --------------- src/math/lp/row_eta_matrix_def.h | 188 ------------------------------- src/test/lp/lp.cpp | 58 ---------- 8 files changed, 676 deletions(-) delete mode 100644 src/math/lp/eta_matrix.cpp delete mode 100644 src/math/lp/eta_matrix.h delete mode 100644 src/math/lp/eta_matrix_def.h delete mode 100644 src/math/lp/row_eta_matrix.cpp delete mode 100644 src/math/lp/row_eta_matrix.h delete mode 100644 src/math/lp/row_eta_matrix_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index ef363509fcb..98241bf603e 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -4,7 +4,6 @@ z3_add_component(lp binary_heap_upair_queue.cpp core_solver_pretty_printer.cpp dense_matrix.cpp - eta_matrix.cpp emonics.cpp factorization.cpp factorization_factory_imp.cpp @@ -40,7 +39,6 @@ z3_add_component(lp nra_solver.cpp permutation_matrix.cpp random_updater.cpp - row_eta_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES util diff --git a/src/math/lp/eta_matrix.cpp b/src/math/lp/eta_matrix.cpp deleted file mode 100644 index 77f53842697..00000000000 --- a/src/math/lp/eta_matrix.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/numeric_pair.h" -#include "math/lp/eta_matrix_def.h" -#ifdef Z3DEBUG -template double lp::eta_matrix::get_elem(unsigned int, unsigned int) const; -template lp::mpq lp::eta_matrix::get_elem(unsigned int, unsigned int) const; -template lp::mpq lp::eta_matrix >::get_elem(unsigned int, unsigned int) const; -#endif -template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_right(vector&); -template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); -template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_right(vector&); -template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); -template void lp::eta_matrix >::apply_from_left(vector >&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_right(vector&); -template void lp::eta_matrix >::conjugate_by_permutation(lp::permutation_matrix >&); -template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_right(lp::indexed_vector&); -template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); -template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); diff --git a/src/math/lp/eta_matrix.h b/src/math/lp/eta_matrix.h deleted file mode 100644 index 14fe3ffea11..00000000000 --- a/src/math/lp/eta_matrix.h +++ /dev/null @@ -1,98 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/tail_matrix.h" -#include "math/lp/permutation_matrix.h" -namespace lp { - -// This is the sum of a unit matrix and a one-column matrix -template -class eta_matrix - : public tail_matrix { -#ifdef Z3DEBUG - unsigned m_length; -#endif - unsigned m_column_index; -public: - sparse_vector m_column_vector; - T m_diagonal_element; -#ifdef Z3DEBUG - eta_matrix(unsigned column_index, unsigned length): -#else - eta_matrix(unsigned column_index): -#endif - -#ifdef Z3DEBUG - m_length(length), -#endif - m_column_index(column_index) {} - - bool is_dense() const override { return false; } - - void print(std::ostream & out) { - print_matrix(*this, out); - } - - bool is_unit() { - return m_column_vector.size() == 0 && m_diagonal_element == 1; - } - - bool set_diagonal_element(T const & diagonal_element) { - m_diagonal_element = diagonal_element; - return !lp_settings::is_eps_small_general(diagonal_element, 1e-12); - } - - const T & get_diagonal_element() const { - return m_diagonal_element; - } - - void apply_from_left(vector & w, lp_settings & ) override; - - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings); - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local(w, settings); - } - - - void push_back(unsigned row_index, T val ) { - lp_assert(row_index != m_column_index); - m_column_vector.push_back(row_index, val); - } - - void apply_from_right(vector & w) override; - void apply_from_right(indexed_vector & w) override; - -#ifdef Z3DEBUG - T get_elem(unsigned i, unsigned j) const override; - unsigned row_count() const override { return m_length; } - unsigned column_count() const override { return m_length; } - void set_number_of_rows(unsigned m) override { m_length = m; } - void set_number_of_columns(unsigned n) override { m_length = n; } -#endif - void divide_by_diagonal_element() { - m_column_vector.divide(m_diagonal_element); - } - void conjugate_by_permutation(permutation_matrix & p); -}; -} diff --git a/src/math/lp/eta_matrix_def.h b/src/math/lp/eta_matrix_def.h deleted file mode 100644 index a6c908572f7..00000000000 --- a/src/math/lp/eta_matrix_def.h +++ /dev/null @@ -1,151 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/eta_matrix.h" -namespace lp { - -// This is the sum of a unit matrix and a one-column matrix -template -void eta_matrix::apply_from_left(vector & w, lp_settings & ) { - auto & w_at_column_index = w[m_column_index]; - for (auto & it : m_column_vector.m_data) { - w[it.first] += w_at_column_index * it.second; - } - w_at_column_index /= m_diagonal_element; -} -template -template -void eta_matrix:: -apply_from_left_local(indexed_vector & w, lp_settings & settings) { - const L w_at_column_index = w[m_column_index]; - if (is_zero(w_at_column_index)) return; - - if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) { - w[m_column_index] = zero_of_type(); - w.erase_from_index(m_column_index); - } - - for (auto & it : m_column_vector.m_data) { - unsigned i = it.first; - if (is_zero(w[i])) { - L v = w[i] = w_at_column_index * it.second; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - w[i] = zero_of_type(); - continue; - } - w.m_index.push_back(i); - } else { - L v = w[i] += w_at_column_index * it.second; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - w[i] = zero_of_type(); - w.erase_from_index(i); - } - } - } -} -template -void eta_matrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, get_number_of_rows()); - // deb.apply_from_right(clone_w); -#endif - T t = w[m_column_index] / m_diagonal_element; - for (auto & it : m_column_vector.m_data) { - t += w[it.first] * it.second; - } - w[m_column_index] = t; -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, get_number_of_rows())); - // delete clone_w; -#endif -} -template -void eta_matrix::apply_from_right(indexed_vector & w) { - if (w.m_index.empty()) - return; -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_from_right(wcopy); -#endif - T & t = w[m_column_index]; - t /= m_diagonal_element; - bool was_in_index = (!numeric_traits::is_zero(t)); - - for (auto & it : m_column_vector.m_data) { - t += w[it.first] * it.second; - } - - if (numeric_traits::precise() ) { - if (!numeric_traits::is_zero(t)) { - if (!was_in_index) - w.m_index.push_back(m_column_index); - } else { - if (was_in_index) - w.erase_from_index(m_column_index); - } - } else { - if (!lp_settings::is_eps_small_general(t, 1e-14)) { - if (!was_in_index) - w.m_index.push_back(m_column_index); - } else { - if (was_in_index) - w.erase_from_index(m_column_index); - t = zero_of_type(); - } - } - -#ifdef Z3DEBUG - // lp_assert(w.is_OK()); - // lp_assert(vectors_are_equal(wcopy, w.m_data)); -#endif -} -#ifdef Z3DEBUG -template -T eta_matrix::get_elem(unsigned i, unsigned j) const { - if (j == m_column_index){ - if (i == j) { - return 1 / m_diagonal_element; - } - return m_column_vector[i]; - } - - return i == j ? numeric_traits::one() : numeric_traits::zero(); -} -#endif -template -void eta_matrix::conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_column_index = p.get_rev(m_column_index); - for (auto & pair : m_column_vector.m_data) { - pair.first = p.get_rev(pair.first); - } -#ifdef Z3DEBUG - // lp_assert(deb == *this); -#endif -} -} diff --git a/src/math/lp/row_eta_matrix.cpp b/src/math/lp/row_eta_matrix.cpp deleted file mode 100644 index 356f80b3c01..00000000000 --- a/src/math/lp/row_eta_matrix.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/row_eta_matrix_def.h" -namespace lp { -template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); -template void row_eta_matrix >::conjugate_by_permutation(permutation_matrix >&); -template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); -#ifdef Z3DEBUG -template mpq row_eta_matrix::get_elem(unsigned int, unsigned int) const; -template mpq row_eta_matrix >::get_elem(unsigned int, unsigned int) const; -template double row_eta_matrix::get_elem(unsigned int, unsigned int) const; -#endif -template void row_eta_matrix::apply_from_left(vector&, lp_settings&); -template void row_eta_matrix::apply_from_right(vector&); -template void row_eta_matrix::apply_from_right(indexed_vector&); -template void row_eta_matrix >::apply_from_left(vector>&, lp_settings&); -template void row_eta_matrix >::apply_from_right(vector&); -template void row_eta_matrix >::apply_from_right(indexed_vector&); -template void row_eta_matrix::apply_from_left(vector&, lp_settings&); -template void row_eta_matrix::apply_from_right(vector&); -template void row_eta_matrix::apply_from_right(indexed_vector&); -template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix >::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix >::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -} diff --git a/src/math/lp/row_eta_matrix.h b/src/math/lp/row_eta_matrix.h deleted file mode 100644 index 50c500f007a..00000000000 --- a/src/math/lp/row_eta_matrix.h +++ /dev/null @@ -1,89 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include -#include "math/lp/sparse_vector.h" -#include "math/lp/indexed_vector.h" -#include "math/lp/permutation_matrix.h" -namespace lp { - // This is the sum of a unit matrix and a lower triangular matrix - // with non-zero elements only in one row -template -class row_eta_matrix - : public tail_matrix { -#ifdef Z3DEBUG - unsigned m_dimension; -#endif - unsigned m_row_start; - unsigned m_row; - sparse_vector m_row_vector; -public: -#ifdef Z3DEBUG - row_eta_matrix(unsigned row_start, unsigned row, unsigned dim): -#else - row_eta_matrix(unsigned row_start, unsigned row): -#endif - -#ifdef Z3DEBUG - m_dimension(dim), -#endif - m_row_start(row_start), m_row(row) { - } - - bool is_dense() const override { return false; } - - void print(std::ostream & out) { - print_matrix(*this, out); - } - - const T & get_diagonal_element() const { - return m_row_vector.m_data[m_row]; - } - - void apply_from_left(vector & w, lp_settings &) override; - - void apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings); - void apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings); - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local_to_T(w, settings); - } - - void push_back(unsigned row_index, T val ) { - lp_assert(row_index != m_row); - m_row_vector.push_back(row_index, val); - } - - void apply_from_right(vector & w) override; - void apply_from_right(indexed_vector & w) override; - - void conjugate_by_permutation(permutation_matrix & p); -#ifdef Z3DEBUG - T get_elem(unsigned row, unsigned col) const override; - unsigned row_count() const override { return m_dimension; } - unsigned column_count() const override { return m_dimension; } - void set_number_of_rows(unsigned m) override { m_dimension = m; } - void set_number_of_columns(unsigned n) override { m_dimension = n; } -#endif -}; // end of row_eta_matrix -} diff --git a/src/math/lp/row_eta_matrix_def.h b/src/math/lp/row_eta_matrix_def.h deleted file mode 100644 index faac5c6fe0b..00000000000 --- a/src/math/lp/row_eta_matrix_def.h +++ /dev/null @@ -1,188 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/row_eta_matrix.h" -namespace lp { -template -void row_eta_matrix::apply_from_left(vector & w, lp_settings &) { - // #ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, m_dimension); - // deb.apply_from_left(clone_w, settings); - // #endif - - auto & w_at_row = w[m_row]; - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - // w[m_row] = w_at_row; - // #ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); - // delete [] clone_w; - // #endif -} - -template -void row_eta_matrix::apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings) { - auto w_at_row = w[m_row]; - bool was_zero_at_m_row = is_zero(w_at_row); - - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ - if (was_zero_at_m_row) { - w.m_index.push_back(m_row); - } - w[m_row] = w_at_row; - } else if (!was_zero_at_m_row){ - w[m_row] = zero_of_type(); - auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); - w.m_index.erase(it); - } - // TBD: lp_assert(check_vector_for_small_values(w, settings)); -} - -template -void row_eta_matrix::apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings) { - auto w_at_row = w[m_row]; - bool was_zero_at_m_row = is_zero(w_at_row); - - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ - if (was_zero_at_m_row) { - w.m_index.push_back(m_row); - } - w[m_row] = w_at_row; - } else if (!was_zero_at_m_row){ - w[m_row] = zero_of_type(); - auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); - w.m_index.erase(it); - } - // TBD: does not compile lp_assert(check_vector_for_small_values(w, settings)); -} - -template -void row_eta_matrix::apply_from_right(vector & w) { - const T & w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, m_dimension); - // deb.apply_from_right(clone_w); -#endif - for (auto & it : m_row_vector.m_data) { - w[it.first] += w_row * it.second; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); - // delete clone_w; -#endif -} - -template -void row_eta_matrix::apply_from_right(indexed_vector & w) { - lp_assert(w.is_OK()); - const T & w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_from_right(wcopy); -#endif - if (numeric_traits::precise()) { - for (auto & it : m_row_vector.m_data) { - unsigned j = it.first; - bool was_zero = numeric_traits::is_zero(w[j]); - const T & v = w[j] += w_row * it.second; - - if (was_zero) { - if (!numeric_traits::is_zero(v)) - w.m_index.push_back(j); - } else { - if (numeric_traits::is_zero(v)) - w.erase_from_index(j); - } - } - } else { // the non precise version - const double drop_eps = 1e-14; - for (auto & it : m_row_vector.m_data) { - unsigned j = it.first; - bool was_zero = numeric_traits::is_zero(w[j]); - T & v = w[j] += w_row * it.second; - - if (was_zero) { - if (!lp_settings::is_eps_small_general(v, drop_eps)) - w.m_index.push_back(j); - else - v = zero_of_type(); - } else { - if (lp_settings::is_eps_small_general(v, drop_eps)) { - w.erase_from_index(j); - v = zero_of_type(); - } - } - } - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(wcopy, w.m_data)); - -#endif -} - -template -void row_eta_matrix::conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_row = p.apply_reverse(m_row); - // copy aside the column indices - vector columns; - for (auto & it : m_row_vector.m_data) - columns.push_back(it.first); - for (unsigned i = static_cast(columns.size()); i-- > 0;) - m_row_vector.m_data[i].first = p.get_rev(columns[i]); -#ifdef Z3DEBUG - // lp_assert(deb == *this); -#endif -} -#ifdef Z3DEBUG -template -T row_eta_matrix::get_elem(unsigned row, unsigned col) const { - if (row == m_row){ - if (col == row) { - return numeric_traits::one(); - } - return m_row_vector[col]; - } - - return col == row ? numeric_traits::one() : numeric_traits::zero(); -} -#endif -} - diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index b997b38f757..c993dbd7ddf 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -622,66 +622,8 @@ void test_dense_matrix() { #endif -vector> vector_of_permutations() { - vector> ret; - { - permutation_matrix p0(5); - p0[0] = 1; p0[1] = 2; p0[2] = 3; p0[3] = 4; - p0[4] = 0; - ret.push_back(p0); - } - { - permutation_matrix p0(5); - p0[0] = 2; p0[1] = 0; p0[2] = 1; p0[3] = 4; - p0[4] = 3; - ret.push_back(p0); - } - return ret; -} -void test_apply_reverse_from_right_to_perm(permutation_matrix & l) { - permutation_matrix p(5); - p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; - p[4] = 1; - permutation_matrix pclone(5); - pclone[0] = 4; pclone[1] = 2; pclone[2] = 0; pclone[3] = 3; - pclone[4] = 1; - - p.multiply_by_reverse_from_right(l); -#ifdef Z3DEBUG - auto rev = l.get_inverse(); - auto rs = pclone * rev; - lp_assert(p == rs) -#endif - } - -void test_apply_reverse_from_right() { - auto vec = vector_of_permutations(); - for (unsigned i = 0; i < vec.size(); i++) { - test_apply_reverse_from_right_to_perm(vec[i]); - } -} - -void test_permutations() { - std::cout << "test permutations" << std::endl; - test_apply_reverse_from_right(); - vector v; v.resize(5, 0); - v[1] = 1; - v[3] = 3; - permutation_matrix p(5); - p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; - p[4] = 1; - - indexed_vector vi(5); - vi.set_value(1, 1); - vi.set_value(3, 3); - - p.apply_reverse_from_right_to_T(v); - p.apply_reverse_from_right_to_T(vi); - lp_assert(vectors_are_equal(v, vi.m_data)); - lp_assert(vi.is_OK()); -} void lp_solver_test() { // lp_revised_solver lp_revised; From 569f5be91f4101eaa5cce897f84501b165f9c2b4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 15:35:54 -0800 Subject: [PATCH 496/597] rm dead code --- src/math/lp/lp_core_solver_base.h | 7 +- src/math/lp/lp_core_solver_base_def.h | 2 - src/math/lp/lp_primal_core_solver.h | 14 +-- src/math/lp/lp_primal_core_solver_def.h | 114 +----------------- .../lp/lp_primal_core_solver_tableau_def.h | 5 +- 5 files changed, 7 insertions(+), 135 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 5f723c1185f..cb275ebc908 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -86,21 +86,18 @@ class lp_core_solver_base { lp_settings & m_settings; vector m_y; // the buffer for yB = cb - // a device that is able to solve Bx=c, xB=d, and change the basis const column_namer & m_column_names; indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; const vector & m_lower_bounds; - const vector & m_upper_bounds; - vector m_column_norms; // the approximate squares of column norms that help choosing a profitable column + const vector & m_upper_bounds; vector m_copy_of_xB; unsigned m_basis_sort_counter; - vector m_steepest_edge_coefficients; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; - u_set* m_pivoted_rows; + u_set* m_pivoted_rows; bool m_look_for_feasible_solution_only; void start_tracing_basis_changes() { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 8b87728e090..474d39b572b 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -62,10 +62,8 @@ lp_core_solver_base(static_matrix & A, m_column_types(column_types), m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), - m_column_norms(m_n()), m_copy_of_xB(m_m()), m_basis_sort_counter(0), - m_steepest_edge_coefficients(A.column_count()), m_tracing_basis_changes(false), m_pivoted_rows(nullptr), m_look_for_feasible_solution_only(false) { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 10de624f55a..219cc2ad35d 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -369,24 +369,12 @@ class lp_primal_core_solver:public lp_core_solver_base { unsigned get_number_of_non_basic_column_to_try_for_enter(); - void print_column_norms(std::ostream & out); // returns the number of iterations unsigned solve(); - // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" - void init_column_norms(); - - T calculate_column_norm_exactly(unsigned j); - - void update_or_init_column_norms(unsigned entering, unsigned leaving); - - // following Swietanowski - A new steepest ... - void update_column_norms(unsigned entering, unsigned leaving); - - T calculate_norm_of_entering_exactly(); - + void find_feasible_solution(); // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7a027f8d38c..dac1c15face 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -58,14 +58,8 @@ void lp_primal_core_solver::sort_non_basis() { sort_non_basis_rational(); return; } - for (unsigned j : this->m_nbasis) { - T const & da = this->m_d[j]; - this->m_steepest_edge_coefficients[j] = da * da / this->m_column_norms[j]; - } - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { - return this->m_steepest_edge_coefficients[a] > this->m_steepest_edge_coefficients[b]; - }); - + + m_non_basis_list.clear(); // reinit m_basis_heading for (unsigned j = 0; j < this->m_nbasis.size(); j++) { @@ -190,41 +184,7 @@ int lp_primal_core_solver::choose_entering_column_presize(unsigned number_ template int lp_primal_core_solver::choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - if (numeric_traits::precise()) - return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); - if (number_of_benefitial_columns_to_go_over == 0) - return -1; - if (this->m_basis_sort_counter == 0) { - sort_non_basis(); - this->m_basis_sort_counter = 20; - } else { - this->m_basis_sort_counter--; - } - T steepest_edge = zero_of_type(); - std::list::iterator entering_iter = m_non_basis_list.end(); - for (auto non_basis_iter= m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { - unsigned j = *non_basis_iter; - if (!column_is_benefitial_for_entering_basis(j)) - continue; - - // if we are here then j is a candidate to enter the basis - T dj = this->m_d[j]; - T t = dj * dj / this->m_column_norms[j]; - if (t > steepest_edge) { - steepest_edge = t; - entering_iter = non_basis_iter; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; - } - }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); - if (entering_iter != m_non_basis_list.end()) { - unsigned entering = *entering_iter; - m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; - m_non_basis_list.erase(entering_iter); - m_non_basis_list.push_back(entering); - return entering; - } - return -1; + return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); } @@ -607,13 +567,6 @@ template unsigned lp_primal_core_solver::get_num return std::max(static_cast(this->m_settings.random_next() % ret), 1u); } -template void lp_primal_core_solver::print_column_norms(std::ostream & out) { - out << " column norms " << std::endl; - for (unsigned j = 0; j < this->m_n(); j++) { - out << this->m_column_norms[j] << " "; - } - out << std::endl; - } // returns the number of iterations template unsigned lp_primal_core_solver::solve() { @@ -677,67 +630,6 @@ template unsigned lp_primal_core_solver::solve() return this->total_iterations(); } -// according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" -template void lp_primal_core_solver::init_column_norms() { - lp_assert(numeric_traits::precise() == false); - for (unsigned j = 0; j < this->m_n(); j++) { - this->m_column_norms[j] = T(static_cast(this->m_A.m_columns[j].size() + 1)) - - + T(static_cast(this->m_settings.random_next() % 10000)) / T(100000); - } -} - -// debug only -template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - lp_assert(false); -} - -template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); - if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { - m_column_norm_update_counter = 0; - init_column_norms(); - } else { - m_column_norm_update_counter++; - update_column_norms(entering, leaving); - } -} - -// following Swietanowski - A new steepest ... -template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - T pivot = this->m_pivot_row[entering]; - T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; - if (!numeric_traits::precise()) { - if (g_ent < T(0.000001)) - g_ent = T(0.000001); - } - this->m_column_norms[leaving] = g_ent; - - for (unsigned j : this->m_pivot_row.m_index) { - if (j == leaving) - continue; - const T & t = this->m_pivot_row[j]; - T s = this->m_A.dot_product_with_column(m_beta.m_data, j); - T k = -2 / pivot; - T tp = t/pivot; - if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column - this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 - 1 + tp * tp); - } - } -} - -template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { - T r = numeric_traits::one(); - for (auto i : this->m_ed.m_index) { - T t = this->m_ed[i]; - r += t * t; - } - return r; -} - // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { this->m_look_for_feasible_solution_only = true; diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index b63c54bfd10..a63a6be17ad 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -295,10 +295,7 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } + if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); From 9ec82632a3e3e746b6fed4b6ea6b85f8e8f587a1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 08:20:32 -0800 Subject: [PATCH 497/597] rp precise --- src/math/lp/core_solver_pretty_printer_def.h | 4 +- src/math/lp/lar_solver.cpp | 45 ++---- src/math/lp/lar_solver.h | 3 +- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 9 +- src/math/lp/lp_core_solver_base_def.h | 18 +-- src/math/lp/lp_primal_core_solver.h | 93 ++--------- src/math/lp/lp_primal_core_solver_def.h | 152 +----------------- .../lp/lp_primal_core_solver_tableau_def.h | 11 +- src/math/lp/lp_settings.h | 9 +- src/math/lp/lp_settings_def.h | 26 +-- src/math/lp/lp_utils.h | 3 - src/math/lp/matrix_def.h | 13 +- src/math/lp/numeric_pair.h | 8 - src/math/lp/static_matrix.h | 1 - 15 files changed, 48 insertions(+), 350 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 6971061832b..2f1d99d22c6 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -88,7 +88,6 @@ template T core_solver_pretty_printer::current_co } template void core_solver_pretty_printer::init_m_A_and_signs() { - if (numeric_traits::precise() ) { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); for (const auto & c : m_core_solver.m_A.m_columns[column]){ @@ -115,8 +114,7 @@ template void core_solver_pretty_printer::init_m_ name); m_rs[row] += t[row] * m_core_solver.m_x[column]; } - } - } + } } template void core_solver_pretty_printer::init_column_widths() { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9bc850848e5..9b2361b74f4 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -281,9 +281,9 @@ namespace lp { unsigned m = A_r().row_count(); clean_popped_elements(m, m_rows_with_changed_bounds); clean_inf_set_of_r_solver_after_pop(); - lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - ( m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau())); - + lp_assert( + m_settings.simplex_strategy() == simplex_strategy_enum::undecided || + m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_constraints.pop(k); m_term_count.pop(k); @@ -627,10 +627,6 @@ namespace lp { m_rows_with_changed_bounds.insert(rid); } - void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { - lp_assert(false); - } - void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { @@ -676,20 +672,16 @@ namespace lp { } void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair& delta) { - { - for (const auto& c : A_r().m_columns[j]) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; - if (tableau_with_costs()) { - m_basic_columns_with_changed_cost.insert(bj); - } - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -A_r().get_val(c) * delta); - TRACE("change_x_del", - tout << "changed basis column " << bj << ", it is " << - (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj) ? "feas" : "inf") << std::endl;); - - } + for (const auto& c : A_r().m_columns[j]) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; + if (tableau_with_costs()) { + m_basic_columns_with_changed_cost.insert(bj); + } + m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -A_r().get_val(c) * delta); + TRACE("change_x_del", + tout << "changed basis column " << bj << ", it is " << + (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj) ? "feas" : "inf") << std::endl;); } - } void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { @@ -819,19 +811,6 @@ namespace lp { A.set(last_row, basis_j, mpq(1)); } - template - void lar_solver::create_matrix_A(static_matrix& matr) { - lp_assert(false); // not implemented - /* - unsigned m = number_or_nontrivial_left_sides(); - unsigned n = m_vec_of_canonic_left_sides.size(); - if (matr.row_count() == m && matr.column_count() == n) - return; - matr.init_empty_matrix(m, n); - copy_from_mpq_matrix(matr); - */ - } - template void lar_solver::copy_from_mpq_matrix(static_matrix& matr) { matr.m_rows.resize(A_r().row_count()); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index b925b5ef039..356c86c2f9b 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -205,10 +205,9 @@ class lar_solver : public column_namer { void set_lower_bound_witness(var_index j, constraint_index ci); void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, vector> &left_side) const; - void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); + void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); bool use_tableau_costs() const; - void detect_rows_of_column_with_bound_change(unsigned j); bool tableau_with_costs() const; bool costs_are_used() const; void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index b76976d371c..059d801a03c 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -43,7 +43,6 @@ template bool lp::lp_core_solver_base::print_statistics_with_ite template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; @@ -51,7 +50,6 @@ template void lp::lp_core_solver_base::fill_reduced_costs_from template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); @@ -61,7 +59,6 @@ template lp::lp_core_solver_base >::lp_core_s const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); -template void lp::lp_core_solver_base >::solve_Ax_eq_b(); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index cb275ebc908..6fa2ddead1c 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -238,11 +238,11 @@ class lp_core_solver_base { } bool below_bound(const X & x, const X & bound) const { - return precise()? x < bound : below_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); + return x < bound ; } bool above_bound(const X & x, const X & bound) const { - return precise()? x > bound : above_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); + return x > bound ; } bool x_below_low_bound(unsigned p) const { @@ -323,9 +323,6 @@ class lp_core_solver_base { void find_error_in_BxB(vector& rs); - // recalculates the projection of x to B, such that Ax = b, whereab is the right side - void solve_Ax_eq_b(); - bool snap_non_basic_x_to_bound() { bool ret = false; for (unsigned j : non_basis()) @@ -628,8 +625,6 @@ class lp_core_solver_base { bool pivot_column_tableau(unsigned j, unsigned row_index); bool divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col); - bool precise() const { return numeric_traits::precise(); } - simplex_strategy_enum simplex_strategy() const { return m_settings.simplex_strategy(); } diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 474d39b572b..213d67e6ea0 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -233,18 +233,12 @@ column_is_dual_feasible(unsigned j) const { } template bool lp_core_solver_base:: d_is_not_negative(unsigned j) const { - if (numeric_traits::precise()) { - return m_d[j] >= numeric_traits::zero(); - } - return m_d[j] > -T(0.00001); + return m_d[j] >= numeric_traits::zero(); } template bool lp_core_solver_base:: d_is_not_positive(unsigned j) const { - if (numeric_traits::precise()) { - return m_d[j] <= numeric_traits::zero(); - } - return m_d[j] < T(0.00001); + return m_d[j] <= numeric_traits::zero(); } @@ -319,7 +313,6 @@ template bool lp_core_solver_base::inf_set_is_cor template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { - lp_assert(numeric_traits::precise()); int pivot_index = -1; auto & row = m_A.m_rows[pivot_row]; unsigned size = row.size(); @@ -517,13 +510,6 @@ find_error_in_BxB(vector& rs){ } } -// recalculates the projection of x to B, such that Ax = b -template void lp_core_solver_base:: -solve_Ax_eq_b() { - lp_assert(false); -} - - template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 219cc2ad35d..aa771d7ee80 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -511,7 +511,6 @@ class lp_primal_core_solver:public lp_core_solver_base { bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller lp_assert(m < 0); - if (numeric_traits::precise()) { if (this->below_bound(x, bound)) return false; if (this->above_bound(x, bound)) { limit_theta((bound - x) / m, theta, unlimited); @@ -519,59 +518,30 @@ class lp_primal_core_solver:public lp_core_solver_base { theta = zero_of_type(); unlimited = false; } - } else { - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - } return true; } bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets larger lp_assert(m > 0); - if (numeric_traits::precise()) { - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + if (this->above_bound(x, bound)) return false; + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); } else { - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + theta = zero_of_type(); + unlimited = false; } + return true; } void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - if (numeric_traits::precise()) { - // x gets larger - lp_assert(m > 0); - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } - } - else { // x gets larger - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } - } + lp_assert(m > 0); + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } + } void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { @@ -877,46 +847,13 @@ class lp_primal_core_solver:public lp_core_solver_base { m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - if (!(numeric_traits::precise())) { - m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - } else { - m_converted_harris_eps = zero_of_type(); - } + + m_converted_harris_eps = zero_of_type(); + this->set_status(lp_status::UNKNOWN); } - // constructor - lp_primal_core_solver(static_matrix & A, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - const vector & column_type_array, - const vector & upper_bound_values, - lp_settings & settings, - const column_namer& column_names): - lp_core_solver_base(A, // b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - m_lower_bounds_dummy, - upper_bound_values), - m_beta(A.row_count()), - m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { - lp_assert(initial_x_is_correct()); - m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); - m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); -#ifdef Z3DEBUG - lp_assert(false); -#endif - } + bool initial_x_is_correct() { std::set basis_set; diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index dac1c15face..fb62bbf540e 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -33,9 +33,7 @@ namespace lp { template void lp_primal_core_solver::sort_non_basis_rational() { - lp_assert(numeric_traits::precise()); - - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { + std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); if (ca == 0 && cb != 0) return false; @@ -54,57 +52,15 @@ void lp_primal_core_solver::sort_non_basis_rational() { template void lp_primal_core_solver::sort_non_basis() { - if (numeric_traits::precise()) { - sort_non_basis_rational(); - return; - } - - - m_non_basis_list.clear(); - // reinit m_basis_heading - for (unsigned j = 0; j < this->m_nbasis.size(); j++) { - unsigned col = this->m_nbasis[j]; - this->m_basis_heading[col] = - static_cast(j) - 1; - m_non_basis_list.push_back(col); - } + sort_non_basis_rational(); } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { - if (numeric_traits::precise()) - return column_is_benefitial_for_entering_basis_precise(j); - const T& dj = this->m_d[j]; - switch (this->m_column_types[j]) { - case column_type::fixed: break; - case column_type::free_column: - if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) - return true; - break; - case column_type::lower_bound: - if (dj > m_epsilon_of_reduced_cost) return true;; - break; - case column_type::upper_bound: - if (dj < -m_epsilon_of_reduced_cost) return true; - break; - case column_type::boxed: - if (dj > m_epsilon_of_reduced_cost) { - if (this->m_x[j] < this->m_upper_bounds[j] - this->bound_span(j)/2) - return true; - break; - } else if (dj < - m_epsilon_of_reduced_cost) { - if (this->m_x[j] > this->m_lower_bounds[j] + this->bound_span(j)/2) - return true; - } - break; - default: - lp_unreachable(); - break; - } - return false; + return column_is_benefitial_for_entering_basis_precise(j); } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { - lp_assert (numeric_traits::precise()); const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { @@ -144,7 +100,6 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis template int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -231,8 +186,6 @@ find_leaving_on_harris_theta(X const & harris_theta, X & t) { } if (++k == steps) k = 0; } while (k != initial_k); - if (!this->precise()) - restore_harris_eps(); return leaving; } @@ -479,47 +432,14 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e // return 0 if the reduced cost at entering is close enough to the refreshed // 1 if it is way off, and 2 if it is unprofitable template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { - if (numeric_traits::precise()) return 0; - T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed - lp_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); - T refreshed_cost = this->m_costs[entering]; - unsigned i = this->m_m(); - while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; - this->m_d[entering] = refreshed_cost; - T delta = abs(reduced_at_entering_was - refreshed_cost); - if (delta * 2 > abs(reduced_at_entering_was)) { - // this->m_status = UNSTABLE; - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entering - } - return 1; // go on with this entering - } else { - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entering - } - } return 0; + } template void lp_primal_core_solver::backup_and_normalize_costs() { if (this->m_look_for_feasible_solution_only) return; // no need to backup cost, since we are going to use only feasibility costs - if (numeric_traits::precise() ) { - m_costs_backup = this->m_costs; - } else { - T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); - lp_assert(m_costs_backup.size() == 0); - for (unsigned j = 0; j < this->m_costs.size(); j++) - m_costs_backup.push_back(this->m_costs[j] /= cost_max); - } + m_costs_backup = this->m_costs; } template void lp_primal_core_solver::init_run() { @@ -527,10 +447,6 @@ template void lp_primal_core_solver::init_run( } -template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ - lp_assert(false); -} - template void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { @@ -571,63 +487,7 @@ template unsigned lp_primal_core_solver::get_num // returns the number of iterations template unsigned lp_primal_core_solver::solve() { TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); - if (numeric_traits::precise()) - return solve_with_tableau(); - - init_run(); - if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FEASIBLE); - return 0; - } - - - do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf" : "feas"), * this->m_settings.get_message_ostream())) { - return this->total_iterations(); - } - one_iteration(); - - TRACE("lar_solver", tout << "one iteration: " << this->get_status() << "\n";); - lp_assert(!this->using_infeas_costs() || this->costs_on_nbasis_are_zeros()); - switch (this->get_status()) { - case lp_status::OPTIMAL: // double check that we are at optimum - case lp_status::INFEASIBLE: - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - break; - { // precise case - - } - break; - case lp_status::TENTATIVE_UNBOUNDED: - lp_assert(false); - break; - case lp_status::UNBOUNDED: - lp_assert(false); - break; - - case lp_status::UNSTABLE: - lp_assert(false); - break; - - default: - break; // do nothing - } - } while ( - this->get_status() != lp_status::UNBOUNDED - && - this->get_status() != lp_status::OPTIMAL - && - this->get_status() != lp_status::INFEASIBLE - && - this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements - && - !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); - - lp_assert( - this->current_x_is_feasible() == false - || - this->calc_current_x_is_feasible_include_non_basis()); - return this->total_iterations(); + return solve_with_tableau(); } // calling it stage1 is too cryptic diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index a63a6be17ad..96df997b17c 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -43,19 +43,10 @@ template void lp_primal_core_solver::advance_on_e } advance_on_entering_and_leaving_tableau(entering, leaving, t); } -/* -template int lp_primal_core_solver::choose_entering_column_tableau_rows() { - int i = find_inf_row(); - if (i == -1) - return -1; - return find_shortest_beneficial_column_in_row(i); - } -*/ template int lp_primal_core_solver::choose_entering_column_tableau() { //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -294,7 +285,7 @@ template void lp_primal_core_solver::init_run_tab return; if (this->m_settings.backup_costs) backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); + m_epsilon_of_reduced_cost = zero_of_type(); if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 38270230e7e..86a97e615e9 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -78,9 +78,8 @@ enum class lp_status { // when the ratio of the vector length to domain size to is greater than the return value we switch to solve_By_for_T_indexed_only template unsigned ratio_of_index_size_to_all_size() { - if (numeric_traits::precise()) return 10; - return 120; + } const char* lp_status_to_string(lp_status status); @@ -274,7 +273,7 @@ struct lp_settings { statistics const& stats() const { return m_stats; } template static bool is_eps_small_general(const T & t, const double & eps) { - return (!numeric_traits::precise())? is_epsilon_small(t, eps) : numeric_traits::is_zero(t); + return numeric_traits::is_zero(t); } template @@ -373,9 +372,7 @@ inline std::string T_to_string(const mpq & t) { template bool val_is_smaller_than_eps(T const & t, double const & eps) { - if (!numeric_traits::precise()) { - return numeric_traits::get_double(t) < eps; - } + return t <= numeric_traits::zero(); } diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index bb87109f6fb..a439466d18c 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -70,19 +70,12 @@ lp_status lp_status_from_string(std::string status) { template bool vectors_are_equal(T * a, vector &b, unsigned n) { - if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } - } else { - for (unsigned i = 0; i < n; i ++){ - if (std::abs(numeric_traits::get_double(a[i] - b[i])) > 0.000001) { - return false; - } - } - } + return true; } @@ -91,27 +84,12 @@ template bool vectors_are_equal(const vector & a, const vector &b) { unsigned n = static_cast(a.size()); if (n != b.size()) return false; - if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } - } else { - for (unsigned i = 0; i < n; i ++){ - double da = numeric_traits::get_double(a[i]); - double db = numeric_traits::get_double(b[i]); - double amax = std::max(fabs(da), fabs(db)); - if (amax > 1) { - da /= amax; - db /= amax; - } - - if (fabs(da - db) > 0.000001) { - return false; - } - } - } + return true; } #ifdef Z3DEBUG diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index 40c5f063282..ad5ba380d91 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -153,9 +153,6 @@ template inline X ceil_ratio(const X & a, const X & b) { return num template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(a, b); } -template inline bool precise() { return numeric_traits::precise(); } - - // returns true if a factor of b template bool is_proper_factor(const T & a, const T & b) { diff --git a/src/math/lp/matrix_def.h b/src/math/lp/matrix_def.h index 95810bd5a96..e3ac08f7e03 100644 --- a/src/math/lp/matrix_def.h +++ b/src/math/lp/matrix_def.h @@ -32,16 +32,9 @@ bool matrix::is_equal(const matrix& other) { for (unsigned j = 0; j < column_count(); j++) { auto a = get_elem(i, j); auto b = other.get_elem(i, j); - if (numeric_traits::precise()) { - if (a != b) return false; - } else if (fabs(numeric_traits::get_double(a - b)) > 0.000001) { - // cout << "returning false from operator== of matrix comparison" << endl; - // cout << "this matrix is " << endl; - // print_matrix(*this); - // cout << "other matrix is " << endl; - // print_matrix(other); - return false; - } + + if (a != b) return false; + } } return true; diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 9f7e27c18a1..f59aa84ba52 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -45,7 +45,6 @@ template class numeric_traits {}; template <> class numeric_traits { public: - static bool precise() { return true; } static unsigned zero() { return 0; } static unsigned one() { return 1; } static bool is_zero(unsigned v) { return v == 0; } @@ -56,7 +55,6 @@ template <> class numeric_traits { template <> class numeric_traits { public: - static bool precise() { return true; } static int zero() { return 0; } static int one() { return 1; } static bool is_zero(int v) { return v == 0; } @@ -71,7 +69,6 @@ template <> class numeric_traits { template <> class numeric_traits { public: - static bool precise() { return false; } static double g_zero; static double const &zero() { return g_zero; } static double g_one; @@ -88,7 +85,6 @@ template <> class numeric_traits { template<> class numeric_traits { public: - static bool precise() { return true; } static rational const & zero() { return rational::zero(); } static rational const & one() { return rational::one(); } static bool is_zero(const rational & v) { return v.is_zero(); } @@ -251,8 +247,6 @@ struct numeric_pair { return numeric_pair(-x, -y); } - static bool precize() { return lp::numeric_traits::precize();} - bool is_zero() const { return x.is_zero() && y.is_zero(); } bool is_pos() const { return x.is_pos() || (x.is_zero() && y.is_pos());} @@ -294,12 +288,10 @@ numeric_pair operator/(const numeric_pair & r, const X & a) { return numeric_pair(r.x / a, r.y / a); } -// template bool precise() { return numeric_traits::precise();} template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} template class numeric_traits> { public: - static bool precise() { return numeric_traits::precise();} static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index a429268530d..7f81cae79f5 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -344,7 +344,6 @@ class static_matrix void fill_last_row_with_pivoting(const term& row, unsigned bj, // the index of the basis column const vector & basis_heading) { - lp_assert(numeric_traits::precise()); lp_assert(row_count() > 0); m_work_vector.resize(column_count()); T a; From f351eb3ab276e9ec084311ecd9c807a794412278 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 08:52:08 -0800 Subject: [PATCH 498/597] remove many methods dealing with double Signed-off-by: Lev Nachmanson --- src/math/lp/binary_heap_priority_queue.cpp | 3 - src/math/lp/conversion_helper.h | 23 ----- src/math/lp/core_solver_pretty_printer.cpp | 3 - src/math/lp/dense_matrix.cpp | 6 -- src/math/lp/dense_matrix.h | 6 +- src/math/lp/dense_matrix_def.h | 11 --- src/math/lp/indexed_vector.cpp | 18 ---- src/math/lp/indexed_vector.h | 55 +----------- src/math/lp/indexed_vector_def.h | 29 +------ src/math/lp/lar_core_solver.h | 44 +--------- src/math/lp/lar_solver.cpp | 75 +--------------- src/math/lp/lar_solver.h | 4 - src/math/lp/lp_core_solver_base.cpp | 27 ------ src/math/lp/lp_core_solver_base.h | 6 +- src/math/lp/lp_core_solver_base_def.h | 17 ---- src/math/lp/lp_primal_core_solver.cpp | 4 - src/math/lp/lp_primal_core_solver.h | 4 +- src/math/lp/lp_primal_core_solver_def.h | 12 --- .../lp/lp_primal_core_solver_tableau_def.h | 2 +- src/math/lp/lp_settings.cpp | 1 - src/math/lp/lp_settings.h | 86 ++----------------- src/math/lp/lp_utils.cpp | 3 +- src/math/lp/matrix.cpp | 2 - src/math/lp/permutation_matrix.cpp | 16 ---- src/math/lp/permutation_matrix_def.h | 6 +- src/math/lp/sparse_vector.h | 1 - src/math/lp/static_matrix.cpp | 20 ----- src/math/lp/static_matrix.h | 1 - 28 files changed, 20 insertions(+), 465 deletions(-) diff --git a/src/math/lp/binary_heap_priority_queue.cpp b/src/math/lp/binary_heap_priority_queue.cpp index bbe735e58bd..ec6630b1dbb 100644 --- a/src/math/lp/binary_heap_priority_queue.cpp +++ b/src/math/lp/binary_heap_priority_queue.cpp @@ -23,15 +23,12 @@ namespace lp { template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); template void binary_heap_priority_queue::enqueue(unsigned int, int const&); -template void binary_heap_priority_queue::enqueue(unsigned int, double const&); template void binary_heap_priority_queue::enqueue(unsigned int, mpq const&); template void binary_heap_priority_queue::remove(unsigned int); template unsigned binary_heap_priority_queue >::dequeue(); -template unsigned binary_heap_priority_queue::dequeue(); template unsigned binary_heap_priority_queue::dequeue(); template void binary_heap_priority_queue >::enqueue(unsigned int, numeric_pair const&); template void binary_heap_priority_queue >::resize(unsigned int); -template void lp::binary_heap_priority_queue::resize(unsigned int); template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template void binary_heap_priority_queue::resize(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); diff --git a/src/math/lp/conversion_helper.h b/src/math/lp/conversion_helper.h index feb99974376..ba8b6a9835f 100644 --- a/src/math/lp/conversion_helper.h +++ b/src/math/lp/conversion_helper.h @@ -31,28 +31,5 @@ struct conversion_helper { } }; -template<> -struct conversion_helper { - static double get_upper_bound(const column_info & ci) { - if (!ci.upper_bound_is_strict()) - return ci.get_upper_bound().get_double(); - double eps = 0.00001; - if (!ci.lower_bound_is_set()) - return ci.get_upper_bound().get_double() - eps; - eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); - return ci.get_upper_bound().get_double() - eps; - } - - static double get_lower_bound(const column_info & ci) { - if (!ci.lower_bound_is_strict()) - return ci.get_lower_bound().get_double(); - double eps = 0.00001; - if (!ci.upper_bound_is_set()) - return ci.get_lower_bound().get_double() + eps; - eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); - return ci.get_lower_bound().get_double() + eps; - } - -}; } diff --git a/src/math/lp/core_solver_pretty_printer.cpp b/src/math/lp/core_solver_pretty_printer.cpp index 74d2f6048ea..18bef83030f 100644 --- a/src/math/lp/core_solver_pretty_printer.cpp +++ b/src/math/lp/core_solver_pretty_printer.cpp @@ -19,9 +19,6 @@ Revision History: --*/ #include "math/lp/numeric_pair.h" #include "math/lp/core_solver_pretty_printer_def.h" -template lp::core_solver_pretty_printer::core_solver_pretty_printer(const lp::lp_core_solver_base &, std::ostream & out); -template void lp::core_solver_pretty_printer::print(); -template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); template lp::core_solver_pretty_printer::core_solver_pretty_printer(const lp::lp_core_solver_base &, std::ostream & out); template void lp::core_solver_pretty_printer::print(); template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); diff --git a/src/math/lp/dense_matrix.cpp b/src/math/lp/dense_matrix.cpp index f12e688d397..25fc65a5d41 100644 --- a/src/math/lp/dense_matrix.cpp +++ b/src/math/lp/dense_matrix.cpp @@ -21,11 +21,6 @@ Revision History: #include "math/lp/dense_matrix_def.h" #ifdef Z3DEBUG #include "util/vector.h" -template lp::dense_matrix lp::operator*(lp::matrix&, lp::matrix&); -template void lp::dense_matrix::apply_from_left(vector &); -template lp::dense_matrix::dense_matrix(lp::matrix const*); -template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); -template lp::dense_matrix& lp::dense_matrix::operator=(lp::dense_matrix const&); template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); template lp::dense_matrix >::dense_matrix(lp::matrix > const*); template void lp::dense_matrix >::apply_from_left(vector&); @@ -35,6 +30,5 @@ template lp::dense_matrix >::dense_matrix(uns template lp::dense_matrix >& lp::dense_matrix >::operator=(lp::dense_matrix > const&); template lp::dense_matrix > lp::operator* >(lp::matrix >&, lp::matrix >&); template void lp::dense_matrix >::apply_from_right( vector< lp::mpq> &); -template void lp::dense_matrix::apply_from_right(vector &); template void lp::dense_matrix::apply_from_left(vector&); #endif diff --git a/src/math/lp/dense_matrix.h b/src/math/lp/dense_matrix.h index 342fc7aebe6..fcc85cdd17d 100644 --- a/src/math/lp/dense_matrix.h +++ b/src/math/lp/dense_matrix.h @@ -90,11 +90,7 @@ class dense_matrix: public matrix { void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; } - // This method pivots row i to row i0 by muliplying row i by - // alpha and adding it to row i0. - void pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, - const double & pivot_epsilon); - + // This method pivots void swap_columns(unsigned a, unsigned b); void swap_rows(unsigned a, unsigned b); diff --git a/src/math/lp/dense_matrix_def.h b/src/math/lp/dense_matrix_def.h index 8eb9ad5dd50..e850a9acd95 100644 --- a/src/math/lp/dense_matrix_def.h +++ b/src/math/lp/dense_matrix_def.h @@ -150,17 +150,6 @@ template void dense_matrix::apply_from_left_to_X( } } -// This method pivots row i to row i0 by muliplying row i by -// alpha and adding it to row i0. -template void dense_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, - const double & pivot_epsilon) { - for (unsigned j = 0; j < m_n; j++) { - m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha; - if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) { - m_values[i0 + m_n + j] = numeric_traits::zero();; - } - } -} template void dense_matrix::swap_columns(unsigned a, unsigned b) { for (unsigned i = 0; i < m_m; i++) { diff --git a/src/math/lp/indexed_vector.cpp b/src/math/lp/indexed_vector.cpp index d7cde2e1de5..fe089254186 100644 --- a/src/math/lp/indexed_vector.cpp +++ b/src/math/lp/indexed_vector.cpp @@ -20,10 +20,6 @@ Revision History: #include "util/vector.h" #include "math/lp/indexed_vector_def.h" namespace lp { -template void indexed_vector::clear(); -template void indexed_vector::clear_all(); -template void indexed_vector::erase_from_index(unsigned int); -template void indexed_vector::set_value(const double&, unsigned int); template void indexed_vector::clear(); template void indexed_vector::clear(); template void indexed_vector::clear_all(); @@ -32,22 +28,8 @@ template void indexed_vector::resize(unsigned int); template void indexed_vector::resize(unsigned int); template void indexed_vector::set_value(const mpq&, unsigned int); template void indexed_vector::set_value(const unsigned&, unsigned int); -#ifdef Z3DEBUG -template bool indexed_vector::is_OK() const; -template bool indexed_vector::is_OK() const; -template bool indexed_vector::is_OK() const; -template bool indexed_vector >::is_OK() const; -#endif template void lp::indexed_vector< lp::mpq>::print(std::basic_ostream > &); -template void lp::indexed_vector::print(std::basic_ostream > &); template void lp::indexed_vector >::print(std::ostream&); } -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector >(vector> const&, std::ostream&); -template void lp::indexed_vector::resize(unsigned int); -// template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); -// template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector >::erase_from_index(unsigned int); diff --git a/src/math/lp/indexed_vector.h b/src/math/lp/indexed_vector.h index 017a25f6be2..9f3119e9a16 100644 --- a/src/math/lp/indexed_vector.h +++ b/src/math/lp/indexed_vector.h @@ -99,47 +99,9 @@ class indexed_vector { return m_data[i]; } - void clean_up() { -#if 0==1 - for (unsigned k = 0; k < m_index.size(); k++) { - unsigned i = m_index[k]; - T & v = m_data[i]; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - m_index.erase(m_index.begin() + k--); - } - } -#endif - vector index_copy; - for (unsigned i : m_index) { - T & v = m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)) { - index_copy.push_back(i); - } else if (!numeric_traits::is_zero(v)) { - v = zero_of_type(); - } - } - m_index = index_copy; - } - void erase_from_index(unsigned j); - - void add_value_at_index_with_drop_tolerance(unsigned j, const T& val_to_add) { - T & v = m_data[j]; - bool was_zero = is_zero(v); - v += val_to_add; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - if (!was_zero) { - erase_from_index(j); - } - } else { - if (was_zero) - m_index.push_back(j); - } - } - + void add_value_at_index(unsigned j, const T& val_to_add) { T & v = m_data[j]; bool was_zero = is_zero(v); @@ -153,18 +115,6 @@ class indexed_vector { } } - void restore_index_and_clean_from_data() { - m_index.resize(0); - for (unsigned i = 0; i < m_data.size(); i++) { - T & v = m_data[i]; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - } else { - m_index.push_back(i); - } - } - } - struct ival { unsigned m_var; const T & m_coeff; @@ -215,9 +165,6 @@ class indexed_vector { } -#ifdef Z3DEBUG - bool is_OK() const; -#endif void print(std::ostream & out); }; } diff --git a/src/math/lp/indexed_vector_def.h b/src/math/lp/indexed_vector_def.h index 0e25ee27115..1b904e14af1 100644 --- a/src/math/lp/indexed_vector_def.h +++ b/src/math/lp/indexed_vector_def.h @@ -43,7 +43,7 @@ template void indexed_vector::resize(unsigned data_size) { clear(); m_data.resize(data_size, numeric_traits::zero()); - lp_assert(is_OK()); + } template @@ -72,33 +72,6 @@ void indexed_vector::erase_from_index(unsigned j) { m_index.erase(it); } -#ifdef Z3DEBUG -template -bool indexed_vector::is_OK() const { - return true; - const double drop_eps = 1e-14; - for (unsigned i = 0; i < m_data.size(); i++) { - if (!is_zero(m_data[i]) && lp_settings::is_eps_small_general(m_data[i], drop_eps)) { - return false; - } - if (lp_settings::is_eps_small_general(m_data[i], drop_eps) != (std::find(m_index.begin(), m_index.end(), i) == m_index.end())) { - return false; - } - } - - std::unordered_set s; - for (unsigned i : m_index) { - //no duplicates!!! - if (s.find(i) != s.end()) - return false; - s.insert(i); - if (i >= m_data.size()) - return false; - } - - return true; -} -#endif template void indexed_vector::print(std::ostream & out) { out << "m_index " << std::endl; diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index d40572b9e24..e024b199c1d 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -27,8 +27,7 @@ class lar_core_solver { int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; vector m_costs_dummy; - vector m_d_right_sides_dummy; - vector m_d_costs_dummy; + public: stacked_value m_stacked_simplex_strategy; stacked_vector m_column_types; @@ -45,10 +44,6 @@ class lar_core_solver { stacked_vector m_r_rows_nz; // d - solver fields, for doubles - vector m_d_x; // the solution in doubles - vector m_d_lower_bounds; - vector m_d_upper_bounds; - static_matrix m_d_A; stacked_vector m_d_pushed_basis; vector m_d_basis; vector m_d_nbasis; @@ -146,7 +141,7 @@ class lar_core_solver { m_r_lower_bounds.push(); m_r_upper_bounds.push(); - m_d_A.push(); + } @@ -185,10 +180,6 @@ class lar_core_solver { m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); - m_d_A.pop(k); - // doubles - - m_d_x.resize(m_d_A.column_count()); pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().set_simplex_strategy(m_stacked_simplex_strategy); @@ -279,15 +270,7 @@ class lar_core_solver { } - void create_double_matrix(static_matrix & A) { - for (unsigned i = 0; i < m_r_A.row_count(); i++) { - auto & row = m_r_A.m_rows[i]; - for (row_cell & c : row) { - A.add_new_element(i, c.var(), c.coeff().get_double()); - } - } - } - + void fill_basis_d( vector& basis_d, vector& heading_d, @@ -308,27 +291,6 @@ class lar_core_solver { } } - void get_bounds_for_double_solver() { - unsigned n = m_n(); - m_d_lower_bounds.resize(n); - m_d_upper_bounds.resize(n); - double delta = find_delta_for_strict_boxed_bounds().get_double(); - if (delta > 0.000001) - delta = 0.000001; - for (unsigned j = 0; j < n; j++) { - if (lower_bound_is_set(j)) { - const auto & lb = m_r_solver.m_lower_bounds[j]; - m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); - } - if (upper_bound_is_set(j)) { - const auto & ub = m_r_solver.m_upper_bounds[j]; - m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); - lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j])); - } - } - } - - bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9b2361b74f4..67cf90bd081 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -8,10 +8,6 @@ namespace lp { - static_matrix& lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A; } - - static_matrix const& lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A; } - lp_settings& lar_solver::settings() { return m_settings; } lp_settings const& lar_solver::settings() const { return m_settings; } @@ -574,7 +570,6 @@ namespace lp { void lar_solver::pop_core_solver_params(unsigned k) { A_r().pop(k); - A_d().pop(k); } @@ -1544,27 +1539,7 @@ namespace lp { add_new_var_to_core_fields_for_mpq(false); // false for not adding a row } - - void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { - unsigned j = A_d().column_count(); - A_d().add_column(); - lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later - m_mpq_lar_core_solver.m_d_x.resize(j + 1); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); - lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - } - else { - m_mpq_lar_core_solver.m_d_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - } - } - + void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { unsigned j = A_r().column_count(); TRACE("add_var", tout << "j = " << j << std::endl;); @@ -1927,34 +1902,7 @@ namespace lp { } } - void lar_solver::adjust_initial_state_for_lu() { - copy_from_mpq_matrix(A_d()); - unsigned n = A_d().column_count(); - m_mpq_lar_core_solver.m_d_x.resize(n); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(n); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(n); - m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading; - m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis; - - /* - unsigned j = A_d().column_count(); - A_d().add_column(); - lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later - m_mpq_lar_core_solver.m_d_x.resize(j + 1 ); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); - lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - }else { - m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - }*/ - } - + void lar_solver::adjust_initial_state_for_tableau_rows() { for (unsigned i = 0; i < m_terms.size(); i++) { if (m_var_register.external_is_used(tv::mask_term(i))) @@ -1963,24 +1911,7 @@ namespace lp { } } - // this fills the last row of A_d and sets the basis column: -1 in the last column of the row - void lar_solver::fill_last_row_of_A_d(static_matrix& A, const lar_term* ls) { - lp_assert(A.row_count() > 0); - lp_assert(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - lp_assert(A.m_rows[last_row].empty()); - - for (auto t : *ls) { - lp_assert(!is_zero(t.coeff())); - var_index j = t.column(); - A.set(last_row, j, -t.coeff().get_double()); - } - - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, -1); - lp_assert(A.is_correct()); - } - + void lar_solver::update_column_type_and_bound_with_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, unsigned constraint_index) { SASSERT(column_has_upper_bound(j)); if (column_has_lower_bound(j)) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 356c86c2f9b..237b9de810a 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -112,8 +112,6 @@ class lar_solver : public column_namer { // end of fields ////////////////// methods //////////////////////////////// - static_matrix & A_d(); - static_matrix const & A_d() const; static bool valid_index(unsigned j) { return static_cast(j) >= 0;} const lar_term & get_term(unsigned j) const; @@ -162,9 +160,7 @@ class lar_solver : public column_namer { unsigned row_of_basic_column(unsigned) const; void decide_on_strategy_and_adjust_initial_state(); void adjust_initial_state(); - void adjust_initial_state_for_lu(); void adjust_initial_state_for_tableau_rows(); - void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 059d801a03c..14c454d6f48 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,27 +23,10 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template bool lp::lp_core_solver_base::basis_heading_is_correct() const; -template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; -template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; -template lp::lp_core_solver_base::lp_core_solver_base( - lp::static_matrix&, // vector&, - vector&, - vector &, vector &, - vector&, - vector&, - lp::lp_settings&, const column_namer&, const vector&, - const vector&, - const vector&); -template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); -template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); @@ -74,35 +57,25 @@ template lp::lp_core_solver_base::lp_core_solver_base( const vector&, const vector&); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); -template std::string lp::lp_core_solver_base::column_name(unsigned int) const; -template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; template void lp::lp_core_solver_base >::pivot_fixed_vars_from_basis(); -template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); -template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base >::inf_set_is_correct() const; -template bool lp::lp_core_solver_base::inf_set_is_correct() const; template bool lp::lp_core_solver_base::inf_set_is_correct() const; template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; -template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int, lp::numeric_pair const&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 6fa2ddead1c..14cb60ebf92 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -309,10 +309,7 @@ class lp_core_solver_base { column_type get_column_type(unsigned j) const {return m_column_types[j]; } - bool pivot_row_element_is_too_small_for_ratio_test(unsigned j) { - return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]); - } - + X bound_span(unsigned j) const { return m_upper_bounds[j] - m_lower_bounds[j]; } @@ -410,7 +407,6 @@ class lp_core_solver_base { non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 213d67e6ea0..16f56bade6d 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -533,23 +533,6 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } -template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { - const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; - const T & row_p = this->m_pivot_row[entering]; - if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero - // the pivots have to have the same sign - if (column_p < 0) { - if (row_p > 0) - return 2; - } else { // column_p > 0 - if (row_p < 0) - return 2; - } - T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); - if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) - return 1; - return 0; -} template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index f4597da762c..22042668da3 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -27,15 +27,11 @@ Revision History: #include "math/lp/lp_primal_core_solver_tableau_def.h" namespace lp { -template void lp_primal_core_solver::find_feasible_solution(); template void lp::lp_primal_core_solver >::find_feasible_solution(); -template unsigned lp_primal_core_solver::solve(); -template unsigned lp_primal_core_solver::solve_with_tableau(); template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); -template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, double const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); template void lp::lp_primal_core_solver >::update_inf_cost_for_column_tableau(unsigned); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index aa771d7ee80..aacabf94426 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -56,7 +56,7 @@ class lp_primal_core_solver:public lp_core_solver_base { unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; - // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); + std::list m_non_basis_list; void sort_non_basis(); void sort_non_basis_rational(); @@ -279,12 +279,10 @@ class lp_primal_core_solver:public lp_core_solver_base { bool get_harris_theta(X & theta); - void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } int find_leaving_on_harris_theta(X const & harris_theta, X & t); bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t(unsigned entering, X & t); int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index fb62bbf540e..cfd8fc947d6 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -144,7 +144,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef template bool lp_primal_core_solver::get_harris_theta(X & theta) { - lp_assert(this->m_ed.is_OK()); bool unlimited = true; for (unsigned i : this->m_ed.m_index) { if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; @@ -311,17 +310,6 @@ template int lp_primal_core_solver::find_leaving_ } -template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { - X theta = zero_of_type(); - bool unlimited = get_harris_theta(theta); - lp_assert(unlimited || theta >= zero_of_type()); - if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; - if (unlimited) - return -1; - return find_leaving_on_harris_theta(theta, t); -} - - // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 96df997b17c..256853be722 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -107,7 +107,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";); switch (this->get_status()) { - case lp_status::OPTIMAL: // double check that we are at optimum + case lp_status::OPTIMAL: // check again that we are at optimum case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; diff --git a/src/math/lp/lp_settings.cpp b/src/math/lp/lp_settings.cpp index 592a9898385..b72b837fd71 100644 --- a/src/math/lp/lp_settings.cpp +++ b/src/math/lp/lp_settings.cpp @@ -21,7 +21,6 @@ Revision History: #include "util/vector.h" #include "smt/params/smt_params_helper.hpp" #include "math/lp/lp_settings_def.h" -template bool lp::vectors_are_equal(vector const&, vector const&); template bool lp::vectors_are_equal(vector const&, vector const&); void lp::lp_settings::updt_params(params_ref const& _p) { diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 86a97e615e9..197b7c04f17 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -92,7 +92,6 @@ lp_status lp_status_from_string(std::string status); enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; -template bool is_epsilon_small(const X & v, const double& eps); // forward definition class lp_resource_limit { public: @@ -182,36 +181,18 @@ struct lp_settings { unsigned reps_in_scaler { 20 }; // when the absolute value of an element is less than pivot_epsilon // in pivoting, we treat it as a zero - double pivot_epsilon { 0.00000001 }; - // see Chatal, page 115 - double positive_price_epsilon { 1e-7 }; // a quotation "if some choice of the entering variable leads to an eta matrix // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... - double entering_diag_epsilon { 1e-8 }; int c_partial_pivoting { 10 }; // this is the constant c from page 410 unsigned depth_of_rook_search { 4 }; bool using_partial_pivoting { true }; // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor - double refactor_tolerance { 1e-4 }; - double pivot_tolerance { 1e-6 }; - double zero_tolerance { 1e-12 }; - double drop_tolerance { 1e-14 }; - double tolerance_for_artificials { 1e-4 }; - double can_be_taken_to_basis_tolerance { 0.00001 }; - + unsigned percent_of_entering_to_check { 5 }; // we try to find a profitable column in a percentage of the columns bool use_scaling { true }; - double scaling_maximum { 1.0 }; - double scaling_minimum { 0.5 }; - double harris_feasibility_tolerance { 1e-7 }; // page 179 of Istvan Maros - double ignore_epsilon_of_harris { 10e-5 }; unsigned max_number_of_iterations_with_no_improvements { 2000000 }; - double time_limit; // the maximum time limit of the total run time in seconds - // dual section - double dual_feasibility_tolerance { 1e-7 }; // page 71 of the PhD thesis of Achim Koberstein - double primal_feasibility_tolerance { 1e-7 }; // page 71 of the PhD thesis of Achim Koberstein - double relative_primal_feasibility_tolerance { 1e-9 }; // page 71 of the PhD thesis of Achim Koberstein + double time_limit; // the maximum time limit of the total run time in seconds // end of dual section bool m_bound_propagation { true }; bool presolve_with_double_solver_for_lar { true }; @@ -221,7 +202,6 @@ struct lp_settings { bool print_statistics { false }; unsigned column_norms_update_frequency { 12000 }; bool scale_with_ratio { true }; - double density_threshold { 0.7 }; unsigned max_row_length_for_bound_propagation { 300 }; bool backup_costs { true }; unsigned column_number_threshold_for_using_lu_in_lar_solver { 4000 }; @@ -272,61 +252,10 @@ struct lp_settings { statistics& stats() { return m_stats; } statistics const& stats() const { return m_stats; } - template static bool is_eps_small_general(const T & t, const double & eps) { - return numeric_traits::is_zero(t); - } - - template - bool abs_val_is_smaller_than_dual_feasibility_tolerance(T const & t) { - return is_eps_small_general(t, dual_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_primal_feasibility_tolerance(T const & t) { - return is_eps_small_general(t, primal_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_can_be_taken_to_basis_tolerance(T const & t) { - return is_eps_small_general(t, can_be_taken_to_basis_tolerance); - } - - template - bool abs_val_is_smaller_than_drop_tolerance(T const & t) const { - return is_eps_small_general(t, drop_tolerance); - } - - - template - bool abs_val_is_smaller_than_zero_tolerance(T const & t) { - return is_eps_small_general(t, zero_tolerance); - } - - template - bool abs_val_is_smaller_than_refactor_tolerance(T const & t) { - return is_eps_small_general(t, refactor_tolerance); - } - - - template - bool abs_val_is_smaller_than_pivot_tolerance(T const & t) { - return is_eps_small_general(t, pivot_tolerance); - } - - template - bool abs_val_is_smaller_than_harris_tolerance(T const & t) { - return is_eps_small_general(t, harris_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_ignore_epslilon_for_harris(T const & t) { - return is_eps_small_general(t, ignore_epsilon_of_harris); - } + - template - bool abs_val_is_smaller_than_artificial_tolerance(T const & t) { - return is_eps_small_general(t, tolerance_for_artificials); - } + + // the method of lar solver to use simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; @@ -370,11 +299,6 @@ inline std::string T_to_string(const mpq & t) { return strs.str(); } -template -bool val_is_smaller_than_eps(T const & t, double const & eps) { - - return t <= numeric_traits::zero(); -} template bool vectors_are_equal(T * a, vector &b, unsigned n); diff --git a/src/math/lp/lp_utils.cpp b/src/math/lp/lp_utils.cpp index 9ce3b989435..b909a0389d0 100644 --- a/src/math/lp/lp_utils.cpp +++ b/src/math/lp/lp_utils.cpp @@ -20,8 +20,7 @@ Revision History: #include "math/lp/lp_utils.h" #ifdef lp_for_z3 namespace lp { -double numeric_traits::g_zero = 0.0; -double numeric_traits::g_one = 1.0; + } #endif diff --git a/src/math/lp/matrix.cpp b/src/math/lp/matrix.cpp index 5367c74d0d0..1ea2da263a3 100644 --- a/src/math/lp/matrix.cpp +++ b/src/math/lp/matrix.cpp @@ -22,10 +22,8 @@ Revision History: #include "math/lp/static_matrix.h" #include #ifdef Z3DEBUG -template bool lp::matrix::is_equal(lp::matrix const&); template bool lp::matrix >::is_equal(lp::matrix > const&); template bool lp::matrix::is_equal(lp::matrix const&); #endif -template void lp::print_matrix(lp::matrix const*, std::ostream & out); template void lp::print_matrix >(lp::matrix > const *, std::basic_ostream > &); template void lp::print_matrix(lp::matrix const*, std::ostream&); diff --git a/src/math/lp/permutation_matrix.cpp b/src/math/lp/permutation_matrix.cpp index 28319c2ee88..762b85d63f0 100644 --- a/src/math/lp/permutation_matrix.cpp +++ b/src/math/lp/permutation_matrix.cpp @@ -21,16 +21,8 @@ Revision History: #include "util/vector.h" #include "math/lp/permutation_matrix_def.h" #include "math/lp/numeric_pair.h" -template void lp::permutation_matrix::apply_from_right(vector&); -template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix>::init(unsigned int); -template bool lp::permutation_matrix::is_identity() const; -template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); -template lp::permutation_matrix::permutation_matrix(unsigned int, vector const&); -template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix::apply_from_right(vector&); template bool lp::permutation_matrix::is_identity() const; @@ -50,21 +42,13 @@ template void lp::permutation_matrix >::multi template lp::permutation_matrix >::permutation_matrix(unsigned int); template void lp::permutation_matrix >::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix >::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); template void lp::permutation_matrix >::apply_reverse_from_left(lp::indexed_vector&); template void lp::permutation_matrix >::apply_reverse_from_left_to_T(vector&); template void lp::permutation_matrix >::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); -template lp::permutation_matrix::permutation_matrix(unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left_to_X(vector &); template void lp::permutation_matrix< lp::mpq, lp::mpq>::apply_reverse_from_left_to_X(vector &); template void lp::permutation_matrix< lp::mpq, lp::numeric_pair< lp::mpq> >::apply_reverse_from_left_to_X(vector> &); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); template void lp::permutation_matrix >::apply_reverse_from_right_to_T(lp::indexed_vector&); diff --git a/src/math/lp/permutation_matrix_def.h b/src/math/lp/permutation_matrix_def.h index 703830ffcf3..b6f9924ff3b 100644 --- a/src/math/lp/permutation_matrix_def.h +++ b/src/math/lp/permutation_matrix_def.h @@ -133,7 +133,7 @@ template void permutation_matrix::apply_from_righ unsigned pj = m_permutation[j]; w.set_value(buffer[i], pj); } - lp_assert(w.is_OK()); + #ifdef Z3DEBUG lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif @@ -235,7 +235,6 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & // vector wcopy(w.m_data); // apply_reverse_from_right_to_T(wcopy); #endif - lp_assert(w.is_OK()); vector tmp; vector tmp_index(w.m_index); for (auto i : w.m_index) { @@ -248,8 +247,7 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w.set_value(tmp[k], m_rev[j]); } - // lp_assert(w.is_OK()); - // lp_assert(vectors_are_equal(w.m_data, wcopy)); + } diff --git a/src/math/lp/sparse_vector.h b/src/math/lp/sparse_vector.h index 1c27a8d96e4..3de701e1036 100644 --- a/src/math/lp/sparse_vector.h +++ b/src/math/lp/sparse_vector.h @@ -42,7 +42,6 @@ class sparse_vector { } #endif void divide(T const & a) { - lp_assert(!lp_settings::is_eps_small_general(a, 1e-12)); for (auto & t : m_data) { t.second /= a; } } diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 12d2f9f0df3..28a23b0c394 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -26,26 +26,8 @@ Revision History: #include "math/lp/lp_primal_core_solver.h" #include "math/lp/lar_solver.h" namespace lp { -template void static_matrix::add_columns_at_the_end(unsigned int); -template void static_matrix::clear(); -#ifdef Z3DEBUG -template bool static_matrix::is_correct() const; -#endif -template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; - -template double static_matrix::get_balance() const; -template std::set> static_matrix::get_domain(); template std::set> lp::static_matrix::get_domain(); template std::set> lp::static_matrix >::get_domain(); -template double static_matrix::get_elem(unsigned int, unsigned int) const; -template double static_matrix::get_max_abs_in_column(unsigned int) const; -template double static_matrix::get_min_abs_in_column(unsigned int) const; -template double static_matrix::get_min_abs_in_row(unsigned int) const; -template void static_matrix::init_empty_matrix(unsigned int, unsigned int); -template void static_matrix::init_row_columns(unsigned int, unsigned int); -template static_matrix::ref & static_matrix::ref::operator=(double const&); -template void static_matrix::set(unsigned int, unsigned int, double const&); -template static_matrix::static_matrix(unsigned int, unsigned int); template void static_matrix::add_column_to_vector(mpq const&, unsigned int, mpq*) const; template void static_matrix::add_columns_at_the_end(unsigned int); template bool static_matrix::is_correct() const; @@ -55,7 +37,6 @@ template mpq static_matrix::get_balance() const; template mpq static_matrix::get_elem(unsigned int, unsigned int) const; template mpq static_matrix::get_max_abs_in_column(unsigned int) const; template mpq static_matrix::get_max_abs_in_row(unsigned int) const; -template double static_matrix::get_max_abs_in_row(unsigned int) const; template mpq static_matrix::get_min_abs_in_column(unsigned int) const; template mpq static_matrix::get_min_abs_in_row(unsigned int) const; template void static_matrix::init_row_columns(unsigned int, unsigned int); @@ -72,7 +53,6 @@ template void static_matrix >::init_empty_matrix(unsigned template void static_matrix >::set(unsigned int, unsigned int, mpq const&); -template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell &, unsigned int); template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell& , unsigned int); template bool lp::static_matrix >::pivot_row_to_row_given_cell(unsigned int, column_cell&, unsigned int); template void lp::static_matrix >::remove_element(vector, true, unsigned int>&, lp::row_cell&); diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index 7f81cae79f5..b9870ceb5f0 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -359,7 +359,6 @@ class static_matrix for (auto p : row) { fill_last_row_with_pivoting_loop_block(p.column().index(), basis_heading); } - lp_assert(m_work_vector.is_OK()); unsigned last_row = row_count() - 1; for (unsigned j : m_work_vector.m_index) { From f6445891f32c7f7c98204ad767779d9e31c0a8b9 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 11:34:43 -0800 Subject: [PATCH 499/597] rm lu related fields from lp_core_solver_base.h --- src/math/lp/core_solver_pretty_printer.h | 18 +--- src/math/lp/core_solver_pretty_printer_def.h | 19 +--- src/math/lp/indexed_vector_def.h | 8 -- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 18 ---- src/math/lp/lp_core_solver_base_def.h | 93 +------------------- src/math/lp/permutation_matrix.h | 1 - src/math/lp/sparse_vector.h | 52 ----------- src/math/lp/static_matrix.h | 1 - 9 files changed, 8 insertions(+), 205 deletions(-) delete mode 100644 src/math/lp/sparse_vector.h diff --git a/src/math/lp/core_solver_pretty_printer.h b/src/math/lp/core_solver_pretty_printer.h index 353212dcd1a..5bf29d511db 100644 --- a/src/math/lp/core_solver_pretty_printer.h +++ b/src/math/lp/core_solver_pretty_printer.h @@ -59,7 +59,7 @@ class core_solver_pretty_printer { unsigned m_artificial_start; indexed_vector m_w_buff; indexed_vector m_ed_buff; - vector m_exact_column_norms; + public: core_solver_pretty_printer(const lp_core_solver_base & core_solver, std::ostream & out); @@ -85,14 +85,7 @@ class core_solver_pretty_printer { } unsigned get_column_width(unsigned column); - - unsigned regular_cell_width(unsigned row, unsigned column, const std::string & name) { - return regular_cell_string(row, column, name).size(); - } - - std::string regular_cell_string(unsigned row, unsigned column, std::string name); - - + void set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name); void print_x(); @@ -105,12 +98,7 @@ class core_solver_pretty_printer { void print_lows(); void print_upps(); - - string get_exact_column_norm_string(unsigned col) { - return T_to_string(m_exact_column_norms[col]); - } - - + void print_approx_norms(); void print(); diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 2f1d99d22c6..931333ee725 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -37,9 +37,8 @@ core_solver_pretty_printer::core_solver_pretty_printer(const lp_core_solve m_signs(core_solver.m_A.row_count(), vector(core_solver.m_A.column_count(), " ")), m_costs(ncols(), ""), m_cost_signs(ncols(), " "), - m_rs(ncols(), zero_of_type()), - m_w_buff(core_solver.m_w), - m_ed_buff(core_solver.m_ed) { + m_rs(ncols(), zero_of_type()) + { m_lower_bounds_title = "low"; m_upp_bounds_title = "upp"; m_exact_norm_title = "exact cn"; @@ -80,13 +79,6 @@ template void core_solver_pretty_printer::init_rs } } -template T core_solver_pretty_printer::current_column_norm() { - T ret = zero_of_type(); - for (auto i : m_core_solver.m_ed.m_index) - ret += m_core_solver.m_ed[i] * m_core_solver.m_ed[i]; - return ret; -} - template void core_solver_pretty_printer::init_m_A_and_signs() { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); @@ -167,13 +159,6 @@ template unsigned core_solver_pretty_printer:: ge return w; } -template std::string core_solver_pretty_printer::regular_cell_string(unsigned row, unsigned /* column */, std::string name) { - T t = fabs(m_core_solver.m_ed[row]); - if ( t == 1) return name; - return T_to_string(t) + name; -} - - template void core_solver_pretty_printer::set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name) { if (numeric_traits::is_zero(t)) { return; diff --git a/src/math/lp/indexed_vector_def.h b/src/math/lp/indexed_vector_def.h index 1b904e14af1..0343250884a 100644 --- a/src/math/lp/indexed_vector_def.h +++ b/src/math/lp/indexed_vector_def.h @@ -24,14 +24,6 @@ Revision History: #include "math/lp/lp_settings.h" namespace lp { -template -void print_sparse_vector(const vector & t, std::ostream & out) { - for (unsigned i = 0; i < t.size(); i++) { - if (is_zero(t[i]))continue; - out << "[" << i << "] = " << t[i] << ", "; - } - out << std::endl; -} void print_vector_as_doubles(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 14c454d6f48..ccd6bf41927 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -29,9 +29,7 @@ template lp::non_basic_column_value_position lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; -template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::init(); @@ -68,7 +66,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); -template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 14cb60ebf92..964e447a125 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -74,7 +74,6 @@ class lp_core_solver_base { void set_using_infeas_costs(bool val) { m_using_infeas_costs = val; } vector m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column vector m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row - indexed_vector m_pivot_row_of_B_1; // the pivot row of the reverse of B indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A // vector const & m_b; // the right side @@ -85,15 +84,11 @@ class lp_core_solver_base { vector & m_costs; lp_settings & m_settings; - vector m_y; // the buffer for yB = cb const column_namer & m_column_names; - indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs - indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; const vector & m_lower_bounds; const vector & m_upper_bounds; - vector m_copy_of_xB; unsigned m_basis_sort_counter; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; @@ -162,10 +157,6 @@ class lp_core_solver_base { return dot_product(m_costs, m_x); } - void copy_m_w(T * buffer); - - void restore_m_w(T * buffer); - void add_delta_to_entering(unsigned entering, const X & delta); const X & get_var_value(unsigned j) const { @@ -298,11 +289,6 @@ class lp_core_solver_base { bool basis_heading_is_correct() const; - void restore_x(unsigned entering, X const & t); - - void fill_reduced_costs_from_m_y_by_rows(); - - void copy_rs_to_xB(vector & rs); virtual bool lower_bounds_are_set() const { return false; } X lower_bound_value(unsigned j) const { return m_lower_bounds[j]; } X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; } @@ -316,10 +302,6 @@ class lp_core_solver_base { std::string column_name(unsigned column) const; - void add_delta_to_xB(vector & del); - - void find_error_in_BxB(vector& rs); - bool snap_non_basic_x_to_bound() { bool ret = false; for (unsigned j : non_basis()) diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 16f56bade6d..2d729656724 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -44,25 +44,19 @@ lp_core_solver_base(static_matrix & A, m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), m_using_infeas_costs(false), - m_pivot_row_of_B_1(A.row_count()), m_pivot_row(A.column_count()), m_A(A), - // m_b(b), m_basis(basis), m_nbasis(nbasis), m_basis_heading(heading), m_x(x), m_costs(costs), m_settings(settings), - m_y(m_m()), m_column_names(column_names), - m_w(m_m()), m_d(m_n()), - m_ed(m_m()), m_column_types(column_types), m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), - m_copy_of_xB(m_m()), m_basis_sort_counter(0), m_tracing_basis_changes(false), m_pivoted_rows(nullptr), @@ -122,25 +116,6 @@ pretty_print(std::ostream & out) { } -template void lp_core_solver_base:: -copy_m_w(T * buffer) { - unsigned i = m_m(); - while (i --) { - buffer[i] = m_w[i]; - } -} - -template void lp_core_solver_base:: -restore_m_w(T * buffer) { - m_w.m_index.clear(); - unsigned i = m_m(); - while (i--) { - if (!is_zero(m_w[i] = buffer[i])) - m_w.m_index.push_back(i); - } -} - - template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { m_x[entering] += delta; @@ -443,73 +418,11 @@ template bool lp_core_solver_base:: return true; } -template void lp_core_solver_base:: -restore_x(unsigned entering, X const & t) { - if (is_zero(t)) return; - m_x[entering] -= t; - for (unsigned i : m_ed.m_index) { - m_x[m_basis[i]] = m_copy_of_xB[i]; - } -} - -template void lp_core_solver_base:: -fill_reduced_costs_from_m_y_by_rows() { - unsigned j = m_n(); - while (j--) { - if (m_basis_heading[j] < 0) - m_d[j] = m_costs[j]; - else - m_d[j] = numeric_traits::zero(); - } - - unsigned i = m_m(); - while (i--) { - const T & y = m_y[i]; - if (is_zero(y)) continue; - for (row_cell & c : m_A.m_rows[i]) { - j = c.var(); - if (m_basis_heading[j] < 0) { - m_d[j] -= y * c.coeff(); - } - } - } -} - -template void lp_core_solver_base:: -copy_rs_to_xB(vector & rs) { - unsigned j = m_m(); - while (j--) { - m_x[m_basis[j]] = rs[j]; - } -} - template std::string lp_core_solver_base:: column_name(unsigned column) const { return m_column_names.get_variable_name(column); } -template void lp_core_solver_base:: -add_delta_to_xB(vector & del) { - unsigned i = m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= del[i]; - } -} - -template void lp_core_solver_base:: -find_error_in_BxB(vector& rs){ - unsigned row = m_m(); - while (row--) { - auto &rsv = rs[row]; - for (auto & it : m_A.m_rows[row]) { - unsigned j = it.var(); - if (m_basis_heading[j] >= 0) { - rsv -= m_x[j] * it.coeff(); - } - } - } -} - template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { @@ -543,9 +456,9 @@ template bool lp_core_solver_base::pivot_column_g lp_assert(m_basis_heading[j_basic] >= 0); unsigned row_index = m_basis_heading[j_basic]; // the tableau case - if (pivot_column_tableau(j, row_index)) - change_basis(j, j_basic); - else return false; + if (pivot_column_tableau(j, row_index)) + change_basis(j, j_basic); + else return false; return true; } diff --git a/src/math/lp/permutation_matrix.h b/src/math/lp/permutation_matrix.h index 8ec78c14a1c..35a58906ade 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -22,7 +22,6 @@ Revision History: #include #include "util/debug.h" #include -#include "math/lp/sparse_vector.h" #include "math/lp/indexed_vector.h" #include "math/lp/lp_settings.h" #include "math/lp/matrix.h" diff --git a/src/math/lp/sparse_vector.h b/src/math/lp/sparse_vector.h deleted file mode 100644 index 3de701e1036..00000000000 --- a/src/math/lp/sparse_vector.h +++ /dev/null @@ -1,52 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include -#include "util/debug.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_settings.h" -namespace lp { - -template -class sparse_vector { -public: - vector> m_data; - void push_back(unsigned index, T val) { - m_data.push_back(std::make_pair(index, val)); - } -#ifdef Z3DEBUG - T operator[] (unsigned i) const { - for (auto &t : m_data) { - if (t.first == i) return t.second; - } - return numeric_traits::zero(); - } -#endif - void divide(T const & a) { - for (auto & t : m_data) { t.second /= a; } - } - - unsigned size() const { - return m_data.size(); - } -}; -} diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index b9870ceb5f0..d7e4370a344 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -12,7 +12,6 @@ Copyright (c) 2017 Microsoft Corporation #include #include #include -#include "math/lp/sparse_vector.h" #include "math/lp/indexed_vector.h" #include "math/lp/permutation_matrix.h" #include From e430f2881397464bc6620b1c955ccba4ad6c0a13 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 13:51:56 -0800 Subject: [PATCH 500/597] remove dead code Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 19 -- src/math/lp/lar_solver.cpp | 36 +-- src/math/lp/lar_solver.h | 16 - src/math/lp/lp_core_solver_base.h | 2 +- src/math/lp/lp_primal_core_solver.h | 135 +-------- src/math/lp/lp_primal_core_solver_def.h | 120 -------- .../lp/lp_primal_core_solver_tableau_def.h | 4 +- src/test/lp/lp.cpp | 279 ------------------ 8 files changed, 13 insertions(+), 598 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index e024b199c1d..52c5b6f1a84 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -43,12 +43,6 @@ class lar_core_solver { stacked_vector m_r_columns_nz; stacked_vector m_r_rows_nz; - // d - solver fields, for doubles - stacked_vector m_d_pushed_basis; - vector m_d_basis; - vector m_d_nbasis; - vector m_d_heading; - lp_primal_core_solver> m_r_solver; // solver in rational numbers @@ -123,14 +117,6 @@ class lar_core_solver { void fill_not_improvable_zero_sum(); - void pop_basis(unsigned k) { - - m_d_basis = m_r_basis; - m_d_nbasis = m_r_nbasis; - m_d_heading = m_r_heading; - - } - void push() { lp_assert(m_r_solver.basis_heading_is_correct()); lp_assert(m_column_types.size() == m_r_A.column_count()); @@ -180,7 +166,6 @@ class lar_core_solver { m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); - pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().set_simplex_strategy(m_stacked_simplex_strategy); lp_assert(m_r_solver.basis_heading_is_correct()); @@ -356,10 +341,6 @@ class lar_core_solver { return delta; } - void init_column_row_nz_for_r_solver() { - m_r_solver.init_column_row_non_zeroes(); - } - bool column_is_fixed(unsigned j) const { return m_column_types()[j] == column_type::fixed || ( m_column_types()[j] == column_type::boxed && diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 67cf90bd081..500ee23e060 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -245,11 +245,7 @@ namespace lp { set.erase(j); } - void lar_solver::shrink_inf_set_after_pop(unsigned n, u_set& set) { - clean_popped_elements(n, set); - set.resize(n); - } - + void lar_solver::pop(unsigned k) { TRACE("lar_solver", tout << "k = " << k << std::endl;); @@ -714,11 +710,6 @@ namespace lp { detect_rows_with_changed_bounds_for_column(j); } - void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds() { - for (auto j : m_columns_with_changed_bounds) - update_x_and_inf_costs_for_column_with_changed_bounds(j); - } - void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bounds) update_x_and_inf_costs_for_column_with_changed_bounds(j); @@ -792,31 +783,6 @@ namespace lp { } - void lar_solver::fill_last_row_of_A_r(static_matrix>& A, const lar_term* ls) { - lp_assert(A.row_count() > 0); - lp_assert(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - lp_assert(A.m_rows[last_row].size() == 0); - for (auto t : *ls) { - lp_assert(!is_zero(t.coeff())); - var_index j = t.column(); - A.set(last_row, j, -t.coeff()); - } - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, mpq(1)); - } - - template - void lar_solver::copy_from_mpq_matrix(static_matrix& matr) { - matr.m_rows.resize(A_r().row_count()); - matr.m_columns.resize(A_r().column_count()); - for (unsigned i = 0; i < matr.row_count(); i++) { - for (auto& it : A_r().m_rows[i]) { - matr.set(i, it.var(), convert_struct::convert(it.coeff())); - } - } - } - bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { for (auto it : left_side) { if (!var_is_registered(it.second)) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 237b9de810a..83d2a25fe29 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -122,7 +122,6 @@ class lar_solver : public column_namer { bool term_is_int(const lar_term * t) const; bool term_is_int(const vector> & coeffs) const; void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int); - void add_new_var_to_core_fields_for_doubles(bool register_in_basis); void add_new_var_to_core_fields_for_mpq(bool register_in_basis); mpq adjust_bound_for_int(lpvar j, lconstraint_kind&, const mpq&); @@ -131,7 +130,6 @@ class lar_solver : public column_namer { var_index add_term_undecided(const vector> & coeffs); bool term_coeffs_are_ok(const vector> & coeffs); void push_term(lar_term* t); - void add_row_for_term(const lar_term * term, unsigned term_ext_index); void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index); void add_basic_var_to_core_fields(); bool compare_values(impq const& lhs, lconstraint_kind k, const mpq & rhs); @@ -187,7 +185,6 @@ class lar_solver : public column_namer { analyze_new_bounds_on_row_tableau(i, bp); } static void clean_popped_elements(unsigned n, u_set& set); - static void shrink_inf_set_after_pop(unsigned n, u_set & set); bool maximize_term_on_tableau(const lar_term & term, impq &term_max); bool costs_are_zeros_for_r_solver() const; @@ -213,17 +210,10 @@ class lar_solver : public column_namer { void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds(); - void update_x_and_inf_costs_for_columns_with_changed_bounds(); void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); bool x_is_correct() const; - void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); - template - void create_matrix_A(static_matrix & matr); - template - void copy_from_mpq_matrix(static_matrix & matr); - bool try_to_set_fixed(column_info & ci); bool all_constrained_variables_are_registered(const vector>& left_side); bool all_constraints_hold() const; bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; @@ -231,7 +221,6 @@ class lar_solver : public column_namer { static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); bool the_left_sides_sum_to_zero(const vector> & evidence) const; - bool the_right_sides_do_not_sum_to_zero(const vector> & evidence); bool explanation_is_correct(explanation&) const; bool inf_explanation_is_correct() const; mpq sum_of_right_sides_of_explanation(explanation &) const; @@ -251,21 +240,16 @@ class lar_solver : public column_namer { void remove_last_column_from_tableau(); void pop_tableau(); void clean_inf_set_of_r_solver_after_pop(); - void shrink_explanation_to_minimum(vector> & explanation) const; inline bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } bool model_is_int_feasible() const; bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; inline lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } - void catch_up_in_updating_int_solver(); var_index to_column(unsigned ext_j) const; void fix_terms_with_rounded_columns(); - void update_delta_for_terms(const impq & delta, unsigned j, const vector&); - void fill_vars_to_terms(vector> & vars_to_terms); bool remove_from_basis(unsigned); lar_term get_term_to_maximize(unsigned ext_j) const; bool sum_first_coords(const lar_term& t, mpq & val) const; - void collect_rounded_rows_to_fix(); void register_normalized_term(const lar_term&, lpvar); void deregister_normalized_term(const lar_term&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 964e447a125..96f8729a009 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -80,7 +80,7 @@ class lp_core_solver_base { vector & m_basis; vector& m_nbasis; vector& m_basis_heading; - vector & m_x; // a feasible solution, the fist time set in the constructor + vector & m_x; // a feasible solution, the first time set in the constructor vector & m_costs; lp_settings & m_settings; diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index aacabf94426..fecf66e68cf 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -49,7 +49,6 @@ class lp_primal_core_solver:public lp_core_solver_base { indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; - T m_converted_harris_eps; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; u_set m_left_basis_tableau; @@ -277,13 +276,8 @@ class lp_primal_core_solver:public lp_core_solver_base { return convert_struct::convert(std::numeric_limits::max()); } - bool get_harris_theta(X & theta); - - void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } - int find_leaving_on_harris_theta(X const & harris_theta, X & t); bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); void limit_theta(const X & lim, X & theta, bool & unlimited) { @@ -317,9 +311,6 @@ class lp_primal_core_solver:public lp_core_solver_base { limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; - X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; - } - void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); vector m_lower_bounds_dummy; // needed for the base class only @@ -489,20 +480,9 @@ class lp_primal_core_solver:public lp_core_solver_base { this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); } - // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { - // lp_assert(m < 0); - // lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed); - // const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]); - // if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) { - // theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta); - // if (theta < zero_of_type()) theta = zero_of_type(); - // } - // } - void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m < 0); - const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); - limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); + limit_theta((this->m_lower_bounds[j] - this->m_x[j]) / m, theta, unlimited); if (theta < zero_of_type()) theta = zero_of_type(); } @@ -545,25 +525,21 @@ class lp_primal_core_solver:public lp_core_solver_base { void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller lp_assert(m < 0); - const X& eps = harris_eps_for_bound(bound); if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); + limit_theta((bound - x) / m, theta, unlimited); } } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; const X & lbound = this->m_lower_bounds[j]; if (this->below_bound(x, lbound)) { - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta((lbound - x + eps) / m, theta, unlimited); + limit_theta((lbound - x) / m, theta, unlimited); } else { const X & ubound = this->m_upper_bounds[j]; if (this->below_bound(x, ubound)){ - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x + eps) / m, theta, unlimited); + limit_theta((ubound - x) / m, theta, unlimited); } else if (!this->above_bound(x, ubound)) { theta = zero_of_type(); unlimited = false; @@ -576,13 +552,11 @@ class lp_primal_core_solver:public lp_core_solver_base { const X & x = this->m_x[j]; const X & ubound = this->m_upper_bounds[j]; if (this->above_bound(x, ubound)) { - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x - eps) / m, theta, unlimited); + limit_theta((ubound - x) / m, theta, unlimited); } else { const X & lbound = this->m_lower_bounds[j]; if (this->above_bound(x, lbound)){ - const X& eps = harris_eps_for_bound(lbound); - limit_theta((lbound - x - eps) / m, theta, unlimited); + limit_theta((lbound - x) / m, theta, unlimited); } else if (!this->below_bound(x, lbound)) { theta = zero_of_type(); unlimited = false; @@ -591,9 +565,8 @@ class lp_primal_core_solver:public lp_core_solver_base { } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m > 0); - const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { - limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + limit_theta((this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); if (theta < zero_of_type()) { theta = zero_of_type(); unlimited = false; @@ -603,8 +576,7 @@ class lp_primal_core_solver:public lp_core_solver_base { void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { lp_assert(m > 0); - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + limit_theta( (this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); if (theta < zero_of_type()) { theta = zero_of_type(); } @@ -612,9 +584,9 @@ class lp_primal_core_solver:public lp_core_solver_base { // j is a basic column or the entering, in any case x[j] has to stay feasible. // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= this->m_lower_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) + // x[j] + t * m >= this->m_lower_bounds[j]( if m < 0 ) // or - // x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0) + // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { switch (this->m_column_types[j]) { case column_type::free_column: break; @@ -679,7 +651,6 @@ class lp_primal_core_solver:public lp_core_solver_base { bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; bool can_enter_basis(unsigned j); - bool done(); void init_infeasibility_costs(); void init_infeasibility_cost_for_column(unsigned j); @@ -694,90 +665,8 @@ class lp_primal_core_solver:public lp_core_solver_base { return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); } - - bool lower_bounds_are_set() const override { return true; } - void print_bound_info_and_x(unsigned j, std::ostream & out); - - void init_infeasibility_after_update_x_if_inf(unsigned leaving) { - if (this->using_infeas_costs()) { - init_infeasibility_costs_for_changed_basis_only(); - this->m_costs[leaving] = zero_of_type(); - this->remove_column_from_inf_set(leaving); - } - } - void init_inf_set() { - this->clear_inf_set(); - for (unsigned j = 0; j < this->m_n(); j++) { - if (this->m_basis_heading[j] < 0) - continue; - if (!this->column_is_feasible(j)) - this->insert_column_into_inf_set(j); - } - } - - int get_column_out_of_bounds_delta_sign(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_below_low_bound(j)) - return -1; - if (this->x_above_upper_bound(j)) - return 1; - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) - return -1; - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) - return 1; - break; - case column_type::free_column: - return 0; - default: - lp_assert(false); - } - return 0; - } - - void init_column_row_non_zeroes() { - this->m_columns_nz.resize(this->m_A.column_count()); - this->m_rows_nz.resize(this->m_A.row_count()); - for (unsigned i = 0; i < this->m_A.column_count(); i++) { - if (this->m_columns_nz[i] == 0) - this->m_columns_nz[i] = this->m_A.m_columns[i].size(); - } - for (unsigned i = 0; i < this->m_A.row_count(); i++) { - if (this->m_rows_nz[i] == 0) - this->m_rows_nz[i] = this->m_A.m_rows[i].size(); - } - } - - - int x_at_bound_sign(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - return 0; - case column_type::boxed: - if (this->x_is_at_lower_bound(j)) - return 1; - return -1; - break; - case column_type::lower_bound: - return 1; - break; - case column_type::upper_bound: - return -1; - break; - default: - lp_assert(false); - } - return 0; - - } - unsigned solve_with_tableau(); bool basis_column_is_set_correctly(unsigned j) const { @@ -844,10 +733,6 @@ class lp_primal_core_solver:public lp_core_solver_base { m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - - - m_converted_harris_eps = zero_of_type(); - this->set_status(lp_status::UNKNOWN); } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index cfd8fc947d6..04c32d41c51 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -142,53 +142,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); } - -template bool lp_primal_core_solver::get_harris_theta(X & theta) { - bool unlimited = true; - for (unsigned i : this->m_ed.m_index) { - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; - limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); - if (!unlimited && is_zero(theta)) break; - } - return unlimited; -} - - -template int lp_primal_core_solver:: -find_leaving_on_harris_theta(X const & harris_theta, X & t) { - int leaving = -1; - T pivot_abs_max = zero_of_type(); - // we know already that there is no bound flip on entering - // we also know that harris_theta is limited, so we will find a leaving - zero_harris_eps(); - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { - if (++k == steps) - k = 0; - continue; - } - X ratio; - unsigned j = this->m_basis[i]; - bool unlimited = true; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); - if ((!unlimited) && ratio <= harris_theta) { - if (leaving == -1 || abs(ed) > pivot_abs_max) { - t = ratio; - leaving = j; - pivot_abs_max = abs(ed); - } - } - if (++k == steps) k = 0; - } while (k != initial_k); - return leaving; -} - - template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, @@ -246,68 +199,6 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { return true; } -template int lp_primal_core_solver::find_leaving_and_t_precise(unsigned entering, X & t) { - bool unlimited = true; - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - unsigned row_min_nz = this->m_n() + 1; - m_leaving_candidates.clear(); - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - lp_assert(!numeric_traits::is_zero(ed)); - unsigned j = this->m_basis[i]; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); - if (!unlimited) { - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } - if (++k == steps) k = 0; - } while (unlimited && k != initial_k); - if (unlimited) { - if (try_jump_to_another_bound_on_entering_unlimited(entering, t)) - return entering; - return -1; - } - - X ratio; - while (k != initial_k) { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - lp_assert(!numeric_traits::is_zero(ed)); - unsigned j = this->m_basis[i]; - unlimited = true; - limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); - if (unlimited) { - if (++k == steps) k = 0; - continue; - } - unsigned i_nz = this->m_rows_nz[i]; - if (ratio < t) { - t = ratio; - m_leaving_candidates.clear(); - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } else if (ratio == t && i_nz < row_min_nz) { - m_leaving_candidates.clear(); - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } else if (ratio == t && i_nz == row_min_nz) { - m_leaving_candidates.push_back(j); - } - if (++k == steps) k = 0; - } - - ratio = t; - unlimited = false; - if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) { - t = ratio; - return entering; - } - k = this->m_settings.random_next() % m_leaving_candidates.size(); - return m_leaving_candidates[k]; -} @@ -499,17 +390,6 @@ template void lp_primal_core_solver::one_iteratio -template bool lp_primal_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL) return true; - if (this->get_status() == lp_status::INFEASIBLE) { - return true; - } - if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) { - this->set_status(lp_status::CANCELLED); - return true; - } - return false; -} template void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 256853be722..d4355fa800e 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -43,6 +43,7 @@ template void lp_primal_core_solver::advance_on_e } advance_on_entering_and_leaving_tableau(entering, leaving, t); } + template int lp_primal_core_solver::choose_entering_column_tableau() { //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); @@ -85,9 +86,6 @@ template void lp_primal_core_solver::advance_on_e } - - - template unsigned lp_primal_core_solver::solve_with_tableau() { init_run_tableau(); diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c993dbd7ddf..24aa8767a37 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -56,7 +56,6 @@ #include "math/lp/int_cube.h" #include "math/lp/emonics.h" #include "math/lp/static_matrix.h" -#include "math/lp/dense_matrix.h" bool my_white_space(const char & a) { return a == ' ' || a == '\t'; @@ -431,205 +430,8 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, nbasis[place_in_non_basis] = leaving; } - - -#ifdef Z3DEBUG -void test_small_lu(lp_settings & settings) { - -} - -#endif - - -void fill_long_row(static_matrix &m, int i) { - int n = m.column_count(); - for (int j = 0; j < n; j ++) { - m (i, (j + i) % n) = j * j; - } -} - - - -void fill_long_row_exp(static_matrix &m, int i) { - int n = m.column_count(); - - for (int j = 0; j < n; j ++) { - m(i, j) = my_random() % 20; - } -} - - - - - - int perm_id = 0; - - - - - - - -void init_b(vector & b, static_matrix & m, vector & x) { - for (unsigned i = 0; i < m.row_count(); i++) { - b.push_back(m.dot_product_with_row(i, x)); - } -} - - -void test_lp_0() { - std::cout << " test_lp_0 " << std::endl; - static_matrix m_(3, 7); - m_(0, 0) = 3; m_(0, 1) = 2; m_(0, 2) = 1; m_(0, 3) = 2; m_(0, 4) = 1; - m_(1, 0) = 1; m_(1, 1) = 1; m_(1, 2) = 1; m_(1, 3) = 1; m_(1, 5) = 1; - m_(2, 0) = 4; m_(2, 1) = 3; m_(2, 2) = 3; m_(2, 3) = 4; m_(2, 6) = 1; - vector x_star(7); - x_star[0] = 225; x_star[1] = 117; x_star[2] = 420; - x_star[3] = x_star[4] = x_star[5] = x_star[6] = 0; - vector b; - init_b(b, m_, x_star); - vector basis(3); - basis[0] = 0; basis[1] = 1; basis[2] = 2; - vector costs(7); - costs[0] = 19; - costs[1] = 13; - costs[2] = 12; - costs[3] = 17; - costs[4] = 0; - costs[5] = 0; - costs[6] = 0; - - vector column_types(7, column_type::lower_bound); - vector upper_bound_values; - lp_settings settings; - simple_column_namer cn; - vector nbasis; - vector heading; - - lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); - - lpsolver.solve(); -} - -void test_lp_1() { - std::cout << " test_lp_1 " << std::endl; - static_matrix m(4, 7); - m(0, 0) = 1; m(0, 1) = 3; m(0, 2) = 1; m(0, 3) = 1; - m(1, 0) = -1; m(1, 2) = 3; m(1, 4) = 1; - m(2, 0) = 2; m(2, 1) = -1; m(2, 2) = 2; m(2, 5) = 1; - m(3, 0) = 2; m(3, 1) = 3; m(3, 2) = -1; m(3, 6) = 1; -#ifdef Z3DEBUG - //print_matrix(m, std::cout); -#endif - vector x_star(7); - x_star[0] = 0; x_star[1] = 0; x_star[2] = 0; - x_star[3] = 3; x_star[4] = 2; x_star[5] = 4; x_star[6] = 2; - - vector basis(4); - basis[0] = 3; basis[1] = 4; basis[2] = 5; basis[3] = 6; - - vector b; - b.push_back(3); - b.push_back(2); - b.push_back(4); - b.push_back(2); - - vector costs(7); - costs[0] = 5; - costs[1] = 5; - costs[2] = 3; - costs[3] = 0; - costs[4] = 0; - costs[5] = 0; - costs[6] = 0; - - - - vector column_types(7, column_type::lower_bound); - vector upper_bound_values; - - std::cout << "calling lp\n"; - lp_settings settings; - simple_column_namer cn; - - vector nbasis; - vector heading; - - lp_primal_core_solver lpsolver(m, b, - x_star, - basis, - nbasis, heading, - costs, - column_types, upper_bound_values, settings, cn); - - lpsolver.solve(); -} - - -void test_lp_primal_core_solver() { - test_lp_0(); - test_lp_1(); -} - - - - -#ifdef Z3DEBUG - -void fill_uniformly(dense_matrix & m, unsigned dim) { - int v = 0; - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - m.set_elem(i, j, v++); - } - } -} - - - - -void test_dense_matrix() { - dense_matrix d(3, 2); - d.set_elem(0, 0, 1); - d.set_elem(1, 1, 2); - d.set_elem(2, 0, 3); - // print_matrix(d); - - dense_matrix unit(2, 2); - d.set_elem(0, 0, 1); - d.set_elem(1, 1, 1); - - dense_matrix c = d * unit; - - // print_matrix(d); - - dense_matrix perm(3, 3); - perm.set_elem(0, 1, 1); - perm.set_elem(1, 0, 1); - perm.set_elem(2, 2, 1); - auto c1 = perm * d; - // print_matrix(c1); - - - dense_matrix p2(2, 2); - p2.set_elem(0, 1, 1); - p2.set_elem(1, 0, 1); - auto c2 = d * p2; -} - -#endif - - - - - -void lp_solver_test() { - // lp_revised_solver lp_revised; - // lp_revised.get_minimal_solution(); -} - bool get_int_from_args_parser(const char * option, argument_parser & args_parser, unsigned & n) { std::string s = args_parser.get_option_value(option); if (!s.empty()) { @@ -650,9 +452,6 @@ bool get_double_from_args_parser(const char * option, argument_parser & args_par -bool values_are_one_percent_close(double a, double b); - - void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition @@ -939,10 +738,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-gomory", "gomory"); parser.add_option_with_help_string("-intd", "test integer_domain"); parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); - parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); - parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); - parser.add_option_with_after_string_with_help("--checklu", "the file name for lu checking"); - parser.add_option_with_after_string_with_help("--partial_pivot", "the partial pivot constant, a number somewhere between 10 and 100"); parser.add_option_with_after_string_with_help("--percent_for_enter", "which percent of columns check for entering column"); parser.add_option_with_help_string("--totalinf", "minimizes the total infeasibility instead of diminishing infeasibility of the rows"); parser.add_option_with_after_string_with_help("--rep_frq", "the report frequency, in how many iterations print the cost and other info "); @@ -1202,71 +997,6 @@ void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { n = atoi(r[1].c_str()); } -void read_row_cols(unsigned i, static_matrix& A, std::ifstream & f) { - do { - std::string line; - getline(f, line); - if (line== "row_end") - break; - auto r = split_and_trim(line); - lp_assert(r.size() == 4); - unsigned j = atoi(r[1].c_str()); - double v = atof(r[3].c_str()); - A.set(i, j, v); - } while (true); -} - -bool read_row(static_matrix & A, std::ifstream & f) { - std::string line; - getline(f, line); - if (static_cast(line.find("row")) == -1) - return false; - auto r = split_and_trim(line); - if (r[0] != "row") - std::cout << "wrong row line" << line << std::endl; - unsigned i = atoi(r[1].c_str()); - read_row_cols(i, A, f); - return true; -} - -void read_rows(static_matrix& A, std::ifstream & f) { - while (read_row(A, f)) {} -} - -void read_basis(vector & basis, std::ifstream & f) { - std::cout << "reading basis" << std::endl; - std::string line; - getline(f, line); - lp_assert(line == "basis_start"); - do { - getline(f, line); - if (line == "basis_end") - break; - unsigned j = atoi(line.c_str()); - basis.push_back(j); - } while (true); -} - -void read_indexed_vector(indexed_vector & v, std::ifstream & f) { - std::string line; - getline(f, line); - lp_assert(line == "vector_start"); - do { - getline(f, line); - if (line == "vector_end") break; - auto r = split_and_trim(line); - unsigned i = atoi(r[0].c_str()); - double val = atof(r[1].c_str()); - v.set_value(val, i); - std::cout << "setting value " << i << " = " << val << std::endl; - } while (true); -} - -void check_lu_from_file(std::string lufile_name) { - lp_assert(false); -} - - void print_st(lp_status status) { std::cout << lp_status_to_string(status) << std::endl; @@ -2298,15 +2028,6 @@ void test_lp_local(int argn, char**argv) { test_bound_propagation(); return finalize(0); } - - - std::string lufile = args_parser.get_option_value("--checklu"); - if (!lufile.empty()) { - check_lu_from_file(lufile); - return finalize(0); - } - - if (args_parser.option_is_used("-tbq")) { From 748c75275fa3f810e06b569ba04e7368fe8a691c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 13:53:23 -0800 Subject: [PATCH 501/597] more dead code removal --- src/math/lp/lar_solver.cpp | 4 ---- src/math/lp/lar_solver.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 500ee23e060..952f2ad43e0 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -2330,10 +2330,6 @@ namespace lp { return true; } - void lar_solver::pivot_column_tableau(unsigned j, unsigned row_index) { - m_mpq_lar_core_solver.m_r_solver.pivot_column_tableau(j, row_index); - m_mpq_lar_core_solver.m_r_solver.change_basis(j, r_basis()[row_index]); - } } // namespace lp diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 83d2a25fe29..a321632fb61 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -519,8 +519,6 @@ class lar_solver : public column_namer { return m_mpq_lar_core_solver.lower_bound(j); } - void pivot_column_tableau(unsigned j, unsigned row_index); - inline const impq & column_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.upper_bound(j); } From c8c0a001907af348c54e885ebbd45d3a8fce4e79 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 15:37:22 -0800 Subject: [PATCH 502/597] remove more dead code --- src/math/lp/lar_core_solver.h | 128 +----------------- src/math/lp/lar_core_solver_def.h | 1 - src/math/lp/lp_core_solver_base.h | 2 - src/math/lp/lp_primal_core_solver.h | 55 +------- src/math/lp/lp_primal_core_solver_def.h | 64 +-------- .../lp/lp_primal_core_solver_tableau_def.h | 1 - src/math/lp/lp_settings.h | 6 - src/math/lp/numeric_pair.h | 15 +- 8 files changed, 5 insertions(+), 267 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 52c5b6f1a84..966e2f0d765 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -20,9 +20,6 @@ Copyright (c) 2017 Microsoft Corporation namespace lp { class lar_core_solver { - // m_sign_of_entering is set to 1 if the entering variable needs - // to grow and is set to -1 otherwise - int m_sign_of_entering_delta; vector> m_infeasible_linear_combination; int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; @@ -40,8 +37,6 @@ class lar_core_solver { vector m_r_basis; vector m_r_nbasis; vector m_r_heading; - stacked_vector m_r_columns_nz; - stacked_vector m_r_rows_nz; lp_primal_core_solver> m_r_solver; // solver in rational numbers @@ -82,16 +77,9 @@ class lar_core_solver { m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } - bool row_is_infeasible(unsigned row); - - bool row_is_evidence(unsigned row); - - bool find_evidence_row(); - + void prefix_r(); - void prefix_d(); - unsigned m_m() const { return m_r_A.row_count(); } unsigned m_n() const { return m_r_A.column_count(); } @@ -103,8 +91,6 @@ class lar_core_solver { template int get_sign(const L & v) { return v > zero_of_type() ? 1 : (v < zero_of_type() ? -1 : 0); } - void fill_evidence(unsigned row); - unsigned get_number_of_non_ints() const; void solve(); @@ -131,31 +117,6 @@ class lar_core_solver { } - template - void push_vector(stacked_vector & pushed_vector, const vector & vector) { - lp_assert(pushed_vector.size() <= vector.size()); - for (unsigned i = 0; i < vector.size();i++) { - if (i == pushed_vector.size()) { - pushed_vector.push_back(vector[i]); - } else { - pushed_vector[i] = vector[i]; - } - } - pushed_vector.push(); - } - - void pop_markowitz_counts(unsigned k) { - m_r_columns_nz.pop(k); - m_r_rows_nz.pop(k); - m_r_solver.m_columns_nz.resize(m_r_columns_nz.size()); - m_r_solver.m_rows_nz.resize(m_r_rows_nz.size()); - for (unsigned i = 0; i < m_r_columns_nz.size(); i++) - m_r_solver.m_columns_nz[i] = m_r_columns_nz[i]; - for (unsigned i = 0; i < m_r_rows_nz.size(); i++) - m_r_solver.m_rows_nz[i] = m_r_rows_nz[i]; - } - - void pop(unsigned k) { // rationals m_r_lower_bounds.pop(k); @@ -172,72 +133,7 @@ class lar_core_solver { } - template - bool is_zero_vector(const vector & b) { - for (const L & m: b) - if (!is_zero(m)) return false; - return true; - } - - - bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair & delta) { - auto & x = m_r_x[j]; - switch (pos_type) { - case at_lower_bound: - if (x == m_r_solver.m_lower_bounds[j]) - return false; - delta = m_r_solver.m_lower_bounds[j] - x; - m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j]; - break; - case at_fixed: - case at_upper_bound: - if (x == m_r_solver.m_upper_bounds[j]) - return false; - delta = m_r_solver.m_upper_bounds[j] - x; - x = m_r_solver.m_upper_bounds[j]; - break; - case free_of_bounds: { - return false; - } - case not_at_bound: - switch (m_column_types[j]) { - case column_type::free_column: - return false; - case column_type::upper_bound: - delta = m_r_solver.m_upper_bounds[j] - x; - x = m_r_solver.m_upper_bounds[j]; - break; - case column_type::lower_bound: - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - break; - case column_type::boxed: - if (x > m_r_solver.m_upper_bounds[j]) { - delta = m_r_solver.m_upper_bounds[j] - x; - x += m_r_solver.m_upper_bounds[j]; - } else { - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - } - break; - case column_type::fixed: - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - break; - - default: - lp_assert(false); - } - break; - default: - lp_unreachable(); - } - m_r_solver.remove_column_from_inf_set(j); - return true; - } - - bool r_basis_is_OK() const { #ifdef Z3DEBUG @@ -255,28 +151,6 @@ class lar_core_solver { } - - void fill_basis_d( - vector& basis_d, - vector& heading_d, - vector& nbasis_d){ - basis_d = m_r_basis; - heading_d = m_r_heading; - nbasis_d = m_r_nbasis; - } - - template - void extract_signature_from_lp_core_solver(const lp_primal_core_solver & solver, lar_solution_signature & signature) { - signature.clear(); - lp_assert(signature.size() == 0); - for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { - if (solver.m_basis_heading[j] < 0) { - signature[j] = solver.get_non_basic_column_value_position(j); - } - } - } - - bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 419345f0ca0..2e205291e6f 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -46,7 +46,6 @@ void lar_core_solver::prefix_r() { } - void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 96f8729a009..9fb4d4333cd 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -72,8 +72,6 @@ class lp_core_solver_base { unsigned inf_set_size() const { return m_inf_set.size(); } bool using_infeas_costs() const { return m_using_infeas_costs; } void set_using_infeas_costs(bool val) { m_using_infeas_costs = val; } - vector m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column - vector m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A // vector const & m_b; // the right side diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index fecf66e68cf..17b1ea494d0 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -41,54 +41,21 @@ namespace lp { template class lp_primal_core_solver:public lp_core_solver_base { public: - // m_sign_of_entering is set to 1 if the entering variable needs - // to grow and is set to -1 otherwise - unsigned m_column_norm_update_counter; - T m_enter_price_eps; int m_sign_of_entering_delta; - indexed_vector m_beta; // see Swietanowski working vector beta for column norms - T m_epsilon_of_reduced_cost; vector m_costs_backup; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; - u_set m_left_basis_tableau; + u_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; std::list m_non_basis_list; void sort_non_basis(); - void sort_non_basis_rational(); int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); int choose_entering_column_tableau(); int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); - bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { - // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos - // we have xbj = -aj * xj - lp_assert(this->m_basis_heading[j] < 0); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: return true; - case column_type::fixed: return false; - case column_type::lower_bound: - if (sign < 0) - return true; - return !this->x_is_at_lower_bound(j); - case column_type::upper_bound: - if (sign > 0) - return true; - return !this->x_is_at_upper_bound(j); - case column_type::boxed: - if (sign < 0) - return !this->x_is_at_lower_bound(j); - return !this->x_is_at_upper_bound(j); - } - - lp_assert(false); // cannot be here - return false; - } - bool needs_to_grow(unsigned bj) const { lp_assert(!this->column_is_feasible(bj)); @@ -272,10 +239,7 @@ class lp_primal_core_solver:public lp_core_solver_base { a_ent = rc.coeff(); return rc.var(); } - static X positive_infinity() { - return convert_struct::convert(std::numeric_limits::max()); - } - + bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); @@ -313,8 +277,6 @@ class lp_primal_core_solver:public lp_core_solver_base { void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); - vector m_lower_bounds_dummy; // needed for the base class only - X get_max_bound(vector & b); #ifdef Z3DEBUG @@ -334,10 +296,6 @@ class lp_primal_core_solver:public lp_core_solver_base { void backup_and_normalize_costs(); - void init_run(); - - void calc_working_vector_beta_for_column_norms(); - void advance_on_entering_and_leaving(int entering, int leaving, X & t); void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t); void advance_on_entering_equal_leaving(int entering, X & t); @@ -368,7 +326,6 @@ class lp_primal_core_solver:public lp_core_solver_base { // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} - void one_iteration(); void one_iteration_tableau(); // this version assumes that the leaving already has the right value, and does not update it @@ -658,12 +615,6 @@ class lp_primal_core_solver:public lp_core_solver_base { void init_infeasibility_costs_for_changed_basis_only(); void print_column(unsigned j, std::ostream & out); - // j is the basic column, x is the value at x[j] - // d is the coefficient before m_entering in the row with j as the basis column - template - bool same_sign_with_entering_delta(const L & a) { - return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); - } void print_bound_info_and_x(unsigned j, std::ostream & out); @@ -730,8 +681,6 @@ class lp_primal_core_solver:public lp_core_solver_base { column_type_array, lower_bound_values, upper_bound_values), - m_beta(A.row_count()), - m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { this->set_status(lp_status::UNKNOWN); } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 04c32d41c51..1c48f11636c 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -32,7 +32,7 @@ namespace lp { // The right side b is given implicitly by x and the basis template -void lp_primal_core_solver::sort_non_basis_rational() { +void lp_primal_core_solver::sort_non_basis() { std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); @@ -50,10 +50,6 @@ void lp_primal_core_solver::sort_non_basis_rational() { } -template -void lp_primal_core_solver::sort_non_basis() { - sort_non_basis_rational(); -} template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { @@ -249,15 +245,6 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( } } -template X lp_primal_core_solver::get_max_bound(vector & b) { - X ret = zero_of_type(); - for (auto & v : b) { - X a = abs(v); - if (a > ret) ret = a; - } - return ret; -} - #ifdef Z3DEBUG template void lp_primal_core_solver::check_Ax_equal_b() { dense_matrix d(this->m_A); @@ -282,38 +269,6 @@ template void lp_primal_core_solver::check_cor } #endif -// from page 183 of Istvan Maros's book -// the basis structures have not changed yet -template -void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving) { - // the basis heading has changed already -#ifdef Z3DEBUG - auto & basis_heading = this->m_basis_heading; - lp_assert(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); - lp_assert(basis_heading[leaving] < 0); -#endif - T pivot = this->m_pivot_row[entering]; - T dq = this->m_d[entering]/pivot; - for (auto j : this->m_pivot_row.m_index) { - // for (auto j : this->m_nbasis) - if (this->m_basis_heading[j] >= 0) continue; - if (j != leaving) - this->m_d[j] -= dq * this->m_pivot_row[j]; - } - this->m_d[leaving] = -dq; - if (this->current_x_is_infeasible()) { - this->m_d[leaving] -= this->m_costs[leaving]; - this->m_costs[leaving] = zero_of_type(); - } - this->m_d[entering] = numeric_traits::zero(); -} - -// return 0 if the reduced cost at entering is close enough to the refreshed -// 1 if it is way off, and 2 if it is unprofitable -template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { - return 0; - -} template void lp_primal_core_solver::backup_and_normalize_costs() { if (this->m_look_for_feasible_solution_only) @@ -321,9 +276,6 @@ template void lp_primal_core_solver::backup_an m_costs_backup = this->m_costs; } -template void lp_primal_core_solver::init_run() { - -} template @@ -377,20 +329,6 @@ template void lp_primal_core_solver::find_feas solve(); } -template void lp_primal_core_solver::one_iteration() { - unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - int entering = choose_entering_column(number_of_benefitial_columns_to_go_over); - if (entering == -1) { - decide_on_status_when_cannot_find_entering(); - } - else { - advance_on_entering(entering); - } -} - - - - template void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { for (unsigned i : this->m_ed.m_index) diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index d4355fa800e..de2f1c208af 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -283,7 +283,6 @@ template void lp_primal_core_solver::init_run_tab return; if (this->m_settings.backup_costs) backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = zero_of_type(); if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 197b7c04f17..314930fc175 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -179,15 +179,9 @@ struct lp_settings { bool int_run_gcd_test() const { return m_int_run_gcd_test; } bool& int_run_gcd_test() { return m_int_run_gcd_test; } unsigned reps_in_scaler { 20 }; - // when the absolute value of an element is less than pivot_epsilon - // in pivoting, we treat it as a zero - // a quotation "if some choice of the entering variable leads to an eta matrix - // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... int c_partial_pivoting { 10 }; // this is the constant c from page 410 unsigned depth_of_rook_search { 4 }; bool using_partial_pivoting { true }; - // dissertation of Achim Koberstein - // if Bx - b is different at any component more that refactor_epsilon then we refactor unsigned percent_of_entering_to_check { 5 }; // we try to find a profitable column in a percentage of the columns bool use_scaling { true }; diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index f59aa84ba52..4a60c82ca9e 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -107,7 +107,6 @@ class numeric_traits { template struct convert_struct { static X convert(const Y & y){ return X(y);} - static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits::get_double(x)) < y; } static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } }; @@ -316,16 +315,12 @@ struct convert_struct> { typedef numeric_pair impq; -template bool is_epsilon_small(const X & v, const double& eps); // forward definition { return convert_struct::is_epsilon_small(v, eps);} template struct convert_struct, double> { static numeric_pair convert(const double & q) { return numeric_pair(convert_struct::convert(q), numeric_traits::zero()); } - static bool is_epsilon_small(const numeric_pair & p, const double & eps) { - return convert_struct::is_epsilon_small(p.x, eps) && convert_struct::is_epsilon_small(p.y, eps); - } static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { // lp_unreachable(); return false; @@ -340,10 +335,7 @@ struct convert_struct, double> { static numeric_pair convert(const double & q) { return numeric_pair(q, 0.0); } - static bool is_epsilon_small(const numeric_pair & p, const double & eps) { - return std::abs(p.x) < eps && std::abs(p.y) < eps; - } - + static int compare_on_coord(const double & x, const double & bound, const double eps) { if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case double relative = (bound > 0)? - eps: eps; @@ -369,9 +361,6 @@ struct convert_struct, double> { template <> struct convert_struct { - static bool is_epsilon_small(const double& x, const double & eps) { - return x < eps && x > -eps; - } static double convert(const double & y){ return y;} static bool below_bound_numeric(const double & x, const double & bound, const double & eps) { if (bound == 0) return x < - eps; @@ -384,8 +373,6 @@ struct convert_struct { return x > bound * (1.0 + relative) + eps; } }; - -template bool is_epsilon_small(const X & v, const double &eps) { return convert_struct::is_epsilon_small(v, eps);} template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} template T floor(const numeric_pair & r) { From c6be67bf3b8f19f7edc37ce34b03805cc0fc040c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 16:58:49 -0800 Subject: [PATCH 503/597] more dead code --- src/math/lp/numeric_pair.h | 75 -------------------------------------- 1 file changed, 75 deletions(-) diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 4a60c82ca9e..a64825cd8f1 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -112,18 +112,6 @@ struct convert_struct { }; -template <> -struct convert_struct { - static double convert(const mpq & q) {return q.get_double();} -}; - - -template <> -struct convert_struct { - static mpq convert(unsigned q) {return mpq(q);} -}; - - template struct numeric_pair { @@ -308,71 +296,8 @@ class numeric_traits> { } }; -template <> -struct convert_struct> { - static double convert(const numeric_pair & q) {return q.x;} -}; - typedef numeric_pair impq; - -template -struct convert_struct, double> { - static numeric_pair convert(const double & q) { - return numeric_pair(convert_struct::convert(q), numeric_traits::zero()); - } - static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // lp_unreachable(); - return false; - } - static bool above_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // lp_unreachable(); - return false; - } -}; -template <> -struct convert_struct, double> { - static numeric_pair convert(const double & q) { - return numeric_pair(q, 0.0); - } - - static int compare_on_coord(const double & x, const double & bound, const double eps) { - if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case - double relative = (bound > 0)? - eps: eps; - return (x < bound * (1.0 + relative) - eps)? -1 : ((x > bound * (1.0 - relative) + eps)? 1 : 0); - } - - static bool below_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { - int r = compare_on_coord(x.x, bound.x, eps); - if (r == 1) return false; - if (r == -1) return true; - // the first coordinates are almost the same - return compare_on_coord(x.y, bound.y, eps) == -1; - } - - static bool above_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { - int r = compare_on_coord(x.x, bound.x, eps); - if (r == -1) return false; - if (r == 1) return true; - // the first coordinates are almost the same - return compare_on_coord(x.y, bound.y, eps) == 1; - } -}; - -template <> -struct convert_struct { - static double convert(const double & y){ return y;} - static bool below_bound_numeric(const double & x, const double & bound, const double & eps) { - if (bound == 0) return x < - eps; - double relative = (bound > 0)? - eps: eps; - return x < bound * (1.0 + relative) - eps; - } - static bool above_bound_numeric(const double & x, const double & bound, const double & eps) { - if (bound == 0) return x > eps; - double relative = (bound > 0)? eps: - eps; - return x > bound * (1.0 + relative) + eps; - } -}; template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} template T floor(const numeric_pair & r) { From 13549aff6669bb9536bd3c1fe9f6aefbadf23abf Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 17:11:26 -0800 Subject: [PATCH 504/597] rm dead code --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lp_primal_core_solver.h | 2 -- src/math/lp/lp_primal_core_solver_def.h | 5 ---- .../lp/lp_primal_core_solver_tableau_def.h | 3 ++- src/math/lp/lp_utils.cpp | 26 ------------------- 5 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 src/math/lp/lp_utils.cpp diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 98241bf603e..4ca8cb1d2ae 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,7 +20,6 @@ z3_add_component(lp lp_core_solver_base.cpp lp_primal_core_solver.cpp lp_settings.cpp - lp_utils.cpp matrix.cpp mon_eq.cpp monomial_bounds.cpp diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 17b1ea494d0..1719f9c556c 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -618,8 +618,6 @@ class lp_primal_core_solver:public lp_core_solver_base { void print_bound_info_and_x(unsigned j, std::ostream & out); - unsigned solve_with_tableau(); - bool basis_column_is_set_correctly(unsigned j) const { return this->m_A.m_columns[j].size() == 1; diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 1c48f11636c..cc8ad88b392 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -315,11 +315,6 @@ template unsigned lp_primal_core_solver::get_num } -// returns the number of iterations -template unsigned lp_primal_core_solver::solve() { - TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); - return solve_with_tableau(); -} // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index de2f1c208af..c7b604b9553 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -87,7 +87,8 @@ template void lp_primal_core_solver::advance_on_e } template -unsigned lp_primal_core_solver::solve_with_tableau() { +unsigned lp_primal_core_solver::solve() { + TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); init_run_tableau(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { this->set_status(lp_status::FEASIBLE); diff --git a/src/math/lp/lp_utils.cpp b/src/math/lp/lp_utils.cpp deleted file mode 100644 index b909a0389d0..00000000000 --- a/src/math/lp/lp_utils.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_utils.h" -#ifdef lp_for_z3 -namespace lp { - -} -#endif - From 11eab94321b1efccf040155fea424d34d8440810 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 17:52:59 -0800 Subject: [PATCH 505/597] more dead code --- src/math/lp/CMakeLists.txt | 2 - src/math/lp/binary_heap_priority_queue.cpp | 38 --- src/math/lp/binary_heap_priority_queue.h | 83 ------ src/math/lp/binary_heap_priority_queue_def.h | 214 --------------- src/math/lp/binary_heap_upair_queue.cpp | 32 --- src/math/lp/binary_heap_upair_queue.h | 65 ----- src/math/lp/binary_heap_upair_queue_def.h | 126 --------- src/math/lp/conversion_helper.h | 35 --- src/math/lp/indexer_of_constraints.h | 45 ---- src/math/lp/lar_core_solver.h | 1 - src/math/lp/lar_solver.h | 1 - src/math/lp/lp_primal_core_solver.h | 1 - src/math/lp/permutation_matrix.cpp | 23 -- src/math/lp/permutation_matrix.h | 61 +---- src/math/lp/permutation_matrix_def.h | 261 +------------------ src/math/lp/tail_matrix.h | 50 ---- src/test/lp/lp.cpp | 67 ----- 17 files changed, 10 insertions(+), 1095 deletions(-) delete mode 100644 src/math/lp/binary_heap_priority_queue.cpp delete mode 100644 src/math/lp/binary_heap_priority_queue.h delete mode 100644 src/math/lp/binary_heap_priority_queue_def.h delete mode 100644 src/math/lp/binary_heap_upair_queue.cpp delete mode 100644 src/math/lp/binary_heap_upair_queue.h delete mode 100644 src/math/lp/binary_heap_upair_queue_def.h delete mode 100644 src/math/lp/conversion_helper.h delete mode 100644 src/math/lp/indexer_of_constraints.h delete mode 100644 src/math/lp/tail_matrix.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 4ca8cb1d2ae..dd72f36ee8a 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -1,7 +1,5 @@ z3_add_component(lp SOURCES - binary_heap_priority_queue.cpp - binary_heap_upair_queue.cpp core_solver_pretty_printer.cpp dense_matrix.cpp emonics.cpp diff --git a/src/math/lp/binary_heap_priority_queue.cpp b/src/math/lp/binary_heap_priority_queue.cpp deleted file mode 100644 index ec6630b1dbb..00000000000 --- a/src/math/lp/binary_heap_priority_queue.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/numeric_pair.h" -#include "math/lp/binary_heap_priority_queue_def.h" -namespace lp { -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue::enqueue(unsigned int, int const&); -template void binary_heap_priority_queue::enqueue(unsigned int, mpq const&); -template void binary_heap_priority_queue::remove(unsigned int); -template unsigned binary_heap_priority_queue >::dequeue(); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue >::enqueue(unsigned int, numeric_pair const&); -template void binary_heap_priority_queue >::resize(unsigned int); -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); -template void binary_heap_priority_queue::resize(unsigned int); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue::enqueue(unsigned int, unsigned int const&); -template void binary_heap_priority_queue::remove(unsigned int); -template void lp::binary_heap_priority_queue::resize(unsigned int); -} diff --git a/src/math/lp/binary_heap_priority_queue.h b/src/math/lp/binary_heap_priority_queue.h deleted file mode 100644 index 1ea8363efe1..00000000000 --- a/src/math/lp/binary_heap_priority_queue.h +++ /dev/null @@ -1,83 +0,0 @@ - -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lp_utils.h" -namespace lp { -// the elements with the smallest priority are dequeued first -template -class binary_heap_priority_queue { - vector m_priorities; - - // indexing for A starts from 1 - vector m_heap; // keeps the elements of the queue - vector m_heap_inverse; // o = m_heap[m_heap_inverse[o]] - unsigned m_heap_size; - // is is the child place in heap - void swap_with_parent(unsigned i); - void put_at(unsigned i, unsigned h); - void decrease_priority(unsigned o, T newPriority); -public: -#ifdef Z3DEBUG - bool is_consistent() const; -#endif -public: - void remove(unsigned o); - unsigned size() const { return m_heap_size; } - binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror - // n is the initial queue capacity. - // The capacity will be enlarged each time twice if needed - binary_heap_priority_queue(unsigned n); - - void clear() { - for (unsigned i = 0; i < m_heap_size; i++) { - unsigned o = m_heap[i+1]; - m_heap_inverse[o] = -1; - } - m_heap_size = 0; - } - - void resize(unsigned n); - void put_to_heap(unsigned i, unsigned o); - - void enqueue_new(unsigned o, const T& priority); - - // This method can work with an element that is already in the queue. - // In this case the priority will be changed and the queue adjusted. - void enqueue(unsigned o, const T & priority); - void change_priority_for_existing(unsigned o, const T & priority); - T get_priority(unsigned o) const { return m_priorities[o]; } - bool is_empty() const { return m_heap_size == 0; } - - /// return the first element of the queue and removes it from the queue - unsigned dequeue_and_get_priority(T & priority); - void fix_heap_under(unsigned i); - void put_the_last_at_the_top_and_fix_the_heap(); - /// return the first element of the queue and removes it from the queue - unsigned dequeue(); - unsigned peek() const { - lp_assert(m_heap_size > 0); - return m_heap[1]; - } - void print(std::ostream & out); -}; -} diff --git a/src/math/lp/binary_heap_priority_queue_def.h b/src/math/lp/binary_heap_priority_queue_def.h deleted file mode 100644 index 0e640d949df..00000000000 --- a/src/math/lp/binary_heap_priority_queue_def.h +++ /dev/null @@ -1,214 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/binary_heap_priority_queue.h" -namespace lp { -// "i" is the child's place in the heap -template void binary_heap_priority_queue::swap_with_parent(unsigned i) { - unsigned parent = m_heap[i >> 1]; - put_at(i >> 1, m_heap[i]); - put_at(i, parent); -} - -template void binary_heap_priority_queue::put_at(unsigned i, unsigned h) { - m_heap[i] = h; - m_heap_inverse[h] = i; -} - -template void binary_heap_priority_queue::decrease_priority(unsigned o, T newPriority) { - m_priorities[o] = newPriority; - int i = m_heap_inverse[o]; - while (i > 1) { - if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]]) - swap_with_parent(i); - else - break; - i >>= 1; - } -} - -#ifdef Z3DEBUG -template bool binary_heap_priority_queue::is_consistent() const { - for (int i = 0; i < m_heap_inverse.size(); i++) { - int i_index = m_heap_inverse[i]; - lp_assert(i_index <= static_cast(m_heap_size)); - lp_assert(i_index == -1 || m_heap[i_index] == i); - } - for (unsigned i = 1; i < m_heap_size; i++) { - unsigned ch = i << 1; - for (int k = 0; k < 2; k++) { - if (ch > m_heap_size) break; - if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){ - return false; - } - ch++; - } - } - return true; -} -#endif - -template void binary_heap_priority_queue::remove(unsigned o) { - T priority_of_o = m_priorities[o]; - int o_in_heap = m_heap_inverse[o]; - if (o_in_heap == -1) { - return; // nothing to do - } - lp_assert(static_cast(o_in_heap) <= m_heap_size); - if (static_cast(o_in_heap) < m_heap_size) { - put_at(o_in_heap, m_heap[m_heap_size--]); - if (m_priorities[m_heap[o_in_heap]] > priority_of_o) { - fix_heap_under(o_in_heap); - } else { // we need to propagate the m_heap[o_in_heap] up - unsigned i = o_in_heap; - while (i > 1) { - unsigned ip = i >> 1; - if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]]) - swap_with_parent(i); - else - break; - i = ip; - } - } - } else { - lp_assert(static_cast(o_in_heap) == m_heap_size); - m_heap_size--; - } - m_heap_inverse[o] = -1; - // lp_assert(is_consistent()); -} -// n is the initial queue capacity. -// The capacity will be enlarged two times automatically if needed -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned n) : - m_priorities(n), - m_heap(n + 1), // because the indexing for A starts from 1 - m_heap_inverse(n, -1), - m_heap_size(0) -{ } - - -template void binary_heap_priority_queue::resize(unsigned n) { - m_priorities.resize(n); - m_heap.resize(n + 1); - m_heap_inverse.resize(n, -1); -} - -template void binary_heap_priority_queue::put_to_heap(unsigned i, unsigned o) { - m_heap[i] = o; - m_heap_inverse[o] = i; -} - -template void binary_heap_priority_queue::enqueue_new(unsigned o, const T& priority) { - m_heap_size++; - int i = m_heap_size; - lp_assert(o < m_priorities.size()); - m_priorities[o] = priority; - put_at(i, o); - while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { - swap_with_parent(i); - i >>= 1; - } -} -// This method can work with an element that is already in the queue. -// In this case the priority will be changed and the queue adjusted. -template void binary_heap_priority_queue::enqueue(unsigned o, const T & priority) { - if (o >= m_priorities.size()) { - if (o == 0) - resize(2); - else - resize(o << 1); // make the size twice larger - } - - if (m_heap_inverse[o] == -1) - enqueue_new(o, priority); - else - change_priority_for_existing(o, priority); -} - -template void binary_heap_priority_queue::change_priority_for_existing(unsigned o, const T & priority) { - if (m_priorities[o] > priority) { - decrease_priority(o, priority); - } else { - m_priorities[o] = priority; - fix_heap_under(m_heap_inverse[o]); - } -} - - -/// return the first element of the queue and removes it from the queue -template unsigned binary_heap_priority_queue::dequeue_and_get_priority(T & priority) { - lp_assert(m_heap_size != 0); - int ret = m_heap[1]; - priority = m_priorities[ret]; - put_the_last_at_the_top_and_fix_the_heap(); - return ret; -} - -template void binary_heap_priority_queue::fix_heap_under(unsigned i) { - while (true) { - unsigned smallest = i; - unsigned l = i << 1; - if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]]) - smallest = l; - unsigned r = l + 1; - if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]]) - smallest = r; - if (smallest != i) - swap_with_parent(smallest); - else - break; - i = smallest; - } -} - -template void binary_heap_priority_queue::put_the_last_at_the_top_and_fix_the_heap() { - if (m_heap_size > 1) { - put_at(1, m_heap[m_heap_size--]); - fix_heap_under(1); - } else { - m_heap_size--; - } -} -/// return the first element of the queue and removes it from the queue -template unsigned binary_heap_priority_queue::dequeue() { - lp_assert(m_heap_size > 0); - int ret = m_heap[1]; - put_the_last_at_the_top_and_fix_the_heap(); - m_heap_inverse[ret] = -1; - return ret; -} -template void binary_heap_priority_queue::print(std::ostream & out) { - vector index; - vector prs; - while (size()) { - T prior; - int j = dequeue_and_get_priority(prior); - index.push_back(j); - prs.push_back(prior); - out << "(" << j << ", " << prior << ")"; - } - out << std::endl; - // restore the queue - for (int i = 0; i < index.size(); i++) - enqueue(index[i], prs[i]); -} -} diff --git a/src/math/lp/binary_heap_upair_queue.cpp b/src/math/lp/binary_heap_upair_queue.cpp deleted file mode 100644 index f1ff1d63946..00000000000 --- a/src/math/lp/binary_heap_upair_queue.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/binary_heap_upair_queue_def.h" -namespace lp { -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); -template unsigned binary_heap_upair_queue::dequeue_available_spot(); -template unsigned binary_heap_upair_queue::dequeue_available_spot(); -template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, int const&); -template void binary_heap_upair_queue::remove(unsigned int, unsigned int); -template void binary_heap_upair_queue::remove(unsigned int, unsigned int); -template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); -template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, unsigned int const&); -template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); -} diff --git a/src/math/lp/binary_heap_upair_queue.h b/src/math/lp/binary_heap_upair_queue.h deleted file mode 100644 index ce454280394..00000000000 --- a/src/math/lp/binary_heap_upair_queue.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include -#include -#include "math/lp/binary_heap_priority_queue.h" - - -typedef std::pair upair; - -namespace lp { -template -class binary_heap_upair_queue { - binary_heap_priority_queue m_q; - std::unordered_map m_pairs_to_index; - svector m_pairs; // inverse to index - svector m_available_spots; -public: - binary_heap_upair_queue(unsigned size); - - unsigned dequeue_available_spot(); - bool is_empty() const { return m_q.is_empty(); } - - unsigned size() const {return m_q.size(); } - - bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end(); - } - - void remove(unsigned i, unsigned j); - bool ij_index_is_new(unsigned ij_index) const; - void enqueue(unsigned i, unsigned j, const T & priority); - void dequeue(unsigned & i, unsigned &j); - T get_priority(unsigned i, unsigned j) const; -#ifdef Z3DEBUG - bool pair_to_index_is_a_bijection() const; - bool available_spots_are_correct() const; - bool is_correct() const { - return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct(); - } -#endif - void resize(unsigned size) { m_q.resize(size); } -}; -} diff --git a/src/math/lp/binary_heap_upair_queue_def.h b/src/math/lp/binary_heap_upair_queue_def.h deleted file mode 100644 index 65485a6ebf8..00000000000 --- a/src/math/lp/binary_heap_upair_queue_def.h +++ /dev/null @@ -1,126 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "math/lp/lp_utils.h" -#include "math/lp/binary_heap_upair_queue.h" -namespace lp { -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) { - for (unsigned i = 0; i < size; i++) - m_available_spots.push_back(i); -} - -template unsigned -binary_heap_upair_queue::dequeue_available_spot() { - lp_assert(m_available_spots.empty() == false); - unsigned ret = m_available_spots.back(); - m_available_spots.pop_back(); - return ret; -} - -template void binary_heap_upair_queue::remove(unsigned i, unsigned j) { - upair p(i, j); - auto it = m_pairs_to_index.find(p); - if (it == m_pairs_to_index.end()) - return; // nothing to do - m_q.remove(it->second); - m_available_spots.push_back(it->second); - m_pairs_to_index.erase(it); -} - - -template bool binary_heap_upair_queue::ij_index_is_new(unsigned ij_index) const { - for (auto it : m_pairs_to_index) { - if (it.second == ij_index) - return false; - } - return true; -} - -template void binary_heap_upair_queue::enqueue(unsigned i, unsigned j, const T & priority) { - upair p(i, j); - auto it = m_pairs_to_index.find(p); - unsigned ij_index; - if (it == m_pairs_to_index.end()) { - // it is a new pair, let us find a spot for it - if (m_available_spots.empty()) { - // we ran out of empty spots - unsigned size_was = static_cast(m_pairs.size()); - unsigned new_size = size_was << 1; - for (unsigned i = size_was; i < new_size; i++) - m_available_spots.push_back(i); - m_pairs.resize(new_size); - } - ij_index = dequeue_available_spot(); - // lp_assert(ij_indexsecond; - } - m_q.enqueue(ij_index, priority); -} - -template void binary_heap_upair_queue::dequeue(unsigned & i, unsigned &j) { - lp_assert(!m_q.is_empty()); - unsigned ij_index = m_q.dequeue(); - upair & p = m_pairs[ij_index]; - i = p.first; - j = p.second; - m_available_spots.push_back(ij_index); - m_pairs_to_index.erase(p); -} - - -template T binary_heap_upair_queue::get_priority(unsigned i, unsigned j) const { - auto it = m_pairs_to_index.find(std::make_pair(i, j)); - if (it == m_pairs_to_index.end()) - return T(0xFFFFFF); // big number - return m_q.get_priority(it->second); -} - -#ifdef Z3DEBUG -template bool binary_heap_upair_queue::pair_to_index_is_a_bijection() const { - std::set tmp; - for (auto p : m_pairs_to_index) { - unsigned j = p.second; - unsigned size = tmp.size(); - tmp.insert(j); - if (tmp.size() == size) - return false; - } - return true; -} - -template bool binary_heap_upair_queue::available_spots_are_correct() const { - std::set tmp; - for (auto p : m_available_spots){ - tmp.insert(p); - } - if (tmp.size() != m_available_spots.size()) - return false; - for (auto it : m_pairs_to_index) - if (tmp.find(it.second) != tmp.end()) - return false; - return true; -} -#endif -} diff --git a/src/math/lp/conversion_helper.h b/src/math/lp/conversion_helper.h deleted file mode 100644 index ba8b6a9835f..00000000000 --- a/src/math/lp/conversion_helper.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -namespace lp { -template -struct conversion_helper { - static V get_lower_bound(const column_info & ci) { - return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0); - } - - static V get_upper_bound(const column_info & ci) { - return V(ci.get_upper_bound(), ci.upper_bound_is_strict()? -1 : 0); - } -}; - - -} diff --git a/src/math/lp/indexer_of_constraints.h b/src/math/lp/indexer_of_constraints.h deleted file mode 100644 index 9bda22bc17a..00000000000 --- a/src/math/lp/indexer_of_constraints.h +++ /dev/null @@ -1,45 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - Nikolaj Bjorner (nbjorner) - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "math/lp/binary_heap_priority_queue.h" -namespace lp { - -class indexer_of_constraints { - binary_heap_priority_queue m_queue_of_released_indices; - unsigned m_max; -public: - indexer_of_constraints() :m_max(0) {} - unsigned get_new_index() { - unsigned ret; - if (m_queue_of_released_indices.is_empty()) { - ret = m_max++; - } - else { - ret = m_queue_of_released_indices.dequeue(); - } - return ret; - }; - void release_index(unsigned i) { - m_queue_of_released_indices.enqueue(i, i); - }; - unsigned max() const { return m_max; } -}; -} diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 966e2f0d765..59929e34f52 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -12,7 +12,6 @@ Copyright (c) 2017 Microsoft Corporation #include "math/lp/lp_core_solver_base.h" #include #include "math/lp/indexed_vector.h" -#include "math/lp/binary_heap_priority_queue.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" #include "math/lp/lar_solution_signature.h" diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index a321632fb61..5c77c679a47 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -37,7 +37,6 @@ #include "math/lp/stacked_vector.h" #include "math/lp/implied_bound.h" #include "math/lp/bound_analyzer_on_row.h" -#include "math/lp/conversion_helper.h" #include "math/lp/int_solver.h" #include "math/lp/nra_solver.h" #include "math/lp/lp_types.h" diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 1719f9c556c..628777f3328 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -32,7 +32,6 @@ Revision History: #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/binary_heap_priority_queue.h" #include "math/lp/u_set.h" namespace lp { diff --git a/src/math/lp/permutation_matrix.cpp b/src/math/lp/permutation_matrix.cpp index 762b85d63f0..be4b7335c65 100644 --- a/src/math/lp/permutation_matrix.cpp +++ b/src/math/lp/permutation_matrix.cpp @@ -24,31 +24,8 @@ Revision History: template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix>::init(unsigned int); -template void lp::permutation_matrix::apply_from_right(vector&); -template bool lp::permutation_matrix::is_identity() const; -template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); template lp::permutation_matrix::permutation_matrix(unsigned int); template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix >::apply_from_right(vector&); -template bool lp::permutation_matrix >::is_identity() const; -template void lp::permutation_matrix >::multiply_by_permutation_from_left(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_permutation_from_right(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_permutation_reverse_from_left(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_reverse_from_right(lp::permutation_matrix >&); template lp::permutation_matrix >::permutation_matrix(unsigned int); template void lp::permutation_matrix >::transpose_from_left(unsigned int, unsigned int); -template void lp::permutation_matrix >::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix >::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix >::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix >::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix< lp::mpq, lp::mpq>::apply_reverse_from_left_to_X(vector &); -template void lp::permutation_matrix< lp::mpq, lp::numeric_pair< lp::mpq> >::apply_reverse_from_left_to_X(vector> &); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); -template void lp::permutation_matrix >::apply_reverse_from_right_to_T(lp::indexed_vector&); diff --git a/src/math/lp/permutation_matrix.h b/src/math/lp/permutation_matrix.h index 35a58906ade..a3fff4f7fa5 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -25,20 +25,16 @@ Revision History: #include "math/lp/indexed_vector.h" #include "math/lp/lp_settings.h" #include "math/lp/matrix.h" -#include "math/lp/tail_matrix.h" namespace lp { -#ifdef Z3DEBUG - inline bool is_even(int k) { return (k/2)*2 == k; } -#endif - template -class permutation_matrix : public tail_matrix { +template +class permutation_matrix +#ifdef Z3DEBUG + : public matrix +#endif +{ vector m_permutation; vector m_rev; - vector m_work_array; - vector m_T_buffer; - vector m_X_buffer; - class ref { permutation_matrix & m_p; @@ -63,42 +59,15 @@ class permutation_matrix : public tail_matrix { // create a unit permutation of the given length void init(unsigned length); unsigned get_rev(unsigned i) { return m_rev[i]; } - bool is_dense() const override { return false; } -#ifdef Z3DEBUG - permutation_matrix get_inverse() const { - return permutation_matrix(size(), m_rev); - } + +#ifdef Z3DEBUG void print(std::ostream & out) const; #endif ref operator[](unsigned i) { return ref(*this, i); } unsigned operator[](unsigned i) const { return m_permutation[i]; } - - void apply_from_left(vector & w, lp_settings &) override; - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; - - void apply_from_right(vector & w) override; - - void apply_from_right(indexed_vector & w) override; - template - void copy_aside(vector & t, vector & tmp_index, indexed_vector & w); - - template - void clear_data(indexed_vector & w); - - template - void apply_reverse_from_left(indexed_vector & w); - - void apply_reverse_from_left_to_T(vector & w); - void apply_reverse_from_left_to_X(vector & w); - - void apply_reverse_from_right_to_T(vector & w); - void apply_reverse_from_right_to_T(indexed_vector & w); - void apply_reverse_from_right_to_X(vector & w); - void set_val(unsigned i, unsigned pi) { lp_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } @@ -116,18 +85,6 @@ class permutation_matrix : public tail_matrix { void set_number_of_rows(unsigned /*m*/) override { } void set_number_of_columns(unsigned /*n*/) override { } #endif - void multiply_by_permutation_from_left(permutation_matrix & p); - - // this is multiplication in the matrix sense - void multiply_by_permutation_from_right(permutation_matrix & p); - - void multiply_by_reverse_from_right(permutation_matrix & q); - - void multiply_by_permutation_reverse_from_left(permutation_matrix & r); - - void shrink_by_one_identity(); - - bool is_identity() const; unsigned size() const { return static_cast(m_rev.size()); } @@ -135,8 +92,6 @@ class permutation_matrix : public tail_matrix { unsigned old_size = m_permutation.size(); m_permutation.resize(size); m_rev.resize(size); - m_T_buffer.resize(size); - m_X_buffer.resize(size); for (unsigned i = old_size; i < size; i++) { m_permutation[i] = m_rev[i] = i; } diff --git a/src/math/lp/permutation_matrix_def.h b/src/math/lp/permutation_matrix_def.h index b6f9924ff3b..c86fef4f422 100644 --- a/src/math/lp/permutation_matrix_def.h +++ b/src/math/lp/permutation_matrix_def.h @@ -22,13 +22,13 @@ Revision History: #include "util/vector.h" #include "math/lp/permutation_matrix.h" namespace lp { -template permutation_matrix::permutation_matrix(unsigned length): m_permutation(length), m_rev(length), m_T_buffer(length), m_X_buffer(length) { +template permutation_matrix::permutation_matrix(unsigned length): m_permutation(length), m_rev(length) { for (unsigned i = 0; i < length; i++) { // do not change the direction of the loop because of the vectorization bug in clang3.3 m_permutation[i] = m_rev[i] = i; } } -template permutation_matrix::permutation_matrix(unsigned length, vector const & values): m_permutation(length), m_rev(length) , m_T_buffer(length), m_X_buffer(length) { +template permutation_matrix::permutation_matrix(unsigned length, vector const & values): m_permutation(length), m_rev(length) { for (unsigned i = 0; i < length; i++) { set_val(i, values[i]); } @@ -37,8 +37,6 @@ template permutation_matrix::permutation_matrix(u template void permutation_matrix::init(unsigned length) { m_permutation.resize(length); m_rev.resize(length); - m_T_buffer.resize(length); - m_X_buffer.resize(length); for (unsigned i = 0; i < length; i++) { m_permutation[i] = m_rev[i] = i; } @@ -59,211 +57,6 @@ template void permutation_matrix::print(std::ostr } #endif -template -void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // L * deb_w = clone_vector(w, row_count()); - // deb.apply_from_left(deb_w); -#endif - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif -} - -template -void permutation_matrix::apply_from_left_to_T(indexed_vector & w, lp_settings & ) { - vector t(w.m_index.size()); - vector tmp_index(w.m_index.size()); - copy_aside(t, tmp_index, w); // todo: is it too much copying - clear_data(w); - // set the new values - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = m_rev[tmp_index[i]]; - w[j] = t[i]; - w.m_index[i] = j; - } -} - -template void permutation_matrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(w, row_count()); - // deb.apply_from_right(deb_w); -#endif - lp_assert(m_T_buffer.size() == w.size()); - for (unsigned i = 0; i < size(); i++) { - m_T_buffer[i] = w[m_rev[i]]; - } - - for (unsigned i = 0; i < size(); i++) { - w[i] = m_T_buffer[i]; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif -} - -template void permutation_matrix::apply_from_right(indexed_vector & w) { -#ifdef Z3DEBUG - vector wcopy(w.m_data); - apply_from_right(wcopy); -#endif - vector buffer(w.m_index.size()); - vector index_copy(w.m_index); - for (unsigned i = 0; i < w.m_index.size(); i++) { - buffer[i] = w.m_data[w.m_index[i]]; - } - w.clear(); - - for (unsigned i = 0; i < index_copy.size(); i++) { - unsigned j = index_copy[i]; - unsigned pj = m_permutation[j]; - w.set_value(buffer[i], pj); - } - -#ifdef Z3DEBUG - lp_assert(vectors_are_equal(wcopy, w.m_data)); -#endif -} - - -template template -void permutation_matrix::copy_aside(vector & t, vector & tmp_index, indexed_vector & w) { - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = w.m_index[i]; - t[i] = w[j]; // copy aside all non-zeroes - tmp_index[i] = j; // and the indices too - } -} - -template template -void permutation_matrix::clear_data(indexed_vector & w) { - // clear old non-zeroes - for (unsigned i = static_cast(w.m_index.size()); i > 0;) { - i--; - unsigned j = w.m_index[i]; - w[j] = zero_of_type(); - } -} - -template template -void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { - // the result will be w = p(-1) * w -#ifdef Z3DEBUG - // dense_matrix deb(get_reverse()); - // L * deb_w = clone_vector(w.m_data, row_count()); - // deb.apply_from_left(deb_w); -#endif - vector t(w.m_index.size()); - vector tmp_index(w.m_index.size()); - - copy_aside(t, tmp_index, w); - clear_data(w); - - // set the new values - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = m_permutation[tmp_index[i]]; - w[j] = t[i]; - w.m_index[i] = j; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w.m_data, row_count())); - // delete [] deb_w; -#endif -} - -template -void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { - // the result will be w = p(-1) * w - lp_assert(m_T_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_T_buffer[m_permutation[i]] = w[i]; - } - i = size(); - while (i-- > 0) { - w[i] = m_T_buffer[i]; - } -} -template -void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { - // the result will be w = p(-1) * w - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[m_permutation[i]] = w[i]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -} - -template -void permutation_matrix::apply_reverse_from_right_to_T(vector & w) { - // the result will be w = w * p(-1) - lp_assert(m_T_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_T_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_T_buffer[i]; - } -} - -template -void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w) { - // the result will be w = w * p(-1) -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_reverse_from_right_to_T(wcopy); -#endif - vector tmp; - vector tmp_index(w.m_index); - for (auto i : w.m_index) { - tmp.push_back(w[i]); - } - w.clear(); - - for (unsigned k = 0; k < tmp_index.size(); k++) { - unsigned j = tmp_index[k]; - w.set_value(tmp[k], m_rev[j]); - } - - -} - - -template -void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { - // the result will be w = w * p(-1) - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -} template void permutation_matrix::transpose_from_left(unsigned i, unsigned j) { // the result will be this = (i,j)*this @@ -283,55 +76,5 @@ template void permutation_matrix::transpose_from_ set_val(j, pi); } -template void permutation_matrix::multiply_by_permutation_from_left(permutation_matrix & p) { - m_work_array = m_permutation; - lp_assert(p.size() == size()); - unsigned i = size(); - while (i-- > 0) { - set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - } -} - -// this is multiplication in the matrix sense -template void permutation_matrix::multiply_by_permutation_from_right(permutation_matrix & p) { - m_work_array = m_permutation; - lp_assert(p.size() == size()); - unsigned i = size(); - while (i-- > 0) - set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - -} - -template void permutation_matrix::multiply_by_reverse_from_right(permutation_matrix & q){ // todo : condensed permutations ? - lp_assert(q.size() == size()); - m_work_array = m_permutation; - // the result is this = this*q(-1) - unsigned i = size(); - while (i-- > 0) { - set_val(i, q.m_rev[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - } -} - -template void permutation_matrix::multiply_by_permutation_reverse_from_left(permutation_matrix & r){ // todo : condensed permutations? - // the result is this = r(-1)*this - m_work_array = m_permutation; - // the result is this = this*q(-1) - unsigned i = size(); - while (i-- > 0) { - set_val(i, m_work_array[r.m_rev[i]]); - } -} - - -template bool permutation_matrix::is_identity() const { - unsigned i = size(); - while (i-- > 0) { - if (m_permutation[i] != i) { - return false; - } - } - return true; -} - } diff --git a/src/math/lp/tail_matrix.h b/src/math/lp/tail_matrix.h deleted file mode 100644 index 9fa1a4a4782..00000000000 --- a/src/math/lp/tail_matrix.h +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/indexed_vector.h" -#include "math/lp/matrix.h" -#include "math/lp/lp_settings.h" -// These matrices appear at the end of the list - -namespace lp { -template -class tail_matrix -#ifdef Z3DEBUG - : public matrix -#endif -{ -public: - virtual void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) = 0; - virtual void apply_from_left(vector & w, lp_settings & settings) = 0; - virtual void apply_from_right(vector & w) = 0; - virtual void apply_from_right(indexed_vector & w) = 0; - virtual ~tail_matrix() = default; - virtual bool is_dense() const = 0; - struct ref_row { - const tail_matrix & m_A; - unsigned m_row; - ref_row(const tail_matrix& m, unsigned row): m_A(m), m_row(row) {} - T operator[](unsigned j) const { return m_A.get_elem(m_row, j);} - }; - ref_row operator[](unsigned i) const { return ref_row(*this, i);} -}; -} diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 24aa8767a37..446ead41526 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -34,13 +34,11 @@ #include #include "math/lp/lp_utils.h" #include "test/lp/smt_reader.h" -#include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" #include "test/lp/test_file_reader.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/numeric_pair.h" -#include "math/lp/binary_heap_upair_queue.h" #include "util/stacked_value.h" #include "math/lp/u_set.h" #include "util/stopwatch.h" @@ -458,72 +456,7 @@ void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, uns -void test_upair_queue() { - int n = 10; - binary_heap_upair_queue q(2); - std::unordered_map m; - for (int k = 0; k < 100; k++) { - int i = my_random()%n; - int j = my_random()%n; - q.enqueue(i, j, my_random()%n); - } - - q.remove(5, 5); - - while (!q.is_empty()) { - unsigned i, j; - q.dequeue(i, j); - } -} -void test_binary_priority_queue() { - std::cout << "testing binary_heap_priority_queue..."; - auto q = binary_heap_priority_queue(10); - q.enqueue(2, 2); - q.enqueue(1, 1); - q.enqueue(9, 9); - q.enqueue(8, 8); - q.enqueue(5, 25); - q.enqueue(3, 3); - q.enqueue(4, 4); - q.enqueue(7, 30); - q.enqueue(6, 6); - q.enqueue(0, 0); - q.enqueue(5, 5); - q.enqueue(7, 7); - - for (unsigned i = 0; i < 10; i++) { - unsigned de = q.dequeue(); - lp_assert(i == de); - std::cout << de << std::endl; - } - q.enqueue(2, 2); - q.enqueue(1, 1); - q.enqueue(9, 9); - q.enqueue(8, 8); - q.enqueue(5, 5); - q.enqueue(3, 3); - q.enqueue(4, 4); - q.enqueue(7, 2); - q.enqueue(0, 1); - q.enqueue(6, 6); - q.enqueue(7, 7); - q.enqueue(33, 1000); - q.enqueue(20, 0); - q.dequeue(); - q.remove(33); - q.enqueue(0, 0); -#ifdef Z3DEBUG - unsigned t = 0; - while (q.size() > 0) { - unsigned d =q.dequeue(); - lp_assert(t++ == d); - std::cout << d << std::endl; - } -#endif - test_upair_queue(); - std::cout << " done" << std::endl; -} int get_random_rows() { From 1fb24ebc3583735c764ccaa38093536d8fa2a8df Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 17:54:34 -0800 Subject: [PATCH 506/597] fix lp_tst --- src/test/lp/lp.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 446ead41526..8ad73f0c003 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -693,7 +693,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); parser.add_option_with_help_string("--lar", "test lar_solver"); parser.add_option_with_after_string_with_help("--maxng", "max iterations without progress"); - parser.add_option_with_help_string("-tbq", "test binary queue"); parser.add_option_with_help_string("--randomize_lar", "test randomize functionality"); parser.add_option_with_help_string("--smap", "test stacked_map"); parser.add_option_with_help_string("--term", "simple term test"); @@ -1962,13 +1961,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - - if (args_parser.option_is_used("-tbq")) { - test_binary_priority_queue(); - ret = 0; - return finalize(ret); - } - return finalize(0); // has_violations() ? 1 : 0); } } From 3efe91c3e323cc3e0ce2d48fd04ed57972b84c29 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 8 Mar 2023 08:22:25 -0800 Subject: [PATCH 507/597] more dead code --- src/math/lp/lar_core_solver.h | 1 - src/math/lp/lar_core_solver_def.h | 1 - src/math/lp/lar_solution_signature.h | 28 ----------------- src/math/lp/lar_solver.cpp | 45 --------------------------- src/math/lp/lar_solver.h | 12 ++----- src/math/lp/lp_core_solver_base.cpp | 4 --- src/math/lp/lp_core_solver_base.h | 5 --- src/math/lp/lp_core_solver_base_def.h | 44 -------------------------- src/math/lp/lp_settings.h | 2 -- 9 files changed, 2 insertions(+), 140 deletions(-) delete mode 100644 src/math/lp/lar_solution_signature.h diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 59929e34f52..4aa62f0df09 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -14,7 +14,6 @@ Copyright (c) 2017 Microsoft Corporation #include "math/lp/indexed_vector.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" -#include "math/lp/lar_solution_signature.h" #include "util/stacked_value.h" namespace lp { diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 2e205291e6f..e22d77c1b30 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -14,7 +14,6 @@ Revision History: #include #include "util/vector.h" #include "math/lp/lar_core_solver.h" -#include "math/lp/lar_solution_signature.h" namespace lp { lar_core_solver::lar_core_solver( lp_settings & settings, diff --git a/src/math/lp/lar_solution_signature.h b/src/math/lp/lar_solution_signature.h deleted file mode 100644 index 5b8bfae48aa..00000000000 --- a/src/math/lp/lar_solution_signature.h +++ /dev/null @@ -1,28 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lp_settings.h" -#include -namespace lp { -typedef std::unordered_map lar_solution_signature; -} diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 952f2ad43e0..1c9f5461beb 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -221,9 +221,6 @@ namespace lp { evidence.add_pair(ul.lower_bound_witness(), -numeric_traits::one()); } - - unsigned lar_solver::get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } - void lar_solver::push() { m_simplex_strategy = m_settings.simplex_strategy(); m_simplex_strategy.push(); @@ -761,20 +758,6 @@ namespace lp { return r; } - bool lar_solver::x_is_correct() const { - if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { - return false; - } - for (unsigned i = 0; i < A_r().row_count(); i++) { - numeric_pair delta = A_r().dot_product_with_row(i, m_mpq_lar_core_solver.m_r_x); - if (!delta.is_zero()) { - return false; - } - } - return true;; - - } - bool lar_solver::var_is_registered(var_index vj) const { if (tv::is_term(vj)) { return tv::unmask_term(vj) < m_terms.size(); @@ -824,28 +807,6 @@ namespace lp { return false; // it is unreachable } - bool lar_solver::the_relations_are_of_same_type(const vector>& evidence, lconstraint_kind& the_kind_of_sum) const { - unsigned n_of_G = 0, n_of_L = 0; - bool strict = false; - for (auto& it : evidence) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - lconstraint_kind kind = coeff.is_pos() ? - m_constraints[con_ind].kind() : - flip_kind(m_constraints[con_ind].kind()); - if (kind == GT || kind == LT) - strict = true; - if (kind == GE || kind == GT) - n_of_G++; - else if (kind == LE || kind == LT) - n_of_L++; - } - the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); - if (strict) - the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); - - return n_of_G == 0 || n_of_L == 0; - } void lar_solver::register_in_map(std::unordered_map& coeffs, const lar_base_constraint& cn, const mpq& a) { for (auto& it : cn.coeffs()) { @@ -1227,12 +1188,6 @@ namespace lp { insert_row_with_changed_bounds(r.var()); } - - - void lar_solver::pivot_fixed_vars_from_basis() { - m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); - } - void lar_solver::pop() { pop(1); } diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 5c77c679a47..955710b3c52 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -212,11 +212,9 @@ class lar_solver : public column_namer { void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); - bool x_is_correct() const; bool all_constrained_variables_are_registered(const vector>& left_side); bool all_constraints_hold() const; bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; - bool the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const; static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); bool the_left_sides_sum_to_zero(const vector> & evidence) const; @@ -229,7 +227,6 @@ class lar_solver : public column_namer { int inf_sign) const; mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const; void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); - void pivot_fixed_vars_from_basis(); bool column_represents_row_in_tableau(unsigned j); void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j); void remove_last_row_and_column_from_tableau(unsigned j); @@ -264,10 +261,7 @@ class lar_solver : public column_namer { return m_fixed_var_table_int; } - map, default_eq>& fixed_var_table_int() { - return m_fixed_var_table_int; - } - + const map, default_eq>& fixed_var_table_real() const { return m_fixed_var_table_real; } @@ -293,9 +287,7 @@ class lar_solver : public column_namer { inline void set_column_value_test(unsigned j, const impq& v) { set_column_value(j, v); } - - unsigned get_total_iterations() const; - + var_index add_named_var(unsigned ext_j, bool is_integer, const std::string&); lp_status maximize_term(unsigned j_or_term, impq &term_max); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index ccd6bf41927..61eae344f6c 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,9 +23,6 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; -template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; - template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; @@ -61,7 +58,6 @@ template std::string lp::lp_core_solver_base template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; -template void lp::lp_core_solver_base >::pivot_fixed_vars_from_basis(); template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 9fb4d4333cd..2e751330b69 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -383,11 +383,6 @@ class lp_core_solver_base { } - - - non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - - void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 2d729656724..a1255ade3e2 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -423,29 +423,6 @@ column_name(unsigned column) const { return m_column_names.get_variable_name(column); } -template non_basic_column_value_position lp_core_solver_base:: -get_non_basic_column_value_position(unsigned j) const { - switch (m_column_types[j]) { - case column_type::fixed: - return x_is_at_lower_bound(j)? at_fixed : not_at_bound; - case column_type::free_column: - return free_of_bounds; - case column_type::boxed: - return x_is_at_lower_bound(j)? at_lower_bound :( - x_is_at_upper_bound(j)? at_upper_bound: - not_at_bound - ); - case column_type::lower_bound: - return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound; - case column_type::upper_bound: - return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound; - default: - lp_unreachable(); - } - lp_unreachable(); - return at_lower_bound; -} - template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); @@ -463,27 +440,6 @@ template bool lp_core_solver_base::pivot_column_g return true; } -template void lp_core_solver_base::pivot_fixed_vars_from_basis() { - // run over basis and non-basis at the same time - indexed_vector w(m_basis.size()); // the buffer - unsigned i = 0; // points to basis - for (; i < m_basis.size(); i++) { - unsigned basic_j = m_basis[i]; - - if (get_column_type(basic_j) != column_type::fixed) continue; - T a; - unsigned j; - for (auto &c : m_A.m_rows[i]) { - j = c.var(); - if (j == basic_j) - continue; - if (get_column_type(j) != column_type::fixed) { - if (pivot_column_general(j, basic_j, w)) - break; - } - } - } -} template bool lp_core_solver_base::remove_from_basis(unsigned basic_j) { indexed_vector w(m_basis.size()); // the buffer diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 314930fc175..9a38fd582cb 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -90,8 +90,6 @@ inline std::ostream& operator<<(std::ostream& out, lp_status status) { lp_status lp_status_from_string(std::string status); -enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; - class lp_resource_limit { public: From 8b0aa22631bb49dac2fd550e783e32aa50ddb310 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 8 Mar 2023 09:27:09 -0800 Subject: [PATCH 508/597] replace lp_assert(false) with UNREACHABLE --- src/math/lp/core_solver_pretty_printer_def.h | 2 +- src/math/lp/general_matrix.h | 22 +- src/math/lp/lar_constraints.h | 2 +- src/math/lp/lar_core_solver.h | 4 +- src/math/lp/lar_solver.cpp | 16 +- src/math/lp/lar_term.h | 2 +- src/math/lp/lia_move.h | 2 +- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 81 +- src/math/lp/lp_core_solver_base_def.h | 40 +- src/math/lp/lp_primal_core_solver.h | 1272 ++++++++--------- src/math/lp/lp_primal_core_solver_def.h | 76 +- .../lp/lp_primal_core_solver_tableau_def.h | 4 +- src/math/lp/lp_settings_def.h | 6 +- src/math/lp/lp_utils.h | 1 - src/math/lp/nra_solver.cpp | 2 +- src/math/lp/numeric_pair.h | 12 +- src/math/lp/static_matrix.cpp | 2 - src/math/lp/static_matrix.h | 4 +- src/math/lp/static_matrix_def.h | 8 - src/test/lp/gomory_test.h | 2 +- src/test/lp/lp.cpp | 26 +- src/test/lp/smt_reader.h | 2 +- 23 files changed, 677 insertions(+), 914 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 931333ee725..27aa6c75d72 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -139,7 +139,7 @@ template void core_solver_pretty_printer::adjust_ case column_type::free_column: break; default: - lp_assert(false); + UNREACHABLE(); break; } } diff --git a/src/math/lp/general_matrix.h b/src/math/lp/general_matrix.h index 749fa30dc43..a4f6693a211 100644 --- a/src/math/lp/general_matrix.h +++ b/src/math/lp/general_matrix.h @@ -114,9 +114,6 @@ class general_matrix { } } - void copy_column_to_indexed_vector(unsigned entering, indexed_vector &w ) const { - lp_assert(false); // not implemented - } general_matrix operator*(const general_matrix & m) const { lp_assert(m.row_count() == column_count()); general_matrix ret(row_count(), m.column_count()); @@ -172,24 +169,7 @@ class general_matrix { return r; } - // bool create_upper_triangle(general_matrix& m, vector& x) { - // for (unsigned i = 1; i < m.row_count(); i++) { - // lp_assert(false); // to be continued - // } - // } - - // bool solve_A_x_equal_b(const general_matrix& m, vector& x, const vector& b) const { - // auto m_copy = m; - // // for square matrices - // lp_assert(row_count() == b.size()); - // lp_assert(x.size() == column_count()); - // lp_assert(row_count() == column_count()); - // x = b; - // create_upper_triangle(copy_of_m, x); - // solve_on_triangle(copy_of_m, x); - // } - // - + void transpose_rows(unsigned i, unsigned l) { lp_assert(i != l); m_row_permutation.transpose_from_right(i, l); diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index 8e6311683b5..f8cffbe5793 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -44,7 +44,7 @@ inline std::string lconstraint_kind_string(lconstraint_kind t) { case EQ: return std::string("="); case NE: return std::string("!="); } - lp_unreachable(); + UNREACHABLE(); return std::string(); // it is unreachable } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 4aa62f0df09..06ef4d50ba3 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -159,7 +159,7 @@ class lar_core_solver { case column_type::fixed: return true; default: - lp_assert(false); + UNREACHABLE(); } return false; } @@ -174,7 +174,7 @@ class lar_core_solver { case column_type::fixed: return true; default: - lp_assert(false); + UNREACHABLE(); } return false; } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 1c9f5461beb..9fab5e4371d 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -466,7 +466,7 @@ namespace lp { default: - lp_unreachable(); // wrong mode + UNREACHABLE(); // wrong mode } return false; } @@ -802,7 +802,7 @@ namespace lp { case GT: return left_side_val > constr.rhs(); case EQ: return left_side_val == constr.rhs(); default: - lp_unreachable(); + UNREACHABLE(); } return false; // it is unreachable } @@ -857,7 +857,7 @@ namespace lp { case EQ: lp_assert(rs != zero_of_type()); break; default: - lp_assert(false); + UNREACHABLE(); return false; } #endif @@ -1816,7 +1816,7 @@ namespace lp { adjust_initial_state_for_tableau_rows(); break; case simplex_strategy_enum::tableau_costs: - lp_assert(false); // not implemented + UNREACHABLE(); // not implemented case simplex_strategy_enum::undecided: adjust_initial_state_for_tableau_rows(); break; @@ -1905,7 +1905,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } if (m_mpq_lar_core_solver.m_r_upper_bounds[j] == m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; @@ -1959,7 +1959,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } @@ -2009,7 +2009,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } void lar_solver::update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { @@ -2050,7 +2050,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 3ef424e243e..fc73f949f1f 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -179,7 +179,7 @@ class lar_term { return p.coeff().is_one(); } } - lp_unreachable(); + UNREACHABLE(); return false; } diff --git a/src/math/lp/lia_move.h b/src/math/lp/lia_move.h index 65da5826e7b..ca61d7b7aba 100644 --- a/src/math/lp/lia_move.h +++ b/src/math/lp/lia_move.h @@ -45,7 +45,7 @@ inline std::string lia_move_to_string(lia_move m) { case lia_move::unsat: return "unsat"; default: - lp_assert(false); + UNREACHABLE(); }; return "strange"; } diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 61eae344f6c..9a4c375f638 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -27,7 +27,6 @@ template bool lp::lp_core_solver_base >::prin template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); @@ -61,7 +60,6 @@ template bool lp::lp_core_solver_base >::calc template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; -template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); @@ -70,6 +68,5 @@ template bool lp::lp_core_solver_base::inf_set_is_correct() co template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); -template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int, lp::numeric_pair const&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 2e751330b69..fc167324234 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -173,8 +173,6 @@ class lp_core_solver_base { void set_total_iterations(unsigned s) { m_total_iterations = s; } - void set_non_basic_x_to_correct_bounds(); - bool at_bound(const X &x, const X & bound) const { return !below_bound(x, bound) && !above_bound(x, bound); } @@ -300,45 +298,6 @@ class lp_core_solver_base { std::string column_name(unsigned column) const; - bool snap_non_basic_x_to_bound() { - bool ret = false; - for (unsigned j : non_basis()) - ret = snap_column_to_bound(j) || ret; - return ret; - } - - bool snap_column_to_bound(unsigned j) { - switch (m_column_types[j]) { - case column_type::fixed: - if (x_is_at_bound(j)) - break; - m_x[j] = m_lower_bounds[j]; - return true; - case column_type::boxed: - if (x_is_at_bound(j)) - break; // we should preserve x if possible - // snap randomly - if (m_settings.random_next() % 2 == 1) - m_x[j] = m_lower_bounds[j]; - else - m_x[j] = m_upper_bounds[j]; - return true; - case column_type::lower_bound: - if (x_is_at_lower_bound(j)) - break; - m_x[j] = m_lower_bounds[j]; - return true; - case column_type::upper_bound: - if (x_is_at_upper_bound(j)) - break; - m_x[j] = m_upper_bounds[j]; - return true; - default: - break; - } - return false; - } - bool make_column_feasible(unsigned j, numeric_pair & delta) { bool ret = false; lp_assert(m_basis_heading[j] < 0); @@ -384,7 +343,6 @@ class lp_core_solver_base { } bool remove_from_basis(unsigned j); - bool remove_from_basis(unsigned j, const impq&); bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); void init_basic_part_of_basis_heading() { unsigned m = m_basis.size(); @@ -456,31 +414,6 @@ class lp_core_solver_base { change_basis_unconditionally(leaving, entering); } - bool non_basic_column_is_set_correctly(unsigned j) const { - if (j >= this->m_n()) - return false; - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (!this->x_is_at_bound(j)) - return false; - break; - case column_type::lower_bound: - if (!this->x_is_at_lower_bound(j)) - return false; - break; - case column_type::upper_bound: - if (!this->x_is_at_upper_bound(j)) - return false; - break; - case column_type::free_column: - break; - default: - lp_assert(false); - break; - } - return true; - } bool non_basic_columns_are_set_correctly() const { for (unsigned j : this->m_nbasis) if (!column_is_feasible(j)) { @@ -540,13 +473,11 @@ class lp_core_solver_base { out << "[-oo, oo]"; break; default: - lp_assert(false); + UNREACHABLE(); } return out << "\n"; } - bool column_is_free(unsigned j) const { return this->m_column_types[j] == column_type::free_column; } - bool column_is_fixed(unsigned j) const { return this->m_column_types[j] == column_type::fixed; } @@ -579,16 +510,6 @@ class lp_core_solver_base { } } - // only check for basic columns - bool calc_current_x_is_feasible() const { - unsigned i = this->m_m(); - while (i--) { - if (!column_is_feasible(m_basis[i])) - return false; - } - return true; - } - void transpose_rows_tableau(unsigned i, unsigned ii); void pivot_to_reduced_costs_tableau(unsigned i, unsigned j); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index a1255ade3e2..cb0084a168a 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -166,26 +166,6 @@ print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream return time_is_over(); } -template void lp_core_solver_base:: -set_non_basic_x_to_correct_bounds() { - for (unsigned j : non_basis()) { - switch (m_column_types[j]) { - case column_type::boxed: - m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_lower_bounds[j]; - break; - case column_type::lower_bound: - m_x[j] = m_lower_bounds[j]; - lp_assert(column_is_dual_feasible(j)); - break; - case column_type::upper_bound: - m_x[j] = m_upper_bounds[j]; - lp_assert(column_is_dual_feasible(j)); - break; - default: - break; - } - } -} template bool lp_core_solver_base:: column_is_dual_feasible(unsigned j) const { switch (m_column_types[j]) { @@ -201,9 +181,9 @@ column_is_dual_feasible(unsigned j) const { case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: - lp_unreachable(); + UNREACHABLE(); } - lp_unreachable(); + UNREACHABLE(); return false; } template bool lp_core_solver_base:: @@ -257,7 +237,7 @@ template bool lp_core_solver_base::column_is_feas return true; break; default: - lp_unreachable(); + UNREACHABLE(); } return false; // it is unreachable } @@ -453,18 +433,6 @@ template bool lp_core_solver_base::remove_from_ba return false; } -template bool lp_core_solver_base::remove_from_basis(unsigned basic_j, const impq& val) { - indexed_vector w(m_basis.size()); // the buffer - unsigned i = m_basis_heading[basic_j]; - for (auto &c : m_A.m_rows[i]) { - if (c.var() == basic_j) - continue; - if (pivot_column_general(c.var(), basic_j, w)) - return true; - } - return false; -} - template bool lp_core_solver_base::infeasibility_costs_are_correct() const { @@ -513,7 +481,7 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) case column_type::free_column: return is_zero(this->m_costs[j]); default: - lp_assert(false); + UNREACHABLE(); return true; } } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 628777f3328..641f6be2160 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -19,704 +19,680 @@ Revision History: --*/ #pragma once -#include -#include -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" +#include "math/lp/static_matrix.h" #include "math/lp/u_set.h" +#include "util/vector.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace lp { -// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) -// The right side b is given implicitly by x and the basis +// This core solver solves (Ax=b, lower_bound_values \leq x \leq +// upper_bound_values, maximize costs*x ) The right side b is given implicitly +// by x and the basis template -class lp_primal_core_solver:public lp_core_solver_base { +class lp_primal_core_solver : public lp_core_solver_base { public: - int m_sign_of_entering_delta; - vector m_costs_backup; - unsigned m_inf_row_index_for_tableau; - bool m_bland_mode_tableau; - u_set m_left_basis_tableau; - unsigned m_bland_mode_threshold; - unsigned m_left_basis_repeated; - vector m_leaving_candidates; - - std::list m_non_basis_list; - void sort_non_basis(); - int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); - int choose_entering_column_tableau(); - int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); - - - bool needs_to_grow(unsigned bj) const { - lp_assert(!this->column_is_feasible(bj)); - switch(this->m_column_types[bj]) { - case column_type::free_column: - return false; - case column_type::fixed: - case column_type::lower_bound: - case column_type::boxed: - return this-> x_below_low_bound(bj); - default: - return false; - } - lp_assert(false); // unreachable - return false; - } - - int inf_sign_of_column(unsigned bj) const { - lp_assert(!this->column_is_feasible(bj)); - switch(this->m_column_types[bj]) { - case column_type::free_column: - return 0; - case column_type::lower_bound: - return 1; - case column_type::fixed: - case column_type::boxed: - return this->x_above_upper_bound(bj)? -1: 1; - default: - return -1; - } - lp_assert(false); // unreachable - return 0; - - } - - - bool monoid_can_decrease(const row_cell & rc) const { - unsigned j = rc.var(); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: - return true; - case column_type::fixed: - return false; - case column_type::lower_bound: - if (is_pos(rc.coeff())) { - return this->x_above_lower_bound(j); - } - - return true; - case column_type::upper_bound: - if (is_pos(rc.coeff())) { - return true; - } - - return this->x_below_upper_bound(j); - case column_type::boxed: - if (is_pos(rc.coeff())) { - return this->x_above_lower_bound(j); - } - - return this->x_below_upper_bound(j); - default: - return false; - } - lp_assert(false); // unreachable - return false; - } - - bool monoid_can_increase(const row_cell & rc) const { - unsigned j = rc.var(); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: - return true; - case column_type::fixed: - return false; - case column_type::lower_bound: - if (is_neg(rc.coeff())) { - return this->x_above_lower_bound(j); - } - - return true; - case column_type::upper_bound: - if (is_neg(rc.coeff())) { - return true; - } - - return this->x_below_upper_bound(j); - case column_type::boxed: - if (is_neg(rc.coeff())) { - return this->x_above_lower_bound(j); - } - - return this->x_below_upper_bound(j); - default: - return false; - } - lp_assert(false); // unreachable - return false; - } + int m_sign_of_entering_delta; + vector m_costs_backup; + unsigned m_inf_row_index_for_tableau; + bool m_bland_mode_tableau; + u_set m_left_basis_tableau; + unsigned m_bland_mode_threshold; + unsigned m_left_basis_repeated; + vector m_leaving_candidates; + + std::list m_non_basis_list; + void sort_non_basis(); + int choose_entering_column_tableau(); + + bool needs_to_grow(unsigned bj) const { + lp_assert(!this->column_is_feasible(bj)); + switch (this->m_column_types[bj]) { + case column_type::free_column: + return false; + case column_type::fixed: + case column_type::lower_bound: + case column_type::boxed: + return this->x_below_low_bound(bj); + default: + return false; + } + UNREACHABLE(); // unreachable + return false; + } + + int inf_sign_of_column(unsigned bj) const { + lp_assert(!this->column_is_feasible(bj)); + switch (this->m_column_types[bj]) { + case column_type::free_column: + return 0; + case column_type::lower_bound: + return 1; + case column_type::fixed: + case column_type::boxed: + return this->x_above_upper_bound(bj) ? -1 : 1; + default: + return -1; + } + UNREACHABLE(); // unreachable + return 0; + } + + bool monoid_can_decrease(const row_cell &rc) const { + unsigned j = rc.var(); + lp_assert(this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::free_column: + return true; + case column_type::fixed: + return false; + case column_type::lower_bound: + if (is_pos(rc.coeff())) { + return this->x_above_lower_bound(j); + } - unsigned get_number_of_basic_vars_that_might_become_inf(unsigned j) const { // consider looking at the signs here: todo - unsigned r = 0; - for (const auto & cc : this->m_A.m_columns[j]) { - unsigned k = this->m_basis[cc.var()]; - if (this->m_column_types[k] != column_type::free_column) - r++; - } - return r; - } + return true; + case column_type::upper_bound: + if (is_pos(rc.coeff())) { + return true; + } - - int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T & a_ent) { - int j = -1; - unsigned bj = this->m_basis[i]; - bool bj_needs_to_grow = needs_to_grow(bj); - for (const row_cell& rc : this->m_A.m_rows[i]) { - if (rc.var() == bj) - continue; - if (bj_needs_to_grow) { - if (!monoid_can_decrease(rc)) - continue; - } else { - if (!monoid_can_increase(rc)) - continue; - } - if (rc.var() < static_cast(j) ) { - j = rc.var(); - a_ent = rc.coeff(); - } - } - if (j == -1) { - m_inf_row_index_for_tableau = i; - } - - return j; - } - - int find_beneficial_column_in_row_tableau_rows(int i, T & a_ent) { - if (m_bland_mode_tableau) - return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); - // a short row produces short infeasibility explanation and benefits at least one pivot operation - int choice = -1; - int nchoices = 0; - unsigned num_of_non_free_basics = 1000000; - unsigned len = 100000000; - unsigned bj = this->m_basis[i]; - bool bj_needs_to_grow = needs_to_grow(bj); - for (unsigned k = 0; k < this->m_A.m_rows[i].size(); k++) { - const row_cell& rc = this->m_A.m_rows[i][k]; - unsigned j = rc.var(); - if (j == bj) - continue; - if (bj_needs_to_grow) { - if (!monoid_can_decrease(rc)) - continue; - } else { - if (!monoid_can_increase(rc)) - continue; - } - unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); - if (damage < num_of_non_free_basics) { - num_of_non_free_basics = damage; - len = this->m_A.m_columns[j].size(); - choice = k; - nchoices = 1; - } else if (damage == num_of_non_free_basics && - this->m_A.m_columns[j].size() <= len && (this->m_settings.random_next() % (++nchoices))) { - choice = k; - len = this->m_A.m_columns[j].size(); - } - } - + return this->x_below_upper_bound(j); + case column_type::boxed: + if (is_pos(rc.coeff())) { + return this->x_above_lower_bound(j); + } - if (choice == -1) { - m_inf_row_index_for_tableau = i; - return -1; - } - const row_cell& rc = this->m_A.m_rows[i][choice]; - a_ent = rc.coeff(); - return rc.var(); - } - - bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); - bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t_tableau(unsigned entering, X & t); - - void limit_theta(const X & lim, X & theta, bool & unlimited) { - if (unlimited) { - theta = lim; - unlimited = false; - } else { - theta = std::min(lim, theta); - } - } + return this->x_below_upper_bound(j); + default: + return false; + } + UNREACHABLE(); // unreachable + return false; + } + + bool monoid_can_increase(const row_cell &rc) const { + unsigned j = rc.var(); + lp_assert(this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::free_column: + return true; + case column_type::fixed: + return false; + case column_type::lower_bound: + if (is_neg(rc.coeff())) { + return this->x_above_lower_bound(j); + } - void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); - limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); - } + return true; + case column_type::upper_bound: + if (is_neg(rc.coeff())) { + return true; + } + return this->x_below_upper_bound(j); + case column_type::boxed: + if (is_neg(rc.coeff())) { + return this->x_above_lower_bound(j); + } - void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); - limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); + return this->x_below_upper_bound(j); + default: + return false; + } + UNREACHABLE(); // unreachable + return false; + } + + unsigned get_number_of_basic_vars_that_might_become_inf( + unsigned j) const { // consider looking at the signs here: todo + unsigned r = 0; + for (const auto &cc : this->m_A.m_columns[j]) { + unsigned k = this->m_basis[cc.var()]; + if (this->m_column_types[k] != column_type::free_column) + r++; + } + return r; + } + + int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T &a_ent) { + int j = -1; + unsigned bj = this->m_basis[i]; + bool bj_needs_to_grow = needs_to_grow(bj); + for (const row_cell &rc : this->m_A.m_rows[i]) { + if (rc.var() == bj) + continue; + if (bj_needs_to_grow) { + if (!monoid_can_decrease(rc)) + continue; + } else { + if (!monoid_can_increase(rc)) + continue; + } + if (rc.var() < static_cast(j)) { + j = rc.var(); + a_ent = rc.coeff(); + } } - - - void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); - limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); + if (j == -1) { + m_inf_row_index_for_tableau = i; + } + + return j; + } + + int find_beneficial_column_in_row_tableau_rows(int i, T &a_ent) { + if (m_bland_mode_tableau) + return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); + // a short row produces short infeasibility explanation and benefits at + // least one pivot operation + int choice = -1; + int nchoices = 0; + unsigned num_of_non_free_basics = 1000000; + unsigned len = 100000000; + unsigned bj = this->m_basis[i]; + bool bj_needs_to_grow = needs_to_grow(bj); + for (unsigned k = 0; k < this->m_A.m_rows[i].size(); k++) { + const row_cell &rc = this->m_A.m_rows[i][k]; + unsigned j = rc.var(); + if (j == bj) + continue; + if (bj_needs_to_grow) { + if (!monoid_can_decrease(rc)) + continue; + } else { + if (!monoid_can_increase(rc)) + continue; + } + unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); + if (damage < num_of_non_free_basics) { + num_of_non_free_basics = damage; + len = this->m_A.m_columns[j].size(); + choice = k; + nchoices = 1; + } else if (damage == num_of_non_free_basics && + this->m_A.m_columns[j].size() <= len && + (this->m_settings.random_next() % (++nchoices))) { + choice = k; + len = this->m_A.m_columns[j].size(); + } } - void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); - limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); - }; - - void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); - - X get_max_bound(vector & b); + if (choice == -1) { + m_inf_row_index_for_tableau = i; + return -1; + } + const row_cell &rc = this->m_A.m_rows[i][choice]; + a_ent = rc.coeff(); + return rc.var(); + } + + bool try_jump_to_another_bound_on_entering(unsigned entering, const X &theta, + X &t, bool &unlimited); + bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X &t); + int find_leaving_and_t_tableau(unsigned entering, X &t); + + void limit_theta(const X &lim, X &theta, bool &unlimited) { + if (unlimited) { + theta = lim; + unlimited = false; + } else { + theta = std::min(lim, theta); + } + } + + void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); + limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], + theta, unlimited); + } + + void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, + unlimited); + } + + void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], + theta, unlimited); + } + + void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); + limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, + unlimited); + }; + + void get_bound_on_variable_and_update_leaving_precisely( + unsigned j, vector &leavings, T m, X &t, + T &abs_of_d_of_leaving); + + X get_max_bound(vector &b); #ifdef Z3DEBUG - void check_Ax_equal_b(); - void check_the_bounds(); - void check_bound(unsigned i); - void check_correctness(); + void check_Ax_equal_b(); + void check_the_bounds(); + void check_bound(unsigned i); + void check_correctness(); #endif - // from page 183 of Istvan Maros's book - // the basis structures have not changed yet - void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); + // from page 183 of Istvan Maros's book + // the basis structures have not changed yet + void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); - // return 0 if the reduced cost at entering is close enough to the refreshed - // 1 if it is way off, and 2 if it is unprofitable - int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering); + // return 0 if the reduced cost at entering is close enough to the refreshed + // 1 if it is way off, and 2 if it is unprofitable + int refresh_reduced_cost_at_entering_and_check_that_it_is_off( + unsigned entering); - void backup_and_normalize_costs(); + void backup_and_normalize_costs(); - void advance_on_entering_and_leaving(int entering, int leaving, X & t); - void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t); - void advance_on_entering_equal_leaving(int entering, X & t); - void advance_on_entering_equal_leaving_tableau(int entering, X & t); + void advance_on_entering_and_leaving_tableau(int entering, int leaving, X &t); + void advance_on_entering_equal_leaving_tableau(int entering, X &t); - bool need_to_switch_costs() const { - if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) - return false; - // lp_assert(calc_current_x_is_feasible() == current_x_is_feasible()); - return this->current_x_is_feasible() == this->using_infeas_costs(); - } + bool need_to_switch_costs() const { + if (this->m_settings.simplex_strategy() == + simplex_strategy_enum::tableau_rows) + return false; + // lp_assert(calc_current_x_is_feasible() == + // current_x_is_feasible()); + return this->current_x_is_feasible() == this->using_infeas_costs(); + } + void advance_on_entering_tableau(int entering); - void advance_on_entering(int entering); - void advance_on_entering_tableau(int entering); - void advance_on_entering_precise(int entering); - void push_forward_offset_in_non_basis(unsigned & offset_in_nb); - - unsigned get_number_of_non_basic_column_to_try_for_enter(); - - - // returns the number of iterations - unsigned solve(); - - - - void find_feasible_solution(); - - // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} - - void one_iteration_tableau(); - - // this version assumes that the leaving already has the right value, and does not update it - void update_x_tableau_rows(unsigned entering, unsigned leaving, const X& delta) { - this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto & c : this->m_A.m_columns[entering]) { - if (leaving != this->m_basis[c.var()]) { - this->add_delta_to_x_and_track_feasibility(this->m_basis[c.var()], - delta * this->m_A.get_val(c)); - } - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned j = this->m_basis[c.var()]; - if (j != leaving) - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } - } - } - - void update_basis_and_x_tableau_rows(int entering, int leaving, X const & tt) { - lp_assert(entering != leaving); - update_x_tableau_rows(entering, leaving, tt); - this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); - this->change_basis(entering, leaving); - } + void push_forward_offset_in_non_basis(unsigned &offset_in_nb); - - void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { - update_basis_and_x_tableau_rows(entering, leaving, theta); - this->track_column_feasibility(entering); - } + unsigned get_number_of_non_basic_column_to_try_for_enter(); - int find_smallest_inf_column() { - int j = -1; - for (unsigned k : this->inf_set()) { - if (k < static_cast(j)) { - j = k; - } - } - return j; - } + // returns the number of iterations + unsigned solve(); - const X& get_val_for_leaving(unsigned j) const { - lp_assert(!this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::upper_bound: - return this->m_upper_bounds[j]; - case column_type::lower_bound: - return this->m_lower_bounds[j]; - break; - case column_type::boxed: - if (this->x_above_upper_bound(j)) - return this->m_upper_bounds[j]; - else - return this->m_lower_bounds[j]; - break; - default: - UNREACHABLE(); - return this->m_lower_bounds[j]; - } - } - - - void one_iteration_tableau_rows() { - int leaving = find_smallest_inf_column(); - if (leaving == -1) { - this->set_status(lp_status::OPTIMAL); - return; - } - - SASSERT(this->column_is_base(leaving)); - - if (!m_bland_mode_tableau) { - if (m_left_basis_tableau.contains(leaving)) { - if (++m_left_basis_repeated > m_bland_mode_threshold) { - m_bland_mode_tableau = true; - } - } else { - m_left_basis_tableau.insert(leaving); - } - } - T a_ent; - int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); - if (entering == -1) { - this->set_status(lp_status::INFEASIBLE); - return; - } - const X& new_val_for_leaving = get_val_for_leaving(leaving); - X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; - this->m_x[leaving] = new_val_for_leaving; - this->remove_column_from_inf_set(leaving); - advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); - if (this->current_x_is_feasible()) - this->set_status(lp_status::OPTIMAL); - } - - void decide_on_status_when_cannot_find_entering() { - lp_assert(!need_to_switch_costs()); - this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); - } + void find_feasible_solution(); - void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0); - limit_theta((this->m_lower_bounds[j] - this->m_x[j]) / m, theta, unlimited); - if (theta < zero_of_type()) theta = zero_of_type(); - } + // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} - bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets smaller - lp_assert(m < 0); - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - return true; - } + void one_iteration_tableau(); - bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets larger - lp_assert(m > 0); - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; + // this version assumes that the leaving already has the right value, and does + // not update it + void update_x_tableau_rows(unsigned entering, unsigned leaving, + const X &delta) { + this->add_delta_to_x(entering, delta); + if (!this->using_infeas_costs()) { + for (const auto &c : this->m_A.m_columns[entering]) { + if (leaving != this->m_basis[c.var()]) { + this->add_delta_to_x_and_track_feasibility( + this->m_basis[c.var()], -delta * this->m_A.get_val(c)); } - - return true; + } + } else { // using_infeas_costs() == true + lp_assert(this->column_is_feasible(entering)); + lp_assert(this->m_costs[entering] == zero_of_type()); + // m_d[entering] can change because of the cost change for basic columns. + for (const auto &c : this->m_A.m_columns[entering]) { + unsigned j = this->m_basis[c.var()]; + if (j != leaving) + this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); + update_inf_cost_for_column_tableau(j); + if (is_zero(this->m_costs[j])) + this->remove_column_from_inf_set(j); + else + this->insert_column_into_inf_set(j); + } } - - void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets larger - lp_assert(m > 0); - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); + } + + void update_basis_and_x_tableau_rows(int entering, int leaving, X const &tt) { + lp_assert(entering != leaving); + update_x_tableau_rows(entering, leaving, tt); + this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); + this->change_basis(entering, leaving); + } + + void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, + const X &theta) { + update_basis_and_x_tableau_rows(entering, leaving, theta); + this->track_column_feasibility(entering); + } + + int find_smallest_inf_column() { + int j = -1; + for (unsigned k : this->inf_set()) { + if (k < static_cast(j)) { + j = k; } - } - - void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets smaller - lp_assert(m < 0); - if (this->above_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); + return j; + } + + const X &get_val_for_leaving(unsigned j) const { + lp_assert(!this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::fixed: + case column_type::upper_bound: + return this->m_upper_bounds[j]; + case column_type::lower_bound: + return this->m_lower_bounds[j]; + break; + case column_type::boxed: + if (this->x_above_upper_bound(j)) + return this->m_upper_bounds[j]; + else + return this->m_lower_bounds[j]; + break; + default: + UNREACHABLE(); + return this->m_lower_bounds[j]; + } + } + + void one_iteration_tableau_rows() { + int leaving = find_smallest_inf_column(); + if (leaving == -1) { + this->set_status(lp_status::OPTIMAL); + return; + } + + SASSERT(this->column_is_base(leaving)); + + if (!m_bland_mode_tableau) { + if (m_left_basis_tableau.contains(leaving)) { + if (++m_left_basis_repeated > m_bland_mode_threshold) { + m_bland_mode_tableau = true; } + } else { + m_left_basis_tableau.insert(leaving); + } } - - void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - const X & x = this->m_x[j]; - const X & lbound = this->m_lower_bounds[j]; - - if (this->below_bound(x, lbound)) { - limit_theta((lbound - x) / m, theta, unlimited); + T a_ent; + int entering = find_beneficial_column_in_row_tableau_rows( + this->m_basis_heading[leaving], a_ent); + if (entering == -1) { + this->set_status(lp_status::INFEASIBLE); + return; + } + const X &new_val_for_leaving = get_val_for_leaving(leaving); + X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; + this->m_x[leaving] = new_val_for_leaving; + this->remove_column_from_inf_set(leaving); + advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta); + if (this->current_x_is_feasible()) + this->set_status(lp_status::OPTIMAL); + } + + void decide_on_status_when_cannot_find_entering() { + lp_assert(!need_to_switch_costs()); + this->set_status(this->current_x_is_feasible() ? lp_status::OPTIMAL + : lp_status::INFEASIBLE); + } + + void limit_theta_on_basis_column_for_feas_case_m_neg_no_check( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0); + limit_theta((this->m_lower_bounds[j] - this->m_x[j]) / m, theta, unlimited); + if (theta < zero_of_type()) + theta = zero_of_type(); + } + + bool limit_inf_on_bound_m_neg(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets smaller + lp_assert(m < 0); + if (this->below_bound(x, bound)) + return false; + if (this->above_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + return true; + } + + bool limit_inf_on_bound_m_pos(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets larger + lp_assert(m > 0); + if (this->above_bound(x, bound)) + return false; + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + + return true; + } + + void limit_inf_on_lower_bound_m_pos(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets larger + lp_assert(m > 0); + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } + } + + void limit_inf_on_upper_bound_m_neg(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets smaller + lp_assert(m < 0); + if (this->above_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } + } + + void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, + const T &m, + X &theta, + bool &unlimited) { + const X &x = this->m_x[j]; + const X &lbound = this->m_lower_bounds[j]; + + if (this->below_bound(x, lbound)) { + limit_theta((lbound - x) / m, theta, unlimited); + } else { + const X &ubound = this->m_upper_bounds[j]; + if (this->below_bound(x, ubound)) { + limit_theta((ubound - x) / m, theta, unlimited); + } else if (!this->above_bound(x, ubound)) { + theta = zero_of_type(); + unlimited = false; + } + } + } + + void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, + const T &m, + X &theta, + bool &unlimited) { + // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); + const X &x = this->m_x[j]; + const X &ubound = this->m_upper_bounds[j]; + if (this->above_bound(x, ubound)) { + limit_theta((ubound - x) / m, theta, unlimited); + } else { + const X &lbound = this->m_lower_bounds[j]; + if (this->above_bound(x, lbound)) { + limit_theta((lbound - x) / m, theta, unlimited); + } else if (!this->below_bound(x, lbound)) { + theta = zero_of_type(); + unlimited = false; + } + } + } + + void limit_theta_on_basis_column_for_feas_case_m_pos_no_check( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0); + limit_theta((this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); + if (theta < zero_of_type()) { + theta = zero_of_type(); + } + } + + // j is a basic column or the entering, in any case x[j] has to stay feasible. + // m is the multiplier. updating t in a way that holds the following + // x[j] + t * m >= this->m_lower_bounds[j]( if m < 0 ) + // or + // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) + void limit_theta_on_basis_column(unsigned j, T m, X &theta, bool &unlimited) { + switch (this->m_column_types[j]) { + case column_type::free_column: + break; + case column_type::upper_bound: + if (this->current_x_is_feasible()) { + if (m > 0) + limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, + unlimited); + } else { // inside of feasibility_loop + if (m > 0) + limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound( + j, m, theta, unlimited); + else + limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound( + j, m, theta, unlimited); + } + break; + case column_type::lower_bound: + if (this->current_x_is_feasible()) { + if (m < 0) + limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, + unlimited); + } else { + if (m < 0) + limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound( + j, m, theta, unlimited); + else + limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound( + j, m, theta, unlimited); + } + break; + // case fixed: + // if (get_this->current_x_is_feasible()) { + // theta = zero_of_type(); + // break; + // } + // if (m < 0) + // limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, + // theta); + // else + // limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, + // theta); + // break; + case column_type::fixed: + case column_type::boxed: + if (this->current_x_is_feasible()) { + if (m > 0) { + limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, + unlimited); } else { - const X & ubound = this->m_upper_bounds[j]; - if (this->below_bound(x, ubound)){ - limit_theta((ubound - x) / m, theta, unlimited); - } else if (!this->above_bound(x, ubound)) { - theta = zero_of_type(); - unlimited = false; - } + limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, + unlimited); } - } - - void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & ubound = this->m_upper_bounds[j]; - if (this->above_bound(x, ubound)) { - limit_theta((ubound - x) / m, theta, unlimited); + } else { + if (m > 0) { + limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, + unlimited); } else { - const X & lbound = this->m_lower_bounds[j]; - if (this->above_bound(x, lbound)){ - limit_theta((lbound - x) / m, theta, unlimited); - } else if (!this->below_bound(x, lbound)) { - theta = zero_of_type(); - unlimited = false; - } + limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, + unlimited); } + } + + break; + default: + UNREACHABLE(); } - void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0); - if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { - limit_theta((this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - unlimited = false; - } - } + if (!unlimited && theta < zero_of_type()) { + theta = zero_of_type(); } + } - void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - lp_assert(m > 0); - limit_theta( (this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - } - } + bool column_is_benefitial_for_entering_basis(unsigned j) const; + void init_infeasibility_costs(); - // j is a basic column or the entering, in any case x[j] has to stay feasible. - // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= this->m_lower_bounds[j]( if m < 0 ) - // or - // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) - void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { - switch (this->m_column_types[j]) { - case column_type::free_column: break; - case column_type::upper_bound: - if (this->current_x_is_feasible()) { - if (m > 0) - limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); - } else { // inside of feasibility_loop - if (m > 0) - limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(j, m, theta, unlimited); - else - limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited); - } - break; - case column_type::lower_bound: - if (this->current_x_is_feasible()) { - if (m < 0) - limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); - } else { - if (m < 0) - limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(j, m, theta, unlimited); - else - limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(j, m, theta, unlimited); - } - break; - // case fixed: - // if (get_this->current_x_is_feasible()) { - // theta = zero_of_type(); - // break; - // } - // if (m < 0) - // limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, theta); - // else - // limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, theta); - // break; - case column_type::fixed: - case column_type::boxed: - if (this->current_x_is_feasible()) { - if (m > 0) { - limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); - } else { - limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); - } - } else { - if (m > 0) { - limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, unlimited); - } else { - limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, unlimited); - } - } - - break; - default: - lp_unreachable(); - } - if (!unlimited && theta < zero_of_type()) { - theta = zero_of_type(); - } - } + void init_infeasibility_cost_for_column(unsigned j); + T get_infeasibility_cost_for_column(unsigned j) const; + void init_infeasibility_costs_for_changed_basis_only(); - - bool column_is_benefitial_for_entering_basis(unsigned j) const; - bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; - bool can_enter_basis(unsigned j); - void init_infeasibility_costs(); - - void init_infeasibility_cost_for_column(unsigned j); - T get_infeasibility_cost_for_column(unsigned j) const; - void init_infeasibility_costs_for_changed_basis_only(); - - void print_column(unsigned j, std::ostream & out); - - void print_bound_info_and_x(unsigned j, std::ostream & out); - - bool basis_column_is_set_correctly(unsigned j) const { - return this->m_A.m_columns[j].size() == 1; - - } - - bool basis_columns_are_set_correctly() const { - for (unsigned j : this->m_basis) - if(!basis_column_is_set_correctly(j)) - return false; - - return this->m_basis_heading.size() == this->m_A.column_count() && this->m_basis.size() == this->m_A.row_count(); - } + void print_column(unsigned j, std::ostream &out); - void init_run_tableau(); - void update_x_tableau(unsigned entering, const X & delta); - void update_inf_cost_for_column_tableau(unsigned j); - -// the delta is between the old and the new cost (old - new) - void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) { - lp_assert(this->m_basis_heading[j] >= 0); - unsigned i = static_cast(this->m_basis_heading[j]); - for (const row_cell & rc : this->m_A.m_rows[i]) { - unsigned k = rc.var(); - if (k == j) - continue; - this->m_d[k] += delta * rc.coeff(); - } - } - - bool update_basis_and_x_tableau(int entering, int leaving, X const & tt); - void init_reduced_costs_tableau(); - void init_tableau_rows() { - m_bland_mode_tableau = false; - m_left_basis_tableau.clear(); - m_left_basis_tableau.resize(this->m_A.column_count()); - m_left_basis_repeated = 0; - } -// stage1 constructor - lp_primal_core_solver(static_matrix & A, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - const vector & column_type_array, - const vector & lower_bound_values, - const vector & upper_bound_values, - lp_settings & settings, - const column_namer& column_names): - lp_core_solver_base(A, // b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - lower_bound_values, - upper_bound_values), - m_bland_mode_threshold(1000) { - this->set_status(lp_status::UNKNOWN); - } + void print_bound_info_and_x(unsigned j, std::ostream &out); - + bool basis_column_is_set_correctly(unsigned j) const { + return this->m_A.m_columns[j].size() == 1; + } - bool initial_x_is_correct() { - std::set basis_set; - for (unsigned i = 0; i < this->m_A.row_count(); i++) { - basis_set.insert(this->m_basis[i]); - } - for (unsigned j = 0; j < this->m_n(); j++) { - if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits::zero()) { - LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl); - return false; - } - - if (this->column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bounds[j]) { - LP_OUT(this->m_settings, "upper bound for " << j << " does not hold: " << this->m_upper_bounds[j] << ">" << this->m_x[j] << std::endl); - return false; - } - - if (basis_set.find(j) != basis_set.end()) continue; - if (this->m_column_types[j] == column_type::lower_bound) { - if (numeric_traits::zero() != this->m_x[j]) { - LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl); - return false; - } - } - if (this->m_column_types[j] == column_type::boxed) { - if (this->m_upper_bounds[j] != this->m_x[j] && !numeric_traits::is_zero(this->m_x[j])) { - return false; - } - } - } - return true; - } + bool basis_columns_are_set_correctly() const { + for (unsigned j : this->m_basis) + if (!basis_column_is_set_correctly(j)) + return false; + return this->m_basis_heading.size() == this->m_A.column_count() && + this->m_basis.size() == this->m_A.row_count(); + } + + void init_run_tableau(); + void update_x_tableau(unsigned entering, const X &delta); + void update_inf_cost_for_column_tableau(unsigned j); + + // the delta is between the old and the new cost (old - new) + void update_reduced_cost_for_basic_column_cost_change(const T &delta, + unsigned j) { + lp_assert(this->m_basis_heading[j] >= 0); + unsigned i = static_cast(this->m_basis_heading[j]); + for (const row_cell &rc : this->m_A.m_rows[i]) { + unsigned k = rc.var(); + if (k == j) + continue; + this->m_d[k] += delta * rc.coeff(); + } + } + + bool update_basis_and_x_tableau(int entering, int leaving, X const &tt); + void init_reduced_costs_tableau(); + void init_tableau_rows() { + m_bland_mode_tableau = false; + m_left_basis_tableau.clear(); + m_left_basis_tableau.resize(this->m_A.column_count()); + m_left_basis_repeated = 0; + } + // stage1 constructor + lp_primal_core_solver( + static_matrix &A, + vector &b, // the right side vector + vector &x, // the number of elements in x needs to be at least as large + // as the number of columns in A + vector &basis, vector &nbasis, vector &heading, + vector &costs, const vector &column_type_array, + const vector &lower_bound_values, const vector &upper_bound_values, + lp_settings &settings, const column_namer &column_names) + : lp_core_solver_base(A, // b, + basis, nbasis, heading, x, costs, settings, + column_names, column_type_array, + lower_bound_values, upper_bound_values), + m_bland_mode_threshold(1000) { + this->set_status(lp_status::UNKNOWN); + } - friend core_solver_pretty_printer; + friend core_solver_pretty_printer; }; -} +} // namespace lp diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index cc8ad88b392..4ee8305fa1a 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -53,10 +53,6 @@ void lp_primal_core_solver::sort_non_basis() { template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { - return column_is_benefitial_for_entering_basis_precise(j); -} -template -bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { @@ -88,56 +84,12 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis } break; default: - lp_unreachable(); + UNREACHABLE(); break; } return false; } -template -int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - if (number_of_benefitial_columns_to_go_over == 0) - return -1; - if (this->m_basis_sort_counter == 0) { - sort_non_basis(); - this->m_basis_sort_counter = 20; - } - else { - this->m_basis_sort_counter--; - } - unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size - std::list::iterator entering_iter = m_non_basis_list.end(); - for (auto non_basis_iter = m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { - unsigned j = *non_basis_iter; - if (!column_is_benefitial_for_entering_basis(j)) - continue; - - // if we are here then j is a candidate to enter the basis - unsigned t = this->m_columns_nz[j]; - if (t < j_nz) { - j_nz = t; - entering_iter = non_basis_iter; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; - } else if (t == j_nz && this->m_settings.random_next() % 2 == 0) { - entering_iter = non_basis_iter; - } - }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); - if (entering_iter == m_non_basis_list.end()) - return -1; - unsigned entering = *entering_iter; - m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - m_non_basis_list.erase(entering_iter); - m_non_basis_list.push_back(entering); - return entering; -} - - -template -int lp_primal_core_solver::choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); -} - template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, @@ -278,24 +230,6 @@ template void lp_primal_core_solver::backup_an -template -void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { - -} - -template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { - -} - - -template void lp_primal_core_solver::advance_on_entering_precise(int entering) { - lp_assert(false); -} - -template void lp_primal_core_solver::advance_on_entering(int entering) { - lp_assert(false); -} - template void lp_primal_core_solver::push_forward_offset_in_non_basis(unsigned & offset_in_nb) { if (++offset_in_nb == this->m_nbasis.size()) offset_in_nb = 0; @@ -377,7 +311,7 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const ret = numeric_traits::zero(); break; default: - lp_assert(false); + UNREACHABLE(); ret = numeric_traits::zero(); // does not matter break; } @@ -427,7 +361,7 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); break; default: - lp_assert(false); + UNREACHABLE(); break; } @@ -458,7 +392,7 @@ template void lp_primal_core_solver::print_column out << "( _" << this->m_x[j] << "_)" << std::endl; break; default: - lp_unreachable(); + UNREACHABLE(); } } @@ -480,7 +414,7 @@ template void lp_primal_core_solver::print_bound_ out << "inf, inf" << std::endl; break; default: - lp_assert(false); + UNREACHABLE(); break; } } diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index c7b604b9553..241164c023f 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -122,7 +122,7 @@ unsigned lp_primal_core_solver::solve() { } break; case lp_status::TENTATIVE_UNBOUNDED: - lp_assert(false); + UNREACHABLE(); break; case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { @@ -132,7 +132,7 @@ unsigned lp_primal_core_solver::solve() { break; case lp_status::UNSTABLE: - lp_assert(false); + UNREACHABLE(); break; default: diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index a439466d18c..a19558949c3 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -31,7 +31,7 @@ std::string column_type_to_string(column_type t) { case column_type::lower_bound: return "lower_bound"; case column_type::upper_bound: return "upper_bound"; case column_type::free_column: return "free_column"; - default: lp_unreachable(); + default: UNREACHABLE(); } return "unknown"; // it is unreachable } @@ -50,7 +50,7 @@ const char* lp_status_to_string(lp_status status) { case lp_status::UNSTABLE: return "UNSTABLE"; case lp_status::CANCELLED: return "CANCELLED"; default: - lp_unreachable(); + UNREACHABLE(); } return "UNKNOWN"; // it is unreachable } @@ -63,7 +63,7 @@ lp_status lp_status_from_string(std::string status) { if (status == "FEASIBLE") return lp_status::FEASIBLE; if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; - lp_unreachable(); + UNREACHABLE(); return lp_status::UNKNOWN; // it is unreachable } diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index ad5ba380d91..3c1383cb39e 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -141,7 +141,6 @@ inline void throw_exception(std::string && str) { typedef z3_exception exception; #define lp_assert(_x_) { SASSERT(_x_); } -inline void lp_unreachable() { lp_assert(false); } template inline X zero_of_type() { return numeric_traits::zero(); } template inline X one_of_type() { return numeric_traits::one(); } template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 56a84d1f0d7..1f4e0b76abb 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -171,7 +171,7 @@ struct solver::imp { lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); break; default: - lp_assert(false); // unreachable + UNREACHABLE(); // unreachable } m_nlsat->mk_clause(1, &lit, a); } diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index a64825cd8f1..25127400627 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -107,8 +107,8 @@ class numeric_traits { template struct convert_struct { static X convert(const Y & y){ return X(y);} - static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} - static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } + static bool below_bound_numeric(const X &, const X &, const Y &) { /*UNREACHABLE();*/ return false;} + static bool above_bound_numeric(const X &, const X &, const Y &) { /*UNREACHABLE();*/ return false; } }; @@ -190,7 +190,7 @@ struct numeric_pair { } numeric_pair operator/(const numeric_pair &) const { - // lp_unreachable(); + // UNREACHABLE(); } @@ -199,7 +199,7 @@ struct numeric_pair { } numeric_pair operator*(const numeric_pair & /*a*/) const { - // lp_unreachable(); + // UNREACHABLE(); } numeric_pair& operator+=(const numeric_pair & a) { @@ -275,14 +275,14 @@ numeric_pair operator/(const numeric_pair & r, const X & a) { return numeric_pair(r.x / a, r.y / a); } -template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} +template double get_double(const lp::numeric_pair & ) { /* UNREACHABLE(); */ return 0;} template class numeric_traits> { public: static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate - static double one() { /*lp_unreachable();*/ return 0;} + static double one() { /*UNREACHABLE();*/ return 0;} static bool is_pos(const numeric_pair &p) { return numeric_traits::is_pos(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_pos(p.y)); diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 28a23b0c394..efb6e07cf75 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -31,7 +31,6 @@ template std::set> lp::static_matrix::add_column_to_vector(mpq const&, unsigned int, mpq*) const; template void static_matrix::add_columns_at_the_end(unsigned int); template bool static_matrix::is_correct() const; -template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix::get_balance() const; template mpq static_matrix::get_elem(unsigned int, unsigned int) const; @@ -47,7 +46,6 @@ template static_matrix::static_matrix(unsigned int, unsigned int); #ifdef Z3DEBUG template bool static_matrix >::is_correct() const; #endif -template void static_matrix >::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix >::get_elem(unsigned int, unsigned int) const; template void static_matrix >::init_empty_matrix(unsigned int, unsigned int); template void static_matrix >::set(unsigned int, unsigned int, mpq const&); diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index d7e4370a344..f79ff36ac38 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -168,8 +168,6 @@ class static_matrix std::set> get_domain(); - void copy_column_to_indexed_vector(unsigned j, indexed_vector & v) const; - T get_max_abs_in_row(unsigned row) const; void add_column_to_vector (const T & a, unsigned j, T * v) const { for (const auto & it : m_columns[j]) { @@ -222,7 +220,7 @@ class static_matrix virtual void set_number_of_columns(unsigned /*n*/) { } #endif - T get_max_val_in_row(unsigned /* i */) const { lp_unreachable(); } + T get_max_val_in_row(unsigned /* i */) const { UNREACHABLE(); } T get_balance() const; diff --git a/src/math/lp/static_matrix_def.h b/src/math/lp/static_matrix_def.h index af2eac36017..76c1dec546c 100644 --- a/src/math/lp/static_matrix_def.h +++ b/src/math/lp/static_matrix_def.h @@ -174,14 +174,6 @@ std::set> static_matrix::get_domain() { return ret; } -template void static_matrix::copy_column_to_indexed_vector (unsigned j, indexed_vector & v) const { - lp_assert(j < m_columns.size()); - for (auto & it : m_columns[j]) { - const T& val = get_val(it); - if (!is_zero(val)) - v.set_value(val, it.var()); - } -} template T static_matrix::get_max_abs_in_row(unsigned row) const { T ret = numeric_traits::zero(); diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h index 890ff90e352..c64c0103653 100644 --- a/src/test/lp/gomory_test.h +++ b/src/test/lp/gomory_test.h @@ -130,7 +130,7 @@ struct gomory_test { void report_conflict_from_gomory_cut(mpq &k) { - lp_assert(false); + UNREACHABLE(); } void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq &lcm_den) { diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 8ad73f0c003..9120d64cfdf 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1365,7 +1365,7 @@ void test_gomory_cut_0() { if (j == 2) return zero_of_type(); if (j == 3) return mpq(3); - lp_assert(false); + UNREACHABLE(); return zero_of_type(); }, [](unsigned j) { // at_low_p @@ -1375,7 +1375,7 @@ void test_gomory_cut_0() { return true; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // at_upper @@ -1385,31 +1385,31 @@ void test_gomory_cut_0() { return true; if (j == 3) return false; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // lower_bound if (j == 1) { - lp_assert(false); //unlimited from below + UNREACHABLE(); //unlimited from below return impq(0); } if (j == 2) return impq(0); if (j == 3) return impq(3); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [](unsigned j) { // upper if (j == 1) { - lp_assert(false); //unlimited from above + UNREACHABLE(); //unlimited from above return impq(0); } if (j == 2) return impq(0); if (j == 3) return impq(10); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [] (unsigned) { return 0; }, @@ -1437,7 +1437,7 @@ void test_gomory_cut_1() { return mpq(4363334, 2730001); if (j == 3) return mpq(1); - lp_assert(false); + UNREACHABLE(); return zero_of_type(); }, [](unsigned j) { // at_low_p @@ -1447,7 +1447,7 @@ void test_gomory_cut_1() { return false; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // at_upper @@ -1457,19 +1457,19 @@ void test_gomory_cut_1() { return false; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // lower_bound if (j == 1) { - lp_assert(false); //unlimited from below + UNREACHABLE(); //unlimited from below return impq(0); } if (j == 2) return impq(1); if (j == 3) return impq(1); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [](unsigned j) { // upper @@ -1480,7 +1480,7 @@ void test_gomory_cut_1() { return impq(3333); if (j == 3) return impq(10000); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [] (unsigned) { return 0; }, diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 75edb23b987..7843d5714d2 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -272,7 +272,7 @@ namespace lp { } else if (el.m_head == "+") { add_sum(c, el.m_elems); } else { - lp_assert(false); // unexpected input + UNREACHABLE(); // unexpected input } } From 4b3408696dfe37d41f30d350fda6e0a667086a21 Mon Sep 17 00:00:00 2001 From: igcontreras Date: Wed, 8 Mar 2023 16:13:38 -0500 Subject: [PATCH 509/597] use uintptr_t instead of size_t (tptr) for portability (#6627) --- src/util/tptr.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/util/tptr.h b/src/util/tptr.h index 806b7637c11..6213b2efa40 100644 --- a/src/util/tptr.h +++ b/src/util/tptr.h @@ -26,19 +26,19 @@ Revision History: #define TAG_MASK (ALIGNMENT_VALUE - 1) #define PTR_MASK (~TAG_MASK) -#define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ - static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) +#define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ + static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) -#define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) +#define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) -#define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) +#define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) -#define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) +#define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) -#define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) +#define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) -#define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) +#define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) -#define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) +#define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) From 1612b57e1a99c4874a761510161a9b4d376d2825 Mon Sep 17 00:00:00 2001 From: Bram V <33070319+BramVerb@users.noreply.github.com> Date: Wed, 8 Mar 2023 22:43:51 +0100 Subject: [PATCH 510/597] Make all methods show in java API (#6626) * Make all methods show in java API * Add final modifier to all generic methods --- src/api/java/Context.java | 170 +++++++++++++++++++------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 0f15d9411f3..06b312303e2 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -227,7 +227,7 @@ public BitVecSort mkBitVecSort(int size) /** * Create a new array sort. **/ - public ArraySort mkArraySort(D domain, R range) + public final ArraySort mkArraySort(D domain, R range) { checkContextMatch(domain); checkContextMatch(range); @@ -238,7 +238,7 @@ public ArraySort mkArraySort(D domain, R /** * Create a new array sort. **/ - public ArraySort mkArraySort(Sort[] domains, R range) + public final ArraySort mkArraySort(Sort[] domains, R range) { checkContextMatch(domains); checkContextMatch(range); @@ -256,7 +256,7 @@ public SeqSort mkStringSort() /** * Create a new sequence sort **/ - public SeqSort mkSeqSort(R s) + public final SeqSort mkSeqSort(R s) { return new SeqSort<>(this, Native.mkSeqSort(nCtx(), s.getNativeObject())); } @@ -264,7 +264,7 @@ public SeqSort mkSeqSort(R s) /** * Create a new regular expression sort **/ - public ReSort mkReSort(R s) + public final ReSort mkReSort(R s) { return new ReSort<>(this, Native.mkReSort(nCtx(), s.getNativeObject())); } @@ -286,7 +286,7 @@ public TupleSort mkTupleSort(Symbol name, Symbol[] fieldNames, /** * Create a new enumeration sort. **/ - public EnumSort mkEnumSort(Symbol name, Symbol... enumNames) + public final EnumSort mkEnumSort(Symbol name, Symbol... enumNames) { checkContextMatch(name); @@ -297,7 +297,7 @@ public EnumSort mkEnumSort(Symbol name, Symbol... enumNames) /** * Create a new enumeration sort. **/ - public EnumSort mkEnumSort(String name, String... enumNames) + public final EnumSort mkEnumSort(String name, String... enumNames) { return new EnumSort<>(this, mkSymbol(name), mkSymbols(enumNames)); @@ -306,7 +306,7 @@ public EnumSort mkEnumSort(String name, String... enumNames) /** * Create a new list sort. **/ - public ListSort mkListSort(Symbol name, R elemSort) + public final ListSort mkListSort(Symbol name, R elemSort) { checkContextMatch(name); checkContextMatch(elemSort); @@ -316,7 +316,7 @@ public ListSort mkListSort(Symbol name, R elemSort) /** * Create a new list sort. **/ - public ListSort mkListSort(String name, R elemSort) + public final ListSort mkListSort(String name, R elemSort) { checkContextMatch(elemSort); return new ListSort<>(this, mkSymbol(name), elemSort); @@ -325,7 +325,7 @@ public ListSort mkListSort(String name, R elemSort) /** * Create a new finite domain sort. **/ - public FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) + public final FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) { checkContextMatch(name); @@ -335,7 +335,7 @@ public FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) /** * Create a new finite domain sort. **/ - public FiniteDomainSort mkFiniteDomainSort(String name, long size) + public final FiniteDomainSort mkFiniteDomainSort(String name, long size) { return new FiniteDomainSort<>(this, mkSymbol(name), size); @@ -352,7 +352,7 @@ public FiniteDomainSort mkFiniteDomainSort(String name, long size) * an index referring to one of the recursive datatypes that is * declared. **/ - public Constructor mkConstructor(Symbol name, Symbol recognizer, + public final Constructor mkConstructor(Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { @@ -362,7 +362,7 @@ public Constructor mkConstructor(Symbol name, Symbol recognizer, /** * Create a datatype constructor. **/ - public Constructor mkConstructor(String name, String recognizer, + public final Constructor mkConstructor(String name, String recognizer, String[] fieldNames, Sort[] sorts, int[] sortRefs) { return of(this, mkSymbol(name), mkSymbol(recognizer), mkSymbols(fieldNames), sorts, sortRefs); @@ -371,7 +371,7 @@ public Constructor mkConstructor(String name, String recognizer, /** * Create a new datatype sort. **/ - public DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) + public final DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) { checkContextMatch(name); checkContextMatch(constructors); @@ -381,7 +381,7 @@ public DatatypeSort mkDatatypeSort(Symbol name, Constructor[] construc /** * Create a new datatype sort. **/ - public DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) + public final DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) { checkContextMatch(constructors); @@ -431,7 +431,7 @@ public DatatypeSort[] mkDatatypeSorts(String[] names, Constructor Expr mkUpdateField(FuncDecl field, Expr t, Expr v) + public final Expr mkUpdateField(FuncDecl field, Expr t, Expr v) throws Z3Exception { return (Expr) Expr.create(this, @@ -444,7 +444,7 @@ public Expr mkUpdateField(FuncDecl field, /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(Symbol name, Sort[] domain, R range) + public final FuncDecl mkFuncDecl(Symbol name, Sort[] domain, R range) { checkContextMatch(name); checkContextMatch(domain); @@ -455,7 +455,7 @@ public FuncDecl mkFuncDecl(Symbol name, Sort[] domain, R ran /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(Symbol name, Sort domain, R range) + public final FuncDecl mkFuncDecl(Symbol name, Sort domain, R range) { checkContextMatch(name); @@ -468,7 +468,7 @@ public FuncDecl mkFuncDecl(Symbol name, Sort domain, R range /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(String name, Sort[] domain, R range) + public final FuncDecl mkFuncDecl(String name, Sort[] domain, R range) { checkContextMatch(domain); @@ -479,7 +479,7 @@ public FuncDecl mkFuncDecl(String name, Sort[] domain, R ran /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(String name, Sort domain, R range) + public final FuncDecl mkFuncDecl(String name, Sort domain, R range) { checkContextMatch(domain); @@ -491,7 +491,7 @@ public FuncDecl mkFuncDecl(String name, Sort domain, R range /** * Creates a new recursive function declaration. **/ - public FuncDecl mkRecFuncDecl(Symbol name, Sort[] domain, R range) + public final FuncDecl mkRecFuncDecl(Symbol name, Sort[] domain, R range) { checkContextMatch(name); checkContextMatch(domain); @@ -506,7 +506,7 @@ public FuncDecl mkRecFuncDecl(Symbol name, Sort[] domain, R * MkRecFuncDecl. The body may contain recursive uses of the function or * other mutually recursive functions. */ - public void AddRecDef(FuncDecl f, Expr[] args, Expr body) + public final void AddRecDef(FuncDecl f, Expr[] args, Expr body) { checkContextMatch(f); checkContextMatch(args); @@ -521,7 +521,7 @@ public void AddRecDef(FuncDecl f, Expr[] args, Expr bo * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ - public FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, R range) + public final FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, R range) { checkContextMatch(domain); @@ -532,7 +532,7 @@ public FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain /** * Creates a new constant function declaration. **/ - public FuncDecl mkConstDecl(Symbol name, R range) + public final FuncDecl mkConstDecl(Symbol name, R range) { checkContextMatch(name); checkContextMatch(range); @@ -542,7 +542,7 @@ public FuncDecl mkConstDecl(Symbol name, R range) /** * Creates a new constant function declaration. **/ - public FuncDecl mkConstDecl(String name, R range) + public final FuncDecl mkConstDecl(String name, R range) { checkContextMatch(range); return new FuncDecl<>(this, mkSymbol(name), null, range); @@ -554,7 +554,7 @@ public FuncDecl mkConstDecl(String name, R range) * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ - public FuncDecl mkFreshConstDecl(String prefix, R range) + public final FuncDecl mkFreshConstDecl(String prefix, R range) { checkContextMatch(range); @@ -566,7 +566,7 @@ public FuncDecl mkFreshConstDecl(String prefix, R range) * @param index The de-Bruijn index of the variable * @param ty The sort of the variable **/ - public Expr mkBound(int index, R ty) + public final Expr mkBound(int index, R ty) { return (Expr) Expr.create(this, Native.mkBound(nCtx(), index, ty.getNativeObject())); @@ -590,7 +590,7 @@ public final Pattern mkPattern(Expr... terms) * Creates a new Constant of sort {@code range} and named * {@code name}. **/ - public Expr mkConst(Symbol name, R range) + public final Expr mkConst(Symbol name, R range) { checkContextMatch(name); checkContextMatch(range); @@ -605,7 +605,7 @@ public Expr mkConst(Symbol name, R range) * Creates a new Constant of sort {@code range} and named * {@code name}. **/ - public Expr mkConst(String name, R range) + public final Expr mkConst(String name, R range) { return mkConst(mkSymbol(name), range); } @@ -614,7 +614,7 @@ public Expr mkConst(String name, R range) * Creates a fresh Constant of sort {@code range} and a name * prefixed with {@code prefix}. **/ - public Expr mkFreshConst(String prefix, R range) + public final Expr mkFreshConst(String prefix, R range) { checkContextMatch(range); return (Expr) Expr.create(this, @@ -625,7 +625,7 @@ public Expr mkFreshConst(String prefix, R range) * Creates a fresh constant from the FuncDecl {@code f}. * @param f A decl of a 0-arity function **/ - public Expr mkConst(FuncDecl f) + public final Expr mkConst(FuncDecl f) { return mkApp(f, (Expr[]) null); } @@ -754,7 +754,7 @@ public final BoolExpr mkDistinct(Expr... args) /** * Create an expression representing {@code not(a)}. **/ - public BoolExpr mkNot(Expr a) + public final BoolExpr mkNot(Expr a) { checkContextMatch(a); return new BoolExpr(this, Native.mkNot(nCtx(), a.getNativeObject())); @@ -767,7 +767,7 @@ public BoolExpr mkNot(Expr a) * @param t2 An expression * @param t3 An expression with the same sort as {@code t2} **/ - public Expr mkITE(Expr t1, Expr t2, Expr t3) + public final Expr mkITE(Expr t1, Expr t2, Expr t3) { checkContextMatch(t1); checkContextMatch(t2); @@ -867,7 +867,7 @@ public final ArithExpr mkSub(Expr... t) /** * Create an expression representing {@code -t}. **/ - public ArithExpr mkUnaryMinus(Expr t) + public final ArithExpr mkUnaryMinus(Expr t) { checkContextMatch(t); return (ArithExpr) Expr.create(this, @@ -877,7 +877,7 @@ public ArithExpr mkUnaryMinus(Expr t) /** * Create an expression representing {@code t1 / t2}. **/ - public ArithExpr mkDiv(Expr t1, Expr t2) + public final ArithExpr mkDiv(Expr t1, Expr t2) { checkContextMatch(t1); checkContextMatch(t2); @@ -914,7 +914,7 @@ public IntExpr mkRem(Expr t1, Expr t2) /** * Create an expression representing {@code t1 ^ t2}. **/ - public ArithExpr mkPower(Expr t1, + public final ArithExpr mkPower(Expr t1, Expr t2) { checkContextMatch(t1); @@ -1693,7 +1693,7 @@ public BoolExpr mkBVMulNoUnderflow(Expr t1, Expr t2) /** * Create an array constant. **/ - public ArrayExpr mkArrayConst(Symbol name, D domain, R range) + public final ArrayExpr mkArrayConst(Symbol name, D domain, R range) { return (ArrayExpr) mkConst(name, mkArraySort(domain, range)); @@ -1702,7 +1702,7 @@ public ArrayExpr mkArrayConst(Symbol name /** * Create an array constant. **/ - public ArrayExpr mkArrayConst(String name, D domain, R range) + public final ArrayExpr mkArrayConst(String name, D domain, R range) { return (ArrayExpr) mkConst(mkSymbol(name), mkArraySort(domain, range)); @@ -1720,7 +1720,7 @@ public ArrayExpr mkArrayConst(String name * @see #mkArraySort(Sort[], R) * @see #mkStore(Expr> a, Expr i, Expr v) **/ - public Expr mkSelect(Expr> a, Expr i) + public final Expr mkSelect(Expr> a, Expr i) { checkContextMatch(a); checkContextMatch(i); @@ -1742,7 +1742,7 @@ public Expr mkSelect(Expr> a * @see #mkArraySort(Sort[], R) * @see #mkStore(Expr> a, Expr i, Expr v) **/ - public Expr mkSelect(Expr> a, Expr[] args) + public final Expr mkSelect(Expr> a, Expr[] args) { checkContextMatch(a); checkContextMatch(args); @@ -1767,7 +1767,7 @@ public Expr mkSelect(Expr> a, Expr[] a * @see #mkSelect(Expr> a, Expr i) **/ - public ArrayExpr mkStore(Expr> a, Expr i, Expr v) + public final ArrayExpr mkStore(Expr> a, Expr i, Expr v) { checkContextMatch(a); checkContextMatch(i); @@ -1792,7 +1792,7 @@ public ArrayExpr mkStore(Expr> a, Expr i) **/ - public ArrayExpr mkStore(Expr> a, Expr[] args, Expr v) + public final ArrayExpr mkStore(Expr> a, Expr[] args, Expr v) { checkContextMatch(a); checkContextMatch(args); @@ -1810,7 +1810,7 @@ public ArrayExpr mkStore(Expr> a, E * @see #mkSelect(Expr> a, Expr i) * **/ - public ArrayExpr mkConstArray(D domain, Expr v) + public final ArrayExpr mkConstArray(D domain, Expr v) { checkContextMatch(domain); checkContextMatch(v); @@ -1847,7 +1847,7 @@ public final ArrayExpr * value, for arrays that can be represented as finite maps with a default * range value. **/ - public Expr mkTermArray(Expr> array) + public final Expr mkTermArray(Expr> array) { checkContextMatch(array); return (Expr) Expr.create(this, @@ -1857,7 +1857,7 @@ public Expr mkTermArray(Expr /** * Create Extentionality index. Two arrays are equal if and only if they are equal on the index returned by MkArrayExt. **/ - public Expr mkArrayExt(Expr> arg1, Expr> arg2) + public final Expr mkArrayExt(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1868,7 +1868,7 @@ public Expr mkArrayExt(Expr> /** * Create a set type. **/ - public SetSort mkSetSort(D ty) + public final SetSort mkSetSort(D ty) { checkContextMatch(ty); return new SetSort<>(this, ty); @@ -1877,7 +1877,7 @@ public SetSort mkSetSort(D ty) /** * Create an empty set. **/ - public ArrayExpr mkEmptySet(D domain) + public final ArrayExpr mkEmptySet(D domain) { checkContextMatch(domain); return (ArrayExpr) Expr.create(this, @@ -1887,7 +1887,7 @@ public ArrayExpr mkEmptySet(D domain) /** * Create the full set. **/ - public ArrayExpr mkFullSet(D domain) + public final ArrayExpr mkFullSet(D domain) { checkContextMatch(domain); return (ArrayExpr) Expr.create(this, @@ -1897,7 +1897,7 @@ public ArrayExpr mkFullSet(D domain) /** * Add an element to the set. **/ - public ArrayExpr mkSetAdd(Expr> set, Expr element) + public final ArrayExpr mkSetAdd(Expr> set, Expr element) { checkContextMatch(set); checkContextMatch(element); @@ -1909,7 +1909,7 @@ public ArrayExpr mkSetAdd(Expr ArrayExpr mkSetDel(Expr> set, Expr element) + public final ArrayExpr mkSetDel(Expr> set, Expr element) { checkContextMatch(set); checkContextMatch(element); @@ -1945,7 +1945,7 @@ public final ArrayExpr mkSetIntersection(Expr ArrayExpr mkSetDifference(Expr> arg1, Expr> arg2) + public final ArrayExpr mkSetDifference(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1957,7 +1957,7 @@ public ArrayExpr mkSetDifference(Expr ArrayExpr mkSetComplement(Expr> arg) + public final ArrayExpr mkSetComplement(Expr> arg) { checkContextMatch(arg); return (ArrayExpr)Expr.create(this, @@ -1967,7 +1967,7 @@ public ArrayExpr mkSetComplement(Expr BoolExpr mkSetMembership(Expr elem, Expr> set) + public final BoolExpr mkSetMembership(Expr elem, Expr> set) { checkContextMatch(elem); checkContextMatch(set); @@ -1979,7 +1979,7 @@ public BoolExpr mkSetMembership(Expr elem, Expr BoolExpr mkSetSubset(Expr> arg1, Expr> arg2) + public final BoolExpr mkSetSubset(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1996,7 +1996,7 @@ public BoolExpr mkSetSubset(Expr> arg1, /** * Create the empty sequence. */ - public SeqExpr mkEmptySeq(R s) + public final SeqExpr mkEmptySeq(R s) { checkContextMatch(s); return (SeqExpr) Expr.create(this, Native.mkSeqEmpty(nCtx(), s.getNativeObject())); @@ -2005,7 +2005,7 @@ public SeqExpr mkEmptySeq(R s) /** * Create the singleton sequence. */ - public SeqExpr mkUnit(Expr elem) + public final SeqExpr mkUnit(Expr elem) { checkContextMatch(elem); return (SeqExpr) Expr.create(this, Native.mkSeqUnit(nCtx(), elem.getNativeObject())); @@ -2073,7 +2073,7 @@ public final SeqExpr mkConcat(Expr>... t) /** * Retrieve the length of a given sequence. */ - public IntExpr mkLength(Expr> s) + public final IntExpr mkLength(Expr> s) { checkContextMatch(s); return (IntExpr) Expr.create(this, Native.mkSeqLength(nCtx(), s.getNativeObject())); @@ -2082,7 +2082,7 @@ public IntExpr mkLength(Expr> s) /** * Check for sequence prefix. */ - public BoolExpr mkPrefixOf(Expr> s1, Expr> s2) + public final BoolExpr mkPrefixOf(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqPrefix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2091,7 +2091,7 @@ public BoolExpr mkPrefixOf(Expr> s1, Expr /** * Check for sequence suffix. */ - public BoolExpr mkSuffixOf(Expr> s1, Expr> s2) + public final BoolExpr mkSuffixOf(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr)Expr.create(this, Native.mkSeqSuffix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2100,7 +2100,7 @@ public BoolExpr mkSuffixOf(Expr> s1, Expr /** * Check for sequence containment of s2 in s1. */ - public BoolExpr mkContains(Expr> s1, Expr> s2) + public final BoolExpr mkContains(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2129,7 +2129,7 @@ public BoolExpr MkStringLe(Expr> s1, Expr> s /** * Retrieve sequence of length one at index. */ - public SeqExpr mkAt(Expr> s, Expr index) + public final SeqExpr mkAt(Expr> s, Expr index) { checkContextMatch(s, index); return (SeqExpr) Expr.create(this, Native.mkSeqAt(nCtx(), s.getNativeObject(), index.getNativeObject())); @@ -2138,7 +2138,7 @@ public SeqExpr mkAt(Expr> s, Expr index) /** * Retrieve element at index. */ - public Expr mkNth(Expr> s, Expr index) + public final Expr mkNth(Expr> s, Expr index) { checkContextMatch(s, index); return (Expr) Expr.create(this, Native.mkSeqNth(nCtx(), s.getNativeObject(), index.getNativeObject())); @@ -2148,7 +2148,7 @@ public Expr mkNth(Expr> s, Expr index) /** * Extract subsequence. */ - public SeqExpr mkExtract(Expr> s, Expr offset, Expr length) + public final SeqExpr mkExtract(Expr> s, Expr offset, Expr length) { checkContextMatch(s, offset, length); return (SeqExpr) Expr.create(this, Native.mkSeqExtract(nCtx(), s.getNativeObject(), offset.getNativeObject(), length.getNativeObject())); @@ -2157,7 +2157,7 @@ public SeqExpr mkExtract(Expr> s, Expr o /** * Extract index of sub-string starting at offset. */ - public IntExpr mkIndexOf(Expr> s, Expr> substr, Expr offset) + public final IntExpr mkIndexOf(Expr> s, Expr> substr, Expr offset) { checkContextMatch(s, substr, offset); return (IntExpr)Expr.create(this, Native.mkSeqIndex(nCtx(), s.getNativeObject(), substr.getNativeObject(), offset.getNativeObject())); @@ -2166,7 +2166,7 @@ public IntExpr mkIndexOf(Expr> s, Expr> s /** * Replace the first occurrence of src by dst in s. */ - public SeqExpr mkReplace(Expr> s, Expr> src, Expr> dst) + public final SeqExpr mkReplace(Expr> s, Expr> src, Expr> dst) { checkContextMatch(s, src, dst); return (SeqExpr) Expr.create(this, Native.mkSeqReplace(nCtx(), s.getNativeObject(), src.getNativeObject(), dst.getNativeObject())); @@ -2175,7 +2175,7 @@ public SeqExpr mkReplace(Expr> s, Expr /** * Convert a regular expression that accepts sequence s. */ - public ReExpr mkToRe(Expr> s) + public final ReExpr mkToRe(Expr> s) { checkContextMatch(s); return (ReExpr) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); @@ -2185,7 +2185,7 @@ public ReExpr mkToRe(Expr> s) /** * Check for regular expression membership. */ - public BoolExpr mkInRe(Expr> s, Expr> re) + public final BoolExpr mkInRe(Expr> s, Expr> re) { checkContextMatch(s, re); return (BoolExpr) Expr.create(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); @@ -2194,7 +2194,7 @@ public BoolExpr mkInRe(Expr> s, Expr> re) /** * Take the Kleene star of a regular expression. */ - public ReExpr mkStar(Expr> re) + public final ReExpr mkStar(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReStar(nCtx(), re.getNativeObject())); @@ -2203,7 +2203,7 @@ public ReExpr mkStar(Expr> re) /** * Create power regular expression. */ - public ReExpr mkPower(Expr> re, int n) + public final ReExpr mkPower(Expr> re, int n) { return (ReExpr) Expr.create(this, Native.mkRePower(nCtx(), re.getNativeObject(), n)); } @@ -2211,7 +2211,7 @@ public ReExpr mkPower(Expr> re, int n) /** * Take the lower and upper-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(Expr> re, int lo, int hi) + public final ReExpr mkLoop(Expr> re, int lo, int hi) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); } @@ -2219,7 +2219,7 @@ public ReExpr mkLoop(Expr> re, int lo, int hi) /** * Take the lower-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(Expr> re, int lo) + public final ReExpr mkLoop(Expr> re, int lo) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, 0)); } @@ -2228,7 +2228,7 @@ public ReExpr mkLoop(Expr> re, int lo) /** * Take the Kleene plus of a regular expression. */ - public ReExpr mkPlus(Expr> re) + public final ReExpr mkPlus(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkRePlus(nCtx(), re.getNativeObject())); @@ -2237,7 +2237,7 @@ public ReExpr mkPlus(Expr> re) /** * Create the optional regular expression. */ - public ReExpr mkOption(Expr> re) + public final ReExpr mkOption(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReOption(nCtx(), re.getNativeObject())); @@ -2246,7 +2246,7 @@ public ReExpr mkOption(Expr> re) /** * Create the complement regular expression. */ - public ReExpr mkComplement(Expr> re) + public final ReExpr mkComplement(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReComplement(nCtx(), re.getNativeObject())); @@ -2285,7 +2285,7 @@ public final ReExpr mkIntersect(Expr>... t) /** * Create a difference regular expression. */ - public ReExpr mkDiff(Expr> a, Expr> b) + public final ReExpr mkDiff(Expr> a, Expr> b) { checkContextMatch(a, b); return (ReExpr) Expr.create(this, Native.mkReDiff(nCtx(), a.getNativeObject(), b.getNativeObject())); @@ -2296,7 +2296,7 @@ public ReExpr mkDiff(Expr> a, Expr> b) * Create the empty regular expression. * Coresponds to re.none */ - public ReExpr mkEmptyRe(R s) + public final ReExpr mkEmptyRe(R s) { return (ReExpr) Expr.create(this, Native.mkReEmpty(nCtx(), s.getNativeObject())); } @@ -2305,7 +2305,7 @@ public ReExpr mkEmptyRe(R s) * Create the full regular expression. * Corresponds to re.all */ - public ReExpr mkFullRe(R s) + public final ReExpr mkFullRe(R s) { return (ReExpr) Expr.create(this, Native.mkReFull(nCtx(), s.getNativeObject())); } @@ -2314,7 +2314,7 @@ public ReExpr mkFullRe(R s) * Create regular expression that accepts all characters * Corresponds to re.allchar */ - public ReExpr mkAllcharRe(R s) + public final ReExpr mkAllcharRe(R s) { return (ReExpr) Expr.create(this, Native.mkReAllchar(nCtx(), s.getNativeObject())); } @@ -2322,7 +2322,7 @@ public ReExpr mkAllcharRe(R s) /** * Create a range expression. */ - public ReExpr mkRange(Expr> lo, Expr> hi) + public final ReExpr mkRange(Expr> lo, Expr> hi) { checkContextMatch(lo, hi); return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); @@ -2429,7 +2429,7 @@ public BoolExpr mkPBEq(int[] coeffs, Expr[] args, int k) * * @return A Term with value {@code v} and sort {@code ty} **/ - public Expr mkNumeral(String v, R ty) + public final Expr mkNumeral(String v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, @@ -2446,7 +2446,7 @@ public Expr mkNumeral(String v, R ty) * * @return A Term with value {@code v} and type {@code ty} **/ - public Expr mkNumeral(int v, R ty) + public final Expr mkNumeral(int v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, Native.mkInt(nCtx(), v, ty.getNativeObject())); @@ -2462,7 +2462,7 @@ public Expr mkNumeral(int v, R ty) * * @return A Term with value {@code v} and type {@code ty} **/ - public Expr mkNumeral(long v, R ty) + public final Expr mkNumeral(long v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, @@ -2717,7 +2717,7 @@ public Quantifier mkQuantifier(boolean universal, Expr[] boundConstants, * @param names names of the bound variables. * @param body the body of the quantifier. **/ - public Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr body) + public final Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr body) { return Lambda.of(this, sorts, names, body); } @@ -2728,7 +2728,7 @@ public Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr * Creates a lambda expression using a list of constants that will * form the set of bound variables. **/ - public Lambda mkLambda(Expr[] boundConstants, Expr body) + public final Lambda mkLambda(Expr[] boundConstants, Expr body) { return Lambda.of(this, boundConstants, body); } @@ -4179,7 +4179,7 @@ public BitVecExpr mkFPToFP(Expr rm, Expr exp, Expr * @param index The index of the order. * @param sort The sort of the order. */ - public FuncDecl mkLinearOrder(R sort, int index) { + public final FuncDecl mkLinearOrder(R sort, int index) { return (FuncDecl) FuncDecl.create( this, Native.mkLinearOrder( @@ -4195,7 +4195,7 @@ public FuncDecl mkLinearOrder(R sort, int index) { * @param index The index of the order. * @param sort The sort of the order. */ - public FuncDecl mkPartialOrder(R sort, int index) { + public final FuncDecl mkPartialOrder(R sort, int index) { return (FuncDecl) FuncDecl.create( this, Native.mkPartialOrder( From cf4df08fd079d6a93a1ed679e9217c5ff07f9d77 Mon Sep 17 00:00:00 2001 From: Declan Hwang Date: Fri, 10 Mar 2023 02:29:30 +0900 Subject: [PATCH 511/597] fix typo (#6628) --- src/api/python/z3/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 92bb2aa24d7..7e7c5805225 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11343,7 +11343,7 @@ def Range(lo, hi, ctx=None): return ReRef(Z3_mk_re_range(lo.ctx_ref(), lo.ast, hi.ast), lo.ctx) def Diff(a, b, ctx=None): - """Create the difference regular epression + """Create the difference regular expression """ return ReRef(Z3_mk_re_diff(a.ctx_ref(), a.ast, b.ast), a.ctx) From a0f3727e90c4446ba2c1fa7e4392637587ad9632 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 12 Mar 2023 19:26:47 +0000 Subject: [PATCH 512/597] BV: add missing neg internalizer usually bvneg is eliminated during rewriting, but it can be left behind during e.g. a badly-timed timeout --- src/smt/theory_bv.cpp | 2 ++ src/smt/theory_bv.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index b3a6e77ff4d..00564ed3e95 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -811,6 +811,7 @@ namespace smt { init_bits(e, bits); } + MK_UNARY(internalize_neg, mk_neg); MK_UNARY(internalize_not, mk_not); MK_UNARY(internalize_redand, mk_redand); MK_UNARY(internalize_redor, mk_redor); @@ -895,6 +896,7 @@ namespace smt { } switch (term->get_decl_kind()) { case OP_BV_NUM: internalize_num(term); return true; + case OP_BNEG: internalize_neg(term); return true; case OP_BADD: internalize_add(term); return true; case OP_BSUB: internalize_sub(term); return true; case OP_BMUL: internalize_mul(term); return true; diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index 588f19d89cb..73d659c688c 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -196,6 +196,7 @@ namespace smt { void internalize_ext_rotate_right(app * n); void internalize_and(app * n); void internalize_or(app * n); + void internalize_neg(app * n); void internalize_not(app * n); void internalize_nand(app * n); void internalize_nor(app * n); From d1c7ff1a369711f64bf16165efa5c6e8c8ad423a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Mar 2023 09:23:13 +0100 Subject: [PATCH 513/597] add unconstrained elimination for sequences --- src/api/api_tactic.cpp | 4 +- src/ast/converters/expr_inverter.cpp | 49 +++++++++++++++++++ src/ast/euf/euf_egraph.cpp | 5 ++ src/ast/rewriter/seq_rewriter.cpp | 36 ++++++++++++++ src/ast/rewriter/seq_rewriter.h | 1 + src/ast/simplifiers/elim_unconstrained.cpp | 6 +-- .../{seq_simplifier.h => then_simplifier.h} | 6 +-- src/cmd_context/simplifier_cmds.cpp | 4 +- src/solver/simplifier_solver.cpp | 4 +- src/solver/solver_preprocess.cpp | 2 +- src/solver/solver_preprocess.h | 4 +- src/tactic/core/elim_uncnstr_tactic.cpp | 43 +++++++++++++++- 12 files changed, 148 insertions(+), 16 deletions(-) rename src/ast/simplifiers/{seq_simplifier.h => then_simplifier.h} (94%) diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 4e5156ba93b..351b3d1b990 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -24,7 +24,7 @@ Revision History: #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) { } @@ -589,7 +589,7 @@ extern "C" { auto fac1 = *to_simplifier_ref(t1); auto fac2 = *to_simplifier_ref(t2); auto new_s = [fac1, fac2](auto& m, auto& p, auto& st) { - auto* r = alloc(seq_simplifier, m, p, st); + auto* r = alloc(then_simplifier, m, p, st); r->add_simplifier(fac1(m, p, st)); r->add_simplifier(fac2(m, p, st)); return r; diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index abf3125c6fc..4d435f7e8f0 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -19,6 +19,7 @@ Module Name: #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" #include "ast/arith_decl_plugin.h" +#include "ast/seq_decl_plugin.h" #include "ast/converters/expr_inverter.h" class basic_expr_inverter : public iexpr_inverter { @@ -742,6 +743,53 @@ class dt_expr_inverter : public iexpr_inverter { }; +class seq_expr_inverter : public iexpr_inverter { + seq_util seq; +public: + seq_expr_inverter(ast_manager& m) : iexpr_inverter(m), seq(m) {} + + family_id get_fid() const override { return seq.get_family_id(); } + + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { + switch (f->get_decl_kind()) { + case _OP_STRING_CONCAT: + case OP_SEQ_CONCAT: { + expr* x, *y; + + if (uncnstr(args[0]) && num == 2 && + seq.str.is_concat(args[1], x, y) && + uncnstr(x)) { + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(args[0], seq.str.mk_empty(args[0]->get_sort())); + add_def(x, r); + } + r = seq.str.mk_concat(r, y); + return true; + } + + if (!uncnstr(num, args)) + return false; + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(args[0], r); + for (unsigned i = 1; i < num; ++i) + add_def(args[i], seq.str.mk_empty(args[0]->get_sort())); + } + return true; + } + default: + return false; + + } + } + + bool mk_diff(expr* t, expr_ref& r) override { + return false; + } + +}; + expr_inverter::~expr_inverter() { for (auto* v : m_inverters) @@ -796,6 +844,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { add(alloc(array_expr_inverter, m, *this)); add(alloc(dt_expr_inverter, m)); add(alloc(basic_expr_inverter, m, *this)); + add(alloc(seq_expr_inverter, m)); } diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 7939c23f2b5..ac60a98ba96 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -67,6 +67,8 @@ namespace euf { } enode_bool_pair egraph::insert_table(enode* p) { + TRACE("euf", tout << bpp(p) << "\n"); + //SASSERT(!m_table.contains_ptr(p)); auto rc = m_table.insert(p); p->m_cg = rc.first; return rc; @@ -280,6 +282,7 @@ namespace euf { if (!m.is_bool(n->get_sort())) return; if (enable_merge_tf != n->merge_tf()) { + TRACE("euf", tout << "set tf " << enable_merge_tf << " " << bpp(n) << "\n"); n->set_merge_tf(enable_merge_tf); m_updates.push_back(update_record(n, update_record::toggle_merge_tf())); } @@ -487,6 +490,7 @@ namespace euf { } void egraph::remove_parents(enode* r) { + TRACE("euf", tout << bpp(r) << "\n"); for (enode* p : enode_parents(r)) { if (p->is_marked1()) continue; @@ -496,6 +500,7 @@ namespace euf { SASSERT(m_table.contains_ptr(p)); p->mark1(); erase_from_table(p); + CTRACE("euf", m_table.contains_ptr(p), tout << bpp(p) << "\n"; display(tout)); SASSERT(!m_table.contains_ptr(p)); } else if (p->is_equality()) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index eed36af813e..ea3e16f6efa 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -5518,6 +5518,7 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ reduce_front(ls, rs, eqs) && reduce_itos(ls, rs, eqs) && reduce_itos(rs, ls, eqs) && + reduce_value_clash(ls, rs, eqs) && reduce_by_length(ls, rs, eqs) && reduce_subsequence(ls, rs, eqs) && reduce_non_overlap(ls, rs, eqs) && @@ -5943,6 +5944,41 @@ bool seq_rewriter::reduce_non_overlap(expr_ref_vector& ls, expr_ref_vector& rs, return true; } + +/** + * partial check for value clash. + * checks that elements that do not occur in + * other sequence are non-values. + * The check could be extended to take non-value + * characters (units) into account. + */ +bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { + ptr_buffer es; + if (ls.empty() || rs.empty()) + return true; + es.append(ls.size(), ls.data()); + auto remove = [&](expr* r) { + for (unsigned i = 0; i < es.size(); ++i) { + if (r == es[i]) { + es[i] = es.back(); + es.pop_back(); + return true; + } + } + return false; + }; + auto is_unit_value = [&](expr* r) { + return m().is_value(r) && str().is_unit(r); + }; + for (expr* r : rs) { + if (remove(r)) + continue; + if (!is_unit_value(r)) + return true; + } + return any_of(es, [&](expr* e) { return is_unit_value(e); }); +} + bool seq_rewriter::reduce_subsequence(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { if (ls.size() > rs.size()) diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 26dc00675e4..92a6a17faf6 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -340,6 +340,7 @@ class seq_rewriter { bool is_sequence(expr* e, expr_ref_vector& seq); bool is_sequence(eautomaton& aut, expr_ref_vector& seq); bool get_lengths(expr* e, expr_ref_vector& lens, rational& pos); + bool reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); bool reduce_back(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); bool reduce_front(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); void remove_empty_and_concats(expr_ref_vector& es); diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 3d2566193e5..231858897c3 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -73,7 +73,7 @@ void elim_unconstrained::eliminate() { node& n = get_node(v); if (n.m_refcount == 0) continue; - if (n.m_refcount > 1) + if (n.m_refcount > 1) return; if (n.m_parents.empty()) { @@ -90,10 +90,10 @@ void elim_unconstrained::eliminate() { unsigned sz = m_args.size(); for (expr* arg : *to_app(t)) m_args.push_back(reconstruct_term(get_node(arg))); - bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r); + bool inverted = m_inverter(t->get_decl(), t->get_num_args(), m_args.data() + sz, r); proof_ref pr(m); if (inverted && m_enable_proofs) { - expr * s = m.mk_app(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + expr * s = m.mk_app(t->get_decl(), t->get_num_args(), m_args.data() + sz); expr * eq = m.mk_eq(s, r); proof * pr1 = m.mk_def_intro(eq); proof * pr = m.mk_apply_def(s, r, pr1); diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/then_simplifier.h similarity index 94% rename from src/ast/simplifiers/seq_simplifier.h rename to src/ast/simplifiers/then_simplifier.h index a5ef91d7c8a..6ee8b94129a 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/then_simplifier.h @@ -3,7 +3,7 @@ Copyright (c) 2022 Microsoft Corporation Module Name: - seq_simplifier.h + then_simplifier.h Abstract: @@ -21,7 +21,7 @@ Module Name: #include "ast/simplifiers/dependent_expr_state.h" -class seq_simplifier : public dependent_expr_simplifier { +class then_simplifier : public dependent_expr_simplifier { scoped_ptr_vector m_simplifiers; struct collect_stats { @@ -53,7 +53,7 @@ class seq_simplifier : public dependent_expr_simplifier { public: - seq_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + then_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls) { } diff --git a/src/cmd_context/simplifier_cmds.cpp b/src/cmd_context/simplifier_cmds.cpp index 0050dc54839..ac8d3e839af 100644 --- a/src/cmd_context/simplifier_cmds.cpp +++ b/src/cmd_context/simplifier_cmds.cpp @@ -21,7 +21,7 @@ Module Name: #include "cmd_context/parametric_cmd.h" #include "model/model_smt2_pp.h" #include "ast/ast_smt2_pp.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" #include "solver/simplifier_solver.h" typedef dependent_expr_simplifier simplifier; @@ -37,7 +37,7 @@ static simplifier_factory mk_and_then(cmd_context & ctx, sexpr * n) { for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2simplifier(ctx, n->get_child(i))); simplifier_factory result = [args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { - scoped_ptr s = alloc(seq_simplifier, m, p, st); + scoped_ptr s = alloc(then_simplifier, m, p, st); for (auto & simp : args) s->add_simplifier(simp(m, p, st)); return s.detach(); diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index a717c4932d7..12222194d38 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -23,7 +23,7 @@ Module Name: #include "ast/ast_util.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" #include "solver/solver.h" #include "solver/simplifier_solver.h" #include "solver/solver_preprocess.h" @@ -91,7 +91,7 @@ class simplifier_solver : public solver { solver_ref s; vector m_fmls; dep_expr_state m_preprocess_state; - seq_simplifier m_preprocess; + then_simplifier m_preprocess; expr_ref_vector m_assumptions; model_converter_ref m_mc; bool m_inconsistent = false; diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp index 7fd9d1dba3c..9cac4b835bd 100644 --- a/src/solver/solver_preprocess.cpp +++ b/src/solver/solver_preprocess.cpp @@ -45,7 +45,7 @@ Module Name: #include "solver/solver_preprocess.h" #include "qe/lite/qe_lite_tactic.h" -void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { +void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, dependent_expr_state& st) { smt_params smtp(p); s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); diff --git a/src/solver/solver_preprocess.h b/src/solver/solver_preprocess.h index c0dfc42f3fd..6f610d59ddb 100644 --- a/src/solver/solver_preprocess.h +++ b/src/solver/solver_preprocess.h @@ -19,7 +19,7 @@ Module Name: #pragma once -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" -void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st); +void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, dependent_expr_state& st); diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index b8b4334f411..869716f590b 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -24,6 +24,7 @@ Module Name: #include "ast/recfun_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" +#include "ast/seq_decl_plugin.h" #include "tactic/core/collect_occs.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" @@ -44,6 +45,7 @@ class elim_uncnstr_tactic : public tactic { bv_util m_bv_util; array_util m_ar_util; datatype_util m_dt_util; + seq_util m_seq_util; app_ref_vector m_fresh_vars; obj_map m_cache; app_ref_vector m_cache_domain; @@ -60,6 +62,7 @@ class elim_uncnstr_tactic : public tactic { m_bv_util(m), m_ar_util(m), m_dt_util(m), + m_seq_util(m), m_fresh_vars(m), m_cache_domain(m), m_max_memory(max_memory), @@ -792,6 +795,43 @@ class elim_uncnstr_tactic : public tactic { } return nullptr; } + + // x ++ y -> z, x -> z, y -> eps + app * process_seq_app(func_decl * f, unsigned num, expr * const * args) { + switch (f->get_decl_kind()) { + case _OP_STRING_CONCAT: + case OP_SEQ_CONCAT: { + app * r = nullptr; + expr* x, *y; + if (uncnstr(args[0]) && num == 2 && + m_seq_util.str.is_concat(args[1], x, y) && + uncnstr(x)) { + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + + if (m_mc) { + add_def(args[0], r); + add_def(x, m_seq_util.str.mk_empty(args[0]->get_sort())); + } + r = m_seq_util.str.mk_concat(r, y); + return r; + + } + if (!uncnstr(num, args)) + return nullptr; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + + expr_ref id(m_seq_util.str.mk_empty(args[0]->get_sort()), m()); + add_defs(num, args, r, id); + + return r; + } + default: + return nullptr; + } + } + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) @@ -817,7 +857,8 @@ class elim_uncnstr_tactic : public tactic { u = process_array_app(f, num, args); else if (fid == m_dt_util.get_family_id()) u = process_datatype_app(f, num, args); - + else if (fid == m_seq_util.get_family_id()) + u = process_seq_app(f, num, args); if (u == nullptr) return BR_FAILED; From a9e6e567b054bf0aaa03463b56c15d2a743696c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Mar 2023 21:43:26 +0100 Subject: [PATCH 514/597] make generation of "some" Boolean value fair --- src/model/value_factory.cpp | 8 ++++---- src/model/value_factory.h | 3 ++- src/smt/proto_model/proto_model.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/model/value_factory.cpp b/src/model/value_factory.cpp index 2b412f85080..30fa82caf57 100644 --- a/src/model/value_factory.cpp +++ b/src/model/value_factory.cpp @@ -28,13 +28,13 @@ value_factory::value_factory(ast_manager & m, family_id fid): value_factory::~value_factory() { } -basic_factory::basic_factory(ast_manager & m): - value_factory(m, m.get_basic_family_id()) { +basic_factory::basic_factory(ast_manager & m, unsigned seed): + value_factory(m, m.get_basic_family_id()), m_rand(seed) { } expr * basic_factory::get_some_value(sort * s) { - if (m_manager.is_bool(s)) - return m_manager.mk_false(); + if (m_manager.is_bool(s)) + return (m_rand() % 2 == 0) ? m_manager.mk_false() : m_manager.mk_true(); return nullptr; } diff --git a/src/model/value_factory.h b/src/model/value_factory.h index cf56439d910..edb12b09562 100644 --- a/src/model/value_factory.h +++ b/src/model/value_factory.h @@ -60,8 +60,9 @@ class value_factory { }; class basic_factory : public value_factory { + random_gen m_rand; public: - basic_factory(ast_manager & m); + basic_factory(ast_manager & m, unsigned seed); expr * get_some_value(sort * s) override; diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index d1614f52151..d61bcf028f6 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -32,7 +32,7 @@ proto_model::proto_model(ast_manager & m, params_ref const & p): model_core(m), m_eval(*this), m_rewrite(m) { - register_factory(alloc(basic_factory, m)); + register_factory(alloc(basic_factory, m, m.get_num_asts())); m_user_sort_factory = alloc(user_sort_factory, m); register_factory(m_user_sort_factory); m_model_partial = model_params(p).partial(); From c6e3fb446abbf9c6d99619e0443ca53ee978d1e6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 17:06:14 +0100 Subject: [PATCH 515/597] print lemmas2console faster - add option pp.no_lets (default = false) to print formulas without let (used by the low-level SMT2 printer). - print lemmas2console faster by using the low level printer --- src/ast/ast_smt_pp.cpp | 5 ++++- src/ast/pp_params.pyg | 1 + src/ast/simplifiers/elim_unconstrained.h | 2 ++ src/smt/smt_internalizer.cpp | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index f40b8a554a0..bea669438c3 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -34,6 +34,7 @@ Revision History: #include "ast/for_each_ast.h" #include "ast/decl_collector.h" #include "math/polynomial/algebraic_numbers.h" +#include "ast/pp_params.hpp" // --------------------------------------- @@ -911,7 +912,9 @@ ast_smt_pp::ast_smt_pp(ast_manager& m): void ast_smt_pp::display_expr_smt2(std::ostream& strm, expr* n, unsigned indent, unsigned num_var_names, char const* const* var_names) { ptr_vector ql; smt_renaming rn; - smt_printer p(strm, m_manager, ql, rn, m_logic, false, m_simplify_implies, indent, num_var_names, var_names); + pp_params params; + bool no_lets = params.no_lets(); + smt_printer p(strm, m_manager, ql, rn, m_logic, no_lets, m_simplify_implies, indent, num_var_names, var_names); p(n); } diff --git a/src/ast/pp_params.pyg b/src/ast/pp_params.pyg index 6b43cbea3c2..c833b624ab3 100644 --- a/src/ast/pp_params.pyg +++ b/src/ast/pp_params.pyg @@ -6,6 +6,7 @@ def_module_params('pp', ('max_width', UINT, 80, 'max. width in pretty printer'), ('max_ribbon', UINT, 80, 'max. ribbon (width - indentation) in pretty printer'), ('max_depth', UINT, 5, 'max. term depth (when pretty printing SMT2 terms/formulas)'), + ('no_lets', BOOL, False, 'dont print lets in low level SMT printer'), ('min_alias_size', UINT, 10, 'min. size for creating an alias for a shared term (when pretty printing SMT2 terms/formulas)'), ('decimal', BOOL, False, 'pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise'), ('decimal_precision', UINT, 10, 'maximum number of decimal places to be used when pp.decimal=true'), diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 0fdde5af2b5..5dced90d04c 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -22,6 +22,8 @@ Module Name: class elim_unconstrained : public dependent_expr_simplifier { + friend class seq_simplifier; + struct node { unsigned m_refcount = 0; expr* m_term = nullptr; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 3a7b95e2c49..68879b8aced 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1536,7 +1536,7 @@ namespace smt { fml = mk_or(fmls); m_lemma_visitor.collect(fml); m_lemma_visitor.display_skolem_decls(std::cout); - m_lemma_visitor.display_assert(std::cout, fml.get(), true); + m_lemma_visitor.display_assert(std::cout, fml.get(), false); } } From 48de7c2da8dfdcd577c56d601f720d1aecbe390e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 17:06:59 +0100 Subject: [PATCH 516/597] missing updates --- src/ast/simplifiers/seq_simplifier.cpp | 156 +++++++++++++++++++++++++ src/ast/simplifiers/seq_simplifier.h | 36 ++++++ 2 files changed, 192 insertions(+) create mode 100644 src/ast/simplifiers/seq_simplifier.cpp create mode 100644 src/ast/simplifiers/seq_simplifier.h diff --git a/src/ast/simplifiers/seq_simplifier.cpp b/src/ast/simplifiers/seq_simplifier.cpp new file mode 100644 index 00000000000..907bfaa93ec --- /dev/null +++ b/src/ast/simplifiers/seq_simplifier.cpp @@ -0,0 +1,156 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + seq_simplifier.cpp + +Abstract: + + Global simplifier for sequences + +Author: + + Nikolaj Bjorner (nbjorner) 2023-03-12. + +--*/ + + +#include "ast/simplifiers/seq_simplifier.h" + +// +// if x, y always are in pattern .. ++ x ++ y ++ .. -> z +// x ++ a ++ y = s, where s containing a, x, y are unique -> true +// Parikh pre-processing based on regex membership constraints and equations +// Sequencing abstractions: +// example x in a*, y in b*, z = (ba)+, xy = z +// last(xy) = a => first(xy) = a +// last(z) = a +// first(z) = b +// +// Lookahead +// to help with sequence abstractions? +// +// solve for queues: +// X = ABC +// XX = AABBCC +// XXX = AAABBBCCC +// B = bB' +// C = cC' +// XX = ABCABC +// |A| = |C| => ABC = AAB = BCC +// + +void seq_simplifier::reduce() { + if (!m_seq.has_seq()) + return; + elim_unconstrained elim(m, m_fmls); + elim.init_nodes(); + eliminate(elim); +} + +bool seq_simplifier::invert(elim_unconstrained& elim, app* t, expr_ref& r) { + if (!m_seq.str.is_concat(t)) + return false; + + + auto is_valid_parent = [&](expr* p) { + return elim.get_node(p).m_refcount > 0 && elim.get_node(p).m_term == elim.get_node(p).m_orig; + }; + + expr* first = nullptr, *second = nullptr; + expr* a, *b, *c, *d, *e; + for (expr* p : elim.get_node(t).m_parents) { + if (!is_valid_parent(p)) + continue; + if (!m_seq.str.is_concat(p, a, b)) + return false; + if (!m_seq.str.is_concat(b, c, d)) + c = b; + if (first && (first != a || second != c)) + return false; + first = a; + second = c; + // parents of b are all of the form (seq.++ a b) + for (expr* q : elim.get_node(b).m_parents) { + if (!is_valid_parent(q)) + continue; + if (!m_seq.str.is_concat(q, d, e)) + return false; + if (e != b || d != a) + return false; + } + } + + if (!first) + return false; + + expr* x = nullptr; + // replace p := a ++ b ++ c by x ++ c + for (expr* p : elim.get_node(t).m_parents) { + if (!is_valid_parent(p)) + continue; + VERIFY(m_seq.str.is_concat(p, a, b)); + if (m_seq.str.is_concat(b, c, d)) + r = m_seq.str.mk_concat(x, d); + else + r = x; + // p := r + } + + return false; +} + +void seq_simplifier::eliminate(elim_unconstrained& elim) { +#if 0 + while (!elim.m_heap.empty()) { + expr_ref r(m); + int v = elim.m_heap.erase_min(); + node& n = elim.get_node(v); + if (n.m_refcount == 0) + continue; + if (n.m_parents.empty()) { + n.m_refcount = 0; + continue; + } + expr* e = elim.get_parent(v); + IF_VERBOSE(11, for (expr* p : n.m_parents) verbose_stream() << "parent " << mk_bounded_pp(p, m) << " @ " << get_node(p).m_refcount << "\n";); + if (!e || !is_app(e) || !is_ground(e)) { + n.m_refcount = 0; + continue; + } + app* t = to_app(e); + bool inverted = invert(elim, t, r); + n.m_refcount = 0; + if (!inverted) { + IF_VERBOSE(11, verbose_stream() << "not inverted " << mk_bounded_pp(e, m) << "\n"); + continue; + } + + TRACE("elim_unconstrained", tout << mk_pp(t, m) << " -> " << r << "\n"); + SASSERT(r->get_sort() == t->get_sort()); + elim.m_stats.m_num_eliminated++; + elim.m_trail.push_back(r); + SASSERT(r); + elim.gc(e); + elim.invalidate_parents(e); + elim.freeze_rec(r); + + elim.m_root.setx(r->get_id(), e->get_id(), UINT_MAX); + elim.get_node(e).m_term = r; + elim.get_node(e).m_proof = pr; + elim.get_node(e).m_refcount++; + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); + SASSERT(!elim.m_heap.contains(root(e))); + if (is_uninterp_const(r)) + elim.m_heap.insert(root(e)); + else + elim.m_created_compound = true; + + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(get_node(v).m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << elim.get_node(e).m_refcount << "\n";); + + } + +#endif +} + diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h new file mode 100644 index 00000000000..11d90476135 --- /dev/null +++ b/src/ast/simplifiers/seq_simplifier.h @@ -0,0 +1,36 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + seq_simplifier.h + +Abstract: + + Global simplifier for sequences + +Author: + + Nikolaj Bjorner (nbjorner) 2023-03-12. + +--*/ + + +#pragma once + +#include "ast/seq_decl_plugin.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/simplifiers/elim_unconstrained.h" + + +class seq_simplifier : public dependent_expr_simplifier { + seq_util m_seq; + + void eliminate(elim_unconstrained& elim); + bool invert(elim_unconstrained& elim, app* t, expr_ref& r); +public: + + seq_simplifier(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_seq(m) {} + char const* name() const override { return "seq-simplifier"; } + void reduce() override; +}; From f075dc28826644052977be6dfe228d3f194b6029 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 17:07:48 +0100 Subject: [PATCH 517/597] remove experimental files --- src/ast/simplifiers/seq_simplifier.cpp | 156 ------------------------- src/ast/simplifiers/seq_simplifier.h | 36 ------ 2 files changed, 192 deletions(-) delete mode 100644 src/ast/simplifiers/seq_simplifier.cpp delete mode 100644 src/ast/simplifiers/seq_simplifier.h diff --git a/src/ast/simplifiers/seq_simplifier.cpp b/src/ast/simplifiers/seq_simplifier.cpp deleted file mode 100644 index 907bfaa93ec..00000000000 --- a/src/ast/simplifiers/seq_simplifier.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/*++ -Copyright (c) 2022 Microsoft Corporation - -Module Name: - - seq_simplifier.cpp - -Abstract: - - Global simplifier for sequences - -Author: - - Nikolaj Bjorner (nbjorner) 2023-03-12. - ---*/ - - -#include "ast/simplifiers/seq_simplifier.h" - -// -// if x, y always are in pattern .. ++ x ++ y ++ .. -> z -// x ++ a ++ y = s, where s containing a, x, y are unique -> true -// Parikh pre-processing based on regex membership constraints and equations -// Sequencing abstractions: -// example x in a*, y in b*, z = (ba)+, xy = z -// last(xy) = a => first(xy) = a -// last(z) = a -// first(z) = b -// -// Lookahead -// to help with sequence abstractions? -// -// solve for queues: -// X = ABC -// XX = AABBCC -// XXX = AAABBBCCC -// B = bB' -// C = cC' -// XX = ABCABC -// |A| = |C| => ABC = AAB = BCC -// - -void seq_simplifier::reduce() { - if (!m_seq.has_seq()) - return; - elim_unconstrained elim(m, m_fmls); - elim.init_nodes(); - eliminate(elim); -} - -bool seq_simplifier::invert(elim_unconstrained& elim, app* t, expr_ref& r) { - if (!m_seq.str.is_concat(t)) - return false; - - - auto is_valid_parent = [&](expr* p) { - return elim.get_node(p).m_refcount > 0 && elim.get_node(p).m_term == elim.get_node(p).m_orig; - }; - - expr* first = nullptr, *second = nullptr; - expr* a, *b, *c, *d, *e; - for (expr* p : elim.get_node(t).m_parents) { - if (!is_valid_parent(p)) - continue; - if (!m_seq.str.is_concat(p, a, b)) - return false; - if (!m_seq.str.is_concat(b, c, d)) - c = b; - if (first && (first != a || second != c)) - return false; - first = a; - second = c; - // parents of b are all of the form (seq.++ a b) - for (expr* q : elim.get_node(b).m_parents) { - if (!is_valid_parent(q)) - continue; - if (!m_seq.str.is_concat(q, d, e)) - return false; - if (e != b || d != a) - return false; - } - } - - if (!first) - return false; - - expr* x = nullptr; - // replace p := a ++ b ++ c by x ++ c - for (expr* p : elim.get_node(t).m_parents) { - if (!is_valid_parent(p)) - continue; - VERIFY(m_seq.str.is_concat(p, a, b)); - if (m_seq.str.is_concat(b, c, d)) - r = m_seq.str.mk_concat(x, d); - else - r = x; - // p := r - } - - return false; -} - -void seq_simplifier::eliminate(elim_unconstrained& elim) { -#if 0 - while (!elim.m_heap.empty()) { - expr_ref r(m); - int v = elim.m_heap.erase_min(); - node& n = elim.get_node(v); - if (n.m_refcount == 0) - continue; - if (n.m_parents.empty()) { - n.m_refcount = 0; - continue; - } - expr* e = elim.get_parent(v); - IF_VERBOSE(11, for (expr* p : n.m_parents) verbose_stream() << "parent " << mk_bounded_pp(p, m) << " @ " << get_node(p).m_refcount << "\n";); - if (!e || !is_app(e) || !is_ground(e)) { - n.m_refcount = 0; - continue; - } - app* t = to_app(e); - bool inverted = invert(elim, t, r); - n.m_refcount = 0; - if (!inverted) { - IF_VERBOSE(11, verbose_stream() << "not inverted " << mk_bounded_pp(e, m) << "\n"); - continue; - } - - TRACE("elim_unconstrained", tout << mk_pp(t, m) << " -> " << r << "\n"); - SASSERT(r->get_sort() == t->get_sort()); - elim.m_stats.m_num_eliminated++; - elim.m_trail.push_back(r); - SASSERT(r); - elim.gc(e); - elim.invalidate_parents(e); - elim.freeze_rec(r); - - elim.m_root.setx(r->get_id(), e->get_id(), UINT_MAX); - elim.get_node(e).m_term = r; - elim.get_node(e).m_proof = pr; - elim.get_node(e).m_refcount++; - IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); - SASSERT(!elim.m_heap.contains(root(e))); - if (is_uninterp_const(r)) - elim.m_heap.insert(root(e)); - else - elim.m_created_compound = true; - - IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(get_node(v).m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << elim.get_node(e).m_refcount << "\n";); - - } - -#endif -} - diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h deleted file mode 100644 index 11d90476135..00000000000 --- a/src/ast/simplifiers/seq_simplifier.h +++ /dev/null @@ -1,36 +0,0 @@ -/*++ -Copyright (c) 2022 Microsoft Corporation - -Module Name: - - seq_simplifier.h - -Abstract: - - Global simplifier for sequences - -Author: - - Nikolaj Bjorner (nbjorner) 2023-03-12. - ---*/ - - -#pragma once - -#include "ast/seq_decl_plugin.h" -#include "ast/simplifiers/dependent_expr_state.h" -#include "ast/simplifiers/elim_unconstrained.h" - - -class seq_simplifier : public dependent_expr_simplifier { - seq_util m_seq; - - void eliminate(elim_unconstrained& elim); - bool invert(elim_unconstrained& elim, app* t, expr_ref& r); -public: - - seq_simplifier(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_seq(m) {} - char const* name() const override { return "seq-simplifier"; } - void reduce() override; -}; From 53ca65a62ec2bc441784f7e2ebff1308a0d3c0ef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 18:55:40 +0100 Subject: [PATCH 518/597] fix unsound rewrite --- src/ast/rewriter/seq_rewriter.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index ea3e16f6efa..6fd518cb0af 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -5954,6 +5954,7 @@ bool seq_rewriter::reduce_non_overlap(expr_ref_vector& ls, expr_ref_vector& rs, */ bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { ptr_buffer es; + if (ls.empty() || rs.empty()) return true; es.append(ls.size(), ls.data()); @@ -5976,7 +5977,12 @@ bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, if (!is_unit_value(r)) return true; } - return any_of(es, [&](expr* e) { return is_unit_value(e); }); + if (es.empty()) + return true; + for (expr* e : es) + if (!is_unit_value(e)) + return true; + return false; } bool seq_rewriter::reduce_subsequence(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { From 2683a2d6ed9039ddd1a59aeccbdd91f67f7b708a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2023 08:49:33 +0100 Subject: [PATCH 519/597] fix #6637 --- src/math/lp/nla_divisions.cpp | 5 +++-- src/sat/smt/euf_proof_checker.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index 91b674d58c4..cbb30d9d9f1 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -58,10 +58,11 @@ namespace nla { auto monotonicity1 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& q1, auto& q1val, auto x2, auto& x2val, auto y2, auto& y2val, auto& q2, auto& q2val) { - if (y1val >= y2val && y2val > 0 && x1val <= x2val && q1val > q2val) { - new_lemma lemma(c, "y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2"); + if (y1val >= y2val && y2val > 0 && 0 <= x1val && x1val <= x2val && q1val > q2val) { + new_lemma lemma(c, "y1 >= y2 > 0 & 0 <= x1 <= x2 => x1/y1 <= x2/y2"); lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); lemma |= ineq(y2, llc::LE, 0); + lemma |= ineq(x1, llc::LT, 0); lemma |= ineq(term(x1, rational(-1), x2), llc::GT, 0); lemma |= ineq(term(q1, rational(-1), q2), llc::LE, 0); return true; diff --git a/src/sat/smt/euf_proof_checker.h b/src/sat/smt/euf_proof_checker.h index 9a84015e4b7..d84e4d19fae 100644 --- a/src/sat/smt/euf_proof_checker.h +++ b/src/sat/smt/euf_proof_checker.h @@ -35,7 +35,7 @@ namespace euf { virtual bool check(app* jst) = 0; virtual expr_ref_vector clause(app* jst) = 0; virtual void register_plugins(theory_checker& pc) = 0; - virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.reset(); v.append(this->clause(jst)); return false; } + virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.append(this->clause(jst)); return false; } }; class theory_checker { From 03a44803b6da631cf7d3773319685e0c0f9fb0da Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2023 13:38:02 +0100 Subject: [PATCH 520/597] fix #6635 --- src/smt/theory_lra.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 3eda1ddff43..568143d36dc 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -436,6 +436,9 @@ class theory_lra::imp { if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); if (m_nla && !a.is_numeral(n2)) { // shortcut to create non-linear division axioms. + internalize_term(to_app(n)); + internalize_term(to_app(n1)); + internalize_term(to_app(n2)); theory_var q = mk_var(n); theory_var x = mk_var(n1); theory_var y = mk_var(n2); @@ -443,6 +446,9 @@ class theory_lra::imp { } if (a.is_numeral(n2) && a.is_bounded(n1)) { ensure_nla(); + internalize_term(to_app(n)); + internalize_term(to_app(n1)); + internalize_term(to_app(n2)); theory_var q = mk_var(n); theory_var x = mk_var(n1); theory_var y = mk_var(n2); @@ -1512,11 +1518,9 @@ class theory_lra::imp { } } TRACE("arith", - for (theory_var v = 0; v < sz; ++v) { - if (th.is_relevant_and_shared(get_enode(v))) { + for (theory_var v = 0; v < sz; ++v) + if (th.is_relevant_and_shared(get_enode(v))) tout << "v" << v << " "; - } - } tout << "\n"; ); if (!vars.empty()) { lp().random_update(vars.size(), vars.data()); From 50bd6efea4baba343afffa26f742b49a47b30970 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2023 14:00:09 +0100 Subject: [PATCH 521/597] fix #6624 --- src/smt/theory_array_full.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index a4876ab4d81..079c2f62e0b 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -834,8 +834,8 @@ namespace smt { } for (enode* n : m_lambdas) for (enode* p : n->get_parents()) - if (!ctx.is_beta_redex(p, n)) { - TRACE("array", tout << "not a beta redex " << enode_pp(p, ctx) << "\n"); + if (!is_default(p) && !ctx.is_beta_redex(p, n)) { + TRACE("array", tout << "lambda is not a beta redex " << enode_pp(p, ctx) << "\n"); return true; } return false; From 9ca0faa0918c25656bda75c86e9df32b53e99d5b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 Mar 2023 18:13:44 +0100 Subject: [PATCH 522/597] enable interactive example --- src/tactic/core/eliminate_predicates_tactic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index c2eb90742e1..51bb4a6c3c9 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -30,7 +30,7 @@ resolution. the predicate `p` occurs once positively. All negative occurrences of `p` are resolved against this positive occurrence. The result of resolution is a set of equalities between arguments to `p`. The function `f` is replaced by a partial solution. -``` +```z3 (declare-fun f (Int Int Int) Int) (declare-fun p (Int) Bool) (declare-const a Int) From cd2ea6b703216eaa68b3c86ee182ae443431f882 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 Mar 2023 18:14:08 +0100 Subject: [PATCH 523/597] add parameter access to C++ API --- src/api/c++/z3++.h | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 52d5e657394..88b520147d3 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -151,6 +151,7 @@ namespace z3 { } + /** \brief A Context manages all other Z3 objects, global configuration options, etc. */ @@ -750,6 +751,7 @@ namespace z3 { func_decl_vector recognizers(); }; + /** \brief Function declaration (aka function definition). It is the signature of interpreted and uninterpreted functions in Z3. The basic building block in Z3 is the function application. @@ -770,6 +772,8 @@ namespace z3 { sort range() const { Z3_sort r = Z3_get_range(ctx(), *this); check_error(); return sort(ctx(), r); } symbol name() const { Z3_symbol s = Z3_get_decl_name(ctx(), *this); check_error(); return symbol(ctx(), s); } Z3_decl_kind decl_kind() const { return Z3_get_decl_kind(ctx(), *this); } + unsigned num_parameters() const { return Z3_get_decl_num_parameters(ctx(), *this); } + func_decl transitive_closure(func_decl const&) { Z3_func_decl tc = Z3_mk_transitive_closure(ctx(), *this); check_error(); return func_decl(ctx(), tc); @@ -2687,6 +2691,43 @@ namespace z3 { return out; } + /** + \brief class for auxiliary parameters associated with func_decl + The class is initialized with a func_decl or application expression and an index + The accessor get_expr, get_sort, ... is available depending on the value of kind(). + The caller is responsible to check that the kind of the parameter aligns with the call (get_expr etc). + + Parameters are available on some declarations to contain additional information that is not passed as + arguments when a function is applied to arguments. For example, bit-vector extraction has two + integer parameters. Array map has a function parameter. + */ + class parameter { + Z3_parameter_kind m_kind; + func_decl m_decl; + unsigned m_index; + context& ctx() const { return m_decl.ctx(); } + void check_error() const { ctx().check_error(); } + public: + parameter(func_decl const& d, unsigned idx) : m_decl(d), m_index(idx) { + if (ctx().enable_exceptions() && idx >= d.num_parameters()) + Z3_THROW(exception("parameter index is out of bounds")); + m_kind = Z3_get_decl_parameter_kind(ctx(), d, idx); + } + parameter(expr const& e, unsigned idx) : m_decl(e.decl()), m_index(idx) { + if (ctx().enable_exceptions() && idx >= m_decl.num_parameters()) + Z3_THROW(exception("parameter index is out of bounds")); + m_kind = Z3_get_decl_parameter_kind(ctx(), m_decl, idx); + } + Z3_parameter_kind kind() const { return m_kind; } + expr get_expr() const { Z3_ast a = Z3_get_decl_ast_parameter(ctx(), m_decl, m_index); check_error(); return expr(ctx(), a); } + sort get_sort() const { Z3_sort s = Z3_get_decl_sort_parameter(ctx(), m_decl, m_index); check_error(); return sort(ctx(), s); } + func_decl get_decl() const { Z3_func_decl f = Z3_get_decl_func_decl_parameter(ctx(), m_decl, m_index); check_error(); return func_decl(ctx(), f); } + symbol get_symbol() const { Z3_symbol s = Z3_get_decl_symbol_parameter(ctx(), m_decl, m_index); check_error(); return symbol(ctx(), s); } + std::string get_rational() const { Z3_string s = Z3_get_decl_rational_parameter(ctx(), m_decl, m_index); check_error(); return s; } + double get_double() const { double d = Z3_get_decl_double_parameter(ctx(), m_decl, m_index); check_error(); return d; } + int get_int() const { int i = Z3_get_decl_int_parameter(ctx(), m_decl, m_index); check_error(); return i; } + }; + class solver : public object { Z3_solver m_solver; From ce501e0b6e6bab4c2d88f6eff434561f172920bf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 Mar 2023 17:37:59 -0700 Subject: [PATCH 524/597] #6646 - always enable special-relations theory to deal with default setting and push - fix bugs related to equality and transitivity. --- src/smt/smt_setup.cpp | 4 +-- src/smt/theory_special_relations.cpp | 39 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 854ddb9e0ec..7699fc3a5d2 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -802,7 +802,7 @@ namespace smt { setup_dl(); setup_seq_str(st); setup_fpa(); - if (st.m_has_sr) setup_special_relations(); + setup_special_relations(); } void setup::setup_unknown(static_features & st) { @@ -818,7 +818,7 @@ namespace smt { setup_seq_str(st); setup_fpa(); setup_recfuns(); - if (st.m_has_sr) setup_special_relations(); + setup_special_relations(); return; } diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 9113f189e47..245a2ee5801 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -71,7 +71,7 @@ namespace smt { ensure_var(v1); ensure_var(v2); literal_vector ls; - ls.push_back(l); + ls.push_back(l); return m_graph.add_non_strict_edge(v1, v2, ls) && m_graph.add_non_strict_edge(v2, v1, ls); } @@ -130,6 +130,7 @@ namespace smt { } bool theory_special_relations::internalize_term(app * term) { + verbose_stream() << mk_pp(term, m) << "\n"; return false; } @@ -156,9 +157,8 @@ namespace smt { } theory_var theory_special_relations::mk_var(expr* e) { - if (!ctx.e_internalized(e)) { + if (!ctx.e_internalized(e)) ctx.internalize(e, false); - } enode * n = ctx.get_enode(e); theory_var v = n->get_th_var(get_id()); if (null_theory_var == v) { @@ -582,18 +582,18 @@ namespace smt { lbool theory_special_relations::final_check_po(relation& r) { for (atom* ap : r.m_asserted_atoms) { atom& a = *ap; - if (!a.phase() && r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) { - // v1 !-> v2 - // find v1 -> v3 -> v4 -> v2 path - r.m_explanation.reset(); - unsigned timestamp = r.m_graph.get_timestamp(); - bool found_path = r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r); - if (found_path) { - TRACE("special_relations", tout << "check po conflict\n";); - r.m_explanation.push_back(a.explanation()); - set_conflict(r); - return l_false; - } + if (a.phase()) + continue; + // v1 !-> v2 + // find v1 -> v3 -> v4 -> v2 path + r.m_explanation.reset(); + unsigned timestamp = r.m_graph.get_timestamp(); + bool found_path = a.v1() == a.v2() || r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r); + if (found_path) { + TRACE("special_relations", tout << "check po conflict\n";); + r.m_explanation.push_back(a.explanation()); + set_conflict(r); + return l_false; } } return l_true; @@ -601,9 +601,8 @@ namespace smt { void theory_special_relations::propagate() { if (m_can_propagate) { - for (auto const& kv : m_relations) { + for (auto const& kv : m_relations) propagate(*kv.m_value); - } m_can_propagate = false; } } @@ -1124,12 +1123,12 @@ namespace smt { } void theory_special_relations::display(std::ostream & out) const { - if (m_relations.empty()) return; + if (m_relations.empty()) + return; out << "Theory Special Relations\n"; display_var2enode(out); - for (auto const& kv : m_relations) { + for (auto const& kv : m_relations) kv.m_value->display(*this, out); - } } void theory_special_relations::collect_asserted_po_atoms(vector>& atoms) const { From 8a3a3dc91bf228209615d5c563c7713e47713873 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 26 Mar 2023 15:31:37 -0700 Subject: [PATCH 525/597] fix #6648 --- src/sat/smt/pb_internalize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index 1a83dbc87ca..17f2bd4ded6 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -119,7 +119,7 @@ namespace pb { else { bool_var v = s().add_var(true); literal lit(v, sign); - add_pb_ge(v, sign, wlits, k.get_unsigned()); + add_pb_ge(v, false, wlits, k.get_unsigned()); TRACE("ba", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } @@ -146,7 +146,7 @@ namespace pb { else { sat::bool_var v = s().add_var(true); sat::literal lit(v, sign); - add_pb_ge(v, sign, wlits, k.get_unsigned()); + add_pb_ge(v, false, wlits, k.get_unsigned()); TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } From b4ad747e0b1ab7926f9863910b39a9c642b9aad8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 09:00:38 -0700 Subject: [PATCH 526/597] fix #6644 --- examples/java/JavaExample.java | 21 +++++++++++++++++++++ src/api/java/Context.java | 15 ++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 150efd54541..9297dee47bc 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2259,6 +2259,24 @@ public void translationExample() { System.out.println(e1.equals(e3)); } + public void stringExample() { + System.out.println("String example"); + Context ctx = new Context(); + var a = ctx.mkToRe(ctx.mkString("abcd")); + var b = ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())); + System.out.println(a); + System.out.println(b); + System.out.println(a.getSort()); + System.out.println(b.getSort()); + var c = ctx.mkConcat(ctx.mkToRe(ctx.mkString("abc")), + ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkEmptyRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkAllcharRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkToRe(ctx.mkString("d"))); + System.out.println(c); + + } + public static void main(String[] args) { JavaExample p = new JavaExample(); @@ -2274,12 +2292,15 @@ public static void main(String[] args) System.out.print("Z3 Full Version String: "); System.out.println(Version.getFullVersion()); + p.stringExample(); + p.simpleExample(); { // These examples need model generation turned on. HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); + p.optimizeExample(ctx); p.basicTests(ctx); diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 06b312303e2..da242630500 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2175,10 +2175,10 @@ public final SeqExpr mkReplace(Expr> s, Expr ReExpr mkToRe(Expr> s) + public final ReExpr> mkToRe(Expr> s) { checkContextMatch(s); - return (ReExpr) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); + return (ReExpr>) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); } @@ -2296,7 +2296,7 @@ public final ReExpr mkDiff(Expr> a, Expr * Create the empty regular expression. * Coresponds to re.none */ - public final ReExpr mkEmptyRe(R s) + public final ReExpr mkEmptyRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReEmpty(nCtx(), s.getNativeObject())); } @@ -2305,16 +2305,17 @@ public final ReExpr mkEmptyRe(R s) * Create the full regular expression. * Corresponds to re.all */ - public final ReExpr mkFullRe(R s) + public final ReExpr mkFullRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReFull(nCtx(), s.getNativeObject())); } /** * Create regular expression that accepts all characters + * R has to be a sequence sort. * Corresponds to re.allchar */ - public final ReExpr mkAllcharRe(R s) + public final ReExpr mkAllcharRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReAllchar(nCtx(), s.getNativeObject())); } @@ -2322,10 +2323,10 @@ public final ReExpr mkAllcharRe(R s) /** * Create a range expression. */ - public final ReExpr mkRange(Expr> lo, Expr> hi) + public final ReExpr> mkRange(Expr> lo, Expr> hi) { checkContextMatch(lo, hi); - return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); + return (ReExpr>) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); } /** From ce09c2ea6dc4fbc92acd435c49c7dcb40650f2e2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 09:56:09 -0700 Subject: [PATCH 527/597] fix build --- examples/java/JavaExample.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 9297dee47bc..f6f171c4652 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2262,13 +2262,13 @@ public void translationExample() { public void stringExample() { System.out.println("String example"); Context ctx = new Context(); - var a = ctx.mkToRe(ctx.mkString("abcd")); - var b = ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())); + Expr a = ctx.mkToRe(ctx.mkString("abcd")); + Expr b = ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())); System.out.println(a); System.out.println(b); System.out.println(a.getSort()); System.out.println(b.getSort()); - var c = ctx.mkConcat(ctx.mkToRe(ctx.mkString("abc")), + Expr c = ctx.mkConcat(ctx.mkToRe(ctx.mkString("abc")), ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())), ctx.mkEmptyRe(ctx.mkReSort(ctx.mkStringSort())), ctx.mkAllcharRe(ctx.mkReSort(ctx.mkStringSort())), From 0a59617bac66ceda59f50d67ea036ec75bb72bb7 Mon Sep 17 00:00:00 2001 From: Patrick LaFontaine <32135464+Pat-Lafon@users.noreply.github.com> Date: Mon, 27 Mar 2023 16:04:32 -0400 Subject: [PATCH 528/597] Fix Ocaml bindings FuncEntry to_string (#6639) Hello, I was looking at the different api string conversions for FuncEntry and I believe that the ml version is incorrect? Clearly we want the argument(`c`) to be comma separated from the accumulated string `p`. The current implementation just so happens to have most of the arguments separated, but the order is flipped and one of the commas is misplaced. --- src/api/ml/z3.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index ac21902fe6c..4c8b0322a95 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1542,7 +1542,7 @@ struct let to_string (x:func_entry) = let a = get_args x in - let f c p = (p ^ (Expr.to_string c) ^ ", ") in + let f c p = ((Expr.to_string c) ^ ", " ^ p) in "[" ^ List.fold_right f a ((Expr.to_string (get_value x)) ^ "]") end From f366772f0c1ce34ee1f2fdaae1f8ad5e31609f51 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 09:56:38 -0700 Subject: [PATCH 529/597] use field 'm' for streamlined representation --- src/tactic/core/symmetry_reduce_tactic.cpp | 108 +++++++++------------ 1 file changed, 45 insertions(+), 63 deletions(-) diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index e94e8367973..9c308b60f7c 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -47,13 +47,13 @@ class symmetry_reduce_tactic : public tactic { }; class ac_rewriter { - ast_manager& m_manager; + ast_manager& m; public: - ac_rewriter(ast_manager& m): m_manager(m) {} + ac_rewriter(ast_manager& m): m(m) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if ((f->is_associative() && f->is_commutative()) || - m_manager.is_distinct(f)) { + m.is_distinct(f)) { ptr_buffer buffer; buffer.append(num_args, args); std::sort(buffer.begin(), buffer.end(), ast_lt_proc()); @@ -62,13 +62,13 @@ class ac_rewriter { change = (args[i] != buffer[i]); } if (change) { - result = m().mk_app(f, num_args, buffer.begin()); + result = m.mk_app(f, num_args, buffer.begin()); return BR_DONE; } } else if (f->is_commutative() && num_args == 2 && args[0]->get_id() > args[1]->get_id()) { expr* args2[2] = { args[1], args[0] }; - result = m().mk_app(f, num_args, args2); + result = m.mk_app(f, num_args, args2); return BR_DONE; } return BR_FAILED; @@ -76,10 +76,8 @@ class ac_rewriter { void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) - result = m().mk_app(f, num_args, args); + result = m.mk_app(f, num_args, args); } -private: - ast_manager& m() const { return m_manager; } }; @@ -110,13 +108,12 @@ class symmetry_reduce_tactic::imp { typedef ptr_vector term_set; typedef obj_map app_map; typedef u_map > inv_app_map; - ast_manager& m_manager; + ast_manager& m; ac_rewriter_star m_rewriter; scoped_ptr m_replace; - ast_manager& m() const { return m_manager; } public: - imp(ast_manager& m) : m_manager(m), m_rewriter(m) { + imp(ast_manager& m) : m(m), m_rewriter(m) { m_replace = mk_default_expr_replacer(m, false); } @@ -124,10 +121,10 @@ class symmetry_reduce_tactic::imp { void operator()(goal & g) { if (g.inconsistent()) - return; + return; tactic_report report("symmetry-reduce", g); vector > P; - expr_ref fml(m()); + expr_ref fml(m); to_formula(g, fml); app_map occs; compute_occurrences(fml, occs); @@ -149,11 +146,12 @@ class symmetry_reduce_tactic::imp { app* c = select_const(consts, cts); if (!c) break; cts.push_back(c); - expr* mem = mk_member(t, cts); + expr_ref mem(mk_member(t, cts), m); g.assert_expr(mem); num_sym_break_preds++; - TRACE("symmetry_reduce", tout << "member predicate: " << mk_pp(mem, m()) << "\n";); - fml = m().mk_and(fml.get(), mem); + TRACE("symmetry_reduce", tout << "member predicate: " << mem << "\n";); + + fml = m.mk_and(fml.get(), mem); normalize(fml); } } @@ -167,7 +165,7 @@ class symmetry_reduce_tactic::imp { for (unsigned i = 0; i < g.size(); ++i) { conjs.push_back(g.form(i)); } - fml = m().mk_and(conjs.size(), conjs.data()); + fml = m.mk_and(conjs.size(), conjs.data()); normalize(fml); } @@ -184,28 +182,17 @@ class symmetry_reduce_tactic::imp { // compute_siblings(fml, coloring); compute_inv_app(coloring, inv_color); - inv_app_map::iterator it = inv_color.begin(), end = inv_color.end(); - for (; it != end; ++it) { - if (it->m_value.size() < 2) { + for (auto const& [k, v] : inv_color) { + if (v.size() < 2) continue; - } - VERIFY(occs.find(it->m_value[0], num_occs)); - if (num_occs < 2) { + VERIFY(occs.find(v[0], num_occs)); + if (num_occs < 2) continue; - } - bool is_const = true; - for (unsigned j = 0; is_const && j < it->m_value.size(); ++j) { - is_const = it->m_value[j]->get_num_args() == 0; - } - if (!is_const) { + bool is_const = all_of(v, [&](app* a) { return a->get_num_args() == 0; }); + if (!is_const) continue; - } - P.push_back(it->m_value); - TRACE("symmetry_reduce", - for (unsigned i = 0; i < it->m_value.size(); ++i) { - tout << mk_pp(it->m_value[i], m()) << " "; - } - tout << "\n";); + P.push_back(v); + TRACE("symmetry_reduce", for (app * a : v) tout << mk_pp(a, m) << " "; tout << "\n";); } } @@ -426,14 +413,14 @@ class symmetry_reduce_tactic::imp { tout << "Not symmetric: "; } for (unsigned i = 0; i < p.size(); ++i) { - tout << mk_pp(p[i], m()) << " "; + tout << mk_pp(p[i], m) << " "; } tout << "\n";); return result; } bool check_swap(expr* fml, app* t1, app* t2) { - expr_substitution sub(m()); + expr_substitution sub(m); sub.insert(t1, t2); sub.insert(t2, t1); m_replace->set_substitution(&sub); @@ -441,7 +428,7 @@ class symmetry_reduce_tactic::imp { } bool check_cycle(expr* fml, permutation& p) { - expr_substitution sub(m()); + expr_substitution sub(m); for (unsigned i = 0; i + 1 < p.size(); ++i) { sub.insert(p[i], p[i+1]); } @@ -451,15 +438,15 @@ class symmetry_reduce_tactic::imp { } bool check_substitution(expr* t) { - expr_ref r(m()); + expr_ref r(m); (*m_replace)(t, r); normalize(r); return t == r.get(); } void normalize(expr_ref& r) { - proof_ref pr(m()); - expr_ref result(m()); + proof_ref pr(m); + expr_ref result(m); m_rewriter(r.get(), result, pr); r = result; } @@ -473,7 +460,7 @@ class symmetry_reduce_tactic::imp { while (!todo.empty()) { fml = todo.back(); todo.pop_back(); - if (m().is_and(fml)) { + if (m.is_and(fml)) { todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); } else if (is_range_restriction(fml, p, t)) { @@ -482,13 +469,13 @@ class symmetry_reduce_tactic::imp { } } bool is_range_restriction(expr* form, term_set const& C, app*& t) { - if (!m().is_or(form)) return false; + if (!m.is_or(form)) return false; unsigned sz = to_app(form)->get_num_args(); t = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* e = to_app(form)->get_arg(i); expr* e1, *e2; - if (!m().is_eq(e, e1, e2)) return false; + if (!m.is_eq(e, e1, e2)) return false; if (!is_app(e1) || !is_app(e2)) return false; app* a1 = to_app(e1), *a2 = to_app(e2); if (C.contains(a1) && (t == nullptr || t == a2)) { @@ -515,13 +502,9 @@ class symmetry_reduce_tactic::imp { num_occurrences(app_map& occs): m_occs(occs) {} void operator()(app* n) { m_occs.insert_if_not_there(n, 0); - unsigned sz = n->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = n->get_arg(i); - if (is_app(arg)) { - m_occs.insert_if_not_there(to_app(arg), 0)++; - } - } + for (expr* arg : *n) + if (is_app(arg)) + m_occs.insert_if_not_there(to_app(arg), 0)++; } void operator()(quantifier * n) {} void operator()(var * n) {} @@ -540,7 +523,7 @@ class symmetry_reduce_tactic::imp { unsigned weight = 0, weight1 = 0; VERIFY(occs.find(t, weight)); unsigned cts_delta = compute_cts_delta(t, cts, consts); - TRACE("symmetry_reduce", tout << mk_pp(t, m()) << " " << weight << " " << cts_delta << "\n";); + TRACE("symmetry_reduce", tout << mk_pp(t, m) << " " << weight << " " << cts_delta << "\n";); for (unsigned i = 1; i < T.size(); ++i) { app* t1 = T[i]; VERIFY(occs.find(t1, weight1)); @@ -548,7 +531,7 @@ class symmetry_reduce_tactic::imp { continue; } unsigned cts_delta1 = compute_cts_delta(t1, cts, consts); - TRACE("symmetry_reduce", tout << mk_pp(t1, m()) << " " << weight1 << " " << cts_delta1 << "\n";); + TRACE("symmetry_reduce", tout << mk_pp(t1, m) << " " << weight1 << " " << cts_delta1 << "\n";); if ((t->get_num_args() == t1->get_num_args() && (weight1 > weight || cts_delta1 < cts_delta)) || t->get_num_args() > t1->get_num_args()) { cts_delta = cts_delta1; @@ -577,15 +560,15 @@ class symmetry_reduce_tactic::imp { member_of mem(consts, cts); for_each_expr(mem, t); TRACE("symmetry_reduce", - tout << "Term: " << mk_pp(t, m()) << "\n"; + tout << "Term: " << mk_pp(t, m) << "\n"; tout << "Support set: "; for (unsigned i = 0; i < consts.size(); ++i) { - tout << mk_pp(consts[i], m()) << " "; + tout << mk_pp(consts[i], m) << " "; } tout << "\n"; tout << "Constants: "; for (unsigned i = 0; i < cts.size(); ++i) { - tout << mk_pp(cts[i], m()) << " "; + tout << mk_pp(cts[i], m) << " "; } tout << "\n"; ); @@ -606,15 +589,14 @@ class symmetry_reduce_tactic::imp { app* select_const(term_set const& A, term_set const& B) { unsigned j; for (j = 0; j < A.size() && B.contains(A[j]); ++j); - return (j == A.size())?0:A[j]; + return (j == A.size())? nullptr:A[j]; } 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 mk_or(m(), eqs.size(), eqs.data()); + expr_ref_vector eqs(m); + for (expr* e : C) + eqs.push_back(m.mk_eq(t, e)); + return mk_or(eqs); } }; From adec9372960c271d1e0b7f67706e8c926a333ead Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 14:02:15 -0700 Subject: [PATCH 530/597] fix #6650 --- src/tactic/core/symmetry_reduce_tactic.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index 9c308b60f7c..9ad12d504dc 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -146,9 +146,10 @@ class symmetry_reduce_tactic::imp { app* c = select_const(consts, cts); if (!c) break; cts.push_back(c); - expr_ref mem(mk_member(t, cts), m); + expr_ref mem = mk_member(t, cts); g.assert_expr(mem); num_sym_break_preds++; + TRACE("symmetry_reduce", tout << "member predicate: " << mem << "\n";); fml = m.mk_and(fml.get(), mem); @@ -592,7 +593,7 @@ class symmetry_reduce_tactic::imp { return (j == A.size())? nullptr:A[j]; } - expr* mk_member(app* t, term_set const& C) { + expr_ref mk_member(app* t, term_set const& C) { expr_ref_vector eqs(m); for (expr* e : C) eqs.push_back(m.mk_eq(t, e)); From fe348b84c96a50c64979b58feac7bff5e8bb57ef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 16:20:33 -0700 Subject: [PATCH 531/597] fix #6652 --- src/tactic/aig/aig_tactic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tactic/aig/aig_tactic.cpp b/src/tactic/aig/aig_tactic.cpp index 9c5390c160f..8027e6484c3 100644 --- a/src/tactic/aig/aig_tactic.cpp +++ b/src/tactic/aig/aig_tactic.cpp @@ -103,6 +103,7 @@ class aig_tactic : public tactic { new_f = conj; g->assert_expr(new_f); } + g->elim_true(); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { From 130400d76e3cc37e4b6f3a9ed6e8a4781b7f3599 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 28 Mar 2023 08:55:52 -0700 Subject: [PATCH 532/597] Remove non feasible costs (#6653) * before rm lu Signed-off-by: Lev Nachmanson * rm get_column_in_lu_mode * rm lu Signed-off-by: Lev Nachmanson * rm lu Signed-off-by: Lev Nachmanson * rm_lp Signed-off-by: Lev Nachmanson * rm_lu * rm lu * rm lu Signed-off-by: Lev Nachmanson * rm lu Signed-off-by: Lev Nachmanson * rm lu * rm lu Signed-off-by: Lev Nachmanson * rm lu Signed-off-by: Lev Nachmanson * rm lu * cleanup * rm breakpoints * rm dealing with doubles * Revert "rm dealing with doubles" This reverts commit 547254abe786c80231ca78bcd245e6ddb5a15c47. * rm lu Signed-off-by: Lev Nachmanson * rm lu * rm lu * rm scaler * rm square_sparse_matrix * more cleanup * rm dead code * rp precise * remove many methods dealing with double Signed-off-by: Lev Nachmanson * rm lu related fields from lp_core_solver_base.h * remove dead code Signed-off-by: Lev Nachmanson * more dead code removal * remove more dead code * more dead code * rm dead code * more dead code * fix lp_tst * more dead code * replace lp_assert(false) with UNREACHABLE * rm non feas costs Signed-off-by: Lev Nachmanson * fix the build Signed-off-by: Lev Nachmanson --------- Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver_def.h | 1 - src/math/lp/lar_solver.cpp | 23 +--- src/math/lp/lp_core_solver_base.cpp | 2 - src/math/lp/lp_core_solver_base.h | 13 +- src/math/lp/lp_core_solver_base_def.h | 56 --------- src/math/lp/lp_primal_core_solver.cpp | 1 - src/math/lp/lp_primal_core_solver.h | 36 +----- src/math/lp/lp_primal_core_solver_def.h | 112 ------------------ .../lp/lp_primal_core_solver_tableau_def.h | 76 ++---------- 9 files changed, 21 insertions(+), 299 deletions(-) diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index e22d77c1b30..550b6fe3674 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -40,7 +40,6 @@ void lar_core_solver::prefix_r() { if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { m_r_solver.m_costs.resize(m_r_solver.m_n()); m_r_solver.m_d.resize(m_r_solver.m_n()); - m_r_solver.set_using_infeas_costs(true); } } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9fab5e4371d..21eec31d839 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -191,9 +191,11 @@ namespace lp { stats().m_max_rows = A_r().row_count(); if (strategy_is_undecided()) decide_on_strategy_and_adjust_initial_state(); - + auto strategy_was = settings().simplex_strategy(); + settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows); m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; auto ret = solve(); + settings().set_simplex_strategy(strategy_was); return ret; } @@ -355,7 +357,6 @@ namespace lp { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); move_non_basic_columns_to_bounds(false); auto& rslv = m_mpq_lar_core_solver.m_r_solver; - rslv.set_using_infeas_costs(false); lp_assert(costs_are_zeros_for_r_solver()); lp_assert(reduced_costs_are_zeroes_for_r_solver()); rslv.m_costs.resize(A_r().column_count(), zero_of_type()); @@ -490,11 +491,11 @@ namespace lp { lp_status lar_solver::maximize_term(unsigned j_or_term, impq& term_max) { TRACE("lar_solver", print_values(tout);); + lar_term term = get_term_to_maximize(j_or_term); if (term.is_empty()) { return lp_status::UNBOUNDED; } - impq prev_value; auto backup = m_mpq_lar_core_solver.m_r_x; if (m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()) { @@ -710,14 +711,6 @@ namespace lp { void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bounds) update_x_and_inf_costs_for_column_with_changed_bounds(j); - - if (tableau_with_costs()) { - if (m_mpq_lar_core_solver.m_r_solver.using_infeas_costs()) { - for (unsigned j : m_basic_columns_with_changed_cost) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - } } @@ -1344,14 +1337,6 @@ namespace lp { for (unsigned j : became_feas) m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_set(j); - - if (use_tableau_costs()) { - for (unsigned j : became_feas) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - for (unsigned j : basic_columns_with_changed_cost) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } } bool lar_solver::model_is_int_feasible() const { diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 9a4c375f638..f1ae95ea0b9 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -65,8 +65,6 @@ template bool lp::lp_core_solver_base::pivot_column_tableau(un template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base >::inf_set_is_correct() const; template bool lp::lp_core_solver_base::inf_set_is_correct() const; -template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; -template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index fc167324234..fb0c2850795 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -63,15 +63,12 @@ class lp_core_solver_base { bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } private: u_set m_inf_set; - bool m_using_infeas_costs; public: const u_set& inf_set() const { return m_inf_set; } u_set& inf_set() { return m_inf_set; } void inf_set_increase_size_by_one() { m_inf_set.increase_size_by_one(); } bool inf_set_contains(unsigned j) const { return m_inf_set.contains(j); } - unsigned inf_set_size() const { return m_inf_set.size(); } - bool using_infeas_costs() const { return m_using_infeas_costs; } - void set_using_infeas_costs(bool val) { m_using_infeas_costs = val; } + unsigned inf_set_size() const { return m_inf_set.size(); } indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A // vector const & m_b; // the right side @@ -198,11 +195,7 @@ class lp_core_solver_base { if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return true; CASSERT("check_static_matrix", m_A.is_correct()); - if (m_using_infeas_costs) { - if (infeasibility_costs_are_correct() == false) { - return false; - } - } + unsigned n = m_A.column_count(); for (unsigned j = 0; j < n; j++) { @@ -236,8 +229,6 @@ class lp_core_solver_base { return below_bound(m_x[p], m_lower_bounds[p]); } - bool infeasibility_costs_are_correct() const; - bool infeasibility_cost_is_correct_for_column(unsigned j) const; bool x_above_lower_bound(unsigned p) const { return above_bound(m_x[p], m_lower_bounds[p]); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index cb0084a168a..8619c926e8d 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -43,7 +43,6 @@ lp_core_solver_base(static_matrix & A, m_iters_with_no_cost_growing(0), m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), - m_using_infeas_costs(false), m_pivot_row(A.column_count()), m_A(A), m_basis(basis), @@ -94,8 +93,6 @@ pivot_to_reduced_costs_tableau(unsigned i, unsigned j) { - - // template void lp_core_solver_base:: // update_index_of_ed() { // m_index_of_ed.clear(); @@ -434,57 +431,4 @@ template bool lp_core_solver_base::remove_from_ba } -template bool -lp_core_solver_base::infeasibility_costs_are_correct() const { - if (! this->m_using_infeas_costs) - return true; - lp_assert(costs_on_nbasis_are_zeros()); - for (unsigned j :this->m_basis) { - if (!infeasibility_cost_is_correct_for_column(j)) { - TRACE("lar_solver", tout << "incorrect cost for column " << j << std::endl;); - return false; - } - if (!is_zero(m_d[j])) { - TRACE("lar_solver", tout << "non zero inf cost for basis j = " << j << std::endl;); - return false; - } - } - return true; -} - -template bool -lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) const { - T r = -one_of_type(); - - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - return (this->m_costs[j] == r); - } - if (this->x_below_low_bound(j)) { - return (this->m_costs[j] == -r); - } - return is_zero(this->m_costs[j]); - - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - return this->m_costs[j] == -r; - } - return is_zero(this->m_costs[j]); - - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - return this->m_costs[j] == r; - } - return is_zero(this->m_costs[j]); - case column_type::free_column: - return is_zero(this->m_costs[j]); - default: - UNREACHABLE(); - return true; - } -} - - } diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index 22042668da3..efbfd27e1cc 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -33,6 +33,5 @@ template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); -template void lp::lp_primal_core_solver >::update_inf_cost_for_column_tableau(unsigned); } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 641f6be2160..207428985bf 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -334,28 +334,12 @@ class lp_primal_core_solver : public lp_core_solver_base { void update_x_tableau_rows(unsigned entering, unsigned leaving, const X &delta) { this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto &c : this->m_A.m_columns[entering]) { - if (leaving != this->m_basis[c.var()]) { - this->add_delta_to_x_and_track_feasibility( - this->m_basis[c.var()], -delta * this->m_A.get_val(c)); - } - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto &c : this->m_A.m_columns[entering]) { - unsigned j = this->m_basis[c.var()]; - if (j != leaving) - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } - } + for (const auto &c : this->m_A.m_columns[entering]) { + if (leaving != this->m_basis[c.var()]) { + this->add_delta_to_x_and_track_feasibility( + this->m_basis[c.var()], -delta * this->m_A.get_val(c)); + } + } } void update_basis_and_x_tableau_rows(int entering, int leaving, X const &tt) { @@ -437,7 +421,6 @@ class lp_primal_core_solver : public lp_core_solver_base { } void decide_on_status_when_cannot_find_entering() { - lp_assert(!need_to_switch_costs()); this->set_status(this->current_x_is_feasible() ? lp_status::OPTIMAL : lp_status::INFEASIBLE); } @@ -628,11 +611,6 @@ class lp_primal_core_solver : public lp_core_solver_base { bool column_is_benefitial_for_entering_basis(unsigned j) const; void init_infeasibility_costs(); - - void init_infeasibility_cost_for_column(unsigned j); - T get_infeasibility_cost_for_column(unsigned j) const; - void init_infeasibility_costs_for_changed_basis_only(); - void print_column(unsigned j, std::ostream &out); void print_bound_info_and_x(unsigned j, std::ostream &out); @@ -652,8 +630,6 @@ class lp_primal_core_solver : public lp_core_solver_base { void init_run_tableau(); void update_x_tableau(unsigned entering, const X &delta); - void update_inf_cost_for_column_tableau(unsigned j); - // the delta is between the old and the new cost (old - new) void update_reduced_cost_for_basic_column_cost_change(const T &delta, unsigned j) { diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 4ee8305fa1a..c3c545fdd8a 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -258,122 +258,10 @@ template void lp_primal_core_solver::find_feas solve(); } -template -void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { - for (unsigned i : this->m_ed.m_index) - init_infeasibility_cost_for_column(this->m_basis[i]); - this->set_using_infeas_costs(true); -} - -template -void lp_primal_core_solver::init_infeasibility_costs() { - lp_assert(this->m_x.size() >= this->m_n()); - lp_assert(this->m_column_types.size() >= this->m_n()); - for (unsigned j = this->m_n(); j--;) - init_infeasibility_cost_for_column(j); - this->set_using_infeas_costs(true); -} -template T -lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const { - if (this->m_basis_heading[j] < 0) { - return zero_of_type(); - } - T ret; - // j is a basis column - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - ret = 1; - } else if (this->x_below_low_bound(j)) { - ret = -1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - ret = -1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - ret = 1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::free_column: - ret = numeric_traits::zero(); - break; - default: - UNREACHABLE(); - ret = numeric_traits::zero(); // does not matter - break; - } - - ret = - ret; - - return ret; -} -// changed m_inf_set too! -template void -lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { - - if (this->m_basis_heading[j] < 0) { - this->m_costs[j] = numeric_traits::zero(); - this->remove_column_from_inf_set(j); - return; - } - // j is a basis column - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - this->m_costs[j] = 1; - } else if (this->x_below_low_bound(j)) { - this->m_costs[j] = -1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - this->m_costs[j] = -1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - this->m_costs[j] = 1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::free_column: - this->m_costs[j] = numeric_traits::zero(); - break; - default: - UNREACHABLE(); - break; - } - - if (numeric_traits::is_zero(this->m_costs[j])) { - this->remove_column_from_inf_set(j); - } else { - this->insert_column_into_inf_set(j); - } - this->m_costs[j] = - this->m_costs[j]; - -} - template void lp_primal_core_solver::print_column(unsigned j, std::ostream & out) { out << this->column_name(j) << " " << j << " " << column_type_to_string(this->m_column_type[j]) << " x = " << this->m_x[j] << " " << "c = " << this->m_costs[j];; diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 241164c023f..898abb1525b 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -96,7 +96,7 @@ unsigned lp_primal_core_solver::solve() { } do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { + if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over( "feas t", * this->m_settings.get_message_ostream())) { return this->total_iterations(); } if (this->m_settings.use_tableau_rows()) { @@ -107,28 +107,12 @@ unsigned lp_primal_core_solver::solve() { TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";); switch (this->get_status()) { case lp_status::OPTIMAL: // check again that we are at optimum - case lp_status::INFEASIBLE: - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - break; - { // precise case - if ((!this->infeasibility_costs_are_correct())) { - init_reduced_costs_tableau(); // forcing recalc - if (choose_entering_column_tableau() == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } - } break; case lp_status::TENTATIVE_UNBOUNDED: UNREACHABLE(); break; case lp_status::UNBOUNDED: - if (this->current_x_is_infeasible()) { - init_reduced_costs_tableau(); - this->set_status(lp_status::UNKNOWN); - } + lp_assert (this->current_x_is_feasible()); break; case lp_status::UNSTABLE: @@ -169,7 +153,7 @@ template void lp_primal_core_solver::advance_on_en lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || m_non_basis_list.back() == static_cast(entering)); - lp_assert(this->using_infeas_costs() || !is_neg(t)); + lp_assert(!is_neg(t)); lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving_tableau(entering, t); @@ -191,11 +175,6 @@ template void lp_primal_core_solver::advance_on_en return; if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { - if (need_to_switch_costs()) { - this->init_reduced_costs_tableau(); - } - - lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); @@ -208,9 +187,7 @@ void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; - if (need_to_switch_costs()) { - init_reduced_costs_tableau(); - } + this->iters_with_no_cost_growing() = 0; } template int lp_primal_core_solver::find_leaving_and_t_tableau(unsigned entering, X & t) { @@ -299,54 +276,19 @@ update_basis_and_x_tableau(int entering, int leaving, X const & tt) { this->change_basis(entering, leaving); return true; } + template void lp_primal_core_solver:: update_x_tableau(unsigned entering, const X& delta) { this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned i = c.var(); - this->add_delta_to_x_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned i = c.var(); - unsigned j = this->m_basis[i]; - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } + for (const auto & c : this->m_A.m_columns[entering]) { + unsigned i = c.var(); + this->add_delta_to_x_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); } } -template void lp_primal_core_solver:: -update_inf_cost_for_column_tableau(unsigned j) { - lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); - - lp_assert(this->using_infeas_costs()); - - T new_cost = get_infeasibility_cost_for_column(j); - T delta = this->m_costs[j] - new_cost; - if (is_zero(delta)) - return; - this->m_costs[j] = new_cost; - update_reduced_cost_for_basic_column_cost_change(delta, j); -} template void lp_primal_core_solver::init_reduced_costs_tableau() { - if (this->current_x_is_infeasible() && !this->using_infeas_costs()) { - init_infeasibility_costs(); - } else if (this->current_x_is_feasible() && this->using_infeas_costs()) { - if (this->m_look_for_feasible_solution_only) - return; - this->m_costs = m_costs_backup; - this->set_using_infeas_costs(false); - } + unsigned size = this->m_basis_heading.size(); for (unsigned j = 0; j < size; j++) { if (this->m_basis_heading[j] >= 0) From 667080710366eab733cbf9ce404a7e7b9cb455b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EC=8A=B9=EC=99=84?= <36997987+Hhro@users.noreply.github.com> Date: Wed, 29 Mar 2023 21:49:33 +0900 Subject: [PATCH 533/597] update ocaml binding to support more string apis (#6656) --- .gitignore | 3 +++ src/api/ml/z3.ml | 12 ++++++++++++ src/api/ml/z3.mli | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/.gitignore b/.gitignore index b0c19a43260..710ce4b7ee4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,12 @@ callgrind.out.* .z3-trace # OCaml generated files *.a +*.o *.cma *.cmo *.cmi +*.cmx +*.byte *.cmxa ocamlz3 # Java generated files diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 4c8b0322a95..98807bedd6f 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1253,7 +1253,9 @@ struct let mk_re_sort = Z3native.mk_re_sort let is_re_sort = Z3native.is_re_sort let mk_string_sort = Z3native.mk_string_sort + let mk_char_sort = Z3native.mk_char_sort let is_string_sort = Z3native.is_string_sort + let is_char_sort = Z3native.is_char_sort let mk_string = Z3native.mk_string let is_string = Z3native.is_string let get_string = Z3native.get_string @@ -1274,6 +1276,10 @@ struct let mk_str_le = Z3native.mk_str_le let mk_str_lt = Z3native.mk_str_lt let mk_int_to_str = Z3native.mk_int_to_str + let mk_string_to_code = Z3native.mk_string_to_code + let mk_string_from_code = Z3native.mk_string_from_code + let mk_ubv_to_str = Z3native.mk_ubv_to_str + let mk_sbv_to_str = Z3native.mk_sbv_to_str let mk_seq_to_re = Z3native.mk_seq_to_re let mk_seq_in_re = Z3native.mk_seq_in_re let mk_re_plus = Z3native.mk_re_plus @@ -1287,6 +1293,12 @@ struct let mk_re_complement = Z3native.mk_re_complement let mk_re_empty = Z3native.mk_re_empty let mk_re_full = Z3native.mk_re_full + let mk_char = Z3native.mk_char + let mk_char_le = Z3native.mk_char_le + let mk_char_to_int = Z3native.mk_char_to_int + let mk_char_to_bv = Z3native.mk_char_to_bv + let mk_char_from_bv = Z3native.mk_char_from_bv + let mk_char_is_digit = Z3native.mk_char_is_digit end module FloatingPoint = diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 27b0992ca62..53e92b491e4 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -1881,9 +1881,15 @@ sig (** create string sort *) val mk_string_sort : context -> Sort.sort + (** create char sort *) + val mk_char_sort : context -> Sort.sort + (** test if sort is a string sort (a sequence of 8-bit bit-vectors) *) val is_string_sort : context -> Sort.sort -> bool + (** test if sort is a char sort *) + val is_char_sort : context -> Sort.sort -> bool + (** create a string literal *) val mk_string : context -> string -> Expr.expr @@ -1936,6 +1942,7 @@ sig (** retrieve integer expression encoded in string *) val mk_str_to_int : context -> Expr.expr -> Expr.expr + (** compare strings less-than-or-equal *) val mk_str_le : context -> Expr.expr -> Expr.expr -> Expr.expr @@ -1945,6 +1952,18 @@ sig (** convert an integer expression to a string *) val mk_int_to_str : context -> Expr.expr -> Expr.expr + (** [mk_string_to_code ctx s] convert a unit length string [s] to integer code *) + val mk_string_to_code : context -> Expr.expr -> Expr.expr + + (** [mk_string_from_code ctx c] convert code [c] to a string *) + val mk_string_from_code : context -> Expr.expr -> Expr.expr + + (** [mk_ubv_to_str ctx ubv] convert a unsigned bitvector [ubv] to a string *) + val mk_ubv_to_str : context -> Expr.expr -> Expr.expr + + (** [mk_sbv_to_str ctx sbv] convert a signed bitvector [sbv] to a string *) + val mk_sbv_to_str : context -> Expr.expr -> Expr.expr + (** create regular expression that accepts the argument sequence *) val mk_seq_to_re : context -> Expr.expr -> Expr.expr @@ -1984,6 +2003,24 @@ sig (** the regular expression that accepts all sequences *) val mk_re_full : context -> Sort.sort -> Expr.expr + (** [mk_char ctx i] converts an integer to a character *) + val mk_char : context -> int -> Expr.expr + + (** [mk_char_le ctx lc rc] compares two characters *) + val mk_char_le : context -> Expr.expr -> Expr.expr -> Expr.expr + + (** [mk_char_to_int ctx c] converts the character [c] to an integer *) + val mk_char_to_int : context -> Expr.expr -> Expr.expr + + (** [mk_char_to_bv ctx c] converts the character [c] to a bitvector *) + val mk_char_to_bv : context -> Expr.expr -> Expr.expr + + (** [mk_char_from_bv ctx bv] converts the bitvector [bv] to a character *) + val mk_char_from_bv : context -> Expr.expr -> Expr.expr + + (** [mk_char_is_digit ctx c] checks if the character [c] is a digit *) + val mk_char_is_digit: context -> Expr.expr -> Expr.expr + end (** Floating-Point Arithmetic *) From b386b84f34b2995147a264c9efc97465592ec974 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 02:55:55 -0700 Subject: [PATCH 534/597] #6658 --- src/api/java/Context.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index da242630500..7aaef4801ec 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2185,7 +2185,7 @@ public final ReExpr> mkToRe(Expr> s) /** * Check for regular expression membership. */ - public final BoolExpr mkInRe(Expr> s, Expr> re) + public final BoolExpr mkInRe(Expr> s, ReExpr> re) { checkContextMatch(s, re); return (BoolExpr) Expr.create(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); From 996e5b1755a30196cf540730954cf7462a7c88e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 03:25:20 -0700 Subject: [PATCH 535/597] fix #6655 --- src/util/zstring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 570510458dd..eaa5bb5eef0 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -78,7 +78,7 @@ zstring::zstring(char const* s) { m_buffer.push_back(ch); } else { - m_buffer.push_back(*s); + m_buffer.push_back((unsigned char)*s); ++s; } } From 6aaaa3b0156c05ddd1a51b28f14eba91db038270 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 09:56:23 -0700 Subject: [PATCH 536/597] fix #6660 Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/update_api.py b/scripts/update_api.py index a3d92a7e94b..4295b8961c1 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1825,6 +1825,7 @@ def write_core_py_preamble(core_py): else: print(" import builtins") print(" builtins.Z3_LIB_DIRS = [ '/path/to/libz3.%s' ] " % _ext) + print(_failures) raise Z3Exception("libz3.%s not found." % _ext) From a849a29b4f0af61730e986b4aed89ba0d05c089f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 10:31:18 -0700 Subject: [PATCH 537/597] fix #6659 --- src/ast/simplifiers/dominator_simplifier.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/dominator_simplifier.h b/src/ast/simplifiers/dominator_simplifier.h index b412f6db0bb..048f6991b70 100644 --- a/src/ast/simplifiers/dominator_simplifier.h +++ b/src/ast/simplifiers/dominator_simplifier.h @@ -41,7 +41,7 @@ class dominator_simplifier : public dependent_expr_simplifier { bool is_subexpr(expr * a, expr * b); expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } - void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } + void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); m_trail.push_back(t); } void reset_cache() { m_result.reset(); } ptr_vector const & tree(expr * e); From a62e4b2893e57192dad477f9f48bb74e1e32a5bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 12:45:51 -0700 Subject: [PATCH 538/597] extract multi-patterns when pattern can be decomposed deals with fluke regression for F* reported by Guido Martinez Background: The automatic pattern inference facility looks for terms that contains all bound variables of a quantifier. It may end up with a term that contains all bound variables but the extracted term can be simplified. Example. The pattern (ApplyTT (ApplyTT @x3!1 (ApplyTT @x4!0 (:var 1))) (ApplyTT @x4!0 (:var 0))) can be decomposed into a multi-pattern (ApplyTT @x4!0 (:var 1))) (ApplyTT @x4!0 (:var 0)) The multi-pattern may enable a quantifier instantiation while the original pattern does not. The multi-pattern should be preferred. The regression showed up based on a change that should not be considered harmful but turned out to be noticeable. The change was a simplification of and-or expressions based on sorting. This played with the case split queue used by F* (smt.case_split = 3) that uses a top-level case split of clauses to avoid redundant branches. The net effect was that without sorting, the benchmarks would always choose the opportune branch that enabled matching against the larger term. With sorting it would mostly choose inopportune branches. --- src/ast/pattern/pattern_inference.cpp | 40 ++++++++++++++++++++++++++- src/ast/pattern/pattern_inference.h | 3 ++ src/smt/smt_case_split_queue.cpp | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index d751a138801..e795188fb31 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -405,6 +405,44 @@ bool pattern_inference_cfg::pattern_weight_lt::operator()(expr * n1, expr * n2) return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size); } + +app* pattern_inference_cfg::mk_pattern(app* candidate) { + auto has_var_arg = [&](expr* e) { + if (!is_app(e)) + return false; + for (expr* arg : *to_app(e)) + if (is_var(arg)) + return true; + return false; + }; + if (has_var_arg(candidate)) + return m.mk_pattern(candidate); + m_args.reset(); + for (expr* arg : *candidate) { + if (!is_app(arg)) + return m.mk_pattern(candidate); + m_args.push_back(to_app(arg)); + } + for (unsigned i = 0; i < m_args.size(); ++i) { + app* arg = m_args[i]; + if (has_var_arg(arg)) + continue; + m_args[i] = m_args.back(); + --i; + m_args.pop_back(); + + if (is_ground(arg)) + continue; + + for (expr* e : *to_app(arg)) { + if (!is_app(e)) + return m.mk_pattern(candidate); + m_args.push_back(to_app(e)); + } + } + return m.mk_pattern(m_args.size(), (app* const*)m_args.data()); +} + /** \brief Create unary patterns (single expressions that contain all bound variables). If a candidate does not contain all bound @@ -418,7 +456,7 @@ void pattern_inference_cfg::candidates2unary_patterns(ptr_vector const & ca expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { - app * new_pattern = m.mk_pattern(candidate); + app * new_pattern = mk_pattern(candidate); result.push_back(new_pattern); } else { diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index bb4cf423833..da905dca412 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -188,6 +188,9 @@ class pattern_inference_cfg : public default_rewriter_cfg { ptr_vector m_pre_patterns; expr_pattern_match m_database; + ptr_buffer m_args; + app* mk_pattern(app* candidate); + void candidates2unary_patterns(ptr_vector const & candidate_patterns, ptr_vector & remaining_candidate_patterns, app_ref_buffer & result); diff --git a/src/smt/smt_case_split_queue.cpp b/src/smt/smt_case_split_queue.cpp index 9f5bdb0d433..711e3de3a04 100644 --- a/src/smt/smt_case_split_queue.cpp +++ b/src/smt/smt_case_split_queue.cpp @@ -964,7 +964,7 @@ namespace { } void display(std::ostream & out) override { - if (m_queue.empty() && m_queue2.empty()) + if (m_queue.empty()) return; out << "case-splits:\n"; display_core(out, m_queue, m_head, 1); From 7664429fda65291ebe4d43c169f3923fc082a1a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 12:51:23 -0700 Subject: [PATCH 539/597] remove cast expression Signed-off-by: Nikolaj Bjorner --- src/ast/pattern/pattern_inference.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index e795188fb31..2fd2b4c82b4 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -440,7 +440,7 @@ app* pattern_inference_cfg::mk_pattern(app* candidate) { m_args.push_back(to_app(e)); } } - return m.mk_pattern(m_args.size(), (app* const*)m_args.data()); + return m.mk_pattern(m_args.size(), m_args.data()); } /** From e0a066efa3d509a7404c503cd95cb5febe32a6fc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 15:38:29 -0700 Subject: [PATCH 540/597] #6654 fix reflexivity for tree-order --- src/smt/theory_special_relations.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 245a2ee5801..ddddfbc000a 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -405,6 +405,12 @@ namespace smt { TRACE("special_relations", tout << "already: " << a.v2() << " <= " << a.v1() << "\n";); continue; } + if (a.v1() == a.v2()) { + r.m_explanation.reset(); + r.m_explanation.push_back(a.explanation()); + set_conflict(r); + return l_false; + } // the nodes visited from v1 become target for v2 if (r.m_graph.reachable(a.v2(), visited, target, w)) { // From 6324db207bd9300928ef610622a399a10040eb97 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Sun, 2 Apr 2023 13:39:13 -0400 Subject: [PATCH 541/597] Only print func-decl names for indexed parameters (#6663) --- src/ast/ast_smt2_pp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 74bb871ece4..bfa26241049 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -121,8 +121,10 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { std::string str = f->get_parameter(i).get_rational().to_string(); fs.push_back(mk_string(get_manager(), str)); } - else - fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); + else { + unsigned len; + fs.push_back(pp_fdecl_name(to_func_decl(f->get_parameter(i).get_ast()), len)); + } } return mk_seq1(get_manager(), fs.begin(), fs.end(), f2f(), "_"); } From 5b385bd2fe03f781e0e59e25139006ae8d9354ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Apr 2023 10:58:01 -0700 Subject: [PATCH 542/597] fix #6665 Signed-off-by: Nikolaj Bjorner --- examples/java/JavaExample.java | 2 +- src/smt/theory_bv.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index f6f171c4652..a27a6072197 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2275,7 +2275,7 @@ public void stringExample() { ctx.mkToRe(ctx.mkString("d"))); System.out.println(c); - } + } public static void main(String[] args) { diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 00564ed3e95..7adab35f409 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -946,6 +946,9 @@ namespace smt { internalize_bv2int(term); } return params().m_bv_enable_int2bv2int; + case OP_BSREM: return false; + case OP_BUREM: return false; + case OP_BSMOD: return false; default: TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, m) << "\n";); UNREACHABLE(); From def83ed26edbe786baabfdc760211d8537e10d9d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Apr 2023 11:13:37 -0700 Subject: [PATCH 543/597] fix #6661 Signed-off-by: Nikolaj Bjorner --- src/ast/ast.h | 16 ++++++++-------- src/ast/special_relations_decl_plugin.cpp | 6 +++++- src/ast/special_relations_decl_plugin.h | 4 +++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 8fae83b26c6..4187fece6f9 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -161,7 +161,7 @@ class parameter { bool is_zstring() const { return get_kind() == PARAM_ZSTRING; } bool is_int(int & i) const { return is_int() && (i = get_int(), true); } - bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), true); } + bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), a && true); } bool is_symbol(symbol & s) const { return is_symbol() && (s = get_symbol(), true); } bool is_rational(rational & r) const { return is_rational() && (r = get_rational(), true); } bool is_double(double & d) const { return is_double() && (d = get_double(), true); } @@ -180,13 +180,13 @@ class parameter { */ void del_eh(ast_manager & m, family_id fid); - int get_int() const { return std::get(m_val); } - ast * get_ast() const { return std::get(m_val); } - symbol get_symbol() const { return std::get(m_val); } - rational const & get_rational() const { return *std::get(m_val); } - zstring const& get_zstring() const { return *std::get(m_val); } - double get_double() const { return std::get(m_val); } - unsigned get_ext_id() const { return std::get(m_val); } + int get_int() const { SASSERT(is_int()); return std::get(m_val); } + ast * get_ast() const { SASSERT(is_ast()); return std::get(m_val); } + symbol get_symbol() const { SASSERT(is_symbol()); return std::get(m_val); } + rational const & get_rational() const { SASSERT(is_rational()); return *std::get(m_val); } + zstring const& get_zstring() const { SASSERT(is_zstring()); return *std::get(m_val); } + double get_double() const { SASSERT(is_double()); return std::get(m_val); } + unsigned get_ext_id() const { SASSERT(is_external()); return std::get(m_val); } bool operator==(parameter const & p) const; bool operator!=(parameter const & p) const { return !operator==(p); } diff --git a/src/ast/special_relations_decl_plugin.cpp b/src/ast/special_relations_decl_plugin.cpp index 7ed5e834636..b45778d2f83 100644 --- a/src/ast/special_relations_decl_plugin.cpp +++ b/src/ast/special_relations_decl_plugin.cpp @@ -54,7 +54,11 @@ func_decl * special_relations_decl_plugin::mk_func_decl( case OP_SPECIAL_RELATION_LO: name = m_lo; break; case OP_SPECIAL_RELATION_PLO: name = m_plo; break; case OP_SPECIAL_RELATION_TO: name = m_to; break; - case OP_SPECIAL_RELATION_TC: name = m_tc; break; + case OP_SPECIAL_RELATION_TC: + name = m_tc; + if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) + m_manager->raise_exception("parameter to transitive closure should be a function declaration"); + break; } return m_manager->mk_func_decl(name, arity, domain, range, info); } diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index daff802e781..75b5cb134d2 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -71,11 +71,13 @@ class special_relations_util { ast_manager& m; mutable family_id m_fid; func_decl* mk_rel_decl(func_decl* f, decl_kind k) { + SASSERT(f); parameter p(f); SASSERT(f->get_arity() == 2); return m.mk_func_decl(fid(), k, 1, &p, 2, f->get_domain(), f->get_range()); } family_id fid() const { - if (null_family_id == m_fid) m_fid = m.get_family_id("specrels"); + if (null_family_id == m_fid) + m_fid = m.get_family_id("specrels"); return m_fid; } public: From 479f8442009987726e3c03fe5618b250acca383a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Apr 2023 11:14:20 -0700 Subject: [PATCH 544/597] fix #6661 Signed-off-by: Nikolaj Bjorner --- src/ast/ast.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 4187fece6f9..3e2f0288dfa 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -161,7 +161,7 @@ class parameter { bool is_zstring() const { return get_kind() == PARAM_ZSTRING; } bool is_int(int & i) const { return is_int() && (i = get_int(), true); } - bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), a && true); } + bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), true); } bool is_symbol(symbol & s) const { return is_symbol() && (s = get_symbol(), true); } bool is_rational(rational & r) const { return is_rational() && (r = get_rational(), true); } bool is_double(double & d) const { return is_double() && (d = get_double(), true); } From f8242c58dd41bdb6cda7860ecdafe1a056b07b56 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 4 Apr 2023 22:29:22 -0700 Subject: [PATCH 545/597] fix regression from Grobner port - scan_for_linear returns true if it finds a new linear equation. It then should break GB. - if scan_for_linear returns false, it should still allow try_modify_eqs. This behavior was masked by requiring scan_for_linear to always be true before allowing try_to_modify_eqs. based on repro from Guido Martinez @mtzguido --- src/smt/theory_arith_nl.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 3aa34cbc7f7..d6d30cb33a5 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -2264,8 +2264,10 @@ typename theory_arith::gb_result theory_arith::compute_grobner(svector return GB_FAIL; if (get_gb_eqs_and_look_for_conflict(eqs, gb)) return GB_PROGRESS; + if (scan_for_linear(eqs, gb)) + return GB_NEW_EQ; } - while(scan_for_linear(eqs, gb) && m_params.m_nl_arith_gb_perturbate && + while(m_params.m_nl_arith_gb_perturbate && (!m_nl_gb_exhausted) && try_to_modify_eqs(eqs, gb, next_weight)); return GB_FAIL; } From 84b92046163239331fc6f20ac2951ff67a640ee1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 5 Apr 2023 16:39:10 -0700 Subject: [PATCH 546/597] inherit and reset rlimit counter on children limits addresses rlimit leak reported by @mtzguido --- src/ast/ast.cpp | 8 ++++++++ src/ast/ast.h | 2 ++ src/ast/rewriter/seq_eq_solver.cpp | 4 ++-- src/ast/special_relations_decl_plugin.h | 6 ++++++ src/util/rlimit.cpp | 2 ++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 0a34d3e12d8..7f9542fe48a 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2322,6 +2322,14 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const return d; } +bool ast_manager::is_parametric_function(func_decl* f, func_decl *& g) const { + // is-as-array + // is-map + // is-transitive-closure + return false; +} + + sort * ast_manager::mk_fresh_sort(char const * prefix) { string_buffer<32> buffer; buffer << prefix << "!" << m_fresh_id; diff --git a/src/ast/ast.h b/src/ast/ast.h index 3e2f0288dfa..e0ae7b92f21 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1924,6 +1924,8 @@ class ast_manager { return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range, skolem); } + bool is_parametric_function(func_decl* f, func_decl *& g) const; + app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) { return mk_const(mk_fresh_func_decl(prefix, 0, nullptr, s, skolem)); } diff --git a/src/ast/rewriter/seq_eq_solver.cpp b/src/ast/rewriter/seq_eq_solver.cpp index 6d8734bc936..c6778c45e36 100644 --- a/src/ast/rewriter/seq_eq_solver.cpp +++ b/src/ast/rewriter/seq_eq_solver.cpp @@ -190,8 +190,8 @@ namespace seq { expr_ref digit = m_ax.sk().mk_digit2int(u); add_consequence(m_ax.mk_ge(digit, 1)); } - expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); - ctx.add_solution(seq.str.mk_itos(n), y); + expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); + ctx.add_solution(seq.str.mk_itos(n), y); return true; } diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index 75b5cb134d2..0c437786421 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -101,6 +101,12 @@ class special_relations_util { bool is_to(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TO); } bool is_tc(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TC); } + bool is_lo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_LO); } + bool is_po(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PO); } + bool is_plo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PLO); } + bool is_to(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TO); } + bool is_tc(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TC); } + app * mk_lo (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_LO, arg1, arg2); } app * mk_po (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_PO, arg1, arg2); } app * mk_plo(expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_PLO, arg1, arg2); } diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index f71d2764a7c..ecc527681a1 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -87,6 +87,8 @@ void reslimit::push_child(reslimit* r) { void reslimit::pop_child() { lock_guard lock(*g_rlimit_mux); + m_count += m_children.back()->m_count; + m_children.back()->m_count = 0; m_children.pop_back(); } From 7b513b4a4012035464f650bfcf4183ed5e4638d6 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Sat, 8 Apr 2023 21:50:46 +0200 Subject: [PATCH 547/597] Some UP bugfixes in the new core (#6673) --- src/sat/sat_solver/inc_sat_solver.cpp | 4 ++++ src/sat/sat_solver/sat_smt_solver.cpp | 4 ++++ src/sat/smt/euf_solver.cpp | 14 ++++++++++++++ src/sat/smt/euf_solver.h | 6 ++++++ src/sat/smt/user_solver.cpp | 15 +++++++++------ src/solver/simplifier_solver.cpp | 1 + 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 75351f05361..8bf665ebf02 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -701,6 +701,10 @@ class inc_sat_solver : public solver { ensure_euf()->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { + ensure_euf()->user_propagate_register_decide(r); + } + private: diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 82aeded6faa..a5dd9b415f1 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -568,6 +568,10 @@ class sat_smt_solver : public solver { ensure_euf()->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { + ensure_euf()->user_propagate_register_decide(r); + } + private: void add_assumption(expr* a) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index f4e9c2c64ba..0ae56beb3fa 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -349,6 +349,20 @@ namespace euf { si.uncache(literal(v, true)); } + bool solver::decide(bool_var& var, lbool& phase) { + for (auto const& th : m_solvers) + if (th->decide(var, phase)) + return true; + return false; + } + + bool solver::get_case_split(bool_var& var, lbool& phase) { + for (auto const& th : m_solvers) + if (th->get_case_split(var, phase)) + return true; + return false; + } + void solver::asserted(literal l) { m_relevancy.asserted(l); if (!m_relevancy.is_relevant(l)) diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 1a2cdf123f5..72776b7ffa7 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -369,6 +369,8 @@ namespace euf { void add_explain(size_t* p) { m_explain.push_back(p); } void reset_explain() { m_explain.reset(); } void set_eliminated(bool_var v) override; + bool decide(bool_var& var, lbool& phase) override; + bool get_case_split(bool_var& var, lbool& phase) override; void asserted(literal l) override; sat::check_result check() override; void push() override; @@ -540,6 +542,10 @@ namespace euf { check_for_user_propagator(); m_user_propagator->register_created(ceh); } + void user_propagate_register_decide(user_propagator::decide_eh_t& ceh) { + check_for_user_propagator(); + m_user_propagator->register_decide(ceh); + } void user_propagate_register_expr(expr* e) { check_for_user_propagator(); m_user_propagator->add_expr(e); diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index 99e4eeeb1a7..34f2b10b470 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -21,7 +21,7 @@ Module Name: namespace user_solver { solver::solver(euf::solver& ctx) : - th_euf_solver(ctx, symbol("user"), ctx.get_manager().mk_family_id("user")) + th_euf_solver(ctx, symbol(user_propagator::plugin::name()), ctx.get_manager().mk_family_id(user_propagator::plugin::name())) {} solver::~solver() { @@ -92,24 +92,24 @@ namespace user_solver { euf::enode* original_enode = bool_var2enode(var); - if (!is_attached_to_var(original_enode)) + if (!original_enode || !is_attached_to_var(original_enode)) return false; unsigned new_bit = 0; // ignored; currently no bv-support - expr* e = bool_var2expr(var); + expr* e = original_enode->get_expr(); m_decide_eh(m_user_context, this, &e, &new_bit, &phase); euf::enode* new_enode = ctx.get_enode(e); - if (original_enode == new_enode) + if (original_enode == new_enode || new_enode->bool_var() == sat::null_bool_var) return false; var = new_enode->bool_var(); return true; } - bool solver::get_case_split(sat::bool_var& var, lbool &phase){ + bool solver::get_case_split(sat::bool_var& var, lbool& phase){ if (!m_next_split_expr) return false; @@ -123,9 +123,12 @@ namespace user_solver { void solver::asserted(sat::literal lit) { if (!m_fixed_eh) return; - force_push(); auto* n = bool_var2enode(lit.var()); euf::theory_var v = n->get_th_var(get_id()); + if (!m_id2justification.get(v, sat::literal_vector()).empty()) + // the core merged variables. We already issued the respective fixed callback for an equated variable + return; + force_push(); sat::literal_vector lits; lits.push_back(lit); m_id2justification.setx(v, lits, sat::literal_vector()); diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 12222194d38..d70d232e47a 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -365,6 +365,7 @@ class simplifier_solver : public solver { void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh) override { s->user_propagate_register_diseq(diseq_eh); } void user_propagate_register_expr(expr* e) override { m_preprocess_state.freeze(e); s->user_propagate_register_expr(e); } void user_propagate_register_created(user_propagator::created_eh_t& r) override { s->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { s->user_propagate_register_decide(r); } }; From ccb250c32b6c1075d97b2e43599100f45ac5b749 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 8 Apr 2023 16:39:40 -0700 Subject: [PATCH 548/597] fix #6671 --- src/smt/theory_lra.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 568143d36dc..97ca025e33c 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1138,6 +1138,17 @@ class theory_lra::imp { expr_ref zero(a.mk_real(0), m); mk_axiom(~mk_literal(a.mk_le(p, zero))); } + bool can_be_underspecified = false; + if (a.is_numeral(x, r) && r == 0 && (!a.is_numeral(y, r) || r == 0)) + can_be_underspecified = true; + if (!a.is_extended_numeral(x, r) && + !a.is_extended_numeral(y, r)) + can_be_underspecified = true; + if (can_be_underspecified) { + literal lit = th.mk_eq(p, a.mk_power0(x, y), false); + ctx().mark_as_relevant(lit); + ctx().assign(lit, nullptr); + } } // n < 0 || rem(a, n) = mod(a, n) @@ -1622,6 +1633,8 @@ class theory_lra::imp { final_check_status eval_unsupported(expr* e) { if (a.is_power(e)) return eval_power(e); + if (a.is_power0(e)) + return FC_DONE; return FC_GIVEUP; } @@ -1681,6 +1694,7 @@ class theory_lra::imp { st = FC_CONTINUE; break; case FC_GIVEUP: + TRACE("arith", tout << "give up " << mk_pp(e, m) << "\n"); if (st != FC_CONTINUE) st = FC_GIVEUP; break; From af9c760a68376fb1bbdc2139f5b57869dfe719a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 8 Apr 2023 16:55:23 -0700 Subject: [PATCH 549/597] fix #6670 --- src/smt/theory_lra.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 97ca025e33c..d73930a8b46 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1314,7 +1314,6 @@ class theory_lra::imp { expr_ref abs_q(m.mk_ite(a.mk_ge(q, zero), q, a.mk_uminus(q)), m); expr_ref mone(a.mk_int(-1), m); expr_ref modmq(a.mk_sub(mod, abs_q), m); - ctx().get_rewriter()(modmq); literal eqz = mk_literal(m.mk_eq(q, zero)); literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); literal mod_lt_q = mk_literal(a.mk_le(modmq, mone)); From e6ea81546eb78b6ad1e5920954796f6d1c2074e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 8 Apr 2023 17:14:39 -0700 Subject: [PATCH 550/597] fix #6662 --- src/ast/special_relations_decl_plugin.cpp | 1 + src/ast/special_relations_decl_plugin.h | 5 +++++ src/smt/smt_model_checker.cpp | 23 ++++++++++++++++++++++- src/smt/smt_model_checker.h | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/ast/special_relations_decl_plugin.cpp b/src/ast/special_relations_decl_plugin.cpp index b45778d2f83..24a756bf79e 100644 --- a/src/ast/special_relations_decl_plugin.cpp +++ b/src/ast/special_relations_decl_plugin.cpp @@ -47,6 +47,7 @@ func_decl * special_relations_decl_plugin::mk_func_decl( if (!m_manager->is_bool(range)) { m_manager->raise_exception("range type is expected to be Boolean for special relations"); } + m_has_special_relation = true; func_decl_info info(m_family_id, k, num_parameters, parameters); symbol name; switch(k) { diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index 0c437786421..c422cbcdc01 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -37,6 +37,7 @@ class special_relations_decl_plugin : public decl_plugin { symbol m_plo; symbol m_to; symbol m_tc; + bool m_has_special_relation = false; public: special_relations_decl_plugin(); @@ -50,6 +51,8 @@ class special_relations_decl_plugin : public decl_plugin { void get_op_names(svector & op_names, symbol const & logic) override; sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { return nullptr; } + + bool has_special_relation() const { return m_has_special_relation; } }; enum sr_property { @@ -82,6 +85,8 @@ class special_relations_util { } public: special_relations_util(ast_manager& m) : m(m), m_fid(null_family_id) { } + + bool has_special_relation() const { return static_cast(m.get_plugin(m.mk_family_id("specrels")))->has_special_relation(); } bool is_special_relation(func_decl* f) const { return f->get_family_id() == fid(); } bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); } diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index e555088538c..2d23504945a 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -28,6 +28,7 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" +#include "ast/special_relations_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_model_checker.h" #include "smt/smt_context.h" @@ -358,7 +359,7 @@ namespace smt { TRACE("model_checker", tout << "[complete] model-checker result: " << to_sat_str(r) << "\n";); if (r != l_true) { - return r == l_false; // quantifier is satisfied by m_curr_model + return is_safe_for_mbqi(q) && r == l_false; // quantifier is satisfied by m_curr_model } model_ref complete_cex; @@ -398,6 +399,26 @@ namespace smt { return false; } + bool model_checker::is_safe_for_mbqi(quantifier * q) const { + special_relations_util sp(m); + if (!sp.has_special_relation()) + return true; + ast_fast_mark1 visited; + struct proc { + special_relations_util& sp; + bool found = false; + proc(special_relations_util& sp):sp(sp) {} + void operator()(app* f) { + found |= sp.is_special_relation(f); + } + void operator()(expr* e) {} + }; + proc p(sp); + quick_for_each_expr(p, visited, q); + return !p.found; + } + + void model_checker::init_aux_context() { if (!m_fparams) { m_fparams = alloc(smt_params, m_context->get_fparams()); diff --git a/src/smt/smt_model_checker.h b/src/smt/smt_model_checker.h index 6332a890e83..46d51f9a015 100644 --- a/src/smt/smt_model_checker.h +++ b/src/smt/smt_model_checker.h @@ -87,6 +87,7 @@ namespace smt { expr_mark m_visited; bool contains_model_value(expr * e); void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation, expr * def); + bool is_safe_for_mbqi(quantifier * q) const; public: model_checker(ast_manager & m, qi_params const & p, model_finder & mf); From 4a142b0f81c16b3e60972a112233d21ee909c540 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 9 Apr 2023 21:10:24 -0700 Subject: [PATCH 551/597] fix #6623 --- src/sat/smt/pb_internalize.cpp | 5 +++++ src/sat/smt/sat_internalizer.h | 1 + src/sat/tactic/goal2sat.cpp | 13 +++++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index 17f2bd4ded6..abbd79c44d2 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -41,6 +41,11 @@ namespace pb { SASSERT(m_pb.is_pb(e)); app* t = to_app(e); rational k = m_pb.get_k(t); + if (!root && is_app(e)) { + sat::literal lit = si.get_cached(to_app(e)); + if (lit != sat::null_literal) + return sign ? ~lit : lit; + } switch (t->get_decl_kind()) { case OP_AT_MOST_K: return convert_at_most_k(t, k, root, sign); diff --git a/src/sat/smt/sat_internalizer.h b/src/sat/smt/sat_internalizer.h index 5be20c4e0ee..7b1d932eca3 100644 --- a/src/sat/smt/sat_internalizer.h +++ b/src/sat/smt/sat_internalizer.h @@ -26,6 +26,7 @@ namespace sat { virtual literal internalize(expr* e) = 0; virtual bool_var to_bool_var(expr* e) = 0; virtual bool_var add_bool_var(expr* e) = 0; + virtual literal get_cached(app* t) const = 0; virtual bool is_cached(app* t, literal l) const = 0; virtual void cache(app* t, literal l) = 0; virtual void uncache(literal l) = 0; diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index c107a74c500..2678888044c 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -276,11 +276,16 @@ struct goal2sat::imp : public sat::sat_internalizer { m_cache_trail.push_back(t); } + sat::literal get_cached(app* t) const override { + sat::literal lit = sat::null_literal; + m_app2lit.find(t, lit); + return lit; + } + bool is_cached(app* t, sat::literal l) const override { - if (!m_app2lit.contains(t)) - return false; - SASSERT(m_app2lit[t] == l); - return true; + sat::literal lit = get_cached(t); + SASSERT(lit == sat::null_literal || l == lit); + return l == lit; } void convert_atom(expr * t, bool root, bool sign) { From 98d3fabc24068182e1b321624a84e14a12db2fc0 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Mon, 10 Apr 2023 21:57:59 +0200 Subject: [PATCH 552/597] Bugfix relevancy propagation + UP (old core) (#6678) * Some UP bugfixes in the new core * Bugfix relevancy propagation + UP (old core) * Revert smt_context.cpp --- src/smt/theory_user_propagator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index f19f933f2ab..8eeaf4382da 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -92,6 +92,9 @@ void theory_user_propagator::propagate_cb( expr_ref _conseq(conseq, m); ctx.get_rewriter()(conseq, _conseq); + if (!ctx.get_manager().is_true(_conseq) && !ctx.get_manager().is_false(_conseq)) + ctx.mark_as_relevant((expr*)_conseq); + if (ctx.lit_internalized(_conseq) && ctx.get_assignment(ctx.get_literal(_conseq)) == l_true) return; m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, _conseq)); From bb44b91e45fbc7eb222baae8d35af15d343cd341 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Apr 2023 15:10:54 -0700 Subject: [PATCH 553/597] fix #6677 --- src/cmd_context/cmd_context.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 1d3476b3d72..c2ec7fe3093 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1237,6 +1237,9 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c if (num_args != 1) return false; + if (!dt.is_datatype(args[0]->get_sort())) + return false; + for (auto* a : dt.plugin().get_accessors(s)) { fn = a->instantiate(args[0]->get_sort()); r = m().mk_app(fn, num_args, args); From 368d60f5536f1881ea10c62c605edf3cc5b30d27 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Apr 2023 22:14:03 -0700 Subject: [PATCH 554/597] add branch / cut selection heuristic from solver=2 disabled for testing. --- src/ast/rewriter/arith_rewriter.cpp | 22 ++++++--- src/math/lp/gomory.cpp | 15 ++++++- src/math/lp/int_branch.cpp | 11 ++++- src/math/lp/int_solver.cpp | 69 +++++++++++++++++++++++++++++ src/math/lp/int_solver.h | 3 ++ src/smt/theory_arith_int.h | 6 ++- 6 files changed, 114 insertions(+), 12 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 7cade3bfa82..ed36562caf0 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -16,6 +16,7 @@ Module Name: Notes: --*/ + #include "params/arith_rewriter_params.hpp" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/poly_rewriter_def.h" @@ -1046,19 +1047,21 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu set_curr_sort(arg1->get_sort()); numeral v1, v2; bool is_int; - if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { + bool is_num1 = m_util.is_numeral(arg1, v1, is_int); + bool is_num2 = m_util.is_numeral(arg2, v2, is_int); + if (is_num1 && is_num2 && !v2.is_zero()) { result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { + if (is_num2 && v2.is_one()) { result = arg1; return BR_DONE; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_minus_one()) { + if (is_num2 && v2.is_minus_one()) { result = m_util.mk_mul(m_util.mk_int(-1), arg1); return BR_REWRITE1; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { + if (is_num2 && v2.is_zero()) { return BR_FAILED; } if (arg1 == arg2) { @@ -1066,7 +1069,7 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu result = m.mk_ite(m.mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); return BR_REWRITE3; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_pos() && m_util.is_add(arg1)) { + if (is_num2 && v2.is_pos() && m_util.is_add(arg1)) { expr_ref_buffer args(m); bool change = false; rational add(0); @@ -1092,7 +1095,14 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu expr_ref zero(m_util.mk_int(0), m); result = m.mk_ite(m.mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result); return BR_REWRITE_FULL; - } + } +#if 0 + expr* x = nullptr, *y = nullptr, *z = nullptr; + if (is_num2 && m_util.is_idiv(arg1, x, y) && m_util.is_numeral(y, v1) && v1 > 0 && v2 > 0) { + result = m_util.mk_idiv(x, m_util.mk_numeral(v1*v2, is_int)); + return BR_DONE; + } +#endif return BR_FAILED; } diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 2d187c9f4f6..8c86e0989b0 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -377,10 +377,20 @@ bool gomory::is_gomory_cut_target(const row_strip& row) { } int gomory::find_basic_var() { - int result = -1; unsigned n = 0; + int result = -1; unsigned min_row_size = UINT_MAX; - // Prefer smaller row size + +#if 0 + result = lia.select_int_infeasible_var(); + + if (result == -1) + return result; + + const row_strip& row = lra.get_row(lia.row_of_basic_column(result)); + if (is_gomory_cut_target(row)) + return result; +#endif for (unsigned j : lra.r_basis()) { if (!lia.column_is_int_inf(j)) @@ -389,6 +399,7 @@ int gomory::find_basic_var() { if (!is_gomory_cut_target(row)) continue; IF_VERBOSE(20, lia.display_row_info(verbose_stream(), lia.row_of_basic_column(j))); + // Prefer smaller row size if (min_row_size == UINT_MAX || 2*row.size() < min_row_size || (4*row.size() < 5*min_row_size && lia.random() % (++n) == 0)) { diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index fc7b5c3ecb2..da34f77fd9e 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -52,16 +52,22 @@ lia_move int_branch::create_branch_on_column(int j) { int int_branch::find_inf_int_base_column() { + +#if 0 + return lia.select_int_infeasible_var(); +#endif + int result = -1; mpq range; mpq new_range; - mpq small_range_thresold(1024); + mpq small_value(1024); unsigned n = 0; lar_core_solver & lcs = lra.m_mpq_lar_core_solver; unsigned prev_usage = 0; // to quiet down the compile unsigned k = 0; unsigned usage; unsigned j; + // this loop looks for a column with the most usages, but breaks when // a column with a small span of bounds is found for (; k < lra.r_basis().size(); k++) { @@ -69,12 +75,13 @@ int int_branch::find_inf_int_base_column() { if (!lia.column_is_int_inf(j)) continue; usage = lra.usage_in_terms(j); - if (lia.is_boxed(j) && (range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_range_thresold) { + if (lia.is_boxed(j) && (range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_value) { result = j; k++; n = 1; break; } + if (n == 0 || usage > prev_usage) { result = j; prev_usage = usage; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index e338e222a27..6c34ce16fec 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -632,4 +632,73 @@ bool int_solver::non_basic_columns_are_at_bounds() const { return true; } +int int_solver::select_int_infeasible_var() { + int result = -1; + mpq range; + mpq new_range; + mpq small_value(1024); + unsigned n = 0; + lar_core_solver & lcs = lra.m_mpq_lar_core_solver; + unsigned prev_usage = 0; // to quiet down the compile + unsigned k = 0; + unsigned usage; + unsigned j; + + enum state { small_box, is_small_value, any_value, not_found }; + state st = not_found; + + // 1. small box + // 2. small value + // 3. any value + for (; k < lra.r_basis().size(); k++) { + j = lra.r_basis()[k]; + if (!column_is_int_inf(j)) + continue; + usage = lra.usage_in_terms(j); + if (is_boxed(j) && (new_range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_value) { + SASSERT(!is_fixed(j)); + if (st != small_box) { + n = 0; + st = small_box; + } + if (n == 0 || new_range < range) { + result = j; + range = new_range; + n = 1; + } + else if (new_range == range && (random() % (++n) == 0)) { + result = j; + } + continue; + } + if (st == small_box) + continue; + impq const& value = get_value(j); + if (abs(value.x) < small_value || + (has_upper(j) && small_value > upper_bound(j).x - value.x) || + (has_lower(j) && small_value > value.x - lower_bound(j).x)) { + if (st != is_small_value) { + n = 0; + st = is_small_value; + } + if (random() % (++n) == 0) + result = j; + } + if (st == is_small_value) + continue; + SASSERT(st == not_found || st == any_value); + st = any_value; + if (n == 0 /*|| usage > prev_usage*/) { + result = j; + prev_usage = usage; + n = 1; + } + else if (usage > 0 && /*usage == prev_usage && */ (random() % (++n) == 0)) + result = j; + } + + return result; +} + + } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index c348f27f28b..822e1cf1e5f 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -129,5 +129,8 @@ class int_solver { void find_feasible_solution(); lia_move hnf_cut(); void patch_nbasic_column(unsigned j) { m_patcher.patch_nbasic_column(j); } + + int select_int_infeasible_var(); + }; } diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 699ebd5d235..7b76960dd68 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -514,9 +514,11 @@ namespace smt { // SASSERT(m_value[x_i].is_rational()); // infinitesimals are not used for integer variables SASSERT(!m_value[x_i].is_int()); // the base variable is not assigned to an integer value. - if (constrain_free_vars(r) || !is_gomory_cut_target(r)) { + bool cfv = constrain_free_vars(r); + + if (cfv || !is_gomory_cut_target(r)) { TRACE("gomory_cut", tout << "failed to apply gomory cut:\n"; - tout << "constrain_free_vars(r): " << constrain_free_vars(r) << "\n";); + tout << "constrain_free_vars(r): " << cfv << "\n";); return false; } From ccc4f2d382540b645c3ffdd4c9b0440d09121330 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2023 05:10:03 -0700 Subject: [PATCH 555/597] fix #6682 --- src/api/python/z3/z3.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 7e7c5805225..6b79dd1fe87 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -3173,12 +3173,8 @@ def _to_int_str(val): return "1" else: return "0" - elif _is_int(val): + else: return str(val) - elif isinstance(val, str): - return val - if z3_debug(): - _z3_assert(False, "Python value cannot be used as a Z3 integer") def IntVal(val, ctx=None): From 58a2a9c79c4b0b48a16c537225489fe3fde0c271 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2023 14:42:47 -0700 Subject: [PATCH 556/597] fix #6680 --- src/parsers/smt2/smt2parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 7a1fd640a13..081a8c83892 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -372,8 +372,8 @@ namespace smt2 { return true; } catch (scanner_exception & ex) { - SASSERT(ex.has_pos()); - error(ex.line(), ex.pos(), ex.msg()); + if (ex.has_pos()) + error(ex.line(), ex.pos(), ex.msg()); ++num_errors; } } From 0b5c38dea555a11a70f0ffe6a67ac8f8b5fee9a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2023 16:46:30 -0700 Subject: [PATCH 557/597] fix #6676 get rid of rem0 declare it to be mod0 semantics to simplify code paths --- src/ast/arith_decl_plugin.cpp | 7 ++----- src/ast/arith_decl_plugin.h | 10 ++++------ src/math/subpaving/tactic/expr2subpaving.cpp | 1 - src/smt/theory_arith_core.h | 1 - src/smt/theory_lra.cpp | 2 +- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index a67464a928f..4778caf8933 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -365,7 +365,6 @@ inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { case OP_MOD: return m_i_mod_decl; case OP_DIV0: return m_manager->mk_func_decl(symbol("/0"), m_real_decl, m_real_decl, m_real_decl, func_decl_info(m_family_id, OP_DIV0)); case OP_IDIV0: return m_manager->mk_func_decl(symbol("div0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_IDIV0)); - case OP_REM0: return m_manager->mk_func_decl(symbol("rem0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_REM0)); case OP_MOD0: return m_manager->mk_func_decl(symbol("mod0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_MOD0)); case OP_POWER0: if (is_real) { @@ -612,7 +611,6 @@ void arith_decl_plugin::get_op_names(svector& op_names, symbol con op_names.push_back(builtin_name("euler", OP_E)); op_names.push_back(builtin_name("/0",OP_DIV0)); op_names.push_back(builtin_name("div0",OP_IDIV0)); - op_names.push_back(builtin_name("rem0",OP_REM0)); op_names.push_back(builtin_name("mod0",OP_MOD0)); } } @@ -821,7 +819,7 @@ bool arith_util::is_considered_uninterpreted(func_decl* f, unsigned n, expr* con } if (is_decl_of(f, arith_family_id, OP_REM) && n == 2 && is_numeral(args[1], r) && r.is_zero()) { sort* rs[2] = { mk_int(), mk_int() }; - f_out = m_manager.mk_func_decl(arith_family_id, OP_REM0, 0, nullptr, 2, rs, mk_int()); + f_out = m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); return true; } if (is_decl_of(f, arith_family_id, OP_POWER) && n == 2 && is_numeral(args[1], r) && r.is_zero() && is_numeral(args[0], r) && r.is_zero()) { @@ -857,7 +855,7 @@ func_decl* arith_util::mk_idiv0() { func_decl* arith_util::mk_rem0() { sort* rs[2] = { mk_int(), mk_int() }; - return m_manager.mk_func_decl(arith_family_id, OP_REM0, 0, nullptr, 2, rs, mk_int()); + return m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); } func_decl* arith_util::mk_mod0() { @@ -942,7 +940,6 @@ bool arith_util::is_underspecified(expr* e) const { case OP_MOD: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: return true; default: diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 93d0edf2ff6..5dbf3e8cfc8 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -50,7 +50,6 @@ enum arith_op_kind { OP_IDIVIDES, OP_REM, OP_MOD, - OP_REM0, OP_MOD0, OP_TO_REAL, OP_TO_INT, @@ -216,7 +215,6 @@ class arith_decl_plugin : public decl_plugin { case OP_U_ACOS: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: case OP_POWER0: return true; @@ -270,7 +268,7 @@ class arith_recognizers { bool is_div0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_DIV0); } bool is_idiv0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_IDIV0); } - bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_REM0); } + bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); } bool is_mod0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); } bool is_power0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER0); } bool is_power(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER); } @@ -296,7 +294,7 @@ class arith_recognizers { bool is_mod(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD); } bool is_rem(expr const * n) const { return is_app_of(n, arith_family_id, OP_REM); } bool is_mod0(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD0); } - bool is_rem0(expr const * n) const { return is_app_of(n, arith_family_id, OP_REM0); } + bool is_rem0(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD0); } bool is_to_real(expr const * n) const { return is_app_of(n, arith_family_id, OP_TO_REAL); } bool is_to_int(expr const * n) const { return is_app_of(n, arith_family_id, OP_TO_INT); } bool is_is_int(expr const * n) const { return is_app_of(n, arith_family_id, OP_IS_INT); } @@ -355,7 +353,7 @@ class arith_recognizers { MATCH_BINARY(is_div); MATCH_BINARY(is_idiv); MATCH_BINARY(is_mod0); - MATCH_BINARY(is_rem0); + // MATCH_BINARY(is_rem0); MATCH_BINARY(is_div0); MATCH_BINARY(is_idiv0); MATCH_BINARY(is_power); @@ -465,7 +463,7 @@ class arith_util : public arith_recognizers { app * mk_mod(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD, arg1, arg2); } app * mk_div0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_DIV0, arg1, arg2); } app * mk_idiv0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_IDIV0, arg1, arg2); } - app * mk_rem0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_REM0, arg1, arg2); } + app * mk_rem0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD0, arg1, arg2); } app * mk_mod0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD0, arg1, arg2); } app * mk_to_real(expr * arg1) { return m_manager.mk_app(arith_family_id, OP_TO_REAL, arg1); } app * mk_to_int(expr * arg1) { return m_manager.mk_app(arith_family_id, OP_TO_INT, arg1); } diff --git a/src/math/subpaving/tactic/expr2subpaving.cpp b/src/math/subpaving/tactic/expr2subpaving.cpp index 3afbc1ecb38..e2c43d12b2d 100644 --- a/src/math/subpaving/tactic/expr2subpaving.cpp +++ b/src/math/subpaving/tactic/expr2subpaving.cpp @@ -311,7 +311,6 @@ struct expr2subpaving::imp { case OP_REM: case OP_IRRATIONAL_ALGEBRAIC_NUM: case OP_DIV0: - case OP_REM0: case OP_MOD0: case OP_IDIV0: throw default_exception("you must apply arithmetic purifier before internalizing expressions into the subpaving module."); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 3a5c86207c2..690775d0835 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -154,7 +154,6 @@ namespace smt { case OP_MOD: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: return true; default: diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d73930a8b46..3b3e3dd79f4 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -469,7 +469,7 @@ class theory_lra::imp { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } - else if (a.is_idiv0(n, n1, n2) || a.is_mod0(n, n1, n2) || a.is_rem0(n, n1, n2)) { + else if (a.is_idiv0(n, n1, n2) || a.is_mod0(n, n1, n2)) { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } From f61168cd538dcdaff586317636cf3d9f90251c7a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 14:22:14 -0700 Subject: [PATCH 558/597] module for maintaining probability distributions --- src/util/distribution.h | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/util/distribution.h diff --git a/src/util/distribution.h b/src/util/distribution.h new file mode 100644 index 00000000000..de385a08e69 --- /dev/null +++ b/src/util/distribution.h @@ -0,0 +1,100 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + distribution.h + +Abstract: + + Probabiltiy distribution + +Author: + + Nikolaj Bjorner (nbjorner) 2023-4-12 + +Notes: + + Distribution class works by pushing identifiers with associated scores. + After they have been pushed, you can access a random element using choose + or you can enumerate the elements in random order, sorted by the score probability. + +--*/ +#pragma once + +#include "vector.h" + +class distribution { + + random_gen m_random; + svector> m_elems; + unsigned m_sum = 0; + + unsigned choose(unsigned sum) { + unsigned s = m_random(sum); + for (auto const& [j, score] : m_elems) { + if (s < score) + return j; + s -= score; + } + UNREACHABLE(); + return 0; + } + +public: + + distribution(unsigned seed): m_random(seed) {} + + void reset() { + m_elems.reset(); + m_sum = 0; + } + + bool empty() const { + return m_elems.empty(); + } + + void push(unsigned id, unsigned score) { + SASSERT(score > 0); + if (score > 0) { + m_elems.push_back({id, score}); + m_sum += score; + } + } + + /** + \brief choose an element at random with probability proportional to the score + relative to the sum of scores of other. + */ + unsigned choose() { + return m_elems[choose(m_sum)].first; + } + + class iterator { + distribution& d; + unsigned m_sz = 0; + unsigned m_sum = 0; + unsigned m_index = 0; + void next_index() { + if (0 == m_sz) + return; + m_index = d.choose(m_sum); + } + public: + iterator(distribution& d, bool start): d(d), m_sz(start?d.m_elems.size():0), m_sum(d.m_sum) { + next_index(); + } + unsigned operator*() const { return d.m_elems[m_index].first; } + iterator operator++() { + m_sum -= d.m_elems[m_index].second; + --m_sz; + std::swap(d.m_elems[m_index], d.m_elems[d.m_elems.size() - 1]); + next_index(); + } + bool operator==(iterator const& other) const { return m_sz == other.m_sz; } + bool operator!=(iterator const& other) const { return m_sz != other.m_sz; } + }; + + iterator begin() { return iterator(*this, true); } + iterator end() { return iterator(*this, false); } +}; From 444238bc538e4017c38cf2955daad873503b04f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 14:28:08 -0700 Subject: [PATCH 559/597] formatting updates --- src/ast/rewriter/maximize_ac_sharing.cpp | 2 +- src/model/value_factory.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/maximize_ac_sharing.cpp b/src/ast/rewriter/maximize_ac_sharing.cpp index a8bee62d53f..b5ebb9f1c1a 100644 --- a/src/ast/rewriter/maximize_ac_sharing.cpp +++ b/src/ast/rewriter/maximize_ac_sharing.cpp @@ -92,7 +92,7 @@ br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr else { result = m.mk_app(f, numeral, _args[0]); } - TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m) << "\n";); + TRACE("ac_sharing_detail", tout << "result: " << result << "\n";); return BR_DONE; } } diff --git a/src/model/value_factory.h b/src/model/value_factory.h index edb12b09562..20c383efec6 100644 --- a/src/model/value_factory.h +++ b/src/model/value_factory.h @@ -194,9 +194,8 @@ class simple_factory : public value_factory { while (!is_new) { result = mk_value(next, s, is_new); next++; - if (has_max && next > max_size + start) { - return nullptr; - } + if (has_max && next > max_size + start) + return nullptr; } SASSERT(result != 0); return result; From e8222433c3676020b4dc03af7160f18be57c5b60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 19:40:19 -0700 Subject: [PATCH 560/597] count internal nodes, use to block expanding use of hoist, #6683 --- src/ast/for_each_expr.cpp | 54 ++++++++++++++++++++++++++++++ src/ast/for_each_expr.h | 2 ++ src/ast/rewriter/bool_rewriter.cpp | 9 +++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 1e7b6da3b89..374d7b496a8 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -44,6 +44,60 @@ unsigned get_num_exprs(expr * n) { return get_num_exprs(n, visited); } + +static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo, expr * n) { + counts.reserve(n->get_id() + 1); + unsigned& rc = counts[n->get_id()]; + if (rc > 0) { + --rc; + return; + } + rc = n->get_ref_count() - 1; + unsigned i = todo.size(); + todo.push_back(n); + unsigned count = 0; + for (; i < todo.size(); ++i) { + n = todo[i]; + if (!is_app(n)) + continue; + for (expr* arg : *to_app(n)) { + unsigned id = arg->get_id(); + counts.reserve(id + 1); + unsigned& rc = counts[id]; + if (rc > 0) { + --rc; + continue; + } + rc = arg->get_ref_count() - 1; + todo.push_back(arg); + } + } +} + +unsigned get_num_internal_exprs(expr * n) { + unsigned_vector counts; + sbuffer todo; + unsigned internal_nodes = 0; + get_num_internal_exprs(counts, todo, n); + for (expr* t : todo) + if (counts[t->get_id()] == 0) + ++internal_nodes; + return internal_nodes; +} + +unsigned get_num_internal_exprs(unsigned sz, expr * const * args) { + unsigned_vector counts; + sbuffer todo; + unsigned internal_nodes = 0; + for (unsigned i = 0; i < sz; ++i) + get_num_internal_exprs(counts, todo, args[i]); + for (expr* t : todo) + if (counts[t->get_id()] == 0) + ++internal_nodes; + return internal_nodes; +} + + namespace has_skolem_functions_ns { struct found {}; struct proc { diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 2d5ed05ae4f..c943489640d 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -163,6 +163,8 @@ struct for_each_expr_proc : public EscapeProc { unsigned get_num_exprs(expr * n); unsigned get_num_exprs(expr * n, expr_mark & visited); unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); +unsigned get_num_internal_exprs(expr * n); +unsigned get_num_internal_exprs(unsigned sz, expr * const * args); bool has_skolem_functions(expr * n); diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 378c794cd13..9ebdbe7fda7 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -20,6 +20,7 @@ Module Name: #include "params/bool_rewriter_params.hpp" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_lt.h" +#include "ast/for_each_expr.h" #include void bool_rewriter::updt_params(params_ref const & _p) { @@ -268,14 +269,18 @@ 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_FAILED) { + unsigned count1 = get_num_internal_exprs(result); + unsigned count2 = get_num_internal_exprs(buffer.size(), buffer.data()); + if (count1 > count2) + st = BR_FAILED; + } if (st == BR_DONE) return BR_REWRITE1; if (st != BR_FAILED) return st; -#endif if (s) { ast_lt lt; From eba0732629e257f67417a46481bd1e54fd61c79e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 19:50:01 -0700 Subject: [PATCH 561/597] fix #6675 disable remove_unused_defs from pb-solver until it is integrated with model reconstruction. --- src/ast/rewriter/pb2bv_rewriter.cpp | 15 ++++++++------- src/cmd_context/cmd_context.cpp | 2 +- src/sat/smt/pb_solver.cpp | 9 +++++++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp index d3dd8ae7677..98c7a4c5ec6 100644 --- a/src/ast/rewriter/pb2bv_rewriter.cpp +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -825,16 +825,17 @@ struct pb2bv_rewriter::imp { if (a->get_family_id() == au.get_family_id()) { switch (a->get_decl_kind()) { case OP_ADD: - for (unsigned i = 0; i < sz; ++i) { - if (!is_pb(a->get_arg(i), mul)) return false; - } + for (unsigned i = 0; i < sz; ++i) + if (!is_pb(a->get_arg(i), mul)) + return false; return true; case OP_SUB: { - if (!is_pb(a->get_arg(0), mul)) return false; + if (!is_pb(a->get_arg(0), mul)) + return false; r = -mul; - for (unsigned i = 1; i < sz; ++i) { - if (!is_pb(a->get_arg(1), r)) return false; - } + for (unsigned i = 1; i < sz; ++i) + if (!is_pb(a->get_arg(i), r)) + return false; return true; } case OP_UMINUS: diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index c2ec7fe3093..938393e88df 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1240,7 +1240,7 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c if (!dt.is_datatype(args[0]->get_sort())) return false; - for (auto* a : dt.plugin().get_accessors(s)) { + for (auto* a : dt.plugin().get_accessors(s)) { fn = a->instantiate(args[0]->get_sort()); r = m().mk_app(fn, num_args, args); return true; diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index fed6aaf7cf7..d334df48f53 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2039,7 +2039,7 @@ namespace pb { for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]); init_use_lists(); - remove_unused_defs(); + // remove_unused_defs(); set_non_external(); elim_pure(); for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]); @@ -2528,8 +2528,13 @@ namespace pb { } void solver::remove_unused_defs() { - if (incremental_mode()) return; + if (incremental_mode()) + return; // remove constraints where indicator literal isn't used. + NOT_IMPLEMENTED_YET(); + // TODO: #6675 + // need to add this inequality to the model reconstruction + // stack in order to produce correct models. for (constraint* cp : m_constraints) { constraint& c = *cp; literal lit = c.lit(); From f0afbcbb877fb6ff222a19e0afa2adefa3debd50 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 20:13:24 -0700 Subject: [PATCH 562/597] fix #6686 --- src/math/interval/interval.h | 9 +++++++++ src/math/interval/interval_def.h | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index fd9f5a2463e..0d036a16e29 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -72,6 +72,7 @@ class im_default_config { void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } + // Reference to numeral manager numeral_manager & m() const { return m_manager; } @@ -184,6 +185,14 @@ class interval_manager { bool upper_is_open(interval const & a) const { return m_c.upper_is_open(a); } bool lower_is_inf(interval const & a) const { return m_c.lower_is_inf(a); } bool upper_is_inf(interval const & a) const { return m_c.upper_is_inf(a); } + bool is_empty(interval const& a) const { + if (lower_is_inf(a) || upper_is_inf(a)) + return false; + ext_numeral_kind lk = lower_kind(a), uk = upper_kind(a); + if (lower_is_open(a) || upper_is_open(a)) + return !(::lt(m(), lower(a), lk, upper(a), uk)); + return ::lt(m(), upper(a), uk, lower(a), lk); + } bool lower_is_neg(interval const & a) const { return ::is_neg(m(), lower(a), lower_kind(a)); } bool lower_is_pos(interval const & a) const { return ::is_pos(m(), lower(a), lower_kind(a)); } diff --git a/src/math/interval/interval_def.h b/src/math/interval/interval_def.h index bbce66420e7..9918f39a1e5 100644 --- a/src/math/interval/interval_def.h +++ b/src/math/interval/interval_def.h @@ -681,7 +681,7 @@ void interval_manager::set(interval & t, interval const & s) { } set_lower_is_open(t, lower_is_open(s)); set_upper_is_open(t, upper_is_open(s)); - SASSERT(check_invariant(t)); + SASSERT(is_empty(t) || check_invariant(t)); } template @@ -813,7 +813,7 @@ void interval_manager::add(interval const & a, interval const & b, interval & set_upper_is_inf(c, new_u_kind == EN_PLUS_INFINITY); set_lower_is_open(c, lower_is_open(a) || lower_is_open(b)); set_upper_is_open(c, upper_is_open(a) || upper_is_open(b)); - SASSERT(check_invariant(c)); + SASSERT(is_empty(a) || is_empty(b) || check_invariant(c)); } template From 7cd8edce1fce4023fc6bcff54d6180987f5aa71e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 21:01:05 -0700 Subject: [PATCH 563/597] perf and memory smash fixes to internal node count routine --- src/ast/for_each_expr.cpp | 27 ++++++++------------------- src/ast/for_each_expr.h | 5 +++-- src/ast/rewriter/bool_rewriter.cpp | 17 +++++++++++++---- src/ast/rewriter/bool_rewriter.h | 2 ++ 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 374d7b496a8..832c1d0bcc0 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -45,7 +45,7 @@ unsigned get_num_exprs(expr * n) { } -static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo, expr * n) { +void get_num_internal_exprs(unsigned_vector& counts, ptr_vector& todo, expr * n) { counts.reserve(n->get_id() + 1); unsigned& rc = counts[n->get_id()]; if (rc > 0) { @@ -55,7 +55,6 @@ static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo rc = n->get_ref_count() - 1; unsigned i = todo.size(); todo.push_back(n); - unsigned count = 0; for (; i < todo.size(); ++i) { n = todo[i]; if (!is_app(n)) @@ -74,27 +73,17 @@ static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo } } -unsigned get_num_internal_exprs(expr * n) { - unsigned_vector counts; - sbuffer todo; +unsigned count_internal_nodes(unsigned_vector& counts, ptr_vector& todo) { unsigned internal_nodes = 0; - get_num_internal_exprs(counts, todo, n); - for (expr* t : todo) - if (counts[t->get_id()] == 0) - ++internal_nodes; - return internal_nodes; -} - -unsigned get_num_internal_exprs(unsigned sz, expr * const * args) { - unsigned_vector counts; - sbuffer todo; - unsigned internal_nodes = 0; - for (unsigned i = 0; i < sz; ++i) - get_num_internal_exprs(counts, todo, args[i]); - for (expr* t : todo) + for (expr* t : todo) { if (counts[t->get_id()] == 0) ++internal_nodes; + else + counts[t->get_id()] = 0; + } + todo.reset(); return internal_nodes; + } diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index c943489640d..0ba0dc9926d 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -163,12 +163,13 @@ struct for_each_expr_proc : public EscapeProc { unsigned get_num_exprs(expr * n); unsigned get_num_exprs(expr * n, expr_mark & visited); unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); -unsigned get_num_internal_exprs(expr * n); -unsigned get_num_internal_exprs(unsigned sz, expr * const * args); +void get_num_internal_exprs(unsigned_vector& counts, ptr_vector& todo, expr * n); +unsigned count_internal_nodes(unsigned_vector& counts, ptr_vector& todo); bool has_skolem_functions(expr * n); // pre-order traversal of subterms + class subterms { bool m_include_bound = false; expr_ref_vector m_es; diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 9ebdbe7fda7..95c0950d87e 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -269,19 +269,28 @@ 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); + expr_ref r(m()); + st = m_hoist.mk_or(buffer.size(), buffer.data(), r); if (st != BR_FAILED) { - unsigned count1 = get_num_internal_exprs(result); - unsigned count2 = get_num_internal_exprs(buffer.size(), buffer.data()); + m_counts1.reserve(m().get_num_asts() + 1); + m_counts2.reserve(m().get_num_asts() + 1); + get_num_internal_exprs(m_counts1, m_todo1, r); + for (unsigned i = 0; i < num_args; ++i) + get_num_internal_exprs(m_counts2, m_todo2, args[i]); + unsigned count1 = count_internal_nodes(m_counts1, m_todo1); + unsigned count2 = count_internal_nodes(m_counts2, m_todo2); if (count1 > count2) st = BR_FAILED; } + if (st != BR_FAILED) + result = r; 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 e02bf86d38f..0693e94ba85 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -62,6 +62,8 @@ class bool_rewriter { unsigned m_local_ctx_limit; unsigned m_local_ctx_cost; bool m_elim_ite; + ptr_vector m_todo1, m_todo2; + unsigned_vector m_counts1, m_counts2; br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result); From b783879752444527a9710fd356c3314bc4d9b1cf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Apr 2023 08:45:11 -0700 Subject: [PATCH 564/597] #6687 --- src/tactic/core/elim_uncnstr_tactic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 869716f590b..a372a1f8b5d 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -804,6 +804,7 @@ class elim_uncnstr_tactic : public tactic { app * r = nullptr; expr* x, *y; if (uncnstr(args[0]) && num == 2 && + args[1]->get_ref_count() == 1 && m_seq_util.str.is_concat(args[1], x, y) && uncnstr(x)) { if (!mk_fresh_uncnstr_var_for(f, num, args, r)) From 1a70ac75df70fb02229d3eebd496721e9d7844e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Apr 2023 09:01:17 -0700 Subject: [PATCH 565/597] fix #6687 --- src/ast/converters/expr_inverter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 4d435f7e8f0..0ee3e130d30 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -757,6 +757,7 @@ class seq_expr_inverter : public iexpr_inverter { expr* x, *y; if (uncnstr(args[0]) && num == 2 && + args[1]->get_ref_count() == 1 && seq.str.is_concat(args[1], x, y) && uncnstr(x)) { mk_fresh_uncnstr_var_for(f, r); From 624907823dbf10e5df874eb72989d6afa807edf8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Apr 2023 11:19:06 -0700 Subject: [PATCH 566/597] add tests for distribution utility and fix loose ends --- src/test/CMakeLists.txt | 1 + src/test/distribution.cpp | 45 +++++++++++++++++++++++++++++++++++++++ src/test/main.cpp | 1 + src/util/distribution.h | 16 ++++++++------ 4 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/test/distribution.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index f959e9bd5c1..df3010295e7 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(test-z3 datalog_parser.cpp ddnf.cpp diff_logic.cpp + distribution.cpp dl_context.cpp dl_product_relation.cpp dl_query.cpp diff --git a/src/test/distribution.cpp b/src/test/distribution.cpp new file mode 100644 index 00000000000..c67757737bf --- /dev/null +++ b/src/test/distribution.cpp @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + distribution.cpp + +Abstract: + + Test distribution + +Author: + + Nikolaj Bjorner (nbjorner) 2023-04-13 + + +--*/ +#include "util/distribution.h" +#include + +static void tst1() { + distribution dist(1); + dist.push(1, 3); + dist.push(2, 1); + dist.push(3, 1); + dist.push(4, 1); + + unsigned counts[4] = { 0, 0, 0, 0 }; + for (unsigned i = 0; i < 1000; ++i) + counts[dist.choose()-1]++; + for (unsigned i = 1; i <= 4; ++i) + std::cout << "count " << i << ": " << counts[i-1] << "\n"; + + for (unsigned i = 0; i < 5; ++i) { + std::cout << "enum "; + for (auto j : dist) + std::cout << j << " "; + std::cout << "\n"; + } + +} + +void tst_distribution() { + tst1(); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index f9e4e081542..7cd4b6cf9d6 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -264,4 +264,5 @@ int main(int argc, char ** argv) { //TST_ARGV(hs); TST(finder); TST(totalizer); + TST(distribution); } diff --git a/src/util/distribution.h b/src/util/distribution.h index de385a08e69..0ed63d510d6 100644 --- a/src/util/distribution.h +++ b/src/util/distribution.h @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2017 Microsoft Corporation +Copyright (c) 2023 Microsoft Corporation Module Name: @@ -18,6 +18,8 @@ Module Name: Distribution class works by pushing identifiers with associated scores. After they have been pushed, you can access a random element using choose or you can enumerate the elements in random order, sorted by the score probability. + Only one iterator can be active at a time because the iterator reshuffles the registered elements. + The requirement is not checked or enforced. --*/ #pragma once @@ -32,10 +34,12 @@ class distribution { unsigned choose(unsigned sum) { unsigned s = m_random(sum); + unsigned idx = 0; for (auto const& [j, score] : m_elems) { if (s < score) - return j; + return idx; s -= score; + ++idx; } UNREACHABLE(); return 0; @@ -76,9 +80,8 @@ class distribution { unsigned m_sum = 0; unsigned m_index = 0; void next_index() { - if (0 == m_sz) - return; - m_index = d.choose(m_sum); + if (0 != m_sz) + m_index = d.choose(m_sum); } public: iterator(distribution& d, bool start): d(d), m_sz(start?d.m_elems.size():0), m_sum(d.m_sum) { @@ -88,8 +91,9 @@ class distribution { iterator operator++() { m_sum -= d.m_elems[m_index].second; --m_sz; - std::swap(d.m_elems[m_index], d.m_elems[d.m_elems.size() - 1]); + std::swap(d.m_elems[m_index], d.m_elems[m_sz]); next_index(); + return *this; } bool operator==(iterator const& other) const { return m_sz == other.m_sz; } bool operator!=(iterator const& other) const { return m_sz != other.m_sz; } From b75d81f3c2b878726982111dc48b9b4765ad7cca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 14 Apr 2023 16:38:33 -0700 Subject: [PATCH 567/597] fix #6690 --- src/sat/smt/arith_internalize.cpp | 6 ++++-- src/smt/theory_lra.cpp | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index d35f7995476..60ca9651a19 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -107,10 +107,12 @@ namespace arith { e = a.mk_idiv0(x, y); } else if (a.is_rem(n, x, y)) { - e = a.mk_rem0(x, y); + n = a.mk_rem(x, a.mk_int(0)); + e = a.mk_rem0(x, a.mk_int(0)); } else if (a.is_mod(n, x, y)) { - e = a.mk_mod0(x, y); + n = a.mk_mod(x, a.mk_int(0)); + e = a.mk_mod0(x, a.mk_int(0)); } else if (a.is_power(n, x, y)) { e = a.mk_power0(x, y); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 3b3e3dd79f4..657c48d389d 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -315,11 +315,13 @@ class theory_lra::imp { else if (a.is_idiv(n, x, y)) { e = a.mk_idiv0(x, y); } - else if (a.is_rem(n, x, y)) { - e = a.mk_rem0(x, y); + else if (a.is_rem(n, x, y)) { + n = a.mk_rem(x, a.mk_int(0)); + e = a.mk_rem0(x, a.mk_int(0)); } else if (a.is_mod(n, x, y)) { - e = a.mk_mod0(x, y); + n = a.mk_mod(x, a.mk_int(0)); + e = a.mk_mod0(x, a.mk_int(0)); } else if (a.is_power(n, x, y)) { e = a.mk_power0(x, y); From 97b66d13c043dd392e5146affd0c7281af8f1311 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2023 17:09:05 -0700 Subject: [PATCH 568/597] fix soundness bug in disabled code --- src/math/lp/gomory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 8c86e0989b0..2ecbc49aca6 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -390,6 +390,7 @@ int gomory::find_basic_var() { const row_strip& row = lra.get_row(lia.row_of_basic_column(result)); if (is_gomory_cut_target(row)) return result; + result = -1; #endif for (unsigned j : lra.r_basis()) { From 1319b64bb03b1db8e2c84b6d04e494aa69603f24 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 17 Apr 2023 09:11:16 -0700 Subject: [PATCH 569/597] fix #6692 --- src/sat/smt/pb_solver.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index d334df48f53..496042f4916 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2858,19 +2858,20 @@ namespace pb { void solver::subsumes(pbc& p1, literal lit) { for (constraint* c : m_cnstr_use_list[lit.index()]) { - if (c == &p1 || c->was_removed()) continue; - bool s = false; + if (c == &p1 || c->was_removed() || c->lit() != sat::null_literal) + continue; + bool sub = false; switch (c->tag()) { case pb::tag_t::card_t: - s = subsumes(p1, c->to_card()); + sub = subsumes(p1, c->to_card()); break; case pb::tag_t::pb_t: - s = subsumes(p1, c->to_pb()); + sub = subsumes(p1, c->to_pb()); break; default: break; } - if (s) { + if (sub) { ++m_stats.m_num_pb_subsumes; set_non_learned(p1); remove_constraint(*c, "subsumed"); From cb041c1b6d43208597744233bfca90ae7340927d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 17 Apr 2023 12:05:08 -0700 Subject: [PATCH 570/597] fix #6689 --- src/ast/normal_forms/nnf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index a3595645471..b04445d16d8 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -149,7 +149,7 @@ class skolemizer { p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) - p = m.mk_skolemization(mk_not(m, q), mk_not(m, r)); + p = m.mk_skolemization(mk_not(m, q), m.mk_not(r)); else p = m.mk_skolemization(q, r); } From ec1480b12a2f10f6050124cfbcf4d3cdf51ecd2a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 18 Apr 2023 08:40:29 -0700 Subject: [PATCH 571/597] fix #6693 --- src/smt/theory_arith_core.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 690775d0835..412744360f3 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -47,11 +47,15 @@ namespace smt { else if (m_util.is_idiv(n)) { e = m_util.mk_idiv0(n->get_arg(0), n->get_arg(1)); } - else if (m_util.is_rem(n)) { - e = m_util.mk_rem0(n->get_arg(0), n->get_arg(1)); - } - else if (m_util.is_mod(n)) { - e = m_util.mk_mod0(n->get_arg(0), n->get_arg(1)); + else if (m_util.is_rem(n)) { + expr* z = m_util.mk_int(0); + e = m_util.mk_rem0(n->get_arg(0), z); + n = m_util.mk_rem(n->get_arg(0), z); + } + else if (m_util.is_mod(n)) { + expr* z = m_util.mk_int(0); + e = m_util.mk_mod0(n->get_arg(0), z); + n = m_util.mk_mod(n->get_arg(0), z); } else if (m_util.is_power(n)) { e = m_util.mk_power0(n->get_arg(0), n->get_arg(1)); From a2bac119d3376ec9be68e88c51590e28772fade3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 18 Apr 2023 08:40:51 -0700 Subject: [PATCH 572/597] differentiate fixed from offset-eq in statistics --- src/math/lp/lp_bound_propagator.h | 8 ++++++-- src/math/lp/lp_settings.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 14f646fc12b..dba93398ea9 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -578,8 +578,12 @@ class lp_bound_propagator { ); bool added = m_imp.add_eq(je, ke, exp, is_fixed); - if (added) - lp().stats().m_offset_eqs++; + if (added) { + if (is_fixed) + lp().stats().m_fixed_eqs++; + else + lp().stats().m_offset_eqs++; + } return added; } diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 9a38fd582cb..5234e3bf0e3 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -121,6 +121,7 @@ struct statistics { unsigned m_grobner_calls; unsigned m_grobner_conflicts; unsigned m_offset_eqs; + unsigned m_fixed_eqs; statistics() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } void collect_statistics(::statistics& st) const { @@ -142,6 +143,7 @@ struct statistics { st.update("arith-grobner-calls", m_grobner_calls); st.update("arith-grobner-conflicts", m_grobner_conflicts); st.update("arith-offset-eqs", m_offset_eqs); + st.update("arith-fixed-eqs", m_fixed_eqs); } }; From 7a689c3298ea70d100eea427d3edc0d613c7cd73 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2023 17:59:41 -0700 Subject: [PATCH 573/597] disable destructive equality resolution simplification if there are patterns - regression from F\star - reported by @mtzguido (stlc_min.smt2) --- src/ast/rewriter/th_rewriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index d75a31a66f2..9278ae5aed4 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -827,7 +827,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr_ref r(m()); bool der_change = false; - if (is_quantifier(result)) { + if (is_quantifier(result) && to_quantifier(result)->get_num_patterns() == 0) { m_der(to_quantifier(result), r, p2); der_change = result.get() != r.get(); if (m().proofs_enabled() && der_change) From fdd5c923edce21a8690ff4c246a6c88e77579e63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2023 20:20:26 -0700 Subject: [PATCH 574/597] use only maxres if there is a lexicographic objective, fix #6697 - maxlex.enable heuristic does not work if it is chained among multiple objectives. Only maxres is set up to commit the proper constraints. --- src/opt/maxsmt.cpp | 6 +++--- src/opt/maxsmt.h | 2 +- src/opt/opt_context.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 3d083447203..37f888120da 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -178,14 +178,14 @@ namespace opt { maxsmt::maxsmt(maxsat_context& c, unsigned index): m(c.get_manager()), m_c(c), m_index(index), m_answer(m) {} - lbool maxsmt::operator()() { + lbool maxsmt::operator()(bool committed) { lbool is_sat = l_undef; m_msolver = nullptr; opt_params optp(m_params); symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); TRACE("opt_verbose", s().display(tout << "maxsmt\n") << "\n";); - if (optp.maxlex_enable() && is_maxlex(m_soft)) + if (!committed && optp.maxlex_enable() && is_maxlex(m_soft)) m_msolver = mk_maxlex(m_c, m_index, m_soft); else if (m_soft.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) m_msolver = mk_maxres(m_c, m_index, m_soft); @@ -401,7 +401,7 @@ namespace opt { for (auto const& p : soft) { maxsmt.add(p.first, p.second); } - lbool r = maxsmt(); + lbool r = maxsmt(true); if (r == l_true) { svector labels; maxsmt.get_model(m_model, labels); diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index ac39d78913a..17306b222f6 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -137,7 +137,7 @@ namespace opt { params_ref m_params; public: maxsmt(maxsat_context& c, unsigned id); - lbool operator()(); + lbool operator()(bool committed); void updt_params(params_ref& p); void add(expr* f, rational const& w); unsigned size() const { return m_soft.size(); } diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 5895643bd06..11eddc2eb08 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -453,8 +453,8 @@ namespace opt { lbool context::execute_maxsat(symbol const& id, bool committed, bool scoped) { model_ref tmp; maxsmt& ms = *m_maxsmts.find(id); - if (scoped) get_solver().push(); - lbool result = ms(); + if (scoped) get_solver().push(); + lbool result = ms(committed); if (result != l_false && (ms.get_model(tmp, m_labels), tmp.get())) { ms.get_model(m_model, m_labels); } From d8156aeff31fb224531b026daacfafe2222f00e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2023 21:14:42 -0700 Subject: [PATCH 575/597] weird latent bug in wmax: init() succeeds and it returns undef --- src/opt/maxsmt.cpp | 3 ++- src/opt/wmax.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 37f888120da..e684c64d6d0 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -213,7 +213,8 @@ namespace opt { try { is_sat = (*m_msolver)(); } - catch (z3_exception&) { + catch (z3_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n"); is_sat = l_undef; } if (is_sat != l_false) { diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index 1fb6c05dda1..9a7d1a3ca51 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -53,7 +53,7 @@ namespace opt { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); reset(); - if (init()) + if (!init()) return l_undef; lbool is_sat = l_true; From d4fa990b6e5c433dc5205dfb1f012385fb7425da Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:47:02 -0700 Subject: [PATCH 576/597] return diagnostics --- src/math/lp/bound_analyzer_on_row.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 6084145c287..0008a0ee90b 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -54,32 +54,40 @@ public : {} - static void analyze_row(const C & row, + static unsigned analyze_row(const C & row, unsigned bj, // basis column for the row const numeric_pair& rs, unsigned row_or_term_index, B & bp) { bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp); - a.analyze(); + return a.analyze(); } private: - void analyze() { + unsigned analyze() { + unsigned num_prop = 0; for (const auto & c : m_row) { if ((m_column_of_l == -2) && (m_column_of_u == -2)) - return; + return 0; analyze_bound_on_var_on_coeff(c.var(), c.coeff()); } + ++num_prop; if (m_column_of_u >= 0) limit_monoid_u_from_below(); else if (m_column_of_u == -1) limit_all_monoids_from_below(); + else + --num_prop; + ++num_prop; if (m_column_of_l >= 0) limit_monoid_l_from_above(); else if (m_column_of_l == -1) limit_all_monoids_from_above(); + else + --num_prop; + return num_prop; } bool bound_is_available(unsigned j, bool lower_bound) { From e689dea99ce257d4cef7c73c831448ebcc17616f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:47:49 -0700 Subject: [PATCH 577/597] basic formatting updates --- src/math/lp/lar_solver.cpp | 66 ++++++++++++++------------------------ 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 21eec31d839..e127a53d15d 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1,11 +1,12 @@ -#include "math/lp/lar_solver.h" -#include "smt/params/smt_params_helper.hpp" - /* Copyright (c) 2017 Microsoft Corporation Author: Nikolaj Bjorner, Lev Nachmanson */ +#include "math/lp/lar_solver.h" +#include "smt/params/smt_params_helper.hpp" + + namespace lp { lp_settings& lar_solver::settings() { return m_settings; } @@ -134,10 +135,9 @@ namespace lp { bool lar_solver::row_has_a_big_num(unsigned i) const { - for (const auto& c : A_r().m_rows[i]) { + for (const auto& c : A_r().m_rows[i]) if (c.coeff().is_big()) return true; - } return false; } @@ -601,15 +601,14 @@ namespace lp { else { const lar_term& term = *m_terms[tv::unmask_term(t.second)]; - for (auto p : term) { + for (auto p : term) register_monoid_in_map(coeffs, t.first * p.coeff(), p.column()); - } } } - for (auto& p : coeffs) - if (!is_zero(p.second)) - left_side.push_back(std::make_pair(p.second, p.first)); + for (auto& [v, c] : coeffs) + if (!is_zero(c)) + left_side.push_back(std::make_pair(c, v)); } void lar_solver::insert_row_with_changed_bounds(unsigned rid) { @@ -633,8 +632,7 @@ namespace lp { r += c.coeff() * m_mpq_lar_core_solver.m_r_x[c.var()]; } CTRACE("lar_solver", !is_zero(r), tout << "row = " << i << ", j = " << m_mpq_lar_core_solver.m_r_basis[i] << "\n"; - print_row(A_r().m_rows[i], tout); tout << " = " << r << "\n"; - ); + print_row(A_r().m_rows[i], tout); tout << " = " << r << "\n"); return is_zero(r); } @@ -692,15 +690,11 @@ namespace lp { } } - void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { - if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]); - return; - } - - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); - + else + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); } void lar_solver::detect_rows_with_changed_bounds() { @@ -1442,8 +1436,7 @@ namespace lp { register_new_ext_var_index(ext_j, is_int); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); - add_new_var_to_core_fields_for_mpq(false); // false for not adding a row - + add_new_var_to_core_fields_for_mpq(false); // false for not adding a row } void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { @@ -1481,24 +1474,21 @@ namespace lp { #if Z3DEBUG_CHECK_UNIQUE_TERMS bool lar_solver::term_coeffs_are_ok(const vector>& coeffs) { - for (const auto& p : coeffs) { + for (const auto& p : coeffs) if (column_is_real(p.second)) return true; - } mpq g; bool g_is_set = false; for (const auto& p : coeffs) { - if (!p.first.is_int()) { + if (!p.first.is_int()) return false; - } if (!g_is_set) { g_is_set = true; g = p.first; } - else { + else g = gcd(g, p.first); - } } if (g == one_of_type()) return true; @@ -1524,20 +1514,17 @@ namespace lp { std::set seen_terms; for (auto p : *t) { auto j = p.column(); - if (this->column_corresponds_to_term(j)) { + if (this->column_corresponds_to_term(j)) seen_terms.insert(j); - } } while (!seen_terms.empty()) { unsigned j = *seen_terms.begin(); seen_terms.erase(j); auto tj = this->m_var_register.local_to_external(j); auto& ot = this->get_term(tj); - for (auto p : ot){ - if (this->column_corresponds_to_term(p.column())) { + for (auto p : ot) + if (this->column_corresponds_to_term(p.column())) seen_terms.insert(p.column()); - } - } t->subst_by_term(ot, j); } } @@ -1555,15 +1542,14 @@ namespace lp { SASSERT(m_terms.size() == m_term_register.size()); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = tv::mask_term(adjusted_term_index); - if ( !coeffs.empty()) { + if (!coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); if (m_settings.bound_propagation()) insert_row_with_changed_bounds(A_r().row_count() - 1); } lp_assert(m_var_register.size() == A_r().column_count()); - if (m_need_register_terms) { + if (m_need_register_terms) register_normalized_term(*t, A_r().column_count() - 1); - } return ret; } @@ -1585,12 +1571,10 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); for (lar_term::ival c : *term) { unsigned j = c.column(); - while (m_usage_in_terms.size() <= j) { + while (m_usage_in_terms.size() <= j) m_usage_in_terms.push_back(0); - } m_usage_in_terms[j] = m_usage_in_terms[j] + 1; } - } void lar_solver::add_basic_var_to_core_fields() { @@ -1603,9 +1587,7 @@ namespace lp { } bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const { - if (!column_is_int(j)) - return true; - return right_side.is_int(); + return !column_is_int(j) || right_side.is_int(); } constraint_index lar_solver::add_var_bound_check_on_equal(var_index j, lconstraint_kind kind, const mpq& right_side, var_index& equal_var) { From 8fb45158724de904bb0cafd5b9df0ded1b201af0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:48:33 -0700 Subject: [PATCH 578/597] remove redundant function, add checker function to test missed propagations --- src/math/lp/lar_solver.h | 43 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 955710b3c52..10ca16d0a9f 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -161,28 +161,23 @@ class lar_solver : public column_namer { bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; - template - void analyze_new_bounds_on_row_tableau( - unsigned row_index, - lp_bound_propagator & bp ) { - - if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation - || row_has_a_big_num(row_index)) - return; - - bound_analyzer_on_row, lp_bound_propagator>::analyze_row(A_r().m_rows[row_index], - null_ci, - zero_of_type>(), - row_index, - bp - ); - } void substitute_basis_var_in_terms_for_row(unsigned i); + template - void calculate_implied_bounds_for_row(unsigned i, lp_bound_propagator & bp) { - analyze_new_bounds_on_row_tableau(i, bp); + unsigned calculate_implied_bounds_for_row(unsigned row_index, lp_bound_propagator & bp) { + + if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation || row_has_a_big_num(row_index)) + return 0; + + return bound_analyzer_on_row, lp_bound_propagator>::analyze_row( + A_r().m_rows[row_index], + null_ci, + zero_of_type>(), + row_index, + bp); } + static void clean_popped_elements(unsigned n, u_set& set); bool maximize_term_on_tableau(const lar_term & term, impq &term_max); @@ -339,8 +334,9 @@ class lar_solver : public column_namer { void mark_rows_for_bound_prop(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { + unsigned num_prop = 0; for (unsigned i : m_rows_with_changed_bounds) { - calculate_implied_bounds_for_row(i, bp); + num_prop += calculate_implied_bounds_for_row(i, bp); if (settings().get_cancel_flag()) return; } @@ -360,6 +356,15 @@ class lar_solver : public column_namer { } m_rows_with_changed_bounds.clear(); } + + template + void check_missed_propagations(lp_bound_propagator & bp) { + for (unsigned i = 0; i < A_r().row_count(); i++) + if (!m_rows_with_changed_bounds.contains(i)) + if (0 < calculate_implied_bounds_for_row(i, bp)) { + verbose_stream() << i << ": " << get_row(i) << "\n"; + } + } bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } From ace6e8eea107e93c5400caff92963c73e9ad4526 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:49:06 -0700 Subject: [PATCH 579/597] add gcd-conflicts stats, formatting updates --- src/smt/theory_arith.h | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 34a76d95537..a2a7617e54f 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -41,7 +41,7 @@ Revision History: namespace smt { struct theory_arith_stats { - unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_patches, m_patches_succ; + unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_gcd_conflicts, m_patches, m_patches_succ; unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs; unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs; unsigned m_max_min; @@ -452,18 +452,18 @@ namespace smt { svector m_var_pos; // temporary array used in add_rows atoms m_atoms; // set of theory atoms ptr_vector m_asserted_bounds; // set of asserted bounds - unsigned m_asserted_qhead; + unsigned m_asserted_qhead = 0; ptr_vector m_new_atoms; // new bound atoms that have yet to be internalized. svector m_nl_monomials; // non linear monomials svector m_nl_propagated; // non linear monomials that became linear v_dependency_manager m_dep_manager; // for tracking bounds during non-linear reasoning vector m_row_vars; // variables in a given row. Used during internalization to detect repeated variables. - unsigned m_row_vars_top; + unsigned m_row_vars_top = 0; var_heap m_to_patch; // heap containing all variables v s.t. m_value[v] does not satisfy bounds of v. nat_set m_left_basis; // temporary: set of variables that already left the basis in make_feasible - bool m_blands_rule; + bool m_blands_rule = false; svector m_update_trail_stack; // temporary trail stack used to restore the last feasible assignment. nat_set m_in_update_trail_stack; // set of variables in m_update_trail_stack @@ -473,11 +473,11 @@ namespace smt { inf_numeral m_tmp; random_gen m_random; - unsigned m_num_conflicts; + unsigned m_num_conflicts = 0; - unsigned m_branch_cut_counter; + unsigned m_branch_cut_counter = 0; bool m_eager_gcd; // true if gcd should be applied at every add_row - unsigned m_final_check_idx; + unsigned m_final_check_idx = 0; // backtracking @@ -676,7 +676,7 @@ namespace smt { See also m_changed_assignment flag. */ - bool m_liberal_final_check; + bool m_liberal_final_check = true; final_check_status final_check_core(); final_check_status final_check_eh() override; @@ -734,7 +734,7 @@ namespace smt { // Assignment management // // ----------------------------------- - bool m_changed_assignment; //!< auxiliary variable set to true when the assignment is changed. + bool m_changed_assignment = false; //!< auxiliary variable set to true when the assignment is changed. void save_value(theory_var v); void discard_update_trail(); void restore_assignment(); @@ -790,11 +790,12 @@ namespace smt { void mark_row_for_bound_prop(unsigned r1); void mark_rows_for_bound_prop(theory_var v); void is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const; - void imply_bound_for_monomial(row const & r, int idx, bool lower); - void imply_bound_for_all_monomials(row const & r, bool lower); + unsigned imply_bound_for_monomial(row const & r, int idx, bool lower); + unsigned imply_bound_for_all_monomials(row const & r, bool lower); void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, antecedents & antecedents); - void mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); + bool m_validating = false; + unsigned mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta); void propagate_bounds(); @@ -821,7 +822,7 @@ namespace smt { var_set m_tmp_var_set; var_set m_tmp_var_set2; svector > m_assume_eq_candidates; - unsigned m_assume_eq_head; + unsigned m_assume_eq_head = 0; bool random_update(theory_var v); void mutate_assignment(); bool assume_eqs_core(); @@ -953,10 +954,10 @@ namespace smt { // // ----------------------------------- typedef int_hashtable > row_set; - bool m_model_depends_on_computed_epsilon; - unsigned m_nl_rounds; - bool m_nl_gb_exhausted; - unsigned m_nl_strategy_idx; // for fairness + bool m_model_depends_on_computed_epsilon = false; + unsigned m_nl_rounds = 0; + bool m_nl_gb_exhausted = false; + unsigned m_nl_strategy_idx = 0; // for fairness expr_ref_vector m_nl_new_exprs; typedef obj_map var2num_occs; var2num_occs m_var2num_occs; From 59bc070268e955cc19a639fdc7f83df4abea49b4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 16:31:56 -0700 Subject: [PATCH 580/597] count gcd conflicts --- src/smt/theory_arith_core.h | 1 - src/smt/theory_arith_int.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 412744360f3..3da706e677f 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -1735,7 +1735,6 @@ namespace smt { m_util(m), m_arith_eq_solver(m), m_arith_eq_adapter(*this, m_util), - m_asserted_qhead(0), m_row_vars_top(0), m_to_patch(1024), m_blands_rule(false), diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 7b76960dd68..c9bc9f31abb 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -754,6 +754,7 @@ namespace smt { if (!(consts / gcds).is_int()) { TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, r);); antecedents ante(*this); + m_stats.m_gcd_conflicts++; collect_fixed_var_justifications(r, ante); context & ctx = get_context(); ctx.set_conflict( @@ -833,6 +834,7 @@ namespace smt { numeral u1 = floor(u/gcds); if (u1 < l1) { + m_stats.m_gcd_conflicts++; TRACE("gcd_test", tout << "row failed the extended GCD test:\n"; display_row_info(tout, r);); collect_fixed_var_justifications(r, ante); context & ctx = get_context(); From 50c855e2ebc715972b1fa8365a5e4c8fc66cda06 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 16:32:16 -0700 Subject: [PATCH 581/597] count gcd conflicts, log row id in rows --- src/smt/theory_arith_pp.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index d10bfca55a2..b0d43bc00ac 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -37,6 +37,7 @@ namespace smt { st.update("arith assume eqs", m_stats.m_assume_eqs); st.update("arith offset eqs", m_stats.m_offset_eqs); st.update("arith gcd tests", m_stats.m_gcd_tests); + st.update("arith gcd conflicts", m_stats.m_gcd_conflicts); st.update("arith ineq splits", m_stats.m_branches); st.update("arith gomory cuts", m_stats.m_gomory_cuts); st.update("arith branch int", m_stats.m_branch_infeasible_int); @@ -82,8 +83,9 @@ namespace smt { template void theory_arith::display_row(std::ostream & out, row const & r, bool compact) const { - - out << "(v" << r.get_base_var() << ") : "; + + column const & c = m_columns[r.get_base_var()]; + out << "(v" << r.get_base_var() << " r" << c[0].m_row_id << ") : "; bool first = true; for (auto const& e : r) { if (!e.is_dead()) { From 3029fb24a16c7d22597ab529172cbf0584c68ecc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 16:34:58 -0700 Subject: [PATCH 582/597] remove references to validating --- src/smt/theory_arith.h | 1 - src/smt/theory_arith_core.h | 62 ++++++++++++++++++++++--------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index a2a7617e54f..92aa4baec66 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -794,7 +794,6 @@ namespace smt { unsigned imply_bound_for_all_monomials(row const & r, bool lower); void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, antecedents & antecedents); - bool m_validating = false; unsigned mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta); void propagate_bounds(); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 3da706e677f..159758c80a1 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -1735,22 +1735,11 @@ namespace smt { m_util(m), m_arith_eq_solver(m), m_arith_eq_adapter(*this, m_util), - m_row_vars_top(0), m_to_patch(1024), - m_blands_rule(false), m_random(ctx.get_fparams().m_arith_random_seed), - m_num_conflicts(0), - m_branch_cut_counter(0), m_eager_gcd(m_params.m_arith_eager_gcd), - m_final_check_idx(0), m_antecedents_index(0), m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), - m_liberal_final_check(true), - m_changed_assignment(false), - m_assume_eq_head(0), - m_model_depends_on_computed_epsilon(false), - m_nl_rounds(0), - m_nl_gb_exhausted(false), m_nl_new_exprs(m), m_bound_watch(null_bool_var) { } @@ -2702,8 +2691,9 @@ namespace smt { Then this bound is used to produce a bound for the monomial variable. */ template - void theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { + unsigned theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { row_entry const & entry = r[idx]; + unsigned count = 0; if (m_unassigned_atoms[entry.m_var] > 0) { inf_numeral implied_k; typename vector::const_iterator it = r.begin_entries(); @@ -2725,7 +2715,7 @@ namespace smt { tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); - mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); + count += mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); } } else { @@ -2736,10 +2726,11 @@ namespace smt { tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); - mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); + count += mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); } } } + return count; } /** @@ -2750,7 +2741,7 @@ namespace smt { for the monomial variables. */ template - void theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { + unsigned theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { // Traverse the row once and compute // bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true // bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false @@ -2763,6 +2754,7 @@ namespace smt { } } + unsigned count = 0; inf_numeral implied_k; typename vector::const_iterator it = r.begin(); typename vector::const_iterator end = r.end(); @@ -2786,7 +2778,7 @@ namespace smt { tout << "implying lower bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); - mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); + count += mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); } } else { @@ -2798,11 +2790,12 @@ namespace smt { tout << "implying upper bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); - mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); + count += mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); } } } } + return count; } /** @@ -2924,10 +2917,11 @@ namespace smt { } template - void theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { + unsigned theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { atoms const & as = m_var_occs[v]; inf_numeral const & epsilon = get_epsilon(v); inf_numeral delta; + unsigned count = 0; for (atom* a : as) { bool_var bv = a->get_bool_var(); literal l(bv); @@ -2944,6 +2938,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); + ++count; } // v <= k k < k2 |- v < k2 |- not v >= k2 if (kind == B_UPPER && k < k2) { @@ -2960,6 +2955,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); + ++count; } } } @@ -2975,6 +2971,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); + ++count; } } // v <= k k <= k2 |- v <= k2 @@ -2986,10 +2983,12 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); + ++count; } } } } + return count; } @@ -3000,11 +2999,17 @@ namespace smt { antecedents ante(*this); explain_bound(r, idx, is_lower, delta, ante); + TRACE("propagate_bounds", ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); + + + TRACE("arith", tout << ctx.get_scope_level() << "\n"; + ctx.display_detailed_literal(tout, l) << "\n"); + if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { literal_vector & lits = m_tmp_literal_vector2; lits.reset(); @@ -3033,6 +3038,7 @@ namespace smt { template void theory_arith::propagate_bounds() { TRACE("propagate_bounds_detail", display(tout);); + unsigned num_prop = 0, count = 0; for (unsigned r_idx : m_to_check) { row & r = m_rows[r_idx]; if (r.get_base_var() != null_theory_var) { @@ -3041,15 +3047,21 @@ namespace smt { int upper_idx; is_row_useful_for_bound_prop(r, lower_idx, upper_idx); + ++num_prop; if (lower_idx >= 0) - imply_bound_for_monomial(r, lower_idx, true); + count += imply_bound_for_monomial(r, lower_idx, true); else if (lower_idx == -1) - imply_bound_for_all_monomials(r, true); - + count += imply_bound_for_all_monomials(r, true); + else + --num_prop; + + ++num_prop; if (upper_idx >= 0) - imply_bound_for_monomial(r, upper_idx, false); + count += imply_bound_for_monomial(r, upper_idx, false); else if (upper_idx == -1) - imply_bound_for_all_monomials(r, false); + count += imply_bound_for_all_monomials(r, false); + else + --num_prop; // sneaking cheap eq detection in this loop propagate_cheap_eq(r_idx); @@ -3065,6 +3077,7 @@ namespace smt { #endif } } + TRACE("arith_eq", tout << "done\n";); m_to_check.reset(); m_in_to_check.reset(); @@ -3379,7 +3392,7 @@ namespace smt { } template - void theory_arith::pop_scope_eh(unsigned num_scopes) { + void theory_arith::pop_scope_eh(unsigned num_scopes) { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); @@ -3399,7 +3412,6 @@ namespace smt { restore_unassigned_atoms(s.m_unassigned_atoms_trail_lim); m_asserted_bounds.shrink(s.m_asserted_bounds_lim); m_asserted_qhead = s.m_asserted_qhead_old; - TRACE("arith_pop_scope_bug", tout << "num_vars: " << get_num_vars() << ", num_old_vars: " << get_old_num_vars(num_scopes) << "\n";); restore_nl_propagated_flag(s.m_nl_propagated_lim); m_nl_monomials.shrink(s.m_nl_monomials_lim); del_atoms(s.m_atoms_lim); From d2e3e4895e5d672fe1cf4d748ab0da4e23584e95 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2023 10:04:45 -0700 Subject: [PATCH 583/597] add instrumentation to theory_lra for shuffling final check --- src/math/lp/hnf_cutter.cpp | 3 +- src/smt/theory_lra.cpp | 198 ++++++++++++++++++++++++++----------- 2 files changed, 144 insertions(+), 57 deletions(-) diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index c1f93c9d893..3c4ea10ab92 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -248,9 +248,8 @@ branch y_i >= ceil(y0_i) is impossible. bool hnf_cutter::init_terms_for_hnf_cut() { clear(); - for (unsigned i = 0; i < lra.terms().size() && !is_full(); i++) { + for (unsigned i = 0; i < lra.terms().size() && !is_full(); i++) try_add_term_to_A_for_hnf(tv::term(i)); - } return hnf_has_var_with_non_integral_value(); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 657c48d389d..bea08011697 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1539,10 +1539,14 @@ class theory_lra::imp { } } - bool assume_eqs() { + bool assume_eqs() { + if (delayed_assume_eqs()) + return true; + TRACE("arith", display(tout);); random_update(); m_model_eqs.reset(); + theory_var sz = static_cast(th.get_num_vars()); unsigned old_sz = m_assume_eq_candidates.size(); unsigned num_candidates = 0; @@ -1639,6 +1643,8 @@ class theory_lra::imp { return FC_GIVEUP; } + unsigned m_final_check_idx = 0; + final_check_status final_check_eh() { if (propagate_core()) return FC_CONTINUE; @@ -1649,44 +1655,79 @@ class theory_lra::imp { if (!lp().is_feasible() || lp().has_changed_columns()) { is_sat = make_feasible(); } - final_check_status st = FC_DONE; - + final_check_status st = FC_DONE, result = FC_DONE; + m_final_check_idx = 0; // remove to experiment. + unsigned old_idx = m_final_check_idx; + switch (is_sat) { case l_true: TRACE("arith", display(tout)); - switch (check_lia()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - TRACE("arith", tout << "check-lia giveup\n";); - if (ctx().get_fparams().m_arith_ignore_int) +#if 0 + distribution dist(++m_final_check_idx); + + dist.add(0, 2); + dist.add(1, 1); + dist.add(1, 1); + + for (auto idx : dist) { + if (!m.inc()) return FC_GIVEUP; - st = FC_CONTINUE; - break; - } - switch (check_nla()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - TRACE("arith", tout << "check-nra giveup\n";); - st = FC_GIVEUP; - break; + switch (idx) { + case 0: + case 1: + case 2: + default: + + } } +#endif - if (delayed_assume_eqs()) { - ++m_stats.m_assume_eqs; - return FC_CONTINUE; - } - if (assume_eqs()) { - ++m_stats.m_assume_eqs; - return FC_CONTINUE; + do { + if (!m.inc()) + return FC_GIVEUP; + + switch (m_final_check_idx) { + case 0: + if (assume_eqs()) { + ++m_stats.m_assume_eqs; + st = FC_CONTINUE; + } + break; + case 1: + st = check_lia(); + break; + case 2: + switch (check_nla()) { + case l_true: + st = FC_DONE; + break; + case l_false: + st = FC_CONTINUE; + break; + case l_undef: + TRACE("arith", tout << "check-nra giveup\n";); + st = FC_GIVEUP; + break; + } + break; + } + m_final_check_idx = (m_final_check_idx + 1) % 3; + switch (st) { + case FC_DONE: + break; + case FC_CONTINUE: + return st; + case FC_GIVEUP: + result = st; + break; + } } + while (old_idx != m_final_check_idx); + + if (result == FC_GIVEUP) + return result; for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; @@ -1914,21 +1955,21 @@ class theory_lra::imp { visitor.display_asserts(out, fmls, true); out << "(check-sat)\n"; } - - lbool check_lia() { + + final_check_status check_lia() { TRACE("arith",); if (!m.inc()) { TRACE("arith", tout << "canceled\n";); - return l_undef; + return FC_CONTINUE; } - lbool lia_check = l_undef; + final_check_status lia_check = FC_GIVEUP; auto cr = m_lia->check(&m_explanation); if (cr != lp::lia_move::sat && ctx().get_fparams().m_arith_ignore_int) - return l_undef; + return FC_GIVEUP; switch (cr) { case lp::lia_move::sat: - lia_check = l_true; + lia_check = FC_DONE; break; case lp::lia_move::branch: { @@ -1951,13 +1992,13 @@ class theory_lra::imp { // TBD: ctx().force_phase(ctx().get_literal(b)); // at this point we have a new unassigned atom that the // SAT core assigns a value to - lia_check = l_false; + lia_check = FC_CONTINUE; ++m_stats.m_branch; break; } case lp::lia_move::cut: { if (ctx().get_fparams().m_arith_ignore_int) - return l_undef; + return FC_GIVEUP; TRACE("arith", tout << "cut\n";); ++m_stats.m_gomory_cuts; // m_explanation implies term <= k @@ -1979,26 +2020,26 @@ class theory_lra::imp { ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.data(), m_eqs.size(), m_eqs.data(), lit); display(tout);); assign(lit, m_core, m_eqs, m_params); - lia_check = l_false; + lia_check = FC_CONTINUE; break; } case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); // ex contains unsat core set_conflict(); - return l_false; + return FC_CONTINUE; case lp::lia_move::undef: TRACE("arith", tout << "lia undef\n";); - lia_check = l_undef; + lia_check = FC_CONTINUE; break; case lp::lia_move::continue_with_check: - lia_check = l_undef; + lia_check = FC_CONTINUE; break; default: UNREACHABLE(); } if (lia_check != l_false && !check_idiv_bounds()) - return l_false; + return FC_CONTINUE; return lia_check; } @@ -2188,7 +2229,6 @@ class theory_lra::imp { set_evidence(j, m_core, m_eqs); m_explanation.add_pair(j, v); } - void propagate_bounds_with_lp_solver() { if (!should_propagate()) @@ -2202,13 +2242,16 @@ class theory_lra::imp { if (is_infeasible()) { get_infeasibility_explanation_and_set_conflict(); + // verbose_stream() << "unsat\n"; } else { + unsigned count = 0, prop = 0; for (auto& ib : m_bp.ibounds()) { m.inc(); if (ctx().inconsistent()) break; - propagate_lp_solver_bound(ib); + ++prop; + count += propagate_lp_solver_bound(ib); } } } @@ -2229,12 +2272,14 @@ class theory_lra::imp { return false; } - void propagate_lp_solver_bound(const lp::implied_bound& be) { + +#if 0 + unsigned propagate_lp_solver_bound_dry_run(const lp::implied_bound& be) { lpvar vi = be.m_j; theory_var v = lp().local_to_external(vi); if (v == null_theory_var) - return; + return 0; TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n";); @@ -2242,21 +2287,59 @@ class theory_lra::imp { if (m_unassigned_bounds[v] == 0 && !should_refine_bounds()) { TRACE("arith", tout << "return\n";); - return; + return 0; } lp_bounds const& bounds = m_bounds[v]; bool first = true; + unsigned count = 0; for (unsigned i = 0; i < bounds.size(); ++i) { api_bound* b = bounds[i]; - if (ctx().get_assignment(b->get_lit()) != l_undef) { + if (ctx().get_assignment(b->get_lit()) != l_undef) continue; - } literal lit = is_bound_implied(be.kind(), be.m_bound, *b); - if (lit == null_literal) { + if (lit == null_literal) + continue; + TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + ctx().display_literal_verbose(verbose_stream() << "miss ", lit) << "\n"; + display(verbose_stream()); + TRACE("arith", ctx().display_literal_verbose(tout << "miss ", lit) << "\n"); + exit(0); + + ++count; + } + return count; + } +#endif + + unsigned propagate_lp_solver_bound(const lp::implied_bound& be) { + lpvar vi = be.m_j; + theory_var v = lp().local_to_external(vi); + + if (v == null_theory_var) + return 0; + + TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n";); + + reserve_bounds(v); + + if (m_unassigned_bounds[v] == 0 && !should_refine_bounds()) { + TRACE("arith", tout << "return\n";); + return 0; + } + lp_bounds const& bounds = m_bounds[v]; + bool first = true; + unsigned count = 0; + for (unsigned i = 0; i < bounds.size(); ++i) { + api_bound* b = bounds[i]; + if (ctx().get_assignment(b->get_lit()) != l_undef) + continue; + literal lit = is_bound_implied(be.kind(), be.m_bound, *b); + if (lit == null_literal) continue; - } TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + ++count; + lp().settings().stats().m_num_of_implied_bounds ++; if (first) { first = false; @@ -2274,6 +2357,8 @@ class theory_lra::imp { display_evidence(tout, m_explanation); lp().print_implied_bound(be, tout); ); + + DEBUG_CODE( for (auto& lit : m_core) { VERIFY(ctx().get_assignment(lit) == l_true); @@ -2283,7 +2368,9 @@ class theory_lra::imp { } if (should_refine_bounds() && first) - refine_bound(v, be); + refine_bound(v, be); + + return count; } void refine_bound(theory_var v, const lp::implied_bound& be) { @@ -2907,7 +2994,7 @@ class theory_lra::imp { propagate_eqs(b.tv(), ci, k, b, value.get_rational()); } #if 0 - if (propagation_mode() != BP_NONE) + if (should_propagate()) lp().mark_rows_for_bound_prop(b.tv().id()); #endif } @@ -3928,5 +4015,6 @@ void theory_lra::setup() { } template class lp::lp_bound_propagator; template void lp::lar_solver::propagate_bounds_for_touched_rows(lp::lp_bound_propagator&); +template void lp::lar_solver::check_missed_propagations(lp::lp_bound_propagator&); template void lp::lar_solver::explain_implied_bound(const lp::implied_bound&, lp::lp_bound_propagator&); -template void lp::lar_solver::calculate_implied_bounds_for_row(unsigned int, lp::lp_bound_propagator&); +template unsigned lp::lar_solver::calculate_implied_bounds_for_row(unsigned int, lp::lp_bound_propagator&); From ef943347eec0fd7af188659fc5f7316518fcbb6b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2023 11:16:15 -0700 Subject: [PATCH 584/597] ensure assume-eqs is invoked after check-lia statically Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 9 +++++ src/math/lp/lar_solver.h | 4 +- src/math/lp/lp_settings.h | 77 ++++++++++++++++---------------------- src/smt/theory_lra.cpp | 13 ++++--- 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index e127a53d15d..1016c972b2e 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1378,6 +1378,15 @@ namespace lp { return m_mpq_lar_core_solver.column_is_free(j); } + // column is at lower or upper bound, lower and upper bound are different. + // the lower/upper bound is not strict. + // the LP obtained by making the bound strict is infeasible + // -> the column has to be fixed + bool is_fixed_at_bound(column_index const& j) { + NOT_IMPLEMENTED_YET(); + return false; + } + // below is the initialization functionality of lar_solver bool lar_solver::strategy_is_undecided() const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 10ca16d0a9f..76d8528a0aa 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -365,8 +365,10 @@ class lar_solver : public column_namer { verbose_stream() << i << ": " << get_row(i) << "\n"; } } + + bool is_fixed_at_bound(column_index const& j); - bool is_fixed(column_index const& j) const { return column_is_fixed(j); } + bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } bool external_is_used(unsigned) const; void pop(unsigned k); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 5234e3bf0e3..c213333e0e3 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -163,11 +163,11 @@ struct lp_settings { }; default_lp_resource_limit m_default_resource_limit; - lp_resource_limit* m_resource_limit; + lp_resource_limit* m_resource_limit = nullptr; // used for debug output - std::ostream* m_debug_out; + std::ostream* m_debug_out = nullptr; // used for messages, for example, the computation progress messages - std::ostream* m_message_out; + std::ostream* m_message_out = nullptr; statistics m_stats; random_gen m_rand; @@ -178,40 +178,40 @@ struct lp_settings { unsigned nlsat_delay() const { return m_nlsat_delay; } bool int_run_gcd_test() const { return m_int_run_gcd_test; } bool& int_run_gcd_test() { return m_int_run_gcd_test; } - unsigned reps_in_scaler { 20 }; - int c_partial_pivoting { 10 }; // this is the constant c from page 410 - unsigned depth_of_rook_search { 4 }; - bool using_partial_pivoting { true }; + unsigned reps_in_scaler = 20; + int c_partial_pivoting = 10; // this is the constant c from page 410 + unsigned depth_of_rook_search = 4; + bool using_partial_pivoting = true; - unsigned percent_of_entering_to_check { 5 }; // we try to find a profitable column in a percentage of the columns - bool use_scaling { true }; - unsigned max_number_of_iterations_with_no_improvements { 2000000 }; + unsigned percent_of_entering_to_check = 5; // we try to find a profitable column in a percentage of the columns + bool use_scaling = true; + unsigned max_number_of_iterations_with_no_improvements = 2000000; double time_limit; // the maximum time limit of the total run time in seconds // end of dual section - bool m_bound_propagation { true }; - bool presolve_with_double_solver_for_lar { true }; + bool m_bound_propagation = true; + bool presolve_with_double_solver_for_lar = true; simplex_strategy_enum m_simplex_strategy; - int report_frequency { 1000 }; - bool print_statistics { false }; - unsigned column_norms_update_frequency { 12000 }; - bool scale_with_ratio { true }; - unsigned max_row_length_for_bound_propagation { 300 }; - bool backup_costs { true }; - unsigned column_number_threshold_for_using_lu_in_lar_solver { 4000 }; - unsigned m_int_gomory_cut_period { 4 }; - unsigned m_int_find_cube_period { 4 }; + int report_frequency = 1000; + bool print_statistics = false; + unsigned column_norms_update_frequency = 12000; + bool scale_with_ratio = true; + unsigned max_row_length_for_bound_propagation = 300; + bool backup_costs = true; + unsigned column_number_threshold_for_using_lu_in_lar_solver = 4000; + unsigned m_int_gomory_cut_period = 4; + unsigned m_int_find_cube_period = 4; private: - unsigned m_hnf_cut_period { 4 }; - bool m_int_run_gcd_test { true }; + unsigned m_hnf_cut_period = 4; + bool m_int_run_gcd_test = true; public: - unsigned limit_on_rows_for_hnf_cutter { 75 }; - unsigned limit_on_columns_for_hnf_cutter { 150 }; + unsigned limit_on_rows_for_hnf_cutter = 75; + unsigned limit_on_columns_for_hnf_cutter = 150; private: unsigned m_nlsat_delay; - bool m_enable_hnf { true }; - bool m_print_external_var_name { false }; - bool m_propagate_eqs { false }; + bool m_enable_hnf = true; + bool m_print_external_var_name = false; + bool m_propagate_eqs = false; public: bool print_external_var_name() const { return m_print_external_var_name; } bool propagate_eqs() const { return m_propagate_eqs;} @@ -244,25 +244,12 @@ struct lp_settings { std::ostream* get_debug_ostream() { return m_debug_out; } std::ostream* get_message_ostream() { return m_message_out; } statistics& stats() { return m_stats; } - statistics const& stats() const { return m_stats; } - - - - + statistics const& stats() const { return m_stats; } // the method of lar solver to use - simplex_strategy_enum simplex_strategy() const { - return m_simplex_strategy; - } - - void set_simplex_strategy(simplex_strategy_enum s) { - m_simplex_strategy = s; - } - - - bool use_tableau_rows() const { - return m_simplex_strategy == simplex_strategy_enum::tableau_rows; - } + simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; } + void set_simplex_strategy(simplex_strategy_enum s) { m_simplex_strategy = s; } + bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } #ifdef Z3DEBUG static unsigned ddd; // used for debugging diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index bea08011697..f5a10f94215 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1595,8 +1595,10 @@ class theory_lra::imp { CTRACE("arith", is_eq(v1, v2) && n1->get_root() != n2->get_root(), tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); - if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) + if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { + ++m_stats.m_assume_eqs; return true; + } } return false; } @@ -1690,13 +1692,12 @@ class theory_lra::imp { switch (m_final_check_idx) { case 0: - if (assume_eqs()) { - ++m_stats.m_assume_eqs; - st = FC_CONTINUE; - } + st = check_lia(); break; case 1: - st = check_lia(); + if (assume_eqs()) + st = FC_CONTINUE; + break; case 2: switch (check_nla()) { From c48dc6905056c7dbfc976ff7c54e3f7b962c240f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2023 19:39:42 -0700 Subject: [PATCH 585/597] adding stubs to find fixed variables Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 55 +++++++++++++++++- src/math/lp/lar_solver.h | 1 + src/smt/theory_lra.cpp | 114 +++++++++++++++++++------------------ 3 files changed, 112 insertions(+), 58 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 1016c972b2e..0eb65e1973b 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1382,11 +1382,62 @@ namespace lp { // the lower/upper bound is not strict. // the LP obtained by making the bound strict is infeasible // -> the column has to be fixed - bool is_fixed_at_bound(column_index const& j) { - NOT_IMPLEMENTED_YET(); + bool lar_solver::is_fixed_at_bound(column_index const& j) { + if (column_is_fixed(j)) + return false; + mpq val; + if (!has_value(j, val)) + return false; + lp::lconstraint_kind k; + if (column_has_upper_bound(j) && + get_upper_bound(j).x == val) { + verbose_stream() << "check upper " << j << "\n"; + push(); + if (column_is_int(j)) + k = LE, val -= 1; + else + k = LT; + auto ci = mk_var_bound(j, k, val); + update_column_type_and_bound(j, k, val, ci); + auto st = find_feasible_solution(); + pop(1); + return st == lp_status::INFEASIBLE; + } + if (column_has_lower_bound(j) && + get_lower_bound(j).x == val) { + verbose_stream() << "check lower " << j << "\n"; + push(); + if (column_is_int(j)) + k = GE, val += 1; + else + k = GT; + auto ci = mk_var_bound(j, k, val); + update_column_type_and_bound(j, k, val, ci); + auto st = find_feasible_solution(); + pop(1); + return st == lp_status::INFEASIBLE; + } + return false; } + bool lar_solver::has_fixed_at_bound() { + verbose_stream() << "has-fixed-at-bound\n"; + unsigned num_fixed = 0; + for (unsigned j = 0; j < A_r().m_columns.size(); ++j) { + auto ci = column_index(j); + if (is_fixed_at_bound(ci)) { + ++num_fixed; + verbose_stream() << "fixed " << j << "\n"; + } + } + verbose_stream() << "num fixed " << num_fixed << "\n"; + if (num_fixed > 0) + find_feasible_solution(); + return num_fixed > 0; + } + + // below is the initialization functionality of lar_solver bool lar_solver::strategy_is_undecided() const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 76d8528a0aa..182ef0be3bf 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -367,6 +367,7 @@ class lar_solver : public column_namer { } bool is_fixed_at_bound(column_index const& j); + bool has_fixed_at_bound(); bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f5a10f94215..a5a31709b26 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -45,6 +45,7 @@ #include "ast/ast_ll_pp.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" +#include "util/distribution.h" typedef lp::var_index lpvar; @@ -1646,6 +1647,7 @@ class theory_lra::imp { } unsigned m_final_check_idx = 0; + distribution m_dist { 0 }; final_check_status final_check_eh() { if (propagate_core()) @@ -1657,34 +1659,53 @@ class theory_lra::imp { if (!lp().is_feasible() || lp().has_changed_columns()) { is_sat = make_feasible(); } - final_check_status st = FC_DONE, result = FC_DONE; - m_final_check_idx = 0; // remove to experiment. + bool giveup = false; + final_check_status st = FC_DONE; + // m_final_check_idx = 0; // remove to experiment. unsigned old_idx = m_final_check_idx; - switch (is_sat) { case l_true: TRACE("arith", display(tout)); -#if 0 - distribution dist(++m_final_check_idx); + // if (lp().has_fixed_at_bound()) // explain and propagate. - dist.add(0, 2); - dist.add(1, 1); - dist.add(1, 1); +#if 0 + m_dist.reset(); + m_dist.push(0, 1); + m_dist.push(1, 1); + m_dist.push(2, 1); - for (auto idx : dist) { + for (auto idx : m_dist) { if (!m.inc()) return FC_GIVEUP; switch (idx) { case 0: + if (assume_eqs()) + st = FC_CONTINUE; + break; case 1: + st = check_nla(); + break; case 2: + st = check_lia(); + break; default: - + UNREACHABLE(); + break; + } + switch (st) { + case FC_DONE: + break; + case FC_CONTINUE: + return st; + case FC_GIVEUP: + giveup = true; + break; } } -#endif + +#else do { if (!m.inc()) @@ -1696,22 +1717,10 @@ class theory_lra::imp { break; case 1: if (assume_eqs()) - st = FC_CONTINUE; - + st = FC_CONTINUE; break; case 2: - switch (check_nla()) { - case l_true: - st = FC_DONE; - break; - case l_false: - st = FC_CONTINUE; - break; - case l_undef: - TRACE("arith", tout << "check-nra giveup\n";); - st = FC_GIVEUP; - break; - } + st = check_nla(); break; } m_final_check_idx = (m_final_check_idx + 1) % 3; @@ -1721,14 +1730,15 @@ class theory_lra::imp { case FC_CONTINUE: return st; case FC_GIVEUP: - result = st; + giveup = true; break; } } while (old_idx != m_final_check_idx); +#endif - if (result == FC_GIVEUP) - return result; + if (giveup) + return FC_GIVEUP; for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; @@ -1963,14 +1973,12 @@ class theory_lra::imp { TRACE("arith", tout << "canceled\n";); return FC_CONTINUE; } - final_check_status lia_check = FC_GIVEUP; auto cr = m_lia->check(&m_explanation); if (cr != lp::lia_move::sat && ctx().get_fparams().m_arith_ignore_int) return FC_GIVEUP; switch (cr) { case lp::lia_move::sat: - lia_check = FC_DONE; break; case lp::lia_move::branch: { @@ -1993,9 +2001,8 @@ class theory_lra::imp { // TBD: ctx().force_phase(ctx().get_literal(b)); // at this point we have a new unassigned atom that the // SAT core assigns a value to - lia_check = FC_CONTINUE; ++m_stats.m_branch; - break; + return FC_CONTINUE; } case lp::lia_move::cut: { if (ctx().get_fparams().m_arith_ignore_int) @@ -2021,8 +2028,7 @@ class theory_lra::imp { ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.data(), m_eqs.size(), m_eqs.data(), lit); display(tout);); assign(lit, m_core, m_eqs, m_params); - lia_check = FC_CONTINUE; - break; + return FC_CONTINUE; } case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); @@ -2031,18 +2037,19 @@ class theory_lra::imp { return FC_CONTINUE; case lp::lia_move::undef: TRACE("arith", tout << "lia undef\n";); - lia_check = FC_CONTINUE; - break; + return FC_CONTINUE; case lp::lia_move::continue_with_check: - lia_check = FC_CONTINUE; - break; + return FC_CONTINUE; default: UNREACHABLE(); } - if (lia_check != l_false && !check_idiv_bounds()) + if (!check_idiv_bounds()) return FC_CONTINUE; - return lia_check; + if (assume_eqs()) + return FC_CONTINUE; + + return FC_DONE; } nla::lemma m_lemma; @@ -2079,36 +2086,31 @@ class theory_lra::imp { set_conflict_or_lemma(core, false); } - lbool check_nla_continue() { + final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; lbool r = m_nla->check(m_nla_lemma_vector); switch (r) { - case l_false: { + case l_false: for (const nla::lemma & l : m_nla_lemma_vector) - false_case_of_check_nla(l); - break; - } + false_case_of_check_nla(l); + return FC_CONTINUE; case l_true: - if (assume_eqs()) { - return l_false; - } - break; - case l_undef: - break; + return assume_eqs()? FC_CONTINUE: FC_DONE; + default: + return FC_GIVEUP; } - return r; } - lbool check_nla() { + final_check_status check_nla() { if (!m.inc()) { TRACE("arith", tout << "canceled\n";); - return l_undef; + return FC_GIVEUP; } CTRACE("arith",!m_nla, tout << "no nla\n";); if (!m_nla) - return l_true; + return FC_DONE; if (!m_nla->need_check()) - return l_true; + return FC_DONE; return check_nla_continue(); } From d5231f8b3318ca4eee922a29414218d9f7a82922 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 27 Apr 2023 08:43:59 -0700 Subject: [PATCH 586/597] fix regressions #6703 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a5a31709b26..c6bd12f03a1 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1661,7 +1661,7 @@ class theory_lra::imp { } bool giveup = false; final_check_status st = FC_DONE; - // m_final_check_idx = 0; // remove to experiment. + m_final_check_idx = 0; // remove to experiment. unsigned old_idx = m_final_check_idx; switch (is_sat) { case l_true: @@ -1713,12 +1713,12 @@ class theory_lra::imp { switch (m_final_check_idx) { case 0: - st = check_lia(); - break; - case 1: if (assume_eqs()) st = FC_CONTINUE; break; + case 1: + st = check_lia(); + break; case 2: st = check_nla(); break; From 392266c278c3b1ca21a89610e8a731c40a41bca2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 12:16:58 -0700 Subject: [PATCH 587/597] fix processing of else expression for model table --- src/model/func_interp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index b180a8a1f9c..ebb22f079c7 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -146,7 +146,8 @@ void func_interp::set_else(expr * e) { ptr_vector args; while (e && is_fi_entry_expr(e, args)) { - insert_entry(args.data(), to_app(e)->get_arg(1)); + if (!get_entry(args.data())) + insert_entry(args.data(), to_app(e)->get_arg(1)); e = to_app(e)->get_arg(2); } From c64d61bd0a19907233ac1bffa5b9fdc9edb15713 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 12:17:32 -0700 Subject: [PATCH 588/597] formatting updates --- src/model/model_smt2_pp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/model/model_smt2_pp.cpp b/src/model/model_smt2_pp.cpp index df1a8b73414..b5ac3fbad00 100644 --- a/src/model/model_smt2_pp.cpp +++ b/src/model/model_smt2_pp.cpp @@ -195,12 +195,10 @@ static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core co ptr_buffer func_decls; sort_fun_decls(m, md, func_decls); for (func_decl * f : func_decls) { - if (recfun_util.is_defined(f) && !recfun_util.is_generated(f)) { + if (recfun_util.is_defined(f) && !recfun_util.is_generated(f)) continue; - } - if (!m.is_considered_uninterpreted(f)) { + if (!m.is_considered_uninterpreted(f)) continue; - } func_interp * f_i = md.get_func_interp(f); SASSERT(f->get_arity() == f_i->get_arity()); format_ref body(fm(m)); From f17691715bbe6c87e9e40921bd354336a43a8e52 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 12:18:31 -0700 Subject: [PATCH 589/597] make default argument to ensure_def and mk_def explicit - insert also macro definitions into models --- src/api/api_ast.cpp | 6 ++---- src/ast/recfun_decl_plugin.cpp | 4 ++-- src/ast/recfun_decl_plugin.h | 5 +++-- src/cmd_context/cmd_context.cpp | 36 +++++++++++++++++++-------------- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index bc76d02bccb..bc29826ff1a 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -120,10 +120,8 @@ extern "C" { RESET_ERROR_CODE(); // recfun::promise_def def = - mk_c(c)->recfun().get_plugin().mk_def(to_symbol(s), - domain_size, - to_sorts(domain), - to_sort(range)); + mk_c(c)->recfun().get_plugin().mk_def( + to_symbol(s), domain_size, to_sorts(domain), to_sort(range), false); func_decl* d = def.get_def()->get_decl(); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 84d68d7823f..7a3e9521da6 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -492,7 +492,7 @@ namespace recfun { def* plugin::mk_def(replace& subst, bool is_macro, symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs) { - promise_def d = mk_def(name, n, params, range); + promise_def d = mk_def(name, n, params, range, false); SASSERT(! m_defs.contains(d.get_def()->get_decl())); set_definition(subst, d, is_macro, n_vars, vars, rhs); return d.get_def(); @@ -581,7 +581,7 @@ namespace recfun { } symbol fresh_name("fold-rec-" + std::to_string(m().mk_fresh_id())); - auto pd = mk_def(fresh_name, n, domain.data(), max_expr->get_sort()); + auto pd = mk_def(fresh_name, n, domain.data(), max_expr->get_sort(), false); func_decl* f = pd.get_def()->get_decl(); expr_ref new_body(m().mk_app(f, n, args.data()), m()); set_definition(subst, pd, false, n, vars, max_expr); diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index 66544bec7b3..442bdadbd0b 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -192,9 +192,9 @@ namespace recfun { void get_op_names(svector & op_names, symbol const & logic) override; - promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); + promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated); - promise_def ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); + promise_def ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated); void set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs); @@ -248,6 +248,7 @@ namespace recfun { bool is_defined(expr * e) const { return is_app_of(e, m_fid, OP_FUN_DEFINED); } bool is_defined(func_decl* f) const { return is_decl_of(f, m_fid, OP_FUN_DEFINED); } bool is_generated(func_decl* f) const { return is_defined(f) && f->get_parameter(0).get_int() == 1; } + bool is_macro(func_decl* f) { return is_defined(f) && get_def(f).is_macro(); } bool is_num_rounds(expr * e) const { return is_app_of(e, m_fid, OP_NUM_ROUNDS); } bool owns_app(app * e) const { return e->get_family_id() == m_fid; } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 938393e88df..a4adbb0ed76 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -361,7 +361,7 @@ void cmd_context::insert_macro(symbol const& s, unsigned arity, sort*const* doma vars.push_back(m().mk_var(i, domain[i])); rvars.push_back(m().mk_var(i, domain[arity - i - 1])); } - recfun::promise_def d = p.ensure_def(s, arity, domain, t->get_sort()); + recfun::promise_def d = p.ensure_def(s, arity, domain, t->get_sort(), false); // recursive functions have opposite calling convention from macros! var_subst sub(m(), true); @@ -984,7 +984,7 @@ recfun::decl::plugin& cmd_context::get_recfun_plugin() { recfun::promise_def cmd_context::decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range) { SASSERT(logic_has_recfun()); - return get_recfun_plugin().mk_def(name, arity, domain, range); + return get_recfun_plugin().mk_def(name, arity, domain, range, false); } void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* rhs) { @@ -1982,25 +1982,31 @@ void cmd_context::complete_model(model_ref& md) const { } } - for (auto kd : m_func_decls) { - symbol const & k = kd.m_key; - func_decls & v = kd.m_value; + for (auto& [k, v] : m_func_decls) { IF_VERBOSE(12, verbose_stream() << "(model.completion " << k << ")\n"; ); for (unsigned i = 0; i < v.get_num_entries(); i++) { func_decl * f = v.get_entry(i); - if (!md->has_interpretation(f)) { - sort * range = f->get_range(); - expr * some_val = m().get_some_value(range); - if (f->get_arity() > 0) { - func_interp * fi = alloc(func_interp, m(), f->get_arity()); - fi->set_else(some_val); - md->register_decl(f, fi); - } - else - md->register_decl(f, some_val); + + if (md->has_interpretation(f)) + continue; + macro_decls decls; + expr* body = nullptr; + + if (m_macros.find(k, decls)) + body = decls.find(f->get_arity(), f->get_domain()); + sort * range = f->get_range(); + if (!body) + body = m().get_some_value(range); + if (f->get_arity() > 0) { + func_interp * fi = alloc(func_interp, m(), f->get_arity()); + fi->set_else(body); + md->register_decl(f, fi); } + else + md->register_decl(f, body); } } + verbose_stream() << *md << "\n"; } /** From 6c24a70c44aeab3d3de78f8af71fce454cd02342 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 13:05:08 -0700 Subject: [PATCH 590/597] remove debug output Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index a4adbb0ed76..8d1e375d101 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -2006,7 +2006,6 @@ void cmd_context::complete_model(model_ref& md) const { md->register_decl(f, body); } } - verbose_stream() << *md << "\n"; } /** From 0c9a5f69fd97e48e45da3dd7ab4edc7b2eb73e4f Mon Sep 17 00:00:00 2001 From: Guillaume Bagan Date: Sat, 6 May 2023 20:53:43 +0200 Subject: [PATCH 591/597] JS/TS: add Optimize class (#6712) * implement Optimize class for the high level Typescript API * javascript and wasm: add _malloc to exported functions fix the bug https://github.com/Z3Prover/z3/issues/6709 * javascript: add tests for the Optimize class * javascript: no reason that minimize and optimize must be constants --- src/api/js/scripts/build-wasm.ts | 2 +- src/api/js/src/high-level/high-level.test.ts | 47 ++++++++++ src/api/js/src/high-level/high-level.ts | 99 ++++++++++++++++++++ src/api/js/src/high-level/types.ts | 37 ++++++++ 4 files changed, 184 insertions(+), 1 deletion(-) diff --git a/src/api/js/scripts/build-wasm.ts b/src/api/js/scripts/build-wasm.ts index 3caf643df51..0f51c84d3ee 100644 --- a/src/api/js/scripts/build-wasm.ts +++ b/src/api/js/scripts/build-wasm.ts @@ -40,7 +40,7 @@ function spawnSync(command: string, opts: SpawnOptions = {}) { } function exportedFuncs(): string[] { - const extras = ['_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)]; + const extras = ['_malloc', '_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)]; // TODO(ritave): This variable is unused in original script, find out if it's important const fns: any[] = (functions as any[]).filter(f => !asyncFuncs.includes(f.name)); diff --git a/src/api/js/src/high-level/high-level.test.ts b/src/api/js/src/high-level/high-level.test.ts index d4207516982..99f95e5b4c6 100644 --- a/src/api/js/src/high-level/high-level.test.ts +++ b/src/api/js/src/high-level/high-level.test.ts @@ -698,4 +698,51 @@ describe('high-level', () => { expect(m.eval(f.call(0, 0)).eqIdentity(Int.val(0))).toBeTruthy(); }); }); + + describe('optimize', () => { + it("maximization problem over reals", async () => { + const { Real, Optimize } = api.Context('main'); + + const opt = new Optimize(); + const x = Real.const('x'); + const y = Real.const('y'); + const z = Real.const('z'); + + opt.add(x.ge(0), y.ge(0), z.ge(0)); + opt.add(x.le(1), y.le(1), z.le(1)); + opt.maximize(x.mul(7).add(y.mul(9)).sub(z.mul(3))) + + const result = await opt.check() + expect(result).toStrictEqual('sat'); + const model = opt.model(); + expect(model.eval(x).eqIdentity(Real.val(1))).toBeTruthy(); + expect(model.eval(y).eqIdentity(Real.val(1))).toBeTruthy(); + expect(model.eval(z).eqIdentity(Real.val(0))).toBeTruthy(); + }); + + it("minimization problem over integers using addSoft", async () => { + const { Int, Optimize } = api.Context('main'); + + const opt = new Optimize(); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + opt.add(x.ge(0), y.ge(0)); + opt.add(x.le(1), y.le(1)); + opt.addSoft(x.eq(1), 2); + opt.addSoft(y.eq(1), 1); + opt.add(z.eq(x.mul(5).add(y.mul(5)))); + opt.add(z.le(5)); + opt.minimize(z); + + const result = await opt.check() + expect(result).toStrictEqual('sat'); + const model = opt.model(); + expect(model.eval(x).eqIdentity(Int.val(1))).toBeTruthy(); + expect(model.eval(y).eqIdentity(Int.val(0))).toBeTruthy(); + expect(model.eval(z).eqIdentity(Int.val(5))).toBeTruthy(); + }); + }); + }); diff --git a/src/api/js/src/high-level/high-level.ts b/src/api/js/src/high-level/high-level.ts index 80f89b104eb..0869dbd7b46 100644 --- a/src/api/js/src/high-level/high-level.ts +++ b/src/api/js/src/high-level/high-level.ts @@ -35,6 +35,7 @@ import { Z3_app, Z3_params, Z3_func_entry, + Z3_optimize, } from '../low-level'; import { AnyAst, @@ -68,6 +69,7 @@ import { FuncInterp, IntNum, Model, + Optimize, Pattern, Probe, Quantifier, @@ -1327,6 +1329,102 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class OptimizeImpl implements Optimize { + declare readonly __typename: Optimize['__typename']; + + readonly ptr: Z3_optimize; + readonly ctx: Context; + + constructor(ptr: Z3_optimize = Z3.mk_optimize(contextPtr)) { + this.ctx = ctx; + let myPtr: Z3_optimize; + myPtr = ptr; + this.ptr = myPtr; + Z3.optimize_inc_ref(contextPtr, myPtr); + cleanup.register(this, () => Z3.optimize_dec_ref(contextPtr, myPtr)); + } + + set(key: string, value: any): void { + Z3.optimize_set_params(contextPtr, this.ptr, _toParams(key, value)); + } + + push() { + Z3.optimize_push(contextPtr, this.ptr); + } + + pop() { + Z3.optimize_pop(contextPtr, this.ptr); + } + + add(...exprs: (Bool | AstVector>)[]) { + _flattenArgs(exprs).forEach(expr => { + _assertContext(expr); + check(Z3.optimize_assert(contextPtr, this.ptr, expr.ast)); + }); + } + + addSoft(expr: Bool, weight: number | bigint | string | CoercibleRational, id: number | string = "") { + if (isCoercibleRational(weight)) { + weight = `${weight.numerator}/${weight.denominator}`; + } + check(Z3.optimize_assert_soft(contextPtr, this.ptr, expr.ast, weight.toString(), _toSymbol(id))) + } + + addAndTrack(expr: Bool, constant: Bool | string) { + if (typeof constant === 'string') { + constant = Bool.const(constant); + } + assert(isConst(constant), 'Provided expression that is not a constant to addAndTrack'); + check(Z3.optimize_assert_and_track(contextPtr, this.ptr, expr.ast, constant.ast)); + } + + assertions(): AstVector> { + return new AstVectorImpl(check(Z3.optimize_get_assertions(contextPtr, this.ptr))); + } + + maximize(expr: Arith) { + check(Z3.optimize_maximize(contextPtr, this.ptr, expr.ast)); + } + + minimize(expr: Arith) { + check(Z3.optimize_minimize(contextPtr, this.ptr, expr.ast)); + } + + async check(...exprs: (Bool | AstVector>)[]): Promise { + const assumptions = _flattenArgs(exprs).map(expr => { + _assertContext(expr); + return expr.ast; + }); + const result = await asyncMutex.runExclusive(() => + check(Z3.optimize_check(contextPtr, this.ptr, assumptions)), + ); + switch (result) { + case Z3_lbool.Z3_L_FALSE: + return 'unsat'; + case Z3_lbool.Z3_L_TRUE: + return 'sat'; + case Z3_lbool.Z3_L_UNDEF: + return 'unknown'; + default: + assertExhaustive(result); + } + } + + model() { + return new ModelImpl(check(Z3.optimize_get_model(contextPtr, this.ptr))); + } + + toString() { + return check(Z3.optimize_to_string(contextPtr, this.ptr)); + } + + fromString(s: string) { + Z3.optimize_from_string(contextPtr, this.ptr, s); + throwIfError(); + } + } + + class ModelImpl implements Model { declare readonly __typename: Model['__typename']; readonly ctx: Context; @@ -2671,6 +2769,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { // Classes // ///////////// Solver: SolverImpl, + Optimize: OptimizeImpl, Model: ModelImpl, Tactic: TacticImpl, AstVector: AstVectorImpl as AstVectorCtor, diff --git a/src/api/js/src/high-level/types.ts b/src/api/js/src/high-level/types.ts index c4b56044208..6f3630a6db5 100644 --- a/src/api/js/src/high-level/types.ts +++ b/src/api/js/src/high-level/types.ts @@ -10,6 +10,7 @@ import { Z3_model, Z3_probe, Z3_solver, + Z3_optimize, Z3_sort, Z3_sort_kind, Z3_tactic, @@ -317,6 +318,9 @@ export interface Context { * @category Classes */ readonly Solver: new (logic?: string) => Solver; + + readonly Optimize: new () => Optimize; + /** * Creates an empty Model * @see {@link Solver.model} for common usage of Model @@ -661,6 +665,39 @@ export interface Solver { model(): Model; } +export interface Optimize { + /** @hidden */ + readonly __typename: 'Optimize'; + + readonly ctx: Context; + readonly ptr: Z3_optimize; + + set(key: string, value: any): void; + + push(): void; + + pop(num?: number): void; + + add(...exprs: (Bool | AstVector>)[]): void; + + addSoft(expr: Bool, weight: number | bigint | string | CoercibleRational, id?: number | string): void; + + addAndTrack(expr: Bool, constant: Bool | string): void; + + assertions(): AstVector>; + + fromString(s: string): void; + + maximize(expr: Arith): void; + + minimize(expr: Arith): void; + + check(...exprs: (Bool | AstVector>)[]): Promise; + + model(): Model; +} + + /** @hidden */ export interface ModelCtor { new (): Model; From 2e441e38c9c3ba202a4c215224c2526100dd422b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 8 May 2023 12:24:20 -0700 Subject: [PATCH 592/597] fix #6713 fix #6714 Signed-off-by: Nikolaj Bjorner --- src/ast/array_decl_plugin.cpp | 4 ++-- src/ast/simplifiers/dependent_expr_state.cpp | 2 +- src/smt/theory_arith_nl.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index 7c2b357c2a7..6778bec7cb5 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -315,13 +315,13 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domain, unsigned i) { if (arity != 2 || domain[0] != domain[1]) { - UNREACHABLE(); + m_manager->raise_exception("incorrect arguments passed to array-ext"); return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); if (num_parameters == 0 || i >= num_parameters - 1) { - UNREACHABLE(); + m_manager->raise_exception("incorrect arguments passed to array-ext"); return nullptr; } sort * r = to_sort(s->get_parameter(i).get_ast()); diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp index d784e6fe42e..7d00708ac2a 100644 --- a/src/ast/simplifiers/dependent_expr_state.cpp +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -81,7 +81,7 @@ void dependent_expr_state::freeze_recfun() { ast_mark visited; for (func_decl* f : rec.get_rec_funs()) { auto& d = rec.get_def(f); - if (!d.is_macro()) + if (!d.is_macro() && d.get_rhs()) freeze_terms(d.get_rhs(), false, visited); } m_trail.push(value_trail(m_num_recfun)); diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index d6d30cb33a5..f44516cad0f 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -892,6 +892,7 @@ bool theory_arith::propagate_linear_monomial(theory_var v) { } tout << "\n";); + return true; } From 12e45c9d17aa48151b2c20573fb3b527b32fdb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Hyv=C3=A4rinen?= Date: Tue, 9 May 2023 19:37:46 +0200 Subject: [PATCH 593/597] Implement proposed smtlib2 bitvector overflow predicates (#6715) * Logical names for function declarations in c++ Currently, for example, the function declaration symbol member for checking whether multiplication *does not* overflow is called `m_bv_smul_ovfl`. Since we are introducing the upcoming smtlib2 symbols that check that multpliciation *does* overflow, the not overflow check symbols are renamed to `m_bv_smul_no_ovfl` etc. * Implement smtlib overflow preds for multiplication Smtlib2 is being extended to include overflow predicates for bit vectors (see https://groups.google.com/u/1/g/smt-lib/c/J4D99wT0aKI). This commit introduces the predicates `bvumulo` and `bvsmulo` that return `true` if the unsigned multiplication overflows or the signed multiplication underflows or overflows, respectively. * Move mul overflow predicates to BV logic * Add a todo on illogical argument order * Implement mk_unary_pred for bv * Implement bvnego * Implement bvuaddo * Implement bvsaddo * Implement bvusubo * Implement bvssubo * Implement bvsdivo --- src/ast/bv_decl_plugin.cpp | 47 +++++++++++- src/ast/bv_decl_plugin.h | 43 ++++++++++- src/ast/rewriter/bv_rewriter.cpp | 123 +++++++++++++++++++++++++++++++ src/ast/rewriter/bv_rewriter.h | 16 ++++ 4 files changed, 222 insertions(+), 7 deletions(-) diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 351d037cb29..4d38327a5e7 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -118,9 +118,22 @@ void bv_decl_plugin::finalize() { DEC_REF(m_bv_redand); DEC_REF(m_bv_comp); + DEC_REF(m_bv_mul_no_ovfl); + DEC_REF(m_bv_smul_no_ovfl); + DEC_REF(m_bv_smul_no_udfl); + DEC_REF(m_bv_mul_ovfl); DEC_REF(m_bv_smul_ovfl); - DEC_REF(m_bv_smul_udfl); + + DEC_REF(m_bv_neg_ovfl); + + DEC_REF(m_bv_uadd_ovfl); + DEC_REF(m_bv_sadd_ovfl); + + DEC_REF(m_bv_usub_ovfl); + DEC_REF(m_bv_ssub_ovfl); + + DEC_REF(m_bv_sdiv_ovfl); DEC_REF(m_bv_shl); DEC_REF(m_bv_lshr); @@ -245,6 +258,16 @@ func_decl * bv_decl_plugin::mk_bv2int(unsigned bv_size, unsigned num_parameters, return m_bv2int[bv_size]; } +func_decl * bv_decl_plugin::mk_unary_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { + force_ptr_array_size(decls, bv_size+1); + + if (decls[bv_size] == 0) { + decls[bv_size] = m_manager->mk_func_decl(symbol(name), get_bv_sort(bv_size), m_manager->mk_bool_sort(), func_decl_info(m_family_id, k)); + m_manager->inc_ref(decls[bv_size]); + } + return decls[bv_size]; +} + func_decl * bv_decl_plugin::mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); @@ -289,6 +312,7 @@ func_decl * bv_decl_plugin::mk_comp(unsigned bv_size) { func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { switch (k) { case OP_BNEG: return mk_unary(m_bv_neg, k, "bvneg", bv_size); + case OP_BNEG_OVFL: return mk_unary_pred(m_bv_neg_ovfl, k, "bvnego", bv_size); case OP_BADD: return mk_binary(m_bv_add, k, "bvadd", bv_size, true); case OP_BSUB: return mk_binary(m_bv_sub, k, "bvsub", bv_size, false); case OP_BMUL: return mk_binary(m_bv_mul, k, "bvmul", bv_size, true); @@ -327,9 +351,16 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { case OP_BREDOR: return mk_reduction(m_bv_redor, k, "bvredor", bv_size); case OP_BREDAND: return mk_reduction(m_bv_redand, k, "bvredand", bv_size); case OP_BCOMP: return mk_comp(bv_size); - case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumul_noovfl", bv_size); - case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmul_noovfl", bv_size); - case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_udfl, k, "bvsmul_noudfl", bv_size); + case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_no_ovfl, k, "bvumul_noovfl", bv_size); + case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_no_ovfl, k, "bvsmul_noovfl", bv_size); + case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_no_udfl, k, "bvsmul_noudfl", bv_size); + case OP_BUMUL_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumulo", bv_size); + case OP_BSMUL_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmulo", bv_size); + case OP_BSDIV_OVFL: return mk_pred(m_bv_sdiv_ovfl, k, "bvsdivo", bv_size); + case OP_BUADD_OVFL: return mk_pred(m_bv_uadd_ovfl, k, "bvuaddo", bv_size); + case OP_BSADD_OVFL: return mk_pred(m_bv_sadd_ovfl, k, "bvsaddo", bv_size); + case OP_BUSUB_OVFL: return mk_pred(m_bv_usub_ovfl, k, "bvusubo", bv_size); + case OP_BSSUB_OVFL: return mk_pred(m_bv_ssub_ovfl, k, "bvssubo", bv_size); case OP_BSHL: return mk_binary(m_bv_shl, k, "bvshl", bv_size, false); case OP_BLSHR: return mk_binary(m_bv_lshr, k, "bvlshr", bv_size, false); @@ -681,10 +712,18 @@ void bv_decl_plugin::get_op_names(svector & op_names, symbol const op_names.push_back(builtin_name("bit1",OP_BIT1)); op_names.push_back(builtin_name("bit0",OP_BIT0)); op_names.push_back(builtin_name("bvneg",OP_BNEG)); + op_names.push_back(builtin_name("bvnego", OP_BNEG_OVFL)); op_names.push_back(builtin_name("bvadd",OP_BADD)); + op_names.push_back(builtin_name("bvuaddo",OP_BUADD_OVFL)); + op_names.push_back(builtin_name("bvsaddo",OP_BSADD_OVFL)); op_names.push_back(builtin_name("bvsub",OP_BSUB)); + op_names.push_back(builtin_name("bvusubo",OP_BUSUB_OVFL)); + op_names.push_back(builtin_name("bvssubo",OP_BSSUB_OVFL)); op_names.push_back(builtin_name("bvmul",OP_BMUL)); + op_names.push_back(builtin_name("bvumulo",OP_BUMUL_OVFL)); + op_names.push_back(builtin_name("bvsmulo",OP_BSMUL_OVFL)); op_names.push_back(builtin_name("bvsdiv",OP_BSDIV)); + op_names.push_back(builtin_name("bvsdivo",OP_BSDIV_OVFL)); op_names.push_back(builtin_name("bvudiv",OP_BUDIV)); op_names.push_back(builtin_name("bvsrem",OP_BSREM)); op_names.push_back(builtin_name("bvurem",OP_BUREM)); diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index fc7e3524557..51faca7edab 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -93,6 +93,19 @@ enum bv_op_kind { OP_BSMUL_NO_OVFL, // no signed multiplication overflow predicate OP_BSMUL_NO_UDFL, // no signed multiplication underflow predicate + OP_BUMUL_OVFL, // unsigned multiplication overflow predicate (negation of OP_BUMUL_NO_OVFL) + OP_BSMUL_OVFL, // signed multiplication over/underflow predicate + + OP_BSDIV_OVFL, // signed division overflow perdicate + + OP_BNEG_OVFL, // negation overflow predicate + + OP_BUADD_OVFL, // unsigned addition overflow predicate + OP_BSADD_OVFL, // signed addition overflow predicate + + OP_BUSUB_OVFL, // unsigned subtraction overflow predicate + OP_BSSUB_OVFL, // signed subtraction overflow predicate + OP_BIT2BOOL, // predicate OP_MKBV, // bools to bv OP_INT2BV, @@ -189,9 +202,22 @@ class bv_decl_plugin : public decl_plugin { ptr_vector m_bv_redand; ptr_vector m_bv_comp; - ptr_vector m_bv_mul_ovfl; - ptr_vector m_bv_smul_ovfl; - ptr_vector m_bv_smul_udfl; + ptr_vector m_bv_mul_no_ovfl; + ptr_vector m_bv_smul_no_ovfl; + ptr_vector m_bv_smul_no_udfl; + + ptr_vector m_bv_mul_ovfl; + ptr_vector m_bv_smul_ovfl; + + ptr_vector m_bv_sdiv_ovfl; + + ptr_vector m_bv_neg_ovfl; + + ptr_vector m_bv_uadd_ovfl; + ptr_vector m_bv_sadd_ovfl; + + ptr_vector m_bv_usub_ovfl; + ptr_vector m_bv_ssub_ovfl; ptr_vector m_bv_shl; ptr_vector m_bv_lshr; @@ -213,6 +239,7 @@ class bv_decl_plugin : public decl_plugin { func_decl * mk_unary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); + func_decl * mk_unary_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_reduction(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_comp(unsigned bv_size); bool get_bv_size(sort * t, int & result); @@ -490,9 +517,19 @@ class bv_util : public bv_recognizers { app * mk_bv2int(expr* e); + // TODO: all these binary ops commute (right?) but it'd be more logical to swap `n` & `m` in the `return` app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } + app * mk_bvsmul_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_OVFL, n, m); } + app * mk_bvumul_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_OVFL, n, m); } + app * mk_bvsdiv_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSDIV_OVFL, m, n); } + app * mk_bvneg_ovfl(expr* m) { return m_manager.mk_app(get_fid(), OP_BNEG_OVFL, m); } + app * mk_bvuadd_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUADD_OVFL, n, m); } + app * mk_bvsadd_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSADD_OVFL, n, m); } + app * mk_bvusub_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUSUB_OVFL, m, n); } + app * mk_bvssub_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSSUB_OVFL, m, n); } + app * mk_bit2bool(expr* e, unsigned idx) { parameter p(idx); return m_manager.mk_app(get_fid(), OP_BIT2BOOL, 1, &p, 1, &e); } private: diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 5f76d3fddb2..751608e1290 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -93,6 +93,10 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons case OP_BNEG: SASSERT(num_args == 1); return mk_uminus(args[0], result); + case OP_BNEG_OVFL: + SASSERT(num_args == 1); + return mk_bvneg_overflow(args[0], result); + case OP_BSHL: SASSERT(num_args == 2); return mk_bv_shl(args[0], args[1], result); @@ -199,6 +203,20 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons return mk_bvsmul_no_overflow(num_args, args, false, result); case OP_BUMUL_NO_OVFL: return mk_bvumul_no_overflow(num_args, args, result); + case OP_BSMUL_OVFL: + return mk_bvsmul_overflow(num_args, args, result); + case OP_BUMUL_OVFL: + return mk_bvumul_overflow(num_args, args, result); + case OP_BSDIV_OVFL: + return mk_bvsdiv_overflow(num_args, args, result); + case OP_BUADD_OVFL: + return mk_bvuadd_overflow(num_args, args, result); + case OP_BSADD_OVFL: + return mk_bvsadd_over_underflow(num_args, args, result); + case OP_BUSUB_OVFL: + return mk_bvusub_underflow(num_args, args, result); + case OP_BSSUB_OVFL: + return mk_bvssub_overflow(num_args, args, result); default: return BR_FAILED; } @@ -2921,6 +2939,21 @@ br_status bv_rewriter::mk_distinct(unsigned num_args, expr * const * args, expr_ return BR_DONE; } +br_status bv_rewriter::mk_bvsmul_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + result = m.mk_or( + m.mk_not(m_util.mk_bvsmul_no_ovfl(args[0], args[1])), + m.mk_not(m_util.mk_bvsmul_no_udfl(args[0], args[1])) + ); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvumul_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + result = m.mk_not(m_util.mk_bvumul_no_ovfl(args[0], args[1])); + return BR_REWRITE2; +} + br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result) { SASSERT(num == 2); unsigned bv_sz; @@ -2980,5 +3013,95 @@ br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, return BR_FAILED; } +br_status bv_rewriter::mk_bvneg_overflow(expr * const arg, expr_ref & result) { + unsigned int sz = get_bv_size(arg); + auto maxUnsigned = mk_numeral(rational::power_of_two(sz)-1, sz); + result = m.mk_eq(arg, maxUnsigned); + return BR_REWRITE3; +} + +br_status bv_rewriter::mk_bvuadd_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto a1 = mk_zero_extend(1, args[0]); + auto a2 = mk_zero_extend(1, args[1]); + auto r = mk_bv_add(a1, a2); + auto extract = m_mk_extract(sz, sz, r); + result = m.mk_eq(extract, mk_one(1)); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto zero = mk_zero(sz); + auto r = mk_bv_add(args[0], args[1]); + auto l1 = m_util.mk_slt(zero, args[0]); + auto l2 = m_util.mk_slt(zero, args[1]); + auto args_pos = m.mk_and(l1, l2); + auto non_pos_sum = m_util.mk_sle(r, zero); + result = m.mk_and(args_pos, non_pos_sum); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto zero = mk_zero(sz); + auto r = mk_bv_add(args[0], args[1]); + auto l1 = m_util.mk_slt(args[0], zero); + auto l2 = m_util.mk_slt(args[1], zero); + auto args_neg = m.mk_and(l1, l2); + expr_ref non_neg_sum{m}; + auto res_rewrite = mk_sge(r, zero, non_neg_sum); + SASSERT(res_rewrite != BR_FAILED); (void)res_rewrite; + result = m.mk_and(args_neg, non_neg_sum); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + expr_ref l1{m}; + expr_ref l2{m}; + (void)mk_bvsadd_overflow(2, args, l1); + (void)mk_bvsadd_underflow(2, args, l2); + result = m.mk_or(l1, l2); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + br_status status = mk_ult(args[0], args[1], result); + SASSERT(status != BR_FAILED); + return status; +} + +br_status bv_rewriter::mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + auto sz = get_bv_size(args[0]); + auto minSigned = mk_numeral(-rational::power_of_two(sz-1), sz); + expr_ref bvsaddo {m}; + expr * args2[2] = { args[0], m_util.mk_bv_neg(args[1]) }; + auto bvsaddo_stat = mk_bvsadd_overflow(2, args2, bvsaddo); + SASSERT(bvsaddo_stat != BR_FAILED); (void)bvsaddo_stat; + auto first_arg_ge_zero = m_util.mk_sle(mk_zero(sz), args[0]); + result = m.mk_ite(m.mk_eq(args[1], minSigned), first_arg_ge_zero, bvsaddo); + return BR_REWRITE_FULL; +} +br_status bv_rewriter::mk_bvsdiv_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + auto sz = get_bv_size(args[1]); + auto minSigned = mk_numeral(-rational::power_of_two(sz-1), sz); + auto minusOne = mk_numeral(rational::power_of_two(sz) - 1, sz); + result = m.mk_and(m.mk_eq(args[0], minSigned), m.mk_eq(args[1], minusOne)); + return BR_REWRITE_FULL; +} template class poly_rewriter; diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 25bd65b6130..09e7996c243 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -139,6 +139,22 @@ class bv_rewriter : public poly_rewriter { br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result); br_status mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result); br_status mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvsmul_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvumul_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvsdiv_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvneg_overflow(expr * const arg, expr_ref & result); + + br_status mk_bvuadd_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_underflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result); + bool is_minus_one_times_t(expr * arg); void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result); From f6ab5a61ac8306a78e09a8a55110c6a125bc8652 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 9 May 2023 13:48:28 -0700 Subject: [PATCH 594/597] reformat code to remove brackets --- src/smt/proto_model/proto_model.cpp | 46 ++++++++++++----------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index d61bcf028f6..4470f9cb8c0 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -288,42 +288,33 @@ bool proto_model::is_finite(sort * s) const { } expr * proto_model::get_some_value(sort * s) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_some_value(s); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_some_value(s); - } - else { + if (m.is_uninterp(s)) + return m_user_sort_factory->get_some_value(s); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_some_value(s); + else // there is no factory for the family id, then assume s is uninterpreted. - return m_user_sort_factory->get_some_value(s); - } + return m_user_sort_factory->get_some_value(s); } bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_some_values(s, v1, v2); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_some_values(s, v1, v2); - } - else { - return false; - } + if (m.is_uninterp(s)) + return m_user_sort_factory->get_some_values(s, v1, v2); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_some_values(s, v1, v2); + else + return false; } expr * proto_model::get_fresh_value(sort * s) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_fresh_value(s); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_fresh_value(s); - } - else { + if (m.is_uninterp(s)) + return m_user_sort_factory->get_fresh_value(s); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_fresh_value(s); + else // Use user_sort_factory if the theory has no support for model construnction. // This is needed when dummy theories are used for arithmetic or arrays. - return m_user_sort_factory->get_fresh_value(s); - } + return m_user_sort_factory->get_fresh_value(s); } void proto_model::register_value(expr * n) { @@ -354,6 +345,7 @@ void proto_model::compress() { void proto_model::complete_partial_func(func_decl * f, bool use_fresh) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { + verbose_stream() << "complete " << f->get_name() << " " << use_fresh << "\n"; expr * else_value; if (use_fresh) { else_value = get_fresh_value(f->get_range()); From 046b80f6a43af5ceff96dfc7114088aa625319b2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 11 May 2023 12:30:57 -0700 Subject: [PATCH 595/597] remove output Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/proto_model.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 4470f9cb8c0..f79f7851f5c 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -345,7 +345,6 @@ void proto_model::compress() { void proto_model::complete_partial_func(func_decl * f, bool use_fresh) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { - verbose_stream() << "complete " << f->get_name() << " " << use_fresh << "\n"; expr * else_value; if (use_fresh) { else_value = get_fresh_value(f->get_range()); From ba911009e4fe9a823564bdf924cf559065739842 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 11 May 2023 16:54:40 -0700 Subject: [PATCH 596/597] disable publish Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index 7d3ec1085a0..9a3fcae8c68 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -558,7 +558,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,1) + condition: eq(1,0) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From e417f7d78509b2d0c9ebc911fee7632e6ef546b6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 12 May 2023 12:59:04 -0700 Subject: [PATCH 597/597] updated release notes for 12.2 Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 4 ++++ scripts/release.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6cac7cc496d..68228df40e7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -27,6 +27,10 @@ Version 4.12.2 and `elim-predicates` that go beyond incremental pre-processing used internally. The advantage of using `solve-eqs` during pre-processing can be significant. Incremental pre-processing simplification using `solve-eqs` and other simplifiers that change interpretations was not possible before. +- Optimize added to JS API, thanks to gbagan +- SMTLIB2 proposal for bit-vector overflow predicates added, thanks to aehyvari +- bug fixes, thanks to Clemens Eisenhofer, hgvk94, Lev Nachmanson, and others + Version 4.12.1 ============== diff --git a/scripts/release.yml b/scripts/release.yml index 9a3fcae8c68..7d3ec1085a0 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -558,7 +558,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,0) + condition: eq(1,1) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest"