mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 09:55:19 +00:00
add sat-euf
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
a7b51d04cd
commit
ecd3315a74
|
@ -39,6 +39,7 @@ def init_project_def():
|
||||||
add_lib('solver', ['model', 'tactic', 'proofs'])
|
add_lib('solver', ['model', 'tactic', 'proofs'])
|
||||||
add_lib('cmd_context', ['solver', 'rewriter'])
|
add_lib('cmd_context', ['solver', 'rewriter'])
|
||||||
add_lib('sat_tactic', ['tactic', 'sat', 'solver'], 'sat/tactic')
|
add_lib('sat_tactic', ['tactic', 'sat', 'solver'], 'sat/tactic')
|
||||||
|
add_lib('sat_euf', ['sat_tactic', 'sat', 'euf'], 'sat/euf')
|
||||||
add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2')
|
add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2')
|
||||||
add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern')
|
add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern')
|
||||||
add_lib('core_tactics', ['tactic', 'macros', 'normal_forms', 'rewriter', 'pattern'], 'tactic/core')
|
add_lib('core_tactics', ['tactic', 'macros', 'normal_forms', 'rewriter', 'pattern'], 'tactic/core')
|
||||||
|
@ -80,7 +81,7 @@ def init_project_def():
|
||||||
includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files)
|
includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files)
|
||||||
add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'qe', 'arith_tactics'], 'cmd_context/extra_cmds')
|
add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'qe', 'arith_tactics'], 'cmd_context/extra_cmds')
|
||||||
add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3')
|
add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3')
|
||||||
add_exe('test', ['api', 'fuzzing', 'simplex', 'euf'], exe_name='test-z3', install=False)
|
add_exe('test', ['api', 'fuzzing', 'simplex', 'sat_euf'], exe_name='test-z3', install=False)
|
||||||
_libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll',
|
_libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll',
|
||||||
reexports=['api'],
|
reexports=['api'],
|
||||||
dll_name='libz3',
|
dll_name='libz3',
|
||||||
|
|
|
@ -60,6 +60,7 @@ add_subdirectory(math/subpaving/tactic)
|
||||||
add_subdirectory(tactic/aig)
|
add_subdirectory(tactic/aig)
|
||||||
add_subdirectory(solver)
|
add_subdirectory(solver)
|
||||||
add_subdirectory(sat/tactic)
|
add_subdirectory(sat/tactic)
|
||||||
|
add_subdirectory(sat/euf)
|
||||||
add_subdirectory(tactic/arith)
|
add_subdirectory(tactic/arith)
|
||||||
add_subdirectory(nlsat/tactic)
|
add_subdirectory(nlsat/tactic)
|
||||||
add_subdirectory(ackermannization)
|
add_subdirectory(ackermannization)
|
||||||
|
|
|
@ -35,8 +35,7 @@ namespace euf {
|
||||||
unmerge_justification(n1);
|
unmerge_justification(n1);
|
||||||
}
|
}
|
||||||
|
|
||||||
enode* egraph::mk_enode(expr* f, enode * const* args) {
|
enode* egraph::mk_enode(expr* f, unsigned num_args, enode * const* args) {
|
||||||
unsigned num_args = is_app(f) ? to_app(f)->get_num_args() : 0;
|
|
||||||
enode* n = enode::mk(m_region, f, num_args, args);
|
enode* n = enode::mk(m_region, f, num_args, args);
|
||||||
m_nodes.push_back(n);
|
m_nodes.push_back(n);
|
||||||
m_exprs.push_back(f);
|
m_exprs.push_back(f);
|
||||||
|
@ -98,15 +97,15 @@ namespace euf {
|
||||||
n->set_update_children();
|
n->set_update_children();
|
||||||
}
|
}
|
||||||
|
|
||||||
enode* egraph::mk(expr* f, enode *const* args) {
|
enode* egraph::mk(expr* f, unsigned num_args, enode *const* args) {
|
||||||
SASSERT(!find(f));
|
SASSERT(!find(f));
|
||||||
force_push();
|
force_push();
|
||||||
enode *n = mk_enode(f, args);
|
enode *n = mk_enode(f, num_args, args);
|
||||||
SASSERT(n->class_size() == 1);
|
SASSERT(n->class_size() == 1);
|
||||||
m_expr2enode.setx(f->get_id(), n, nullptr);
|
m_expr2enode.setx(f->get_id(), n, nullptr);
|
||||||
if (n->num_args() == 0 && m.is_unique_value(f))
|
if (num_args == 0 && m.is_unique_value(f))
|
||||||
n->mark_interpreted();
|
n->mark_interpreted();
|
||||||
if (n->num_args() == 0)
|
if (num_args == 0)
|
||||||
return n;
|
return n;
|
||||||
if (is_equality(n)) {
|
if (is_equality(n)) {
|
||||||
update_children(n);
|
update_children(n);
|
||||||
|
@ -171,6 +170,8 @@ namespace euf {
|
||||||
std::swap(r1, r2);
|
std::swap(r1, r2);
|
||||||
std::swap(n1, n2);
|
std::swap(n1, n2);
|
||||||
}
|
}
|
||||||
|
if ((m.is_true(r2->get_owner()) || m.is_false(r2->get_owner())) && j.is_congruence())
|
||||||
|
m_new_lits.push_back(n1);
|
||||||
for (enode* p : enode_parents(n1))
|
for (enode* p : enode_parents(n1))
|
||||||
m_table.erase(p);
|
m_table.erase(p);
|
||||||
for (enode* p : enode_parents(n2))
|
for (enode* p : enode_parents(n2))
|
||||||
|
@ -187,6 +188,7 @@ namespace euf {
|
||||||
|
|
||||||
void egraph::propagate() {
|
void egraph::propagate() {
|
||||||
m_new_eqs.reset();
|
m_new_eqs.reset();
|
||||||
|
m_new_lits.reset();
|
||||||
SASSERT(m_num_scopes == 0 || m_worklist.empty());
|
SASSERT(m_num_scopes == 0 || m_worklist.empty());
|
||||||
unsigned head = 0, tail = m_worklist.size();
|
unsigned head = 0, tail = m_worklist.size();
|
||||||
while (head < tail && m.limit().inc() && !inconsistent()) {
|
while (head < tail && m.limit().inc() && !inconsistent()) {
|
||||||
|
@ -239,6 +241,88 @@ namespace euf {
|
||||||
SASSERT(n1->get_root()->m_target == nullptr);
|
SASSERT(n1->get_root()->m_target == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief generate an explanation for a congruence.
|
||||||
|
Each pair of children under a congruence have the same roots
|
||||||
|
and therefore have a least common ancestor. We only need
|
||||||
|
explanations up to the least common ancestors.
|
||||||
|
*/
|
||||||
|
void egraph::push_congruence(enode* n1, enode* n2, bool comm) {
|
||||||
|
SASSERT(n1->get_decl() == n2->get_decl());
|
||||||
|
if (comm &&
|
||||||
|
n1->get_arg(0)->get_root() == n2->get_arg(1)->get_root() &&
|
||||||
|
n1->get_arg(1)->get_root() == n2->get_arg(0)->get_root()) {
|
||||||
|
push_lca(n1->get_arg(0), n2->get_arg(1));
|
||||||
|
push_lca(n1->get_arg(1), n2->get_arg(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < n1->num_args(); ++i)
|
||||||
|
push_lca(n1->get_arg(i), n2->get_arg(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
void egraph::push_lca(enode* a, enode* b) {
|
||||||
|
SASSERT(a->get_root() == b->get_root());
|
||||||
|
enode* n = a;
|
||||||
|
while (n) {
|
||||||
|
n->mark2();
|
||||||
|
n = n->m_target;
|
||||||
|
}
|
||||||
|
n = b;
|
||||||
|
while (n) {
|
||||||
|
if (n->is_marked2())
|
||||||
|
n->unmark2();
|
||||||
|
else if (!n->is_marked1())
|
||||||
|
m_todo.push_back(n);
|
||||||
|
n = n->m_target;
|
||||||
|
}
|
||||||
|
n = a;
|
||||||
|
while (n->is_marked2()) {
|
||||||
|
n->unmark2();
|
||||||
|
if (!n->is_marked1())
|
||||||
|
m_todo.push_back(n);
|
||||||
|
n = n->m_target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void egraph::push_todo(enode* n) {
|
||||||
|
while (n) {
|
||||||
|
m_todo.push_back(n);
|
||||||
|
n = n->m_target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void egraph::explain(ptr_vector<T>& justifications) {
|
||||||
|
SASSERT(m_inconsistent);
|
||||||
|
SASSERT(m_todo.empty());
|
||||||
|
push_todo(m_n1);
|
||||||
|
push_todo(m_n2);
|
||||||
|
explain_eq(justifications, m_n1, m_n2, m_justification);
|
||||||
|
explain_todo(justifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, bool comm) {
|
||||||
|
SASSERT(m_todo.empty());
|
||||||
|
push_congruence(a, b, comm);
|
||||||
|
explain_todo(justifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void egraph::explain_todo(ptr_vector<T>& justifications) {
|
||||||
|
for (unsigned i = 0; i < m_todo.size(); ++i) {
|
||||||
|
enode* n = m_todo[i];
|
||||||
|
if (n->m_target && !n->is_marked1()) {
|
||||||
|
n->mark1();
|
||||||
|
explain_eq(justifications, n, n->m_target, n->m_justification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (enode* n : m_todo)
|
||||||
|
n->unmark1();
|
||||||
|
m_todo.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void egraph::invariant() {
|
void egraph::invariant() {
|
||||||
for (enode* n : m_nodes)
|
for (enode* n : m_nodes)
|
||||||
n->invariant();
|
n->invariant();
|
||||||
|
@ -267,3 +351,12 @@ namespace euf {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template void euf::egraph::explain(ptr_vector<int>& justifications);
|
||||||
|
template void euf::egraph::explain_todo(ptr_vector<int>& justifications);
|
||||||
|
template void euf::egraph::explain_eq(ptr_vector<int>& justifications, enode* a, enode* b, bool comm);
|
||||||
|
|
||||||
|
template void euf::egraph::explain(ptr_vector<unsigned>& justifications);
|
||||||
|
template void euf::egraph::explain_todo(ptr_vector<unsigned>& justifications);
|
||||||
|
template void euf::egraph::explain_eq(ptr_vector<unsigned>& justifications, enode* a, enode* b, bool comm);
|
||||||
|
|
||||||
|
|
|
@ -58,13 +58,14 @@ namespace euf {
|
||||||
enode *m_n2 { nullptr };
|
enode *m_n2 { nullptr };
|
||||||
justification m_justification;
|
justification m_justification;
|
||||||
enode_vector m_new_eqs;
|
enode_vector m_new_eqs;
|
||||||
|
enode_vector m_new_lits;
|
||||||
enode_vector m_todo;
|
enode_vector m_todo;
|
||||||
|
|
||||||
void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
|
void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
|
||||||
m_eqs.push_back(add_eq_record(r1, n1, r2_num_parents));
|
m_eqs.push_back(add_eq_record(r1, n1, r2_num_parents));
|
||||||
}
|
}
|
||||||
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
|
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
|
||||||
enode* mk_enode(expr* f, enode * const* args);
|
enode* mk_enode(expr* f, unsigned num_args, enode * const* args);
|
||||||
void reinsert(enode* n);
|
void reinsert(enode* n);
|
||||||
void force_push();
|
void force_push();
|
||||||
void set_conflict(enode* n1, enode* n2, justification j);
|
void set_conflict(enode* n1, enode* n2, justification j);
|
||||||
|
@ -72,15 +73,30 @@ namespace euf {
|
||||||
void merge_justification(enode* n1, enode* n2, justification j);
|
void merge_justification(enode* n1, enode* n2, justification j);
|
||||||
void unmerge_justification(enode* n1);
|
void unmerge_justification(enode* n1);
|
||||||
void dedup_equalities();
|
void dedup_equalities();
|
||||||
bool is_equality(enode* n) const;
|
|
||||||
void reinsert_equality(enode* p);
|
void reinsert_equality(enode* p);
|
||||||
void update_children(enode* n);
|
void update_children(enode* n);
|
||||||
|
void push_lca(enode* a, enode* b);
|
||||||
|
void push_congruence(enode* n1, enode* n2, bool commutative);
|
||||||
|
void push_todo(enode* n);
|
||||||
|
template <typename T>
|
||||||
|
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, justification const& j) {
|
||||||
|
if (j.is_external())
|
||||||
|
justifications.push_back(j.ext<T>());
|
||||||
|
else if (j.is_congruence())
|
||||||
|
push_congruence(a, b, j.is_commutative());
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
void explain_todo(ptr_vector<T>& justifications);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
egraph(ast_manager& m): m(m), m_table(m), m_exprs(m) {}
|
egraph(ast_manager& m): m(m), m_table(m), m_exprs(m) {}
|
||||||
enode* find(expr* f) { return m_expr2enode.get(f->get_id(), nullptr); }
|
enode* find(expr* f) { return m_expr2enode.get(f->get_id(), nullptr); }
|
||||||
enode* mk(expr* f, enode *const* args);
|
enode* mk(expr* f, unsigned n, enode *const* args);
|
||||||
void push() { ++m_num_scopes; }
|
void push() { ++m_num_scopes; }
|
||||||
void pop(unsigned num_scopes);
|
void pop(unsigned num_scopes);
|
||||||
|
|
||||||
|
bool is_equality(enode* n) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief merge nodes, all effects are deferred to the propagation step.
|
\brief merge nodes, all effects are deferred to the propagation step.
|
||||||
*/
|
*/
|
||||||
|
@ -98,45 +114,11 @@ namespace euf {
|
||||||
void propagate();
|
void propagate();
|
||||||
bool inconsistent() const { return m_inconsistent; }
|
bool inconsistent() const { return m_inconsistent; }
|
||||||
enode_vector const& new_eqs() const { return m_new_eqs; }
|
enode_vector const& new_eqs() const { return m_new_eqs; }
|
||||||
|
enode_vector const& new_lits() const { return m_new_lits; }
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void explain(ptr_vector<T>& justifications) {
|
void explain(ptr_vector<T>& justifications);
|
||||||
SASSERT(m_inconsistent);
|
template <typename T>
|
||||||
SASSERT(m_todo.empty());
|
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, bool comm);
|
||||||
auto push_congruence = [&](enode* p, enode* q) {
|
|
||||||
SASSERT(p->get_decl() == q->get_decl());
|
|
||||||
for (enode* arg : enode_args(p))
|
|
||||||
m_todo.push_back(arg);
|
|
||||||
for (enode* arg : enode_args(q))
|
|
||||||
m_todo.push_back(arg);
|
|
||||||
};
|
|
||||||
auto explain_node = [&](enode* n) {
|
|
||||||
if (!n->m_target)
|
|
||||||
return;
|
|
||||||
if (n->is_marked1())
|
|
||||||
return;
|
|
||||||
n->mark1();
|
|
||||||
if (n->m_justification.is_external())
|
|
||||||
justifications.push_back(n->m_justification.ext<T>());
|
|
||||||
else if (n->m_justification.is_congruence())
|
|
||||||
push_congruence(n, n->m_target);
|
|
||||||
n = n->m_target;
|
|
||||||
if (!n->is_marked1())
|
|
||||||
m_todo.push_back(n);
|
|
||||||
};
|
|
||||||
m_todo.push_back(m_n1);
|
|
||||||
m_todo.push_back(m_n2);
|
|
||||||
if (m_justification.is_external())
|
|
||||||
justifications.push_back(m_justification.ext<T>());
|
|
||||||
else if (m_justification.is_congruence())
|
|
||||||
push_congruence(m_n1, m_n2);
|
|
||||||
for (unsigned i = 0; i < m_todo.size(); ++i)
|
|
||||||
explain_node(m_todo[i]);
|
|
||||||
for (enode* n : m_todo)
|
|
||||||
n->unmark1();
|
|
||||||
m_todo.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void invariant();
|
void invariant();
|
||||||
std::ostream& display(std::ostream& out) const;
|
std::ostream& display(std::ostream& out) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,7 @@ namespace euf {
|
||||||
enode* m_root;
|
enode* m_root;
|
||||||
enode* m_target { nullptr };
|
enode* m_target { nullptr };
|
||||||
justification m_justification;
|
justification m_justification;
|
||||||
|
unsigned m_num_args;
|
||||||
enode* m_args[0];
|
enode* m_args[0];
|
||||||
|
|
||||||
friend class enode_args;
|
friend class enode_args;
|
||||||
|
@ -56,12 +57,14 @@ namespace euf {
|
||||||
}
|
}
|
||||||
|
|
||||||
static enode* mk(region& r, expr* f, unsigned num_args, enode* const* args) {
|
static enode* mk(region& r, expr* f, unsigned num_args, enode* const* args) {
|
||||||
|
SASSERT(num_args <= (is_app(f) ? to_app(f)->get_num_args() : 0));
|
||||||
void* mem = r.allocate(get_enode_size(num_args));
|
void* mem = r.allocate(get_enode_size(num_args));
|
||||||
enode* n = new (mem) enode();
|
enode* n = new (mem) enode();
|
||||||
n->m_owner = f;
|
n->m_owner = f;
|
||||||
n->m_next = n;
|
n->m_next = n;
|
||||||
n->m_root = n;
|
n->m_root = n;
|
||||||
n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative();
|
n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative();
|
||||||
|
n->m_num_args = num_args;
|
||||||
for (unsigned i = 0; i < num_args; ++i) {
|
for (unsigned i = 0; i < num_args; ++i) {
|
||||||
SASSERT(to_app(f)->get_arg(i) == args[i]->get_owner());
|
SASSERT(to_app(f)->get_arg(i) == args[i]->get_owner());
|
||||||
n->m_args[i] = args[i];
|
n->m_args[i] = args[i];
|
||||||
|
@ -83,9 +86,10 @@ namespace euf {
|
||||||
}
|
}
|
||||||
|
|
||||||
enode* const* args() const { return m_args; }
|
enode* const* args() const { return m_args; }
|
||||||
unsigned num_args() const { return is_app(m_owner) ? to_app(m_owner)->get_num_args() : 0; }
|
unsigned num_args() const { return m_num_args; }
|
||||||
unsigned num_parents() const { return m_parents.size(); }
|
unsigned num_parents() const { return m_parents.size(); }
|
||||||
bool interpreted() const { return m_interpreted; }
|
bool interpreted() const { return m_interpreted; }
|
||||||
|
bool commutative() const { return m_commutative; }
|
||||||
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
|
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
|
||||||
|
|
||||||
enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }
|
enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }
|
||||||
|
@ -97,6 +101,9 @@ namespace euf {
|
||||||
void mark1() { m_mark1 = true; }
|
void mark1() { m_mark1 = true; }
|
||||||
void unmark1() { m_mark1 = false; }
|
void unmark1() { m_mark1 = false; }
|
||||||
bool is_marked1() { return m_mark1; }
|
bool is_marked1() { return m_mark1; }
|
||||||
|
void mark2() { m_mark2 = true; }
|
||||||
|
void unmark2() { m_mark2 = false; }
|
||||||
|
bool is_marked2() { return m_mark2; }
|
||||||
void add_parent(enode* p) { m_parents.push_back(p); }
|
void add_parent(enode* p) { m_parents.push_back(p); }
|
||||||
unsigned class_size() const { return m_class_size; }
|
unsigned class_size() const { return m_class_size; }
|
||||||
enode* get_root() const { return m_root; }
|
enode* get_root() const { return m_root; }
|
||||||
|
|
8
src/sat/euf/CMakeLists.txt
Normal file
8
src/sat/euf/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
z3_add_component(sat_euf
|
||||||
|
SOURCES
|
||||||
|
euf_solver.cpp
|
||||||
|
COMPONENT_DEPENDENCIES
|
||||||
|
sat
|
||||||
|
sat_tactic
|
||||||
|
euf
|
||||||
|
)
|
186
src/sat/euf/euf_solver.cpp
Normal file
186
src/sat/euf/euf_solver.cpp
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2020 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
euf_solver.cpp
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Solver plugin for EUF
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2020-08-25
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#include "sat/euf/euf_solver.h"
|
||||||
|
#include "sat/sat_solver.h"
|
||||||
|
#include "tactic/tactic_exception.h"
|
||||||
|
|
||||||
|
namespace euf_sat {
|
||||||
|
|
||||||
|
bool solver::propagate(literal l, ext_constraint_idx idx) {
|
||||||
|
UNREACHABLE();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) {
|
||||||
|
m_explain.reset();
|
||||||
|
euf::enode* n = nullptr;
|
||||||
|
bool sign = false;
|
||||||
|
if (idx != 0) {
|
||||||
|
auto p = m_var2node[l.var()];
|
||||||
|
n = p.first;
|
||||||
|
sign = l.sign() != p.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (idx) {
|
||||||
|
case 0:
|
||||||
|
SASSERT(m_egraph.inconsistent());
|
||||||
|
m_egraph.explain<unsigned>(m_explain);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
SASSERT(m_egraph.is_equality(n));
|
||||||
|
m_egraph.explain_eq<unsigned>(m_explain, n->get_arg(0), n->get_arg(1), n->commutative());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
SASSERT(m.is_bool(n->get_owner()));
|
||||||
|
m_egraph.explain_eq<unsigned>(m_explain, n, (sign ? m_false : m_true), false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
for (unsigned* idx : m_explain)
|
||||||
|
r.push_back(sat::to_literal((unsigned)(idx - base_ptr())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::asserted(literal l) {
|
||||||
|
auto p = m_var2node[l.var()];
|
||||||
|
bool sign = p.second != l.sign();
|
||||||
|
euf::enode* n = p.first;
|
||||||
|
expr* e = n->get_owner();
|
||||||
|
if (m.is_eq(e) && !sign) {
|
||||||
|
euf::enode* na = n->get_arg(0);
|
||||||
|
euf::enode* nb = n->get_arg(1);
|
||||||
|
m_egraph.merge(na, nb, base_ptr() + l.index());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
euf::enode* nb = sign ? m_false : m_true;
|
||||||
|
m_egraph.merge(n, nb, base_ptr() + l.index());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TBD: delay propagation?
|
||||||
|
m_egraph.propagate();
|
||||||
|
if (m_egraph.inconsistent()) {
|
||||||
|
s().set_conflict(sat::justification::mk_ext_justification(s().scope_lvl(), 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (euf::enode* eq : m_egraph.new_eqs()) {
|
||||||
|
bool_var v = m_expr2var.to_bool_var(eq->get_owner());
|
||||||
|
s().assign(literal(v, false), sat::justification::mk_ext_justification(s().scope_lvl(), 1));
|
||||||
|
}
|
||||||
|
for (euf::enode* p : m_egraph.new_lits()) {
|
||||||
|
expr* e = p->get_owner();
|
||||||
|
bool sign = m.is_false(p->get_root()->get_owner());
|
||||||
|
SASSERT(m.is_bool(e));
|
||||||
|
SASSERT(m.is_true(p->get_root()->get_owner()) || sign);
|
||||||
|
bool_var v = m_expr2var.to_bool_var(e);
|
||||||
|
s().assign(literal(v, sign), sat::justification::mk_ext_justification(s().scope_lvl(), 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sat::check_result solver::check() {
|
||||||
|
return sat::CR_DONE;
|
||||||
|
}
|
||||||
|
void solver::push() {
|
||||||
|
m_egraph.push();
|
||||||
|
}
|
||||||
|
void solver::pop(unsigned n) {
|
||||||
|
m_egraph.pop(n);
|
||||||
|
}
|
||||||
|
void solver::pre_simplify() {}
|
||||||
|
void solver::simplify() {}
|
||||||
|
// have a way to replace l by r in all constraints
|
||||||
|
void solver::clauses_modifed() {}
|
||||||
|
lbool solver::get_phase(bool_var v) { return l_undef; }
|
||||||
|
std::ostream& solver::display(std::ostream& out) const {
|
||||||
|
m_egraph.display(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
std::ostream& solver::display_justification(std::ostream& out, ext_justification_idx idx) const { return out; }
|
||||||
|
std::ostream& solver::display_constraint(std::ostream& out, ext_constraint_idx idx) const { return out; }
|
||||||
|
void solver::collect_statistics(statistics& st) const {}
|
||||||
|
sat::extension* solver::copy(sat::solver* s) { return nullptr; }
|
||||||
|
sat::extension* solver::copy(sat::lookahead* s, bool learned) { return nullptr; }
|
||||||
|
void solver::find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) {}
|
||||||
|
void solver::gc() {}
|
||||||
|
void solver::pop_reinit() {}
|
||||||
|
bool solver::validate() { return true; }
|
||||||
|
void solver::init_use_list(sat::ext_use_list& ul) {}
|
||||||
|
bool solver::is_blocked(literal l, ext_constraint_idx) { return false; }
|
||||||
|
bool solver::check_model(sat::model const& m) const { return true;}
|
||||||
|
unsigned solver::max_var(unsigned w) const { return w; }
|
||||||
|
|
||||||
|
void solver::internalize(sat_internalizer& si, expr* e) {
|
||||||
|
SASSERT(!si.is_bool_op(e));
|
||||||
|
unsigned sz = m_stack.size();
|
||||||
|
euf::enode* n = visit(si, e);
|
||||||
|
while (m_stack.size() > sz) {
|
||||||
|
loop:
|
||||||
|
if (!m.inc())
|
||||||
|
throw tactic_exception(m.limit().get_cancel_msg());
|
||||||
|
frame & fr = m_stack.back();
|
||||||
|
expr* e = fr.m_e;
|
||||||
|
if (m_egraph.find(e)) {
|
||||||
|
m_stack.pop_back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsigned num = is_app(e) ? to_app(e)->get_num_args() : 0;
|
||||||
|
m_args.reset();
|
||||||
|
while (fr.m_idx < num) {
|
||||||
|
expr* arg = to_app(e)->get_arg(fr.m_idx);
|
||||||
|
fr.m_idx++;
|
||||||
|
n = visit(si, arg);
|
||||||
|
if (!n)
|
||||||
|
goto loop;
|
||||||
|
m_args.push_back(n);
|
||||||
|
}
|
||||||
|
n = m_egraph.mk(e, num, m_args.c_ptr());
|
||||||
|
attach_bool_var(si, n);
|
||||||
|
}
|
||||||
|
SASSERT(m_egraph.find(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
euf::enode* solver::visit(sat_internalizer& si, expr* e) {
|
||||||
|
euf::enode* n = m_egraph.find(e);
|
||||||
|
if (n)
|
||||||
|
return n;
|
||||||
|
if (si.is_bool_op(e)) {
|
||||||
|
sat::literal lit = si.internalize(e);
|
||||||
|
n = m_egraph.mk(e, 0, nullptr);
|
||||||
|
attach_bool_var(lit.var(), lit.sign(), n);
|
||||||
|
s().set_external(lit.var());
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
if (is_app(e) && to_app(e)->get_num_args() > 0)
|
||||||
|
return nullptr;
|
||||||
|
n = m_egraph.mk(e, 0, nullptr);
|
||||||
|
attach_bool_var(si, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::attach_bool_var(sat_internalizer& si, euf::enode* n) {
|
||||||
|
expr* e = n->get_owner();
|
||||||
|
if (m.is_bool(e)) {
|
||||||
|
sat::bool_var v = si.add_bool_var(e);
|
||||||
|
attach_bool_var(v, false, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::attach_bool_var(sat::bool_var v, bool sign, euf::enode* n) {
|
||||||
|
m_var2node.reserve(v + 1);
|
||||||
|
m_var2node[v] = euf::enode_bool_pair(n, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
98
src/sat/euf/euf_solver.h
Normal file
98
src/sat/euf/euf_solver.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2020 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
euf_solver.h
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Solver plugin for EUF
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2020-08-25
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sat/sat_extension.h"
|
||||||
|
#include "ast/euf/euf_egraph.h"
|
||||||
|
#include "sat/tactic/atom2bool_var.h"
|
||||||
|
#include "sat/tactic/goal2sat.h"
|
||||||
|
|
||||||
|
namespace euf_sat {
|
||||||
|
typedef sat::literal literal;
|
||||||
|
typedef sat::ext_constraint_idx ext_constraint_idx;
|
||||||
|
typedef sat::ext_justification_idx ext_justification_idx;
|
||||||
|
typedef sat::literal_vector literal_vector;
|
||||||
|
typedef sat::bool_var bool_var;
|
||||||
|
|
||||||
|
struct frame {
|
||||||
|
expr* m_e;
|
||||||
|
unsigned m_idx;
|
||||||
|
frame(expr* e) : m_e(e), m_idx(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class solver : public sat::extension {
|
||||||
|
ast_manager& m;
|
||||||
|
atom2bool_var& m_expr2var;
|
||||||
|
euf::egraph m_egraph;
|
||||||
|
sat::solver* m_solver;
|
||||||
|
|
||||||
|
euf::enode* m_true;
|
||||||
|
euf::enode* m_false;
|
||||||
|
svector<euf::enode_bool_pair> m_var2node;
|
||||||
|
ptr_vector<unsigned> m_explain;
|
||||||
|
euf::enode_vector m_args;
|
||||||
|
svector<frame> m_stack;
|
||||||
|
|
||||||
|
sat::solver& s() { return *m_solver; }
|
||||||
|
unsigned * base_ptr() { return reinterpret_cast<unsigned*>(this); }
|
||||||
|
euf::enode* visit(sat_internalizer& si, expr* e);
|
||||||
|
void attach_bool_var(sat_internalizer& si, euf::enode* n);
|
||||||
|
void attach_bool_var(sat::bool_var v, bool sign, euf::enode* n);
|
||||||
|
|
||||||
|
public:
|
||||||
|
solver(ast_manager& m, atom2bool_var& expr2var):
|
||||||
|
m(m),
|
||||||
|
m_expr2var(expr2var),
|
||||||
|
m_egraph(m),
|
||||||
|
m_solver(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void set_solver(sat::solver* s) override { m_solver = s; }
|
||||||
|
void set_lookahead(sat::lookahead* s) override { }
|
||||||
|
double get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const override { return 0; }
|
||||||
|
bool is_extended_binary(ext_justification_idx idx, literal_vector & r) override { return false; }
|
||||||
|
|
||||||
|
bool propagate(literal l, ext_constraint_idx idx) override;
|
||||||
|
void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) override;
|
||||||
|
void asserted(literal l) override;
|
||||||
|
sat::check_result check() override;
|
||||||
|
void push() override;
|
||||||
|
void pop(unsigned n) override;
|
||||||
|
void pre_simplify() override;
|
||||||
|
void simplify() override;
|
||||||
|
// have a way to replace l by r in all constraints
|
||||||
|
void clauses_modifed() override;
|
||||||
|
lbool get_phase(bool_var v) override;
|
||||||
|
std::ostream& display(std::ostream& out) const override;
|
||||||
|
std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const override;
|
||||||
|
std::ostream& display_constraint(std::ostream& out, ext_constraint_idx idx) const override;
|
||||||
|
void collect_statistics(statistics& st) const override;
|
||||||
|
extension* copy(sat::solver* s) override;
|
||||||
|
extension* copy(sat::lookahead* s, bool learned) override;
|
||||||
|
void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override;
|
||||||
|
void gc() override;
|
||||||
|
void pop_reinit() override;
|
||||||
|
bool validate() override;
|
||||||
|
void init_use_list(sat::ext_use_list& ul) override;
|
||||||
|
bool is_blocked(literal l, ext_constraint_idx) override;
|
||||||
|
bool check_model(sat::model const& m) const override;
|
||||||
|
unsigned max_var(unsigned w) const override;
|
||||||
|
|
||||||
|
void internalize(sat_internalizer& si, expr* e);
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
|
@ -42,7 +42,7 @@ Notes:
|
||||||
#include "tactic/generic_model_converter.h"
|
#include "tactic/generic_model_converter.h"
|
||||||
#include<sstream>
|
#include<sstream>
|
||||||
|
|
||||||
struct goal2sat::imp {
|
struct goal2sat::imp : public sat_internalizer {
|
||||||
struct frame {
|
struct frame {
|
||||||
app * m_t;
|
app * m_t;
|
||||||
unsigned m_root:1;
|
unsigned m_root:1;
|
||||||
|
@ -131,6 +131,18 @@ struct goal2sat::imp {
|
||||||
return m_true;
|
return m_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sat::bool_var add_bool_var(expr* t) override {
|
||||||
|
sat::bool_var v = m_map.to_bool_var(t);
|
||||||
|
if (v == sat::null_bool_var) {
|
||||||
|
v = m_solver.add_var(true);
|
||||||
|
m_map.insert(t, v);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_solver.set_external(v);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
void convert_atom(expr * t, bool root, bool sign) {
|
void convert_atom(expr * t, bool root, bool sign) {
|
||||||
SASSERT(m.is_bool(t));
|
SASSERT(m.is_bool(t));
|
||||||
sat::literal l;
|
sat::literal l;
|
||||||
|
@ -149,7 +161,7 @@ struct goal2sat::imp {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t);
|
bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t);
|
||||||
sat::bool_var v = m_solver.add_var(ext);
|
v = m_solver.add_var(ext);
|
||||||
m_map.insert(t, v);
|
m_map.insert(t, v);
|
||||||
l = sat::literal(v, sign);
|
l = sat::literal(v, sign);
|
||||||
TRACE("sat", tout << "new_var: " << v << ": " << mk_bounded_pp(t, m, 2) << " " << is_uninterp_const(t) << "\n";);
|
TRACE("sat", tout << "new_var: " << v << ": " << mk_bounded_pp(t, m, 2) << " " << is_uninterp_const(t) << "\n";);
|
||||||
|
@ -812,7 +824,7 @@ struct goal2sat::imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sat::literal internalize(expr* n) {
|
sat::literal internalize(expr* n) override {
|
||||||
SASSERT(m_result_stack.empty());
|
SASSERT(m_result_stack.empty());
|
||||||
process(n, false);
|
process(n, false);
|
||||||
SASSERT(m_result_stack.size() == 1);
|
SASSERT(m_result_stack.size() == 1);
|
||||||
|
@ -821,6 +833,30 @@ struct goal2sat::imp {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_bool_op(expr* t) const override {
|
||||||
|
if (!is_app(t))
|
||||||
|
return false;
|
||||||
|
if (to_app(t)->get_family_id() == m.get_basic_family_id()) {
|
||||||
|
switch (to_app(t)->get_decl_kind()) {
|
||||||
|
case OP_OR:
|
||||||
|
case OP_AND:
|
||||||
|
case OP_TRUE:
|
||||||
|
case OP_FALSE:
|
||||||
|
case OP_NOT:
|
||||||
|
return true;
|
||||||
|
case OP_ITE:
|
||||||
|
case OP_EQ:
|
||||||
|
return m.is_bool(to_app(t)->get_arg(1));
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (to_app(t)->get_family_id() == pb.get_family_id())
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void process(expr * n) {
|
void process(expr * n) {
|
||||||
m_result_stack.reset();
|
m_result_stack.reset();
|
||||||
process(n, true);
|
process(n, true);
|
||||||
|
|
|
@ -34,6 +34,14 @@ Notes:
|
||||||
#include "tactic/generic_model_converter.h"
|
#include "tactic/generic_model_converter.h"
|
||||||
#include "sat/tactic/atom2bool_var.h"
|
#include "sat/tactic/atom2bool_var.h"
|
||||||
|
|
||||||
|
|
||||||
|
class sat_internalizer {
|
||||||
|
public:
|
||||||
|
virtual bool is_bool_op(expr* e) const = 0;
|
||||||
|
virtual sat::literal internalize(expr* e) = 0;
|
||||||
|
virtual sat::bool_var add_bool_var(expr* e) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class goal2sat {
|
class goal2sat {
|
||||||
struct imp;
|
struct imp;
|
||||||
imp * m_imp;
|
imp * m_imp;
|
||||||
|
|
|
@ -83,6 +83,8 @@ static void test2() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void test3() {
|
static void test3() {
|
||||||
ast_manager m;
|
ast_manager m;
|
||||||
reg_decl_plugins(m);
|
reg_decl_plugins(m);
|
||||||
|
|
Loading…
Reference in a new issue