mirror of
https://github.com/Z3Prover/z3
synced 2025-06-21 05:13:39 +00:00
Track existing constraints with indexed_uint_set
This commit is contained in:
parent
6c8e8dada6
commit
bb227c0d6e
4 changed files with 113 additions and 42 deletions
|
@ -16,21 +16,14 @@ Notes:
|
||||||
|
|
||||||
TODO: try a final core reduction step or other core minimization
|
TODO: try a final core reduction step or other core minimization
|
||||||
|
|
||||||
|
|
||||||
TODO: maybe implement by marking literals instead (like SAT solvers are doing); if we need to iterate, keep an indexed_uint_set (from util/uint_set.h)
|
|
||||||
(i.e., instead of keeping an explicit list of constraints as core, we just mark them.)
|
|
||||||
(we still need the list though, for new/temporary constraints.)
|
|
||||||
The approach would be as follows:
|
|
||||||
m_vars - set of variables used in conflict
|
|
||||||
m_constraints - set of new constraints used in conflict
|
|
||||||
indexed_uint_set - for Boolean variables that are in the conflict
|
|
||||||
When iterating over the core acessing the uint_set would require some support
|
|
||||||
|
|
||||||
TODO: fallback lemma is redundant:
|
TODO: fallback lemma is redundant:
|
||||||
The core lemma uses m_vars and m_conflict directly instead of walking the stack.
|
The core lemma uses m_vars and m_conflict directly instead of walking the stack.
|
||||||
It should be an invariant that the core is false (the negation of the core is valid modulo assertions).
|
It should be an invariant that the core is false (the negation of the core is valid modulo assertions).
|
||||||
The fallback lemma prunes at least the last value assignment.
|
The fallback lemma prunes at least the last value assignment.
|
||||||
|
|
||||||
|
TODO: If we have e.g. 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.
|
||||||
|
(works currently if x is unassigned; for other cases we would need extra info from constraint::is_currently_false)
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
#include "math/polysat/conflict_core.h"
|
#include "math/polysat/conflict_core.h"
|
||||||
|
@ -58,7 +51,7 @@ namespace polysat {
|
||||||
|
|
||||||
conflict_core::~conflict_core() {}
|
conflict_core::~conflict_core() {}
|
||||||
|
|
||||||
constraint_manager& conflict_core::cm() { return s().m_constraints; }
|
constraint_manager& conflict_core::cm() const { return s().m_constraints; }
|
||||||
|
|
||||||
std::ostream& conflict_core::display(std::ostream& out) const {
|
std::ostream& conflict_core::display(std::ostream& out) const {
|
||||||
char const* sep = "";
|
char const* sep = "";
|
||||||
|
@ -71,6 +64,19 @@ namespace polysat {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void conflict_core::reset() {
|
||||||
|
for (auto c : *this)
|
||||||
|
unset_mark(c.get());
|
||||||
|
m_constraints.reset();
|
||||||
|
m_literals.reset();
|
||||||
|
m_vars.reset();
|
||||||
|
m_conflict_var = null_var;
|
||||||
|
m_saturation_premises.reset();
|
||||||
|
m_bailout = false;
|
||||||
|
m_bailout_lemma.reset();
|
||||||
|
SASSERT(empty());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constraint is false under the current assignment of variables.
|
* The constraint is false under the current assignment of variables.
|
||||||
* The core is then the conjuction of this constraint and assigned variables.
|
* The core is then the conjuction of this constraint and assigned variables.
|
||||||
|
@ -108,17 +114,25 @@ namespace polysat {
|
||||||
if (c->is_marked())
|
if (c->is_marked())
|
||||||
return;
|
return;
|
||||||
set_mark(c.get());
|
set_mark(c.get());
|
||||||
m_constraints.push_back(c);
|
if (c->has_bvar())
|
||||||
|
insert_literal(c.blit());
|
||||||
|
else
|
||||||
|
m_constraints.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict_core::insert(signed_constraint c, vector<signed_constraint> premises) {
|
void conflict_core::insert(signed_constraint c, vector<signed_constraint> premises) {
|
||||||
insert(c);
|
insert(c);
|
||||||
m_saturation_premises.insert(c, std::move(premises)); // TODO: map doesn't have move-insertion, so this still copies the vector. Maybe we want a clause_ref (but this doesn't work either since c doesn't have a boolean variable yet).
|
m_saturation_premises.insert(c, std::move(premises)); // TODO: map doesn't have move-insertion, so this still copies the vector.
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict_core::remove(signed_constraint c) {
|
void conflict_core::remove(signed_constraint c) {
|
||||||
unset_mark(c.get());
|
unset_mark(c.get());
|
||||||
m_constraints.erase(c);
|
if (c->has_bvar()) {
|
||||||
|
SASSERT(std::count(m_constraints.begin(), m_constraints.end(), c) == 0);
|
||||||
|
remove_literal(c.blit());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_constraints.erase(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict_core::replace(signed_constraint c_old, signed_constraint c_new, vector<signed_constraint> c_new_premises) {
|
void conflict_core::replace(signed_constraint c_old, signed_constraint c_new, vector<signed_constraint> c_new_premises) {
|
||||||
|
@ -169,7 +183,11 @@ namespace polysat {
|
||||||
|
|
||||||
/** If the constraint c is a temporary constraint derived by core saturation, insert it (and recursively, its premises) into \Gamma */
|
/** If the constraint c is a temporary constraint derived by core saturation, insert it (and recursively, its premises) into \Gamma */
|
||||||
void conflict_core::keep(signed_constraint c) {
|
void conflict_core::keep(signed_constraint c) {
|
||||||
cm().ensure_bvar(c.get());
|
if (!c->has_bvar()) {
|
||||||
|
m_constraints.erase(c);
|
||||||
|
cm().ensure_bvar(c.get());
|
||||||
|
insert_literal(c.blit());
|
||||||
|
}
|
||||||
LOG_H3("keeping: " << c);
|
LOG_H3("keeping: " << c);
|
||||||
// NOTE: maybe we should skip intermediate steps and just collect the leaf premises for c?
|
// NOTE: maybe we should skip intermediate steps and just collect the leaf premises for c?
|
||||||
auto it = m_saturation_premises.find_iterator(c);
|
auto it = m_saturation_premises.find_iterator(c);
|
||||||
|
@ -372,4 +390,13 @@ namespace polysat {
|
||||||
bool conflict_core::is_bmarked(sat::bool_var b) const {
|
bool conflict_core::is_bmarked(sat::bool_var b) const {
|
||||||
return m_bvar2mark.get(b, false);
|
return m_bvar2mark.get(b, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void conflict_core::insert_literal(sat::literal lit) {
|
||||||
|
m_literals.insert(lit.to_uint());
|
||||||
|
}
|
||||||
|
|
||||||
|
void conflict_core::remove_literal(sat::literal lit) {
|
||||||
|
m_literals.remove(lit.to_uint());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,20 +22,18 @@ namespace polysat {
|
||||||
class explainer;
|
class explainer;
|
||||||
class inference_engine;
|
class inference_engine;
|
||||||
class variable_elimination_engine;
|
class variable_elimination_engine;
|
||||||
|
class conflict_core_iterator;
|
||||||
|
|
||||||
/** Conflict state, represented as core (~negation of clause). */
|
/** Conflict state, represented as core (~negation of clause). */
|
||||||
class conflict_core {
|
class conflict_core {
|
||||||
vector<signed_constraint> m_constraints; // constraints used as premises
|
signed_constraints m_constraints; // new constraints used as premises
|
||||||
uint_set m_vars; // variable assignments used as premises
|
indexed_uint_set m_literals; // set of boolean literals in the conflict
|
||||||
|
uint_set m_vars; // variable assignments used as premises
|
||||||
|
|
||||||
// If this is not null_var, the conflict was due to empty viable set for this variable.
|
// 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.
|
// Can be treated like "v = x" for any value x.
|
||||||
pvar m_conflict_var = null_var;
|
pvar m_conflict_var = null_var;
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
unsigned_vector m_pvar2count; // reference count of variables
|
unsigned_vector m_pvar2count; // reference count of variables
|
||||||
void inc_pref(pvar v);
|
void inc_pref(pvar v);
|
||||||
void dec_pref(pvar v);
|
void dec_pref(pvar v);
|
||||||
|
@ -47,14 +45,16 @@ namespace polysat {
|
||||||
void set_mark(constraint* c);
|
void set_mark(constraint* c);
|
||||||
void unset_mark(constraint* c);
|
void unset_mark(constraint* c);
|
||||||
|
|
||||||
|
void insert_literal(sat::literal lit);
|
||||||
|
void remove_literal(sat::literal lit);
|
||||||
|
|
||||||
/** Whether we are in a bailout state. We enter a bailout state when we give up on proper conflict resolution. */
|
/** Whether we are in a bailout state. We enter a bailout state when we give up on proper conflict resolution. */
|
||||||
bool m_bailout = false;
|
bool m_bailout = false;
|
||||||
std::optional<clause_builder> m_bailout_lemma;
|
std::optional<clause_builder> m_bailout_lemma;
|
||||||
|
|
||||||
solver* m_solver = nullptr;
|
solver* m_solver = nullptr;
|
||||||
solver& s() { return *m_solver; }
|
solver& s() const { return *m_solver; }
|
||||||
constraint_manager& cm();
|
constraint_manager& cm() const;
|
||||||
scoped_ptr_vector<explainer> ex_engines;
|
scoped_ptr_vector<explainer> ex_engines;
|
||||||
scoped_ptr_vector<variable_elimination_engine> ve_engines;
|
scoped_ptr_vector<variable_elimination_engine> ve_engines;
|
||||||
scoped_ptr_vector<inference_engine> inf_engines;
|
scoped_ptr_vector<inference_engine> inf_engines;
|
||||||
|
@ -65,27 +65,16 @@ namespace polysat {
|
||||||
conflict_core(solver& s);
|
conflict_core(solver& s);
|
||||||
~conflict_core();
|
~conflict_core();
|
||||||
|
|
||||||
vector<signed_constraint> const& constraints() const { return m_constraints; }
|
|
||||||
pvar conflict_var() const { return m_conflict_var; }
|
pvar conflict_var() const { return m_conflict_var; }
|
||||||
|
|
||||||
bool is_bailout() const { return m_bailout; }
|
bool is_bailout() const { return m_bailout; }
|
||||||
void set_bailout() { SASSERT(!is_bailout()); m_bailout = true; }
|
void set_bailout() { SASSERT(!is_bailout()); m_bailout = true; }
|
||||||
|
|
||||||
bool empty() const {
|
bool empty() const {
|
||||||
return m_constraints.empty() && m_vars.empty() && conflict_var() == null_var;
|
return m_constraints.empty() && m_vars.empty() && m_literals.empty() && m_conflict_var == null_var;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset();
|
||||||
for (auto c : m_constraints)
|
|
||||||
unset_mark(c.get());
|
|
||||||
m_constraints.reset();
|
|
||||||
m_vars.reset();
|
|
||||||
m_conflict_var = null_var;
|
|
||||||
m_saturation_premises.reset();
|
|
||||||
m_bailout = false;
|
|
||||||
m_bailout_lemma.reset();
|
|
||||||
SASSERT(empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_pmarked(pvar v) const;
|
bool is_pmarked(pvar v) const;
|
||||||
bool is_bmarked(sat::bool_var b) const;
|
bool is_bmarked(sat::bool_var b) const;
|
||||||
|
@ -123,13 +112,68 @@ namespace polysat {
|
||||||
bool try_eliminate(pvar v);
|
bool try_eliminate(pvar v);
|
||||||
bool try_saturate(pvar v);
|
bool try_saturate(pvar v);
|
||||||
|
|
||||||
using const_iterator = decltype(m_constraints)::const_iterator;
|
using const_iterator = conflict_core_iterator;
|
||||||
const_iterator begin() { return constraints().begin(); }
|
const_iterator begin() const;
|
||||||
const_iterator end() { return constraints().end(); }
|
const_iterator end() const;
|
||||||
|
|
||||||
std::ostream& display(std::ostream& out) const;
|
std::ostream& display(std::ostream& out) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, conflict_core const& c) { return c.display(out); }
|
inline std::ostream& operator<<(std::ostream& out, conflict_core const& c) { return c.display(out); }
|
||||||
|
|
||||||
|
|
||||||
|
class conflict_core_iterator {
|
||||||
|
friend class conflict_core;
|
||||||
|
|
||||||
|
using it1_t = signed_constraints::const_iterator;
|
||||||
|
using it2_t = indexed_uint_set::iterator;
|
||||||
|
|
||||||
|
constraint_manager* m_cm;
|
||||||
|
it1_t m_it1;
|
||||||
|
it1_t m_end1;
|
||||||
|
it2_t m_it2;
|
||||||
|
|
||||||
|
conflict_core_iterator(constraint_manager& cm, it1_t it1, it1_t end1, it2_t it2):
|
||||||
|
m_cm(&cm), m_it1(it1), m_end1(end1), m_it2(it2) {}
|
||||||
|
|
||||||
|
static conflict_core_iterator begin(constraint_manager& cm, signed_constraints cs, indexed_uint_set lits) {
|
||||||
|
return {cm, cs.begin(), cs.end(), lits.begin()};
|
||||||
|
}
|
||||||
|
|
||||||
|
static conflict_core_iterator end(constraint_manager& cm, signed_constraints cs, indexed_uint_set lits) {
|
||||||
|
return {cm, cs.end(), cs.end(), lits.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = signed_constraint;
|
||||||
|
using difference_type = unsigned;
|
||||||
|
using pointer = signed_constraint const*;
|
||||||
|
using reference = signed_constraint const&;
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
|
||||||
|
conflict_core_iterator& operator++() {
|
||||||
|
if (m_it1 != m_end1)
|
||||||
|
++m_it1;
|
||||||
|
else
|
||||||
|
++m_it2;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
signed_constraint operator*() const {
|
||||||
|
if (m_it1 != m_end1)
|
||||||
|
return *m_it1;
|
||||||
|
else
|
||||||
|
return m_cm->lookup(sat::to_literal(*m_it2));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(conflict_core_iterator const& other) const {
|
||||||
|
return m_it1 == other.m_it1 && m_it2 == other.m_it2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(conflict_core_iterator const& other) const { return !operator==(other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline conflict_core::const_iterator conflict_core::begin() const { return conflict_core_iterator::begin(cm(), m_constraints, m_literals); }
|
||||||
|
inline conflict_core::const_iterator conflict_core::end() const { return conflict_core_iterator::end(cm(), m_constraints, m_literals); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@ namespace polysat {
|
||||||
using constraint_eq = deref_eq<constraint>;
|
using constraint_eq = deref_eq<constraint>;
|
||||||
using constraint_table = ptr_hashtable<constraint, constraint_hash, constraint_eq>;
|
using constraint_table = ptr_hashtable<constraint, constraint_hash, constraint_eq>;
|
||||||
|
|
||||||
|
using constraints = ptr_vector<constraint>;
|
||||||
|
using signed_constraints = vector<signed_constraint>;
|
||||||
|
|
||||||
// Manage constraint lifetime, deduplication, and connection to boolean variables/literals.
|
// Manage constraint lifetime, deduplication, and connection to boolean variables/literals.
|
||||||
class constraint_manager {
|
class constraint_manager {
|
||||||
friend class constraint;
|
friend class constraint;
|
||||||
|
|
|
@ -66,9 +66,6 @@ namespace polysat {
|
||||||
friend class inf_saturate;
|
friend class inf_saturate;
|
||||||
friend class constraint_manager;
|
friend class constraint_manager;
|
||||||
|
|
||||||
typedef ptr_vector<constraint> constraints;
|
|
||||||
typedef vector<signed_constraint> signed_constraints;
|
|
||||||
|
|
||||||
reslimit& m_lim;
|
reslimit& m_lim;
|
||||||
params_ref m_params;
|
params_ref m_params;
|
||||||
viable m_viable; // viable sets per variable
|
viable m_viable; // viable sets per variable
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue