mirror of
https://github.com/Z3Prover/z3
synced 2025-06-25 15:23:41 +00:00
Polysat: constraint refactor cont'd, deduplicate constraints (#5520)
* Assign boolean variables only to long-lived constraints, and deduplicate constraints when they are created * scoped_signed_constraint * update other classes * fix * Don't use scoped_ptr<constraint> with dedup()
This commit is contained in:
parent
ebaea2159e
commit
0c1e44da77
16 changed files with 365 additions and 298 deletions
|
@ -31,7 +31,7 @@ namespace polysat {
|
||||||
|
|
||||||
clause_ref clause_builder::build() {
|
clause_ref clause_builder::build() {
|
||||||
// TODO: here we could set all the levels of the new constraints. so we do not have to compute the max at every use site.
|
// TODO: here we could set all the levels of the new constraints. so we do not have to compute the max at every use site.
|
||||||
clause_ref cl = clause::from_literals(m_level, std::move(m_dep), std::move(m_literals), std::move(m_new_constraints));
|
clause_ref cl = clause::from_literals(m_level, std::move(m_dep), std::move(m_literals));
|
||||||
m_level = 0;
|
m_level = 0;
|
||||||
SASSERT(empty());
|
SASSERT(empty());
|
||||||
return cl;
|
return cl;
|
||||||
|
@ -52,13 +52,12 @@ namespace polysat {
|
||||||
m_literals.push_back(lit);
|
m_literals.push_back(lit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clause_builder::push_new_constraint(constraint_literal_ref c) {
|
void clause_builder::push_new_constraint(scoped_signed_constraint c) {
|
||||||
// TODO: assert that constraint is new (not 'inserted' into manager yet)
|
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
if (c.get().is_always_false())
|
if (c.is_always_false())
|
||||||
return;
|
return;
|
||||||
m_level = std::max(m_level, c->level());
|
m_level = std::max(m_level, c->level());
|
||||||
m_literals.push_back(c.literal());
|
m_literals.push_back(c.blit());
|
||||||
m_new_constraints.push_back(c.detach());
|
m_new_constraints.push_back(c.detach());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,11 @@ Notes:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
// TODO: this is now incorporated in conflict_core
|
||||||
class clause_builder {
|
class clause_builder {
|
||||||
solver& m_solver;
|
solver& m_solver;
|
||||||
sat::literal_vector m_literals;
|
sat::literal_vector m_literals;
|
||||||
constraint_ref_vector m_new_constraints;
|
scoped_ptr_vector<constraint> m_new_constraints;
|
||||||
p_dependency_ref m_dep;
|
p_dependency_ref m_dep;
|
||||||
unsigned m_level = 0;
|
unsigned m_level = 0;
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ namespace polysat {
|
||||||
void push_literal(sat::literal lit);
|
void push_literal(sat::literal lit);
|
||||||
|
|
||||||
/// Add a constraint to the clause that does not yet exist in the solver so far.
|
/// Add a constraint to the clause that does not yet exist in the solver so far.
|
||||||
void push_new_constraint(constraint_literal_ref c);
|
void push_new_constraint(scoped_signed_constraint c);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,17 +39,18 @@ namespace polysat {
|
||||||
m_needs_model = false;
|
m_needs_model = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict_core::set(constraint_literal c) {
|
void conflict_core::set(signed_constraint c) {
|
||||||
LOG("Conflict: " << c);
|
LOG("Conflict: " << c);
|
||||||
SASSERT(empty());
|
SASSERT(empty());
|
||||||
m_constraints.push_back(std::move(c));
|
m_constraints.push_back(std::move(c));
|
||||||
m_needs_model = true;
|
m_needs_model = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict_core::set(pvar v, vector<constraint_literal> const& cjust_v) {
|
void conflict_core::set(pvar v, vector<signed_constraint> const& cjust_v) {
|
||||||
LOG("Conflict for v" << v << ": " << cjust_v);
|
LOG("Conflict for v" << v << ": " << cjust_v);
|
||||||
SASSERT(empty());
|
SASSERT(empty());
|
||||||
NOT_IMPLEMENTED_YET();
|
NOT_IMPLEMENTED_YET();
|
||||||
|
m_conflict_var = v;
|
||||||
m_constraints.append(cjust_v);
|
m_constraints.append(cjust_v);
|
||||||
if (cjust_v.empty())
|
if (cjust_v.empty())
|
||||||
m_constraints.push_back({});
|
m_constraints.push_back({});
|
||||||
|
|
|
@ -18,8 +18,14 @@ namespace polysat {
|
||||||
|
|
||||||
/** Conflict state, represented as core (~negation of clause). */
|
/** Conflict state, represented as core (~negation of clause). */
|
||||||
class conflict_core {
|
class conflict_core {
|
||||||
// TODO: core needs to own the constraint literals...
|
vector<signed_constraint> m_constraints;
|
||||||
vector<constraint_literal> m_constraints;
|
|
||||||
|
/** Storage for new constraints that may not yet have a boolean variable yet */
|
||||||
|
ptr_vector<constraint> m_storage;
|
||||||
|
|
||||||
|
// If this is not null_var, the conflict was due to empty viable set for this variable.
|
||||||
|
// Can be treated like "v = x" for any value x.
|
||||||
|
pvar m_conflict_var = null_var;
|
||||||
|
|
||||||
/** True iff the conflict depends on the current variable assignment. (If so, additional constraints must be added to the final learned clause.) */
|
/** True iff the conflict depends on the current variable assignment. (If so, additional constraints must be added to the final learned clause.) */
|
||||||
bool m_needs_model = false;
|
bool m_needs_model = false;
|
||||||
|
@ -28,8 +34,11 @@ namespace polysat {
|
||||||
// For example: if we have 4x+y=2 and y=0, then we have a conflict no matter the value of x, so we should drop x=? from the core.
|
// For example: if we have 4x+y=2 and y=0, then we have a conflict no matter the value of x, so we should drop x=? from the core.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
~conflict_core() {
|
||||||
|
gc();
|
||||||
|
}
|
||||||
|
|
||||||
vector<constraint_literal> const& constraints() const { return m_constraints; }
|
vector<signed_constraint> const& constraints() const { return m_constraints; }
|
||||||
bool needs_model() const { return m_needs_model; }
|
bool needs_model() const { return m_needs_model; }
|
||||||
|
|
||||||
bool empty() const {
|
bool empty() const {
|
||||||
|
@ -39,15 +48,24 @@ namespace polysat {
|
||||||
void reset() {
|
void reset() {
|
||||||
m_constraints.reset();
|
m_constraints.reset();
|
||||||
m_needs_model = false;
|
m_needs_model = false;
|
||||||
|
gc();
|
||||||
SASSERT(empty());
|
SASSERT(empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** for bailing out with a conflict at the base level */
|
/** for bailing out with a conflict at the base level */
|
||||||
void set(std::nullptr_t);
|
void set(std::nullptr_t);
|
||||||
/** conflict because the constraint c is false under current variable assignment */
|
/** conflict because the constraint c is false under current variable assignment */
|
||||||
void set(constraint_literal c);
|
void set(signed_constraint c);
|
||||||
/** conflict because there is no viable value for the variable v */
|
/** conflict because there is no viable value for the variable v */
|
||||||
void set(pvar v, vector<constraint_literal> const& cjust_v);
|
void set(pvar v, vector<signed_constraint> const& cjust_v);
|
||||||
|
|
||||||
|
/** Garbage-collect temporary constraints */
|
||||||
|
void gc() {
|
||||||
|
for (auto* c : m_storage)
|
||||||
|
if (!c->has_bvar())
|
||||||
|
dealloc(c);
|
||||||
|
m_storage.reset();
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& display(std::ostream& out) const;
|
std::ostream& display(std::ostream& out) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,31 +21,47 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
constraint* constraint_manager::store(constraint_ref c) {
|
static_assert(!std::is_copy_assignable_v<scoped_signed_constraint>);
|
||||||
LOG_V("Store constraint: " << show_deref(c));
|
static_assert(!std::is_copy_constructible_v<scoped_signed_constraint>);
|
||||||
SASSERT(c);
|
|
||||||
SASSERT(c->bvar() != sat::null_bool_var);
|
void constraint_manager::assign_bv2c(sat::bool_var bv, constraint* c) {
|
||||||
SASSERT(get_bv2c(c->bvar()) == c.get());
|
SASSERT_EQ(get_bv2c(bv), nullptr);
|
||||||
while (m_constraints.size() <= c->level())
|
SASSERT(!c->has_bvar());
|
||||||
m_constraints.push_back({});
|
SASSERT(!m_constraint_table.contains(c));
|
||||||
constraint* pc = c.get();
|
c->m_bvar = bv;
|
||||||
m_constraints[c->level()].push_back(std::move(c));
|
m_bv2constraint.setx(bv, c, nullptr);
|
||||||
return pc;
|
m_constraint_table.insert(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
clause* constraint_manager::store(clause_ref cl) {
|
void constraint_manager::erase_bv2c(constraint* c) {
|
||||||
LOG_V("Store clause: " << show_deref(cl));
|
SASSERT(c->has_bvar());
|
||||||
SASSERT(cl);
|
SASSERT_EQ(get_bv2c(c->bvar()), c);
|
||||||
// Insert new constraints
|
SASSERT(m_constraint_table.contains(c));
|
||||||
for (constraint* c : cl->m_new_constraints)
|
m_bv2constraint[c->bvar()] = nullptr;
|
||||||
store(c);
|
m_constraint_table.remove(c);
|
||||||
cl->m_new_constraints = {}; // free vector memory
|
c->m_bvar = sat::null_bool_var;
|
||||||
// Insert clause
|
}
|
||||||
|
|
||||||
|
constraint* constraint_manager::get_bv2c(sat::bool_var bv) const {
|
||||||
|
return m_bv2constraint.get(bv, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraint* constraint_manager::store(scoped_constraint_ptr scoped_c) {
|
||||||
|
constraint* c = scoped_c.detach();
|
||||||
|
LOG_V("Store constraint: " << show_deref(c));
|
||||||
|
assign_bv2c(m_bvars.new_var(), c);
|
||||||
|
while (m_constraints.size() <= c->level())
|
||||||
|
m_constraints.push_back({});
|
||||||
|
m_constraints[c->level()].push_back(c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
clause* constraint_manager::store(clause_ref cl_ref) {
|
||||||
|
clause* cl = cl_ref.get();
|
||||||
while (m_clauses.size() <= cl->level())
|
while (m_clauses.size() <= cl->level())
|
||||||
m_clauses.push_back({});
|
m_clauses.push_back({});
|
||||||
clause* pcl = cl.get();
|
m_clauses[cl->level()].push_back(std::move(cl_ref));
|
||||||
m_clauses[cl->level()].push_back(std::move(cl));
|
return cl;
|
||||||
return pcl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void constraint_manager::register_external(constraint* c) {
|
void constraint_manager::register_external(constraint* c) {
|
||||||
|
@ -60,18 +76,15 @@ namespace polysat {
|
||||||
// Release constraints at the given level and above.
|
// Release constraints at the given level and above.
|
||||||
void constraint_manager::release_level(unsigned lvl) {
|
void constraint_manager::release_level(unsigned lvl) {
|
||||||
for (unsigned l = m_constraints.size(); l-- > lvl; ) {
|
for (unsigned l = m_constraints.size(); l-- > lvl; ) {
|
||||||
for (auto const& c : m_constraints[l]) {
|
for (auto* c : m_constraints[l]) {
|
||||||
LOG_V("Removing constraint: " << show_deref(c));
|
LOG_V("Destroying constraint: " << show_deref(c));
|
||||||
if (c->m_ref_count > 2) {
|
|
||||||
// NOTE: ref count could be two if the constraint was added twice (once as part of clause, and later as unit constraint)
|
|
||||||
LOG_H1("Expected ref_count 1 or 2, got " << c->m_ref_count << " for " << *c);
|
|
||||||
}
|
|
||||||
auto* d = c->unit_dep();
|
auto* d = c->unit_dep();
|
||||||
if (d && d->is_leaf()) {
|
if (d && d->is_leaf()) {
|
||||||
unsigned const dep = d->leaf_value();
|
unsigned const dep = d->leaf_value();
|
||||||
SASSERT(m_external_constraints.contains(dep));
|
SASSERT(m_external_constraints.contains(dep));
|
||||||
m_external_constraints.remove(dep);
|
m_external_constraints.remove(dep);
|
||||||
}
|
}
|
||||||
|
erase_bv2c(c);
|
||||||
}
|
}
|
||||||
m_constraints[l].reset();
|
m_constraints[l].reset();
|
||||||
}
|
}
|
||||||
|
@ -93,36 +106,34 @@ namespace polysat {
|
||||||
return get_bv2c(var);
|
return get_bv2c(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal constraint_manager::lookup(sat::literal lit) const {
|
signed_constraint constraint_manager::lookup(sat::literal lit) const {
|
||||||
return {lit, lookup(lit.var())};
|
return {lookup(lit.var()), lit};
|
||||||
}
|
}
|
||||||
|
|
||||||
eq_constraint& constraint::to_eq() {
|
/** Look up constraint among stored constraints. */
|
||||||
return *dynamic_cast<eq_constraint*>(this);
|
constraint* constraint_manager::dedup(constraint* c1) {
|
||||||
|
auto it = m_constraint_table.find(c1);
|
||||||
|
if (it == m_constraint_table.end())
|
||||||
|
return c1;
|
||||||
|
constraint* c0 = *it;
|
||||||
|
// TODO: can this assertion be violated?
|
||||||
|
// Yes, e.g.: constraint c was asserted at level 1, the conflict resolution derived ~c from c1,c2 at level 0...
|
||||||
|
// In that case we have to adjust c0's level in the storage.
|
||||||
|
// What about the level of the boolean variable? That depends on its position in the assignment stack and should probably stay where it is. Will be updated separately when we patch the assignment stack.
|
||||||
|
SASSERT(c0->level() <= c1->level());
|
||||||
|
dealloc(c1);
|
||||||
|
return c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
eq_constraint const& constraint::to_eq() const {
|
scoped_signed_constraint constraint_manager::eq(unsigned lvl, pdd const& p) {
|
||||||
return *dynamic_cast<eq_constraint const*>(this);
|
return {dedup(alloc(eq_constraint, *this, lvl, p)), true};
|
||||||
}
|
}
|
||||||
|
|
||||||
ule_constraint& constraint::to_ule() {
|
scoped_signed_constraint constraint_manager::ule(unsigned lvl, pdd const& a, pdd const& b) {
|
||||||
return *dynamic_cast<ule_constraint*>(this);
|
return {dedup(alloc(ule_constraint, *this, lvl, a, b)), true};
|
||||||
}
|
}
|
||||||
|
|
||||||
ule_constraint const& constraint::to_ule() const {
|
scoped_signed_constraint constraint_manager::ult(unsigned lvl, pdd const& a, pdd const& b) {
|
||||||
return *dynamic_cast<ule_constraint const*>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
constraint_literal_ref constraint_manager::eq(unsigned lvl, pdd const& p) {
|
|
||||||
return constraint_literal_ref{alloc(eq_constraint, *this, lvl, p)};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
constraint_literal_ref constraint_manager::ule(unsigned lvl, pdd const& a, pdd const& b) {
|
|
||||||
return constraint_literal_ref{alloc(ule_constraint, *this, lvl, a, b)};
|
|
||||||
}
|
|
||||||
|
|
||||||
constraint_literal_ref constraint_manager::ult(unsigned lvl, pdd const& a, pdd const& b) {
|
|
||||||
// a < b <=> !(b <= a)
|
// a < b <=> !(b <= a)
|
||||||
return ~ule(lvl, b, a);
|
return ~ule(lvl, b, a);
|
||||||
}
|
}
|
||||||
|
@ -143,16 +154,32 @@ namespace polysat {
|
||||||
//
|
//
|
||||||
// Argument: flipping the msb swaps the negative and non-negative blocks
|
// Argument: flipping the msb swaps the negative and non-negative blocks
|
||||||
//
|
//
|
||||||
constraint_literal_ref constraint_manager::sle(unsigned lvl, pdd const& a, pdd const& b) {
|
scoped_signed_constraint constraint_manager::sle(unsigned lvl, pdd const& a, pdd const& b) {
|
||||||
auto shift = rational::power_of_two(a.power_of_2() - 1);
|
auto shift = rational::power_of_two(a.power_of_2() - 1);
|
||||||
return ule(lvl, a + shift, b + shift);
|
return ule(lvl, a + shift, b + shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal_ref constraint_manager::slt(unsigned lvl, pdd const& a, pdd const& b) {
|
scoped_signed_constraint constraint_manager::slt(unsigned lvl, pdd const& a, pdd const& b) {
|
||||||
auto shift = rational::power_of_two(a.power_of_2() - 1);
|
auto shift = rational::power_of_two(a.power_of_2() - 1);
|
||||||
return ult(lvl, a + shift, b + shift);
|
return ult(lvl, a + shift, b + shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eq_constraint& constraint::to_eq() {
|
||||||
|
return *dynamic_cast<eq_constraint*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
eq_constraint const& constraint::to_eq() const {
|
||||||
|
return *dynamic_cast<eq_constraint const*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ule_constraint& constraint::to_ule() {
|
||||||
|
return *dynamic_cast<ule_constraint*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ule_constraint const& constraint::to_ule() const {
|
||||||
|
return *dynamic_cast<ule_constraint const*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& constraint::display_extra(std::ostream& out, lbool status) const {
|
std::ostream& constraint::display_extra(std::ostream& out, lbool status) const {
|
||||||
out << " @" << level() << " (b" << bvar() << ")";
|
out << " @" << level() << " (b" << bvar() << ")";
|
||||||
(void)status;
|
(void)status;
|
||||||
|
@ -190,41 +217,28 @@ namespace polysat {
|
||||||
narrow(s, is_positive);
|
narrow(s, is_positive);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream &constraint_literal::display(std::ostream &out) const {
|
clause_ref clause::from_unit(signed_constraint c, p_dependency_ref d) {
|
||||||
if (*this)
|
SASSERT(c->has_bvar());
|
||||||
return out << m_literal << "{ " << *m_constraint << " }";
|
|
||||||
else
|
|
||||||
return out << "<null>";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream &constraint_literal_ref::display(std::ostream &out) const {
|
|
||||||
return out << get();
|
|
||||||
}
|
|
||||||
|
|
||||||
clause_ref clause::from_unit(constraint_literal_ref c, p_dependency_ref d) {
|
|
||||||
SASSERT(c);
|
|
||||||
unsigned const lvl = c->level();
|
unsigned const lvl = c->level();
|
||||||
sat::literal_vector lits;
|
sat::literal_vector lits;
|
||||||
lits.push_back(c.literal());
|
lits.push_back(c.blit());
|
||||||
constraint_ref_vector cs;
|
return clause::from_literals(lvl, std::move(d), std::move(lits));
|
||||||
cs.push_back(c.detach());
|
|
||||||
return clause::from_literals(lvl, std::move(d), std::move(lits), std::move(cs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clause_ref clause::from_literals(unsigned lvl, p_dependency_ref d, sat::literal_vector literals, constraint_ref_vector constraints) {
|
clause_ref clause::from_literals(unsigned lvl, p_dependency_ref d, sat::literal_vector literals) {
|
||||||
return alloc(clause, lvl, std::move(d), std::move(literals), std::move(constraints));
|
return alloc(clause, lvl, std::move(d), std::move(literals));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool clause::is_always_false(solver& s) const {
|
bool clause::is_always_false(solver& s) const {
|
||||||
return std::all_of(m_literals.begin(), m_literals.end(), [&s](sat::literal lit) {
|
return std::all_of(m_literals.begin(), m_literals.end(), [&s](sat::literal lit) {
|
||||||
constraint_literal c = s.m_constraints.lookup(lit);
|
signed_constraint c = s.m_constraints.lookup(lit);
|
||||||
return c.is_always_false();
|
return c.is_always_false();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool clause::is_currently_false(solver& s) const {
|
bool clause::is_currently_false(solver& s) const {
|
||||||
return std::all_of(m_literals.begin(), m_literals.end(), [&s](sat::literal lit) {
|
return std::all_of(m_literals.begin(), m_literals.end(), [&s](sat::literal lit) {
|
||||||
constraint_literal c = s.m_constraints.lookup(lit);
|
signed_constraint c = s.m_constraints.lookup(lit);
|
||||||
return c.is_currently_false(s);
|
return c.is_currently_false(s);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -240,8 +254,6 @@ namespace polysat {
|
||||||
SASSERT((this_has_pos && other_has_neg) || (this_has_neg && other_has_pos));
|
SASSERT((this_has_pos && other_has_neg) || (this_has_neg && other_has_pos));
|
||||||
});
|
});
|
||||||
// The resolved var should not be one of the new constraints
|
// 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;
|
int j = 0;
|
||||||
for (auto lit : m_literals)
|
for (auto lit : m_literals)
|
||||||
if (lit.var() != var)
|
if (lit.var() != var)
|
||||||
|
|
|
@ -19,54 +19,65 @@ Author:
|
||||||
#include "util/map.h"
|
#include "util/map.h"
|
||||||
#include "util/ref.h"
|
#include "util/ref.h"
|
||||||
#include "util/ref_vector.h"
|
#include "util/ref_vector.h"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
enum ckind_t { eq_t, ule_t };
|
enum ckind_t { eq_t, ule_t };
|
||||||
enum csign_t : bool { neg_t = false, pos_t = true };
|
enum csign_t : bool { neg_t = false, pos_t = true };
|
||||||
|
|
||||||
class constraint_literal;
|
|
||||||
class constraint_literal_ref;
|
|
||||||
class constraint;
|
class constraint;
|
||||||
class constraint_manager;
|
|
||||||
class clause;
|
|
||||||
class scoped_clause;
|
|
||||||
class eq_constraint;
|
class eq_constraint;
|
||||||
class ule_constraint;
|
class ule_constraint;
|
||||||
using constraint_ref = ref<constraint>;
|
|
||||||
using constraint_ref_vector = sref_vector<constraint>;
|
class scoped_constraint_ptr;
|
||||||
using constraint_literal_ref_vector = vector<constraint_literal_ref>;
|
|
||||||
|
template <bool is_owned>
|
||||||
|
class signed_constraint_base;
|
||||||
|
using signed_constraint = signed_constraint_base<false>;
|
||||||
|
using scoped_signed_constraint = signed_constraint_base<true>;
|
||||||
|
|
||||||
|
class clause;
|
||||||
using clause_ref = ref<clause>;
|
using clause_ref = ref<clause>;
|
||||||
using clause_ref_vector = sref_vector<clause>;
|
using clause_ref_vector = sref_vector<clause>;
|
||||||
|
|
||||||
|
using constraint_table = ptr_hashtable<constraint, obj_ptr_hash<constraint>, deref_eq<constraint>>;
|
||||||
|
|
||||||
// Manage constraint lifetime, deduplication, and connection to boolean variables/literals.
|
// Manage constraint lifetime, deduplication, and connection to boolean variables/literals.
|
||||||
class constraint_manager {
|
class constraint_manager {
|
||||||
friend class constraint;
|
friend class constraint;
|
||||||
|
|
||||||
bool_var_manager& m_bvars;
|
bool_var_manager& m_bvars;
|
||||||
|
|
||||||
// Association to boolean variables
|
// Constraints indexed by their boolean variable
|
||||||
ptr_vector<constraint> m_bv2constraint;
|
ptr_vector<constraint> m_bv2constraint;
|
||||||
void insert_bv2c(sat::bool_var bv, constraint* c) { /* NOTE: will be called with incompletely constructed c! */ m_bv2constraint.setx(bv, c, nullptr); }
|
// Constraints that have a boolean variable, for deduplication
|
||||||
void erase_bv2c(sat::bool_var bv) { m_bv2constraint[bv] = nullptr; }
|
constraint_table m_constraint_table;
|
||||||
constraint* get_bv2c(sat::bool_var bv) const { return m_bv2constraint.get(bv, nullptr); }
|
|
||||||
|
|
||||||
// Constraint storage per level; should be destructed before m_bv2constraint because constraint's destructor calls erase_bv2c
|
// Constraint storage per level
|
||||||
vector<vector<constraint_ref>> m_constraints;
|
vector<scoped_ptr_vector<constraint>> m_constraints;
|
||||||
vector<vector<clause_ref>> m_clauses;
|
vector<vector<clause_ref>> m_clauses;
|
||||||
|
|
||||||
// Association to external dependency values (i.e., external names for constraints)
|
// Association to external dependency values (i.e., external names for constraints)
|
||||||
u_map<constraint*> m_external_constraints;
|
u_map<constraint*> m_external_constraints;
|
||||||
|
|
||||||
// TODO: some hashmaps to look up whether constraint (or its negation) already exists
|
// Manage association of constraints to boolean variables
|
||||||
|
void assign_bv2c(sat::bool_var bv, constraint* c);
|
||||||
|
void erase_bv2c(constraint* c);
|
||||||
|
constraint* get_bv2c(sat::bool_var bv) const;
|
||||||
|
|
||||||
|
constraint* dedup(constraint* c);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constraint_manager(bool_var_manager& bvars): m_bvars(bvars) {}
|
constraint_manager(bool_var_manager& bvars): m_bvars(bvars) {}
|
||||||
// constraint_manager(bool_var_manager& bvars, poly_dep_manager& dm): m_bvars(bvars), m_dm(dm) {}
|
|
||||||
~constraint_manager();
|
~constraint_manager();
|
||||||
|
|
||||||
// Start managing lifetime of the given constraint
|
/** Start managing the lifetime of the given constraint
|
||||||
constraint* store(constraint_ref c);
|
* - Keeps the constraint until the corresponding level is popped
|
||||||
|
* - Allocates a boolean variable for the constraint
|
||||||
|
*/
|
||||||
|
constraint* store(scoped_constraint_ptr c);
|
||||||
|
|
||||||
clause* store(clause_ref cl);
|
clause* store(clause_ref cl);
|
||||||
|
|
||||||
/// Register a unit clause with an external dependency.
|
/// Register a unit clause with an external dependency.
|
||||||
|
@ -76,16 +87,14 @@ namespace polysat {
|
||||||
void release_level(unsigned lvl);
|
void release_level(unsigned lvl);
|
||||||
|
|
||||||
constraint* lookup(sat::bool_var var) const;
|
constraint* lookup(sat::bool_var var) const;
|
||||||
constraint_literal lookup(sat::literal lit) const;
|
signed_constraint lookup(sat::literal lit) const;
|
||||||
constraint* lookup_external(unsigned dep) const { return m_external_constraints.get(dep, nullptr); }
|
constraint* lookup_external(unsigned dep) const { return m_external_constraints.get(dep, nullptr); }
|
||||||
|
|
||||||
constraint_literal_ref eq(unsigned lvl, pdd const& p);
|
scoped_signed_constraint eq(unsigned lvl, pdd const& p);
|
||||||
constraint_literal_ref ule(unsigned lvl, pdd const& a, pdd const& b);
|
scoped_signed_constraint ule(unsigned lvl, pdd const& a, pdd const& b);
|
||||||
constraint_literal_ref ult(unsigned lvl, pdd const& a, pdd const& b);
|
scoped_signed_constraint ult(unsigned lvl, pdd const& a, pdd const& b);
|
||||||
constraint_literal_ref sle(unsigned lvl, pdd const& a, pdd const& b);
|
scoped_signed_constraint sle(unsigned lvl, pdd const& a, pdd const& b);
|
||||||
constraint_literal_ref slt(unsigned lvl, pdd const& a, pdd const& b);
|
scoped_signed_constraint slt(unsigned lvl, pdd const& a, pdd const& b);
|
||||||
|
|
||||||
// p_dependency_ref null_dep() const { return {nullptr, m_dm}; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,35 +114,28 @@ namespace polysat {
|
||||||
class constraint {
|
class constraint {
|
||||||
friend class constraint_manager;
|
friend class constraint_manager;
|
||||||
friend class clause;
|
friend class clause;
|
||||||
friend class scoped_clause;
|
|
||||||
friend class eq_constraint;
|
friend class eq_constraint;
|
||||||
friend class ule_constraint;
|
friend class ule_constraint;
|
||||||
|
|
||||||
constraint_manager* m_manager;
|
// constraint_manager* m_manager;
|
||||||
clause* m_unit_clause = nullptr; ///< If this constraint was asserted by a unit clause, we store that clause here.
|
clause* m_unit_clause = nullptr; ///< If this constraint was asserted by a unit clause, we store that clause here.
|
||||||
unsigned m_ref_count = 0;
|
|
||||||
unsigned m_storage_level; ///< Controls lifetime of the constraint object. Always a base level.
|
unsigned m_storage_level; ///< Controls lifetime of the constraint object. Always a base level.
|
||||||
ckind_t m_kind;
|
ckind_t m_kind;
|
||||||
unsigned_vector m_vars;
|
unsigned_vector m_vars;
|
||||||
sat::bool_var m_bvar; ///< boolean variable associated to this constraint; convention: a constraint itself always represents the positive sat::literal
|
/** The boolean variable associated to this constraint, if any.
|
||||||
|
* If this is not null_bool_var, then the constraint corresponds to a literal on the assignment stack.
|
||||||
|
* Convention: the plain constraint corresponds the positive sat::literal.
|
||||||
|
*/
|
||||||
|
sat::bool_var m_bvar = sat::null_bool_var;
|
||||||
|
|
||||||
constraint(constraint_manager& m, unsigned lvl, ckind_t k):
|
constraint(constraint_manager& m, unsigned lvl, ckind_t k):
|
||||||
m_manager(&m), m_storage_level(lvl), m_kind(k), m_bvar(m_manager->m_bvars.new_var()) {
|
/*m_manager(&m),*/ m_storage_level(lvl), m_kind(k) {}
|
||||||
SASSERT_EQ(m_manager->get_bv2c(bvar()), nullptr);
|
|
||||||
m_manager->insert_bv2c(bvar(), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::ostream& display_extra(std::ostream& out, lbool status) const;
|
std::ostream& display_extra(std::ostream& out, lbool status) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void inc_ref() { m_ref_count++; }
|
virtual ~constraint() {}
|
||||||
void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (!m_ref_count) dealloc(this); }
|
|
||||||
virtual ~constraint() {
|
|
||||||
SASSERT_EQ(m_manager->get_bv2c(bvar()), this);
|
|
||||||
m_manager->erase_bv2c(bvar());
|
|
||||||
m_manager->m_bvars.del_var(m_bvar);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual unsigned hash() const = 0;
|
virtual unsigned hash() const = 0;
|
||||||
virtual bool operator==(constraint const& other) const = 0;
|
virtual bool operator==(constraint const& other) const = 0;
|
||||||
|
@ -158,6 +160,7 @@ namespace polysat {
|
||||||
unsigned_vector& vars() { return m_vars; }
|
unsigned_vector& vars() { return m_vars; }
|
||||||
unsigned_vector const& vars() const { return m_vars; }
|
unsigned_vector const& vars() const { return m_vars; }
|
||||||
unsigned level() const { return m_storage_level; }
|
unsigned level() const { return m_storage_level; }
|
||||||
|
bool has_bvar() const { return m_bvar != sat::null_bool_var; }
|
||||||
sat::bool_var bvar() const { return m_bvar; }
|
sat::bool_var bvar() const { return m_bvar; }
|
||||||
|
|
||||||
clause* unit_clause() const { return m_unit_clause; }
|
clause* unit_clause() const { return m_unit_clause; }
|
||||||
|
@ -171,121 +174,148 @@ namespace polysat {
|
||||||
* \returns True iff a forbidden interval exists and the output parameters were set.
|
* \returns True iff a forbidden interval exists and the output parameters were set.
|
||||||
*/
|
*/
|
||||||
// TODO: we can probably remove this and unify the implementations for both cases by relying on as_inequality().
|
// TODO: we can probably remove this and unify the implementations for both cases by relying on as_inequality().
|
||||||
virtual bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond) { return false; }
|
virtual bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, scoped_signed_constraint& out_neg_cond) { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, constraint const& c) { return c.display(out); }
|
inline std::ostream& operator<<(std::ostream& out, constraint const& c) { return c.display(out); }
|
||||||
|
|
||||||
|
|
||||||
/// Literal together with the constraint it represents (i.e., constraint with polarity).
|
// Like scoped_ptr<constraint>, but only deallocates the constraint if it is temporary (i.e., does not have a boolean variable).
|
||||||
/// Non-owning version.
|
// This is needed because when a constraint is created, due to deduplication, we might get either a new constraint or an existing one.
|
||||||
class constraint_literal {
|
// (We want early deduplication because otherwise we might overlook possible boolean resolutions during conflict resolution.)
|
||||||
sat::literal m_literal = sat::null_literal;
|
// (TODO: we could replace this class by std::unique_ptr with a custom deleter)
|
||||||
constraint* m_constraint = nullptr;
|
class scoped_constraint_ptr {
|
||||||
|
constraint* m_ptr;
|
||||||
|
|
||||||
|
void dealloc_ptr() const {
|
||||||
|
if (m_ptr && !m_ptr->has_bvar())
|
||||||
|
dealloc(m_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constraint_literal() {}
|
scoped_constraint_ptr(constraint* ptr = nullptr): m_ptr(ptr) {}
|
||||||
constraint_literal(sat::literal lit, constraint* c):
|
|
||||||
m_literal(lit), m_constraint(c) {
|
|
||||||
SASSERT(get_constraint());
|
|
||||||
SASSERT(literal().var() == get_constraint()->bvar());
|
|
||||||
}
|
|
||||||
constraint_literal(constraint* c, bool is_positive): constraint_literal(sat::literal(c->bvar(), !is_positive), c) {}
|
|
||||||
|
|
||||||
constraint_literal operator~() const {
|
scoped_constraint_ptr(scoped_constraint_ptr &&other) noexcept : m_ptr(nullptr) {
|
||||||
return {~m_literal, m_constraint};
|
std::swap(m_ptr, other.m_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void negate() {
|
~scoped_constraint_ptr() {
|
||||||
m_literal = ~m_literal;
|
dealloc_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_positive() const { return !m_literal.sign(); }
|
scoped_constraint_ptr& operator=(scoped_constraint_ptr&& other) {
|
||||||
bool is_negative() const { return m_literal.sign(); }
|
*this = other.detach();
|
||||||
|
return *this;
|
||||||
bool propagate(solver& s, pvar v) { return get_constraint()->propagate(s, !literal().sign(), v); }
|
|
||||||
void propagate_core(solver& s, pvar v, pvar other_v) { get_constraint()->propagate_core(s, !literal().sign(), v, other_v); }
|
|
||||||
bool is_always_false() { return get_constraint()->is_always_false(!literal().sign()); }
|
|
||||||
bool is_currently_false(solver& s) { return get_constraint()->is_currently_false(s, !literal().sign()); }
|
|
||||||
bool is_currently_true(solver& s) { return get_constraint()->is_currently_true(s, !literal().sign()); }
|
|
||||||
void narrow(solver& s) { get_constraint()->narrow(s, !literal().sign()); }
|
|
||||||
inequality as_inequality() const { return get_constraint()->as_inequality(!literal().sign()); }
|
|
||||||
|
|
||||||
sat::literal literal() const { return m_literal; }
|
|
||||||
constraint* get_constraint() const { return m_constraint; }
|
|
||||||
|
|
||||||
explicit operator bool() const { return !!m_constraint; }
|
|
||||||
bool operator!() const { return !m_constraint; }
|
|
||||||
constraint* operator->() const { return m_constraint; }
|
|
||||||
constraint const& operator*() const { return *m_constraint; }
|
|
||||||
|
|
||||||
constraint_literal& operator=(std::nullptr_t) { m_literal = sat::null_literal; m_constraint = nullptr; return *this; }
|
|
||||||
|
|
||||||
std::ostream& display(std::ostream& out) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, constraint_literal const& c) { return c.display(out); }
|
scoped_constraint_ptr& operator=(constraint* n) {
|
||||||
|
if (m_ptr != n) {
|
||||||
inline bool operator==(constraint_literal const& lhs, constraint_literal const& rhs) {
|
dealloc_ptr();
|
||||||
if (lhs.literal() == rhs.literal())
|
m_ptr = n;
|
||||||
SASSERT(lhs.get_constraint() == rhs.get_constraint());
|
}
|
||||||
return lhs.literal() == rhs.literal();
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void swap(scoped_constraint_ptr& p) {
|
||||||
|
std::swap(m_ptr, p.m_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
/// Version of constraint_literal that owns the constraint.
|
constraint* detach() {
|
||||||
class constraint_literal_ref {
|
constraint* tmp = m_ptr;
|
||||||
sat::literal m_literal = sat::null_literal;
|
m_ptr = nullptr;
|
||||||
constraint_ref m_constraint = nullptr;
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const { return !!m_ptr; }
|
||||||
|
bool operator!() const { return !m_ptr; }
|
||||||
|
constraint* get() const { return m_ptr; }
|
||||||
|
constraint* operator->() const { return m_ptr; }
|
||||||
|
const constraint& operator*() const { return *m_ptr; }
|
||||||
|
constraint &operator*() { return *m_ptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <bool is_owned>
|
||||||
|
class signed_constraint_base final {
|
||||||
public:
|
public:
|
||||||
constraint_literal_ref() {}
|
using ptr_t = std::conditional_t<is_owned, scoped_constraint_ptr, constraint*>;
|
||||||
constraint_literal_ref(sat::literal lit, constraint_ref c):
|
|
||||||
m_literal(lit), m_constraint(std::move(c)) {
|
|
||||||
SASSERT(get_constraint());
|
|
||||||
SASSERT(literal().var() == get_constraint()->bvar());
|
|
||||||
}
|
|
||||||
constraint_literal_ref(constraint_literal cl): constraint_literal_ref(cl.literal(), cl.get_constraint()) {}
|
|
||||||
|
|
||||||
constraint_literal_ref operator~() const&& {
|
|
||||||
return {~m_literal, std::move(m_constraint)};
|
|
||||||
}
|
|
||||||
|
|
||||||
void negate() {
|
|
||||||
m_literal = ~m_literal;
|
|
||||||
}
|
|
||||||
|
|
||||||
sat::literal literal() const { return m_literal; }
|
|
||||||
constraint* get_constraint() const { return m_constraint.get(); }
|
|
||||||
constraint_literal get() const { return {literal(), get_constraint()}; }
|
|
||||||
constraint_ref detach() { m_literal = sat::null_literal; return std::move(m_constraint); }
|
|
||||||
|
|
||||||
explicit operator bool() const { return !!m_constraint; }
|
|
||||||
bool operator!() const { return !m_constraint; }
|
|
||||||
constraint* operator->() const { return m_constraint.operator->(); }
|
|
||||||
constraint const& operator*() const { return *m_constraint; }
|
|
||||||
|
|
||||||
constraint_literal_ref& operator=(std::nullptr_t) { m_literal = sat::null_literal; m_constraint = nullptr; return *this; }
|
|
||||||
|
|
||||||
std::ostream& display(std::ostream& out) const;
|
|
||||||
private:
|
private:
|
||||||
friend class constraint_manager;
|
ptr_t m_constraint = nullptr;
|
||||||
explicit constraint_literal_ref(constraint* c): constraint_literal_ref(sat::literal(c->bvar()), c) {}
|
bool m_positive = true;
|
||||||
|
|
||||||
|
public:
|
||||||
|
signed_constraint_base() {}
|
||||||
|
signed_constraint_base(constraint* c, bool is_positive):
|
||||||
|
m_constraint(c), m_positive(is_positive) {}
|
||||||
|
signed_constraint_base(constraint* c, sat::literal lit):
|
||||||
|
signed_constraint_base(c, !lit.sign()) {
|
||||||
|
SASSERT_EQ(blit(), lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void negate() {
|
||||||
|
m_positive = !m_positive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_positive() const { return m_positive; }
|
||||||
|
bool is_negative() const { return !is_positive(); }
|
||||||
|
|
||||||
|
bool propagate(solver& s, pvar v) { return get()->propagate(s, is_positive(), v); }
|
||||||
|
void propagate_core(solver& s, pvar v, pvar other_v) { get()->propagate_core(s, is_positive(), v, other_v); }
|
||||||
|
bool is_always_false() { return get()->is_always_false(is_positive()); }
|
||||||
|
bool is_currently_false(solver& s) { return get()->is_currently_false(s, is_positive()); }
|
||||||
|
bool is_currently_true(solver& s) { return get()->is_currently_true(s, is_positive()); }
|
||||||
|
void narrow(solver& s) { get()->narrow(s, is_positive()); }
|
||||||
|
inequality as_inequality() const { return get()->as_inequality(is_positive()); }
|
||||||
|
|
||||||
|
sat::bool_var bvar() const { return m_constraint->bvar(); }
|
||||||
|
sat::literal blit() const { return sat::literal(bvar(), is_negative()); }
|
||||||
|
constraint* get() const { if constexpr (is_owned) return m_constraint.get(); else return m_constraint; }
|
||||||
|
signed_constraint get_signed() const { return {get(), m_positive}; }
|
||||||
|
template <bool Owned = is_owned>
|
||||||
|
std::enable_if_t<Owned, constraint*> detach() { return m_constraint.detach(); }
|
||||||
|
|
||||||
|
explicit operator bool() const { return !!m_constraint; }
|
||||||
|
bool operator!() const { return !m_constraint; }
|
||||||
|
constraint* operator->() const { return get(); }
|
||||||
|
constraint& operator*() { return *m_constraint; }
|
||||||
|
constraint const& operator*() const { return *m_constraint; }
|
||||||
|
|
||||||
|
signed_constraint_base<is_owned>& operator=(std::nullptr_t) { m_constraint = nullptr; return *this; }
|
||||||
|
|
||||||
|
bool operator==(signed_constraint_base<is_owned> const& other) const {
|
||||||
|
return get() == other.get() && is_positive() == other.is_positive();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& display(std::ostream& out) const {
|
||||||
|
if (m_constraint)
|
||||||
|
return m_constraint->display(out, to_lbool(is_positive()));
|
||||||
|
else
|
||||||
|
return out << "<null>";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, constraint_literal_ref const& c) { return c.display(out); }
|
template <bool is_owned>
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, signed_constraint_base<is_owned> const& c) { return c.display(out); }
|
||||||
|
|
||||||
|
inline signed_constraint operator~(signed_constraint const& c) {
|
||||||
|
return {c.get(), !c.is_positive()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline scoped_signed_constraint operator~(scoped_signed_constraint&& c) {
|
||||||
|
return {c.detach(), !c.is_positive()};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Disjunction of constraints represented by boolean literals
|
/// Disjunction of constraints represented by boolean literals
|
||||||
class clause {
|
class clause {
|
||||||
friend class constraint_manager;
|
friend class constraint_manager;
|
||||||
|
|
||||||
unsigned m_ref_count = 0;
|
unsigned m_ref_count = 0; // TODO: remove refcount once we confirm it's not needed anymore
|
||||||
unsigned m_level;
|
unsigned m_level;
|
||||||
unsigned m_next_guess = 0; // next guess for enumerative backtracking
|
unsigned m_next_guess = 0; // next guess for enumerative backtracking
|
||||||
p_dependency_ref m_dep;
|
p_dependency_ref m_dep;
|
||||||
sat::literal_vector m_literals;
|
sat::literal_vector m_literals;
|
||||||
constraint_ref_vector m_new_constraints; // new constraints, temporarily owned by this clause
|
|
||||||
|
|
||||||
/* TODO: embed literals to save an indirection?
|
/* TODO: embed literals to save an indirection?
|
||||||
unsigned m_num_literals;
|
unsigned m_num_literals;
|
||||||
|
@ -296,8 +326,8 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
clause(unsigned lvl, p_dependency_ref d, sat::literal_vector literals, constraint_ref_vector new_constraints):
|
clause(unsigned lvl, p_dependency_ref d, sat::literal_vector literals):
|
||||||
m_level(lvl), m_dep(std::move(d)), m_literals(std::move(literals)), m_new_constraints(std::move(new_constraints))
|
m_level(lvl), m_dep(std::move(d)), m_literals(std::move(literals))
|
||||||
{
|
{
|
||||||
SASSERT(std::count(m_literals.begin(), m_literals.end(), sat::null_literal) == 0);
|
SASSERT(std::count(m_literals.begin(), m_literals.end(), sat::null_literal) == 0);
|
||||||
}
|
}
|
||||||
|
@ -306,8 +336,8 @@ namespace polysat {
|
||||||
void inc_ref() { m_ref_count++; }
|
void inc_ref() { m_ref_count++; }
|
||||||
void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (!m_ref_count) dealloc(this); }
|
void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (!m_ref_count) dealloc(this); }
|
||||||
|
|
||||||
static clause_ref from_unit(constraint_literal_ref c, p_dependency_ref d);
|
static clause_ref from_unit(signed_constraint c, p_dependency_ref d);
|
||||||
static clause_ref from_literals(unsigned lvl, p_dependency_ref d, sat::literal_vector literals, constraint_ref_vector new_constraints);
|
static clause_ref from_literals(unsigned lvl, p_dependency_ref d, sat::literal_vector literals);
|
||||||
|
|
||||||
// Resolve with 'other' upon 'var'.
|
// Resolve with 'other' upon 'var'.
|
||||||
bool resolve(sat::bool_var var, clause const& other);
|
bool resolve(sat::bool_var var, clause const& other);
|
||||||
|
@ -316,7 +346,6 @@ namespace polysat {
|
||||||
p_dependency* dep() const { return m_dep; }
|
p_dependency* dep() const { return m_dep; }
|
||||||
unsigned level() const { return m_level; }
|
unsigned level() const { return m_level; }
|
||||||
|
|
||||||
constraint_ref_vector const& new_constraints() const { return m_new_constraints; }
|
|
||||||
bool empty() const { return m_literals.empty(); }
|
bool empty() const { return m_literals.empty(); }
|
||||||
unsigned size() const { return m_literals.size(); }
|
unsigned size() const { return m_literals.size(); }
|
||||||
sat::literal operator[](unsigned idx) const { return m_literals[idx]; }
|
sat::literal operator[](unsigned idx) const { return m_literals[idx]; }
|
||||||
|
|
|
@ -121,7 +121,7 @@ namespace polysat {
|
||||||
|
|
||||||
|
|
||||||
/// Compute forbidden interval for equality constraint by considering it as p <=u 0 (or p >u 0 for disequality)
|
/// 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, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond)
|
bool eq_constraint::forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, scoped_signed_constraint& out_neg_cond)
|
||||||
{
|
{
|
||||||
// Current only works when degree(v) is at most one
|
// Current only works when degree(v) is at most one
|
||||||
unsigned const deg = p().degree(v);
|
unsigned const deg = p().degree(v);
|
||||||
|
|
|
@ -17,7 +17,7 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
class eq_constraint : public constraint {
|
class eq_constraint final : public constraint {
|
||||||
pdd m_poly;
|
pdd m_poly;
|
||||||
public:
|
public:
|
||||||
eq_constraint(constraint_manager& m, unsigned lvl, pdd const& p):
|
eq_constraint(constraint_manager& m, unsigned lvl, pdd const& p):
|
||||||
|
@ -31,7 +31,7 @@ namespace polysat {
|
||||||
bool is_currently_false(solver& s, bool is_positive) override;
|
bool is_currently_false(solver& s, bool is_positive) override;
|
||||||
bool is_currently_true(solver& s, bool is_positive) override;
|
bool is_currently_true(solver& s, bool is_positive) override;
|
||||||
void narrow(solver& s, bool is_positive) override;
|
void narrow(solver& s, bool is_positive) override;
|
||||||
bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond) override;
|
bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, scoped_signed_constraint& out_neg_cond) override;
|
||||||
inequality as_inequality(bool is_positive) const override;
|
inequality as_inequality(bool is_positive) const override;
|
||||||
unsigned hash() const override;
|
unsigned hash() const override;
|
||||||
bool operator==(constraint const& other) const override;
|
bool operator==(constraint const& other) const override;
|
||||||
|
|
|
@ -561,4 +561,7 @@ namespace polysat {
|
||||||
// return true;
|
// return true;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
void conflict_explainer::resolve()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ Author:
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "math/polysat/conflict_core.h"
|
||||||
#include "math/polysat/constraint.h"
|
#include "math/polysat/constraint.h"
|
||||||
#include "math/polysat/clause_builder.h"
|
#include "math/polysat/clause_builder.h"
|
||||||
#include "math/polysat/interval.h"
|
#include "math/polysat/interval.h"
|
||||||
|
@ -43,13 +44,14 @@ namespace polysat {
|
||||||
conflict_explainer(solver& s);
|
conflict_explainer(solver& s);
|
||||||
|
|
||||||
/** resolve conflict state against assignment to v */
|
/** resolve conflict state against assignment to v */
|
||||||
clause_ref resolve(pvar v, ptr_vector<constraint> const& cjust_v);
|
void resolve(pvar v, ptr_vector<constraint> const& cjust_v);
|
||||||
|
void resolve(sat::literal lit);
|
||||||
|
|
||||||
// TODO: move conflict resolution from solver into this class.
|
// TODO: move conflict resolution from solver into this class.
|
||||||
// we have a single public method as entry point to conflict resolution.
|
// we have a single public method as entry point to conflict resolution.
|
||||||
// what do we need to return?
|
// what do we need to return?
|
||||||
|
|
||||||
/** conflict resolution */
|
/** conflict resolution until first (relevant) decision */
|
||||||
void resolve();
|
void resolve();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ namespace polysat {
|
||||||
|
|
||||||
struct fi_record {
|
struct fi_record {
|
||||||
eval_interval interval;
|
eval_interval interval;
|
||||||
constraint_literal_ref neg_cond; // could be multiple constraints later
|
scoped_signed_constraint neg_cond; // could be multiple constraints later
|
||||||
constraint_literal src;
|
signed_constraint src;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,17 +64,17 @@ namespace polysat {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool forbidden_intervals::explain(solver& s, vector<constraint_literal> const& conflict, pvar v, clause_ref& out_lemma) {
|
bool forbidden_intervals::explain(solver& s, vector<signed_constraint> const& conflict, pvar v, clause_ref& out_lemma) {
|
||||||
|
|
||||||
// Extract forbidden intervals from conflicting constraints
|
// Extract forbidden intervals from conflicting constraints
|
||||||
vector<fi_record> records;
|
vector<fi_record> records;
|
||||||
bool has_full = false;
|
bool has_full = false;
|
||||||
rational longest_len;
|
rational longest_len;
|
||||||
unsigned longest_i = UINT_MAX;
|
unsigned longest_i = UINT_MAX;
|
||||||
for (constraint_literal c : conflict) {
|
for (signed_constraint c : conflict) {
|
||||||
LOG_H3("Computing forbidden interval for: " << c);
|
LOG_H3("Computing forbidden interval for: " << c);
|
||||||
eval_interval interval = eval_interval::full();
|
eval_interval interval = eval_interval::full();
|
||||||
constraint_literal_ref neg_cond;
|
scoped_signed_constraint neg_cond;
|
||||||
if (c->forbidden_interval(s, c.is_positive(), v, interval, neg_cond)) {
|
if (c->forbidden_interval(s, c.is_positive(), v, interval, neg_cond)) {
|
||||||
LOG("interval: " << interval);
|
LOG("interval: " << interval);
|
||||||
LOG("neg_cond: " << neg_cond);
|
LOG("neg_cond: " << neg_cond);
|
||||||
|
@ -102,7 +102,8 @@ namespace polysat {
|
||||||
auto& full_record = records.back();
|
auto& full_record = records.back();
|
||||||
SASSERT(full_record.interval.is_full());
|
SASSERT(full_record.interval.is_full());
|
||||||
clause_builder clause(s);
|
clause_builder clause(s);
|
||||||
clause.push_literal(~full_record.src.literal());
|
sat::literal src_lit = full_record.src.blit();
|
||||||
|
clause.push_literal(~src_lit);
|
||||||
if (full_record.neg_cond)
|
if (full_record.neg_cond)
|
||||||
clause.push_new_constraint(std::move(full_record.neg_cond));
|
clause.push_new_constraint(std::move(full_record.neg_cond));
|
||||||
out_lemma = clause.build();
|
out_lemma = clause.build();
|
||||||
|
@ -130,7 +131,7 @@ namespace polysat {
|
||||||
|
|
||||||
unsigned lemma_lvl = 0;
|
unsigned lemma_lvl = 0;
|
||||||
for (unsigned i : seq) {
|
for (unsigned i : seq) {
|
||||||
constraint_literal const& c = records[i].src;
|
signed_constraint const& c = records[i].src;
|
||||||
lemma_lvl = std::max(lemma_lvl, c->level());
|
lemma_lvl = std::max(lemma_lvl, c->level());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ namespace polysat {
|
||||||
clause_builder clause(s);
|
clause_builder clause(s);
|
||||||
// Add negation of src constraints as antecedents (may be resolved during backtracking)
|
// Add negation of src constraints as antecedents (may be resolved during backtracking)
|
||||||
for (unsigned const i : seq) {
|
for (unsigned const i : seq) {
|
||||||
sat::literal src_lit = records[i].src.literal();
|
sat::literal src_lit = records[i].src.blit();
|
||||||
clause.push_literal(~src_lit);
|
clause.push_literal(~src_lit);
|
||||||
}
|
}
|
||||||
// Add side conditions and interval constraints
|
// Add side conditions and interval constraints
|
||||||
|
@ -159,12 +160,12 @@ namespace polysat {
|
||||||
auto const& next_hi = records[next_i].interval.hi();
|
auto const& next_hi = records[next_i].interval.hi();
|
||||||
auto const& lhs = hi - next_lo;
|
auto const& lhs = hi - next_lo;
|
||||||
auto const& rhs = next_hi - next_lo;
|
auto const& rhs = next_hi - next_lo;
|
||||||
constraint_literal_ref c = ~s.m_constraints.ult(lemma_lvl, lhs, rhs);
|
scoped_signed_constraint c = ~s.m_constraints.ult(lemma_lvl, lhs, rhs);
|
||||||
LOG("constraint: " << c);
|
LOG("constraint: " << c);
|
||||||
clause.push_new_constraint(std::move(c));
|
clause.push_new_constraint(std::move(c));
|
||||||
// Side conditions
|
// Side conditions
|
||||||
// TODO: check whether the condition is subsumed by c? maybe at the end do a "lemma reduction" step, to try and reduce branching?
|
// TODO: check whether the condition is subsumed by c? maybe at the end do a "lemma reduction" step, to try and reduce branching?
|
||||||
constraint_literal_ref& neg_cond = records[i].neg_cond;
|
scoped_signed_constraint& neg_cond = records[i].neg_cond;
|
||||||
if (neg_cond)
|
if (neg_cond)
|
||||||
clause.push_new_constraint(std::move(neg_cond));
|
clause.push_new_constraint(std::move(neg_cond));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace polysat {
|
||||||
class forbidden_intervals {
|
class forbidden_intervals {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool explain(solver& s, vector<constraint_literal> const& conflict, pvar v, clause_ref& out_lemma);
|
static bool explain(solver& s, vector<signed_constraint> const& conflict, pvar v, clause_ref& out_lemma);
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,43 +111,44 @@ namespace polysat {
|
||||||
m_free_vars.del_var_eh(v);
|
m_free_vars.del_var_eh(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal_ref solver::mk_eq(pdd const& p) {
|
scoped_signed_constraint solver::mk_eq(pdd const& p) {
|
||||||
return m_constraints.eq(m_level, p);
|
return m_constraints.eq(m_level, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal_ref solver::mk_diseq(pdd const& p) {
|
scoped_signed_constraint solver::mk_diseq(pdd const& p) {
|
||||||
return ~m_constraints.eq(m_level, p);
|
return ~m_constraints.eq(m_level, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal_ref solver::mk_ule(pdd const& p, pdd const& q) {
|
scoped_signed_constraint solver::mk_ule(pdd const& p, pdd const& q) {
|
||||||
return m_constraints.ule(m_level, p, q);
|
return m_constraints.ule(m_level, p, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal_ref solver::mk_ult(pdd const& p, pdd const& q) {
|
scoped_signed_constraint solver::mk_ult(pdd const& p, pdd const& q) {
|
||||||
return m_constraints.ult(m_level, p, q);
|
return m_constraints.ult(m_level, p, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal_ref solver::mk_sle(pdd const& p, pdd const& q) {
|
scoped_signed_constraint solver::mk_sle(pdd const& p, pdd const& q) {
|
||||||
return m_constraints.sle(m_level, p, q);
|
return m_constraints.sle(m_level, p, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint_literal_ref solver::mk_slt(pdd const& p, pdd const& q) {
|
scoped_signed_constraint solver::mk_slt(pdd const& p, pdd const& q) {
|
||||||
return m_constraints.slt(m_level, p, q);
|
return m_constraints.slt(m_level, p, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::new_constraint(constraint_literal_ref cl, unsigned dep, bool activate) {
|
void solver::new_constraint(scoped_signed_constraint sc, unsigned dep, bool activate) {
|
||||||
VERIFY(at_base_level());
|
VERIFY(at_base_level());
|
||||||
SASSERT(cl);
|
SASSERT(sc);
|
||||||
SASSERT(activate || dep != null_dependency); // if we don't activate the constraint, we need the dependency to access it again later.
|
SASSERT(activate || dep != null_dependency); // if we don't activate the constraint, we need the dependency to access it again later.
|
||||||
constraint_literal c = cl.get();
|
signed_constraint c = sc.get_signed();
|
||||||
clause* unit = m_constraints.store(clause::from_unit(std::move(cl), mk_dep_ref(dep)));
|
m_constraints.store(sc.detach());
|
||||||
|
clause* unit = m_constraints.store(clause::from_unit(c, mk_dep_ref(dep)));
|
||||||
c->set_unit_clause(unit);
|
c->set_unit_clause(unit);
|
||||||
if (dep != null_dependency)
|
if (dep != null_dependency)
|
||||||
m_constraints.register_external(c.get_constraint());
|
m_constraints.register_external(c.get());
|
||||||
LOG("New constraint: " << c);
|
LOG("New constraint: " << c);
|
||||||
m_original.push_back(c);
|
m_original.push_back(c);
|
||||||
#if ENABLE_LINEAR_SOLVER
|
#if ENABLE_LINEAR_SOLVER
|
||||||
m_linear_solver.new_constraint(*c.constraint());
|
m_linear_solver.new_constraint(*c.get());
|
||||||
#endif
|
#endif
|
||||||
if (activate && !is_conflict())
|
if (activate && !is_conflict())
|
||||||
activate_constraint_base(c);
|
activate_constraint_base(c);
|
||||||
|
@ -164,8 +165,8 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
if (is_conflict())
|
if (is_conflict())
|
||||||
return;
|
return;
|
||||||
// TODO: this is wrong. (e.g., if the external constraint was negative) we need to store constraint_literals
|
// TODO: this is wrong. (e.g., if the external constraint was negative) we need to store signed_constraints
|
||||||
constraint_literal cl{c, is_true};
|
signed_constraint cl{c, is_true};
|
||||||
activate_constraint_base(cl);
|
activate_constraint_base(cl);
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@ -202,7 +203,7 @@ namespace polysat {
|
||||||
|
|
||||||
void solver::propagate(sat::literal lit) {
|
void solver::propagate(sat::literal lit) {
|
||||||
LOG_H2("Propagate boolean literal " << lit);
|
LOG_H2("Propagate boolean literal " << lit);
|
||||||
constraint_literal c = m_constraints.lookup(lit);
|
signed_constraint c = m_constraints.lookup(lit);
|
||||||
(void)c;
|
(void)c;
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
// c->narrow(*this);
|
// c->narrow(*this);
|
||||||
|
@ -220,7 +221,7 @@ namespace polysat {
|
||||||
wlist.shrink(j);
|
wlist.shrink(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::propagate(pvar v, rational const& val, constraint_literal c) {
|
void solver::propagate(pvar v, rational const& val, signed_constraint c) {
|
||||||
LOG("Propagation: " << assignment_pp(*this, v, val) << ", due to " << c);
|
LOG("Propagation: " << assignment_pp(*this, v, val) << ", due to " << c);
|
||||||
if (m_viable.is_viable(v, val)) {
|
if (m_viable.is_viable(v, val)) {
|
||||||
m_free_vars.del_var_eh(v);
|
m_free_vars.del_var_eh(v);
|
||||||
|
@ -276,7 +277,7 @@ namespace polysat {
|
||||||
case trail_instr_t::assign_bool_i: {
|
case trail_instr_t::assign_bool_i: {
|
||||||
sat::literal lit = m_search.back().lit();
|
sat::literal lit = m_search.back().lit();
|
||||||
LOG_V("Undo assign_bool_i: " << lit);
|
LOG_V("Undo assign_bool_i: " << lit);
|
||||||
constraint_literal c = m_constraints.lookup(lit);
|
signed_constraint c = m_constraints.lookup(lit);
|
||||||
deactivate_constraint(c);
|
deactivate_constraint(c);
|
||||||
m_bvars.unassign(lit);
|
m_bvars.unassign(lit);
|
||||||
m_search.pop();
|
m_search.pop();
|
||||||
|
@ -300,7 +301,7 @@ namespace polysat {
|
||||||
SASSERT(m_level == target_level);
|
SASSERT(m_level == target_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::pop_constraints(constraint_literals& cs) {
|
void solver::pop_constraints(signed_constraints& cs) {
|
||||||
VERIFY(invariant(cs));
|
VERIFY(invariant(cs));
|
||||||
while (!cs.empty() && cs.back()->level() > m_level) {
|
while (!cs.empty() && cs.back()->level() > m_level) {
|
||||||
deactivate_constraint(cs.back());
|
deactivate_constraint(cs.back());
|
||||||
|
@ -308,30 +309,30 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::add_watch(constraint_literal c) {
|
void solver::add_watch(signed_constraint c) {
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
auto const& vars = c.get_constraint()->vars();
|
auto const& vars = c->vars();
|
||||||
if (vars.size() > 0)
|
if (vars.size() > 0)
|
||||||
add_watch(c, vars[0]);
|
add_watch(c, vars[0]);
|
||||||
if (vars.size() > 1)
|
if (vars.size() > 1)
|
||||||
add_watch(c, vars[1]);
|
add_watch(c, vars[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::add_watch(constraint_literal c, pvar v) {
|
void solver::add_watch(signed_constraint c, pvar v) {
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
LOG("Watching v" << v << " in constraint " << c);
|
LOG("Watching v" << v << " in constraint " << c);
|
||||||
m_watch[v].push_back(c);
|
m_watch[v].push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::erase_watch(constraint_literal c) {
|
void solver::erase_watch(signed_constraint c) {
|
||||||
auto const& vars = c.get_constraint()->vars();
|
auto const& vars = c->vars();
|
||||||
if (vars.size() > 0)
|
if (vars.size() > 0)
|
||||||
erase_watch(vars[0], c);
|
erase_watch(vars[0], c);
|
||||||
if (vars.size() > 1)
|
if (vars.size() > 1)
|
||||||
erase_watch(vars[1], c);
|
erase_watch(vars[1], c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::erase_watch(pvar v, constraint_literal c) {
|
void solver::erase_watch(pvar v, signed_constraint c) {
|
||||||
if (v == null_var)
|
if (v == null_var)
|
||||||
return;
|
return;
|
||||||
auto& wlist = m_watch[v];
|
auto& wlist = m_watch[v];
|
||||||
|
@ -392,7 +393,7 @@ namespace polysat {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::set_conflict(constraint_literal c) {
|
void solver::set_conflict(signed_constraint c) {
|
||||||
m_conflict.set(c);
|
m_conflict.set(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,7 +700,7 @@ namespace polysat {
|
||||||
add_lemma(std::move(lemma));
|
add_lemma(std::move(lemma));
|
||||||
if (cl->size() == 1) {
|
if (cl->size() == 1) {
|
||||||
sat::literal lit = cl->literals()[0];
|
sat::literal lit = cl->literals()[0];
|
||||||
constraint_literal c = m_constraints.lookup(lit);
|
signed_constraint c = m_constraints.lookup(lit);
|
||||||
c->set_unit_clause(cl);
|
c->set_unit_clause(cl);
|
||||||
push_cjust(v, c);
|
push_cjust(v, c);
|
||||||
activate_constraint_base(c);
|
activate_constraint_base(c);
|
||||||
|
@ -707,7 +708,7 @@ namespace polysat {
|
||||||
else {
|
else {
|
||||||
sat::literal lit = decide_bool(*cl);
|
sat::literal lit = decide_bool(*cl);
|
||||||
SASSERT(lit != sat::null_literal);
|
SASSERT(lit != sat::null_literal);
|
||||||
constraint_literal c = m_constraints.lookup(lit);
|
signed_constraint c = m_constraints.lookup(lit);
|
||||||
push_cjust(v, c);
|
push_cjust(v, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -724,7 +725,7 @@ namespace polysat {
|
||||||
if (m_bvars.value(lit) == l_false) // already assigned => cannot decide on this (comes from either lemma LHS or previously decided literals that are now changed to propagation)
|
if (m_bvars.value(lit) == l_false) // already assigned => cannot decide on this (comes from either lemma LHS or previously decided literals that are now changed to propagation)
|
||||||
return false;
|
return false;
|
||||||
SASSERT(m_bvars.value(lit) != l_true); // cannot happen in a valid lemma
|
SASSERT(m_bvars.value(lit) != l_true); // cannot happen in a valid lemma
|
||||||
constraint_literal c = m_constraints.lookup(lit);
|
signed_constraint c = m_constraints.lookup(lit);
|
||||||
SASSERT(!c.is_currently_true(*this)); // should not happen in a valid lemma
|
SASSERT(!c.is_currently_true(*this)); // should not happen in a valid lemma
|
||||||
return !c.is_currently_false(*this);
|
return !c.is_currently_false(*this);
|
||||||
};
|
};
|
||||||
|
@ -905,7 +906,7 @@ namespace polysat {
|
||||||
SASSERT(reason);
|
SASSERT(reason);
|
||||||
if (reason->literals().size() == 1) {
|
if (reason->literals().size() == 1) {
|
||||||
SASSERT(reason->literals()[0] == lit);
|
SASSERT(reason->literals()[0] == lit);
|
||||||
constraint_literal c = m_constraints.lookup(lit);
|
signed_constraint c = m_constraints.lookup(lit);
|
||||||
// m_redundant.push_back(c);
|
// m_redundant.push_back(c);
|
||||||
activate_constraint_base(c);
|
activate_constraint_base(c);
|
||||||
}
|
}
|
||||||
|
@ -921,18 +922,18 @@ namespace polysat {
|
||||||
m_trail.push_back(trail_instr_t::assign_bool_i);
|
m_trail.push_back(trail_instr_t::assign_bool_i);
|
||||||
m_search.push_boolean(lit);
|
m_search.push_boolean(lit);
|
||||||
|
|
||||||
constraint_literal c = m_constraints.lookup(lit);
|
signed_constraint c = m_constraints.lookup(lit);
|
||||||
activate_constraint(c);
|
activate_constraint(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Activate a constraint at the base level.
|
/// Activate a constraint at the base level.
|
||||||
/// Used for external unit constraints and unit consequences.
|
/// Used for external unit constraints and unit consequences.
|
||||||
void solver::activate_constraint_base(constraint_literal c) {
|
void solver::activate_constraint_base(signed_constraint c) {
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
LOG("\n" << *this);
|
LOG("\n" << *this);
|
||||||
// c must be in m_original or m_redundant so it can be deactivated properly when popping the base level
|
// c must be in m_original or m_redundant so it can be deactivated properly when popping the base level
|
||||||
SASSERT_EQ(std::count(m_original.begin(), m_original.end(), c) + std::count(m_redundant.begin(), m_redundant.end(), c), 1);
|
SASSERT_EQ(std::count(m_original.begin(), m_original.end(), c) + std::count(m_redundant.begin(), m_redundant.end(), c), 1);
|
||||||
assign_bool_core(c.literal(), nullptr, nullptr);
|
assign_bool_core(c.blit(), nullptr, nullptr);
|
||||||
activate_constraint(c);
|
activate_constraint(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -948,19 +949,19 @@ namespace polysat {
|
||||||
* constraints activated within the linear solver are de-activated when the linear
|
* constraints activated within the linear solver are de-activated when the linear
|
||||||
* solver is popped.
|
* solver is popped.
|
||||||
*/
|
*/
|
||||||
void solver::activate_constraint(constraint_literal c) {
|
void solver::activate_constraint(signed_constraint c) {
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
LOG("Activating constraint: " << c);
|
LOG("Activating constraint: " << c);
|
||||||
SASSERT(m_bvars.value(c.literal()) == l_true);
|
SASSERT(m_bvars.value(c.blit()) == l_true);
|
||||||
add_watch(c);
|
add_watch(c);
|
||||||
c.narrow(*this);
|
c.narrow(*this);
|
||||||
#if ENABLE_LINEAR_SOLVER
|
#if ENABLE_LINEAR_SOLVER
|
||||||
m_linear_solver.activate_constraint(c.is_positive(), c.constraint()); // TODO: linear solver should probably take a constraint_literal
|
m_linear_solver.activate_constraint(c.is_positive(), c.get()); // TODO: linear solver should probably take a signed_constraint
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deactivate constraint
|
/// Deactivate constraint
|
||||||
void solver::deactivate_constraint(constraint_literal c) {
|
void solver::deactivate_constraint(signed_constraint c) {
|
||||||
LOG("Deactivating constraint: " << c);
|
LOG("Deactivating constraint: " << c);
|
||||||
erase_watch(c);
|
erase_watch(c);
|
||||||
}
|
}
|
||||||
|
@ -988,12 +989,12 @@ namespace polysat {
|
||||||
clause* cl = m_constraints.store(std::move(lemma));
|
clause* cl = m_constraints.store(std::move(lemma));
|
||||||
m_redundant_clauses.push_back(cl);
|
m_redundant_clauses.push_back(cl);
|
||||||
if (cl->size() == 1) {
|
if (cl->size() == 1) {
|
||||||
constraint_literal c = m_constraints.lookup(cl->literals()[0]);
|
signed_constraint c = m_constraints.lookup(cl->literals()[0]);
|
||||||
insert_constraint(m_redundant, c);
|
insert_constraint(m_redundant, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::insert_constraint(constraint_literals& cs, constraint_literal c) {
|
void solver::insert_constraint(signed_constraints& cs, signed_constraint c) {
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
LOG_V("INSERTING: " << c);
|
LOG_V("INSERTING: " << c);
|
||||||
cs.push_back(c);
|
cs.push_back(c);
|
||||||
|
@ -1117,7 +1118,7 @@ namespace polysat {
|
||||||
/**
|
/**
|
||||||
* constraints are sorted by levels so they can be removed when levels are popped.
|
* constraints are sorted by levels so they can be removed when levels are popped.
|
||||||
*/
|
*/
|
||||||
bool solver::invariant(constraint_literals const& cs) {
|
bool solver::invariant(signed_constraints const& cs) {
|
||||||
unsigned sz = cs.size();
|
unsigned sz = cs.size();
|
||||||
for (unsigned i = 0; i + 1 < sz; ++i)
|
for (unsigned i = 0; i + 1 < sz; ++i)
|
||||||
VERIFY(cs[i]->level() <= cs[i + 1]->level());
|
VERIFY(cs[i]->level() <= cs[i + 1]->level());
|
||||||
|
@ -1128,7 +1129,7 @@ namespace polysat {
|
||||||
* Check that two variables of each constraint are watched.
|
* Check that two variables of each constraint are watched.
|
||||||
*/
|
*/
|
||||||
bool solver::wlist_invariant() {
|
bool solver::wlist_invariant() {
|
||||||
constraint_literals cs;
|
signed_constraints cs;
|
||||||
cs.append(m_original.size(), m_original.data());
|
cs.append(m_original.size(), m_original.data());
|
||||||
cs.append(m_redundant.size(), m_redundant.data());
|
cs.append(m_redundant.size(), m_redundant.data());
|
||||||
for (auto c : cs) {
|
for (auto c : cs) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace polysat {
|
||||||
friend class assignments_pp;
|
friend class assignments_pp;
|
||||||
|
|
||||||
typedef ptr_vector<constraint> constraints;
|
typedef ptr_vector<constraint> constraints;
|
||||||
typedef vector<constraint_literal> constraint_literals;
|
typedef vector<signed_constraint> signed_constraints;
|
||||||
|
|
||||||
reslimit& m_lim;
|
reslimit& m_lim;
|
||||||
params_ref m_params;
|
params_ref m_params;
|
||||||
|
@ -84,8 +84,8 @@ namespace polysat {
|
||||||
|
|
||||||
// Per constraint state
|
// Per constraint state
|
||||||
constraint_manager m_constraints;
|
constraint_manager m_constraints;
|
||||||
constraint_literals m_original;
|
signed_constraints m_original;
|
||||||
constraint_literals m_redundant;
|
signed_constraints m_redundant;
|
||||||
ptr_vector<clause> m_redundant_clauses;
|
ptr_vector<clause> m_redundant_clauses;
|
||||||
|
|
||||||
svector<sat::bool_var> m_disjunctive_lemma;
|
svector<sat::bool_var> m_disjunctive_lemma;
|
||||||
|
@ -93,8 +93,8 @@ namespace polysat {
|
||||||
// Per variable information
|
// Per variable information
|
||||||
vector<rational> m_value; // assigned value
|
vector<rational> m_value; // assigned value
|
||||||
vector<justification> m_justification; // justification for variable assignment
|
vector<justification> m_justification; // justification for variable assignment
|
||||||
vector<constraint_literals> m_cjust; // constraints justifying variable range.
|
vector<signed_constraints> m_cjust; // constraints justifying variable range.
|
||||||
vector<constraint_literals> m_watch; // watch list datastructure into constraints.
|
vector<signed_constraints> m_watch; // watch list datastructure into constraints.
|
||||||
unsigned_vector m_activity;
|
unsigned_vector m_activity;
|
||||||
vector<pdd> m_vars;
|
vector<pdd> m_vars;
|
||||||
unsigned_vector m_size; // store size of variables.
|
unsigned_vector m_size; // store size of variables.
|
||||||
|
@ -128,7 +128,7 @@ namespace polysat {
|
||||||
m_qhead_trail.pop_back();
|
m_qhead_trail.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_cjust(pvar v, constraint_literal c) {
|
void push_cjust(pvar v, signed_constraint c) {
|
||||||
if (m_cjust[v].contains(c)) // TODO: better check (flag on constraint?)
|
if (m_cjust[v].contains(c)) // TODO: better check (flag on constraint?)
|
||||||
return;
|
return;
|
||||||
LOG_V("cjust[v" << v << "] += " << c);
|
LOG_V("cjust[v" << v << "] += " << c);
|
||||||
|
@ -151,14 +151,14 @@ namespace polysat {
|
||||||
|
|
||||||
void push_level();
|
void push_level();
|
||||||
void pop_levels(unsigned num_levels);
|
void pop_levels(unsigned num_levels);
|
||||||
void pop_constraints(constraint_literals& cs);
|
void pop_constraints(signed_constraints& cs);
|
||||||
|
|
||||||
void assign_bool_backtrackable(sat::literal lit, clause* reason, clause* lemma);
|
void assign_bool_backtrackable(sat::literal lit, clause* reason, clause* lemma);
|
||||||
void activate_constraint_base(constraint_literal c);
|
void activate_constraint_base(signed_constraint c);
|
||||||
void assign_bool_core(sat::literal lit, clause* reason, clause* lemma);
|
void assign_bool_core(sat::literal lit, clause* reason, clause* lemma);
|
||||||
// void assign_bool_base(sat::literal lit);
|
// void assign_bool_base(sat::literal lit);
|
||||||
void activate_constraint(constraint_literal c);
|
void activate_constraint(signed_constraint c);
|
||||||
void deactivate_constraint(constraint_literal c);
|
void deactivate_constraint(signed_constraint c);
|
||||||
sat::literal decide_bool(clause& lemma);
|
sat::literal decide_bool(clause& lemma);
|
||||||
void decide_bool(sat::literal lit, clause& lemma);
|
void decide_bool(sat::literal lit, clause& lemma);
|
||||||
void propagate_bool(sat::literal lit, clause* reason);
|
void propagate_bool(sat::literal lit, clause* reason);
|
||||||
|
@ -171,13 +171,13 @@ namespace polysat {
|
||||||
|
|
||||||
void propagate(sat::literal lit);
|
void propagate(sat::literal lit);
|
||||||
void propagate(pvar v);
|
void propagate(pvar v);
|
||||||
void propagate(pvar v, rational const& val, constraint_literal c);
|
void propagate(pvar v, rational const& val, signed_constraint c);
|
||||||
void erase_watch(pvar v, constraint_literal c);
|
void erase_watch(pvar v, signed_constraint c);
|
||||||
void erase_watch(constraint_literal c);
|
void erase_watch(signed_constraint c);
|
||||||
void add_watch(constraint_literal c);
|
void add_watch(signed_constraint c);
|
||||||
void add_watch(constraint_literal c, pvar v);
|
void add_watch(signed_constraint c, pvar v);
|
||||||
|
|
||||||
void set_conflict(constraint_literal c);
|
void set_conflict(signed_constraint c);
|
||||||
void set_conflict(pvar v);
|
void set_conflict(pvar v);
|
||||||
|
|
||||||
unsigned_vector m_marks;
|
unsigned_vector m_marks;
|
||||||
|
@ -219,17 +219,17 @@ namespace polysat {
|
||||||
void backjump(unsigned new_level);
|
void backjump(unsigned new_level);
|
||||||
void add_lemma(clause_ref lemma);
|
void add_lemma(clause_ref lemma);
|
||||||
|
|
||||||
constraint_literal_ref mk_eq(pdd const& p);
|
scoped_signed_constraint mk_eq(pdd const& p);
|
||||||
constraint_literal_ref mk_diseq(pdd const& p);
|
scoped_signed_constraint mk_diseq(pdd const& p);
|
||||||
constraint_literal_ref mk_ule(pdd const& p, pdd const& q);
|
scoped_signed_constraint mk_ule(pdd const& p, pdd const& q);
|
||||||
constraint_literal_ref mk_ult(pdd const& p, pdd const& q);
|
scoped_signed_constraint mk_ult(pdd const& p, pdd const& q);
|
||||||
constraint_literal_ref mk_sle(pdd const& p, pdd const& q);
|
scoped_signed_constraint mk_sle(pdd const& p, pdd const& q);
|
||||||
constraint_literal_ref mk_slt(pdd const& p, pdd const& q);
|
scoped_signed_constraint mk_slt(pdd const& p, pdd const& q);
|
||||||
void new_constraint(constraint_literal_ref c, unsigned dep, bool activate);
|
void new_constraint(scoped_signed_constraint c, unsigned dep, bool activate);
|
||||||
static void insert_constraint(constraint_literals& cs, constraint_literal c);
|
static void insert_constraint(signed_constraints& cs, signed_constraint c);
|
||||||
|
|
||||||
bool invariant();
|
bool invariant();
|
||||||
static bool invariant(constraint_literals const& cs);
|
static bool invariant(signed_constraints const& cs);
|
||||||
bool wlist_invariant();
|
bool wlist_invariant();
|
||||||
bool verify_sat();
|
bool verify_sat();
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ namespace polysat {
|
||||||
return p.is_val() && q.is_val() && p.val() > q.val();
|
return p.is_val() && q.is_val() && p.val() > q.val();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ule_constraint::forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond)
|
bool ule_constraint::forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, scoped_signed_constraint& out_neg_cond)
|
||||||
{
|
{
|
||||||
// Current only works when degree(v) is at most one on both sides
|
// Current only works when degree(v) is at most one on both sides
|
||||||
unsigned const deg1 = lhs().degree(v);
|
unsigned const deg1 = lhs().degree(v);
|
||||||
|
|
|
@ -17,7 +17,7 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
class ule_constraint : public constraint {
|
class ule_constraint final : public constraint {
|
||||||
pdd m_lhs;
|
pdd m_lhs;
|
||||||
pdd m_rhs;
|
pdd m_rhs;
|
||||||
public:
|
public:
|
||||||
|
@ -37,7 +37,7 @@ namespace polysat {
|
||||||
bool is_currently_false(solver& s, bool is_positive) override;
|
bool is_currently_false(solver& s, bool is_positive) override;
|
||||||
bool is_currently_true(solver& s, bool is_positive) override;
|
bool is_currently_true(solver& s, bool is_positive) override;
|
||||||
void narrow(solver& s, bool is_positive) override;
|
void narrow(solver& s, bool is_positive) override;
|
||||||
bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond) override;
|
bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, scoped_signed_constraint& out_neg_cond) override;
|
||||||
inequality as_inequality(bool is_positive) const override;
|
inequality as_inequality(bool is_positive) const override;
|
||||||
unsigned hash() const override;
|
unsigned hash() const override;
|
||||||
bool operator==(constraint const& other) const override;
|
bool operator==(constraint const& other) const override;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue