3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-05-07 07:45:46 +00:00

Polysat: disjunctive lemmas (#5311)

* Add macro to disable linear solver for development

* Move new_* and add_* to header

* Add ref_vector_core::push_back(ref<T>&&)

* Attempt to simplify lifetime handling

* Make operator bool() explicit

* clause improvements

* display boolean assignment

* clause::resolve

* bug fixes

* more fixes

* final case of backtrack
This commit is contained in:
Jakob Rath 2021-05-28 22:53:08 +02:00 committed by GitHub
parent 5fd3ef6580
commit 8757f04d20
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 524 additions and 294 deletions

View file

@ -22,31 +22,51 @@ Author:
namespace polysat {
constraint* constraint_manager::insert(scoped_ptr<constraint>&& sc) {
constraint* c = sc.detach();
constraint* constraint_manager::insert(constraint_ref c) {
LOG_V("Inserting constraint: " << show_deref(c));
SASSERT(c);
SASSERT(c->bvar() != sat::null_bool_var);
SASSERT(get_bv2c(c->bvar()) == nullptr);
insert_bv2c(c->bvar(), c);
SASSERT(get_bv2c(c->bvar()) == c.get());
// TODO: use explicit insert_external(constraint* c, unsigned dep) for that.
if (c->dep() && c->dep()->is_leaf()) {
unsigned dep = c->dep()->leaf_value();
SASSERT(!m_external_constraints.contains(dep));
m_external_constraints.insert(dep, c);
m_external_constraints.insert(dep, c.get());
}
while (m_constraints.size() <= c->level())
m_constraints.push_back({});
m_constraints[c->level()].push_back(c);
return c;
constraint* pc = c.get();
m_constraints[c->level()].push_back(std::move(c));
return pc;
}
clause* constraint_manager::insert(clause_ref cl) {
SASSERT(cl);
// Insert new constraints
for (constraint* c : cl->m_new_constraints)
// TODO: if (!inserted) ?
insert(c);
cl->m_new_constraints = {}; // free vector memory
// Insert clause
while (m_clauses.size() <= cl->level())
m_clauses.push_back({});
clause* pcl = cl.get();
m_clauses[cl->level()].push_back(std::move(cl));
return pcl;
}
// Release constraints at the given level and above.
void constraint_manager::release_level(unsigned lvl) {
for (unsigned l = m_clauses.size(); l-- > lvl; ) {
for (auto const& cl : m_clauses[l]) {
SASSERT_EQ(cl->m_ref_count, 1); // otherwise there is a leftover reference somewhere
}
m_clauses[l].reset();
}
for (unsigned l = m_constraints.size(); l-- > lvl; ) {
for (constraint* c : m_constraints[l]) {
for (auto const& c : m_constraints[l]) {
LOG_V("Removing constraint: " << show_deref(c));
erase_bv2c(c->bvar());
SASSERT_EQ(c->m_ref_count, 1); // otherwise there is a leftover reference somewhere
if (c->dep() && c->dep()->is_leaf()) {
unsigned dep = c->dep()->leaf_value();
SASSERT(m_external_constraints.contains(dep));
@ -57,6 +77,12 @@ namespace polysat {
}
}
constraint_manager::~constraint_manager() {
// Release explicitly to check for leftover references in debug mode,
// and to make sure all constraints are destructed before the bvar->constraint mapping.
release_level(0);
}
constraint* constraint_manager::lookup(sat::bool_var var) const {
return get_bv2c(var);
}
@ -85,19 +111,19 @@ namespace polysat {
return *dynamic_cast<var_constraint const*>(this);
}
scoped_ptr<constraint> constraint_manager::eq(unsigned lvl, csign_t sign, pdd const& p, p_dependency_ref const& d) {
constraint_ref constraint_manager::eq(unsigned lvl, csign_t sign, pdd const& p, p_dependency_ref const& d) {
return alloc(eq_constraint, *this, lvl, sign, p, d);
}
scoped_ptr<constraint> constraint_manager::viable(unsigned lvl, csign_t sign, pvar v, bdd const& b, p_dependency_ref const& d) {
constraint_ref constraint_manager::viable(unsigned lvl, csign_t sign, pvar v, bdd const& b, p_dependency_ref const& d) {
return alloc(var_constraint, *this, lvl, sign, v, b, d);
}
scoped_ptr<constraint> constraint_manager::ule(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
constraint_ref constraint_manager::ule(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
return alloc(ule_constraint, *this, lvl, sign, a, b, d);
}
scoped_ptr<constraint> constraint_manager::ult(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
constraint_ref constraint_manager::ult(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
// a < b <=> !(b <= a)
return ule(lvl, static_cast<csign_t>(!sign), b, a, d);
}
@ -118,12 +144,12 @@ namespace polysat {
//
// Argument: flipping the msb swaps the negative and non-negative blocks
//
scoped_ptr<constraint> constraint_manager::sle(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
constraint_ref constraint_manager::sle(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
auto shift = rational::power_of_two(a.power_of_2() - 1);
return ule(lvl, sign, a + shift, b + shift, d);
}
scoped_ptr<constraint> constraint_manager::slt(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
constraint_ref constraint_manager::slt(unsigned lvl, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
auto shift = rational::power_of_two(a.power_of_2() - 1);
return ult(lvl, sign, a + shift, b + shift, d);
}
@ -156,8 +182,19 @@ namespace polysat {
narrow(s);
}
clause* clause::from_literals(unsigned lvl, p_dependency_ref const& d, sat::literal_vector const& literals) {
return alloc(clause, lvl, d, literals);
clause_ref clause::from_unit(constraint_ref c) {
SASSERT(c);
unsigned const lvl = c->level();
auto const& dep = c->m_dep;
sat::literal_vector lits;
lits.push_back(sat::literal(c->bvar()));
constraint_ref_vector cs;
cs.push_back(std::move(c));
return clause::from_literals(lvl, dep, std::move(lits), std::move(cs));
}
clause_ref clause::from_literals(unsigned lvl, p_dependency_ref const& d, sat::literal_vector literals, constraint_ref_vector constraints) {
return alloc(clause, lvl, d, literals, constraints);
}
bool clause::is_always_false(solver& s) const {
@ -173,6 +210,31 @@ namespace polysat {
return c->is_currently_false(s);
});
}
bool clause::resolve(sat::bool_var var, clause const& other) {
DEBUG_CODE({
bool this_has_pos = std::count(begin(), end(), sat::literal(var)) > 0;
bool this_has_neg = std::count(begin(), end(), ~sat::literal(var)) > 0;
bool other_has_pos = std::count(other.begin(), other.end(), sat::literal(var)) > 0;
bool other_has_neg = std::count(other.begin(), other.end(), ~sat::literal(var)) > 0;
SASSERT(!this_has_pos || !this_has_neg); // otherwise this is tautology
SASSERT(!other_has_pos || !other_has_neg); // otherwise other is tautology
SASSERT((this_has_pos && other_has_neg) || (this_has_neg && other_has_pos));
});
// The resolved var should not be one of the new constraints
SASSERT(std::all_of(new_constraints().begin(), new_constraints().end(), [var](constraint* c) { return c->bvar() != var; }));
SASSERT(std::all_of(other.new_constraints().begin(), other.new_constraints().end(), [var](constraint* c) { return c->bvar() != var; }));
int j = 0;
for (int i = 0; i < m_literals.size(); ++i)
if (m_literals[i].var() != var)
m_literals[j++] = m_literals[i];
m_literals.shrink(j);
for (sat::literal lit : other.literals())
if (lit.var() != var)
m_literals.push_back(lit);
return true;
}
std::ostream& clause::display(std::ostream& out) const {
bool first = true;
for (auto lit : literals()) {
@ -185,31 +247,16 @@ namespace polysat {
return out;
}
scoped_clause::scoped_clause(scoped_ptr<constraint>&& c) {
SASSERT(c);
sat::literal_vector lits;
lits.push_back(sat::literal(c->bvar()));
m_clause = clause::from_literals(c->level(), c->m_dep, lits);
m_owned.push_back(c.detach());
}
std::ostream& scoped_clause::display(std::ostream& out) const {
if (m_clause)
return out << *m_clause;
else
return out << "<NULL>";
}
std::ostream& constraints_and_clauses::display(std::ostream& out) const {
bool first = true;
for (auto* c : units()) {
for (auto c : units()) {
if (first)
first = false;
else
out << " ; ";
out << show_deref(c);
}
for (auto* cl : clauses()) {
for (auto cl : clauses()) {
if (first)
first = false;
else