3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-19 20:33:38 +00:00

add propagators to grobner

This commit is contained in:
Nikolaj Bjorner 2022-07-14 15:45:00 -07:00
parent af80bd18ce
commit 7c177584f3
4 changed files with 184 additions and 73 deletions

View file

@ -169,7 +169,7 @@ namespace dd {
/* /*
Use the given equation to simplify equations in set Use the given equation to simplify equations in set
*/ */
void solver::simplify_using(equation_vector& set, equation const& eq) { void solver::simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier) {
struct scoped_update { struct scoped_update {
equation_vector& set; equation_vector& set;
unsigned i, j, sz; unsigned i, j, sz;
@ -191,7 +191,7 @@ namespace dd {
equation& target = *set[sr.i]; equation& target = *set[sr.i];
bool changed_leading_term = false; bool changed_leading_term = false;
bool simplified = true; bool simplified = true;
simplified = !done() && try_simplify_using(target, eq, changed_leading_term); simplified = !done() && simplifier(target, changed_leading_term);
if (simplified && is_trivial(target)) { if (simplified && is_trivial(target)) {
retire(&target); retire(&target);
@ -210,6 +210,13 @@ namespace dd {
sr.nextj(); sr.nextj();
} }
} }
}
void solver::simplify_using(equation_vector& set, equation const& eq) {
std::function<bool(equation&, bool&)> simplifier = [&](equation& target, bool& changed_leading_term) {
return try_simplify_using(target, eq, changed_leading_term);
};
simplify_using(set, simplifier);
} }
/* /*
@ -353,7 +360,8 @@ namespace dd {
} }
void solver::add(pdd const& p, u_dependency * dep) { void solver::add(pdd const& p, u_dependency * dep) {
if (p.is_zero()) return; if (p.is_zero())
return;
equation * eq = alloc(equation, p, dep); equation * eq = alloc(equation, p, dep);
if (check_conflict(*eq)) if (check_conflict(*eq))
return; return;
@ -365,18 +373,30 @@ namespace dd {
} }
void solver::add_subst(unsigned v, pdd const& p, u_dependency* dep) { void solver::add_subst(unsigned v, pdd const& p, u_dependency* dep) {
SASSERT(m_processed.empty());
SASSERT(m_solved.empty());
m_subst.push_back({v, p, dep}); m_subst.push_back({v, p, dep});
if (!m_var2level.empty())
m_levelp1 = std::max(m_var2level[v]+1, std::max(m_var2level[p.var()]+1, m_levelp1));
for (auto* e : m_to_simplify) { std::function<bool(equation&, bool&)> simplifier = [&](equation& dst, bool& changed_leading_term) {
auto r = e->poly().subst_pdd(v, p); auto r = dst.poly().subst_pdd(v, p);
if (r == e->poly()) if (r == dst.poly())
continue; return false;
*e = m_dep_manager.mk_join(dep, e->dep()); if (is_too_complex(r)) {
*e = r; m_too_complex = true;
} return false;
}
changed_leading_term = m.different_leading_term(r, dst.poly());
dst = r;
dst = m_dep_manager.mk_join(dst.dep(), dep);
update_stats_max_degree_and_size(dst);
return true;
};
if (!done())
simplify_using(m_processed, simplifier);
if (!done())
simplify_using(m_to_simplify, simplifier);
if (!done())
simplify_using(m_solved, simplifier);
} }
void solver::simplify(pdd& p, u_dependency*& d) { void solver::simplify(pdd& p, u_dependency*& d) {

View file

@ -164,6 +164,7 @@ private:
void simplify_using(equation& eq, equation_vector const& eqs); void simplify_using(equation& eq, equation_vector const& eqs);
void simplify_using(equation_vector& set, equation const& eq); void simplify_using(equation_vector& set, equation const& eq);
void simplify_using(equation & dst, equation const& src, bool& changed_leading_term); void simplify_using(equation & dst, equation const& src, bool& changed_leading_term);
void simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier);
bool try_simplify_using(equation& target, equation const& source, bool& changed_leading_term); bool try_simplify_using(equation& target, equation const& source, bool& changed_leading_term);
bool is_trivial(equation const& eq) const { return eq.poly().is_zero(); } bool is_trivial(equation const& eq) const { return eq.poly().is_zero(); }

View file

@ -42,28 +42,43 @@ namespace nla {
configure(); configure();
m_solver.saturate(); m_solver.saturate();
if (find_conflict()) if (is_conflicting())
return; return;
#if 0
if (propagate_bounds()) if (propagate_bounds())
return; return;
if (propagate_eqs()) if (propagate_eqs())
return; return;
if (propagate_factorization())
return;
#endif
if (quota > 1) if (quota > 1)
quota--; quota--;
IF_VERBOSE(2, verbose_stream() << "grobner miss, quota " << quota << "\n"); IF_VERBOSE(2, verbose_stream() << "grobner miss, quota " << quota << "\n");
IF_VERBOSE(4, diagnose_pdd_miss(verbose_stream())); IF_VERBOSE(4, diagnose_pdd_miss(verbose_stream()));
#if 0
// diagnostics: did we miss something
vector<dd::pdd> eqs;
for (auto eq : m_solver.equations())
eqs.push_back(eq->poly());
c().m_nra.check(eqs);
#endif
} }
bool grobner::find_conflict() { bool grobner::is_conflicting() {
unsigned conflicts = 0; unsigned conflicts = 0;
for (auto eq : m_solver.equations()) { for (auto eq : m_solver.equations())
if (check_pdd_eq(eq) && ++conflicts >= m_solver.number_of_conflicts_to_report()) if (is_conflicting(*eq) && ++conflicts >= m_solver.number_of_conflicts_to_report())
break; break;
}
if (conflicts > 0)
lp_settings().stats().m_grobner_conflicts++;
TRACE("grobner", m_solver.display(tout)); TRACE("grobner", m_solver.display(tout));
IF_VERBOSE(2, if (conflicts > 0) verbose_stream() << "grobner conflict\n"); IF_VERBOSE(2, if (conflicts > 0) verbose_stream() << "grobner conflict\n");
@ -71,39 +86,100 @@ namespace nla {
} }
bool grobner::propagate_bounds() { bool grobner::propagate_bounds() {
for (auto eq : m_solver.equations()) { unsigned bounds = 0;
for (auto eq : m_solver.equations())
if (propagate_bounds(*eq) && ++bounds >= m_solver.number_of_conflicts_to_report())
return true;
return bounds > 0;
}
bool grobner::propagate_eqs() {
unsigned fixed = 0;
for (auto eq : m_solver.equations())
if (propagate_fixed(*eq) && ++fixed >= m_solver.number_of_conflicts_to_report())
return true;
return fixed > 0;
}
bool grobner::propagate_factorization() {
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_factorization(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
/**
\brief detect equalities
- k*x = 0, that is x = 0
- ax + b = 0
*/
typedef lp::lar_term term;
bool grobner::propagate_fixed(const dd::solver::equation& eq) {
dd::pdd const& p = eq.poly();
//IF_VERBOSE(0, verbose_stream() << p << "\n");
if (p.is_unary()) {
unsigned v = p.var();
if (c().var_is_fixed(v))
return false;
new_lemma lemma(c(), "pdd-eq");
add_dependencies(lemma, eq);
lemma |= ineq(v, llc::EQ, rational::zero());
return true;
}
if (p.is_offset()) {
unsigned v = p.var();
if (c().var_is_fixed(v))
return false;
rational a = p.hi().val();
rational b = -p.lo().val();
rational d = lcm(denominator(a), denominator(b));
a *= d;
b *= d;
new_lemma lemma(c(), "pdd-eq");
add_dependencies(lemma, eq);
lemma |= ineq(term(a, v), llc::EQ, b);
return true;
}
return false;
}
/**
\brief detect simple factors
x*q = 0 => x = 0 or q = 0
*/
bool grobner::propagate_factorization(const dd::solver::equation& eq) {
dd::pdd const& p = eq.poly();
if (!p.is_val() && p.lo().is_zero() && !p.hi().is_val() && p.hi().is_linear()) {
//IF_VERBOSE(0, verbose_stream() << "factored " << p << "\n");
unsigned v = p.var();
auto q = p.hi();
new_lemma lemma(c(), "pdd-factored");
add_dependencies(lemma, eq);
term t;
while (!q.is_val()) {
t.add_monomial(q.hi().val(), q.var());
q = q.lo();
}
lemma |= ineq(v, llc::EQ, rational::zero());
lemma |= ineq(t, llc::EQ, -q.val());
//lemma.display(verbose_stream());
return true;
} }
return false; return false;
} }
bool grobner::propagate_eqs() {
#if 0 void grobner::add_dependencies(new_lemma& lemma, const dd::solver::equation& eq) {
bool propagated = false; lp::explanation ex;
for (auto eq : m_solver.equations()) { u_dependency_manager dm;
auto const& p = eq->poly(); vector<unsigned, false> lv;
if (p.is_offset()) { dm.linearize(eq.dep(), lv);
lpvar v = p.var(); for (unsigned ci : lv)
if (m_lar_solver.column_has_lower_bound(v) && ex.push_back(ci);
m_lar_solver.column_has_upper_bound(v)) lemma &= ex;
continue;
rational fixed_val = -p.lo().val();
lp::explanation ex;
u_dependency_manager dm;
vector<unsigned, false> lv;
dm.linearize(eq->dep(), lv);
for (unsigned ci : lv)
ex.push_back(ci);
new_lemma lemma(*this, "pdd-eq");
lemma &= ex;
lemma |= ineq(v, llc::EQ, fixed_val);
propagated = true;
}
}
if (propagated)
return;
#endif
return false;
} }
void grobner::configure() { void grobner::configure() {
@ -178,18 +254,10 @@ namespace nla {
out << "]\n"; out << "]\n";
} }
} }
#if 0
// diagnostics: did we miss something
vector<dd::pdd> eqs;
for (auto eq : m_solver.equations())
eqs.push_back(eq->poly());
m_nra.check(eqs);
#endif
return out; return out;
} }
bool grobner::check_pdd_eq(const dd::solver::equation* e) { bool grobner::is_conflicting(const dd::solver::equation& e) {
auto& di = c().m_intervals.get_dep_intervals(); auto& di = c().m_intervals.get_dep_intervals();
dd::pdd_interval eval(di); dd::pdd_interval eval(di);
eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) { eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) {
@ -197,12 +265,12 @@ namespace nla {
else c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a); else c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
}; };
scoped_dep_interval i(di), i_wd(di); scoped_dep_interval i(di), i_wd(di);
eval.get_interval<dd::w_dep::without_deps>(e->poly(), i); eval.get_interval<dd::w_dep::without_deps>(e.poly(), i);
if (!di.separated_from_zero(i)) { if (!di.separated_from_zero(i)) {
TRACE("grobner", m_solver.display(tout << "not separated from 0 ", *e) << "\n"; TRACE("grobner", m_solver.display(tout << "not separated from 0 ", e) << "\n";
eval.get_interval_distributed<dd::w_dep::without_deps>(e->poly(), i); eval.get_interval_distributed<dd::w_dep::without_deps>(e.poly(), i);
tout << "separated from 0: " << di.separated_from_zero(i) << "\n"; tout << "separated from 0: " << di.separated_from_zero(i) << "\n";
for (auto j : e->poly().free_vars()) { for (auto j : e.poly().free_vars()) {
scoped_dep_interval a(di); scoped_dep_interval a(di);
c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a); c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
c().m_intervals.display(tout << "j" << j << " ", a); tout << " "; c().m_intervals.display(tout << "j" << j << " ", a); tout << " ";
@ -211,22 +279,35 @@ namespace nla {
return false; return false;
} }
eval.get_interval<dd::w_dep::with_deps>(e->poly(), i_wd); eval.get_interval<dd::w_dep::with_deps>(e.poly(), i_wd);
std::function<void (const lp::explanation&)> f = [this](const lp::explanation& e) { std::function<void (const lp::explanation&)> f = [this](const lp::explanation& e) {
new_lemma lemma(m_core, "pdd"); new_lemma lemma(m_core, "pdd");
lemma &= e; lemma &= e;
}; };
if (di.check_interval_for_conflict_on_zero(i_wd, e->dep(), f)) { if (di.check_interval_for_conflict_on_zero(i_wd, e.dep(), f)) {
TRACE("grobner", m_solver.display(tout << "conflict ", *e) << "\n"); TRACE("grobner", m_solver.display(tout << "conflict ", e) << "\n");
lp_settings().stats().m_grobner_conflicts++;
return true; return true;
} }
else { else {
TRACE("grobner", m_solver.display(tout << "no conflict ", *e) << "\n"); TRACE("grobner", m_solver.display(tout << "no conflict ", e) << "\n");
return false; return false;
} }
} }
bool grobner::propagate_bounds(const dd::solver::equation& e) {
return false;
// TODO
auto& di = c().m_intervals.get_dep_intervals();
dd::pdd_interval eval(di);
eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) {
if (deps) c().m_intervals.set_var_interval<dd::w_dep::with_deps>(j, a);
else c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
};
scoped_dep_interval i(di), i_wd(di);
eval.get_interval<dd::w_dep::without_deps>(e.poly(), i);
return false;
}
void grobner::add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar> & q) { void grobner::add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar> & q) {
if (c().active_var_set_contains(j)) if (c().active_var_set_contains(j))
return; return;

View file

@ -27,23 +27,32 @@ namespace nla {
lp::lp_settings& lp_settings(); lp::lp_settings& lp_settings();
// solving // solving
bool find_conflict(); bool is_conflicting();
bool is_conflicting(const dd::solver::equation& eq);
bool propagate_bounds(); bool propagate_bounds();
bool propagate_bounds(const dd::solver::equation& eq);
bool propagate_eqs(); bool propagate_eqs();
bool propagate_fixed(const dd::solver::equation& eq);
bool propagate_factorization();
bool propagate_factorization(const dd::solver::equation& eq);
void add_dependencies(new_lemma& lemma, const dd::solver::equation& eq);
// setup
void configure();
void set_level2var();
void find_nl_cluster(); void find_nl_cluster();
void prepare_rows_and_active_vars(); void prepare_rows_and_active_vars();
void add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar>& q); void add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar>& q);
void add_row(const vector<lp::row_cell<rational>>& row); void add_row(const vector<lp::row_cell<rational>>& row);
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 check_pdd_eq(const dd::solver::equation*);
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*&); dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*& dep);
void set_level2var();
void configure();
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);