3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-02 13:26:10 +00:00

Port throttle and soundness fixes from master

- Fix soundness: pop incomplete lemma from m_lemmas on add_lemma failure
- Gracefully handle root atoms in add_lemma
- Throttle check_assignment with failure counter (decrement on success)
- Add arith.nl.nra_check_assignment parameter

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Lev Nachmanson 2026-03-12 14:35:52 -10:00
parent 77d81de03f
commit 6c27140bc9
4 changed files with 62 additions and 41 deletions

View file

@ -1330,10 +1330,14 @@ lbool core::check(unsigned level) {
return l_false; return l_false;
} }
if (no_effect()) { if (no_effect() && params().arith_nl_nra_check_assignment() && m_check_assignment_fail_cnt < 10) {
scoped_limits sl(m_reslim); scoped_limits sl(m_reslim);
sl.push_child(&m_nra_lim); sl.push_child(&m_nra_lim);
ret = m_nra.check_assignment(); ret = m_nra.check_assignment();
if (ret != l_true)
++m_check_assignment_fail_cnt;
else
--m_check_assignment_fail_cnt;
} }
if (no_effect() && should_run_bounded_nlsat()) if (no_effect() && should_run_bounded_nlsat())

View file

@ -63,6 +63,7 @@ class core {
unsigned m_nlsat_delay = 0; unsigned m_nlsat_delay = 0;
unsigned m_nlsat_delay_bound = 0; unsigned m_nlsat_delay_bound = 0;
unsigned m_check_assignment_fail_cnt = 0;
bool should_run_bounded_nlsat(); bool should_run_bounded_nlsat();
lbool bounded_nlsat(); lbool bounded_nlsat();

View file

@ -448,48 +448,63 @@ struct solver::imp {
lbool add_lemma(nlsat::literal_vector const &clause) { lbool add_lemma(nlsat::literal_vector const &clause) {
u_map<lp::lpvar> nl2lp = reverse_lp2nl(); u_map<lp::lpvar> nl2lp = reverse_lp2nl();
polynomial::manager &pm = m_nlsat->pm(); polynomial::manager &pm = m_nlsat->pm();
nla::lemma_builder lemma(m_nla_core, __FUNCTION__); lbool result = l_false;
for (nlsat::literal l : clause) { {
if (m_literal2constraint.get((~l).index(), lp::null_ci) != lp::null_ci) { nla::lemma_builder lemma(m_nla_core, __FUNCTION__);
auto ci = m_literal2constraint[(~l).index()]; for (nlsat::literal l : clause) {
lp::explanation ex; if (m_literal2constraint.get((~l).index(), lp::null_ci) != lp::null_ci) {
ex.push_back(ci); auto ci = m_literal2constraint[(~l).index()];
lemma &= ex; lp::explanation ex;
continue; ex.push_back(ci);
} lemma &= ex;
nlsat::atom *a = m_nlsat->bool_var2atom(l.var()); continue;
SASSERT(!a->is_root_atom()); }
SASSERT(a->is_ineq_atom()); nlsat::atom *a = m_nlsat->bool_var2atom(l.var());
auto &ia = *to_ineq_atom(a); if (a->is_root_atom()) {
if (ia.size() != 1) { result = l_undef;
return l_undef; // factored polynomials not handled here break;
} }
polynomial::polynomial const *p = ia.p(0); SASSERT(a->is_ineq_atom());
rational bound(0); auto &ia = *to_ineq_atom(a);
lp::lar_term t; if (ia.size() != 1) {
process_polynomial_check_assignment(p, bound, nl2lp, t); result = l_undef; // factored polynomials not handled here
break;
}
polynomial::polynomial const *p = ia.p(0);
rational bound(0);
lp::lar_term t;
process_polynomial_check_assignment(p, bound, nl2lp, t);
nla::ineq inq(lp::lconstraint_kind::EQ, t, bound); // initial value overwritten in cases below nla::ineq inq(lp::lconstraint_kind::EQ, t, bound); // initial value overwritten in cases below
switch (a->get_kind()) { switch (a->get_kind()) {
case nlsat::atom::EQ: case nlsat::atom::EQ:
inq = nla::ineq(l.sign() ? lp::lconstraint_kind::NE : lp::lconstraint_kind::EQ, t, bound); inq = nla::ineq(l.sign() ? lp::lconstraint_kind::NE : lp::lconstraint_kind::EQ, t, bound);
break; break;
case nlsat::atom::LT: case nlsat::atom::LT:
inq = nla::ineq(l.sign() ? lp::lconstraint_kind::GE : lp::lconstraint_kind::LT, t, bound); inq = nla::ineq(l.sign() ? lp::lconstraint_kind::GE : lp::lconstraint_kind::LT, t, bound);
break; break;
case nlsat::atom::GT: case nlsat::atom::GT:
inq = nla::ineq(l.sign() ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GT, t, bound); inq = nla::ineq(l.sign() ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GT, t, bound);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return l_undef; result = l_undef;
break;
}
if (result == l_undef)
break;
if (m_nla_core.ineq_holds(inq)) {
result = l_undef;
break;
}
lemma |= inq;
} }
if (m_nla_core.ineq_holds(inq)) if (result == l_false)
return l_undef; this->m_nla_core.m_check_feasible = true;
lemma |= inq; } // lemma_builder destructor runs here
} if (result == l_undef)
this->m_nla_core.m_check_feasible = true; m_nla_core.m_lemmas.pop_back(); // discard incomplete lemma
return l_false; return result;
} }

View file

@ -60,6 +60,7 @@ def_module_params(module_name='smt',
('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.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'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation, relevant only if smt.arith.solver=2'),
('arith.nl.nra', BOOL, True, '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'), ('arith.nl.nra', BOOL, True, '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'),
('arith.nl.nra_check_assignment', BOOL, True, 'call check_assignment in nra_solver to verify current assignment against nlsat constraints'),
('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'),
('arith.nl.expensive_patching', BOOL, False, 'use the expensive of monomials'), ('arith.nl.expensive_patching', BOOL, False, 'use the expensive of monomials'),
('arith.nl.rounds', UINT, 1024, 'threshold for number of (nested) final checks for non linear arithmetic, relevant only if smt.arith.solver=2'), ('arith.nl.rounds', UINT, 1024, 'threshold for number of (nested) final checks for non linear arithmetic, relevant only if smt.arith.solver=2'),