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

add option to propagation quotients

for equations x*y + z = 0,
with x, y, z integer, enforce that x divides z
It is (currently) enabled within Grobner completion
and applied partially to x a variable, z linear, and
only when |z| < |x|.
This commit is contained in:
Nikolaj Bjorner 2025-08-31 14:41:23 -07:00
parent 91b4873b79
commit e91e432496
10 changed files with 516 additions and 258 deletions

View file

@ -32,6 +32,7 @@ z3_add_component(lp
nla_monotone_lemmas.cpp nla_monotone_lemmas.cpp
nla_order_lemmas.cpp nla_order_lemmas.cpp
nla_powers.cpp nla_powers.cpp
nla_pp.cpp
nla_solver.cpp nla_solver.cpp
nla_tangent_lemmas.cpp nla_tangent_lemmas.cpp
nla_throttle.cpp nla_throttle.cpp

View file

@ -52,6 +52,10 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) :
}; };
} }
void core::updt_params(params_ref const& p) {
m_grobner.updt_params(p);
}
bool core::compare_holds(const rational& ls, llc cmp, const rational& rs) const { bool core::compare_holds(const rational& ls, llc cmp, const rational& rs) const {
switch(cmp) { switch(cmp) {
case llc::LE: return ls <= rs; case llc::LE: return ls <= rs;
@ -172,108 +176,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 { bool core::explain_upper_bound(const lp::lar_term& t, const rational& rs, lp::explanation& e) const {
rational b(0); // the bound rational b(0); // the bound
for (lp::lar_term::ival p : t) { for (lp::lar_term::ival p : t) {
@ -551,69 +453,6 @@ bool core::var_is_free(lpvar j) const {
return lra.column_is_free(j); 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 { bool core::find_canonical_monic_of_vars(const svector<lpvar>& vars, lpvar & i) const {
monic const* sv = m_emons.find_canonical(vars); monic const* sv = m_emons.find_canonical(vars);
return sv && (i = sv->var(), true); return sv && (i = sv->var(), true);
@ -623,16 +462,6 @@ bool core::is_canonical_monic(lpvar j) const {
return m_emons.is_canonical_monic(j); 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 { 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>(); return lra.column_has_lower_bound(j) && lra.get_lower_bound(j) > lp::zero_of_type<lp::impq>();
} }
@ -771,35 +600,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() { void core::clear() {
m_lemmas.clear(); m_lemmas.clear();
m_literals.clear(); m_literals.clear();
@ -1620,40 +1420,11 @@ bool core::no_lemmas_hold() const {
return true; return true;
} }
lbool core::test_check() { lbool core::test_check() {
lra.set_status(lp::lp_status::OPTIMAL); lra.set_status(lp::lp_status::OPTIMAL);
return check(); 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 ) { std::unordered_set<lpvar> core::get_vars_of_expr_with_opening_terms(const nex *e ) {
auto ret = get_vars_of_expr(e); auto ret = get_vars_of_expr(e);
auto & ls = lra; auto & ls = lra;
@ -1676,12 +1447,10 @@ std::unordered_set<lpvar> core::get_vars_of_expr_with_opening_terms(const nex *e
return ret; return ret;
} }
bool core::is_nl_var(lpvar j) const { bool core::is_nl_var(lpvar j) const {
return is_monic_var(j) || m_emons.is_used_in_monic(j); return is_monic_var(j) || m_emons.is_used_in_monic(j);
} }
unsigned core::get_var_weight(lpvar j) const { unsigned core::get_var_weight(lpvar j) const {
unsigned k = 0; unsigned k = 0;
switch (lra.get_column_type(j)) { switch (lra.get_column_type(j)) {

View file

@ -120,6 +120,8 @@ public:
void insert_to_refine(lpvar j); void insert_to_refine(lpvar j);
void erase_from_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;} 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); } bool active_var_set_contains(unsigned j) const { return m_active_var_set.contains(j); }
@ -224,6 +226,8 @@ public:
bool check_monic(const monic& m) const; 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 & print_ineq(const ineq & in, std::ostream & out) const; std::ostream & print_ineq(const ineq & in, std::ostream & out) const;
std::ostream & print_var(lpvar j, std::ostream & out) const; std::ostream & print_var(lpvar j, std::ostream & out) const;
std::ostream & print_monics(std::ostream & out) const; std::ostream & print_monics(std::ostream & out) const;

View file

@ -11,6 +11,7 @@ Author:
--*/ --*/
#include "util/uint_set.h" #include "util/uint_set.h"
#include "params/smt_params_helper.hpp"
#include "math/lp/nla_core.h" #include "math/lp/nla_core.h"
#include "math/lp/factorization_factory_imp.h" #include "math/lp/factorization_factory_imp.h"
#include "math/grobner/pdd_solver.h" #include "math/grobner/pdd_solver.h"
@ -27,6 +28,11 @@ namespace nla {
m_quota(m_core.params().arith_nl_gr_q()) m_quota(m_core.params().arith_nl_gr_q())
{} {}
void grobner::updt_params(params_ref const& p) {
smt_params_helper ph(p);
m_config.m_propagate_quotients = ph.arith_nl_grobner_propagate_quotients();
}
lp::lp_settings& grobner::lp_settings() { lp::lp_settings& grobner::lp_settings() {
return c().lp_settings(); return c().lp_settings();
} }
@ -73,6 +79,10 @@ namespace nla {
if (propagate_linear_equations()) if (propagate_linear_equations())
return; return;
if (propagate_quotients())
return;
IF_VERBOSE(0, m_solver.display(verbose_stream() << "grobner\n"));
} }
catch (...) { catch (...) {
@ -181,23 +191,12 @@ namespace nla {
// IF_VERBOSE(0, verbose_stream() << "factored " << q << " : " << vars << "\n"); // IF_VERBOSE(0, verbose_stream() << "factored " << q << " : " << vars << "\n");
term t; auto [t, offset] = linear_to_term(q);
rational lc(1);
auto ql = q;
while (!ql.is_val()) {
lc = lcm(lc, denominator(ql.hi().val()));
ql = ql.lo();
}
lc = lcm(denominator(ql.val()), lc);
while (!q.is_val()) {
t.add_monomial(lc*q.hi().val(), q.var());
q = q.lo();
}
vector<ineq> ineqs; vector<ineq> ineqs;
for (auto v : vars) for (auto v : vars)
ineqs.push_back(ineq(v, llc::EQ, rational::zero())); ineqs.push_back(ineq(v, llc::EQ, rational::zero()));
ineqs.push_back(ineq(t, llc::EQ, -lc*q.val())); ineqs.push_back(ineq(t, llc::EQ, -offset));
for (auto const& i : ineqs) for (auto const& i : ineqs)
if (c().ineq_holds(i)) if (c().ineq_holds(i))
return false; return false;
@ -210,6 +209,151 @@ namespace nla {
return true; return true;
} }
std::pair<lp::lar_term, rational> grobner::linear_to_term(dd::pdd q) {
SASSERT(q.is_linear());
rational lc(1);
auto ql = q;
lp::lar_term t;
while (!ql.is_val()) {
lc = lcm(lc, denominator(ql.hi().val()));
ql = ql.lo();
}
lc = lcm(denominator(ql.val()), lc);
while (!q.is_val()) {
t.add_monomial(lc * q.hi().val(), q.var());
q = q.lo();
}
rational offset = lc * q.val();
return {t, offset};
}
bool grobner::propagate_quotients() {
if (!m_config.m_propagate_quotients)
return false;
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_quotients(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
// factor each nl var at a time.
// x*y + z = 0
// x = 0 => z = 0
// y = 0 => z = 0
// z = 0 => x = 0 or y = 0
// z > 0 & x > 0 => x <= z
// z < 0 & x > 0 => x <= -z
// z > 0 & x < 0 => -x <= z
// z < 0 & x < 0 => -x <= -z
bool grobner::propagate_quotients(dd::solver::equation const& eq) {
dd::pdd const& p = eq.poly();
if (p.is_linear())
return false;
if (p.is_val())
return false;
auto v = p.var();
if (!c().var_is_int(v))
return false;
for (auto v : p.free_vars())
if (!c().var_is_int(v))
return false;
tracked_uint_set nl_vars;
for (auto const& m : p) {
if (m.vars.size() == 1)
continue;
for (auto j : m.vars)
nl_vars.insert(j);
}
dd::pdd_eval eval;
eval.var2val() = [&](unsigned j) { return val(j); };
for (auto v : nl_vars) {
auto& m = p.manager();
dd::pdd lc(m), r(m);
p.factor(v, 1, lc, r);
if (!r.is_linear())
continue;
auto v_value = val(v);
auto r_value = eval(r);
auto lc_value = eval(lc);
if (r_value == 0) {
if (v_value == 0)
continue;
if (lc_value == 0)
continue;
if (!lc.is_linear())
continue;
auto [t, offset] = linear_to_term(lc);
auto [t2, offset2] = linear_to_term(r);
lemma_builder lemma(c(), "pdd-quotient");
add_dependencies(lemma, eq);
// v = 0 or lc = 0 or r != 0
lemma |= ineq(v, llc::EQ, rational::zero());
lemma |= ineq(t, llc::EQ, -offset);
lemma |= ineq(t2, llc::NE, -offset2);
return true;
}
// r_value != 0
if (v_value == 0) {
// v = 0 => r = 0
lemma_builder lemma(c(), "pdd-quotient");
add_dependencies(lemma, eq);
auto [t, offset] = linear_to_term(r);
lemma |= ineq(v, llc::NE, rational::zero());
lemma |= ineq(t, llc::EQ, -offset);
return true;
}
if (lc_value == 0) {
if (!lc.is_linear())
continue;
// lc = 0 => r = 0
lemma_builder lemma(c(), "pdd-quotient");
add_dependencies(lemma, eq);
auto [t, offset] = linear_to_term(lc);
auto [t2, offset2] = linear_to_term(r);
lemma |= ineq(t, llc::NE, -offset);
lemma |= ineq(t2, llc::EQ, -offset2);
return true;
}
if (divides(v_value, r_value))
continue;
if (abs(v_value) > abs(r_value)) {
// v*c + r = 0 & v > 0 => r >= v or -r >= v or r = 0
lemma_builder lemma(c(), "pdd-quotient");
auto [t, offset] = linear_to_term(r);
add_dependencies(lemma, eq);
if (v_value > 0) {
lemma |= ineq(v, llc::LE, rational::zero());
lemma |= ineq(t, llc::EQ, -offset);
t.add_monomial(rational(-1), v);
lemma |= ineq(t, llc::GE, -offset);
auto [t2, offset2] = linear_to_term(-r);
t2.add_monomial(rational(-1), v);
lemma |= ineq(t2, llc::GE, -offset2);
}
else {
// v*lc + r = 0 & v < 0 => r <= v or -r <= v or r = 0
lemma |= ineq(v, llc::GE, rational::zero());
lemma |= ineq(t, llc::EQ, -offset);
t.add_monomial(rational(-1), v);
lemma |= ineq(t, llc::LE, -offset);
auto [t2, offset2] = linear_to_term(-r);
t2.add_monomial(rational(-1), v);
lemma |= ineq(t2, llc::LE, -offset2);
}
return true;
}
// other division lemmas are possible.
// also extend to non-linear r, non-linear lc
}
return false;
}
void grobner::explain(dd::solver::equation const& eq, lp::explanation& exp) { void grobner::explain(dd::solver::equation const& eq, lp::explanation& exp) {
u_dependency_manager dm; u_dependency_manager dm;
vector<unsigned, false> lv; vector<unsigned, false> lv;

View file

@ -19,6 +19,9 @@ namespace nla {
class core; class core;
class grobner : common { class grobner : common {
struct config {
bool m_propagate_quotients = false;
};
dd::pdd_manager m_pdd_manager; dd::pdd_manager m_pdd_manager;
dd::solver m_solver; dd::solver m_solver;
lp::lar_solver& lra; lp::lar_solver& lra;
@ -27,6 +30,7 @@ namespace nla {
unsigned m_delay_base = 0; unsigned m_delay_base = 0;
unsigned m_delay = 0; unsigned m_delay = 0;
bool m_add_all_eqs = false; bool m_add_all_eqs = false;
config m_config;
std::unordered_map<unsigned_vector, lpvar, hash_svector> m_mon2var; std::unordered_map<unsigned_vector, lpvar, hash_svector> m_mon2var;
lp::lp_settings& lp_settings(); lp::lp_settings& lp_settings();
@ -44,6 +48,11 @@ namespace nla {
bool propagate_linear_equations(); bool propagate_linear_equations();
bool propagate_linear_equations(dd::solver::equation const& eq); bool propagate_linear_equations(dd::solver::equation const& eq);
bool propagate_quotients();
bool propagate_quotients(dd::solver::equation const& eq);
std::pair<lp::lar_term, rational> linear_to_term(dd::pdd q);
void add_dependencies(lemma_builder& lemma, dd::solver::equation const& eq); void add_dependencies(lemma_builder& lemma, dd::solver::equation const& eq);
void explain(dd::solver::equation const& eq, lp::explanation& exp); void explain(dd::solver::equation const& eq, lp::explanation& exp);
@ -73,6 +82,7 @@ namespace nla {
public: public:
grobner(core *core); grobner(core *core);
void operator()(); void operator()();
void updt_params(params_ref const& p);
dd::solver::equation_vector const& core_equations(bool all_eqs); dd::solver::equation_vector const& core_equations(bool all_eqs);
}; };
} }

319
src/math/lp/nla_pp.cpp Normal file
View file

@ -0,0 +1,319 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
nla_core.cpp
Author:
Lev Nachmanson (levnach)
Nikolaj Bjorner (nbjorner)
--*/
#include "math/lp/nla_core.h"
using namespace nla;
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;
}
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;
}
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";
}
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) {
}
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::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::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::display_row(std::ostream& out, lp::row_strip<lp::mpq> const& row) const {
auto display_coeff = [&](bool first, lp::mpq const& p) {
if (first && p == 1)
return;
if (first && p > 0)
out << p;
else if (p == 1)
out << " + ";
else if (p > 0)
out << " + " << p << " * ";
else if (p == -1)
out << " - ";
else if (first)
out << p << " * ";
else
out << " - " << -p << " * ";
};
auto display_var = [&](bool first, lp::mpq p, lp::lpvar v) {
if (is_monic_var(v)) {
for (auto w : m_emons[v].vars())
p *= m_evars.find(w).rsign();
}
else
p *= m_evars.find(v).rsign();
display_coeff(first, p);
if (is_monic_var(v)) {
bool first = true;
for (auto w : m_emons[v].vars())
out << (first ? (first = false, "") : " * ") << "j" << m_evars.find(w).var();
}
else
out << "j" << m_evars.find(v).var();
};
bool first = true;
for (auto const& ri : row) {
auto v = ri.var();
if (lra.column_is_fixed(v)) {
auto q = lra.get_column_value(v).x;
if (q == 0)
continue;
q = q * ri.coeff();
if (first)
out << q;
else if (q > 0)
out << " + " << q;
else if (q < 0)
out << " - " << -q;
}
else if (lra.column_has_term(v)) {
auto const& t = lra.get_term(v);
for (lp::lar_term::ival p : t) {
display_var(first, p.coeff() * ri.coeff(), p.j());
first = false;
}
}
else {
display_var(first, ri.coeff(), ri.var());
}
first = false;
}
out << " = 0\n";
return out;
}
std::ostream& core::display(std::ostream& out) {
print_monics(out);
for (unsigned i = 0; i < lra.row_count(); ++i)
display_row(out, lra.get_row(i));
return out;
}

View file

@ -36,6 +36,10 @@ namespace nla {
m_core->set_relevant(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 { bool solver::is_monic_var(lpvar v) const {
return m_core->is_monic_var(v); return m_core->is_monic_var(v);
} }
@ -71,7 +75,7 @@ namespace nla {
} }
std::ostream& solver::display(std::ostream& out) const { std::ostream& solver::display(std::ostream& out) const {
m_core->print_monics(out); m_core->display(out);
if (use_nra_model()) if (use_nra_model())
m_core->m_nra.display(out); m_core->m_nra.display(out);
return out; return out;

View file

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

View file

@ -78,6 +78,7 @@ def_module_params(module_name='smt',
('arith.nl.grobner_expr_degree_growth', UINT, 2, 'grobner\'s maximum expr degree growth'), ('arith.nl.grobner_expr_degree_growth', UINT, 2, 'grobner\'s maximum expr degree growth'),
('arith.nl.grobner_max_simplified', UINT, 10000, 'grobner\'s maximum number of simplifications'), ('arith.nl.grobner_max_simplified', UINT, 10000, 'grobner\'s maximum number of simplifications'),
('arith.nl.grobner_cnfl_to_report', UINT, 1, 'grobner\'s maximum number of conflicts to report'), ('arith.nl.grobner_cnfl_to_report', UINT, 1, 'grobner\'s maximum number of conflicts to report'),
('arith.nl.grobner_propagate_quotients', BOOL, False, 'detect conflicts x*y + z = 0 where x doesn\'t divide z'),
('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'), ('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'),
('arith.nl.grobner_subs_fixed', UINT, 1, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'), ('arith.nl.grobner_subs_fixed', UINT, 1, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.nl.delay', UINT, 10, 'number of calls to final check before invoking bounded nlsat check'), ('arith.nl.delay', UINT, 10, 'number of calls to final check before invoking bounded nlsat check'),

View file

@ -269,6 +269,7 @@ class theory_lra::imp {
return ctx().is_relevant(th.get_enode(u)); return ctx().is_relevant(th.get_enode(u));
}; };
m_nla->set_relevant(is_relevant); m_nla->set_relevant(is_relevant);
m_nla->updt_params(ctx().get_params());
} }
} }
@ -1286,23 +1287,24 @@ public:
} }
else { else {
expr_ref abs_q(m.mk_ite(a.mk_ge(q, zero), q, a.mk_uminus(q)), m);
expr_ref mone(a.mk_int(-1), m); expr_ref mone(a.mk_int(-1), m);
expr_ref modmq(a.mk_sub(mod, abs_q), m);
literal eqz = mk_literal(m.mk_eq(q, zero)); literal eqz = mk_literal(m.mk_eq(q, zero));
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
literal mod_lt_q = mk_literal(a.mk_le(modmq, mone));
// q = 0 or p = (p mod q) + q * (p div q) // q = 0 or p = (p mod q) + q * (p div q)
// q = 0 or (p mod q) >= 0 // q = 0 or (p mod q) >= 0
// q = 0 or (p mod q) < abs(q) // q >= 0 or (p mod q) + q <= -1
// q >= 0 or (p mod q) = (p mod -q) // q <= 0 or (p mod q) - q <= -1
// (p mod q) = (p mod -q)
mk_axiom(eqz, eq); mk_axiom(eqz, eq);
mk_axiom(eqz, mod_ge_0); mk_axiom(eqz, mod_ge_0);
mk_axiom(eqz, mod_lt_q); mk_axiom(mk_literal(a.mk_le(q, zero)), mk_literal(a.mk_le(a.mk_add(mod, a.mk_mul(mone, q)), mone)));
if (!a.is_uminus(q)) mk_axiom(mk_literal(a.mk_ge(q, zero)), mk_literal(a.mk_le(a.mk_add(mod, q), mone)));
mk_axiom(mk_literal(m.mk_eq(mod, a.mk_mod(p, a.mk_uminus(q))))); expr* x = nullptr, * y = nullptr;
if (false && !(a.is_mul(q, x, y) && mone == x))
mk_axiom(mk_literal(m.mk_eq(mod, a.mk_mod(p, a.mk_mul(mone, q)))));
m_arith_eq_adapter.mk_axioms(th.ensure_enode(mod_r), th.ensure_enode(p)); m_arith_eq_adapter.mk_axioms(th.ensure_enode(mod_r), th.ensure_enode(p));
@ -1658,6 +1660,9 @@ public:
return FC_CONTINUE; return FC_CONTINUE;
} }
if (st == FC_GIVEUP)
IF_VERBOSE(0, display(verbose_stream()));
if (!int_undef && !check_bv_terms()) if (!int_undef && !check_bv_terms())
return FC_CONTINUE; return FC_CONTINUE;