mirror of
https://github.com/Z3Prover/z3
synced 2025-04-30 20:35:51 +00:00
Polysat: fixes in solver, forbidden intervals for eq_constraint (#5240)
* Rename to neg_cond * Add some logging utilities * Implement case of forbidden interval covering the whole domain * Implement diseq_narrow * Do not activate constraint if we are in a conflict state * comments * Assert that lemma isn't undefined * Update revert_decision to work in the case where narrowing causes propagation * Fix case of non-disjunctive lemma from forbidden intervals * Conflict should not leak outside user scope * Add guard to decide(), some notes * Add test case * Add constraints to watchlist of unassigned variable during propagation * Move common propagation functionality into base class * Combine eq/diseq narrow * Compute forbidden interval for equality constraints by considering them as p <=u 0 (or p >u 0 for disequalities)
This commit is contained in:
parent
04876ba8b7
commit
f7e476a4a0
15 changed files with 350 additions and 130 deletions
|
@ -22,26 +22,6 @@ namespace polysat {
|
|||
return out << p() << (sign() == pos_t ? " == 0" : " != 0") << " [" << m_status << "]";
|
||||
}
|
||||
|
||||
bool eq_constraint::propagate(solver& s, pvar v) {
|
||||
LOG_H3("Propagate " << s.m_vars[v] << " in " << *this);
|
||||
SASSERT(!vars().empty());
|
||||
unsigned idx = 0;
|
||||
if (vars()[idx] != v)
|
||||
idx = 1;
|
||||
SASSERT(v == vars()[idx]);
|
||||
// find other watch variable.
|
||||
for (unsigned i = vars().size(); i-- > 2; ) {
|
||||
if (!s.is_assigned(vars()[i])) {
|
||||
std::swap(vars()[idx], vars()[i]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// at most one variable remains unassigned.
|
||||
|
||||
narrow(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
constraint* eq_constraint::resolve(solver& s, pvar v) {
|
||||
if (is_positive())
|
||||
return eq_resolve(s, v);
|
||||
|
@ -52,10 +32,50 @@ namespace polysat {
|
|||
}
|
||||
|
||||
void eq_constraint::narrow(solver& s) {
|
||||
if (is_positive())
|
||||
eq_narrow(s);
|
||||
if (is_negative())
|
||||
diseq_narrow(s);
|
||||
SASSERT(!is_undef());
|
||||
LOG("Assignment: " << s.m_search);
|
||||
auto q = p().subst_val(s.m_search);
|
||||
LOG("Substituted: " << p() << " := " << q);
|
||||
if (q.is_zero()) {
|
||||
if (is_positive())
|
||||
return;
|
||||
if (is_negative()) {
|
||||
LOG("Conflict (zero under current assignment)");
|
||||
s.set_conflict(*this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (q.is_never_zero()) {
|
||||
if (is_positive()) {
|
||||
LOG("Conflict (never zero under current assignment)");
|
||||
s.set_conflict(*this);
|
||||
return;
|
||||
}
|
||||
if (is_negative())
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.is_unilinear()) {
|
||||
// a*x + b == 0
|
||||
pvar v = q.var();
|
||||
rational a = q.hi().val();
|
||||
rational b = q.lo().val();
|
||||
bddv const& x = s.var2bits(v).var();
|
||||
bddv lhs = a * x + b;
|
||||
rational zero = rational::zero();
|
||||
bdd xs = is_positive() ? (lhs == zero) : (lhs != zero);
|
||||
s.push_cjust(v, this);
|
||||
s.intersect_viable(v, xs);
|
||||
|
||||
rational val;
|
||||
if (s.find_viable(v, val) == dd::find_t::singleton) {
|
||||
s.propagate(v, val, *this);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: what other constraints can be extracted cheaply?
|
||||
}
|
||||
|
||||
bool eq_constraint::is_always_false() {
|
||||
|
@ -87,6 +107,7 @@ namespace polysat {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Equality constraints
|
||||
*/
|
||||
|
@ -112,39 +133,6 @@ namespace polysat {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void eq_constraint::eq_narrow(solver& s) {
|
||||
LOG("Assignment: " << s.m_search);
|
||||
auto q = p().subst_val(s.m_search);
|
||||
LOG("Substituted: " << p() << " := " << q);
|
||||
if (q.is_zero())
|
||||
return;
|
||||
if (q.is_never_zero()) {
|
||||
LOG("Conflict (never zero under current assignment)");
|
||||
s.set_conflict(*this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.is_unilinear()) {
|
||||
// a*x + b == 0
|
||||
pvar v = q.var();
|
||||
rational a = q.hi().val();
|
||||
rational b = q.lo().val();
|
||||
bddv const& x = s.var2bits(v).var();
|
||||
bdd xs = (a * x + b == rational(0));
|
||||
s.push_cjust(v, this);
|
||||
s.intersect_viable(v, xs);
|
||||
|
||||
rational val;
|
||||
if (s.find_viable(v, val) == dd::find_t::singleton) {
|
||||
s.propagate(v, val, *this);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: what other constraints can be extracted cheaply?
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disequality constraints
|
||||
|
@ -155,8 +143,67 @@ namespace polysat {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void eq_constraint::diseq_narrow(solver& s) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
|
||||
|
||||
/// Compute forbidden interval for equality constraint by considering it as p <=u 0 (or p >u 0 for disequality)
|
||||
bool eq_constraint::forbidden_interval(solver& s, pvar v, eval_interval& i, constraint*& neg_condition)
|
||||
{
|
||||
SASSERT(!is_undef());
|
||||
|
||||
// Current only works when degree(v) is at most one
|
||||
unsigned const deg = p().degree(v);
|
||||
if (deg > 1)
|
||||
return false;
|
||||
|
||||
if (deg == 0) {
|
||||
UNREACHABLE(); // this case is not useful for conflict resolution (but it could be handled in principle)
|
||||
// i is empty or full, condition would be this constraint itself?
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned const sz = s.size(v);
|
||||
dd::pdd_manager& m = s.sz2pdd(sz);
|
||||
|
||||
pdd p1 = m.zero();
|
||||
pdd e1 = m.zero();
|
||||
p().factor(v, 1, p1, e1);
|
||||
|
||||
pdd e2 = m.zero();
|
||||
|
||||
// Currently only works if coefficient is a power of two
|
||||
if (!p1.is_val())
|
||||
return false;
|
||||
rational a1 = p1.val();
|
||||
// TODO: to express the interval for coefficient 2^i symbolically, we need right-shift/upper-bits-extract in the language.
|
||||
// So currently we can only do it if the coefficient is 1.
|
||||
if (!a1.is_zero() && !a1.is_one())
|
||||
return false;
|
||||
/*
|
||||
unsigned j1 = 0;
|
||||
if (!a1.is_zero() && !a1.is_power_of_two(j1))
|
||||
return false;
|
||||
*/
|
||||
|
||||
// Concrete values of evaluable terms
|
||||
auto e1s = e1.subst_val(s.m_search);
|
||||
auto e2s = m.zero();
|
||||
SASSERT(e1s.is_val());
|
||||
SASSERT(e2s.is_val());
|
||||
|
||||
// e1 + t <= 0, with t = 2^j1*y
|
||||
// condition for empty/full: 0 == -1, never satisfied, so we always have a proper interval!
|
||||
SASSERT(!a1.is_zero());
|
||||
pdd lo = 1 - e1;
|
||||
rational lo_val = (1 - e1s).val();
|
||||
pdd hi = -e1;
|
||||
rational hi_val = (-e1s).val();
|
||||
if (is_negative()) {
|
||||
swap(lo, hi);
|
||||
lo_val.swap(hi_val);
|
||||
}
|
||||
i = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||
neg_condition = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue