3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-27 08:28:44 +00:00

Polysat: use constraint_literal and begin move to core-based conflict representation (#5489)

* Rename solver_scope for fixplex tests

(otherwise the wrong constructor is called for polysat's solver_scope)

* Update conflict_core

* simplify

* Be clearer about constraint_literal lifetime

* remove old comment

* Remove status (positive/negative) from constraint

* Use constraint_literal in the solver

* Fix build (constraint -> get_constraint)
This commit is contained in:
Jakob Rath 2021-08-18 20:02:46 +02:00 committed by GitHub
parent 30e9f24fa3
commit ebaea2159e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 933 additions and 1004 deletions

View file

@ -52,12 +52,10 @@ namespace polysat {
m_literals.push_back(lit); m_literals.push_back(lit);
} }
void clause_builder::push_new_constraint(constraint_literal c) { void clause_builder::push_new_constraint(constraint_literal_ref c) {
// TODO: assert that constraint is new (not 'inserted' into manager yet) // TODO: assert that constraint is new (not 'inserted' into manager yet)
SASSERT(c); SASSERT(c);
SASSERT(c->is_undef()); if (c.get().is_always_false())
tmp_assign _t(c.get(), c.literal());
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.literal());

View file

@ -46,7 +46,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 c); void push_new_constraint(constraint_literal_ref c);
}; };
} }

View file

@ -20,9 +20,40 @@ Author:
namespace polysat { namespace polysat {
std::ostream& conflict_core::display(std::ostream& out) const { std::ostream& conflict_core::display(std::ostream& out) const {
out << "TODO: display conflict_core"; bool first = true;
// depending on sign: A /\ B /\ C or ¬A \/ ¬B \/ ¬C for (auto c : m_constraints) {
if (first)
first = false;
else
out << " ; ";
out << c;
}
if (m_needs_model)
out << " ; + current model";
return out; return out;
} }
void conflict_core::set(std::nullptr_t) {
SASSERT(empty());
m_constraints.push_back({});
m_needs_model = false;
}
void conflict_core::set(constraint_literal c) {
LOG("Conflict: " << c);
SASSERT(empty());
m_constraints.push_back(std::move(c));
m_needs_model = true;
}
void conflict_core::set(pvar v, vector<constraint_literal> const& cjust_v) {
LOG("Conflict for v" << v << ": " << cjust_v);
SASSERT(empty());
NOT_IMPLEMENTED_YET();
m_constraints.append(cjust_v);
if (cjust_v.empty())
m_constraints.push_back({});
m_needs_model = true;
}
} }

View file

@ -16,47 +16,38 @@ Author:
namespace polysat { namespace polysat {
/// Represents a conflict as core (~negation of clause). /** Conflict state, represented as core (~negation of clause). */
///
/// TODO: can probably move some clause_builder functionality into this class.
class conflict_core { class conflict_core {
// constraint_ref_vector m_constraints; // TODO: core needs to own the constraint literals...
vector<constraint_literal> m_constraints; vector<constraint_literal> m_constraints;
bool m_needs_model = true; ///< 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;
// NOTE: for now we keep this simple implementation.
// The drawback is that we may get weaker lemmas in some cases (but they are still correct).
// 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:
vector<constraint_literal> const& constraints() const {
return m_constraints; vector<constraint_literal> const& constraints() const { return m_constraints; }
} bool needs_model() const { return m_needs_model; }
bool empty() const { bool empty() const {
return m_constraints.empty(); return m_constraints.empty() && !m_needs_model;
} }
void reset() { void reset() {
m_constraints.reset(); m_constraints.reset();
m_needs_model = true;
}
// for bailing out with a conflict at the base level
void set(std::nullptr_t) {
SASSERT(empty());
m_constraints.push_back({});
m_needs_model = false; m_needs_model = false;
SASSERT(empty());
} }
// void set( /** for bailing out with a conflict at the base level */
void set(std::nullptr_t);
// TODO: set core from conflicting units /** conflict because the constraint c is false under current variable assignment */
// TODO: set clause void set(constraint_literal c);
/** conflict because there is no viable value for the variable v */
// TODO: use iterator instead (this method is needed for marking so duplicates don't necessarily have to be skipped) void set(pvar v, vector<constraint_literal> const& cjust_v);
unsigned_vector vars(constraint_manager const& cm) const {
unsigned_vector vars;
for (auto const& c : m_constraints)
vars.append(c->vars());
return vars;
}
std::ostream& display(std::ostream& out) const; std::ostream& display(std::ostream& out) const;
}; };

View file

@ -93,6 +93,10 @@ namespace polysat {
return get_bv2c(var); return get_bv2c(var);
} }
constraint_literal constraint_manager::lookup(sat::literal lit) const {
return {lit, lookup(lit.var())};
}
eq_constraint& constraint::to_eq() { eq_constraint& constraint::to_eq() {
return *dynamic_cast<eq_constraint*>(this); return *dynamic_cast<eq_constraint*>(this);
} }
@ -109,16 +113,16 @@ namespace polysat {
return *dynamic_cast<ule_constraint const*>(this); return *dynamic_cast<ule_constraint const*>(this);
} }
constraint_literal constraint_manager::eq(unsigned lvl, pdd const& p) { constraint_literal_ref constraint_manager::eq(unsigned lvl, pdd const& p) {
return constraint_literal{alloc(eq_constraint, *this, lvl, p)}; return constraint_literal_ref{alloc(eq_constraint, *this, lvl, p)};
} }
constraint_literal constraint_manager::ule(unsigned lvl, pdd const& a, pdd const& b) { constraint_literal_ref constraint_manager::ule(unsigned lvl, pdd const& a, pdd const& b) {
return constraint_literal{alloc(ule_constraint, *this, lvl, a, b)}; return constraint_literal_ref{alloc(ule_constraint, *this, lvl, a, b)};
} }
constraint_literal constraint_manager::ult(unsigned lvl, pdd const& a, pdd const& 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);
} }
@ -139,25 +143,26 @@ 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 constraint_manager::sle(unsigned lvl, pdd const& a, pdd const& b) { constraint_literal_ref 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 constraint_manager::slt(unsigned lvl, pdd const& a, pdd const& b) { constraint_literal_ref 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);
} }
std::ostream& constraint::display_extra(std::ostream& out) const { std::ostream& constraint::display_extra(std::ostream& out, lbool status) const {
out << " @" << level() << " (b" << bvar() << ")"; out << " @" << level() << " (b" << bvar() << ")";
if (is_positive()) out << " [pos]"; (void)status;
if (is_negative()) out << " [neg]"; // if (is_positive()) out << " [pos]";
if (is_undef()) out << " [inactive]"; // if (is_negative()) out << " [neg]";
// if (is_undef()) out << " [inactive]"; // TODO: not sure if we still need/want this... decide later
return out; return out;
} }
bool constraint::propagate(solver& s, pvar v) { bool constraint::propagate(solver& s, bool is_positive, pvar v) {
LOG_H3("Propagate " << s.m_vars[v] << " in " << *this); LOG_H3("Propagate " << s.m_vars[v] << " in " << *this);
SASSERT(!vars().empty()); SASSERT(!vars().empty());
unsigned idx = 0; unsigned idx = 0;
@ -168,24 +173,35 @@ namespace polysat {
for (unsigned i = vars().size(); i-- > 2; ) { for (unsigned i = vars().size(); i-- > 2; ) {
unsigned other_v = vars()[i]; unsigned other_v = vars()[i];
if (!s.is_assigned(other_v)) { if (!s.is_assigned(other_v)) {
s.add_watch(*this, other_v); s.add_watch({this, is_positive}, other_v);
std::swap(vars()[idx], vars()[i]); std::swap(vars()[idx], vars()[i]);
return true; return true;
} }
} }
// at most one variable remains unassigned. // at most one variable remains unassigned.
unsigned other_v = vars()[idx]; unsigned other_v = vars()[idx];
propagate_core(s, v, other_v); propagate_core(s, is_positive, v, other_v);
return false; return false;
} }
void constraint::propagate_core(solver& s, pvar v, pvar other_v) { void constraint::propagate_core(solver& s, bool is_positive, pvar v, pvar other_v) {
(void)v; (void)v;
(void)other_v; (void)other_v;
narrow(s); narrow(s, is_positive);
} }
clause_ref clause::from_unit(constraint_literal c, p_dependency_ref d) { std::ostream &constraint_literal::display(std::ostream &out) const {
if (*this)
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); SASSERT(c);
unsigned const lvl = c->level(); unsigned const lvl = c->level();
sat::literal_vector lits; sat::literal_vector lits;
@ -201,17 +217,15 @@ namespace polysat {
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 *c = s.m_constraints.lookup(lit.var()); constraint_literal c = s.m_constraints.lookup(lit);
tmp_assign _t(c, 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 *c = s.m_constraints.lookup(lit.var()); constraint_literal c = s.m_constraints.lookup(lit);
tmp_assign _t(c, lit); return c.is_currently_false(s);
return c->is_currently_false(s);
}); });
} }
@ -251,22 +265,4 @@ namespace polysat {
return out; return out;
} }
std::ostream& constraints_and_clauses::display(std::ostream& out) const {
bool first = true;
for (auto c : units()) {
if (first)
first = false;
else
out << " ; ";
out << show_deref(c);
}
for (auto cl : clauses()) {
if (first)
first = false;
else
out << " ; ";
out << show_deref(cl);
}
return out;
}
} }

View file

@ -26,6 +26,7 @@ namespace polysat {
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;
class constraint_literal_ref;
class constraint; class constraint;
class constraint_manager; class constraint_manager;
class clause; class clause;
@ -34,6 +35,7 @@ namespace polysat {
class ule_constraint; class ule_constraint;
using constraint_ref = ref<constraint>; using constraint_ref = ref<constraint>;
using constraint_ref_vector = sref_vector<constraint>; using constraint_ref_vector = sref_vector<constraint>;
using constraint_literal_ref_vector = vector<constraint_literal_ref>;
using clause_ref = ref<clause>; using clause_ref = ref<clause>;
using clause_ref_vector = sref_vector<clause>; using clause_ref_vector = sref_vector<clause>;
@ -49,7 +51,7 @@ namespace polysat {
void erase_bv2c(sat::bool_var bv) { m_bv2constraint[bv] = 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); } constraint* get_bv2c(sat::bool_var bv) const { return m_bv2constraint.get(bv, nullptr); }
// Constraint storage per level; should be destructed before m_bv2constraint // Constraint storage per level; should be destructed before m_bv2constraint because constraint's destructor calls erase_bv2c
vector<vector<constraint_ref>> m_constraints; vector<vector<constraint_ref>> m_constraints;
vector<vector<clause_ref>> m_clauses; vector<vector<clause_ref>> m_clauses;
@ -74,13 +76,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;
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 eq(unsigned lvl, pdd const& p); constraint_literal_ref eq(unsigned lvl, pdd const& p);
constraint_literal ule(unsigned lvl, pdd const& a, pdd const& b); constraint_literal_ref ule(unsigned lvl, pdd const& a, pdd const& b);
constraint_literal ult(unsigned lvl, pdd const& a, pdd const& b); constraint_literal_ref ult(unsigned lvl, pdd const& a, pdd const& b);
constraint_literal sle(unsigned lvl, pdd const& a, pdd const& b); constraint_literal_ref sle(unsigned lvl, pdd const& a, pdd const& b);
constraint_literal slt(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}; } // p_dependency_ref null_dep() const { return {nullptr, m_dm}; }
}; };
@ -109,12 +112,10 @@ namespace polysat {
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_ref_count = 0;
// TODO: we could remove the level on constraints and instead store constraint_refs for all literals inside the clause? (clauses will then be 4 times larger though...)
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 sat::bool_var m_bvar; ///< boolean variable associated to this constraint; convention: a constraint itself always represents the positive sat::literal
lbool m_status = l_undef; ///< current constraint status; intended to be the same as m_manager->m_bvars.value(bvar()) if that value is set.
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), m_bvar(m_manager->m_bvars.new_var()) {
@ -123,7 +124,7 @@ namespace polysat {
} }
protected: protected:
std::ostream& display_extra(std::ostream& out) const; std::ostream& display_extra(std::ostream& out, lbool status) const;
public: public:
void inc_ref() { m_ref_count++; } void inc_ref() { m_ref_count++; }
@ -140,14 +141,16 @@ namespace polysat {
bool is_eq() const { return m_kind == ckind_t::eq_t; } bool is_eq() const { return m_kind == ckind_t::eq_t; }
bool is_ule() const { return m_kind == ckind_t::ule_t; } bool is_ule() const { return m_kind == ckind_t::ule_t; }
ckind_t kind() const { return m_kind; } ckind_t kind() const { return m_kind; }
virtual std::ostream& display(std::ostream& out) const = 0; virtual std::ostream& display(std::ostream& out, lbool status = l_undef) const = 0;
bool propagate(solver& s, pvar v);
virtual void propagate_core(solver& s, pvar v, pvar other_v); bool propagate(solver& s, bool is_positive, pvar v);
virtual bool is_always_false() = 0; virtual void propagate_core(solver& s, bool is_positive, pvar v, pvar other_v);
virtual bool is_currently_false(solver& s) = 0; virtual bool is_always_false(bool is_positive) = 0;
virtual bool is_currently_true(solver& s) = 0; virtual bool is_currently_false(solver& s, bool is_positive) = 0;
virtual void narrow(solver& s) = 0; virtual bool is_currently_true(solver& s, bool is_positive) = 0;
virtual inequality as_inequality() const = 0; virtual void narrow(solver& s, bool is_positive) = 0;
virtual inequality as_inequality(bool is_positive) const = 0;
eq_constraint& to_eq(); eq_constraint& to_eq();
eq_constraint const& to_eq() const; eq_constraint const& to_eq() const;
ule_constraint& to_ule(); ule_constraint& to_ule();
@ -155,27 +158,8 @@ 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; }
// unsigned active_level() const {
// SASSERT(!is_undef());
// return m_manager->m_bvars.level(bvar());
// }
// unsigned active_at_base_level(solver& s) const {
// return !is_undef() && active_level() <= s.base_level();
// }
sat::bool_var bvar() const { return m_bvar; } sat::bool_var bvar() const { return m_bvar; }
sat::literal blit() const { SASSERT(!is_undef()); return m_status == l_true ? sat::literal(m_bvar) : ~sat::literal(m_bvar); }
void assign(bool is_true) {
SASSERT(m_status == l_undef || m_status == to_lbool(is_true));
// SASSERT(m_status == l_undef /* || m_status == to_lbool(is_true) */);
m_status = to_lbool(is_true);
// SASSERT(m_manager->m_bvars.value(bvar()) == l_undef || m_manager->m_bvars.value(bvar()) == m_status); // TODO: is this always true? maybe we sometimes want to check the opposite phase temporarily.
}
void unassign() { m_status = l_undef; }
bool is_undef() const { return m_status == l_undef; }
bool is_positive() const { return m_status == l_true; }
bool is_negative() const { return m_status == l_false; }
clause* unit_clause() const { return m_unit_clause; } clause* unit_clause() const { return m_unit_clause; }
void set_unit_clause(clause* cl) { SASSERT(cl); SASSERT(!m_unit_clause || m_unit_clause == cl); m_unit_clause = cl; } void set_unit_clause(clause* cl) { SASSERT(cl); SASSERT(!m_unit_clause || m_unit_clause == cl); m_unit_clause = cl; }
p_dependency* unit_dep() const; p_dependency* unit_dep() const;
@ -186,43 +170,111 @@ namespace polysat {
* \param[out] out_neg_cond Negation of the side condition (the side condition is true when the forbidden interval is trivial). May be NULL if the condition is constant. * \param[out] out_neg_cond Negation of the side condition (the side condition is true when the forbidden interval is trivial). May be NULL if the condition is constant.
* \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.
*/ */
virtual bool forbidden_interval(solver& s, pvar v, eval_interval& out_interval, constraint_literal& out_neg_cond) { return false; } // 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; }
}; };
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. /// Literal together with the constraint it represents (i.e., constraint with polarity).
/// (or: constraint with polarity) /// Non-owning version.
class constraint_literal { class constraint_literal {
sat::literal m_literal = sat::null_literal;
constraint* m_constraint = nullptr;
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) {}
constraint_literal operator~() const {
return {~m_literal, m_constraint};
}
void negate() {
m_literal = ~m_literal;
}
bool is_positive() const { return !m_literal.sign(); }
bool is_negative() const { return m_literal.sign(); }
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); }
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();
}
/// Version of constraint_literal that owns the constraint.
class constraint_literal_ref {
sat::literal m_literal = sat::null_literal; sat::literal m_literal = sat::null_literal;
constraint_ref m_constraint = nullptr; constraint_ref m_constraint = nullptr;
public: public:
constraint_literal() {} constraint_literal_ref() {}
constraint_literal(sat::literal lit, constraint_ref c): constraint_literal_ref(sat::literal lit, constraint_ref c):
m_literal(lit), m_constraint(std::move(c)) { m_literal(lit), m_constraint(std::move(c)) {
SASSERT(get()); SASSERT(get_constraint());
SASSERT(literal().var() == get()->bvar()); SASSERT(literal().var() == get_constraint()->bvar());
} }
constraint_literal operator~() const&& { 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)}; return {~m_literal, std::move(m_constraint)};
} }
void negate() {
m_literal = ~m_literal;
}
sat::literal literal() const { return m_literal; } sat::literal literal() const { return m_literal; }
constraint* get() const { return m_constraint.get(); } 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); } constraint_ref detach() { m_literal = sat::null_literal; return std::move(m_constraint); }
explicit operator bool() const { return !!m_constraint; } explicit operator bool() const { return !!m_constraint; }
bool operator!() const { return !m_constraint; } bool operator!() const { return !m_constraint; }
constraint* operator->() const { return m_constraint.get(); } constraint* operator->() const { return m_constraint.operator->(); }
constraint const& 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; } 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; friend class constraint_manager;
explicit constraint_literal(constraint* c): constraint_literal(sat::literal(c->bvar()), c) {} 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); }
/// Disjunction of constraints represented by boolean literals /// Disjunction of constraints represented by boolean literals
class clause { class clause {
@ -254,7 +306,7 @@ 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 c, p_dependency_ref d); 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_literals(unsigned lvl, p_dependency_ref d, sat::literal_vector literals, constraint_ref_vector new_constraints);
// Resolve with 'other' upon 'var'. // Resolve with 'other' upon 'var'.
@ -286,98 +338,5 @@ namespace polysat {
inline std::ostream& operator<<(std::ostream& out, clause const& c) { return c.display(out); } inline std::ostream& operator<<(std::ostream& out, clause const& c) { return c.display(out); }
// Container for unit constraints and clauses.
class constraints_and_clauses {
constraint_ref_vector m_units;
clause_ref_vector m_clauses;
public:
constraint_ref_vector const& units() const { return m_units; }
constraint_ref_vector& units() { return m_units; }
clause_ref_vector const& clauses() const { return m_clauses; }
clause_ref_vector& clauses() { return m_clauses; }
bool is_unit() const { return units().size() == 1 && clauses().empty(); }
constraint* get_unit() const { SASSERT(is_unit()); return units()[0]; }
bool is_clause() const { return units().empty() && clauses().size() == 1; }
clause* get_clause() const { SASSERT(is_clause()); return clauses()[0]; }
unsigned size() const {
return m_units.size() + m_clauses.size();
}
bool empty() const {
return m_units.empty() && m_clauses.empty();
}
void reset() {
m_units.reset();
m_clauses.reset();
}
void append(ptr_vector<constraint> const& cs) {
for (constraint* c : cs)
m_units.push_back(c);
}
void push_back(std::nullptr_t) { m_units.push_back(nullptr); }
void push_back(constraint_ref c) { m_units.push_back(std::move(c)); }
void push_back(clause_ref cl) { m_clauses.push_back(std::move(cl)); }
// TODO: use iterator instead
unsigned_vector vars(constraint_manager const& cm) const {
unsigned_vector vars;
for (auto const& c : m_units)
vars.append(c->vars());
for (auto const& cl : m_clauses)
for (auto lit : *cl) {
constraint* c = cm.lookup(lit.var());
if (c)
vars.append(c->vars());
}
return vars;
}
std::ostream& display(std::ostream& out) const;
};
inline std::ostream& operator<<(std::ostream& out, constraints_and_clauses const& c) { return c.display(out); }
/// Temporarily assign a constraint according to the sign of the given literal.
class tmp_assign final {
constraint* m_constraint;
bool m_should_unassign = false;
public:
/// c must live longer than tmp_assign.
tmp_assign(constraint* c, sat::literal lit):
m_constraint(c) {
SASSERT(c);
SASSERT_EQ(c->bvar(), lit.var());
if (c->is_undef()) {
c->assign(!lit.sign());
m_should_unassign = true;
}
else
SASSERT_EQ(c->blit(), lit);
}
void revert() {
if (m_should_unassign) {
m_constraint->unassign();
m_should_unassign = false;
}
}
~tmp_assign() {
revert();
}
tmp_assign(tmp_assign&) = delete;
tmp_assign(tmp_assign&&) = delete;
tmp_assign& operator=(tmp_assign&) = delete;
tmp_assign& operator=(tmp_assign&&) = delete;
};
inline p_dependency* constraint::unit_dep() const { return m_unit_clause ? m_unit_clause->dep() : nullptr; } inline p_dependency* constraint::unit_dep() const { return m_unit_clause ? m_unit_clause->dep() : nullptr; }
} }

View file

@ -18,82 +18,73 @@ Author:
namespace polysat { namespace polysat {
std::ostream& eq_constraint::display(std::ostream& out) const { std::ostream& eq_constraint::display(std::ostream& out, lbool status) const {
out << p() << " == 0"; out << p();
return display_extra(out); if (status == l_true) out << " == 0";
else if (status == l_false) out << " != 0";
else out << " ==/!= 0";
return display_extra(out, status);
} }
void eq_constraint::narrow(solver& s) { void eq_constraint::narrow(solver& s, bool is_positive) {
SASSERT(!is_undef());
LOG("Assignment: " << assignments_pp(s)); LOG("Assignment: " << assignments_pp(s));
auto q = p().subst_val(s.assignment()); auto q = p().subst_val(s.assignment());
LOG("Substituted: " << p() << " := " << q); LOG("Substituted: " << p() << " := " << q);
if (q.is_zero()) { if (q.is_zero()) {
if (is_positive()) if (!is_positive) {
return;
if (is_negative()) {
LOG("Conflict (zero under current assignment)"); LOG("Conflict (zero under current assignment)");
s.set_conflict(*this); s.set_conflict({this, is_positive});
return;
} }
return;
} }
if (q.is_never_zero()) { if (q.is_never_zero()) {
if (is_positive()) { if (is_positive) {
LOG("Conflict (never zero under current assignment)"); LOG("Conflict (never zero under current assignment)");
s.set_conflict(*this); s.set_conflict({this, is_positive});
return;
} }
if (is_negative()) return;
return;
} }
if (q.is_unilinear()) { if (q.is_unilinear()) {
// a*x + b == 0 // a*x + b == 0
pvar v = q.var(); pvar v = q.var();
s.push_cjust(v, this); s.push_cjust(v, {this, is_positive});
rational a = q.hi().val(); rational a = q.hi().val();
rational b = q.lo().val(); rational b = q.lo().val();
s.m_viable.intersect_eq(a, v, b, is_positive()); s.m_viable.intersect_eq(a, v, b, is_positive);
rational val; rational val;
if (s.m_viable.find_viable(v, val) == dd::find_t::singleton) if (s.m_viable.find_viable(v, val) == dd::find_t::singleton)
s.propagate(v, val, *this); s.propagate(v, val, {this, is_positive});
return; return;
} }
// TODO: what other constraints can be extracted cheaply? // TODO: what other constraints can be extracted cheaply?
} }
bool eq_constraint::is_always_false() { bool eq_constraint::is_always_false(bool is_positive) {
if (is_positive()) if (is_positive)
return p().is_never_zero(); return p().is_never_zero();
if (is_negative()) else
return p().is_zero(); return p().is_zero();
UNREACHABLE();
return false;
} }
bool eq_constraint::is_currently_false(solver& s) { bool eq_constraint::is_currently_false(solver& s, bool is_positive) {
pdd r = p().subst_val(s.assignment()); pdd r = p().subst_val(s.assignment());
if (is_positive()) if (is_positive)
return r.is_never_zero(); return r.is_never_zero();
if (is_negative()) else
return r.is_zero(); return r.is_zero();
UNREACHABLE();
return false;
} }
bool eq_constraint::is_currently_true(solver& s) { bool eq_constraint::is_currently_true(solver& s, bool is_positive) {
pdd r = p().subst_val(s.assignment()); pdd r = p().subst_val(s.assignment());
if (is_positive()) if (is_positive)
return r.is_zero(); return r.is_zero();
if (is_negative()) else
return r.is_never_zero(); return r.is_never_zero();
UNREACHABLE();
return false;
} }
@ -130,10 +121,8 @@ 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, pvar v, eval_interval& out_interval, constraint_literal& out_neg_cond) bool eq_constraint::forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond)
{ {
SASSERT(!is_undef());
// 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);
if (deg > 1) if (deg > 1)
@ -182,7 +171,7 @@ namespace polysat {
rational lo_val = (1 - e1s).val(); rational lo_val = (1 - e1s).val();
pdd hi = -e1; pdd hi = -e1;
rational hi_val = (-e1s).val(); rational hi_val = (-e1s).val();
if (is_negative()) { if (!is_positive) {
swap(lo, hi); swap(lo, hi);
lo_val.swap(hi_val); lo_val.swap(hi_val);
} }
@ -191,10 +180,9 @@ namespace polysat {
return true; return true;
} }
inequality eq_constraint::as_inequality() const { inequality eq_constraint::as_inequality(bool is_positive) const {
SASSERT(!is_undef());
pdd zero = p() - p(); pdd zero = p() - p();
if (is_positive()) if (is_positive)
// p <= 0 // p <= 0
return inequality(p(), zero, false, this); return inequality(p(), zero, false, this);
else else

View file

@ -26,13 +26,13 @@ namespace polysat {
} }
~eq_constraint() override {} ~eq_constraint() override {}
pdd const & p() const { return m_poly; } pdd const & p() const { return m_poly; }
std::ostream& display(std::ostream& out) const override; std::ostream& display(std::ostream& out, lbool status) const override;
bool is_always_false() override; bool is_always_false(bool is_positive) override;
bool is_currently_false(solver& s) override; bool is_currently_false(solver& s, bool is_positive) override;
bool is_currently_true(solver& s) override; bool is_currently_true(solver& s, bool is_positive) override;
void narrow(solver& s) override; void narrow(solver& s, bool is_positive) override;
bool forbidden_interval(solver& s, pvar v, eval_interval& out_interval, constraint_literal& out_neg_cond) override; bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond) override;
inequality as_inequality() 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;
}; };

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@ Copyright (c) 2021 Microsoft Corporation
Module Name: Module Name:
Conflict explanation / resolution Conflict explanation
Author: Author:
@ -15,33 +15,41 @@ Author:
#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"
#include "math/polysat/solver.h"
namespace polysat { namespace polysat {
// TODO: later, we probably want to update this class incrementally (adding/removing constraints as we go back through the trail) class solver;
// TODO: indexing of constraints/clauses?
class conflict_explainer { class conflict_explainer {
solver& m_solver; solver& m_solver;
constraints_and_clauses m_conflict;
ptr_vector<constraint> m_conflict_units;
pvar m_var = null_var; pvar m_var = null_var;
ptr_vector<constraint> m_cjust_v; ptr_vector<constraint> m_cjust_v;
// TODO: instead of listing methods, create an abstract class for explanation methods, register them in a vector and call them in turn
/* Conflict explanation methods */
clause_ref by_polynomial_superposition(); clause_ref by_polynomial_superposition();
clause_ref by_ugt_x(); clause_ref by_ugt_x();
clause_ref by_ugt_y(); clause_ref by_ugt_y();
clause_ref by_ugt_z(); clause_ref by_ugt_z();
clause_ref y_ule_ax_and_x_ule_z(); clause_ref y_ule_ax_and_x_ule_z();
p_dependency_ref null_dep() const { return m_solver.mk_dep_ref(null_dependency); } p_dependency_ref null_dep();
// p_dependency_ref null_dep() const { return m_solver.mk_dep_ref(null_dependency); }
bool push_omega_mul(clause_builder& clause, unsigned level, unsigned p, pdd const& x, pdd const& y); bool push_omega_mul(clause_builder& clause, unsigned level, unsigned p, pdd const& x, pdd const& y);
public:
conflict_explainer(solver& s, constraints_and_clauses const& conflict);
public:
/** Create empty conflict */
conflict_explainer(solver& s);
/** resolve conflict state against assignment to v */
clause_ref resolve(pvar v, ptr_vector<constraint> const& cjust_v); clause_ref resolve(pvar v, ptr_vector<constraint> const& cjust_v);
// 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 */
void resolve();
}; };
} }

View file

@ -22,8 +22,8 @@ namespace polysat {
struct fi_record { struct fi_record {
eval_interval interval; eval_interval interval;
constraint_literal neg_cond; // could be multiple constraints later constraint_literal_ref neg_cond; // could be multiple constraints later
constraint* src; constraint_literal src;
}; };
/** /**
@ -64,24 +64,20 @@ namespace polysat {
return true; return true;
} }
bool forbidden_intervals::explain(solver& s, constraint_ref_vector const& conflict, pvar v, clause_ref& out_lemma) { bool forbidden_intervals::explain(solver& s, vector<constraint_literal> 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* c : conflict) { for (constraint_literal c : conflict) {
LOG_H3("Computing forbidden interval for: " << *c); LOG_H3("Computing forbidden interval for: " << c);
if (c->is_undef()) {
LOG("TODO: undef constraint in conflict... what does this mean???");
continue;
}
eval_interval interval = eval_interval::full(); eval_interval interval = eval_interval::full();
constraint_literal neg_cond; constraint_literal_ref neg_cond;
if (c->forbidden_interval(s, 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: " << show_deref(neg_cond)); LOG("neg_cond: " << neg_cond);
if (interval.is_currently_empty()) if (interval.is_currently_empty())
continue; continue;
if (interval.is_full()) if (interval.is_full())
@ -106,7 +102,7 @@ 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->blit()); clause.push_literal(~full_record.src.literal());
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();
@ -134,7 +130,7 @@ namespace polysat {
unsigned lemma_lvl = 0; unsigned lemma_lvl = 0;
for (unsigned i : seq) { for (unsigned i : seq) {
constraint* c = records[i].src; constraint_literal const& c = records[i].src;
lemma_lvl = std::max(lemma_lvl, c->level()); lemma_lvl = std::max(lemma_lvl, c->level());
} }
@ -149,7 +145,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->blit(); sat::literal src_lit = records[i].src.literal();
clause.push_literal(~src_lit); clause.push_literal(~src_lit);
} }
// Add side conditions and interval constraints // Add side conditions and interval constraints
@ -163,12 +159,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 c = ~s.m_constraints.ult(lemma_lvl, lhs, rhs); constraint_literal_ref 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& neg_cond = records[i].neg_cond; constraint_literal_ref& 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));
} }

View file

@ -22,7 +22,7 @@ namespace polysat {
class forbidden_intervals { class forbidden_intervals {
public: public:
static bool explain(solver& s, constraint_ref_vector const& conflict, pvar v, clause_ref& out_lemma); static bool explain(solver& s, vector<constraint_literal> const& conflict, pvar v, clause_ref& out_lemma);
}; };
} }

View file

@ -134,7 +134,6 @@ namespace polysat {
if (!fp) if (!fp)
return; return;
rational z(0), o(1); rational z(0), o(1);
SASSERT(!c.is_undef());
if (is_positive) if (is_positive)
fp->set_bounds(v, z, z, c_dep); fp->set_bounds(v, z, z, c_dep);
else else
@ -211,7 +210,6 @@ namespace polysat {
void linear_solver::activate_constraint(bool is_positive, constraint& c) { void linear_solver::activate_constraint(bool is_positive, constraint& c) {
m_active.push_back(&c); m_active.push_back(&c);
m_trail.push_back(trail_i::set_active_i); m_trail.push_back(trail_i::set_active_i);
SASSERT(!c.is_undef());
switch (c.kind()) { switch (c.kind()) {
case ckind_t::eq_t: case ckind_t::eq_t:
assert_eq(is_positive, c.to_eq()); assert_eq(is_positive, c.to_eq());

View file

@ -40,21 +40,6 @@ show_deref_t<T> show_deref(T* ptr) {
return show_deref_t<T>{ptr}; return show_deref_t<T>{ptr};
} }
// template <typename T>
// show_deref_t<T> show_deref(ref<T> const& ptr) {
// return show_deref_t<T>{ptr.get()};
// }
// template <typename T>
// show_deref_t<T> show_deref(scoped_ptr<T> const& ptr) {
// return show_deref_t<T>{ptr.get()};
// }
// template <typename Ptr, typename T = decltype(&*std::declval<Ptr>())>
// show_deref_t<T> show_deref(Ptr const& ptr) {
// return show_deref_t<T>{&*ptr};
// }
template <typename Ptr, typename T = typename std::remove_pointer<decltype(std::declval<Ptr>().get())>::type> template <typename Ptr, typename T = typename std::remove_pointer<decltype(std::declval<Ptr>().get())>::type>
show_deref_t<T> show_deref(Ptr const& ptr) { show_deref_t<T> show_deref(Ptr const& ptr) {
return show_deref_t<T>{ptr.get()}; return show_deref_t<T>{ptr.get()};

View file

@ -26,7 +26,6 @@ Author:
namespace polysat { namespace polysat {
dd::pdd_manager& solver::sz2pdd(unsigned sz) { dd::pdd_manager& solver::sz2pdd(unsigned sz) {
m_pdd.reserve(sz + 1); m_pdd.reserve(sz + 1);
if (!m_pdd[sz]) if (!m_pdd[sz])
@ -83,8 +82,8 @@ namespace polysat {
m_value.push_back(rational::zero()); m_value.push_back(rational::zero());
m_justification.push_back(justification::unassigned()); m_justification.push_back(justification::unassigned());
m_viable.push(sz); m_viable.push(sz);
m_cjust.push_back(constraints()); m_cjust.push_back({});
m_watch.push_back(ptr_vector<constraint>()); m_watch.push_back({});
m_activity.push_back(0); m_activity.push_back(0);
m_vars.push_back(sz2pdd(sz).mk_var(v)); m_vars.push_back(sz2pdd(sz).mk_var(v));
m_size.push_back(sz); m_size.push_back(sz);
@ -112,51 +111,52 @@ namespace polysat {
m_free_vars.del_var_eh(v); m_free_vars.del_var_eh(v);
} }
constraint_literal solver::mk_eq(pdd const& p) { constraint_literal_ref solver::mk_eq(pdd const& p) {
return m_constraints.eq(m_level, p); return m_constraints.eq(m_level, p);
} }
constraint_literal solver::mk_diseq(pdd const& p) { constraint_literal_ref solver::mk_diseq(pdd const& p) {
return ~m_constraints.eq(m_level, p); return ~m_constraints.eq(m_level, p);
} }
constraint_literal solver::mk_ule(pdd const& p, pdd const& q) { constraint_literal_ref 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 solver::mk_ult(pdd const& p, pdd const& q) { constraint_literal_ref 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 solver::mk_sle(pdd const& p, pdd const& q) { constraint_literal_ref 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 solver::mk_slt(pdd const& p, pdd const& q) { constraint_literal_ref 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 cl, unsigned dep, bool activate) { void solver::new_constraint(constraint_literal_ref cl, unsigned dep, bool activate) {
VERIFY(at_base_level()); VERIFY(at_base_level());
SASSERT(cl); SASSERT(cl);
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.
sat::literal lit = cl.literal(); constraint_literal c = cl.get();
constraint* c = cl.get();
clause* unit = m_constraints.store(clause::from_unit(std::move(cl), mk_dep_ref(dep))); clause* unit = m_constraints.store(clause::from_unit(std::move(cl), 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); m_constraints.register_external(c.get_constraint());
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); m_linear_solver.new_constraint(*c.constraint());
#endif #endif
if (activate && !is_conflict()) if (activate && !is_conflict())
activate_constraint_base(c, !lit.sign()); activate_constraint_base(c);
} }
void solver::assign_eh(unsigned dep, bool is_true) { void solver::assign_eh(unsigned dep, bool is_true) {
VERIFY(at_base_level()); VERIFY(at_base_level());
NOT_IMPLEMENTED_YET();
/*
constraint* c = m_constraints.lookup_external(dep); constraint* c = m_constraints.lookup_external(dep);
if (!c) { if (!c) {
LOG("WARN: there is no constraint for dependency " << dep); LOG("WARN: there is no constraint for dependency " << dep);
@ -164,7 +164,10 @@ namespace polysat {
} }
if (is_conflict()) if (is_conflict())
return; return;
activate_constraint_base(c, is_true); // TODO: this is wrong. (e.g., if the external constraint was negative) we need to store constraint_literals
constraint_literal cl{c, is_true};
activate_constraint_base(cl);
*/
} }
@ -199,10 +202,9 @@ 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* c = m_constraints.lookup(lit.var()); constraint_literal c = m_constraints.lookup(lit);
(void)c; (void)c;
SASSERT(c); SASSERT(c);
SASSERT(!c->is_undef());
// c->narrow(*this); // c->narrow(*this);
} }
@ -211,14 +213,14 @@ namespace polysat {
auto& wlist = m_watch[v]; auto& wlist = m_watch[v];
unsigned i = 0, j = 0, sz = wlist.size(); unsigned i = 0, j = 0, sz = wlist.size();
for (; i < sz && !is_conflict(); ++i) for (; i < sz && !is_conflict(); ++i)
if (!wlist[i]->propagate(*this, v)) if (!wlist[i].propagate(*this, v))
wlist[j++] = wlist[i]; wlist[j++] = wlist[i];
for (; i < sz; ++i) for (; i < sz; ++i)
wlist[j++] = wlist[i]; wlist[j++] = wlist[i];
wlist.shrink(j); wlist.shrink(j);
} }
void solver::propagate(pvar v, rational const& val, constraint& c) { void solver::propagate(pvar v, rational const& val, constraint_literal 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);
@ -274,8 +276,8 @@ 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* c = m_constraints.lookup(lit.var()); constraint_literal 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();
break; break;
@ -298,42 +300,44 @@ namespace polysat {
SASSERT(m_level == target_level); SASSERT(m_level == target_level);
} }
void solver::pop_constraints(ptr_vector<constraint>& cs) { void solver::pop_constraints(constraint_literals& 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());
cs.pop_back(); cs.pop_back();
} }
} }
void solver::add_watch(constraint& c) { void solver::add_watch(constraint_literal c) {
auto const& vars = c.vars(); SASSERT(c);
auto const& vars = c.get_constraint()->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 &c, pvar v) { void solver::add_watch(constraint_literal c, pvar v) {
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& c) { void solver::erase_watch(constraint_literal c) {
auto const& vars = c.vars(); auto const& vars = c.get_constraint()->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& c) { void solver::erase_watch(pvar v, constraint_literal c) {
if (v == null_var) if (v == null_var)
return; return;
auto& wlist = m_watch[v]; auto& wlist = m_watch[v];
unsigned sz = wlist.size(); unsigned sz = wlist.size();
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
if (&c == wlist[i]) { if (c == wlist[i]) {
wlist[i] = wlist.back(); wlist[i] = wlist.back();
wlist.pop_back(); wlist.pop_back();
return; return;
@ -388,49 +392,27 @@ namespace polysat {
#endif #endif
} }
void solver::set_conflict(constraint& c) { void solver::set_conflict(constraint_literal c) {
LOG("Conflict: " << c); m_conflict.set(c);
LOG("\n" << *this);
SASSERT(!is_conflict());
m_conflict.push_back(&c);
}
void solver::set_conflict(clause& cl) {
LOG("Conflict: " << cl);
SASSERT(!is_conflict());
m_conflict.push_back(&cl);
} }
void solver::set_conflict(pvar v) { void solver::set_conflict(pvar v) {
SASSERT(!is_conflict()); m_conflict.set(v, m_cjust[v]);
m_conflict.append(m_cjust[v]); }
if (m_cjust[v].empty())
m_conflict.push_back(nullptr); void solver::set_marks(conflict_core const& cc) {
LOG("Conflict for v" << v << ": " << m_conflict); for (auto c : cc.constraints())
if (c)
set_marks(*c);
} }
void solver::set_marks(constraint const& c) { void solver::set_marks(constraint const& c) {
LOG_V("Marking in: " << c);
if (c.bvar() != sat::null_bool_var) if (c.bvar() != sat::null_bool_var)
m_bvars.set_mark(c.bvar()); m_bvars.set_mark(c.bvar());
for (auto v : c.vars()) for (auto v : c.vars())
set_mark(v); set_mark(v);
} }
void solver::set_marks(clause const& cl) {
LOG_V("Marking in: " << cl);
for (auto lit : cl)
set_marks(*m_constraints.lookup(lit.var()));
}
void solver::set_marks(constraints_and_clauses const& cc) {
for (auto c : cc.units())
if (c)
set_marks(*c);
for (auto cl : cc.clauses())
set_marks(*cl);
}
/** /**
* Conflict resolution. * Conflict resolution.
* - m_conflict are constraints that are infeasible in the current assignment. * - m_conflict are constraints that are infeasible in the current assignment.
@ -456,6 +438,9 @@ namespace polysat {
SASSERT(is_conflict()); SASSERT(is_conflict());
NOT_IMPLEMENTED_YET(); // TODO: needs to be refactored to use conflict_core, will be moved to conflict_explainer
/*
if (m_conflict.units().size() == 1 && !m_conflict.units()[0]) { if (m_conflict.units().size() == 1 && !m_conflict.units()[0]) {
report_unsat(); report_unsat();
return; return;
@ -595,9 +580,12 @@ namespace polysat {
} }
} }
report_unsat(); report_unsat();
*/
} }
clause_ref solver::resolve_bool(sat::literal lit) { clause_ref solver::resolve_bool(sat::literal lit) {
NOT_IMPLEMENTED_YET(); return nullptr;
/*
if (m_conflict.size() != 1) if (m_conflict.size() != 1)
return nullptr; return nullptr;
if (m_conflict.clauses().size() != 1) if (m_conflict.clauses().size() != 1)
@ -623,9 +611,12 @@ namespace polysat {
} }
return lemma; // currently modified in-place return lemma; // currently modified in-place
*/
} }
void solver::backtrack(unsigned i, clause_ref lemma) { void solver::backtrack(unsigned i, clause_ref lemma) {
NOT_IMPLEMENTED_YET();
/*
do { do {
auto const& item = m_search[i]; auto const& item = m_search[i];
if (item.is_assignment()) { if (item.is_assignment()) {
@ -676,6 +667,7 @@ namespace polysat {
while (i-- > 0); while (i-- > 0);
add_lemma(lemma); // TODO: this lemma is stored but otherwise "lost" because it will not be activated / not added to any watch data structures add_lemma(lemma); // TODO: this lemma is stored but otherwise "lost" because it will not be activated / not added to any watch data structures
report_unsat(); report_unsat();
*/
} }
void solver::report_unsat() { void solver::report_unsat() {
@ -684,6 +676,8 @@ namespace polysat {
} }
void solver::unsat_core(unsigned_vector& deps) { void solver::unsat_core(unsigned_vector& deps) {
NOT_IMPLEMENTED_YET(); // TODO: needs to be fixed to work with conflict_core
/*
deps.reset(); deps.reset();
p_dependency_ref conflict_dep(m_dm); p_dependency_ref conflict_dep(m_dm);
for (auto& c : m_conflict.units()) for (auto& c : m_conflict.units())
@ -692,6 +686,7 @@ namespace polysat {
for (auto& c : m_conflict.clauses()) for (auto& c : m_conflict.clauses())
conflict_dep = m_dm.mk_join(c->dep(), conflict_dep); conflict_dep = m_dm.mk_join(c->dep(), conflict_dep);
m_dm.linearize(conflict_dep, deps); m_dm.linearize(conflict_dep, deps);
*/
} }
void solver::learn_lemma(pvar v, clause_ref lemma) { void solver::learn_lemma(pvar v, clause_ref lemma) {
@ -704,15 +699,15 @@ 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* c = m_constraints.lookup(lit.var()); constraint_literal 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, !lit.sign()); activate_constraint_base(c);
} }
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* c = m_constraints.lookup(lit.var()); constraint_literal c = m_constraints.lookup(lit);
push_cjust(v, c); push_cjust(v, c);
} }
} }
@ -729,10 +724,9 @@ 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* c = m_constraints.lookup(lit.var()); constraint_literal c = m_constraints.lookup(lit);
tmp_assign _t(c, 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);
}; };
sat::literal choice = sat::null_literal; sat::literal choice = sat::null_literal;
@ -747,11 +741,12 @@ namespace polysat {
} }
LOG_V("num_choices: " << num_choices); LOG_V("num_choices: " << num_choices);
if (num_choices == 0) if (num_choices == 0) {
// This case may happen when all undefined literals are false under the current variable assignment. // This case may happen when all undefined literals are false under the current variable assignment.
// TODO: The question is whether such lemmas should be generated? Check test_monot() for such a case. // TODO: The question is whether such lemmas should be generated? Check test_monot() for such a case.
set_conflict(lemma); // set_conflict(lemma);
else if (num_choices == 1) NOT_IMPLEMENTED_YET();
} else if (num_choices == 1)
propagate_bool(choice, &lemma); propagate_bool(choice, &lemma);
else else
decide_bool(choice, lemma); decide_bool(choice, lemma);
@ -772,6 +767,8 @@ namespace polysat {
void solver::revert_decision(pvar v, clause_ref reason) { void solver::revert_decision(pvar v, clause_ref reason) {
rational val = m_value[v]; rational val = m_value[v];
LOG_H3("Reverting decision: pvar " << v << " := " << val); LOG_H3("Reverting decision: pvar " << v << " := " << val);
NOT_IMPLEMENTED_YET();
/*
SASSERT(m_justification[v].is_decision()); SASSERT(m_justification[v].is_decision());
backjump(m_justification[v].level()-1); backjump(m_justification[v].level()-1);
@ -816,6 +813,7 @@ namespace polysat {
m_free_vars.del_var_eh(v); m_free_vars.del_var_eh(v);
decide(v); decide(v);
} }
*/
} }
void solver::revert_bool_decision(sat::literal lit, clause_ref reason) { void solver::revert_bool_decision(sat::literal lit, clause_ref reason) {
@ -823,6 +821,9 @@ namespace polysat {
LOG_H3("Reverting boolean decision: " << lit); LOG_H3("Reverting boolean decision: " << lit);
SASSERT(m_bvars.is_decision(var)); SASSERT(m_bvars.is_decision(var));
NOT_IMPLEMENTED_YET();
/*
if (reason) { if (reason) {
LOG("Reason: " << show_deref(reason)); LOG("Reason: " << show_deref(reason));
bool contains_var = std::any_of(reason->begin(), reason->end(), [var](sat::literal reason_lit) { return reason_lit.var() == var; }); bool contains_var = std::any_of(reason->begin(), reason->end(), [var](sat::literal reason_lit) { return reason_lit.var() == var; });
@ -890,6 +891,7 @@ namespace polysat {
} }
decide_bool(*lemma); decide_bool(*lemma);
*/
} }
void solver::decide_bool(sat::literal lit, clause& lemma) { void solver::decide_bool(sat::literal lit, clause& lemma) {
@ -903,9 +905,9 @@ 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* c = m_constraints.lookup(lit.var()); constraint_literal c = m_constraints.lookup(lit);
// m_redundant.push_back(c); // m_redundant.push_back(c);
activate_constraint_base(c, !lit.sign()); activate_constraint_base(c);
} }
else else
assign_bool_backtrackable(lit, reason, nullptr); assign_bool_backtrackable(lit, reason, nullptr);
@ -919,22 +921,19 @@ 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* c = m_constraints.lookup(lit.var()); constraint_literal c = m_constraints.lookup(lit);
SASSERT(c); activate_constraint(c);
bool is_true = !lit.sign();
activate_constraint(*c, is_true);
} }
/// 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* c, bool is_true) { void solver::activate_constraint_base(constraint_literal 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);
sat::literal lit(c->bvar(), !is_true); assign_bool_core(c.literal(), nullptr, nullptr);
assign_bool_core(lit, nullptr, nullptr); activate_constraint(c);
activate_constraint(*c, is_true);
} }
/// Assign a boolean literal /// Assign a boolean literal
@ -949,22 +948,21 @@ 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& c, bool is_true) { void solver::activate_constraint(constraint_literal c) {
LOG("Activating constraint: " << c << " ; is_true = " << is_true); SASSERT(c);
SASSERT(m_bvars.value(c.bvar()) == to_lbool(is_true)); LOG("Activating constraint: " << c);
c.assign(is_true); SASSERT(m_bvars.value(c.literal()) == 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); m_linear_solver.activate_constraint(c.is_positive(), c.constraint()); // TODO: linear solver should probably take a constraint_literal
#endif #endif
} }
/// Deactivate constraint /// Deactivate constraint
void solver::deactivate_constraint(constraint& c) { void solver::deactivate_constraint(constraint_literal c) {
LOG("Deactivating constraint: " << c); LOG("Deactivating constraint: " << c);
erase_watch(c); erase_watch(c);
c.unassign();
} }
void solver::backjump(unsigned new_level) { void solver::backjump(unsigned new_level) {
@ -973,23 +971,6 @@ namespace polysat {
if (num_levels > 0) if (num_levels > 0)
pop_levels(num_levels); pop_levels(num_levels);
} }
/**
* Return residue of superposing p and q with respect to v.
*/
clause_ref solver::resolve(pvar v) {
LOG_H3("Resolve v" << v);
SASSERT(!m_cjust[v].empty());
SASSERT(m_justification[v].is_propagation());
LOG("Conflict: " << m_conflict);
LOG("cjust[v" << v << "]: " << m_cjust[v]);
conflict_explainer cx(*this, m_conflict);
clause_ref res = cx.resolve(v, m_cjust[v]);
LOG("resolved: " << show_deref(res));
// SASSERT(false && "pause on explanation");
return res;
}
/** /**
* placeholder for factoring/gcd common factors * placeholder for factoring/gcd common factors
@ -1007,18 +988,18 @@ 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* c = m_constraints.lookup(cl->literals()[0].var()); constraint_literal c = m_constraints.lookup(cl->literals()[0]);
insert_constraint(m_redundant, c); insert_constraint(m_redundant, c);
} }
} }
void solver::insert_constraint(ptr_vector<constraint>& cs, constraint* c) { void solver::insert_constraint(constraint_literals& cs, constraint_literal c) {
SASSERT(c); SASSERT(c);
LOG_V("INSERTING: " << *c); LOG_V("INSERTING: " << c);
cs.push_back(c); cs.push_back(c);
for (unsigned i = cs.size() - 1; i-- > 0; ) { for (unsigned i = cs.size() - 1; i-- > 0; ) {
auto* c1 = cs[i + 1]; auto c1 = cs[i + 1];
auto* c2 = cs[i]; auto c2 = cs[i];
if (c1->level() >= c2->level()) if (c1->level() >= c2->level())
break; break;
std::swap(cs[i], cs[i+1]); std::swap(cs[i], cs[i+1]);
@ -1088,11 +1069,11 @@ namespace polysat {
} }
out << "Boolean assignment:\n\t" << m_bvars << "\n"; out << "Boolean assignment:\n\t" << m_bvars << "\n";
out << "Original:\n"; out << "Original:\n";
for (auto* c : m_original) for (auto c : m_original)
out << "\t" << *c << "\n"; out << "\t" << c << "\n";
out << "Redundant:\n"; out << "Redundant:\n";
for (auto* c : m_redundant) for (auto c : m_redundant)
out << "\t" << *c << "\n"; out << "\t" << c << "\n";
out << "Redundant clauses:\n"; out << "Redundant clauses:\n";
for (auto* cl : m_redundant_clauses) { for (auto* cl : m_redundant_clauses) {
out << "\t" << *cl << "\n"; out << "\t" << *cl << "\n";
@ -1136,7 +1117,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(ptr_vector<constraint> const& cs) { bool solver::invariant(constraint_literals 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());
@ -1147,12 +1128,10 @@ 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() {
constraints cs; constraint_literals 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) {
if (c->is_undef())
continue;
int64_t num_watches = 0; int64_t num_watches = 0;
for (auto const& wlist : m_watch) { for (auto const& wlist : m_watch) {
auto n = std::count(wlist.begin(), wlist.end(), c); auto n = std::count(wlist.begin(), wlist.end(), c);
@ -1172,12 +1151,13 @@ namespace polysat {
bool solver::verify_sat() { bool solver::verify_sat() {
LOG_H1("Checking current model..."); LOG_H1("Checking current model...");
LOG("Assignment: " << assignments_pp(*this)); LOG("Assignment: " << assignments_pp(*this));
for (auto* c : m_original) { bool all_ok = true;
bool ok = c->is_currently_true(*this); for (auto c : m_original) {
LOG((ok ? "PASS" : "FAIL") << ": " << show_deref(c)); bool ok = c.is_currently_true(*this);
if (!ok) return false; LOG((ok ? "PASS" : "FAIL") << ": " << c);
all_ok = all_ok && ok;
} }
LOG("All good!"); if (all_ok) LOG("All good!");
return true; return true;
} }
} }

View file

@ -21,9 +21,11 @@ Author:
#include "util/statistics.h" #include "util/statistics.h"
#include "util/params.h" #include "util/params.h"
#include "math/polysat/boolean.h" #include "math/polysat/boolean.h"
#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/eq_constraint.h" #include "math/polysat/eq_constraint.h"
#include "math/polysat/explain.h"
#include "math/polysat/ule_constraint.h" #include "math/polysat/ule_constraint.h"
#include "math/polysat/justification.h" #include "math/polysat/justification.h"
#include "math/polysat/linear_solver.h" #include "math/polysat/linear_solver.h"
@ -59,6 +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;
reslimit& m_lim; reslimit& m_lim;
params_ref m_params; params_ref m_params;
@ -68,7 +71,7 @@ namespace polysat {
small_object_allocator m_alloc; small_object_allocator m_alloc;
poly_dep_manager m_dm; poly_dep_manager m_dm;
linear_solver m_linear_solver; linear_solver m_linear_solver;
constraints_and_clauses m_conflict; conflict_core m_conflict;
// constraints m_stash_just; // constraints m_stash_just;
var_queue m_free_vars; var_queue m_free_vars;
stats m_stats; stats m_stats;
@ -81,8 +84,8 @@ namespace polysat {
// Per constraint state // Per constraint state
constraint_manager m_constraints; constraint_manager m_constraints;
ptr_vector<constraint> m_original; constraint_literals m_original;
ptr_vector<constraint> m_redundant; constraint_literals 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;
@ -90,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<constraints> m_cjust; // constraints justifying variable range. vector<constraint_literals> m_cjust; // constraints justifying variable range.
vector<constraints> m_watch; // watch list datastructure into constraints. vector<constraint_literals> 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.
@ -125,12 +128,12 @@ namespace polysat {
m_qhead_trail.pop_back(); m_qhead_trail.pop_back();
} }
void push_cjust(pvar v, constraint* c) { void push_cjust(pvar v, constraint_literal 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 << "] += " << show_deref(c)); LOG_V("cjust[v" << v << "] += " << c);
SASSERT(c); SASSERT(c);
m_cjust[v].push_back(c); m_cjust[v].push_back(c);
m_trail.push_back(trail_instr_t::just_i); m_trail.push_back(trail_instr_t::just_i);
m_cjust_trail.push_back(v); m_cjust_trail.push_back(v);
} }
@ -148,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(ptr_vector<constraint>& cs); void pop_constraints(constraint_literals& 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* c, bool is_true); void activate_constraint_base(constraint_literal 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& c, bool is_true); void activate_constraint(constraint_literal c);
void deactivate_constraint(constraint& c); void deactivate_constraint(constraint_literal 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);
@ -168,14 +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& c); void propagate(pvar v, rational const& val, constraint_literal c);
void erase_watch(pvar v, constraint& c); void erase_watch(pvar v, constraint_literal c);
void erase_watch(constraint& c); void erase_watch(constraint_literal c);
void add_watch(constraint& c); void add_watch(constraint_literal c);
void add_watch(constraint& c, pvar v); void add_watch(constraint_literal c, pvar v);
void set_conflict(constraint& c); void set_conflict(constraint_literal c);
void set_conflict(clause& cl);
void set_conflict(pvar v); void set_conflict(pvar v);
unsigned_vector m_marks; unsigned_vector m_marks;
@ -184,13 +186,11 @@ namespace polysat {
bool is_marked(pvar v) const { return m_clock == m_marks[v]; } bool is_marked(pvar v) const { return m_clock == m_marks[v]; }
void set_mark(pvar v) { LOG_V("Marking: v" << v); m_marks[v] = m_clock; } void set_mark(pvar v) { LOG_V("Marking: v" << v); m_marks[v] = m_clock; }
void set_marks(constraints_and_clauses const& cc); void set_marks(conflict_core const& cc);
void set_marks(constraint const& c); void set_marks(constraint const& c);
void set_marks(clause const& cl);
unsigned m_conflict_level { 0 }; unsigned m_conflict_level { 0 };
clause_ref resolve(pvar v);
clause_ref resolve_bool(sat::literal lit); clause_ref resolve_bool(sat::literal lit);
bool can_decide() const { return !m_free_vars.empty(); } bool can_decide() const { return !m_free_vars.empty(); }
@ -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 mk_eq(pdd const& p); constraint_literal_ref mk_eq(pdd const& p);
constraint_literal mk_diseq(pdd const& p); constraint_literal_ref mk_diseq(pdd const& p);
constraint_literal mk_ule(pdd const& p, pdd const& q); constraint_literal_ref mk_ule(pdd const& p, pdd const& q);
constraint_literal mk_ult(pdd const& p, pdd const& q); constraint_literal_ref mk_ult(pdd const& p, pdd const& q);
constraint_literal mk_sle(pdd const& p, pdd const& q); constraint_literal_ref mk_sle(pdd const& p, pdd const& q);
constraint_literal mk_slt(pdd const& p, pdd const& q); constraint_literal_ref mk_slt(pdd const& p, pdd const& q);
void new_constraint(constraint_literal c, unsigned dep, bool activate); void new_constraint(constraint_literal_ref c, unsigned dep, bool activate);
static void insert_constraint(ptr_vector<constraint>& cs, constraint* c); static void insert_constraint(constraint_literals& cs, constraint_literal c);
bool invariant(); bool invariant();
static bool invariant(ptr_vector<constraint> const& cs); static bool invariant(constraint_literals const& cs);
bool wlist_invariant(); bool wlist_invariant();
bool verify_sat(); bool verify_sat();

View file

@ -18,12 +18,16 @@ Author:
namespace polysat { namespace polysat {
std::ostream& ule_constraint::display(std::ostream& out) const { std::ostream& ule_constraint::display(std::ostream& out, lbool status) const {
out << m_lhs << " <= " << m_rhs; out << m_lhs;
return display_extra(out); if (status == l_true) out << " <= ";
else if (status == l_false) out << " > ";
else out << " <=/> ";
out << m_rhs;
return display_extra(out, status);
} }
void ule_constraint::narrow(solver& s) { void ule_constraint::narrow(solver& s, bool is_positive) {
LOG_H3("Narrowing " << *this); LOG_H3("Narrowing " << *this);
LOG("Assignment: " << assignments_pp(s)); LOG("Assignment: " << assignments_pp(s));
auto p = lhs().subst_val(s.assignment()); auto p = lhs().subst_val(s.assignment());
@ -31,15 +35,13 @@ namespace polysat {
auto q = rhs().subst_val(s.assignment()); auto q = rhs().subst_val(s.assignment());
LOG("Substituted RHS: " << rhs() << " := " << q); LOG("Substituted RHS: " << rhs() << " := " << q);
SASSERT(!is_undef()); if (is_always_false(is_positive, p, q)) {
s.set_conflict({this, is_positive});
if (is_always_false(p, q)) {
s.set_conflict(*this);
return; return;
} }
if (p.is_val() && q.is_val()) { if (p.is_val() && q.is_val()) {
SASSERT(!is_positive() || p.val() <= q.val()); SASSERT(!is_positive || p.val() <= q.val());
SASSERT(!is_negative() || p.val() > q.val()); SASSERT(is_positive || p.val() > q.val());
return; return;
} }
@ -70,13 +72,13 @@ namespace polysat {
d = q.lo().val(); d = q.lo().val();
} }
if (v != null_var) { if (v != null_var) {
s.push_cjust(v, this); s.push_cjust(v, {this, is_positive});
s.m_viable.intersect_ule(v, a, b, c, d, is_positive()); s.m_viable.intersect_ule(v, a, b, c, d, is_positive);
rational val; rational val;
if (s.m_viable.find_viable(v, val) == dd::find_t::singleton) if (s.m_viable.find_viable(v, val) == dd::find_t::singleton)
s.propagate(v, val, *this); s.propagate(v, val, {this, is_positive});
return; return;
} }
@ -84,39 +86,35 @@ namespace polysat {
// TODO: other cheap constraints possible? // TODO: other cheap constraints possible?
} }
bool ule_constraint::is_always_false(pdd const& lhs, pdd const& rhs) { bool ule_constraint::is_always_false(bool is_positive, pdd const& lhs, pdd const& rhs) {
// TODO: other conditions (e.g. when forbidden interval would be full) // TODO: other conditions (e.g. when forbidden interval would be full)
VERIFY(!is_undef()); if (is_positive)
if (is_positive())
return lhs.is_val() && rhs.is_val() && lhs.val() > rhs.val(); return lhs.is_val() && rhs.is_val() && lhs.val() > rhs.val();
else else
return (lhs.is_val() && rhs.is_val() && lhs.val() <= rhs.val()) || (lhs == rhs); return (lhs.is_val() && rhs.is_val() && lhs.val() <= rhs.val()) || (lhs == rhs);
} }
bool ule_constraint::is_always_false() { bool ule_constraint::is_always_false(bool is_positive) {
return is_always_false(lhs(), rhs()); return is_always_false(is_positive, lhs(), rhs());
} }
bool ule_constraint::is_currently_false(solver& s) { bool ule_constraint::is_currently_false(solver& s, bool is_positive) {
auto p = lhs().subst_val(s.assignment()); auto p = lhs().subst_val(s.assignment());
auto q = rhs().subst_val(s.assignment()); auto q = rhs().subst_val(s.assignment());
return is_always_false(p, q); return is_always_false(is_positive, p, q);
} }
bool ule_constraint::is_currently_true(solver& s) { bool ule_constraint::is_currently_true(solver& s, bool is_positive) {
auto p = lhs().subst_val(s.assignment()); auto p = lhs().subst_val(s.assignment());
auto q = rhs().subst_val(s.assignment()); auto q = rhs().subst_val(s.assignment());
VERIFY(!is_undef()); if (is_positive)
if (is_positive())
return p.is_val() && q.is_val() && p.val() <= q.val(); return p.is_val() && q.is_val() && p.val() <= q.val();
else else
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, pvar v, eval_interval& out_interval, constraint_literal& out_neg_cond) bool ule_constraint::forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond)
{ {
SASSERT(!is_undef());
// 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);
unsigned const deg2 = rhs().degree(v); unsigned const deg2 = rhs().degree(v);
@ -249,7 +247,7 @@ namespace polysat {
out_neg_cond = s.m_constraints.eq(level(), condition_body); out_neg_cond = s.m_constraints.eq(level(), condition_body);
if (is_trivial) { if (is_trivial) {
if (is_positive()) if (is_positive)
// TODO: we cannot use empty intervals for interpolation. So we // TODO: we cannot use empty intervals for interpolation. So we
// can remove the empty case (make it represent 'full' instead), // can remove the empty case (make it represent 'full' instead),
// and return 'false' here. Then we do not need the proper/full // and return 'false' here. Then we do not need the proper/full
@ -276,7 +274,7 @@ namespace polysat {
else else
SASSERT(y_coeff.is_one()); SASSERT(y_coeff.is_one());
if (is_negative()) { if (!is_positive) {
swap(lo, hi); swap(lo, hi);
lo_val.swap(hi_val); lo_val.swap(hi_val);
} }
@ -286,9 +284,8 @@ namespace polysat {
return true; return true;
} }
inequality ule_constraint::as_inequality() const { inequality ule_constraint::as_inequality(bool is_positive) const {
SASSERT(!is_undef()); if (is_positive)
if (is_positive())
return inequality(lhs(), rhs(), false, this); return inequality(lhs(), rhs(), false, this);
else else
return inequality(rhs(), lhs(), true, this); return inequality(rhs(), lhs(), true, this);

View file

@ -31,14 +31,14 @@ namespace polysat {
~ule_constraint() override {} ~ule_constraint() override {}
pdd const& lhs() const { return m_lhs; } pdd const& lhs() const { return m_lhs; }
pdd const& rhs() const { return m_rhs; } pdd const& rhs() const { return m_rhs; }
std::ostream& display(std::ostream& out) const override; std::ostream& display(std::ostream& out, lbool status) const override;
bool is_always_false(pdd const& lhs, pdd const& rhs); bool is_always_false(bool is_positive, pdd const& lhs, pdd const& rhs);
bool is_always_false() override; bool is_always_false(bool is_positive) override;
bool is_currently_false(solver& s) override; bool is_currently_false(solver& s, bool is_positive) override;
bool is_currently_true(solver& s) override; bool is_currently_true(solver& s, bool is_positive) override;
void narrow(solver& s) override; void narrow(solver& s, bool is_positive) override;
bool forbidden_interval(solver& s, pvar v, eval_interval& out_interval, constraint_literal& out_neg_cond) override; bool forbidden_interval(solver& s, bool is_positive, pvar v, eval_interval& out_interval, constraint_literal_ref& out_neg_cond) override;
inequality as_inequality() 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;
}; };

View file

@ -11,11 +11,12 @@ namespace polysat {
typedef uint64_ext::numeral numeral; typedef uint64_ext::numeral numeral;
struct solver_scope { struct fp_scope {
params_ref p; params_ref p;
reslimit lim; reslimit lim;
}; };
struct scoped_fp : public solver_scope, public fixplex<uint64_ext> {
struct scoped_fp : public fp_scope, public fixplex<uint64_ext> {
scoped_fp(): fixplex<uint64_ext>(p, lim) {} scoped_fp(): fixplex<uint64_ext>(p, lim) {}