3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-11-25 15:09:32 +00:00

revamp stellensatz to use polynomals

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2025-11-12 20:21:42 -08:00
parent 21b36868b5
commit c7084e9998
4 changed files with 397 additions and 542 deletions

View file

@ -1908,6 +1908,36 @@ namespace dd {
return m->mk_var(var())*h + l;
}
void pdd::merge(unsigned_vector& lo, unsigned_vector& hi, unsigned_vector& common) {
std::sort(lo.begin(), lo.end());
std::sort(hi.begin(), hi.end());
unsigned ir = 0, jr = 0;
for (unsigned i = 0, j = 0; i < lo.size() || j < hi.size(); ) {
if (i == lo.size())
hi[jr++] = hi[j++];
else if (j == hi.size())
lo[ir++] = lo[i++];
else if (lo[i] == hi[j]) {
common.push_back(lo[i]);
++i;
++j;
}
else if (lo[i] > hi[j])
hi[jr++] = hi[j++];
else
lo[ir++] = lo[i++];
}
lo.shrink(ir);
hi.shrink(jr);
}
pdd pdd::mul(unsigned_vector const& vars) const {
pdd r = *this;
for (auto v : vars)
r = m->mk_var(v) * r;
return r;
}
std::pair<unsigned_vector, pdd> pdd::var_factors() const {
if (is_val())
return { unsigned_vector(), *this };
@ -1924,49 +1954,23 @@ namespace dd {
return { unsigned_vector(), *this };
unsigned_vector lo_and_hi;
auto merge = [&](unsigned_vector& lo_vars, unsigned_vector& hi_vars) {
unsigned ir = 0, jr = 0;
for (unsigned i = 0, j = 0; i < lo_vars.size() || j < hi_vars.size(); ) {
if (i == lo_vars.size())
hi_vars[jr++] = hi_vars[j++];
else if (j == hi_vars.size())
lo_vars[ir++] = lo_vars[i++];
else if (lo_vars[i] == hi_vars[j]) {
lo_and_hi.push_back(lo_vars[i]);
++i;
++j;
}
else if (m->m_var2level[lo_vars[i]] > m->m_var2level[hi_vars[j]])
hi_vars[jr++] = hi_vars[j++];
else
lo_vars[ir++] = lo_vars[i++];
}
lo_vars.shrink(ir);
hi_vars.shrink(jr);
};
auto mul = [&](unsigned_vector const& vars, pdd p) {
for (auto v : vars)
p *= m->mk_var(v);
return p;
};
auto [hi_vars, p] = hi().var_factors();
if (lo_vars.back() == v) {
lo_vars.pop_back();
merge(lo_vars, hi_vars);
merge(lo_vars, hi_vars, lo_and_hi);
lo_and_hi.push_back(v);
return { lo_and_hi, mul(lo_vars, q) + mul(hi_vars, p) };
return { lo_and_hi, q.mul(lo_vars) + p.mul(hi_vars) };
}
if (hi_vars.empty())
return { unsigned_vector(), *this };
merge(lo_vars, hi_vars);
merge(lo_vars, hi_vars, lo_and_hi);
hi_vars.push_back(v);
if (lo_and_hi.empty())
return { unsigned_vector(), *this };
else
return { lo_and_hi, mul(lo_vars, q) + mul(hi_vars, p) };
return { lo_and_hi, q.mul(lo_vars) + p.mul(hi_vars) };
}

View file

@ -483,6 +483,8 @@ namespace dd {
* \brief factor out variables
*/
std::pair<unsigned_vector, pdd> var_factors() const;
static void merge(unsigned_vector &lo, unsigned_vector &hi, unsigned_vector& common);
pdd mul(unsigned_vector const &vars) const;
pdd subst_val0(vector<std::pair<unsigned, rational>> const& s) const { return m->subst_val0(*this, s); }
pdd subst_val(pdd const& s) const { VERIFY_EQ(m, s.m); return m->subst_val(*this, s); }

File diff suppressed because it is too large Load diff

View file

@ -5,9 +5,9 @@
#pragma once
#include "util/scoped_ptr_vector.h"
#include "math/dd/dd_pdd.h"
#include "math/lp/nla_coi.h"
#include "math/lp/int_solver.h"
#include "math/polynomial/polynomial.h"
#include <variant>
namespace nla {
@ -26,7 +26,6 @@ namespace nla {
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();
@ -39,12 +38,11 @@ namespace nla {
solver m_solver;
// factor t into coeff*x^degree*p + q, such that degree(x, q) < degree,
// factor t into x^degree*p + q, such that degree(x, q) < degree,
struct factorization {
rational coeff;
unsigned degree;
lp::lar_term p;
lp::lar_term q;
dd::pdd p, q;
};
struct external_justification {
@ -60,6 +58,10 @@ namespace nla {
lp::constraint_index ci;
rational mul;
};
struct multiplication_var_justification {
lp::constraint_index ci;
lpvar v;
};
struct multiplication_justification {
lp::constraint_index left, right;
};
@ -73,16 +75,32 @@ namespace nla {
gcd_justification,
addition_justification,
multiplication_const_justification,
multiplication_var_justification,
multiplication_justification
>;
using term_offset = std::pair<lp::lar_term, rational>;
struct constraint {
dd::pdd p;
lp::lconstraint_kind k;
justification j;
};
coi m_coi;
scoped_ptr_vector<justification> m_justifications;
void add_justification(lp::constraint_index ci, justification const &j) {
VERIFY(ci == m_justifications.size());
m_justifications.push_back(alloc(justification, j));
}
vector<rational> m_values;
dd::pdd_manager pddm;
vector<constraint> m_constraints;
dd::pdd to_pdd(lpvar v);
lpvar mk_monomial(svector<lpvar> const &vars);
void init_monomial(unsigned mon_var);
term_offset to_term_offset(dd::pdd const &p);
lp::constraint_index add_constraint(dd::pdd p, lp::lconstraint_kind k, justification j);
lp::constraint_index add_var_bound(lp::lpvar v, lp::lconstraint_kind k, rational const &rhs, justification j);
struct eq {
bool operator()(unsigned_vector const& a, unsigned_vector const& b) const {
return a == b;
@ -92,55 +110,30 @@ namespace nla {
u_map<unsigned_vector> m_mon2vars;
bool is_mon_var(lpvar v) const { return m_mon2vars.contains(v); }
unsigned m_max_monomial_degree = 0;
vector<svector<lp::constraint_index>> m_occurs; // map from variable to constraints they occur.
bool is_new_inequality(vector<std::pair<rational, lpvar>> lhs, lp::lconstraint_kind k, rational const &rhs);
// initialization
void init_solver();
void init_vars();
void init_monomial(unsigned mon_var);
void init_occurs();
void init_occurs(lp::constraint_index ci);
void eliminate_variables();
lp::lpvar select_variable_to_eliminate(lp::constraint_index ci);
unsigned degree_of_var_in_constraint(lpvar v, lp::constraint_index ci) const;
unsigned degree_of_var_in_monomial(lpvar v, lpvar mi) const;
factorization factor(lpvar v, lp::constraint_index ci);
lp::lpvar divide(lpvar v, unsigned degree, lpvar mi);
bool constraint_is_true(lp::constraint_index ci) const;
// additional variables and monomials and constraints
using term_offset = std::pair<lp::lar_term, rational>; // term and its offset
svector<lp::lpvar> expand_monomial(svector<lp::lpvar> const & vars);
lpvar mk_monomial(svector<lp::lpvar> const& vars);
lpvar mk_monomial(svector<lp::lpvar> const &vars, lp::lpvar j);
lpvar mk_term(term_offset &t);
void gcd_normalize(vector<std::pair<rational, lpvar>> &t, lp::lconstraint_kind k, rational &rhs);
lp::constraint_index add_ineq(justification const& just, lp::lar_term &t, lp::lconstraint_kind k, rational const &rhs);
lp::constraint_index add_ineq(justification const &just, lpvar j, lp::lconstraint_kind k,
rational const &rhs);
lp::constraint_index assume(lp::lar_term &t, lp::lconstraint_kind k, rational const &rhs);
lp::constraint_index normalize_ineq(lp::constraint_index ci);
lp::constraint_index gcd_normalize(lp::constraint_index ci);
lp::constraint_index assume(dd::pdd const& p, lp::lconstraint_kind k);
lp::constraint_index add(lp::constraint_index left, lp::constraint_index right);
lp::constraint_index multiply(lp::constraint_index ci, rational const &mul);
lp::constraint_index multiply(lp::constraint_index left, lp::constraint_index right);
lp::constraint_index multiply_var(lp::constraint_index ci, lpvar x);
bool is_int(svector<lp::lpvar> const& vars) const;
rational cvalue(lp::lar_term const &t) const;
rational mvalue(lp::lar_term const &t) const;
rational cvalue(svector<lpvar> const &prod) const;
rational mvalue(svector<lpvar> const &prod) const;
rational cvalue(dd::pdd const& p) const;
lpvar add_var(bool is_int);
// lemmas
@ -162,12 +155,11 @@ namespace nla {
std::ostream& display(std::ostream& out) const;
std::ostream& display_product(std::ostream& out, svector<lpvar> const& vars) const;
std::ostream& display_constraint(std::ostream& out, lp::constraint_index ci) const;
std::ostream& display_constraint(std::ostream& out, lp::lar_term const& lhs,
lp::lconstraint_kind k, rational const& rhs) const;
std::ostream& display(std::ostream &out, term_offset const &t) const;
std::ostream& display(std::ostream &out, justification const &bounds) const;
std::ostream& display_constraint(std::ostream& out, constraint const& c) const;
std::ostream &display(std::ostream &out, justification const &j) const;
std::ostream &display_var(std::ostream &out, lpvar j) const;
std::ostream &display_lemma(std::ostream &out, lp::explanation const &ex, vector<ineq> const &ineqs);
std::ostream &display(std::ostream &out, term_offset const &t) const;
public:
stellensatz(core* core);