3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-12-02 01:59:05 +00:00

integrating changes from master related to work with polynomials

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
Lev Nachmanson 2025-11-24 13:00:43 -10:00
parent 6cbc6f4f84
commit ddba2cc381
9 changed files with 390 additions and 530 deletions

View file

@ -24,6 +24,7 @@ z3_add_component(lp
monomial_bounds.cpp
nex_creator.cpp
nla_basics_lemmas.cpp
nla_coi.cpp
nla_common.cpp
nla_core.cpp
nla_divisions.cpp
@ -32,6 +33,7 @@ z3_add_component(lp
nla_monotone_lemmas.cpp
nla_order_lemmas.cpp
nla_powers.cpp
nla_pp.cpp
nla_solver.cpp
nla_tangent_lemmas.cpp
nla_throttle.cpp

View file

@ -1,4 +1,4 @@
/*++
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
@ -17,6 +17,7 @@ Author:
#include "math/grobner/pdd_solver.h"
#include "math/dd/pdd_interval.h"
#include "math/dd/pdd_eval.h"
using namespace nla;
typedef lp::lar_term term;
@ -38,7 +39,7 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) :
m_grobner(this),
m_emons(m_evars),
m_use_nra_model(false),
m_nra(s, m_nra_lim, *this),
m_nra(s, m_nra_lim, *this, p),
m_throttle(lra.trail(),
lra.settings().stats()) {
m_nlsat_delay_bound = lp_settings().nlsat_delay();
@ -51,6 +52,10 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) :
}
};
}
void core::updt_params(params_ref const& p) {
// grobner updt_params is a no-op on this branch
}
bool core::compare_holds(const rational& ls, llc cmp, const rational& rs) const {
switch(cmp) {
@ -88,13 +93,6 @@ lpvar core::map_to_root(lpvar j) const {
return m_evars.find(j).var();
}
// Reduce a single variable to its root and provide the reduction sign.
lpvar core::reduce_var_to_rooted(lpvar j, rational & sign) const {
auto root = m_evars.find(j);
sign = rational(root.sign() ? -1 : 1);
return root.var();
}
svector<lpvar> core::sorted_rvars(const factor& f) const {
if (f.is_var()) {
svector<lpvar> r; r.push_back(map_to_root(f.var()));
@ -179,108 +177,6 @@ bool core::check_monic(const monic& m) const {
}
template <typename T>
std::ostream& core::print_product(const T & m, std::ostream& out) const {
bool first = true;
for (lpvar v : m) {
if (!first) out << "*"; else first = false;
if (lp_settings().print_external_var_name())
out << "(" << lra.get_variable_name(v) << "=" << val(v) << ")";
else
out << "(j" << v << " = " << val(v) << ")";
}
return out;
}
template <typename T>
std::string core::product_indices_str(const T & m) const {
std::stringstream out;
bool first = true;
for (lpvar v : m) {
if (!first)
out << "*";
else
first = false;
out << "j" << v;;
}
return out.str();
}
std::ostream & core::print_factor(const factor& f, std::ostream& out) const {
if (f.sign())
out << "- ";
if (f.is_var()) {
out << "VAR, " << pp(f.var());
} else {
out << "MON, v" << m_emons[f.var()] << " = ";
print_product(m_emons[f.var()].rvars(), out);
}
out << "\n";
return out;
}
std::ostream & core::print_factor_with_vars(const factor& f, std::ostream& out) const {
if (f.is_var()) {
out << pp(f.var());
}
else {
out << " MON = " << pp_mon_with_vars(*this, m_emons[f.var()]);
}
return out;
}
std::ostream& core::print_monic(const monic& m, std::ostream& out) const {
if (lp_settings().print_external_var_name())
out << "([" << m.var() << "] = " << lra.get_variable_name(m.var()) << " = " << val(m.var()) << " = ";
else
out << "(j" << m.var() << " = " << val(m.var()) << " = ";
print_product(m.vars(), out) << ")\n";
return out;
}
std::ostream& core::print_bfc(const factorization& m, std::ostream& out) const {
SASSERT(m.size() == 2);
out << "( x = " << pp(m[0]) << "* y = " << pp(m[1]) << ")";
return out;
}
std::ostream& core::print_monic_with_vars(lpvar v, std::ostream& out) const {
return print_monic_with_vars(m_emons[v], out);
}
template <typename T>
std::ostream& core::print_product_with_vars(const T& m, std::ostream& out) const {
print_product(m, out) << "\n";
for (unsigned k = 0; k < m.size(); k++) {
print_var(m[k], out);
}
return out;
}
std::ostream& core::print_monic_with_vars(const monic& m, std::ostream& out) const {
out << "[" << pp(m.var()) << "]\n";
out << "vars:"; print_product_with_vars(m.vars(), out) << "\n";
if (m.vars() == m.rvars())
out << "same rvars, and m.rsign = " << m.rsign() << " of course\n";
else {
out << "rvars:"; print_product_with_vars(m.rvars(), out) << "\n";
out << "rsign:" << m.rsign() << "\n";
}
return out;
}
std::ostream& core::print_explanation(const lp::explanation& exp, std::ostream& out) const {
out << "expl: ";
unsigned i = 0;
for (auto p : exp) {
out << "(" << p.ci() << ")";
lra.constraints().display(out, [this](lpvar j) { return var_str(j);}, p.ci());
if (++i < exp.size())
out << " ";
}
return out;
}
bool core::explain_upper_bound(const lp::lar_term& t, const rational& rs, lp::explanation& e) const {
rational b(0); // the bound
for (lp::lar_term::ival p : t) {
@ -558,69 +454,6 @@ bool core::var_is_free(lpvar j) const {
return lra.column_is_free(j);
}
std::ostream & core::print_ineq(const ineq & in, std::ostream & out) const {
lra.print_term_as_indices(in.term(), out);
return out << " " << lconstraint_kind_string(in.cmp()) << " " << in.rs();
}
std::ostream & core::print_var(lpvar j, std::ostream & out) const {
if (is_monic_var(j))
print_monic(m_emons[j], out);
lra.print_column_info(j, out);
signed_var jr = m_evars.find(j);
out << "root=";
if (jr.sign()) {
out << "-";
}
out << lra.get_variable_name(jr.var()) << "\n";
return out;
}
std::ostream & core::print_monics(std::ostream & out) const {
for (auto &m : m_emons) {
print_monic_with_vars(m, out);
}
return out;
}
std::ostream & core::print_ineqs(const lemma& l, std::ostream & out) const {
std::unordered_set<lpvar> vars;
out << "ineqs: ";
if (l.ineqs().size() == 0) {
out << "conflict\n";
} else {
for (unsigned i = 0; i < l.ineqs().size(); i++) {
auto & in = l.ineqs()[i];
print_ineq(in, out);
if (i + 1 < l.ineqs().size()) out << " or ";
for (lp::lar_term::ival p: in.term())
vars.insert(p.j());
}
out << std::endl;
for (lpvar j : vars) {
print_var(j, out);
}
out << "\n";
}
return out;
}
std::ostream & core::print_factorization(const factorization& f, std::ostream& out) const {
if (f.is_mon()){
out << "is_mon " << pp_mon(*this, f.mon());
}
else {
for (unsigned k = 0; k < f.size(); k++ ) {
out << "(" << pp(f[k]) << ")";
if (k < f.size() - 1)
out << "*";
}
}
return out;
}
bool core::find_canonical_monic_of_vars(const svector<lpvar>& vars, lpvar & i) const {
monic const* sv = m_emons.find_canonical(vars);
return sv && (i = sv->var(), true);
@ -630,16 +463,6 @@ bool core::is_canonical_monic(lpvar j) const {
return m_emons.is_canonical_monic(j);
}
void core::trace_print_monic_and_factorization(const monic& rm, const factorization& f, std::ostream& out) const {
out << "rooted vars: ";
print_product(rm.rvars(), out) << "\n";
out << "mon: " << pp_mon(*this, rm.var()) << "\n";
out << "value: " << var_val(rm) << "\n";
print_factorization(f, out << "fact: ") << "\n";
}
bool core::var_has_positive_lower_bound(lpvar j) const {
return lra.column_has_lower_bound(j) && lra.get_lower_bound(j) > lp::zero_of_type<lp::impq>();
}
@ -778,35 +601,6 @@ bool core::vars_are_roots(const T& v) const {
}
template <typename T>
void core::trace_print_rms(const T& p, std::ostream& out) {
out << "p = {\n";
for (auto j : p) {
out << "j = " << j << ", rm = " << m_emons[j] << "\n";
}
out << "}";
}
void core::print_monic_stats(const monic& m, std::ostream& out) {
if (m.size() == 2) return;
monic_coeff mc = canonize_monic(m);
for(unsigned i = 0; i < mc.vars().size(); i++){
if (abs(val(mc.vars()[i])) == rational(1)) {
auto vv = mc.vars();
vv.erase(vv.begin()+i);
monic const* sv = m_emons.find_canonical(vv);
if (!sv) {
out << "nf length" << vv.size() << "\n"; ;
}
}
}
}
void core::print_stats(std::ostream& out) {
}
void core::clear() {
m_lemmas.clear();
m_literals.clear();
@ -1055,7 +849,6 @@ lemma_builder::lemma_builder(core& c, const char* name):name(name), c(c) {
lemma_builder& lemma_builder::operator|=(ineq const& ineq) {
if (!c.explain_ineq(*this, ineq.term(), ineq.cmp(), ineq.rs())) {
CTRACE(nla_solver, c.ineq_holds(ineq), c.print_ineq(ineq, tout) << "\n";);
CTRACE(nra, c.ineq_holds(ineq), c.print_ineq(ineq, tout) << "\n";);
SASSERT(c.m_use_nra_model || !c.ineq_holds(ineq));
current().push_back(ineq);
}
@ -1515,6 +1308,10 @@ lbool core::check() {
if (no_effect())
m_monomial_bounds.propagate();
if (no_effect() && refine_pseudo_linear())
return l_false;
{
std::function<void(void)> check1 = [&]() { if (no_effect() && run_horner) m_horner.horner_lemmas(); };
@ -1537,9 +1334,9 @@ lbool core::check() {
scoped_limits sl(m_reslim);
sl.push_child(&m_nra_lim);
ret = m_nra.check_assignment();
IF_VERBOSE(1, verbose_stream() << "nra check_assignment returned " << ret << "\n";);
}
else if (no_effect() && should_run_bounded_nlsat())
}
if (no_effect() && should_run_bounded_nlsat())
ret = bounded_nlsat();
if (no_effect())
@ -1551,6 +1348,7 @@ lbool core::check() {
if (no_effect())
m_divisions.check();
if (no_effect()) {
std::function<void(void)> check1 = [&]() { m_order.order_lemma();
};
@ -1577,10 +1375,7 @@ lbool core::check() {
lp_settings().stats().m_nra_calls++;
}
if (ret == l_undef)
return l_undef;
if (!no_effect() && m_reslim.inc())
if (ret == l_undef && !no_effect() && m_reslim.inc())
ret = l_false;
lp_settings().stats().m_nla_lemmas += m_lemmas.size();
@ -1588,6 +1383,8 @@ lbool core::check() {
TRACE(nla_solver, tout << "ret = " << ret << ", lemmas count = " << m_lemmas.size() << "\n";);
IF_VERBOSE(5, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monics(verbose_stream());});
CTRACE(nla_solver, ret == l_undef, tout << "Monomials\n"; print_monics(tout););
CTRACE(nla_solver, ret == l_undef, display_smt(tout););
// if (ret == l_undef) IF_VERBOSE(0, display_smt(verbose_stream()));
return ret;
}
@ -1600,18 +1397,13 @@ bool core::should_run_bounded_nlsat() {
}
lbool core::bounded_nlsat() {
params_ref p;
lbool ret;
p.set_uint("max_conflicts", 100);
m_nra.updt_params(p);
{
scoped_limits sl(m_reslim);
sl.push_child(&m_nra_lim);
scoped_rlimit sr(m_nra_lim, 100000);
ret = m_nra.check();
}
p.set_uint("max_conflicts", UINT_MAX);
m_nra.updt_params(p);
lp_settings().stats().m_nra_calls++;
if (ret == l_undef)
++m_nlsat_delay_bound;
@ -1635,40 +1427,11 @@ bool core::no_lemmas_hold() const {
return true;
}
lbool core::test_check() {
lra.set_status(lp::lp_status::OPTIMAL);
return check();
}
std::ostream& core::print_terms(std::ostream& out) const {
for (const auto * t: lra.terms()) {
out << "term:"; print_term(*t, out) << std::endl;
print_var(t->j(), out);
}
return out;
}
std::string core::var_str(lpvar j) const {
std::string result;
if (is_monic_var(j))
result += product_indices_str(m_emons[j].vars()) + (check_monic(m_emons[j])? "": "_");
else
result += std::string("j") + lp::T_to_string(j);
// result += ":w" + lp::T_to_string(get_var_weight(j));
return result;
}
std::ostream& core::print_term( const lp::lar_term& t, std::ostream& out) const {
return lp::print_linear_combination_customized(
t.coeffs_as_vector(),
[this](lpvar j) { return var_str(j); },
out);
}
std::unordered_set<lpvar> core::get_vars_of_expr_with_opening_terms(const nex *e ) {
auto ret = get_vars_of_expr(e);
auto & ls = lra;
@ -1691,12 +1454,10 @@ std::unordered_set<lpvar> core::get_vars_of_expr_with_opening_terms(const nex *e
return ret;
}
bool core::is_nl_var(lpvar j) const {
return is_monic_var(j) || m_emons.is_used_in_monic(j);
}
unsigned core::get_var_weight(lpvar j) const {
unsigned k = 0;
switch (lra.get_column_type(j)) {
@ -1765,6 +1526,49 @@ void core::simplify() {
}
bool core::is_pseudo_linear(monic const& m) const {
bool has_unbounded = false;
for (auto v : m.vars()) {
if (lra.column_is_bounded(v) && lra.var_is_int(v)) {
auto lb = lra.get_lower_bound(v);
auto ub = lra.get_upper_bound(v);
if (ub - lb <= rational(4))
continue;
}
if (has_unbounded)
return false;
has_unbounded = true;
}
return true;
}
bool core::refine_pseudo_linear() {
// Parameter support for this optimization is unavailable in this branch.
return false;
}
void core::refine_pseudo_linear(monic const& m) {
lemma_builder lemma(*this, "nla-pseudo-linear");
lpvar nlvar = null_lpvar;
rational prod(1);
for (unsigned i = 0; i < m.vars().size(); ++i) {
auto v = m.vars()[i];
if (i == m.vars().size() - 1 && nlvar == null_lpvar) {
nlvar = v;
break;
}
if (lra.column_is_bounded(v) && lra.var_is_int(v)) {
auto lb = lra.get_lower_bound(v);
auto ub = lra.get_upper_bound(v);
if (ub - lb <= rational(4)) {
lemma |= ineq(v, llc::NE, val(v));
prod *= val(v);
continue;
}
}
SASSERT(nlvar == null_lpvar);
nlvar = v;
}
SASSERT(nlvar != null_lpvar);
lemma |= ineq(lp::lar_term(m.var(), rational(-prod), nlvar), llc::EQ, rational(0));
}

View file

@ -113,7 +113,12 @@ class core {
void check_weighted(unsigned sz, std::pair<unsigned, std::function<void(void)>>* checks);
void add_bounds();
bool refine_pseudo_linear();
bool is_pseudo_linear(monic const& m) const;
void refine_pseudo_linear(monic const& m);
std::ostream& display_constraint_smt(std::ostream& out, unsigned id, lp::lar_base_constraint const& c) const;
std::ostream& display_declarations_smt(std::ostream& out) const;
public:
// constructor
@ -121,6 +126,8 @@ public:
const auto& monics_with_changed_bounds() const { return m_monics_with_changed_bounds; }
void insert_to_refine(lpvar j);
void erase_from_to_refine(lpvar j);
void updt_params(params_ref const& p);
const indexed_uint_set& active_var_set () const { return m_active_var_set;}
bool active_var_set_contains(unsigned j) const { return m_active_var_set.contains(j); }
@ -228,6 +235,9 @@ public:
bool check_monic(const monic& m) const;
std::ostream & display_row(std::ostream& out, lp::row_strip<lp::mpq> const& row) const;
std::ostream & display(std::ostream& out);
std::ostream& display_smt(std::ostream& out);
std::ostream & print_ineq(const ineq & in, std::ostream & out) const;
std::ostream & print_var(lpvar j, std::ostream & out) const;
std::ostream & print_monics(std::ostream & out) const;
@ -285,12 +295,8 @@ public:
svector<lpvar> reduce_monic_to_rooted(const svector<lpvar> & vars, rational & sign) const;
// Reduce a single variable to its canonical root under current equalities
// and return the convertion sign as either 1 or -1
lpvar reduce_var_to_rooted(lpvar v, rational & sign) const;
monic_coeff canonize_monic(monic const& m) const;
int vars_sign(const svector<lpvar>& v);
bool has_upper_bound(lpvar j) const;
bool has_lower_bound(lpvar j) const;
@ -365,6 +371,10 @@ public:
template <typename T>
bool vars_are_roots(const T& v) const;
void register_monic_in_tables(unsigned i_mon);
void register_monics_in_tables();
void clear();
void init_search();
@ -444,6 +454,12 @@ public:
nla_throttle& throttle() { return m_throttle; }
const nla_throttle& throttle() const { return m_throttle; }
lp::lar_solver& lra_solver() { return lra; }
indexed_uint_set const& to_refine() const {
return m_to_refine;
}
}; // end of core
struct pp_mon {
@ -466,4 +482,3 @@ inline std::ostream& operator<<(std::ostream& out, pp_factorization const& f) {
inline std::ostream& operator<<(std::ostream& out, pp_var const& v) { return v.c.print_var(v.v, out); }
} // end of namespace nla

View file

@ -98,6 +98,10 @@ namespace nla {
return m_solver.equations();
}
void grobner::updt_params(params_ref const&) {
// placeholder: nl2lin branch does not expose grobner-specific params from master
}
bool grobner::is_conflicting() {
for (auto eq : m_solver.equations()) {
if (is_conflicting(*eq)) {

View file

@ -12,6 +12,7 @@
#include "math/lp/nla_intervals.h"
#include "math/lp/nex.h"
#include "math/lp/cross_nested.h"
#include "util/params.h"
#include "util/uint_set.h"
#include "math/grobner/pdd_solver.h"
@ -73,6 +74,7 @@ namespace nla {
public:
grobner(core *core);
void operator()();
void updt_params(params_ref const& p);
dd::solver::equation_vector const& core_equations(bool all_eqs);
};
}

View file

@ -35,6 +35,10 @@ namespace nla {
void solver::set_relevant(std::function<bool(lpvar)>& is_relevant) {
m_core->set_relevant(is_relevant);
}
void solver::updt_params(params_ref const& p) {
m_core->updt_params(p);
}
bool solver::is_monic_var(lpvar v) const {
return m_core->is_monic_var(v);
@ -71,7 +75,7 @@ namespace nla {
}
std::ostream& solver::display(std::ostream& out) const {
m_core->print_monics(out);
m_core->display(out);
if (use_nra_model())
m_core->m_nra.display(out);
return out;

View file

@ -33,6 +33,7 @@ namespace nla {
void add_bounded_division(lpvar q, lpvar x, lpvar y);
void check_bounded_divisions();
void set_relevant(std::function<bool(lpvar)>& is_relevant);
void updt_params(params_ref const& p);
void push();
void pop(unsigned scopes);
bool need_check();

View file

@ -9,6 +9,7 @@
#include <fstream>
#include "math/lp/lar_solver.h"
#include "math/lp/nra_solver.h"
#include "math/lp/nla_coi.h"
#include "nlsat/nlsat_solver.h"
#include "nlsat/nlsat_assignment.h"
#include "math/polynomial/polynomial.h"
@ -16,7 +17,6 @@
#include "util/map.h"
#include "util/uint_set.h"
#include "math/lp/nla_core.h"
#include "math/lp/monic.h"
#include "params/smt_params_helper.hpp"
@ -27,174 +27,82 @@ typedef nla::mon_eq mon_eq;
typedef nla::variable_map_type variable_map_type;
struct solver::imp {
lp::lar_solver& lra;
reslimit& m_limit;
params_ref m_params;
u_map<polynomial::var> m_lp2nl; // map from lar_solver variables to nlsat::solver variables
indexed_uint_set m_term_set;
scoped_ptr<nlsat::solver> m_nlsat;
scoped_ptr<scoped_anum_vector> m_values; // values provided by LRA solver
scoped_ptr<scoped_anum> m_tmp1, m_tmp2;
nla::coi m_coi;
svector<lp::constraint_index> m_literal2constraint;
struct eq {
bool operator()(unsigned_vector const &a, unsigned_vector const &b) const {
return a == b;
}
};
map<unsigned_vector, unsigned, svector_hash<unsigned_hash>, eq> m_vars2mon;
nla::core& m_nla_core;
imp(lp::lar_solver& s, reslimit& lim, params_ref const& p, nla::core& nla_core):
lra(s),
m_limit(lim),
m_params(p),
m_coi(nla_core),
m_nla_core(nla_core) {}
bool need_check() {
return m_nla_core.m_to_refine.size() != 0;
}
indexed_uint_set m_mon_set, m_constraint_set;
struct occurs {
unsigned_vector constraints;
unsigned_vector monics;
unsigned_vector terms;
};
void init_cone_of_influence() {
indexed_uint_set visited;
unsigned_vector todo;
vector<occurs> var2occurs;
m_term_set.reset();
m_mon_set.reset();
m_constraint_set.reset();
for (auto ci : lra.constraints().indices()) {
auto const& c = lra.constraints()[ci];
if (c.is_auxiliary())
continue;
for (auto const& [coeff, v] : c.coeffs()) {
var2occurs.reserve(v + 1);
var2occurs[v].constraints.push_back(ci);
}
}
for (auto const& m : m_nla_core.emons()) {
for (auto v : m.vars()) {
var2occurs.reserve(v + 1);
var2occurs[v].monics.push_back(m.var());
}
}
for (const auto *t : lra.terms() ) {
for (auto const iv : *t) {
auto v = iv.j();
var2occurs.reserve(v + 1);
var2occurs[v].terms.push_back(t->j());
}
}
for (auto const& m : m_nla_core.m_to_refine)
todo.push_back(m);
for (unsigned i = 0; i < todo.size(); ++i) {
auto v = todo[i];
if (visited.contains(v))
continue;
visited.insert(v);
var2occurs.reserve(v + 1);
for (auto ci : var2occurs[v].constraints) {
m_constraint_set.insert(ci);
auto const& c = lra.constraints()[ci];
for (auto const& [coeff, w] : c.coeffs())
todo.push_back(w);
}
for (auto w : var2occurs[v].monics)
todo.push_back(w);
for (auto ti : var2occurs[v].terms) {
for (auto iv : lra.get_term(ti))
todo.push_back(iv.j());
todo.push_back(ti);
}
if (lra.column_has_term(v)) {
m_term_set.insert(v);
for (auto kv : lra.get_term(v))
todo.push_back(kv.j());
}
if (m_nla_core.is_monic_var(v)) {
m_mon_set.insert(v);
for (auto w : m_nla_core.emons()[v])
todo.push_back(w);
}
}
}
void reset() {
m_values = nullptr;
m_tmp1 = nullptr; m_tmp2 = nullptr;
m_nlsat = alloc(nlsat::solver, m_limit, m_params, false);
m_values = alloc(scoped_anum_vector, am());
m_term_set.reset();
m_lp2nl.reset();
}
void setup_solver() {
SASSERT(need_check());
reset();
init_cone_of_influence();
// add linear inequalities from lra_solver
for (auto ci : m_constraint_set)
add_constraint(ci);
// add polynomial definitions.
for (auto const& m : m_mon_set)
add_monic_eq(m_nla_core.emons()[m]);
// add term definitions.
for (unsigned i : m_term_set)
add_term(i);
TRACE(nra, m_nlsat->display(tout));
log_nl();
}
void log_nl() {
smt_params_helper p(m_params);
if (p.arith_nl_log()) {
static unsigned id = 0;
std::stringstream strm;
#ifndef SINGLE_THREAD
std::thread::id this_id = std::this_thread::get_id();
strm << "nla_" << this_id << "." << (++id) << ".smt2";
#else
strm << "nla_" << (++id) << ".smt2";
#endif
std::ofstream out(strm.str());
m_nlsat->display_smt2(out);
out << "(check-sat)\n";
out.close();
}
}
//
// This setup is for check_assignment which is better suitated for working with input polynomials diretly.
// First slice the set of constraints to remove clusters that are satisfied by the current assignment.
// Then initialize each variable as either a polynomial variable or as a definition for a term or monomial.
// Finally process all constraints and use the definitions to represent these into the nlsat solver.
//
svector<lp::constraint_index> m_literal2constraint;
struct eq {
bool operator()(unsigned_vector const &a, unsigned_vector const &b) const {
return a == b;
}
};
map<unsigned_vector, unsigned, svector_hash<unsigned_hash>, eq> m_vars2mon;
// Create polynomial definition for variable v used in setup_assignment_solver.
// Side-effects: updates m_vars2mon when v is a monic variable.
void mk_definition(unsigned v, polynomial_ref_vector & definitions) {
void mk_definition(unsigned v, polynomial_ref_vector &definitions, vector<rational>& denominators) {
auto &pm = m_nlsat->pm();
polynomial::polynomial_ref p(pm);
rational den(1);
if (m_nla_core.emons().is_monic_var(v)) {
auto const &m = m_nla_core.emons()[v];
for (auto v2 : m.vars()) {
polynomial_ref pw(definitions.get(v2), m_nlsat->pm());
if (!p)
p = pw;
else
p = p * pw;
}
}
else if (lra.column_has_term(v)) {
for (auto const &[w, coeff] : lra.get_term(v)) {
den = lcm(denominator(coeff), den);
}
for (auto const &[w, coeff] : lra.get_term(v)) {
auto coeff1 = den * coeff;
polynomial_ref pw(definitions.get(w), m_nlsat->pm());
if (!p)
p = constant(coeff1) * pw;
else
p = p + (constant(coeff1) * pw);
}
}
else {
p = pm.mk_polynomial(lp2nl(v)); // nlsat var index equals v (verified above when created)
}
definitions.push_back(p);
denominators.push_back(den);
}
// Create polynomial definition for variable v used in setup_assignment_solver.
// Side-effects: updates m_vars2mon when v is a monic variable.
void mk_definition_assignment(unsigned v, polynomial_ref_vector &definitions) {
auto &pm = m_nlsat->pm();
polynomial::polynomial_ref p(pm);
if (m_nla_core.emons().is_monic_var(v)) {
@ -209,95 +117,94 @@ struct solver::imp {
else
p = pm.mul(p, pv);
}
}
else if (lra.column_has_term(v)) {
// Scale coefficients by a common denominator so that they become integers.
}
else if (lra.column_has_term(v)) {
rational den(1);
for (auto const& [w, coeff] : lra.get_term(v))
den = lcm(den, denominator(coeff));
for (auto const& [w, coeff] : lra.get_term(v)) {
auto pw = definitions.get(w);
if (!p)
p = pm.mul(den*coeff, pw);
p = pm.mul(den * coeff, pw);
else
p = pm.add(p, pm.mul(den*coeff, pw));
p = pm.add(p, pm.mul(den * coeff, pw));
}
}
else {
p = pm.mk_polynomial(v); // nlsat var index equals v (verified above when created)
}
else {
p = pm.mk_polynomial(lp2nl(v));
}
definitions.push_back(p);
}
void setup_assignment_solver() {
SASSERT(need_check());
reset();
m_literal2constraint.reset();
m_vars2mon.reset();
init_cone_of_influence();
void setup_solver_poly() {
m_coi.init();
auto &pm = m_nlsat->pm();
polynomial_ref_vector definitions(pm);
vector<rational> denominators;
for (unsigned v = 0; v < lra.number_of_vars(); ++v) {
auto j = m_nlsat->mk_var(lra.var_is_int(v));
VERIFY(j == v);
m_lp2nl.insert(v, j); // we don't really need this. It is going to be the identify map.
scoped_anum a(am());
am().set(a, m_nla_core.val(v).to_mpq());
m_values->push_back(a);
mk_definition(v, definitions);
if (m_coi.vars().contains(v)) {
auto j = m_nlsat->mk_var(lra.var_is_int(v));
m_lp2nl.insert(v, j); // we don't really need this. It is going to be the identify map.
mk_definition(v, definitions, denominators);
}
else {
definitions.push_back(nullptr);
denominators.push_back(rational(0));
}
}
// we rely on that all information encoded into the tableau is present as a constraint.
for (auto ci : m_constraint_set) {
for (auto ci : m_coi.constraints()) {
auto &c = lra.constraints()[ci];
auto &pm = m_nlsat->pm();
auto k = c.kind();
auto rhs = c.rhs();
auto lhs = c.coeffs();
rational den = denominator(rhs);
for (auto [coeff, v] : lhs)
den = lcm(den, denominator(coeff));
for (auto [coeff, v] : lhs)
den = lcm(lcm(den, denominator(coeff)), denominators[v]);
polynomial::polynomial_ref p(pm);
p = pm.mk_const(-den * rhs);
for (auto [coeff, v] : lhs) {
polynomial_ref poly(pm);
poly = pm.mul(den * coeff, definitions.get(v));
poly = definitions.get(v);
poly = poly * constant(den * coeff / denominators[v]);
p = p + poly;
}
auto lit = add_constraint(p, ci, k);
m_literal2constraint.setx(lit.index(), ci, lp::null_ci);
}
log_nl();
definitions.reset();
}
void validate_constraints() {
for (lp::constraint_index ci : lra.constraints().indices())
if (!check_constraint(ci)) {
IF_VERBOSE(0, verbose_stream() << "constraint " << ci << " violated\n";
lra.constraints().display(verbose_stream()));
UNREACHABLE();
return;
}
for (auto const& m : m_nla_core.emons()) {
if (!check_monic(m)) {
IF_VERBOSE(0, verbose_stream() << "monic " << m << " violated\n";
lra.constraints().display(verbose_stream()));
UNREACHABLE();
return;
}
}
void setup_solver_terms() {
m_coi.init();
// add linear inequalities from lra_solver
for (auto ci : m_coi.constraints())
add_constraint(ci);
// add polynomial definitions.
for (auto const &m : m_coi.mons())
add_monic_eq(m_nla_core.emons()[m]);
// add term definitions.
for (unsigned i : m_coi.terms())
add_term(i);
}
bool check_constraints() {
for (lp::constraint_index ci : lra.constraints().indices())
if (!check_constraint(ci))
return false;
for (auto const& m : m_nla_core.emons())
if (!check_monic(m))
return false;
return true;
polynomial::polynomial_ref sub(polynomial::polynomial *a, polynomial::polynomial *b) {
return polynomial_ref(m_nlsat->pm().sub(a, b), m_nlsat->pm());
}
polynomial::polynomial_ref mul(polynomial::polynomial *a, polynomial::polynomial *b) {
return polynomial_ref(m_nlsat->pm().mul(a, b), m_nlsat->pm());
}
polynomial::polynomial_ref var(lp::lpvar v) {
return polynomial_ref(m_nlsat->pm().mk_polynomial(lp2nl(v)), m_nlsat->pm());
}
polynomial::polynomial_ref constant(rational const& r) {
return polynomial_ref(m_nlsat->pm().mk_const(r), m_nlsat->pm());
}
/**
@ -312,7 +219,32 @@ struct solver::imp {
TBD: explore more incremental ways of applying nlsat (using assumptions)
*/
lbool check() {
setup_solver();
SASSERT(need_check());
reset();
vector<nlsat::assumption, false> core;
smt_params_helper p(m_params);
setup_solver_poly();
TRACE(nra, m_nlsat->display(tout));
if (p.arith_nl_log()) {
static unsigned id = 0;
std::stringstream strm;
#ifndef SINGLE_THREAD
std::thread::id this_id = std::this_thread::get_id();
strm << "nla_" << this_id << "." << (++id) << ".smt2";
#else
strm << "nla_" << (++id) << ".smt2";
#endif
std::ofstream out(strm.str());
m_nlsat->display_smt2(out);
out << "(check-sat)\n";
out.close();
}
lbool r = l_undef;
statistics& st = m_nla_core.lp_settings().stats().m_st;
try {
@ -328,19 +260,43 @@ struct solver::imp {
}
}
m_nlsat->collect_statistics(st);
TRACE(nra, m_nlsat->display(tout << r << "\n"););
TRACE(nra, tout << "nra result " << r << "\n");
CTRACE(nra, false,
m_nlsat->display(tout << r << "\n");
display(tout);
for (auto [j, x] : m_lp2nl) tout << "j" << j << " := x" << x << "\n";);
switch (r) {
case l_true:
m_nla_core.set_use_nra_model(true);
lra.init_model();
validate_constraints();
for (lp::constraint_index ci : lra.constraints().indices())
if (!check_constraint(ci)) {
IF_VERBOSE(0, verbose_stream() << "constraint " << ci << " violated\n";
lra.constraints().display(verbose_stream()));
UNREACHABLE();
return l_undef;
}
for (auto const &m : m_nla_core.emons()) {
if (!check_monic(m)) {
IF_VERBOSE(0, verbose_stream() << "monic " << m << " violated\n";
lra.constraints().display(verbose_stream()));
UNREACHABLE();
return l_undef;
}
}
break;
case l_false: {
lp::explanation ex;
constraint_core2ex(ex);
m_nlsat->get_core(core);
nla::lemma_builder lemma(m_nla_core, __FUNCTION__);
for (auto c : core) {
unsigned idx = static_cast<unsigned>(static_cast<imp *>(c) - this);
ex.push_back(idx);
}
lemma &= ex;
m_nla_core.set_use_nra_model(true);
TRACE(nra, tout << lemma << "\n");
break;
}
case l_undef:
@ -349,13 +305,51 @@ struct solver::imp {
return r;
}
void setup_assignment_solver() {
SASSERT(need_check());
reset();
m_literal2constraint.reset();
m_vars2mon.reset();
m_coi.init();
auto &pm = m_nlsat->pm();
polynomial_ref_vector definitions(pm);
for (unsigned v = 0; v < lra.number_of_vars(); ++v) {
auto j = m_nlsat->mk_var(lra.var_is_int(v));
VERIFY(j == v);
m_lp2nl.insert(v, j);
scoped_anum a(am());
am().set(a, m_nla_core.val(v).to_mpq());
m_values->push_back(a);
mk_definition_assignment(v, definitions);
}
for (auto ci : m_coi.constraints()) {
auto &c = lra.constraints()[ci];
auto &pm = m_nlsat->pm();
auto k = c.kind();
auto rhs = c.rhs();
auto lhs = c.coeffs();
rational den = denominator(rhs);
for (auto [coeff, v] : lhs)
den = lcm(den, denominator(coeff));
polynomial::polynomial_ref p(pm);
p = pm.mk_const(-den * rhs);
for (auto [coeff, v] : lhs) {
polynomial_ref poly(pm);
poly = pm.mul(den * coeff, definitions.get(v));
p = p + poly;
}
auto lit = add_constraint(p, ci, k);
m_literal2constraint.setx(lit.index(), ci, lp::null_ci);
}
}
void process_polynomial_check_assignment(polynomial::polynomial const* p, rational& bound, const u_map<lp::lpvar>& nl2lp, lp::lar_term& t) {
polynomial::manager& pm = m_nlsat->pm();
for (unsigned i = 0; i < pm.size(p); ++i) {
polynomial::monomial* m = pm.get_monomial(p, i);
TRACE(nra, tout << "monomial: "; pm.display(tout, m); tout << "\n";);
auto& coeff = pm.coeff(p, i);
TRACE(nra, tout << "coeff:"; pm.m().display(tout, coeff););
unsigned num_vars = pm.size(m);
// add mon * coeff to t;
@ -378,7 +372,6 @@ struct solver::imp {
v = m_vars2mon[vars];
else
v = m_nla_core.add_mul_def(vars.size(), vars.data());
TRACE(nra, tout << " vars=" << vars << "\n");
t.add_monomial(coeff, v);
break;
}
@ -394,7 +387,6 @@ struct solver::imp {
}
lbool check_assignment() {
IF_VERBOSE(1, verbose_stream() << "nra::solver::check_assignment\n";);
setup_assignment_solver();
lbool r = l_undef;
statistics &st = m_nla_core.lp_settings().stats().m_st;
@ -418,13 +410,16 @@ struct solver::imp {
}
}
m_nlsat->collect_statistics(st);
TRACE(nra, m_nlsat->display(tout << r << "\n"); display(tout); tout << "m_lp2nl:\n";
for (auto [j, x] : m_lp2nl) tout << "j" << j << " := x" << x << "\n";);
switch (r) {
case l_true:
m_nla_core.set_use_nra_model(true);
lra.init_model();
validate_constraints();
for (lp::constraint_index ci : lra.constraints().indices())
if (!check_constraint(ci))
return l_undef;
for (auto const& m : m_nla_core.emons())
if (!check_monic(m))
return l_undef;
m_nla_core.set_use_nra_model(true);
break;
case l_false:
@ -449,22 +444,17 @@ struct solver::imp {
continue;
}
nlsat::atom *a = m_nlsat->bool_var2atom(l.var());
TRACE(nra, tout << "atom: "; m_nlsat->display(tout, *a); tout << "\n";);
SASSERT(!a->is_root_atom());
SASSERT(a->is_ineq_atom());
auto &ia = *to_ineq_atom(a);
if (ia.size() != 1) {
return l_undef; // levnach: not sure what to do here
return l_undef; // factored polynomials not handled here
}
// VERIFY(ia.size() == 1); // deal with factored polynomials later
// a is an inequality atom, i.e., p > 0, p < 0, or p = 0.
polynomial::polynomial const *p = ia.p(0);
TRACE(nra, tout << "polynomial: "; pm.display(tout, p); tout << "\n";);
rational bound(0);
lp::lar_term t;
process_polynomial_check_assignment(p, bound, nl2lp, t);
// Introduce a single ineq variable and assign it per case; common handling after switch.
nla::ineq inq(lp::lconstraint_kind::EQ, t, bound); // initial value overwritten in cases below
switch (a->get_kind()) {
case nlsat::atom::EQ:
@ -480,26 +470,14 @@ struct solver::imp {
UNREACHABLE();
return l_undef;
}
// Ignore a lemma which is satisfied
if (m_nla_core.ineq_holds(inq))
return l_undef;
lemma |= inq;
}
IF_VERBOSE(1, verbose_stream() << "linear lemma: " << lemma << "\n");
this->m_nla_core.m_check_feasible = true;
return l_false;
}
void constraint_core2ex(lp::explanation& ex) {
vector<nlsat::assumption, false> core;
m_nlsat->get_core(core);
for (auto c : core) {
unsigned idx = static_cast<unsigned>(static_cast<imp*>(c) - this);
ex.push_back(idx);
TRACE(nra, lra.display_constraint(tout << "ex: " << idx << ": ", idx) << "\n";);
}
}
void add_monic_eq_bound(mon_eq const& m) {
if (!lra.column_has_lower_bound(m.var()) &&
@ -533,15 +511,28 @@ struct solver::imp {
coeffs.push_back(mpz(1));
coeffs.push_back(mpz(-1));
polynomial::polynomial_ref p(pm.mk_polynomial(2, coeffs.data(), mls), pm);
polynomial::polynomial* ps[1] = { p };
bool even[1] = { false };
nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, even);
auto lit = mk_literal(p.get(), lp::lconstraint_kind::EQ);
nlsat::assumption a = nullptr;
m_nlsat->mk_clause(1, &lit, nullptr);
}
nlsat::literal mk_literal(polynomial::polynomial *p, lp::lconstraint_kind k) {
polynomial::polynomial *ps[1] = { p };
bool is_even[1] = { false };
switch (k) {
case lp::lconstraint_kind::LE: return ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even);
case lp::lconstraint_kind::GE: return ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even);
case lp::lconstraint_kind::LT: return m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even);
case lp::lconstraint_kind::GT: return m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even);
case lp::lconstraint_kind::EQ: return m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even);
default: UNREACHABLE(); // unreachable
}
throw default_exception("uexpected operator");
}
void add_constraint(unsigned idx) {
auto &c = lra.constraints()[idx];
auto &pm = m_nlsat->pm();
auto& c = lra.constraints()[idx];
auto& pm = m_nlsat->pm();
auto k = c.kind();
auto rhs = c.rhs();
auto lhs = c.coeffs();
@ -558,32 +549,23 @@ struct solver::imp {
}
rhs *= den;
polynomial::polynomial_ref p(pm.mk_linear(sz, coeffs.data(), vars.data(), -rhs), pm);
add_constraint(p, idx, k);
nlsat::literal lit = mk_literal(p.get(), k);
nlsat::assumption a = this + idx;
m_nlsat->mk_clause(1, &lit, a);
}
nlsat::literal add_constraint(polynomial::polynomial* p, unsigned idx, lp::lconstraint_kind k) {
polynomial::polynomial* ps[1] = { p };
bool is_even[1] = { false };
nlsat::literal add_constraint(polynomial::polynomial *p, unsigned idx, lp::lconstraint_kind k) {
polynomial::polynomial *ps[1] = {p};
bool is_even[1] = {false};
nlsat::literal lit;
nlsat::assumption a = this + idx;
switch (k) {
case lp::lconstraint_kind::LE:
lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even);
break;
case lp::lconstraint_kind::GE:
lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even);
break;
case lp::lconstraint_kind::LT:
lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even);
break;
case lp::lconstraint_kind::GT:
lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even);
break;
case lp::lconstraint_kind::EQ:
lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even);
break;
default:
UNREACHABLE(); // unreachable
case lp::lconstraint_kind::LE: lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); break;
case lp::lconstraint_kind::GE: lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); break;
case lp::lconstraint_kind::LT: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); break;
case lp::lconstraint_kind::GT: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); break;
case lp::lconstraint_kind::EQ: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); break;
default: UNREACHABLE(); // unreachable
}
m_nlsat->mk_clause(1, &lit, a);
return lit;
@ -636,7 +618,7 @@ struct solver::imp {
for (auto const& m : m_nla_core.emons())
if (any_of(m.vars(), [&](lp::lpvar v) { return m_lp2nl.contains(v); }))
add_monic_eq_bound(m);
for (unsigned i : m_term_set)
for (unsigned i : m_coi.terms())
add_term(i);
for (auto const& [v, w] : m_lp2nl) {
if (lra.column_has_lower_bound(v))
@ -665,8 +647,12 @@ struct solver::imp {
case l_true:
m_nla_core.set_use_nra_model(true);
lra.init_model();
if (!check_constraints())
return l_undef;
for (lp::constraint_index ci : lra.constraints().indices())
if (!check_constraint(ci))
return l_undef;
for (auto const& m : m_nla_core.emons())
if (!check_monic(m))
return l_undef;
break;
case l_false: {
lp::explanation ex;
@ -680,6 +666,7 @@ struct solver::imp {
ex.push_back(ci);
nla::lemma_builder lemma(m_nla_core, __FUNCTION__);
lemma &= ex;
TRACE(nra, tout << lemma << "\n");
break;
}
case l_undef:
@ -816,8 +803,8 @@ struct solver::imp {
if (!m_lp2nl.find(v, r)) {
r = m_nlsat->mk_var(is_int(v));
m_lp2nl.insert(v, r);
if (!m_term_set.contains(v) && lra.column_has_term(v)) {
m_term_set.insert(v);
if (!m_coi.terms().contains(v) && lra.column_has_term(v)) {
m_coi.terms().insert(v);
}
}
return r;
@ -848,20 +835,55 @@ struct solver::imp {
m_nlsat->mk_clause(1, &lit, nullptr);
}
nlsat::anum const& value(lp::lpvar v) {
polynomial::var pv;
if (m_lp2nl.find(v, pv))
return m_nlsat->value(pv);
else {
for (unsigned w = m_values->size(); w <= v; ++w) {
scoped_anum a(am());
am().set(a, m_nla_core.val(w).to_mpq());
nlsat::anum const &value(lp::lpvar v) {
init_values(v + 1);
return (*m_values)[v];
}
void init_values(unsigned sz) {
if (m_values->size() >= sz)
return;
unsigned w;
scoped_anum a(am());
for (unsigned v = m_values->size(); v < sz; ++v) {
if (m_nla_core.emons().is_monic_var(v)) {
am().set(a, 1);
auto &m = m_nla_core.emon(v);
for (auto x : m.vars())
am().mul(a, (*m_values)[x], a);
m_values->push_back(a);
}
return (*m_values)[v];
}
else if (lra.column_has_term(v)) {
scoped_anum b(am());
am().set(a, 0);
for (auto const &[w, coeff] : lra.get_term(v)) {
am().set(b, coeff.to_mpq());
am().mul(b, (*m_values)[w], b);
am().add(a, b, a);
}
m_values->push_back(a);
}
else if (m_lp2nl.find(v, w)) {
m_values->push_back(m_nlsat->value(w));
}
else {
am().set(a, m_nla_core.val(v).to_mpq());
m_values->push_back(a);
}
}
}
void set_value(lp::lpvar v, rational const& value) {
if (!m_values)
m_values = alloc(scoped_anum_vector, am());
scoped_anum a(am());
am().set(a, value.to_mpq());
while (m_values->size() <= v)
m_values->push_back(a);
am().set((*m_values)[v], a);
}
nlsat::anum_manager& am() {
return m_nlsat->am();
}
@ -919,7 +941,7 @@ lbool solver::check(dd::solver::equation_vector const& eqs) {
lbool solver::check_assignment() {
return m_imp->check_assignment();
}
}
bool solver::need_check() {
return m_imp->need_check();
@ -946,4 +968,8 @@ void solver::updt_params(params_ref& p) {
m_imp->updt_params(p);
}
void solver::set_value(lp::lpvar v, rational const& value) {
m_imp->set_value(v, value);
}
}

View file

@ -48,7 +48,7 @@ namespace nra {
lbool check(dd::solver::equation_vector const& eqs);
/**
\brief Check feasibility moduo current value assignment.
\brief Check feasibility modulo current value assignment.
*/
lbool check_assignment();
@ -64,6 +64,8 @@ namespace nra {
nlsat::anum_manager& am();
void set_value(lp::lpvar v, rational const &value);
scoped_anum& tmp1();
scoped_anum& tmp2();