3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-06 06:03:23 +00:00

fix perf regression for new arithmetic solver, missing equality propagation #5106

This commit is contained in:
Nikolaj Bjorner 2021-03-28 14:14:52 -07:00
parent d6691830c7
commit 6aa766a544

View file

@ -435,20 +435,14 @@ class theory_lra::imp {
app_ref mod(a.mk_mod(n1, n2), m); app_ref mod(a.mk_mod(n1, n2), m);
ctx().internalize(mod, false); ctx().internalize(mod, false);
if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod);
st.to_ensure_var().push_back(n1);
st.to_ensure_var().push_back(n2);
} }
else if (a.is_mod(n, n1, n2)) { else if (a.is_mod(n, n1, n2)) {
if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n); if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n);
if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2); if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2);
st.to_ensure_var().push_back(n1);
st.to_ensure_var().push_back(n2);
} }
else if (a.is_rem(n, n1, n2)) { else if (a.is_rem(n, n1, n2)) {
if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n); if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n);
if (!ctx().relevancy()) mk_rem_axiom(n1, n2); if (!ctx().relevancy()) mk_rem_axiom(n1, n2);
st.to_ensure_var().push_back(n1);
st.to_ensure_var().push_back(n2);
} }
else if (a.is_div(n, n1, n2)) { else if (a.is_div(n, n1, n2)) {
if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n); if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n);
@ -570,6 +564,7 @@ class theory_lra::imp {
} }
void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) {
TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_smt2(tout, 3, lits); tout << "\n";);
ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params);
} }
@ -579,11 +574,7 @@ class theory_lra::imp {
} }
bool has_var(expr* n) { bool has_var(expr* n) {
if (!ctx().e_internalized(n)) { return ctx().e_internalized(n) && th.is_attached_to_var(get_enode(n));
return false;
}
enode* e = get_enode(n);
return th.is_attached_to_var(e);
} }
void reserve_bounds(theory_var v) { void reserve_bounds(theory_var v) {
@ -601,7 +592,6 @@ class theory_lra::imp {
theory_var v; theory_var v;
if (!th.is_attached_to_var(e)) { if (!th.is_attached_to_var(e)) {
v = th.mk_var(e); v = th.mk_var(e);
TRACE("arith", tout << "fresh var: v" << v << " " << mk_pp(n, m) << "\n";);
SASSERT(m_bounds.size() <= static_cast<unsigned>(v) || m_bounds[v].empty()); SASSERT(m_bounds.size() <= static_cast<unsigned>(v) || m_bounds[v].empty());
reserve_bounds(v); reserve_bounds(v);
ctx().attach_th_var(e, &th, v); ctx().attach_th_var(e, &th, v);
@ -610,7 +600,6 @@ class theory_lra::imp {
v = e->get_th_var(get_id()); v = e->get_th_var(get_id());
} }
SASSERT(null_theory_var != v); SASSERT(null_theory_var != v);
TRACE("arith", tout << mk_pp(n, m) << " " << v << "\n";);
return v; return v;
} }
@ -684,6 +673,25 @@ class theory_lra::imp {
return lp().get_status() == lp::lp_status::INFEASIBLE; return lp().get_status() == lp::lp_status::INFEASIBLE;
} }
vector<rational> m_fixed_values;
map<rational, theory_var, rational::hash_proc, rational::eq_proc> m_value2var;
struct undo_value : public trail {
imp& s;
undo_value(imp& s):s(s) {}
void undo() override {
s.m_value2var.erase(s.m_fixed_values.back());
s.m_fixed_values.pop_back();
}
};
void register_fixed_var(theory_var v, rational const& value) {
if (m_value2var.contains(value))
return;
m_fixed_values.push_back(value);
m_value2var.insert(value, v);
ctx().push_trail(undo_value(*this));
}
void add_def_constraint_and_equality(lpvar vi, lp::lconstraint_kind kind, void add_def_constraint_and_equality(lpvar vi, lp::lconstraint_kind kind,
const rational& bound) { const rational& bound) {
lpvar vi_equal; lpvar vi_equal;
@ -808,6 +816,7 @@ class theory_lra::imp {
vi = lp().add_var(v, a.is_int(term)); vi = lp().add_var(v, a.is_int(term));
add_def_constraint_and_equality(vi, lp::GE, st.offset()); add_def_constraint_and_equality(vi, lp::GE, st.offset());
add_def_constraint_and_equality(vi, lp::LE, st.offset()); add_def_constraint_and_equality(vi, lp::LE, st.offset());
register_fixed_var(v, st.offset());
return v; return v;
} }
if (!st.offset().is_zero()) { if (!st.offset().is_zero()) {
@ -965,31 +974,14 @@ public:
void internalize_eq_eh(app * atom, bool_var) { void internalize_eq_eh(app * atom, bool_var) {
if (!ctx().get_fparams().m_arith_eager_eq_axioms) if (!ctx().get_fparams().m_arith_eager_eq_axioms)
return; return;
TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";);
expr* lhs = nullptr, *rhs = nullptr; expr* lhs = nullptr, *rhs = nullptr;
VERIFY(m.is_eq(atom, lhs, rhs)); VERIFY(m.is_eq(atom, lhs, rhs));
enode * n1 = get_enode(lhs); enode * n1 = get_enode(lhs);
enode * n2 = get_enode(rhs); enode * n2 = get_enode(rhs);
TRACE("arith_verbose", tout << mk_pp(atom, m) << " " << is_arith(n1) << " " << is_arith(n2) << "\n";);
if (is_arith(n1) && is_arith(n2) && n1 != n2) { if (is_arith(n1) && is_arith(n2) && n1 != n2) {
m_arith_eq_adapter.mk_axioms(n1, n2); m_arith_eq_adapter.mk_axioms(n1, n2);
} }
#if 0
// this is super expensive and not used in the legacy solver.
// if this is really needed, some other solution has to be possible.
// internalization of ite expressions produces equalities of the form
// (= x (ite c x y)) and (= y (ite c x y))
// this step ensures that a shared enode is attached
// with the ite expression.
else {
if (m.is_ite(lhs) && !is_arith(n1)) {
internalize_term(to_app(lhs));
}
if (m.is_ite(rhs) && !is_arith(n2)) {
internalize_term(to_app(rhs));
}
}
#endif
} }
void assign_eh(bool_var v, bool is_true) { void assign_eh(bool_var v, bool is_true) {
@ -1237,39 +1229,29 @@ public:
} }
expr_ref mod_r(a.mk_add(a.mk_mul(q, div), mod), m); expr_ref mod_r(a.mk_add(a.mk_mul(q, div), mod), m);
#if 0 expr_ref eq_r(th.mk_eq_atom(mod_r, p), m);
expr_ref eqr(m.mk_eq(mod_r, p), m); ctx().internalize(eq_r, false);
ctx().get_rewriter()(eqr); literal eq = ctx().get_literal(eq_r);
literal eq = mk_literal(eqr);
#else
literal eq = th.mk_eq(mod_r, p, false);
#endif
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
rational k(0); rational k(0);
expr_ref upper(m); expr_ref upper(m);
if (a.is_numeral(q, k)) { if (!a.is_numeral(q, k))
if (k.is_pos()) { ;
else if (k.is_pos())
upper = a.mk_numeral(k - 1, true); upper = a.mk_numeral(k - 1, true);
} else if (k.is_neg())
else if (k.is_neg()) {
upper = a.mk_numeral(-k - 1, true); upper = a.mk_numeral(-k - 1, true);
}
}
else {
k = rational::zero();
}
context& c = ctx(); context& c = ctx();
if (!k.is_zero()) { if (!k.is_zero()) {
mk_axiom(eq); mk_axiom(eq);
mk_axiom(mod_ge_0); mk_axiom(mk_literal(a.mk_ge(mod, zero)));
mk_axiom(mk_literal(a.mk_le(mod, upper))); mk_axiom(mk_literal(a.mk_le(mod, upper)));
{ {
std::function<void(void)> log = [&,this]() { std::function<void(void)> log = [&,this]() {
th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(eq.var()))); th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(eq.var())));
th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(mod_ge_0.var()))); th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), a.mk_ge(mod, zero)));
th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), a.mk_le(mod, upper))); th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), a.mk_le(mod, upper)));
}; };
if_trace_stream _ts(m, log); if_trace_stream _ts(m, log);
@ -1290,6 +1272,7 @@ public:
// q >= 0 or (p mod q) < -q // q >= 0 or (p mod q) < -q
literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); literal q_ge_0 = mk_literal(a.mk_ge(q, zero));
literal q_le_0 = mk_literal(a.mk_le(q, zero)); literal q_le_0 = mk_literal(a.mk_le(q, zero));
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
mk_axiom(q_ge_0, eq); mk_axiom(q_ge_0, eq);
mk_axiom(q_le_0, eq); mk_axiom(q_le_0, eq);
@ -1348,10 +1331,10 @@ public:
} }
void mk_axiom(literal l) { void mk_axiom(literal l) {
ctx().mk_th_axiom(get_id(), false_literal, l);
if (ctx().relevancy()) { if (ctx().relevancy()) {
ctx().mark_as_relevant(l); ctx().mark_as_relevant(l);
} }
ctx().mk_th_axiom(get_id(), false_literal, l);
} }
void mk_axiom(literal l1, literal l2) { void mk_axiom(literal l1, literal l2) {
@ -1359,15 +1342,15 @@ public:
mk_axiom(l2); mk_axiom(l2);
return; return;
} }
ctx().mk_th_axiom(get_id(), l1, l2); mk_clause(l1, l2, 0, nullptr);
if (ctx().relevancy()) { if (ctx().relevancy()) {
ctx().mark_as_relevant(l1); ctx().mark_as_relevant(l1);
ctx().mark_as_relevant(l2); ctx().add_rel_watch(~l1, ctx().bool_var2expr(l2.var())); // mark consequent as relevant if antecedent is false.
} }
} }
void mk_axiom(literal l1, literal l2, literal l3) { void mk_axiom(literal l1, literal l2, literal l3) {
ctx().mk_th_axiom(get_id(), l1, l2, l3); mk_clause(l1, l2, l3, 0, nullptr);
if (ctx().relevancy()) { if (ctx().relevancy()) {
ctx().mark_as_relevant(l1); ctx().mark_as_relevant(l1);
ctx().mark_as_relevant(l2); ctx().mark_as_relevant(l2);
@ -2174,15 +2157,12 @@ public:
if (should_refine_bounds()) if (should_refine_bounds())
return true; return true;
if (m_bounds.size() <= static_cast<unsigned>(v) || m_unassigned_bounds[v] == 0) if (static_cast<unsigned>(v) < m_bounds.size())
return false; for (api_bound* b : m_bounds[v])
for (api_bound* b : m_bounds[v]) {
if (ctx().get_assignment(b->get_lit()) == l_undef && if (ctx().get_assignment(b->get_lit()) == l_undef &&
null_literal != is_bound_implied(kind, bval, *b)) { null_literal != is_bound_implied(kind, bval, *b))
return true; return true;
}
}
return false; return false;
} }
void propagate_lp_solver_bound(const lp::implied_bound& be) { void propagate_lp_solver_bound(const lp::implied_bound& be) {
@ -2286,10 +2266,9 @@ public:
theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form
enode* n1 = get_enode(uv); enode* n1 = get_enode(uv);
enode* n2 = get_enode(vv); enode* n2 = get_enode(vv);
TRACE("arith", tout << "add-eq " << mk_pp(n1->get_expr(), m) << " == " << mk_pp(n2->get_expr(), m) << "\n";);
if (n1->get_root() == n2->get_root()) if (n1->get_root() == n2->get_root())
return; return;
if (!ctx().is_shared(n1) || !ctx().is_shared(n2))
return;
expr* e1 = n1->get_expr(); expr* e1 = n1->get_expr();
expr* e2 = n2->get_expr(); expr* e2 = n2->get_expr();
if (e1->get_sort() != e2->get_sort()) if (e1->get_sort() != e2->get_sort())
@ -2299,13 +2278,7 @@ public:
reset_evidence(); reset_evidence();
for (auto ev : e) for (auto ev : e)
set_evidence(ev.ci(), m_core, m_eqs); set_evidence(ev.ci(), m_core, m_eqs);
justification* js = ctx().mk_justification( assign_eq(uv, vv);
ext_theory_eq_propagation_justification(
get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), n1, n2));
std::function<expr*(void)> fn = [&]() { return m.mk_eq(e1, e2); };
scoped_trace_stream _sts(th, fn);
ctx().assign_eq(n1, n2, eq_justification(js));
} }
literal_vector m_core2; literal_vector m_core2;
@ -2501,10 +2474,8 @@ public:
--i; --i;
} }
} }
CTRACE("arith_verbose", !atoms.empty(), CTRACE("arith", atoms.size() > 1,
for (unsigned i = 0; i < atoms.size(); ++i) { for (auto* a : atoms) a->display(tout) << "\n";);
atoms[i]->display(tout); tout << "\n";
});
lp_bounds occs(m_bounds[v]); lp_bounds occs(m_bounds[v]);
std::sort(atoms.begin(), atoms.end(), compare_bounds()); std::sort(atoms.begin(), atoms.end(), compare_bounds());
@ -2638,7 +2609,7 @@ public:
literal lit1(bv, !is_true); literal lit1(bv, !is_true);
literal lit2 = null_literal; literal lit2 = null_literal;
bool find_glb = (is_true == (k == lp_api::lower_t)); bool find_glb = (is_true == (k == lp_api::lower_t));
TRACE("arith", tout << "v" << v << " find_glb: " << find_glb << " is_true: " << is_true << " k: " << k << " is_lower: " << (k == lp_api::lower_t) << "\n";); TRACE("arith_verbose", tout << "v" << v << " find_glb: " << find_glb << " is_true: " << is_true << " k: " << k << " is_lower: " << (k == lp_api::lower_t) << "\n";);
if (find_glb) { if (find_glb) {
rational glb; rational glb;
api_bound* lb = nullptr; api_bound* lb = nullptr;
@ -2920,12 +2891,13 @@ public:
vector<constraint_bound> m_lower_terms; vector<constraint_bound> m_lower_terms;
vector<constraint_bound> m_upper_terms; vector<constraint_bound> m_upper_terms;
void propagate_eqs(lp::tv t, lp::constraint_index ci, lp::lconstraint_kind k, api_bound& b, rational const& value) { void propagate_eqs(lp::tv t, lp::constraint_index ci1, lp::lconstraint_kind k, api_bound& b, rational const& value) {
if (k == lp::GE && set_lower_bound(t, ci, value) && has_upper_bound(t.index(), ci, value)) { lp::constraint_index ci2;
fixed_var_eh(b.get_var(), value); if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t.index(), ci2, value)) {
fixed_var_eh(b.get_var(), t, ci1, ci2, value);
} }
else if (k == lp::LE && set_upper_bound(t, ci, value) && has_lower_bound(t.index(), ci, value)) { else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t.index(), ci2, value)) {
fixed_var_eh(b.get_var(), value); fixed_var_eh(b.get_var(), t, ci1, ci2, value);
} }
} }
@ -3033,7 +3005,7 @@ public:
unsigned get_num_vars() const { return th.get_num_vars(); } unsigned get_num_vars() const { return th.get_num_vars(); }
void report_equality_of_fixed_vars(unsigned vi1, unsigned vi2) { void report_equality_of_fixed_vars(unsigned vi1, unsigned vi2) {
rational bound; rational bound(0);
lp::constraint_index ci1, ci2, ci3, ci4; lp::constraint_index ci1, ci2, ci3, ci4;
theory_var v1 = lp().local_to_external(vi1); theory_var v1 = lp().local_to_external(vi1);
theory_var v2 = lp().local_to_external(vi2); theory_var v2 = lp().local_to_external(vi2);
@ -3052,21 +3024,24 @@ public:
if (!has_upper_bound(vi2, ci4, bound)) if (!has_upper_bound(vi2, ci4, bound))
return; return;
++m_stats.m_fixed_eqs;
reset_evidence(); reset_evidence();
set_evidence(ci1, m_core, m_eqs); set_evidence(ci1, m_core, m_eqs);
set_evidence(ci2, m_core, m_eqs); set_evidence(ci2, m_core, m_eqs);
set_evidence(ci3, m_core, m_eqs); set_evidence(ci3, m_core, m_eqs);
set_evidence(ci4, m_core, m_eqs); set_evidence(ci4, m_core, m_eqs);
++m_stats.m_fixed_eqs;
assign_eq(v1, v2);
}
void assign_eq(theory_var v1, theory_var v2) {
enode* x = get_enode(v1); enode* x = get_enode(v1);
enode* y = get_enode(v2); enode* y = get_enode(v2);
justification* js = justification* js =
ctx().mk_justification( ctx().mk_justification(
ext_theory_eq_propagation_justification( ext_theory_eq_propagation_justification(
get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, nullptr)); get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y));
TRACE("arith", TRACE("arith",
tout << "bound " << bound << "\n";
for (auto c : m_core) for (auto c : m_core)
ctx().display_detailed_literal(tout, c) << "\n"; ctx().display_detailed_literal(tout, c) << "\n";
for (auto e : m_eqs) for (auto e : m_eqs)
@ -3075,13 +3050,34 @@ public:
tout << pp(x, m) << " = " << pp(y, m) << "\n"; tout << pp(x, m) << " = " << pp(y, m) << "\n";
); );
std::function<expr*(void)> fn = [&]() { return m.mk_eq(x->get_expr(), y->get_expr()); };
scoped_trace_stream _sts(th, fn);
// parameters are TBD. // parameters are TBD.
// SASSERT(validate_eq(x, y)); // SASSERT(validate_eq(x, y));
ctx().assign_eq(x, y, eq_justification(js)); ctx().assign_eq(x, y, eq_justification(js));
} }
void fixed_var_eh(theory_var v1, rational const& bound) { void fixed_var_eh(theory_var v, lp::tv t, lp::constraint_index ci1, lp::constraint_index ci2, rational const& bound) {
// no op theory_var w = null_theory_var;
enode* x = get_enode(v);
if (bound.is_zero())
w = lp().local_to_external(get_zero(a.is_int(x->get_expr())));
else if (bound.is_one())
w = lp().local_to_external(get_one(a.is_int(x->get_expr())));
else if (!m_value2var.find(bound, w))
return;
enode* y = get_enode(w);
if (x->get_sort() != y->get_sort())
return;
if (x->get_root() == y->get_root())
return;
SASSERT(a.is_numeral(y->get_expr()));
reset_evidence();
set_evidence(ci1, m_core, m_eqs);
set_evidence(ci2, m_core, m_eqs);
++m_stats.m_fixed_eqs;
assign_eq(v, w);
} }
lbool make_feasible() { lbool make_feasible() {
@ -3658,6 +3654,7 @@ public:
void display(std::ostream & out) const { void display(std::ostream & out) const {
out << "Theory arithmetic:\n";
if (m_solver) { if (m_solver) {
m_solver->display(out); m_solver->display(out);
} }