3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-08-02 12:13:25 +00:00

nla: add LP-based nonlinear bound optimization for cross-nested confl… (#10180)

…icts

Add core::optimize_nl_bounds() (gated by arith.nl.optimize_bounds) which
runs LP max/min over monomial leaf variables inside core::propagate(),
analogous to solver=2's max_min_nl_vars, so nla (arith.solver=6) can
detect cross-nested conflicts previously missed. Collect improved bounds
first, then apply them and re-establish feasibility once; reconcile the
core solver via find_feasible_solution before the raw maximize solves to
preserve inf_heap_is_correct(). Skip null witnesses in
get_dependencies_of_maximum for implied/unconditional bounds.

On FStar-UInt128-divergence solver=6 this yields unsat in 2
final-checks, seed-insensitive (seeds 1-10).


Copilot-Session: ac36bb84-de91-4e6c-86df-6008c7396ceb

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ac36bb84-de91-4e6c-86df-6008c7396ceb
Copilot-Session: 96a14756-2ffe-4cc3-87e7-49fda1b6113a
This commit is contained in:
Nikolaj Bjorner 2026-07-23 09:00:46 -07:00 committed by GitHub
parent 53fa8f9cc4
commit aba41d026f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 370 additions and 292 deletions

View file

@ -1349,7 +1349,6 @@ public:
literal eqz = mk_literal(m.mk_eq(q, zero));
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
// q = 0 or p = (p mod q) + q * (p div q)
// q = 0 or (p mod q) >= 0
// q >= 0 or (p mod q) + q <= -1
@ -1746,6 +1745,7 @@ public:
IF_VERBOSE(12, verbose_stream() << "final-check " << lp().get_status() << "\n");
lbool is_sat = l_true;
SASSERT(lp().ax_is_correct());
propagate_nla();
if (!lp().is_feasible() || lp().has_changed_columns())
is_sat = make_feasible();
final_check_status st = FC_DONE;
@ -2126,7 +2126,6 @@ public:
default:
UNREACHABLE();
}
TRACE(arith, tout << "is_lower: " << is_lower << " pos " << pos << "\n";);
expr_ref atom(m);
// TBD utility: lp::lar_term term = mk_term(ineq.m_poly);
// then term is used instead of ineq.m_term
@ -2135,6 +2134,7 @@ public:
else
// create term >= 0 (or term <= 0)
atom = mk_bound(ineq.term(), ineq.rs(), is_lower);
TRACE(arith, tout << "is_lower: " << is_lower << " pos " << pos << " " << atom << "\n";);
return literal(ctx().get_bool_var(atom), pos);
}
@ -2265,7 +2265,6 @@ public:
bool propagate_core() {
m_model_is_initialized = false;
flush_bound_axioms();
propagate_nla();
if (ctx().inconsistent())
return true;
if (!can_propagate_core())
@ -2329,12 +2328,14 @@ public:
return true;
}
void propagate_nla() {
bool propagate_nla() {
bool propagated = false;
if (m_nla) {
m_nla->propagate();
propagated = m_nla->propagate() || propagated;
add_lemmas();
lp().collect_more_rows_for_lp_propagation();
}
return propagated;
}
void add_equality(lpvar j, rational const& k, lp::explanation const& exp) {