3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-09-30 13:19:04 +00:00

filter pseudo-linear monomials

This commit is contained in:
Nikolaj Bjorner 2025-09-03 17:51:01 -07:00
parent 449704ef64
commit e0c315bc3e
3 changed files with 77 additions and 2 deletions

View file

@ -32,6 +32,7 @@ namespace nla {
smt_params_helper ph(p); smt_params_helper ph(p);
m_config.m_propagate_quotients = ph.arith_nl_grobner_propagate_quotients(); m_config.m_propagate_quotients = ph.arith_nl_grobner_propagate_quotients();
m_config.m_gcd_test = ph.arith_nl_grobner_gcd_test(); m_config.m_gcd_test = ph.arith_nl_grobner_gcd_test();
m_config.m_expand_terms = ph.arith_nl_grobner_expand_terms();
} }
lp::lp_settings& grobner::lp_settings() { lp::lp_settings& grobner::lp_settings() {
@ -63,6 +64,7 @@ namespace nla {
if (!configure()) if (!configure())
return; return;
m_solver.saturate(); m_solver.saturate();
TRACE(grobner, m_solver.display(tout));
if (m_delay_base > 0) if (m_delay_base > 0)
--m_delay_base; --m_delay_base;
@ -359,6 +361,11 @@ namespace nla {
auto v_value = val(v); auto v_value = val(v);
auto r_value = eval(r); auto r_value = eval(r);
auto lc_value = eval(lc); auto lc_value = eval(lc);
TRACE(grobner, tout << "nl-var: " << v << " " << v_value << "\n"
<< "lc: " << lc << " " << lc_value << "\n"
<< "r: " << r << " " << r_value << "\n";
);
SASSERT(v_value.is_int()); SASSERT(v_value.is_int());
SASSERT(r_value.is_int()); SASSERT(r_value.is_int());
SASSERT(lc_value.is_int()); SASSERT(lc_value.is_int());
@ -447,6 +454,44 @@ namespace nla {
found_lemma = true; found_lemma = true;
continue; continue;
} }
if (abs(lc_value) > 1 && abs(v_value) > 1 && abs(v_value) >= abs(r_value)) {
// v*c + t = 0 & |c| > 1, |v| > 1 => |v| < |t|
lemma_builder lemma(c(), "grobner-bounds");
auto [tr, offset_t] = linear_to_term(r);
auto [tc, offset_c] = linear_to_term(lc);
add_dependencies(lemma, eq);
if (lc_value > 1) // c <= 1
lemma |= ineq(tc, llc::LE, rational(1) - offset_c); // lc <= 1
else // c >= -1
lemma |= ineq(tc, llc::GE, rational(-1) - offset_c); // lc >= -1
if (v_value > 1)
lemma |= ineq(v, llc::LE, rational(1)); // v <= 1
else
lemma |= ineq(v, llc::GE, rational(-1)); // v >= -1
if (v_value > 0 && r_value > 0) {
// t >= v + 1
tr.add_monomial(rational(-1), v);
lemma |= ineq(tr, llc::GE, rational(1) - offset_t);
}
else if (v_value > 0 && r_value < 0) {
// t + v <= -1
tr.add_monomial(rational(1), v);
lemma |= ineq(tr, llc::LE, rational(-1) - offset_t);
}
else if (v_value < 0 && r_value > 0) {
// t + v >= 1
tr.add_monomial(rational(1), v);
lemma |= ineq(tr, llc::GE, rational(1) - offset_t);
}
else if (v_value < 0 && r_value < 0) {
// t - v <= -1
tr.add_monomial(rational(-1), v);
lemma |= ineq(tr, llc::LE, rational(-1) - offset_t);
}
found_lemma = true;
TRACE(grobner, lemma.display(tout << "quotient5\n"));
continue;
}
// other division lemmas are possible. // other division lemmas are possible.
// also extend to non-linear r, non-linear lc // also extend to non-linear r, non-linear lc
} }
@ -693,6 +738,13 @@ namespace nla {
return lra.column_lower_bound(j).x; return lra.column_lower_bound(j).x;
} }
dd::pdd grobner::pdd_expr(lp::lar_term const& t, u_dependency*& dep) {
dd::pdd r = m_pdd_manager.mk_val(0);
for (auto const& ti : t)
r += pdd_expr(ti.coeff(), ti.j(), dep);
return r;
}
dd::pdd grobner::pdd_expr(const rational& coeff, lpvar j, u_dependency*& dep) { dd::pdd grobner::pdd_expr(const rational& coeff, lpvar j, u_dependency*& dep) {
dd::pdd r = m_pdd_manager.mk_val(coeff); dd::pdd r = m_pdd_manager.mk_val(coeff);
sbuffer<lpvar> vars; sbuffer<lpvar> vars;
@ -709,6 +761,8 @@ namespace nla {
} }
if (c().params().arith_nl_grobner_subs_fixed() == 1 && c().var_is_fixed(j)) if (c().params().arith_nl_grobner_subs_fixed() == 1 && c().var_is_fixed(j))
r *= val_of_fixed_var_with_deps(j, dep); r *= val_of_fixed_var_with_deps(j, dep);
else if (m_config.m_expand_terms && c().lra.column_has_term(j))
r *= pdd_expr(c().lra.get_term(j), dep);
else if (!c().is_monic_var(j)) else if (!c().is_monic_var(j))
r *= m_pdd_manager.mk_var(j); r *= m_pdd_manager.mk_var(j);
else else
@ -787,6 +841,22 @@ namespace nla {
m_solver.add(p, dep); m_solver.add(p, dep);
} }
bool grobner::is_pseudo_linear(unsigned_vector const& vars) const {
bool has_unbounded = false;
for (auto v : vars) {
if (c().lra.column_is_bounded(v) && c().lra.var_is_int(v)) {
auto lb = c().lra.get_lower_bound(v);
auto ub = c().lra.get_upper_bound(v);
if (ub - lb <= rational(4))
continue;
}
if (has_unbounded)
return false;
has_unbounded = true;
}
return true;
}
void grobner::add_fixed_monic(unsigned j) { void grobner::add_fixed_monic(unsigned j) {
u_dependency* dep = nullptr; u_dependency* dep = nullptr;
dd::pdd r = m_pdd_manager.mk_val(rational(1)); dd::pdd r = m_pdd_manager.mk_val(rational(1));
@ -810,9 +880,10 @@ namespace nla {
prepare_rows_and_active_vars(); prepare_rows_and_active_vars();
svector<lpvar> q; svector<lpvar> q;
TRACE(grobner, for (lpvar j : c().m_to_refine) print_monic(c().emons()[j], tout) << "\n";); TRACE(grobner, for (lpvar j : c().m_to_refine) print_monic(c().emons()[j], tout) << "\n";);
for (lpvar j : c().m_to_refine) for (lpvar j : c().m_to_refine)
q.push_back(j); if (!is_pseudo_linear(c().emons()[j].vars()))
q.push_back(j);
while (!q.empty()) { while (!q.empty()) {
lpvar j = q.back(); lpvar j = q.back();

View file

@ -22,6 +22,7 @@ namespace nla {
struct config { struct config {
bool m_propagate_quotients = false; bool m_propagate_quotients = false;
bool m_gcd_test = false; bool m_gcd_test = false;
bool m_expand_terms = false;
}; };
dd::pdd_manager m_pdd_manager; dd::pdd_manager m_pdd_manager;
dd::solver m_solver; dd::solver m_solver;
@ -78,8 +79,10 @@ namespace nla {
void add_fixed_monic(unsigned j); void add_fixed_monic(unsigned j);
bool is_solved(dd::pdd const& p, unsigned& v, dd::pdd& r); bool is_solved(dd::pdd const& p, unsigned& v, dd::pdd& r);
void add_eq(dd::pdd& p, u_dependency* dep); void add_eq(dd::pdd& p, u_dependency* dep);
bool is_pseudo_linear(unsigned_vector const& vars) const;
const rational& val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep); const rational& val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*& dep); dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(lp::lar_term const& t, u_dependency*& dep);
void display_matrix_of_m_rows(std::ostream& out) const; void display_matrix_of_m_rows(std::ostream& out) const;
std::ostream& diagnose_pdd_miss(std::ostream& out); std::ostream& diagnose_pdd_miss(std::ostream& out);

View file

@ -82,6 +82,7 @@ def_module_params(module_name='smt',
('arith.nl.grobner_gcd_test', BOOL, False, 'detect gcd conflicts for polynomial powers x^k - y = 0'), ('arith.nl.grobner_gcd_test', BOOL, False, 'detect gcd conflicts for polynomial powers x^k - y = 0'),
('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.grobner_expand_terms', BOOL, False, 'expand terms before computing grobner basis'),
('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'),
('arith.nl.propagate_linear_monomials', BOOL, True, 'propagate linear monomials'), ('arith.nl.propagate_linear_monomials', BOOL, True, 'propagate linear monomials'),
('arith.nl.optimize_bounds', BOOL, True, 'enable bounds optimization'), ('arith.nl.optimize_bounds', BOOL, True, 'enable bounds optimization'),