mirror of
https://github.com/Z3Prover/z3
synced 2025-07-20 03:12:03 +00:00
First pass at free variable elimination
This commit is contained in:
parent
4cdd3bf77d
commit
29695391de
5 changed files with 144 additions and 21 deletions
|
@ -62,11 +62,13 @@ namespace polysat {
|
||||||
class conflict_resolver {
|
class conflict_resolver {
|
||||||
inf_saturate m_saturate;
|
inf_saturate m_saturate;
|
||||||
ex_polynomial_superposition m_poly_sup;
|
ex_polynomial_superposition m_poly_sup;
|
||||||
|
free_variable_elimination m_free_variable_elimination;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
conflict_resolver(solver& s)
|
conflict_resolver(solver& s)
|
||||||
: m_saturate(s)
|
: m_saturate(s)
|
||||||
, m_poly_sup(s)
|
, m_poly_sup(s)
|
||||||
|
, m_free_variable_elimination(s)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool try_resolve_value(pvar v, conflict& core) {
|
bool try_resolve_value(pvar v, conflict& core) {
|
||||||
|
@ -76,6 +78,11 @@ namespace polysat {
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Analyse current conflict core to extract additional lemmas
|
||||||
|
void find_extra_lemmas(conflict& core) {
|
||||||
|
m_free_variable_elimination.find_lemma(core);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class header_with_var : public displayable {
|
class header_with_var : public displayable {
|
||||||
|
@ -524,6 +531,10 @@ namespace polysat {
|
||||||
clause_ref conflict::build_lemma() {
|
clause_ref conflict::build_lemma() {
|
||||||
LOG_H3("Build lemma from core");
|
LOG_H3("Build lemma from core");
|
||||||
LOG("core: " << *this);
|
LOG("core: " << *this);
|
||||||
|
|
||||||
|
// TODO: do this after each step?
|
||||||
|
m_resolver->find_extra_lemmas(*this);
|
||||||
|
|
||||||
clause_builder lemma(s);
|
clause_builder lemma(s);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
|
@ -258,6 +258,7 @@ namespace polysat {
|
||||||
|
|
||||||
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(s, us, dep, is_positive()); }
|
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(s, us, dep, is_positive()); }
|
||||||
|
|
||||||
|
unsigned_vector const& vars() const { return m_constraint->vars(); }
|
||||||
unsigned var(unsigned idx) const { return m_constraint->var(idx); }
|
unsigned var(unsigned idx) const { return m_constraint->var(idx); }
|
||||||
bool contains_var(pvar v) const { return m_constraint->contains_var(v); }
|
bool contains_var(pvar v) const { return m_constraint->contains_var(v); }
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ namespace polysat {
|
||||||
friend class assignment_pp;
|
friend class assignment_pp;
|
||||||
friend class assignments_pp;
|
friend class assignments_pp;
|
||||||
friend class ex_polynomial_superposition;
|
friend class ex_polynomial_superposition;
|
||||||
|
friend class free_variable_elimination;
|
||||||
friend class inf_saturate;
|
friend class inf_saturate;
|
||||||
friend class constraint_manager;
|
friend class constraint_manager;
|
||||||
friend class scoped_solverv;
|
friend class scoped_solverv;
|
||||||
|
|
|
@ -8,26 +8,134 @@ Module Name:
|
||||||
Author:
|
Author:
|
||||||
|
|
||||||
Nikolaj Bjorner (nbjorner) 2021-03-19
|
Nikolaj Bjorner (nbjorner) 2021-03-19
|
||||||
Jakob Rath 2021-04-6
|
Jakob Rath 2021-04-06
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
#include "math/polysat/variable_elimination.h"
|
#include "math/polysat/variable_elimination.h"
|
||||||
|
#include "math/polysat/conflict.h"
|
||||||
|
#include "math/polysat/clause_builder.h"
|
||||||
#include "math/polysat/solver.h"
|
#include "math/polysat/solver.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
bool ve_reduction::perform(solver& s, pvar v, conflict& core) {
|
void free_variable_elimination::find_lemma(conflict& core) {
|
||||||
// without any further hints, we just do core reduction with the stronger premise "C contains a c' that evaluates to false",
|
LOG_H1("Free Variable Elimination");
|
||||||
// and kick out all other constraints.
|
LOG("core: " << core);
|
||||||
|
LOG("Free variables: " << s.m_free_pvars);
|
||||||
|
for (pvar v : core.vars_occurring_in_constraints())
|
||||||
|
if (!s.is_assigned(v)) // TODO: too restrictive. should also consider variables that will be unassigned only after backjumping (can update this after assignment handling in search state is refactored.)
|
||||||
|
find_lemma(v, core);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_variable_elimination::find_lemma(pvar v, conflict& core) {
|
||||||
|
LOG_H2("Free Variable Elimination for v" << v);
|
||||||
|
// find constraint that allows computing v from other variables
|
||||||
|
// (currently, consider only equations that contain v with degree 1)
|
||||||
for (signed_constraint c : core) {
|
for (signed_constraint c : core) {
|
||||||
if (!c->contains_var(v) && c.is_currently_false(s)) {
|
if (!c.is_eq())
|
||||||
core.set(c);
|
continue;
|
||||||
core.logger().log("ve_reduction");
|
if (c.eq().degree(v) != 1)
|
||||||
return true;
|
continue;
|
||||||
}
|
find_lemma(v, c, core);
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
void free_variable_elimination::find_lemma(pvar v, signed_constraint c, conflict& core) {
|
||||||
|
LOG_H3("Free Variable Elimination for v" << v << " using equation " << c);
|
||||||
|
pdd const& p = c.eq();
|
||||||
|
SASSERT_EQ(p.degree(v), 1);
|
||||||
|
auto& m = p.manager();
|
||||||
|
pdd lc = m.zero();
|
||||||
|
pdd rest = m.zero();
|
||||||
|
p.factor(v, 1, lc, rest);
|
||||||
|
if (rest.is_val())
|
||||||
|
return;
|
||||||
|
// lc * v + rest == p == 0
|
||||||
|
// v == -1 * rest * lc^-1
|
||||||
|
|
||||||
|
SASSERT(!lc.free_vars().contains(v));
|
||||||
|
SASSERT(!rest.free_vars().contains(v));
|
||||||
|
|
||||||
|
LOG("lc: " << lc);
|
||||||
|
LOG("rest: " << rest);
|
||||||
|
|
||||||
|
assignment_t a;
|
||||||
|
pdd const lcs = eval(lc, core, a);
|
||||||
|
LOG("lcs: " << lcs);
|
||||||
|
pdd lci = m.zero();
|
||||||
|
if (!inv(lcs, lci))
|
||||||
|
return;
|
||||||
|
|
||||||
|
pdd const rs = s.subst(a, rest);
|
||||||
|
pdd const vs = -rs * lci; // this is the polynomial that computes v
|
||||||
|
LOG("vs: " << vs);
|
||||||
|
SASSERT(!vs.free_vars().contains(v));
|
||||||
|
|
||||||
|
// Find another constraint where we want to substitute v
|
||||||
|
for (signed_constraint c_target : core) {
|
||||||
|
if (c == c_target)
|
||||||
|
continue;
|
||||||
|
if (c_target.vars().size() <= 1)
|
||||||
|
continue;
|
||||||
|
if (!c_target.contains_var(v))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO: helper method constraint::subst(pvar v, pdd const& p)
|
||||||
|
// (or rather, add it on constraint_manager since we need to allocate/dedup the new constraint)
|
||||||
|
// For now, just restrict to ule_constraint.
|
||||||
|
if (!c_target->is_ule())
|
||||||
|
continue;
|
||||||
|
// TODO: maybe apply assignment a here as well
|
||||||
|
pdd const lhs = c_target->to_ule().lhs().subst_pdd(v, vs);
|
||||||
|
pdd const rhs = c_target->to_ule().rhs().subst_pdd(v, vs);
|
||||||
|
signed_constraint c_new = s.ule(lhs, rhs);
|
||||||
|
if (c_target.is_negative())
|
||||||
|
c_new.negate();
|
||||||
|
LOG("c_target: " << lit_pp(s, c_target));
|
||||||
|
LOG("c_new: " << lit_pp(s, c_new));
|
||||||
|
|
||||||
|
clause_builder cb(s);
|
||||||
|
for (auto [w, wv] : a)
|
||||||
|
cb.push(~s.eq(s.var(w), wv));
|
||||||
|
cb.push(~c);
|
||||||
|
cb.push(~c_target);
|
||||||
|
cb.push(c_new);
|
||||||
|
core.add_lemma(cb.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate p under assignments in the core.
|
||||||
|
pdd free_variable_elimination::eval(pdd const& p, conflict& core, assignment_t& out_assignment) {
|
||||||
|
// TODO: this should probably be a helper method on conflict.
|
||||||
|
// TODO: recognize constraints of the form "v1 == 27" to be used in the assignment?
|
||||||
|
// (but maybe useful evaluations are always part of core.vars() anyway?)
|
||||||
|
|
||||||
|
assignment_t& a = out_assignment;
|
||||||
|
SASSERT(a.empty());
|
||||||
|
|
||||||
|
for (auto v : p.free_vars())
|
||||||
|
if (core.contains_pvar(v))
|
||||||
|
a.push_back({v, s.get_value(v)});
|
||||||
|
|
||||||
|
pdd q = s.subst(a, p);
|
||||||
|
|
||||||
|
// TODO: like in the old conflict::minimize_vars, we can now try to remove unnecessary variables from a.
|
||||||
|
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the multiplicative inverse of p.
|
||||||
|
bool free_variable_elimination::inv(pdd const& p, pdd& out_p_inv) {
|
||||||
|
// TODO: in the non-val case, we could introduce an additional variable to represent the inverse
|
||||||
|
// (and a constraint p * p_inv == 1)
|
||||||
|
if (!p.is_val())
|
||||||
|
return false;
|
||||||
|
rational iv;
|
||||||
|
if (!p.val().mult_inverse(p.power_of_2(), iv))
|
||||||
|
return false;
|
||||||
|
out_p_inv = p.manager().mk_val(iv);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,25 +8,27 @@ Module Name:
|
||||||
Author:
|
Author:
|
||||||
|
|
||||||
Nikolaj Bjorner (nbjorner) 2021-03-19
|
Nikolaj Bjorner (nbjorner) 2021-03-19
|
||||||
Jakob Rath 2021-04-6
|
Jakob Rath 2021-04-06
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "math/polysat/conflict.h"
|
|
||||||
|
#include "math/polysat/types.h"
|
||||||
|
#include "math/polysat/constraint.h"
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
class solver;
|
class conflict;
|
||||||
|
|
||||||
class variable_elimination_engine {
|
class free_variable_elimination {
|
||||||
|
solver& s;
|
||||||
|
void find_lemma(pvar v, conflict& core);
|
||||||
|
void find_lemma(pvar v, signed_constraint c, conflict& core);
|
||||||
|
pdd eval(pdd const& p, conflict& core, assignment_t& out_assignment);
|
||||||
|
bool inv(pdd const& p, pdd& out_p_inv);
|
||||||
public:
|
public:
|
||||||
virtual ~variable_elimination_engine() {}
|
free_variable_elimination(solver& s): s(s) {}
|
||||||
virtual bool perform(solver& s, pvar v, conflict& core) = 0;
|
void find_lemma(conflict& core);
|
||||||
};
|
|
||||||
|
|
||||||
class ve_reduction : public variable_elimination_engine {
|
|
||||||
public:
|
|
||||||
bool perform(solver& s, pvar v, conflict& core) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue