diff --git a/src/math/polysat/clause_builder.cpp b/src/math/polysat/clause_builder.cpp index 2135e82a7..9f05296ef 100644 --- a/src/math/polysat/clause_builder.cpp +++ b/src/math/polysat/clause_builder.cpp @@ -31,7 +31,7 @@ namespace polysat { 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. - 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; SASSERT(empty()); return cl; @@ -52,13 +52,12 @@ namespace polysat { m_literals.push_back(lit); } - void clause_builder::push_new_constraint(constraint_literal_ref c) { - // TODO: assert that constraint is new (not 'inserted' into manager yet) + void clause_builder::push_new_constraint(scoped_signed_constraint c) { SASSERT(c); - if (c.get().is_always_false()) + if (c.is_always_false()) return; 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()); } diff --git a/src/math/polysat/clause_builder.h b/src/math/polysat/clause_builder.h index dfa65a21d..ba1de674d 100644 --- a/src/math/polysat/clause_builder.h +++ b/src/math/polysat/clause_builder.h @@ -23,10 +23,11 @@ Notes: namespace polysat { + // TODO: this is now incorporated in conflict_core class clause_builder { solver& m_solver; sat::literal_vector m_literals; - constraint_ref_vector m_new_constraints; + scoped_ptr_vector m_new_constraints; p_dependency_ref m_dep; unsigned m_level = 0; @@ -46,7 +47,7 @@ namespace polysat { void push_literal(sat::literal lit); /// 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); }; } diff --git a/src/math/polysat/conflict_core.cpp b/src/math/polysat/conflict_core.cpp index 37ceff69e..5dd8140e8 100644 --- a/src/math/polysat/conflict_core.cpp +++ b/src/math/polysat/conflict_core.cpp @@ -39,17 +39,18 @@ namespace polysat { m_needs_model = false; } - void conflict_core::set(constraint_literal c) { + void conflict_core::set(signed_constraint c) { LOG("Conflict: " << c); SASSERT(empty()); m_constraints.push_back(std::move(c)); m_needs_model = true; } - void conflict_core::set(pvar v, vector const& cjust_v) { + void conflict_core::set(pvar v, vector const& cjust_v) { LOG("Conflict for v" << v << ": " << cjust_v); SASSERT(empty()); NOT_IMPLEMENTED_YET(); + m_conflict_var = v; m_constraints.append(cjust_v); if (cjust_v.empty()) m_constraints.push_back({}); diff --git a/src/math/polysat/conflict_core.h b/src/math/polysat/conflict_core.h index 4b081297b..8692c4b37 100644 --- a/src/math/polysat/conflict_core.h +++ b/src/math/polysat/conflict_core.h @@ -18,8 +18,14 @@ namespace polysat { /** Conflict state, represented as core (~negation of clause). */ class conflict_core { - // TODO: core needs to own the constraint literals... - vector m_constraints; + vector m_constraints; + + /** Storage for new constraints that may not yet have a boolean variable yet */ + ptr_vector 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.) */ 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. public: + ~conflict_core() { + gc(); + } - vector const& constraints() const { return m_constraints; } + vector const& constraints() const { return m_constraints; } bool needs_model() const { return m_needs_model; } bool empty() const { @@ -39,15 +48,24 @@ namespace polysat { void reset() { m_constraints.reset(); m_needs_model = false; + gc(); SASSERT(empty()); } /** for bailing out with a conflict at the base level */ void set(std::nullptr_t); /** 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 */ - void set(pvar v, vector const& cjust_v); + void set(pvar v, vector 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; }; diff --git a/src/math/polysat/constraint.cpp b/src/math/polysat/constraint.cpp index 0a6b668bd..98c813a1a 100644 --- a/src/math/polysat/constraint.cpp +++ b/src/math/polysat/constraint.cpp @@ -21,31 +21,47 @@ Author: namespace polysat { - constraint* constraint_manager::store(constraint_ref c) { - LOG_V("Store constraint: " << show_deref(c)); - SASSERT(c); - SASSERT(c->bvar() != sat::null_bool_var); - SASSERT(get_bv2c(c->bvar()) == c.get()); - while (m_constraints.size() <= c->level()) - m_constraints.push_back({}); - constraint* pc = c.get(); - m_constraints[c->level()].push_back(std::move(c)); - return pc; + static_assert(!std::is_copy_assignable_v); + static_assert(!std::is_copy_constructible_v); + + void constraint_manager::assign_bv2c(sat::bool_var bv, constraint* c) { + SASSERT_EQ(get_bv2c(bv), nullptr); + SASSERT(!c->has_bvar()); + SASSERT(!m_constraint_table.contains(c)); + c->m_bvar = bv; + m_bv2constraint.setx(bv, c, nullptr); + m_constraint_table.insert(c); } - clause* constraint_manager::store(clause_ref cl) { - LOG_V("Store clause: " << show_deref(cl)); - SASSERT(cl); - // Insert new constraints - for (constraint* c : cl->m_new_constraints) - store(c); - cl->m_new_constraints = {}; // free vector memory - // Insert clause + void constraint_manager::erase_bv2c(constraint* c) { + SASSERT(c->has_bvar()); + SASSERT_EQ(get_bv2c(c->bvar()), c); + SASSERT(m_constraint_table.contains(c)); + m_bv2constraint[c->bvar()] = nullptr; + m_constraint_table.remove(c); + c->m_bvar = sat::null_bool_var; + } + + 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()) m_clauses.push_back({}); - clause* pcl = cl.get(); - m_clauses[cl->level()].push_back(std::move(cl)); - return pcl; + m_clauses[cl->level()].push_back(std::move(cl_ref)); + return cl; } void constraint_manager::register_external(constraint* c) { @@ -60,18 +76,15 @@ namespace polysat { // Release constraints at the given level and above. void constraint_manager::release_level(unsigned lvl) { for (unsigned l = m_constraints.size(); l-- > lvl; ) { - for (auto const& c : m_constraints[l]) { - LOG_V("Removing 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); - } + for (auto* c : m_constraints[l]) { + LOG_V("Destroying constraint: " << show_deref(c)); auto* d = c->unit_dep(); if (d && d->is_leaf()) { unsigned const dep = d->leaf_value(); SASSERT(m_external_constraints.contains(dep)); m_external_constraints.remove(dep); } + erase_bv2c(c); } m_constraints[l].reset(); } @@ -93,36 +106,34 @@ namespace polysat { return get_bv2c(var); } - constraint_literal constraint_manager::lookup(sat::literal lit) const { - return {lit, lookup(lit.var())}; + signed_constraint constraint_manager::lookup(sat::literal lit) const { + return {lookup(lit.var()), lit}; } - eq_constraint& constraint::to_eq() { - return *dynamic_cast(this); + /** Look up constraint among stored constraints. */ + 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 { - return *dynamic_cast(this); + scoped_signed_constraint constraint_manager::eq(unsigned lvl, pdd const& p) { + return {dedup(alloc(eq_constraint, *this, lvl, p)), true}; } - ule_constraint& constraint::to_ule() { - return *dynamic_cast(this); + scoped_signed_constraint constraint_manager::ule(unsigned lvl, pdd const& a, pdd const& b) { + return {dedup(alloc(ule_constraint, *this, lvl, a, b)), true}; } - ule_constraint const& constraint::to_ule() const { - return *dynamic_cast(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) { + scoped_signed_constraint constraint_manager::ult(unsigned lvl, pdd const& a, pdd const& b) { // a < b <=> !(b <= a) return ~ule(lvl, b, a); } @@ -143,16 +154,32 @@ namespace polysat { // // 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); 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); return ult(lvl, a + shift, b + shift); } + eq_constraint& constraint::to_eq() { + return *dynamic_cast(this); + } + + eq_constraint const& constraint::to_eq() const { + return *dynamic_cast(this); + } + + ule_constraint& constraint::to_ule() { + return *dynamic_cast(this); + } + + ule_constraint const& constraint::to_ule() const { + return *dynamic_cast(this); + } + std::ostream& constraint::display_extra(std::ostream& out, lbool status) const { out << " @" << level() << " (b" << bvar() << ")"; (void)status; @@ -190,41 +217,28 @@ namespace polysat { narrow(s, is_positive); } - std::ostream &constraint_literal::display(std::ostream &out) const { - if (*this) - return out << m_literal << "{ " << *m_constraint << " }"; - else - return out << ""; - } - - 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); + clause_ref clause::from_unit(signed_constraint c, p_dependency_ref d) { + SASSERT(c->has_bvar()); unsigned const lvl = c->level(); sat::literal_vector lits; - lits.push_back(c.literal()); - constraint_ref_vector cs; - cs.push_back(c.detach()); - return clause::from_literals(lvl, std::move(d), std::move(lits), std::move(cs)); + lits.push_back(c.blit()); + return clause::from_literals(lvl, std::move(d), std::move(lits)); } - clause_ref clause::from_literals(unsigned lvl, p_dependency_ref d, sat::literal_vector literals, constraint_ref_vector constraints) { - return alloc(clause, lvl, std::move(d), std::move(literals), std::move(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)); } bool clause::is_always_false(solver& s) const { 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(); }); } bool clause::is_currently_false(solver& s) const { 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); }); } @@ -240,8 +254,6 @@ namespace polysat { 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 (auto lit : m_literals) if (lit.var() != var) diff --git a/src/math/polysat/constraint.h b/src/math/polysat/constraint.h index 34c58701f..b2805dd28 100644 --- a/src/math/polysat/constraint.h +++ b/src/math/polysat/constraint.h @@ -19,54 +19,65 @@ Author: #include "util/map.h" #include "util/ref.h" #include "util/ref_vector.h" +#include namespace polysat { enum ckind_t { eq_t, ule_t }; enum csign_t : bool { neg_t = false, pos_t = true }; - class constraint_literal; - class constraint_literal_ref; class constraint; - class constraint_manager; - class clause; - class scoped_clause; class eq_constraint; class ule_constraint; - using constraint_ref = ref; - using constraint_ref_vector = sref_vector; - using constraint_literal_ref_vector = vector; + + class scoped_constraint_ptr; + + template + class signed_constraint_base; + using signed_constraint = signed_constraint_base; + using scoped_signed_constraint = signed_constraint_base; + + class clause; using clause_ref = ref; using clause_ref_vector = sref_vector; + using constraint_table = ptr_hashtable, deref_eq>; + // Manage constraint lifetime, deduplication, and connection to boolean variables/literals. class constraint_manager { friend class constraint; bool_var_manager& m_bvars; - // Association to boolean variables - ptr_vector 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); } - void erase_bv2c(sat::bool_var bv) { m_bv2constraint[bv] = nullptr; } - constraint* get_bv2c(sat::bool_var bv) const { return m_bv2constraint.get(bv, nullptr); } + // Constraints indexed by their boolean variable + ptr_vector m_bv2constraint; + // Constraints that have a boolean variable, for deduplication + constraint_table m_constraint_table; - // Constraint storage per level; should be destructed before m_bv2constraint because constraint's destructor calls erase_bv2c - vector> m_constraints; + // Constraint storage per level + vector> m_constraints; vector> m_clauses; // Association to external dependency values (i.e., external names for constraints) u_map 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: 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(); - // Start managing lifetime of the given constraint - constraint* store(constraint_ref c); + /** Start managing the lifetime of the given constraint + * - 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); /// Register a unit clause with an external dependency. @@ -76,16 +87,14 @@ namespace polysat { void release_level(unsigned lvl); 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_literal_ref eq(unsigned lvl, pdd const& p); - constraint_literal_ref ule(unsigned lvl, pdd const& a, pdd const& b); - constraint_literal_ref ult(unsigned lvl, pdd const& a, pdd const& b); - constraint_literal_ref sle(unsigned lvl, pdd const& a, pdd const& b); - constraint_literal_ref slt(unsigned lvl, pdd const& a, pdd const& b); - - // p_dependency_ref null_dep() const { return {nullptr, m_dm}; } + scoped_signed_constraint eq(unsigned lvl, pdd const& p); + scoped_signed_constraint ule(unsigned lvl, pdd const& a, pdd const& b); + scoped_signed_constraint ult(unsigned lvl, pdd const& a, pdd const& b); + scoped_signed_constraint sle(unsigned lvl, pdd const& a, pdd const& b); + scoped_signed_constraint slt(unsigned lvl, pdd const& a, pdd const& b); }; @@ -105,35 +114,28 @@ namespace polysat { class constraint { friend class constraint_manager; friend class clause; - friend class scoped_clause; friend class eq_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. - unsigned m_ref_count = 0; unsigned m_storage_level; ///< Controls lifetime of the constraint object. Always a base level. ckind_t m_kind; 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): - m_manager(&m), m_storage_level(lvl), m_kind(k), m_bvar(m_manager->m_bvars.new_var()) { - SASSERT_EQ(m_manager->get_bv2c(bvar()), nullptr); - m_manager->insert_bv2c(bvar(), this); - } + /*m_manager(&m),*/ m_storage_level(lvl), m_kind(k) {} protected: std::ostream& display_extra(std::ostream& out, lbool status) const; public: - void inc_ref() { m_ref_count++; } - 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 ~constraint() {} virtual unsigned hash() const = 0; virtual bool operator==(constraint const& other) const = 0; @@ -158,6 +160,7 @@ namespace polysat { unsigned_vector& vars() { return m_vars; } unsigned_vector const& vars() const { return m_vars; } 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; } 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. */ // 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); } - /// Literal together with the constraint it represents (i.e., constraint with polarity). - /// Non-owning version. - class constraint_literal { - sat::literal m_literal = sat::null_literal; - constraint* m_constraint = nullptr; + // Like scoped_ptr, but only deallocates the constraint if it is temporary (i.e., does not have a boolean variable). + // This is needed because when a constraint is created, due to deduplication, we might get either a new constraint or an existing one. + // (We want early deduplication because otherwise we might overlook possible boolean resolutions during conflict resolution.) + // (TODO: we could replace this class by std::unique_ptr with a custom deleter) + class scoped_constraint_ptr { + constraint* m_ptr; + + void dealloc_ptr() const { + if (m_ptr && !m_ptr->has_bvar()) + dealloc(m_ptr); + } public: - constraint_literal() {} - 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) {} + scoped_constraint_ptr(constraint* ptr = nullptr): m_ptr(ptr) {} - constraint_literal operator~() const { - return {~m_literal, m_constraint}; + scoped_constraint_ptr(scoped_constraint_ptr &&other) noexcept : m_ptr(nullptr) { + std::swap(m_ptr, other.m_ptr); + } + + ~scoped_constraint_ptr() { + dealloc_ptr(); + } + + scoped_constraint_ptr& operator=(scoped_constraint_ptr&& other) { + *this = other.detach(); + return *this; + }; + + scoped_constraint_ptr& operator=(constraint* n) { + if (m_ptr != n) { + dealloc_ptr(); + m_ptr = n; + } + return *this; + } + + void swap(scoped_constraint_ptr& p) { + std::swap(m_ptr, p.m_ptr); + } + + constraint* detach() { + constraint* tmp = m_ptr; + m_ptr = 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 + class signed_constraint_base final { + public: + using ptr_t = std::conditional_t; + + private: + ptr_t m_constraint = nullptr; + 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_literal = ~m_literal; + m_positive = !m_positive; } - bool is_positive() const { return !m_literal.sign(); } - bool is_negative() const { return m_literal.sign(); } + bool is_positive() const { return m_positive; } + bool is_negative() const { return !is_positive(); } - 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()); } + 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::literal literal() const { return m_literal; } - constraint* get_constraint() const { return m_constraint; } + 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 + std::enable_if_t detach() { return m_constraint.detach(); } explicit operator bool() const { return !!m_constraint; } bool operator!() const { return !m_constraint; } - constraint* operator->() const { return m_constraint; } + constraint* operator->() const { return get(); } + constraint& operator*() { 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; } + signed_constraint_base& operator=(std::nullptr_t) { m_constraint = nullptr; return *this; } - std::ostream& display(std::ostream& out) const; + bool operator==(signed_constraint_base 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 << ""; + } }; - inline std::ostream& operator<<(std::ostream& out, constraint_literal const& c) { return c.display(out); } + template + inline std::ostream& operator<<(std::ostream& out, signed_constraint_base const& c) { return c.display(out); } - inline bool operator==(constraint_literal const& lhs, constraint_literal const& rhs) { - if (lhs.literal() == rhs.literal()) - SASSERT(lhs.get_constraint() == rhs.get_constraint()); - return lhs.literal() == rhs.literal(); + inline signed_constraint operator~(signed_constraint const& c) { + return {c.get(), !c.is_positive()}; } - - /// Version of constraint_literal that owns the constraint. - class constraint_literal_ref { - sat::literal m_literal = sat::null_literal; - constraint_ref m_constraint = nullptr; - - public: - constraint_literal_ref() {} - 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: - friend class constraint_manager; - explicit constraint_literal_ref(constraint* c): constraint_literal_ref(sat::literal(c->bvar()), c) {} - }; - - inline std::ostream& operator<<(std::ostream& out, constraint_literal_ref const& c) { return c.display(out); } + inline scoped_signed_constraint operator~(scoped_signed_constraint&& c) { + return {c.detach(), !c.is_positive()}; + } /// Disjunction of constraints represented by boolean literals class clause { 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_next_guess = 0; // next guess for enumerative backtracking p_dependency_ref m_dep; 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? 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): - m_level(lvl), m_dep(std::move(d)), m_literals(std::move(literals)), m_new_constraints(std::move(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)) { 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 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_literals(unsigned lvl, p_dependency_ref d, sat::literal_vector literals, constraint_ref_vector new_constraints); + 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); // Resolve with 'other' upon 'var'. bool resolve(sat::bool_var var, clause const& other); @@ -316,7 +346,6 @@ namespace polysat { p_dependency* dep() const { return m_dep; } unsigned level() const { return m_level; } - constraint_ref_vector const& new_constraints() const { return m_new_constraints; } bool empty() const { return m_literals.empty(); } unsigned size() const { return m_literals.size(); } sat::literal operator[](unsigned idx) const { return m_literals[idx]; } diff --git a/src/math/polysat/eq_constraint.cpp b/src/math/polysat/eq_constraint.cpp index 27f5ff1d0..a5fa4b103 100644 --- a/src/math/polysat/eq_constraint.cpp +++ b/src/math/polysat/eq_constraint.cpp @@ -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) - 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 unsigned const deg = p().degree(v); diff --git a/src/math/polysat/eq_constraint.h b/src/math/polysat/eq_constraint.h index bd6597ab7..dd05aec74 100644 --- a/src/math/polysat/eq_constraint.h +++ b/src/math/polysat/eq_constraint.h @@ -17,7 +17,7 @@ Author: namespace polysat { - class eq_constraint : public constraint { + class eq_constraint final : public constraint { pdd m_poly; public: 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_true(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; unsigned hash() const override; bool operator==(constraint const& other) const override; diff --git a/src/math/polysat/explain.cpp b/src/math/polysat/explain.cpp index f358211d3..0149d2189 100644 --- a/src/math/polysat/explain.cpp +++ b/src/math/polysat/explain.cpp @@ -561,4 +561,7 @@ namespace polysat { // return true; // } + void conflict_explainer::resolve() + { + } } diff --git a/src/math/polysat/explain.h b/src/math/polysat/explain.h index 48fe0faad..5c4144b2e 100644 --- a/src/math/polysat/explain.h +++ b/src/math/polysat/explain.h @@ -12,6 +12,7 @@ Author: --*/ #pragma once +#include "math/polysat/conflict_core.h" #include "math/polysat/constraint.h" #include "math/polysat/clause_builder.h" #include "math/polysat/interval.h" @@ -43,13 +44,14 @@ namespace polysat { conflict_explainer(solver& s); /** resolve conflict state against assignment to v */ - clause_ref resolve(pvar v, ptr_vector const& cjust_v); + void resolve(pvar v, ptr_vector const& cjust_v); + void resolve(sat::literal lit); // TODO: move conflict resolution from solver into this class. // we have a single public method as entry point to conflict resolution. // what do we need to return? - /** conflict resolution */ + /** conflict resolution until first (relevant) decision */ void resolve(); }; } diff --git a/src/math/polysat/forbidden_intervals.cpp b/src/math/polysat/forbidden_intervals.cpp index fb3068002..b68083158 100644 --- a/src/math/polysat/forbidden_intervals.cpp +++ b/src/math/polysat/forbidden_intervals.cpp @@ -22,8 +22,8 @@ namespace polysat { struct fi_record { eval_interval interval; - constraint_literal_ref neg_cond; // could be multiple constraints later - constraint_literal src; + scoped_signed_constraint neg_cond; // could be multiple constraints later + signed_constraint src; }; /** @@ -64,17 +64,17 @@ namespace polysat { return true; } - bool forbidden_intervals::explain(solver& s, vector const& conflict, pvar v, clause_ref& out_lemma) { + bool forbidden_intervals::explain(solver& s, vector const& conflict, pvar v, clause_ref& out_lemma) { // Extract forbidden intervals from conflicting constraints vector records; bool has_full = false; rational longest_len; unsigned longest_i = UINT_MAX; - for (constraint_literal c : conflict) { + for (signed_constraint c : conflict) { LOG_H3("Computing forbidden interval for: " << c); 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)) { LOG("interval: " << interval); LOG("neg_cond: " << neg_cond); @@ -102,7 +102,8 @@ namespace polysat { auto& full_record = records.back(); SASSERT(full_record.interval.is_full()); 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) clause.push_new_constraint(std::move(full_record.neg_cond)); out_lemma = clause.build(); @@ -130,7 +131,7 @@ namespace polysat { unsigned lemma_lvl = 0; 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()); } @@ -145,7 +146,7 @@ namespace polysat { clause_builder clause(s); // Add negation of src constraints as antecedents (may be resolved during backtracking) 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); } // Add side conditions and interval constraints @@ -159,12 +160,12 @@ namespace polysat { auto const& next_hi = records[next_i].interval.hi(); auto const& lhs = 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); clause.push_new_constraint(std::move(c)); // 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? - constraint_literal_ref& neg_cond = records[i].neg_cond; + scoped_signed_constraint& neg_cond = records[i].neg_cond; if (neg_cond) clause.push_new_constraint(std::move(neg_cond)); } diff --git a/src/math/polysat/forbidden_intervals.h b/src/math/polysat/forbidden_intervals.h index e47ed340d..e6f03c4cc 100644 --- a/src/math/polysat/forbidden_intervals.h +++ b/src/math/polysat/forbidden_intervals.h @@ -22,7 +22,7 @@ namespace polysat { class forbidden_intervals { public: - static bool explain(solver& s, vector const& conflict, pvar v, clause_ref& out_lemma); + static bool explain(solver& s, vector const& conflict, pvar v, clause_ref& out_lemma); }; } diff --git a/src/math/polysat/solver.cpp b/src/math/polysat/solver.cpp index 7a08f7327..6e5f494f4 100644 --- a/src/math/polysat/solver.cpp +++ b/src/math/polysat/solver.cpp @@ -111,43 +111,44 @@ namespace polysat { 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); } - 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); } - 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); } - 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); } - 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); } - 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); } - 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()); - 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. - constraint_literal c = cl.get(); - clause* unit = m_constraints.store(clause::from_unit(std::move(cl), mk_dep_ref(dep))); + signed_constraint c = sc.get_signed(); + m_constraints.store(sc.detach()); + clause* unit = m_constraints.store(clause::from_unit(c, mk_dep_ref(dep))); c->set_unit_clause(unit); if (dep != null_dependency) - m_constraints.register_external(c.get_constraint()); + m_constraints.register_external(c.get()); LOG("New constraint: " << c); m_original.push_back(c); #if ENABLE_LINEAR_SOLVER - m_linear_solver.new_constraint(*c.constraint()); + m_linear_solver.new_constraint(*c.get()); #endif if (activate && !is_conflict()) activate_constraint_base(c); @@ -164,8 +165,8 @@ namespace polysat { } if (is_conflict()) return; - // TODO: this is wrong. (e.g., if the external constraint was negative) we need to store constraint_literals - constraint_literal cl{c, is_true}; + // TODO: this is wrong. (e.g., if the external constraint was negative) we need to store signed_constraints + signed_constraint cl{c, is_true}; activate_constraint_base(cl); */ } @@ -202,7 +203,7 @@ namespace polysat { void solver::propagate(sat::literal lit) { LOG_H2("Propagate boolean literal " << lit); - constraint_literal c = m_constraints.lookup(lit); + signed_constraint c = m_constraints.lookup(lit); (void)c; SASSERT(c); // c->narrow(*this); @@ -220,7 +221,7 @@ namespace polysat { 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); if (m_viable.is_viable(v, val)) { m_free_vars.del_var_eh(v); @@ -276,7 +277,7 @@ namespace polysat { case trail_instr_t::assign_bool_i: { sat::literal lit = m_search.back().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); m_bvars.unassign(lit); m_search.pop(); @@ -300,7 +301,7 @@ namespace polysat { SASSERT(m_level == target_level); } - void solver::pop_constraints(constraint_literals& cs) { + void solver::pop_constraints(signed_constraints& cs) { VERIFY(invariant(cs)); while (!cs.empty() && cs.back()->level() > m_level) { 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); - auto const& vars = c.get_constraint()->vars(); + auto const& vars = c->vars(); if (vars.size() > 0) add_watch(c, vars[0]); if (vars.size() > 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); LOG("Watching v" << v << " in constraint " << c); m_watch[v].push_back(c); } - void solver::erase_watch(constraint_literal c) { - auto const& vars = c.get_constraint()->vars(); + void solver::erase_watch(signed_constraint c) { + auto const& vars = c->vars(); if (vars.size() > 0) erase_watch(vars[0], c); if (vars.size() > 1) 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) return; auto& wlist = m_watch[v]; @@ -392,7 +393,7 @@ namespace polysat { #endif } - void solver::set_conflict(constraint_literal c) { + void solver::set_conflict(signed_constraint c) { m_conflict.set(c); } @@ -699,7 +700,7 @@ namespace polysat { add_lemma(std::move(lemma)); if (cl->size() == 1) { 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); push_cjust(v, c); activate_constraint_base(c); @@ -707,7 +708,7 @@ namespace polysat { else { sat::literal lit = decide_bool(*cl); SASSERT(lit != sat::null_literal); - constraint_literal c = m_constraints.lookup(lit); + signed_constraint c = m_constraints.lookup(lit); 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) return false; 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 return !c.is_currently_false(*this); }; @@ -905,7 +906,7 @@ namespace polysat { SASSERT(reason); if (reason->literals().size() == 1) { SASSERT(reason->literals()[0] == lit); - constraint_literal c = m_constraints.lookup(lit); + signed_constraint c = m_constraints.lookup(lit); // m_redundant.push_back(c); activate_constraint_base(c); } @@ -921,18 +922,18 @@ namespace polysat { m_trail.push_back(trail_instr_t::assign_bool_i); m_search.push_boolean(lit); - constraint_literal c = m_constraints.lookup(lit); + signed_constraint c = m_constraints.lookup(lit); activate_constraint(c); } /// Activate a constraint at the base level. /// 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); LOG("\n" << *this); // 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); - assign_bool_core(c.literal(), nullptr, nullptr); + assign_bool_core(c.blit(), nullptr, nullptr); activate_constraint(c); } @@ -948,19 +949,19 @@ namespace polysat { * constraints activated within the linear solver are de-activated when the linear * solver is popped. */ - void solver::activate_constraint(constraint_literal c) { + void solver::activate_constraint(signed_constraint c) { SASSERT(c); LOG("Activating constraint: " << c); - SASSERT(m_bvars.value(c.literal()) == l_true); + SASSERT(m_bvars.value(c.blit()) == l_true); add_watch(c); c.narrow(*this); #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 } /// Deactivate constraint - void solver::deactivate_constraint(constraint_literal c) { + void solver::deactivate_constraint(signed_constraint c) { LOG("Deactivating constraint: " << c); erase_watch(c); } @@ -988,12 +989,12 @@ namespace polysat { clause* cl = m_constraints.store(std::move(lemma)); m_redundant_clauses.push_back(cl); 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); } } - void solver::insert_constraint(constraint_literals& cs, constraint_literal c) { + void solver::insert_constraint(signed_constraints& cs, signed_constraint c) { SASSERT(c); LOG_V("INSERTING: " << 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. */ - bool solver::invariant(constraint_literals const& cs) { + bool solver::invariant(signed_constraints const& cs) { unsigned sz = cs.size(); for (unsigned i = 0; i + 1 < sz; ++i) VERIFY(cs[i]->level() <= cs[i + 1]->level()); @@ -1128,7 +1129,7 @@ namespace polysat { * Check that two variables of each constraint are watched. */ bool solver::wlist_invariant() { - constraint_literals cs; + signed_constraints cs; cs.append(m_original.size(), m_original.data()); cs.append(m_redundant.size(), m_redundant.data()); for (auto c : cs) { diff --git a/src/math/polysat/solver.h b/src/math/polysat/solver.h index 56d63ee7b..5a4e1fe80 100644 --- a/src/math/polysat/solver.h +++ b/src/math/polysat/solver.h @@ -61,7 +61,7 @@ namespace polysat { friend class assignments_pp; typedef ptr_vector constraints; - typedef vector constraint_literals; + typedef vector signed_constraints; reslimit& m_lim; params_ref m_params; @@ -84,8 +84,8 @@ namespace polysat { // Per constraint state constraint_manager m_constraints; - constraint_literals m_original; - constraint_literals m_redundant; + signed_constraints m_original; + signed_constraints m_redundant; ptr_vector m_redundant_clauses; svector m_disjunctive_lemma; @@ -93,8 +93,8 @@ namespace polysat { // Per variable information vector m_value; // assigned value vector m_justification; // justification for variable assignment - vector m_cjust; // constraints justifying variable range. - vector m_watch; // watch list datastructure into constraints. + vector m_cjust; // constraints justifying variable range. + vector m_watch; // watch list datastructure into constraints. unsigned_vector m_activity; vector m_vars; unsigned_vector m_size; // store size of variables. @@ -128,7 +128,7 @@ namespace polysat { 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?) return; LOG_V("cjust[v" << v << "] += " << c); @@ -151,14 +151,14 @@ namespace polysat { void push_level(); 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 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_base(sat::literal lit); - void activate_constraint(constraint_literal c); - void deactivate_constraint(constraint_literal c); + void activate_constraint(signed_constraint c); + void deactivate_constraint(signed_constraint c); sat::literal decide_bool(clause& lemma); void decide_bool(sat::literal lit, clause& lemma); void propagate_bool(sat::literal lit, clause* reason); @@ -171,13 +171,13 @@ namespace polysat { void propagate(sat::literal lit); void propagate(pvar v); - void propagate(pvar v, rational const& val, constraint_literal c); - void erase_watch(pvar v, constraint_literal c); - void erase_watch(constraint_literal c); - void add_watch(constraint_literal c); - void add_watch(constraint_literal c, pvar v); + void propagate(pvar v, rational const& val, signed_constraint c); + void erase_watch(pvar v, signed_constraint c); + void erase_watch(signed_constraint c); + void add_watch(signed_constraint c); + 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); unsigned_vector m_marks; @@ -219,17 +219,17 @@ namespace polysat { void backjump(unsigned new_level); void add_lemma(clause_ref lemma); - constraint_literal_ref mk_eq(pdd const& p); - constraint_literal_ref mk_diseq(pdd const& p); - constraint_literal_ref mk_ule(pdd const& p, pdd const& q); - constraint_literal_ref mk_ult(pdd const& p, pdd const& q); - constraint_literal_ref mk_sle(pdd const& p, pdd const& q); - constraint_literal_ref mk_slt(pdd const& p, pdd const& q); - void new_constraint(constraint_literal_ref c, unsigned dep, bool activate); - static void insert_constraint(constraint_literals& cs, constraint_literal c); + scoped_signed_constraint mk_eq(pdd const& p); + scoped_signed_constraint mk_diseq(pdd const& p); + scoped_signed_constraint mk_ule(pdd const& p, pdd const& q); + scoped_signed_constraint mk_ult(pdd const& p, pdd const& q); + scoped_signed_constraint mk_sle(pdd const& p, pdd const& q); + scoped_signed_constraint mk_slt(pdd const& p, pdd const& q); + void new_constraint(scoped_signed_constraint c, unsigned dep, bool activate); + static void insert_constraint(signed_constraints& cs, signed_constraint c); bool invariant(); - static bool invariant(constraint_literals const& cs); + static bool invariant(signed_constraints const& cs); bool wlist_invariant(); bool verify_sat(); diff --git a/src/math/polysat/ule_constraint.cpp b/src/math/polysat/ule_constraint.cpp index ac0121432..b0c8f88d6 100644 --- a/src/math/polysat/ule_constraint.cpp +++ b/src/math/polysat/ule_constraint.cpp @@ -113,7 +113,7 @@ namespace polysat { 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 unsigned const deg1 = lhs().degree(v); diff --git a/src/math/polysat/ule_constraint.h b/src/math/polysat/ule_constraint.h index c70461d7d..4a1a6372a 100644 --- a/src/math/polysat/ule_constraint.h +++ b/src/math/polysat/ule_constraint.h @@ -17,7 +17,7 @@ Author: namespace polysat { - class ule_constraint : public constraint { + class ule_constraint final : public constraint { pdd m_lhs; pdd m_rhs; public: @@ -37,7 +37,7 @@ namespace polysat { bool is_currently_false(solver& s, bool is_positive) override; bool is_currently_true(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; unsigned hash() const override; bool operator==(constraint const& other) const override;