3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-29 11:55:51 +00:00
z3/src/math/polysat/eq_constraint.cpp
Jakob Rath 77350d97da
BDD vectors: add subtract and quot_rem, move finite domain abstraction out of bdd_manager (#5201)
* Coding style

* Simplify bddv class

* mk_eq: run loop from below

* Add unit test for bddv unsigned comparison

* Add test that shows contains_num/find_num fail after reordering

* Add BDD vector subtraction

* Call apply_rec in mk_ite_rec instead of apply

* Question about mk_quant

* Implement quot_rem over BDD vectors

* Move shl/shr to bddv

* Make unit test smaller

* Add class dd::fdd to manage association between BDDs and numbers

* Remove contains_num/find_num from bdd_manager
2021-04-20 09:09:32 -07:00

128 lines
3.8 KiB
C++

/*++
Copyright (c) 2021 Microsoft Corporation
Module Name:
polysat eq_constraints
Author:
Nikolaj Bjorner (nbjorner) 2021-03-19
Jakob Rath 2021-04-6
--*/
#include "math/polysat/constraint.h"
#include "math/polysat/solver.h"
#include "math/polysat/log.h"
namespace polysat {
std::ostream& eq_constraint::display(std::ostream& out) const {
return out << p() << " == 0";
}
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;
}
}
LOG("Assignments: " << s.m_search);
auto q = p().subst_val(s.m_search);
LOG("Substituted: " << p() << " := " << q);
TRACE("polysat", tout << p() << " := " << q << "\n";);
if (q.is_zero())
return false;
if (q.is_never_zero()) {
LOG("Conflict (never zero under current assignment)");
s.set_conflict(*this);
return false;
}
// at most one variable remains unassigned.
auto other_var = vars()[1 - idx];
SASSERT(!q.is_val() && q.var() == other_var);
// Detect and apply unit propagation.
if (try_narrow_with(q, s)) {
rational val;
switch (s.find_viable(other_var, val)) {
case dd::find_t::empty:
s.set_conflict(*this);
return false;
case dd::find_t::singleton:
s.propagate(other_var, val, *this);
return false;
case dd::find_t::multiple:
/* do nothing */
break;
}
}
return false;
}
constraint* eq_constraint::resolve(solver& s, pvar v) {
if (s.m_conflict.size() != 1)
return nullptr;
constraint* c = s.m_conflict[0];
if (c->is_eq()) {
pdd a = c->to_eq().p();
pdd b = p();
pdd r = a;
if (!a.resolve(v, b, r))
return nullptr;
p_dependency_ref d(s.m_dm.mk_join(c->dep(), dep()), s.m_dm);
unsigned lvl = std::max(c->level(), level());
return constraint::eq(lvl, r, d);
}
return nullptr;
}
bool eq_constraint::try_narrow_with(pdd const& q, solver& s) {
if (q.is_linear() && q.free_vars().size() == 1) {
// a*x + b == 0
pvar v = q.var();
rational a = q.hi().val();
rational b = q.lo().val();
bddv const& x = s.sz2bits(s.size(v)).var();
bdd xs = (a * x + b == rational(0));
s.intersect_viable(v, xs);
s.push_cjust(v, this);
return true;
}
// TODO: what other constraints can be extracted cheaply?
return false;
}
void eq_constraint::narrow(solver& s) {
// NSB code review:
// This should also use the current assignment so be similar to propagate.
// The idea is that narrow is invoked when the constraint is first added
// and also when the constraint is used in a conflict.
// When it is used in a conflict, there could be a partial assignment in s.m_search
// that fixes variables in p().
//
(void)try_narrow_with(p(), s);
}
bool eq_constraint::is_always_false() {
return p().is_never_zero();
}
bool eq_constraint::is_currently_false(solver& s) {
pdd r = p().subst_val(s.m_search);
return r.is_never_zero();
}
}