3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-10-08 08:51:55 +00:00

limit sos loop

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2025-09-30 13:51:05 -07:00
parent 3b1ac52ff9
commit 538480b4f8
2 changed files with 72 additions and 54 deletions

View file

@ -51,15 +51,13 @@ namespace nla {
{}
lbool stellensatz::saturate() {
lp::explanation ex;
vector<ineq> ineqs;
init_solver();
saturate_constraints();
saturate_basic_linearize();
TRACE(arith, display(tout << "stellensatz after saturation\n"));
lbool r = m_solver.solve(ex, ineqs);
lbool r = m_solver.solve();
if (r == l_false)
add_lemma(ex, ineqs);
add_lemma();
return r;
}
@ -139,7 +137,9 @@ namespace nla {
// 1. constraints that are obtained by multiplication are explained from the original constraint
// 2. bounds justifications are added as justifications to the lemma.
//
void stellensatz::add_lemma(lp::explanation const& ex1, vector<ineq> const& ineqs) {
void stellensatz::add_lemma() {
lp::explanation const &ex1 = m_solver.ex();
vector<ineq> const &ineqs = m_solver.ineqs();
TRACE(arith, display(tout); display_lemma(tout, ex1, ineqs));
auto& lra = c().lra_solver();
lp::explanation ex2;
@ -390,20 +390,18 @@ namespace nla {
return p;
}
// TODO add gcd reduce.
lp::constraint_index stellensatz::add_ineq(
justification const& bounds,
lp::lar_term const& t, lp::lconstraint_kind k,
justification const& just, lp::lar_term const& t, lp::lconstraint_kind k,
rational const& rhs) {
SASSERT(!t.coeffs_as_vector().empty());
auto ti = m_solver.lra().add_term(t.coeffs_as_vector(), m_solver.lra().number_of_vars());
auto coeffs = t.coeffs_as_vector();
auto ti = m_solver.lra().add_term(coeffs, m_solver.lra().number_of_vars());
m_values.push_back(value(t));
auto new_ci = m_solver.lra().add_var_bound(ti, k, rhs);
TRACE(arith,
// display_constraint(tout << bounds.rule << ": ", new_ci) << "\n";
// if (!bounds.empty()) display(tout << "\t <- ", bounds) << "\n";
);
TRACE(arith, display(tout, just) << "\n");
SASSERT(m_values.size() - 1 == ti);
add_justification(new_ci, bounds);
add_justification(new_ci, just);
init_occurs(new_ci);
return new_ci;
}
@ -463,7 +461,7 @@ namespace nla {
auto &constraints = m_solver.lra().constraints();
unsigned initial_false_constraints = m_false_constraints.size();
for (unsigned it = 0; it < m_false_constraints.size(); ++it) {
if (it > 5 * initial_false_constraints)
if (it > 10 * initial_false_constraints)
break;
auto ci1 = m_false_constraints[it];
auto const &con = constraints[ci1];
@ -477,7 +475,8 @@ namespace nla {
for (auto v : vars) {
if (v >= m_occurs.size())
continue;
for (unsigned cidx = 0; cidx < m_occurs[v].size(); ++cidx) {
unsigned sz = m_occurs[v].size();
for (unsigned cidx = 0; cidx < sz; ++cidx) {
auto ci2 = m_occurs[v][cidx];
if (ci1 == ci2)
continue;
@ -491,6 +490,8 @@ namespace nla {
}
}
}
IF_VERBOSE(1, verbose_stream() << "stsz " << initial_false_constraints << " -> " << m_false_constraints.size()
<< " false constraints\n");
IF_VERBOSE(5,
c().lra_solver().display(verbose_stream() << "original\n");
c().display(verbose_stream());
@ -574,8 +575,11 @@ namespace nla {
}
auto lhs = mul1 * lhs1 + mul2 * lhs2;
auto rhs = mul1 * con1.rhs() + mul2 * con2.rhs();
if (lhs.size() == 0) // trivial conflict, will be found using LIA
if (lhs.size() == 0) { // trivial conflict, will be found using LIA
IF_VERBOSE(0, verbose_stream() << "trivial conflict\n");
TRACE(arith, tout << "trivial conflict\n");
return;
}
resolvent r = {ci1, ci2, j};
auto new_ci = add_ineq(r, lhs, k1, rhs);
insert_monomials_from_constraint(new_ci);
@ -619,6 +623,13 @@ namespace nla {
multiplication_justification just{old_ci, mi, j2};
// compute bounds constraints and sign of vars
lbool sign = add_bounds(vars, just.assumptions);
#if 1
// just skip vacuous lemmas?
if (l_undef == sign)
return lp::null_ci;
#endif
lp::lar_term new_lhs;
rational new_rhs(rhs);
for (auto const & cv : lhs) {
@ -637,25 +648,26 @@ namespace nla {
new_rhs = 0;
}
if (sign == l_true)
k = swap_side(k);
if (sign == l_undef) {
// x = 0 => x*y >= 0
switch (k) {
case lp::lconstraint_kind::GT:
k = lp::lconstraint_kind::GE;
break;
case lp::lconstraint_kind::LT:
k = lp::lconstraint_kind::LE;
break;
case lp::lconstraint_kind::GT:
k = lp::lconstraint_kind::GE;
break;
default:
break;
}
}
if (sign == l_true)
k = swap_side(k);
auto new_ci = add_ineq(just, new_lhs, k, new_rhs);
IF_VERBOSE(4, display_constraint(verbose_stream(), old_ci) << " -> ";
display_constraint(verbose_stream(), new_lhs.coeffs_as_vector(), k, new_rhs) << "\n");
insert_monomials_from_constraint(new_ci);
//insert_monomials_from_constraint(new_ci);
m_factored_constraints.insert(new_ci); // don't bother to factor this because it comes from factors already
return new_ci;
}
@ -725,13 +737,13 @@ namespace nla {
return t;
}
bool stellensatz::saturate_factors(lp::explanation &ex, vector<ineq> &ineqs) {
bool stellensatz::saturate_factors() {
return true;
auto indices = m_solver.lra().constraints().indices();
return all_of(indices, [&](auto ci) { return saturate_factors(ci, ex, ineqs); });
return all_of(indices, [&](auto ci) { return saturate_factors(ci); });
}
bool stellensatz::saturate_factors(lp::constraint_index ci, lp::explanation& ex, vector<ineq>& ineqs) {
bool stellensatz::saturate_factors(lp::constraint_index ci) {
if (m_factored_constraints.contains(ci))
return true;
m_factored_constraints.insert(ci);
@ -820,12 +832,12 @@ namespace nla {
return true;
// this is a conflict with the current evaluation. Produce a lemma that blocks
// TODO, need to check if variables use fresh monomials
ex.push_back(ci);
m_solver.ex().push_back(ci);
for (auto &f : factors) {
auto [t, offset] = f.first;
auto val = value(t) + offset;
auto k = val > 0 ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GE;
ineqs.push_back(ineq(t, k, -offset));
m_solver.ineqs().push_back(ineq(t, k, -offset));
}
TRACE(arith, tout << "factor conflict\n";
for (auto const &f : factors) display(tout, f.first) << "; "; tout << "\n";
@ -1123,14 +1135,16 @@ namespace nla {
void stellensatz::solver::init() {
lra_solver = alloc(lp::lar_solver);
int_solver = alloc(lp::int_solver, *lra_solver);
m_ex.clear();
m_ineqs.reset();
}
lbool stellensatz::solver::solve(lp::explanation &ex, vector<ineq>& ineqs) {
lbool stellensatz::solver::solve() {
while (true) {
lbool r = solve_lra(ex);
lbool r = solve_lra();
if (r != l_true)
return r;
r = solve_lia(ex);
r = solve_lia();
if (r != l_true)
return r;
unsigned sz = lra_solver->number_of_vars();
@ -1144,7 +1158,7 @@ namespace nla {
// this can be fixed by asserting the value justifications as bounds directly and
// making them depend on themselves.
TRACE(arith, s.display(tout));
if (!s.saturate_factors(ex, ineqs))
if (!s.saturate_factors())
return l_false;
// s.saturate_constraints(); ?
if (sz == lra_solver->number_of_vars())
@ -1152,19 +1166,19 @@ namespace nla {
}
}
lbool stellensatz::solver::solve_lra(lp::explanation &ex) {
lbool stellensatz::solver::solve_lra() {
auto status = lra_solver->find_feasible_solution();;
if (lra_solver->is_feasible())
return l_true;
if (status == lp::lp_status::INFEASIBLE) {
lra_solver->get_infeasibility_explanation(ex);
lra_solver->get_infeasibility_explanation(m_ex);
return l_false;
}
return l_undef;
}
lbool stellensatz::solver::solve_lia(lp::explanation &ex) {
switch (int_solver->check(&ex)) {
lbool stellensatz::solver::solve_lia() {
switch (int_solver->check(&m_ex)) {
case lp::lia_move::sat:
return l_true;
case lp::lia_move::conflict:

View file

@ -20,17 +20,21 @@ namespace nla {
stellensatz &s;
scoped_ptr<lp::lar_solver> lra_solver;
scoped_ptr<lp::int_solver> int_solver;
lbool solve_lra(lp::explanation &ex);
lbool solve_lia(lp::explanation &ex);
lp::explanation m_ex;
vector<ineq> m_ineqs;
lbool solve_lra();
lbool solve_lia();
bool update_values();
vector<std::pair<lpvar, rational>> m_to_refine;
void saturate_basic_linearize();
public:
solver(stellensatz &s) : s(s) {};
void init();
lbool solve(lp::explanation &ex, vector<ineq>& ineqs);
lbool solve();
lp::lar_solver &lra() { return *lra_solver; }
lp::lar_solver const &lra() const { return *lra_solver; }
lp::explanation &ex() { return m_ex; }
vector<ineq> &ineqs() { return m_ineqs; }
};
solver m_solver;
@ -156,11 +160,11 @@ namespace nla {
bool get_factors(term_offset &t, vector<std::pair<term_offset, unsigned>> &factors);
polynomial::polynomial_ref to_poly(term_offset const &t);
term_offset to_term(polynomial::polynomial const &p);
bool saturate_factors(lp::constraint_index ci, lp::explanation& ex, vector<ineq>& ineqs);
bool saturate_factors(lp::explanation& ex, vector<ineq>& ineqs);
bool saturate_factors(lp::constraint_index ci);
bool saturate_factors();
// lemmas
void add_lemma(lp::explanation const& ex, vector<ineq> const& ineqs);
void add_lemma();
indexed_uint_set m_constraints_in_conflict;
void explain_constraint(lemma_builder& new_lemma, lp::constraint_index ci, lp::explanation &ex);