3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 09:55:19 +00:00

bv and gc of literals (#4692)

* bv and gc of literals

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* overload

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* diseq

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* diseq

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2020-09-17 14:24:07 -07:00 committed by GitHub
parent 2d52367368
commit 549753845e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1480 additions and 854 deletions

View file

@ -41,6 +41,7 @@ namespace euf {
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);
m_expr2enode.setx(f->get_id(), n, nullptr);
push_node(n); push_node(n);
for (unsigned i = 0; i < num_args; ++i) for (unsigned i = 0; i < num_args; ++i)
set_merge_enabled(args[i], true); set_merge_enabled(args[i], true);
@ -65,7 +66,7 @@ namespace euf {
void egraph::reinsert_equality(enode* p) { void egraph::reinsert_equality(enode* p) {
SASSERT(is_equality(p)); SASSERT(is_equality(p));
if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) { if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root() && m_value(p) != l_true) {
add_literal(p, true); add_literal(p, true);
} }
} }
@ -97,8 +98,7 @@ namespace euf {
SASSERT(!find(f)); SASSERT(!find(f));
force_push(); force_push();
enode *n = mk_enode(f, num_args, 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);
if (num_args == 0 && m.is_unique_value(f)) if (num_args == 0 && m.is_unique_value(f))
n->mark_interpreted(); n->mark_interpreted();
if (num_args == 0) if (num_args == 0)
@ -110,26 +110,17 @@ namespace euf {
} }
enode_bool_pair p = m_table.insert(n); enode_bool_pair p = m_table.insert(n);
enode* n2 = p.first; enode* n2 = p.first;
if (n2 == n) { if (n2 == n)
update_children(n); update_children(n);
} else
else { merge(n, n2, justification::congruence(p.second));
merge(n, n2, justification::congruence(p.second));
#if 0
SASSERT(n2->get_expr() != n->get_expr());
SASSERT(n->class_size() == 1);
SASSERT(n->is_root());
merge_justification(n, n2, justification::congruence(p.second));
enode* r2 = n2->get_root();
std::swap(n->m_next, r2->m_next);
n->m_root = r2;
r2->inc_class_size(n->class_size());
push_eq(n, n, r2->num_parents());
#endif
}
return n; return n;
} }
egraph::egraph(ast_manager& m) : m(m), m_table(m), m_exprs(m) {
m_tmp_eq = enode::mk_tmp(m_region, 2);
}
egraph::~egraph() { egraph::~egraph() {
for (enode* n : m_nodes) for (enode* n : m_nodes)
n->m_parents.finalize(); n->m_parents.finalize();
@ -142,6 +133,15 @@ namespace euf {
++m_stats.m_num_th_eqs; ++m_stats.m_num_th_eqs;
} }
void egraph::add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq) {
if (!th_propagates_diseqs(id))
return;
TRACE("euf_verbose", tout << "eq: " << v1 << " != " << v2 << "\n";);
m_new_th_eqs.push_back(th_eq(id, v1, v2, eq));
m_updates.push_back(update_record(update_record::new_th_eq()));
++m_stats.m_num_th_diseqs;
}
void egraph::add_literal(enode* n, bool is_eq) { void egraph::add_literal(enode* n, bool is_eq) {
TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";); TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";);
m_new_lits.push_back(enode_bool_pair(n, is_eq)); m_new_lits.push_back(enode_bool_pair(n, is_eq));
@ -149,6 +149,68 @@ namespace euf {
if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits; if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
} }
void egraph::new_diseq(enode* n1) {
SASSERT(m.is_eq(n1->get_expr()));
enode* arg1 = n1->get_arg(0), * arg2 = n1->get_arg(1);
enode* r1 = arg1->get_root();
enode* r2 = arg2->get_root();
TRACE("euf", tout << "new-diseq: " << mk_pp(r1->get_expr(), m) << " " << mk_pp(r2->get_expr(), m) << ": " << r1->has_th_vars() << " " << r2->has_th_vars() << "\n";);
if (r1 == r2)
return;
if (!r1->has_th_vars())
return;
if (!r2->has_th_vars())
return;
if (r1->has_one_th_var() && r2->has_one_th_var() && r1->get_first_th_id() == r2->get_first_th_id()) {
theory_id id = r1->get_first_th_id();
if (!th_propagates_diseqs(id))
return;
theory_var v1 = arg1->get_closest_th_var(id);
theory_var v2 = arg2->get_closest_th_var(id);
add_th_diseq(id, v1, v2, n1->get_expr());
return;
}
for (auto p : euf::enode_th_vars(r1)) {
if (!th_propagates_diseqs(p.get_id()))
continue;
for (auto q : euf::enode_th_vars(r2))
if (p.get_id() == q.get_id())
add_th_diseq(p.get_id(), p.get_var(), q.get_var(), n1->get_expr());
}
}
/*
* Propagate disequalities over equality atoms that are assigned to false.
*/
void egraph::add_th_diseqs(theory_id id, theory_var v1, enode* r) {
SASSERT(r->is_root());
if (!th_propagates_diseqs(id))
return;
for (enode* p : enode_parents(r)) {
if (m.is_eq(p->get_expr()) && m.is_false(p->get_root()->get_expr())) {
enode* n = nullptr;
n = (r == p->get_arg(0)->get_root()) ? p->get_arg(1) : p->get_arg(0);
n = n->get_root();
theory_var v2 = n->get_closest_th_var(id);
if (v2 != null_theory_var)
add_th_diseq(id, v1, v2, p->get_expr());
}
}
}
void egraph::set_th_propagates_diseqs(theory_id id) {
m_th_propagates_diseqs.reserve(id + 1, false);
m_th_propagates_diseqs[id] = true;
}
bool egraph::th_propagates_diseqs(theory_id id) const {
return m_th_propagates_diseqs.get(id, false);
}
void egraph::add_th_var(enode* n, theory_var v, theory_id id) { void egraph::add_th_var(enode* n, theory_var v, theory_id id) {
force_push(); force_push();
theory_var w = n->get_th_var(id); theory_var w = n->get_th_var(id);
@ -159,10 +221,12 @@ namespace euf {
m_updates.push_back(update_record(n, id, update_record::add_th_var())); m_updates.push_back(update_record(n, id, update_record::add_th_var()));
if (r != n) { if (r != n) {
theory_var u = r->get_th_var(id); theory_var u = r->get_th_var(id);
if (u == null_theory_var) if (u == null_theory_var) {
r->add_th_var(v, id, m_region); r->add_th_var(v, id, m_region);
else add_th_diseqs(id, v, r);
add_th_eq(id, v, u, n, r); }
else
add_th_eq(id, v, u, n, r);
} }
} }
else { else {
@ -201,10 +265,12 @@ namespace euf {
SASSERT(m_new_lits_qhead <= m_new_lits.size()); SASSERT(m_new_lits_qhead <= m_new_lits.size());
unsigned old_lim = m_scopes.size() - num_scopes; unsigned old_lim = m_scopes.size() - num_scopes;
unsigned num_updates = m_scopes[old_lim]; unsigned num_updates = m_scopes[old_lim];
auto undo_node = [&](enode* n) { auto undo_node = [&]() {
if (n->num_args() > 1) enode* n = m_nodes.back();
expr* e = m_exprs.back();
if (n->num_args() > 0)
m_table.erase(n); m_table.erase(n);
m_expr2enode[n->get_expr_id()] = nullptr; m_expr2enode[e->get_id()] = nullptr;
n->~enode(); n->~enode();
m_nodes.pop_back(); m_nodes.pop_back();
m_exprs.pop_back(); m_exprs.pop_back();
@ -213,7 +279,7 @@ namespace euf {
auto const& p = m_updates[i]; auto const& p = m_updates[i];
switch (p.tag) { switch (p.tag) {
case update_record::tag_t::is_add_node: case update_record::tag_t::is_add_node:
undo_node(p.r1); undo_node();
break; break;
case update_record::tag_t::is_toggle_merge: case update_record::tag_t::is_toggle_merge:
p.r1->set_merge_enabled(!p.r1->merge_enabled()); p.r1->set_merge_enabled(!p.r1->merge_enabled());
@ -257,13 +323,13 @@ namespace euf {
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size()); SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
} }
void egraph::merge(enode* n1, enode* n2, justification j) { void egraph::merge(enode* n1, enode* n2, justification j) {
SASSERT(m.get_sort(n1->get_expr()) == m.get_sort(n2->get_expr())); SASSERT(m.get_sort(n1->get_expr()) == m.get_sort(n2->get_expr()));
enode* r1 = n1->get_root(); enode* r1 = n1->get_root();
enode* r2 = n2->get_root(); enode* r2 = n2->get_root();
if (r1 == r2) if (r1 == r2)
return; return;
TRACE("euf", j.display(tout << n1->get_expr_id() << " == " << n2->get_expr_id() << " ", m_display_justification) << "\n";); TRACE("euf", j.display(tout << "merge: " << mk_bounded_pp(n1->get_expr(), m) << " == " << mk_bounded_pp(n2->get_expr(), m) << " ", m_display_justification) << "\n";);
force_push(); force_push();
SASSERT(m_num_scopes == 0); SASSERT(m_num_scopes == 0);
++m_stats.m_num_merge; ++m_stats.m_num_merge;
@ -275,9 +341,10 @@ namespace euf {
std::swap(r1, r2); std::swap(r1, r2);
std::swap(n1, n2); std::swap(n1, n2);
} }
if ((m.is_true(r2->get_expr()) || m.is_false(r2->get_expr())) && j.is_congruence()) { if ((m.is_true(r2->get_expr()) || m.is_false(r2->get_expr())) && j.is_congruence())
add_literal(n1, false); add_literal(n1, false);
} if (m.is_false(r2->get_expr()) && m.is_eq(n1->get_expr()))
new_diseq(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))
@ -301,6 +368,7 @@ namespace euf {
if (v == null_theory_var) { if (v == null_theory_var) {
root->add_th_var(iv.get_var(), id, m_region); root->add_th_var(iv.get_var(), id, m_region);
m_updates.push_back(update_record(root, id, update_record::add_th_var())); m_updates.push_back(update_record(root, id, update_record::add_th_var()));
add_th_diseqs(id, iv.get_var(), root);
} }
else { else {
SASSERT(v != iv.get_var()); SASSERT(v != iv.get_var());
@ -378,6 +446,25 @@ namespace euf {
SASSERT(!n1->get_root()->m_target); SASSERT(!n1->get_root()->m_target);
} }
bool egraph::are_diseq(enode* a, enode* b) const {
enode* ra = a->get_root(), * rb = b->get_root();
if (ra == rb)
return false;
if (ra->interpreted() && rb->interpreted())
return true;
if (m.get_sort(ra->get_expr()) != m.get_sort(rb->get_expr()))
return true;
expr_ref eq(m.mk_eq(a->get_expr(), b->get_expr()), m);
m_tmp_eq->m_args[0] = a;
m_tmp_eq->m_args[1] = b;
m_tmp_eq->m_expr = eq;
SASSERT(m_tmp_eq->num_args() == 2);
enode* r = m_table.find(m_tmp_eq);
if (r && m_value(r->get_root()) == l_false)
return true;
return false;
}
/** /**
\brief generate an explanation for a congruence. \brief generate an explanation for a congruence.
Each pair of children under a congruence have the same roots Each pair of children under a congruence have the same roots
@ -529,9 +616,10 @@ namespace euf {
void egraph::collect_statistics(statistics& st) const { void egraph::collect_statistics(statistics& st) const {
st.update("euf merge", m_stats.m_num_merge); st.update("euf merge", m_stats.m_num_merge);
st.update("euf conflicts", m_stats.m_num_conflicts); st.update("euf conflicts", m_stats.m_num_conflicts);
st.update("euf equality propagations", m_stats.m_num_eqs); st.update("euf propagations eqs", m_stats.m_num_eqs);
st.update("euf theory equality propagations", m_stats.m_num_th_eqs); st.update("euf propagations theory eqs", m_stats.m_num_th_eqs);
st.update("euf literal propagations", m_stats.m_num_lits); st.update("euf propagations theory diseqs", m_stats.m_num_th_diseqs);
st.update("euf propagations literal", m_stats.m_num_lits);
} }
void egraph::copy_from(egraph const& src, std::function<void*(void*)>& copy_justification) { void egraph::copy_from(egraph const& src, std::function<void*(void*)>& copy_justification) {

View file

@ -26,26 +26,47 @@ Notes:
#pragma once #pragma once
#include "util/statistics.h" #include "util/statistics.h"
#include "util/trail.h" #include "util/trail.h"
#include "util/lbool.h"
#include "ast/euf/euf_enode.h" #include "ast/euf/euf_enode.h"
#include "ast/euf/euf_etable.h" #include "ast/euf/euf_etable.h"
namespace euf { namespace euf {
/*** /***
\brief store derived theory equalities. \brief store derived theory equalities and disequalities
Theory 'id' is notified with the equality of theory variables v1, v2 Theory 'id' is notified with the equality/disequality of theory variables v1, v2.
that are merged into the common root of child and root (their roots may For equalities, v1 and v2 are merged into the common root of child and root (their roots may
have been updated since the equality was derived, but the explanation for have been updated since the equality was derived, but the explanation for
v1 == v2 is provided by explaining the equality child == root. v1 == v2 is provided by explaining the equality child == root.
For disequalities, m_child refers to an equality atom of the form e1 == e2.
It is equal to false under the current context.
The explanation for the disequality v1 != v2 is derived from explaining the
equality between the expression for v1 and e1, and the expression for v2 and e2
and the equality of m_eq and false: the literal corresponding to m_eq is false in the
current assignment stack, or m_child is congruent to false in the egraph.
*/ */
struct th_eq { class th_eq {
theory_id m_id; theory_id m_id;
theory_var m_v1; theory_var m_v1;
theory_var m_v2; theory_var m_v2;
enode* m_child; union {
enode* m_child;
expr* m_eq;
};
enode* m_root; enode* m_root;
public:
bool is_eq() const { return m_root != nullptr; }
theory_id id() const { return m_id; }
theory_var v1() const { return m_v1; }
theory_var v2() const { return m_v2; }
enode* child() const { SASSERT(is_eq()); return m_child; }
enode* root() const { SASSERT(is_eq()); return m_root; }
expr* eq() const { SASSERT(!is_eq()); return m_eq; }
th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r) : th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r) :
m_id(id), m_v1(v1), m_v2(v2), m_child(c), m_root(r) {} m_id(id), m_v1(v1), m_v2(v2), m_child(c), m_root(r) {}
th_eq(theory_id id, theory_var v1, theory_var v2, expr* eq) :
m_id(id), m_v1(v1), m_v2(v2), m_eq(eq), m_root(nullptr) {}
}; };
class egraph { class egraph {
@ -53,6 +74,7 @@ namespace euf {
struct stats { struct stats {
unsigned m_num_merge; unsigned m_num_merge;
unsigned m_num_th_eqs; unsigned m_num_th_eqs;
unsigned m_num_th_diseqs;
unsigned m_num_lits; unsigned m_num_lits;
unsigned m_num_eqs; unsigned m_num_eqs;
unsigned m_num_conflicts; unsigned m_num_conflicts;
@ -111,6 +133,7 @@ namespace euf {
svector<update_record> m_updates; svector<update_record> m_updates;
unsigned_vector m_scopes; unsigned_vector m_scopes;
enode_vector m_expr2enode; enode_vector m_expr2enode;
enode* m_tmp_eq { nullptr };
enode_vector m_nodes; enode_vector m_nodes;
expr_ref_vector m_exprs; expr_ref_vector m_exprs;
unsigned m_num_scopes { 0 }; unsigned m_num_scopes { 0 };
@ -122,11 +145,13 @@ namespace euf {
unsigned m_new_th_eqs_qhead { 0 }; unsigned m_new_th_eqs_qhead { 0 };
svector<enode_bool_pair> m_new_lits; svector<enode_bool_pair> m_new_lits;
svector<th_eq> m_new_th_eqs; svector<th_eq> m_new_th_eqs;
bool_vector m_th_propagates_diseqs;
enode_vector m_todo; enode_vector m_todo;
stats m_stats; stats m_stats;
std::function<void(expr*,expr*,expr*)> m_used_eq; std::function<void(expr*,expr*,expr*)> m_used_eq;
std::function<void(app*,app*)> m_used_cc; std::function<void(app*,app*)> m_used_cc;
std::function<void(std::ostream&, void*)> m_display_justification; std::function<void(std::ostream&, void*)> m_display_justification;
std::function<lbool(enode*)> m_value;
void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) { void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
m_updates.push_back(update_record(r1, n1, r2_num_parents)); m_updates.push_back(update_record(r1, n1, r2_num_parents));
@ -134,6 +159,10 @@ namespace euf {
void push_node(enode* n) { m_updates.push_back(update_record(n)); } void push_node(enode* n) { m_updates.push_back(update_record(n)); }
void add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r); void add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r);
void new_diseq(enode* n1);
void add_th_diseqs(theory_id id, theory_var v1, enode* r);
bool th_propagates_diseqs(theory_id id) const;
void add_literal(enode* n, bool is_eq); void add_literal(enode* n, bool is_eq);
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents); void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
void undo_add_th_var(enode* n, theory_id id); void undo_add_th_var(enode* n, theory_id id);
@ -166,7 +195,7 @@ namespace euf {
std::ostream& display(std::ostream& out, unsigned max_args, enode* n) const; std::ostream& display(std::ostream& out, unsigned max_args, enode* n) const;
public: public:
egraph(ast_manager& m): m(m), m_table(m), m_exprs(m) {} egraph(ast_manager& m);
~egraph(); ~egraph();
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, unsigned n, enode *const* args); enode* mk(expr* f, unsigned n, enode *const* args);
@ -192,12 +221,18 @@ namespace euf {
bool propagate(); bool propagate();
bool inconsistent() const { return m_inconsistent; } bool inconsistent() const { return m_inconsistent; }
/**
* \brief check if two nodes are known to be disequal.
*/
bool are_diseq(enode* a, enode* b) const;
/** /**
\brief Maintain and update cursor into propagated consequences. \brief Maintain and update cursor into propagated consequences.
The result of get_literal() is a pair (n, is_eq) The result of get_literal() is a pair (n, is_eq)
where \c n is an enode and \c is_eq indicates whether the enode where \c n is an enode and \c is_eq indicates whether the enode
is an equality consequence. is an equality consequence.
*/ */
void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq);
bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); } bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); }
bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); } bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); }
enode_bool_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; } enode_bool_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; }
@ -205,13 +240,14 @@ namespace euf {
void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; } void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; }
void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; } void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; }
void add_th_var(enode* n, theory_var v, theory_id id); void add_th_var(enode* n, theory_var v, theory_id id);
void set_th_propagates_diseqs(theory_id id);
void set_merge_enabled(enode* n, bool enable_merge); void set_merge_enabled(enode* n, bool enable_merge);
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; } void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; } void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }
void set_display_justification(std::function<void (std::ostream&, void*)> & d) { m_display_justification = d; } void set_display_justification(std::function<void (std::ostream&, void*)> & d) { m_display_justification = d; }
void set_eval(std::function<lbool(enode*)>& eval) { m_value = eval; }
void begin_explain(); void begin_explain();
void end_explain(); void end_explain();

View file

@ -70,6 +70,21 @@ namespace euf {
return true; return true;
} }
/*
* Find the theory var that that is heuristically
* linked with the fewest equality justifications.
*/
theory_var enode::get_closest_th_var(theory_id id) const {
enode const* n = this;
while (n) {
theory_var v = n->get_th_var(id);
if (v != null_theory_var)
return v;
n = n->m_target;
}
return null_theory_var;
}
bool enode::acyclic() const { bool enode::acyclic() const {
enode const* n = this; enode const* n = this;
enode const* p = this; enode const* p = this;

View file

@ -36,23 +36,23 @@ namespace euf {
const theory_id null_theory_id = -1; const theory_id null_theory_id = -1;
class enode { class enode {
expr* m_expr{ nullptr }; expr* m_expr{ nullptr };
bool m_mark1 { false }; bool m_mark1{ false };
bool m_mark2 { false }; bool m_mark2{ false };
bool m_commutative { false }; bool m_commutative{ false };
bool m_update_children { false }; bool m_update_children{ false };
bool m_interpreted { false }; bool m_interpreted{ false };
bool m_merge_enabled { true }; bool m_merge_enabled{ true };
unsigned m_class_size { 1 }; unsigned m_class_size{ 1 };
unsigned m_table_id { UINT_MAX }; unsigned m_table_id{ UINT_MAX };
enode_vector m_parents; enode_vector m_parents;
enode* m_next{ nullptr }; enode* m_next{ nullptr };
enode* m_root{ nullptr }; enode* m_root{ nullptr };
enode* m_target { nullptr }; enode* m_target{ nullptr };
th_var_list m_th_vars; th_var_list m_th_vars;
justification m_justification; justification m_justification;
unsigned m_num_args { 0 }; unsigned m_num_args{ 0 };
enode* m_args[0]; enode* m_args[0];
friend class enode_args; friend class enode_args;
friend class enode_parents; friend class enode_parents;
@ -81,6 +81,20 @@ namespace euf {
} }
return n; return n;
} }
static enode* mk_tmp(region& r, unsigned num_args) {
void* mem = r.allocate(get_enode_size(num_args));
enode* n = new (mem) enode();
n->m_expr = nullptr;
n->m_next = n;
n->m_root = n;
n->m_commutative = true;
n->m_num_args = 2;
n->m_merge_enabled = true;
for (unsigned i = 0; i < num_args; ++i)
n->m_args[i] = nullptr;
return n;
}
void set_update_children() { m_update_children = true; } void set_update_children() { m_update_children = true; }
@ -148,10 +162,12 @@ namespace euf {
unsigned get_expr_id() const { return m_expr->get_id(); } unsigned get_expr_id() const { return m_expr->get_id(); }
unsigned get_root_id() const { return m_root->m_expr->get_id(); } unsigned get_root_id() const { return m_root->m_expr->get_id(); }
theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); } theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); }
theory_var get_closest_th_var(theory_id id) const;
bool is_attached_to(theory_id id) const { return get_th_var(id) != null_theory_var; } bool is_attached_to(theory_id id) const { return get_th_var(id) != null_theory_var; }
bool has_th_vars() const { return !m_th_vars.empty(); } bool has_th_vars() const { return !m_th_vars.empty(); }
bool has_one_th_var() const { return !m_th_vars.empty() && !m_th_vars.get_next();} bool has_one_th_var() const { return !m_th_vars.empty() && !m_th_vars.get_next();}
theory_var get_first_th_id() const { SASSERT(has_th_vars()); return m_th_vars.get_id(); } theory_var get_first_th_id() const { SASSERT(has_th_vars()); return m_th_vars.get_id(); }
void inc_class_size(unsigned n) { m_class_size += n; } void inc_class_size(unsigned n) { m_class_size += n; }
void dec_class_size(unsigned n) { m_class_size -= n; } void dec_class_size(unsigned n) { m_class_size -= n; }

View file

@ -5,6 +5,7 @@ z3_add_component(sat
sat_aig_finder.cpp sat_aig_finder.cpp
sat_anf_simplifier.cpp sat_anf_simplifier.cpp
sat_asymm_branch.cpp sat_asymm_branch.cpp
sat_bcd.cpp
sat_big.cpp sat_big.cpp
sat_binspr.cpp sat_binspr.cpp
sat_clause.cpp sat_clause.cpp
@ -18,7 +19,7 @@ z3_add_component(sat
sat_drat.cpp sat_drat.cpp
sat_elim_eqs.cpp sat_elim_eqs.cpp
sat_elim_vars.cpp sat_elim_vars.cpp
sat_bcd.cpp sat_gc.cpp
sat_integrity_checker.cpp sat_integrity_checker.cpp
sat_local_search.cpp sat_local_search.cpp
sat_lookahead.cpp sat_lookahead.cpp

View file

@ -235,12 +235,15 @@ namespace dimacs {
skip_whitespace(in); skip_whitespace(in);
switch (*in) { switch (*in) {
case EOF: case EOF:
return false; return false;
case 'c': case 'c':
// parse comment line
case 'p': case 'p':
// parse meta-data information
skip_line(in); skip_line(in);
goto loop; goto loop;
case 'i': case 'i':
// parse input clause
++in; ++in;
skip_whitespace(in); skip_whitespace(in);
read_clause(in, err, m_record.m_lits); read_clause(in, err, m_record.m_lits);
@ -248,6 +251,7 @@ namespace dimacs {
m_record.m_status = sat::status::input(); m_record.m_status = sat::status::input();
break; break;
case 'a': case 'a':
// parse non-redundant theory clause
++in; ++in;
skip_whitespace(in); skip_whitespace(in);
theory_id = read_theory_id(); theory_id = read_theory_id();
@ -256,16 +260,32 @@ namespace dimacs {
m_record.m_tag = drat_record::tag_t::is_clause; m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::th(false, theory_id); m_record.m_status = sat::status::th(false, theory_id);
break; break;
case 'g':
// parse garbage collected Boolean variable
++in;
skip_whitespace(in);
b = parse_int(in, err);
e = parse_int(in, err);
if (e != 0 || b <= 0)
throw lex_error();
m_record.m_tag = drat_record::tag_t::is_var_gc;
m_record.m_node_id = b;
break;
case 'e': case 'e':
// parse expression definition
parse_ast(drat_record::tag_t::is_node); parse_ast(drat_record::tag_t::is_node);
break; break;
case 'f': case 'f':
// parse function declaration
parse_ast(drat_record::tag_t::is_decl); parse_ast(drat_record::tag_t::is_decl);
break; break;
case 's': case 's':
// parse sort declaration (not used)
parse_ast(drat_record::tag_t::is_sort); parse_ast(drat_record::tag_t::is_sort);
break; break;
case 'b': case 'b':
// parse bridge between Boolean variable identifier b
// and expression identifier e, which is of type Bool
++in; ++in;
skip_whitespace(in); skip_whitespace(in);
b = parse_int(in, err); b = parse_int(in, err);
@ -279,6 +299,7 @@ namespace dimacs {
m_record.m_args.push_back(n); m_record.m_args.push_back(n);
break; break;
case 'd': case 'd':
// parse clause deletion
++in; ++in;
skip_whitespace(in); skip_whitespace(in);
read_clause(in, err, m_record.m_lits); read_clause(in, err, m_record.m_lits);
@ -286,6 +307,8 @@ namespace dimacs {
m_record.m_status = sat::status::deleted(); m_record.m_status = sat::status::deleted();
break; break;
case 'r': case 'r':
// parse redundant theory clause
// the clause must be DRUP redundant modulo T
++in; ++in;
skip_whitespace(in); skip_whitespace(in);
theory_id = read_theory_id(); theory_id = read_theory_id();
@ -294,6 +317,7 @@ namespace dimacs {
m_record.m_status = sat::status::th(true, theory_id); m_record.m_status = sat::status::th(true, theory_id);
break; break;
default: default:
// parse clause redundant modulo DRAT (or mostly just DRUP)
read_clause(in, err, m_record.m_lits); read_clause(in, err, m_record.m_lits);
m_record.m_tag = drat_record::tag_t::is_clause; m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::redundant(); m_record.m_status = sat::status::redundant();

View file

@ -53,7 +53,7 @@ namespace dimacs {
}; };
struct drat_record { struct drat_record {
enum class tag_t { is_clause, is_node, is_decl, is_sort, is_bool_def }; enum class tag_t { is_clause, is_node, is_decl, is_sort, is_bool_def, is_var_gc };
tag_t m_tag{ tag_t::is_clause }; tag_t m_tag{ tag_t::is_clause };
// a clause populates m_lits and m_status // a clause populates m_lits and m_status
// a node populates m_node_id, m_name, m_args // a node populates m_node_id, m_name, m_args

View file

@ -165,6 +165,7 @@ namespace sat {
explicit clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {} explicit clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {}
bool is_binary() const { return m_l2_idx != null_literal.to_uint(); } bool is_binary() const { return m_l2_idx != null_literal.to_uint(); }
bool is_ternary() const { return size() == 3; }
unsigned size() const { return is_binary() ? 2 : m_cls->size(); } unsigned size() const { return is_binary() ? 2 : m_cls->size(); }
literal operator[](unsigned idx) const { literal operator[](unsigned idx) const {
SASSERT(idx < size()); SASSERT(idx < size());
@ -178,6 +179,20 @@ namespace sat {
bool contains(bool_var v) const; bool contains(bool_var v) const;
clause * get_clause() const { SASSERT(!is_binary()); return m_cls; } clause * get_clause() const { SASSERT(!is_binary()); return m_cls; }
bool was_removed() const { return !is_binary() && get_clause()->was_removed(); } bool was_removed() const { return !is_binary() && get_clause()->was_removed(); }
bool is_learned() const { return !is_binary() && get_clause()->is_learned(); }
class iterator {
unsigned m_idx;
clause_wrapper const& m_cw;
public:
iterator(clause_wrapper const& c, unsigned idx): m_idx(idx), m_cw(c) {}
iterator& operator++() { ++m_idx; return *this; }
literal operator*() { return m_cw[m_idx]; }
bool operator==(iterator const& other) const { SASSERT(&m_cw == &other.m_cw); return m_idx == other.m_idx; }
bool operator!=(iterator const& other) const { SASSERT(&m_cw == &other.m_cw); return m_idx != other.m_idx; }
};
iterator begin() const { return iterator(*this, 0); }
iterator end() const { return iterator(*this, size()); }
}; };
typedef svector<clause_wrapper> clause_wrapper_vector; typedef svector<clause_wrapper> clause_wrapper_vector;

View file

@ -257,6 +257,22 @@ namespace sat {
} }
} }
void drat::gc_var(bool_var v) {
sat::literal l(v, false);
// TBD: we want to remove all clauses that mention v.
std::cout << "GC " << v << "\n";
m_watches[l.index()].reset();
m_watches[(~l).index()].reset();
if (m_assignment[l.var()] != l_undef) {
unsigned j = 0;
for (literal lit : m_units)
if (lit.var() != v)
m_units[j++] = lit;
m_units.shrink(j);
m_assignment[l.var()] = l_undef;
}
}
void drat::bool_def(bool_var v, unsigned n) { void drat::bool_def(bool_var v, unsigned n) {
if (m_out) if (m_out)
(*m_out) << "b " << v << " " << n << " 0\n"; (*m_out) << "b " << v << " " << n << " 0\n";
@ -277,6 +293,11 @@ namespace sat {
(*m_out) << " 0\n"; (*m_out) << " 0\n";
} }
void drat::log_gc_var(bool_var v) {
if (m_out)
(*m_out) << "g " << v << " 0\n";
}
void drat::log_adhoc(std::function<void(std::ostream&)>& fn) { void drat::log_adhoc(std::function<void(std::ostream&)>& fn) {
if (m_out) if (m_out)
fn(*m_out); fn(*m_out);

View file

@ -38,6 +38,12 @@ Notes:
Redundant clause (theory lemma if theory id is given) Redundant clause (theory lemma if theory id is given)
[r [<theory-id>]] <literal>* 0 [r [<theory-id>]] <literal>* 0
Declaration of an auxiliary function:
f <smtlib2-function-declaration> 0
Garbage collection of a Boolean variable:
g <bool-var-id> 0
Available theories are: Available theories are:
- euf The theory lemma should be a consequence of congruence closure. - euf The theory lemma should be a consequence of congruence closure.
- ba TBD (need to also log cardinality and pb constraints) - ba TBD (need to also log cardinality and pb constraints)
@ -125,6 +131,8 @@ namespace sat {
void add(literal_vector const& c); // add learned clause void add(literal_vector const& c); // add learned clause
void add(unsigned sz, literal const* lits, status st); void add(unsigned sz, literal const* lits, status st);
void gc_var(bool_var v);
// support for SMT - connect Boolean variables with AST nodes // support for SMT - connect Boolean variables with AST nodes
// associate AST node id with Boolean variable v // associate AST node id with Boolean variable v
void bool_def(bool_var v, unsigned n); void bool_def(bool_var v, unsigned n);
@ -134,6 +142,8 @@ namespace sat {
void def_add_arg(unsigned arg); void def_add_arg(unsigned arg);
void def_end(); void def_end();
void log_gc_var(bool_var v);
// ad-hoc logging until a format is developed // ad-hoc logging until a format is developed
void log_adhoc(std::function<void(std::ostream&)>& fn); void log_adhoc(std::function<void(std::ostream&)>& fn);

562
src/sat/sat_gc.cpp Normal file
View file

@ -0,0 +1,562 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sat_solver.cpp
Abstract:
SAT solver main class.
Author:
Leonardo de Moura (leonardo) 2011-05-21.
Revision History:
--*/
#include "sat/sat_solver.h"
#define ENABLE_TERNARY true
namespace sat {
// -----------------------
//
// GC
//
// -----------------------
bool solver::should_gc() const {
return
m_conflicts_since_gc > m_gc_threshold &&
(m_config.m_gc_strategy != GC_DYN_PSM || at_base_lvl());
}
void solver::do_gc() {
if (!should_gc()) return;
TRACE("sat", tout << m_conflicts_since_gc << " " << m_gc_threshold << "\n";);
unsigned gc = m_stats.m_gc_clause;
m_conflicts_since_gc = 0;
m_gc_threshold += m_config.m_gc_increment;
IF_VERBOSE(10, verbose_stream() << "(sat.gc)\n";);
CASSERT("sat_gc_bug", check_invariant());
switch (m_config.m_gc_strategy) {
case GC_GLUE:
gc_glue();
break;
case GC_PSM:
gc_psm();
break;
case GC_GLUE_PSM:
gc_glue_psm();
break;
case GC_PSM_GLUE:
gc_psm_glue();
break;
case GC_DYN_PSM:
if (!m_assumptions.empty()) {
gc_glue_psm();
break;
}
if (!at_base_lvl())
return;
gc_dyn_psm();
break;
default:
UNREACHABLE();
break;
}
if (m_ext) m_ext->gc();
if (gc > 0 && should_defrag()) {
defrag_clauses();
}
CASSERT("sat_gc_bug", check_invariant());
}
/**
\brief Lex on (glue, size)
*/
struct glue_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->glue() < c2->glue()) return true;
return c1->glue() == c2->glue() && c1->size() < c2->size();
}
};
/**
\brief Lex on (psm, size)
*/
struct psm_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->psm() < c2->psm()) return true;
return c1->psm() == c2->psm() && c1->size() < c2->size();
}
};
/**
\brief Lex on (glue, psm, size)
*/
struct glue_psm_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->glue() < c2->glue()) return true;
if (c1->glue() > c2->glue()) return false;
if (c1->psm() < c2->psm()) return true;
if (c1->psm() > c2->psm()) return false;
return c1->size() < c2->size();
}
};
/**
\brief Lex on (psm, glue, size)
*/
struct psm_glue_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->psm() < c2->psm()) return true;
if (c1->psm() > c2->psm()) return false;
if (c1->glue() < c2->glue()) return true;
if (c1->glue() > c2->glue()) return false;
return c1->size() < c2->size();
}
};
void solver::gc_glue() {
std::stable_sort(m_learned.begin(), m_learned.end(), glue_lt());
gc_half("glue");
}
void solver::gc_psm() {
save_psm();
std::stable_sort(m_learned.begin(), m_learned.end(), psm_lt());
gc_half("psm");
}
void solver::gc_glue_psm() {
save_psm();
std::stable_sort(m_learned.begin(), m_learned.end(), glue_psm_lt());
gc_half("glue-psm");
}
void solver::gc_psm_glue() {
save_psm();
std::stable_sort(m_learned.begin(), m_learned.end(), psm_glue_lt());
gc_half("psm-glue");
}
/**
\brief Compute the psm of all learned clauses.
*/
void solver::save_psm() {
for (clause* cp : m_learned) {
cp->set_psm(psm(*cp));
}
}
/**
\brief GC (the second) half of the clauses in the database.
*/
void solver::gc_half(char const * st_name) {
TRACE("sat", tout << "gc\n";);
unsigned sz = m_learned.size();
unsigned new_sz = sz/2; // std::min(sz/2, m_clauses.size()*2);
unsigned j = new_sz;
for (unsigned i = new_sz; i < sz; i++) {
clause & c = *(m_learned[i]);
if (can_delete(c)) {
detach_clause(c);
del_clause(c);
}
else {
m_learned[j] = &c;
j++;
}
}
new_sz = j;
m_stats.m_gc_clause += sz - new_sz;
m_learned.shrink(new_sz);
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << (sz - new_sz) << ")\n";);
}
bool solver::can_delete3(literal l1, literal l2, literal l3) const {
if (value(l1) == l_true &&
value(l2) == l_false &&
value(l3) == l_false) {
justification const& j = m_justification[l1.var()];
if (j.is_ternary_clause()) {
watched w1(l2, l3);
watched w2(j.get_literal1(), j.get_literal2());
return w1 != w2;
}
}
return true;
}
bool solver::can_delete(clause const & c) const {
if (c.on_reinit_stack())
return false;
if (ENABLE_TERNARY && c.size() == 3) {
return
can_delete3(c[0],c[1],c[2]) &&
can_delete3(c[1],c[0],c[2]) &&
can_delete3(c[2],c[0],c[1]);
}
literal l0 = c[0];
if (value(l0) != l_true)
return true;
justification const & jst = m_justification[l0.var()];
return !jst.is_clause() || cls_allocator().get_clause(jst.get_clause_offset()) != &c;
}
/**
\brief Use gc based on dynamic psm. Clauses are initially frozen.
*/
void solver::gc_dyn_psm() {
TRACE("sat", tout << "gc\n";);
// To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact
// that I may miss some propagations for reactivated clauses.
SASSERT(at_base_lvl());
// compute
// d_tk
unsigned h = 0;
unsigned V_tk = 0;
for (bool_var v = 0; v < num_vars(); v++) {
if (m_assigned_since_gc[v]) {
V_tk++;
m_assigned_since_gc[v] = false;
}
if (m_phase[v] != m_prev_phase[v]) {
h++;
m_prev_phase[v] = m_phase[v];
}
}
double d_tk = V_tk == 0 ? static_cast<double>(num_vars() + 1) : static_cast<double>(h)/static_cast<double>(V_tk);
if (d_tk < m_min_d_tk)
m_min_d_tk = d_tk;
TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";);
unsigned frozen = 0;
unsigned deleted = 0;
unsigned activated = 0;
clause_vector::iterator it = m_learned.begin();
clause_vector::iterator it2 = it;
clause_vector::iterator end = m_learned.end();
for (; it != end; ++it) {
clause & c = *(*it);
if (!c.frozen()) {
// Active clause
if (c.glue() > m_config.m_gc_small_lbd) {
// I never delete clauses with small lbd
if (c.was_used()) {
c.reset_inact_rounds();
}
else {
c.inc_inact_rounds();
if (c.inact_rounds() > m_config.m_gc_k) {
detach_clause(c);
del_clause(c);
m_stats.m_gc_clause++;
deleted++;
continue;
}
}
c.unmark_used();
if (psm(c) > static_cast<unsigned>(c.size() * m_min_d_tk)) {
// move to frozen;
TRACE("sat_frozen", tout << "freezing size: " << c.size() << " psm: " << psm(c) << " " << c << "\n";);
detach_clause(c);
c.reset_inact_rounds();
c.freeze();
m_num_frozen++;
frozen++;
}
}
}
else {
// frozen clause
clause & c = *(*it);
if (psm(c) <= static_cast<unsigned>(c.size() * m_min_d_tk)) {
c.unfreeze();
m_num_frozen--;
activated++;
if (!activate_frozen_clause(c)) {
// clause was satisfied, reduced to a conflict, unit or binary clause.
del_clause(c);
continue;
}
}
else {
c.inc_inact_rounds();
if (c.inact_rounds() > m_config.m_gc_k) {
del_clause(c);
m_stats.m_gc_clause++;
deleted++;
continue;
}
}
}
*it2 = *it;
++it2;
}
m_learned.set_end(it2);
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk <<
" :frozen " << frozen << " :activated " << activated << " :deleted " << deleted << ")\n";);
}
// return true if should keep the clause, and false if we should delete it.
bool solver::activate_frozen_clause(clause & c) {
TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";);
SASSERT(at_base_lvl());
// do some cleanup
unsigned sz = c.size();
unsigned j = 0;
for (unsigned i = 0; i < sz; i++) {
literal l = c[i];
switch (value(l)) {
case l_true:
return false;
case l_false:
break;
case l_undef:
if (i != j) {
std::swap(c[i], c[j]);
}
j++;
break;
}
}
TRACE("sat", tout << "after cleanup:\n" << mk_lits_pp(j, c.begin()) << "\n";);
unsigned new_sz = j;
switch (new_sz) {
case 0:
if (m_config.m_drat) m_drat.add();
set_conflict();
return false;
case 1:
assign_unit(c[0]);
return false;
case 2:
mk_bin_clause(c[0], c[1], true);
return false;
default:
shrink(c, sz, new_sz);
attach_clause(c);
return true;
}
}
/**
\brief Compute phase saving measure for the given clause.
*/
unsigned solver::psm(clause const & c) const {
unsigned r = 0;
for (literal l : c) {
if (l.sign() ^ m_phase[l.var()]) {
r++;
}
}
return r;
}
/**
* Control the size of the reinit-stack.
* by agressively garbage collecting lemmas that are not asserting.
*/
void solver::gc_reinit_stack(unsigned num_scopes) {
return;
SASSERT (!at_base_lvl());
unsigned new_lvl = scope_lvl() - num_scopes;
ptr_vector<clause> to_gc;
unsigned j = m_scopes[new_lvl].m_clauses_to_reinit_lim;
unsigned sz = m_clauses_to_reinit.size();
if (sz - j <= 2000)
return;
for (unsigned i = j; i < sz; ++i) {
auto cw = m_clauses_to_reinit[i];
if (cw.is_binary() || is_asserting(new_lvl, cw))
m_clauses_to_reinit[j++] = cw;
else
to_gc.push_back(cw.get_clause());
}
m_clauses_to_reinit.shrink(j);
if (to_gc.empty())
return;
for (clause* c : to_gc) {
SASSERT(c->on_reinit_stack());
c->set_removed(true);
c->set_reinit_stack(false);
}
j = 0;
for (unsigned i = 0; i < m_learned.size(); ++i) {
clause & c = *(m_learned[i]);
if (c.was_removed()) {
detach_clause(c);
del_clause(c);
}
else
m_learned[j++] = &c;
}
std::cout << "gc: " << to_gc.size() << " " << m_learned.size() << " -> " << j << "\n";
SASSERT(m_learned.size() - j == to_gc.size());
m_learned.shrink(j);
}
bool solver::is_asserting(unsigned new_lvl, clause_wrapper const& cw) const {
if (!cw.is_learned())
return true;
bool seen_true = false;
for (literal lit : cw) {
switch (value(lit)) {
case l_true:
if (lvl(lit) > new_lvl || seen_true)
return false;
seen_true = true;
continue;
case l_false:
continue;
case l_undef:
return false;
}
}
return true;
}
#if 0
void solver::gc_reinit_stack(unsigned num_scopes) {
SASSERT (!at_base_lvl());
collect_clauses_to_gc(num_scopes);
for (literal lit : m_watch_literals_to_gc) {
mark_members_of_watch_list(lit);
shrink_watch_list(lit);
}
unsigned j = 0;
for (clause* c : m_learned)
if (!c->was_removed())
m_learned[j++] = c;
m_learned.shrink(j);
for (clause* c : m_clauses_to_gc)
del_clause(*c);
m_clauses_to_gc.reset();
}
void solver::add_to_gc(literal lit, clause_wrapper const& cw) {
m_literal2gc_clause.reserve(lit.index()+1);
m_literal2gc_clause[lit.index()].push_back(cw);
if (!is_visited(lit)) {
mark_visited(lit);
m_watch_literals_to_gc.push_back(lit);
}
}
void solver::add_to_gc(clause_wrapper const& cw) {
std::cout << "add-to-gc " << cw << "\n";
if (cw.is_binary()) {
add_to_gc(cw[0], cw);
add_to_gc(cw[1], clause_wrapper(cw[1], cw[0]));
}
else if (ENABLE_TERNARY && cw.is_ternary()) {
SASSERT(cw.is_learned());
add_to_gc(cw[0], cw);
add_to_gc(cw[1], cw);
add_to_gc(cw[2], cw);
cw.get_clause()->set_removed(true);
}
else {
SASSERT(cw.is_learned());
add_to_gc(cw[0], cw);
add_to_gc(cw[1], cw);
cw.get_clause()->set_removed(true);
}
}
void solver::insert_ternary_to_delete(literal lit, clause_wrapper const& cw) {
SASSERT(cw.is_ternary());
SASSERT(lit == cw[0] || lit == cw[1] || lit == cw[2]);
literal lit1, lit2;
if (cw[0] == lit)
lit1 = cw[1], lit2 = cw[2];
else if (cw[1] == lit)
lit1 = cw[0], lit2 = cw[2];
else
lit1 = cw[0], lit2 = cw[1];
insert_ternary_to_delete(lit1, lit2, true);
}
void solver::insert_ternary_to_delete(literal lit1, literal lit2, bool should_delete) {
if (lit1.index() > lit2.index())
std::swap(lit1, lit2);
m_ternary_to_delete.push_back(std::tuple(lit1, lit2, should_delete));
}
bool solver::in_ternary_to_delete(literal lit1, literal lit2) {
if (lit1.index() > lit2.index())
std::swap(lit1, lit2);
bool found = m_ternary_to_delete.contains(std::make_pair(lit1, lit2));
if (found) std::cout << lit1 << " " << lit2 << "\n";
return found;
}
void solver::collect_clauses_to_gc(unsigned num_scopes) {
unsigned new_lvl = scope_lvl() - num_scopes;
init_visited();
m_watch_literals_to_gc.reset();
unsigned j = m_scopes[new_lvl].m_clauses_to_reinit_lim;
for (unsigned i = j, sz = m_clauses_to_reinit.size(); i < sz; ++i) {
auto cw = m_clauses_to_reinit[i];
if (is_asserting(new_lvl, cw))
m_clauses_to_reinit[j++] = cw;
else
add_to_gc(cw);
}
m_clauses_to_reinit.shrink(j);
SASSERT(m_clauses_to_reinit.size() >= j);
}
void solver::mark_members_of_watch_list(literal lit) {
init_visited();
m_ternary_to_delete.reset();
for (auto const& cw : m_literal2gc_clause[lit.index()]) {
SASSERT(!cw.is_binary() || lit == cw[0]);
SASSERT(cw.is_binary() || cw.was_removed());
if (cw.is_binary())
mark_visited(cw[1]);
else if (ENABLE_TERNARY && cw.is_ternary())
insert_ternary_to_delete(lit, cw);
}
}
void solver::shrink_watch_list(literal lit) {
auto& wl = get_wlist((~lit).index());
unsigned j = 0, sz = wl.size(), end = sz;
for (unsigned i = 0; i < end; ++i) {
auto w = wl[i];
if (w.is_binary_clause() && !is_visited(w.get_literal()))
wl[j++] = w;
else if (ENABLE_TERNARY && w.is_ternary_clause())
insert_ternary_to_delete(w.literal1(), w.literal2(), false);
else if (w.is_clause() && !get_clause(w).was_removed())
wl[j++] = w;
else if (w.is_ext_constraint())
wl[j++] = w;
}
#if 0
std::sort(m_ternary_to_delete.begin(), m_ternary_to_delete.end());
int prev = -1;
unsigned k = 0;
std::tuple<literal, literal, bool> p = tuple(null_literal, null_literal, false);
for (unsigned i = 0; i < m_ternary_to_delete.size(); ++i) {
auto const& t = m_ternary_to_delete[i];
}
#endif
std::cout << "gc-watch-list " << lit << " " << wl.size() << " -> " << j << "\n";
wl.shrink(j);
m_literal2gc_clause[lit.index()].reset();
}
#endif
}

View file

@ -202,6 +202,7 @@ namespace sat {
bool integrity_checker::check_reinit_stack() const { bool integrity_checker::check_reinit_stack() const {
for (auto const& c : s.m_clauses_to_reinit) { for (auto const& c : s.m_clauses_to_reinit) {
SASSERT(c.is_binary() || c.get_clause()->on_reinit_stack());
VERIFY(c.is_binary() || c.get_clause()->on_reinit_stack()); VERIFY(c.is_binary() || c.get_clause()->on_reinit_stack());
} }
return true; return true;

View file

@ -167,9 +167,11 @@ namespace sat {
return; return;
if (m_probing_binary) { if (m_probing_binary) {
watch_list & wlist = s.get_wlist(~l); unsigned sz = s.get_wlist(~l).size();
for (unsigned i = 0; i < wlist.size(); ++i) { for (unsigned i = 0; i < sz; ++i) {
watch_list& wlist = s.get_wlist(~l);
watched & w = wlist[i]; watched & w = wlist[i];
sz = wlist.size();
if (!w.is_binary_clause()) if (!w.is_binary_clause())
continue; continue;
literal l2 = w.get_literal(); literal l2 = w.get_literal();
@ -177,7 +179,8 @@ namespace sat {
continue; continue;
if (s.value(l2) != l_undef) if (s.value(l2) != l_undef)
continue; continue;
// Note: that try_lit calls propagate, which may update the watch lists. // Note: that try_lit calls propagate, which may update the watch lists
// and potentially change the set of variables.
if (!try_lit(l2, false)) if (!try_lit(l2, false))
return; return;
if (s.inconsistent()) if (s.inconsistent())

File diff suppressed because it is too large Load diff

View file

@ -124,6 +124,7 @@ namespace sat {
bool_vector m_lit_mark; bool_vector m_lit_mark;
bool_vector m_eliminated; bool_vector m_eliminated;
bool_vector m_external; bool_vector m_external;
unsigned_vector m_var_scope;
unsigned_vector m_touched; unsigned_vector m_touched;
unsigned m_touch_index; unsigned m_touch_index;
literal_vector m_replay_assign; literal_vector m_replay_assign;
@ -286,6 +287,8 @@ namespace sat {
clause * mk_ter_clause(literal * lits, status st); clause * mk_ter_clause(literal * lits, status st);
bool attach_ter_clause(clause & c, status st); bool attach_ter_clause(clause & c, status st);
clause * mk_nary_clause(unsigned num_lits, literal * lits, status st); clause * mk_nary_clause(unsigned num_lits, literal * lits, status st);
bool has_variables_to_reinit(clause const& c) const;
bool has_variables_to_reinit(literal l1, literal l2) const;
bool attach_nary_clause(clause & c); bool attach_nary_clause(clause & c);
void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c, bool & reinit);
void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); }
@ -320,6 +323,7 @@ namespace sat {
void detach_nary_clause(clause & c); void detach_nary_clause(clause & c);
void detach_ter_clause(clause & c); void detach_ter_clause(clause & c);
void push_reinit_stack(clause & c); void push_reinit_stack(clause & c);
void push_reinit_stack(literal l1, literal l2);
void init_visited(); void init_visited();
void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; } void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; }
@ -441,6 +445,7 @@ namespace sat {
protected: protected:
bool should_propagate() const; bool should_propagate() const;
bool propagate_core(bool update); bool propagate_core(bool update);
bool propagate_literal(literal l, bool update);
// ----------------------- // -----------------------
// //
@ -546,6 +551,10 @@ namespace sat {
bool can_delete(clause const & c) const; bool can_delete(clause const & c) const;
bool can_delete3(literal l1, literal l2, literal l3) const; bool can_delete3(literal l1, literal l2, literal l3) const;
// gc for lemmas in the reinit-stack
void gc_reinit_stack(unsigned num_scopes);
bool is_asserting(unsigned new_lvl, clause_wrapper const& cw) const;
clause& get_clause(watch_list::iterator it) const { clause& get_clause(watch_list::iterator it) const {
SASSERT(it->get_kind() == watched::CLAUSE); SASSERT(it->get_kind() == watched::CLAUSE);
return get_clause(it->get_clause_offset()); return get_clause(it->get_clause_offset());

View file

@ -84,6 +84,7 @@ namespace array {
} }
sat::check_result solver::check() { sat::check_result solver::check() {
force_push();
// flet<bool> _is_redundant(m_is_redundant, true); // flet<bool> _is_redundant(m_is_redundant, true);
bool turn[2] = { false, false }; bool turn[2] = { false, false };
turn[s().rand()(2)] = true; turn[s().rand()(2)] = true;
@ -96,14 +97,8 @@ namespace array {
return sat::check_result::CR_DONE; return sat::check_result::CR_DONE;
} }
void solver::push() { void solver::pop_core(unsigned n) {
th_euf_solver::lazy_push(); th_euf_solver::pop_core(n);
}
void solver::pop(unsigned n) {
n = lazy_pop(n);
if (n == 0)
return;
m_var_data.resize(get_num_vars()); m_var_data.resize(get_num_vars());
} }
@ -111,9 +106,9 @@ namespace array {
for (unsigned i = 0; i < get_num_vars(); ++i) { for (unsigned i = 0; i < get_num_vars(); ++i) {
auto& d = get_var_data(i); auto& d = get_var_data(i);
out << var2enode(i)->get_expr_id() << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n"; out << var2enode(i)->get_expr_id() << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n";
display_info(out, "parent beta", d.m_parent_lambdas); display_info(out, "parent lambdas", d.m_parent_lambdas);
display_info(out, "parent select", d.m_parent_selects); display_info(out, "parent select", d.m_parent_selects);
display_info(out, "beta ", d.m_lambdas); display_info(out, "b ", d.m_lambdas);
} }
return out; return out;
} }
@ -159,12 +154,14 @@ namespace array {
} }
void solver::new_eq_eh(euf::th_eq const& eq) { void solver::new_eq_eh(euf::th_eq const& eq) {
m_find.merge(eq.m_v1, eq.m_v2); force_push();
m_find.merge(eq.v1(), eq.v2());
} }
bool solver::unit_propagate() { bool solver::unit_propagate() {
if (m_qhead == m_axiom_trail.size()) if (m_qhead == m_axiom_trail.size())
return false; return false;
force_push();
bool prop = false; bool prop = false;
ctx.push(value_trail<euf::solver, unsigned>(m_qhead)); ctx.push(value_trail<euf::solver, unsigned>(m_qhead));
for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead) for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead)

View file

@ -175,6 +175,8 @@ namespace array {
var_data& get_var_data(euf::enode* n) { return get_var_data(n->get_th_var(get_id())); } var_data& get_var_data(euf::enode* n) { return get_var_data(n->get_th_var(get_id())); }
var_data& get_var_data(theory_var v) { return *m_var_data[v]; } var_data& get_var_data(theory_var v) { return *m_var_data[v]; }
var_data const& get_var_data(theory_var v) const { return *m_var_data[v]; } var_data const& get_var_data(theory_var v) const { return *m_var_data[v]; }
void pop_core(unsigned n) override;
// models // models
bool have_different_model_values(theory_var v1, theory_var v2); bool have_different_model_values(theory_var v1, theory_var v2);
@ -191,8 +193,7 @@ namespace array {
void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) override {} void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) override {}
void asserted(literal l) override {} void asserted(literal l) override {}
sat::check_result check() override; sat::check_result check() override;
void push() override;
void pop(unsigned n) override;
std::ostream& display(std::ostream& out) const override; std::ostream& display(std::ostream& out) const override;
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override; std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override; std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;

View file

@ -55,6 +55,25 @@ namespace bv {
} }
} }
void ackerman::used_diseq_eh(euf::theory_var v1, euf::theory_var v2) {
if (v1 == v2)
return;
if (v1 > v2)
std::swap(v1, v2);
vv* n = m_tmp_vv;
n->v1 = v1;
n->v2 = v2;
vv* other = m_table.insert_if_not_there(n);
other->m_count++;
if (other->m_count > m_propagate_high_watermark || other->m_glue == 0)
s.s().set_should_simplify();
vv::push_to_front(m_queue, other);
if (other == n) {
new_tmp();
gc();
}
}
void ackerman::update_glue(vv& v) { void ackerman::update_glue(vv& v) {
unsigned sz = s.m_bits[v.v1].size(); unsigned sz = s.m_bits[v.v1].size();
m_diff_levels.reserve(s.s().scope_lvl() + 1, false); m_diff_levels.reserve(s.s().scope_lvl() + 1, false);

View file

@ -72,6 +72,8 @@ namespace bv {
void used_eq_eh(euf::theory_var v1, euf::theory_var v2); void used_eq_eh(euf::theory_var v1, euf::theory_var v2);
void used_diseq_eh(euf::theory_var v1, euf::theory_var v2);
void propagate(); void propagate();
}; };

View file

@ -62,15 +62,17 @@ namespace bv {
m_zero_one_bits.push_back(zero_one_bits()); m_zero_one_bits.push_back(zero_one_bits());
ctx.attach_th_var(n, this, r); ctx.attach_th_var(n, this, r);
TRACE("bv", tout << "mk-var: " << r << " " << n->get_expr_id() << " " << mk_pp(n->get_expr(), m) << "\n";); TRACE("bv", tout << "mk-var: " << r << " " << n->get_expr_id() << " " << mk_bounded_pp(n->get_expr(), m) << "\n";);
return r; return r;
} }
void solver::apply_sort_cnstr(euf::enode * n, sort * s) { void solver::apply_sort_cnstr(euf::enode * n, sort * s) {
force_push();
get_var(n); get_var(n);
} }
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
force_push();
SASSERT(m.is_bool(e)); SASSERT(m.is_bool(e));
if (!visit_rec(m, e, sign, root, redundant)) if (!visit_rec(m, e, sign, root, redundant))
return sat::null_literal; return sat::null_literal;
@ -81,6 +83,7 @@ namespace bv {
} }
void solver::internalize(expr* e, bool redundant) { void solver::internalize(expr* e, bool redundant) {
force_push();
visit_rec(m, e, false, false, redundant); visit_rec(m, e, false, false, redundant);
} }
@ -337,7 +340,7 @@ namespace bv {
for (expr* b : k_bits) for (expr* b : k_bits)
args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero)); args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));
expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m); expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m);
expr_ref eq(m.mk_eq(n, sum), m); expr_ref eq = mk_eq(n, sum);
sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant); sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant);
add_unit(lit); add_unit(lit);
} }
@ -367,7 +370,7 @@ namespace bv {
unsigned sz = bv.get_bv_size(n); unsigned sz = bv.get_bv_size(n);
numeral mod = power(numeral(2), sz); numeral mod = power(numeral(2), sz);
rhs = m_autil.mk_mod(e, m_autil.mk_int(mod)); rhs = m_autil.mk_mod(e, m_autil.mk_int(mod));
expr_ref eq(m.mk_eq(lhs, rhs), m); expr_ref eq = mk_eq(lhs, rhs);
TRACE("bv", tout << eq << "\n";); TRACE("bv", tout << eq << "\n";);
add_unit(ctx.internalize(eq, false, false, m_is_redundant)); add_unit(ctx.internalize(eq, false, false, m_is_redundant));
@ -378,9 +381,9 @@ namespace bv {
numeral div = power2(i); numeral div = power2(i);
rhs = m_autil.mk_idiv(e, m_autil.mk_int(div)); rhs = m_autil.mk_idiv(e, m_autil.mk_int(div));
rhs = m_autil.mk_mod(rhs, m_autil.mk_int(2)); rhs = m_autil.mk_mod(rhs, m_autil.mk_int(2));
rhs = m.mk_eq(rhs, m_autil.mk_int(1)); rhs = mk_eq(rhs, m_autil.mk_int(1));
lhs = n_bits.get(i); lhs = n_bits.get(i);
expr_ref eq(m.mk_eq(lhs, rhs), m); expr_ref eq = mk_eq(lhs, rhs);
TRACE("bv", tout << eq << "\n";); TRACE("bv", tout << eq << "\n";);
add_unit(ctx.internalize(eq, false, false, m_is_redundant)); add_unit(ctx.internalize(eq, false, false, m_is_redundant));
} }
@ -403,10 +406,10 @@ namespace bv {
void solver::internalize_carry(app* n) { void solver::internalize_carry(app* n) {
SASSERT(n->get_num_args() == 3); SASSERT(n->get_num_args() == 3);
literal r = ctx.get_literal(n); literal r = expr2literal(n);
literal l1 = ctx.get_literal(n->get_arg(0)); literal l1 = expr2literal(n->get_arg(0));
literal l2 = ctx.get_literal(n->get_arg(1)); literal l2 = expr2literal(n->get_arg(1));
literal l3 = ctx.get_literal(n->get_arg(2)); literal l3 = expr2literal(n->get_arg(2));
add_clause(~r, l1, l2); add_clause(~r, l1, l2);
add_clause(~r, l1, l3); add_clause(~r, l1, l3);
add_clause(~r, l2, l3); add_clause(~r, l2, l3);
@ -417,7 +420,7 @@ namespace bv {
void solver::internalize_xor3(app* n) { void solver::internalize_xor3(app* n) {
SASSERT(n->get_num_args() == 3); SASSERT(n->get_num_args() == 3);
literal r = ctx.get_literal(n); literal r = expr2literal(n);
literal l1 = expr2literal(n->get_arg(0)); literal l1 = expr2literal(n->get_arg(0));
literal l2 = expr2literal(n->get_arg(1)); literal l2 = expr2literal(n->get_arg(1));
literal l3 = expr2literal(n->get_arg(2)); literal l3 = expr2literal(n->get_arg(2));
@ -483,7 +486,7 @@ namespace bv {
expr_ref out(m); expr_ref out(m);
fn(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), out); fn(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), out);
sat::literal def = ctx.internalize(out, false, false, m_is_redundant); sat::literal def = ctx.internalize(out, false, false, m_is_redundant);
add_def(def, ctx.get_literal(n)); add_def(def, expr2literal(n));
} }
void solver::add_def(sat::literal def, sat::literal l) { void solver::add_def(sat::literal def, sat::literal l) {
@ -568,14 +571,18 @@ namespace bv {
} }
void solver::assert_ackerman(theory_var v1, theory_var v2) { void solver::assert_ackerman(theory_var v1, theory_var v2) {
flet<bool> _red(m_is_redundant, true); if (v1 == v2)
return;
if (v1 > v2)
std::swap(v1, v2);
flet<bool> _red(m_is_redundant, true);
++m_stats.m_ackerman; ++m_stats.m_ackerman;
expr* o1 = var2expr(v1); expr* o1 = var2expr(v1);
expr* o2 = var2expr(v2); expr* o2 = var2expr(v2);
expr_ref oe(m.mk_eq(o1, o2), m); expr_ref oe = mk_var_eq(v1, v2);
literal oeq = ctx.internalize(oe, false, false, m_is_redundant); literal oeq = ctx.internalize(oe, false, false, m_is_redundant);
unsigned sz = get_bv_size(v1); unsigned sz = m_bits[v1].size();
TRACE("bv", tout << oe << "\n";); TRACE("bv", tout << "ackerman-eq: " << s().scope_lvl() << " " << oe << "\n";);
literal_vector eqs; literal_vector eqs;
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
literal l1 = m_bits[v1][i]; literal l1 = m_bits[v1][i];
@ -583,7 +590,7 @@ namespace bv {
expr_ref e1(m), e2(m); expr_ref e1(m), e2(m);
e1 = bv.mk_bit2bool(o1, i); e1 = bv.mk_bit2bool(o1, i);
e2 = bv.mk_bit2bool(o2, i); e2 = bv.mk_bit2bool(o2, i);
expr_ref e(m.mk_eq(e1, e2), m); expr_ref e = mk_eq(e1, e2);
literal eq = ctx.internalize(e, false, false, m_is_redundant); literal eq = ctx.internalize(e, false, false, m_is_redundant);
add_clause(l1, ~l2, ~eq); add_clause(l1, ~l2, ~eq);
add_clause(~l1, l2, ~eq); add_clause(~l1, l2, ~eq);

View file

@ -37,15 +37,14 @@ namespace bv {
}; };
class solver::bit_occs_trail : public trail<euf::solver> { class solver::bit_occs_trail : public trail<euf::solver> {
solver& s;
bit_atom& a; bit_atom& a;
var_pos_occ* m_occs; var_pos_occ* m_occs;
public: public:
bit_occs_trail(solver& s, bit_atom& a):s(s), a(a), m_occs(a.m_occs) {} bit_occs_trail(solver& s, bit_atom& a): a(a), m_occs(a.m_occs) {}
virtual void undo(euf::solver& euf) { virtual void undo(euf::solver& euf) {
std::cout << "add back occurrences " << & a << "\n"; IF_VERBOSE(1, verbose_stream() << "add back occurrences " << & a << "\n");
a.m_occs = m_occs; a.m_occs = m_occs;
} }
}; };
@ -57,6 +56,7 @@ namespace bv {
m_ackerman(*this), m_ackerman(*this),
m_bb(m, get_config()), m_bb(m, get_config()),
m_find(*this) { m_find(*this) {
ctx.get_egraph().set_th_propagates_diseqs(id);
} }
void solver::fixed_var_eh(theory_var v1) { void solver::fixed_var_eh(theory_var v1) {
@ -194,9 +194,37 @@ namespace bv {
} }
void solver::new_eq_eh(euf::th_eq const& eq) { void solver::new_eq_eh(euf::th_eq const& eq) {
TRACE("bv", tout << "new eq " << eq.m_v1 << " == " << eq.m_v2 << "\n";); force_push();
if (is_bv(eq.m_v1)) TRACE("bv", tout << "new eq " << eq.v1() << " == " << eq.v2() << "\n";);
m_find.merge(eq.m_v1, eq.m_v2); if (is_bv(eq.v1()))
m_find.merge(eq.v1(), eq.v2());
}
void solver::new_diseq_eh(euf::th_eq const& ne) {
theory_var v1 = ne.v1(), v2 = ne.v2();
if (!is_bv(v1))
return;
if (!get_config().m_bv_eq_axioms)
return;
TRACE("bv", tout << "diff: " << v1 << " != " << v2 << "\n";);
unsigned sz = m_bits[v1].size();
for (unsigned i = 0; i < sz; ++i) {
sat::literal a = m_bits[v1][i];
sat::literal b = m_bits[v2][i];
if (a == ~b)
return;
auto va = s().value(a);
auto vb = s().value(b);
if (va != l_undef && vb != l_undef && va != vb)
return;
}
if (s().at_search_lvl()) {
force_push();
assert_ackerman(v1, v2);
}
else
m_ackerman.used_diseq_eh(v1, v2);
} }
double solver::get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const { return 0; } double solver::get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const { return 0; }
@ -209,6 +237,7 @@ namespace bv {
TRACE("bv", display_constraint(tout, idx);); TRACE("bv", display_constraint(tout, idx););
switch (c.m_kind) { switch (c.m_kind) {
case bv_justification::kind_t::bv2bit: case bv_justification::kind_t::bv2bit:
SASSERT(s().value(c.m_antecedent) == l_true);
r.push_back(c.m_antecedent); r.push_back(c.m_antecedent);
ctx.add_antecedent(var2enode(c.m_v1), var2enode(c.m_v2)); ctx.add_antecedent(var2enode(c.m_v1), var2enode(c.m_v2));
break; break;
@ -235,9 +264,16 @@ namespace bv {
} }
void solver::log_drat(bv_justification const& c) { void solver::log_drat(bv_justification const& c) {
// this has a side-effect so changes provability: // introduce dummy literal for equality.
expr_ref eq(m.mk_eq(var2expr(c.m_v1), var2expr(c.m_v2)), m); sat::literal leq(s().num_vars() + 1, false);
sat::literal leq = ctx.internalize(eq, false, false, false); expr* e1 = var2expr(c.m_v1);
expr* e2 = var2expr(c.m_v2);
expr_ref eq(m.mk_eq(e1, e2), m);
ctx.get_drat().def_begin('e', eq->get_id(), std::string("="));
ctx.get_drat().def_add_arg(e1->get_id());
ctx.get_drat().def_add_arg(e2->get_id());
ctx.get_drat().def_end();
ctx.get_drat().bool_def(leq.var(), eq->get_id());
sat::literal_vector lits; sat::literal_vector lits;
auto add_bit = [&](sat::literal lit) { auto add_bit = [&](sat::literal lit) {
if (s().value(lit) == l_true) if (s().value(lit) == l_true)
@ -263,19 +299,24 @@ namespace bv {
break; break;
} }
ctx.get_drat().add(lits, status()); ctx.get_drat().add(lits, status());
ctx.get_drat().log_gc_var(leq.var());
} }
void solver::asserted(literal l) { void solver::asserted(literal l) {
atom* a = get_bv2a(l.var()); atom* a = get_bv2a(l.var());
TRACE("bv", tout << "asserted: " << l << "\n";); TRACE("bv", tout << "asserted: " << l << "\n";);
if (a && a->is_bit()) if (a && a->is_bit()) {
force_push();
for (auto vp : a->to_bit()) for (auto vp : a->to_bit())
m_prop_queue.push_back(vp); m_prop_queue.push_back(vp);
}
} }
bool solver::unit_propagate() { bool solver::unit_propagate() {
if (m_prop_queue_head == m_prop_queue.size()) if (m_prop_queue_head == m_prop_queue.size())
return false; return false;
force_push();
ctx.push(value_trail<euf::solver, unsigned>(m_prop_queue_head)); ctx.push(value_trail<euf::solver, unsigned>(m_prop_queue_head));
for (; m_prop_queue_head < m_prop_queue.size() && !s().inconsistent(); ++m_prop_queue_head) for (; m_prop_queue_head < m_prop_queue.size() && !s().inconsistent(); ++m_prop_queue_head)
propagate_bits(m_prop_queue[m_prop_queue_head]); propagate_bits(m_prop_queue[m_prop_queue_head]);
@ -311,26 +352,26 @@ namespace bv {
} }
sat::check_result solver::check() { sat::check_result solver::check() {
force_push();
SASSERT(m_prop_queue.size() == m_prop_queue_head); SASSERT(m_prop_queue.size() == m_prop_queue_head);
return sat::check_result::CR_DONE; return sat::check_result::CR_DONE;
} }
void solver::push() { void solver::push_core() {
th_euf_solver::lazy_push(); th_euf_solver::push_core();
m_prop_queue_lim.push_back(m_prop_queue.size()); m_prop_queue_lim.push_back(m_prop_queue.size());
} }
void solver::pop(unsigned n) { void solver::pop_core(unsigned n) {
SASSERT(m_num_scopes == 0);
unsigned old_sz = m_prop_queue_lim.size() - n; unsigned old_sz = m_prop_queue_lim.size() - n;
m_prop_queue.shrink(m_prop_queue_lim[old_sz]); m_prop_queue.shrink(m_prop_queue_lim[old_sz]);
m_prop_queue_lim.shrink(old_sz); m_prop_queue_lim.shrink(old_sz);
n = lazy_pop(n); th_euf_solver::pop_core(n);
if (n > 0) { old_sz = get_num_vars();
old_sz = get_num_vars(); m_bits.shrink(old_sz);
m_bits.shrink(old_sz); m_wpos.shrink(old_sz);
m_wpos.shrink(old_sz); m_zero_one_bits.shrink(old_sz);
m_zero_one_bits.shrink(old_sz);
}
} }
void solver::pre_simplify() {} void solver::pre_simplify() {}
@ -559,8 +600,7 @@ namespace bv {
SASSERT(s().inconsistent()); SASSERT(s().inconsistent());
} }
else { else {
if (get_config().m_bv_eq_axioms && false) { if (false && get_config().m_bv_eq_axioms) {
// TODO - enable when pop_reinit is available
expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m); expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m);
flet<bool> _is_redundant(m_is_redundant, true); flet<bool> _is_redundant(m_is_redundant, true);
literal eq_lit = ctx.internalize(eq, false, false, m_is_redundant); literal eq_lit = ctx.internalize(eq, false, false, m_is_redundant);

View file

@ -246,8 +246,8 @@ namespace bv {
void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) override; void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) override;
void asserted(literal l) override; void asserted(literal l) override;
sat::check_result check() override; sat::check_result check() override;
void push() override; void push_core() override;
void pop(unsigned n) override; void pop_core(unsigned n) override;
void pre_simplify() override; void pre_simplify() override;
void simplify() override; void simplify() override;
bool set_root(literal l, literal r) override; bool set_root(literal l, literal r) override;
@ -270,6 +270,8 @@ namespace bv {
unsigned max_var(unsigned w) const override; unsigned max_var(unsigned w) const override;
void new_eq_eh(euf::th_eq const& eq) override; void new_eq_eh(euf::th_eq const& eq) override;
void new_diseq_eh(euf::th_eq const& ne) override;
bool use_diseqs() const override { return true; }
bool unit_propagate() override; bool unit_propagate() override;
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override; void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;

View file

@ -54,9 +54,11 @@ namespace euf {
if (is_app(e) && to_app(e)->get_num_args() > 0) { if (is_app(e) && to_app(e)->get_num_args() > 0) {
m_stack.push_back(sat::eframe(e)); m_stack.push_back(sat::eframe(e));
return false; return false;
} }
n = m_egraph.mk(e, 0, nullptr); if (auto* s = expr2solver(e))
attach_node(n); s->internalize(e, m_is_redundant);
else
attach_node(m_egraph.mk(e, 0, nullptr));
return true; return true;
} }
@ -67,12 +69,10 @@ namespace euf {
m_args.push_back(m_egraph.find(to_app(e)->get_arg(i))); m_args.push_back(m_egraph.find(to_app(e)->get_arg(i)));
if (root && internalize_root(to_app(e), sign, m_args)) if (root && internalize_root(to_app(e), sign, m_args))
return false; return false;
if (auto* s = expr2solver(e)) { if (auto* s = expr2solver(e))
s->internalize(e, m_is_redundant); s->internalize(e, m_is_redundant);
return true; else
} attach_node(m_egraph.mk(e, num, m_args.c_ptr()));
enode* n = m_egraph.mk(e, num, m_args.c_ptr());
attach_node(n);
return true; return true;
} }
@ -146,7 +146,7 @@ namespace euf {
sat::literal_vector lits; sat::literal_vector lits;
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) { for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq(m.mk_eq(args[i]->get_expr(), args[j]->get_expr()), m); expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr());
sat::literal lit = internalize(eq, false, false, m_is_redundant); sat::literal lit = internalize(eq, false, false, m_is_redundant);
lits.push_back(lit); lits.push_back(lit);
} }
@ -167,10 +167,10 @@ namespace euf {
for (expr* arg : *e) { for (expr* arg : *e) {
expr_ref fapp(m.mk_app(f, arg), m); expr_ref fapp(m.mk_app(f, arg), m);
expr_ref gapp(m.mk_app(g, fapp.get()), m); expr_ref gapp(m.mk_app(g, fapp.get()), m);
expr_ref eq(m.mk_eq(gapp, arg), m); expr_ref eq = mk_eq(gapp, arg);
sat::literal lit = internalize(eq, false, false, m_is_redundant); sat::literal lit = internalize(eq, false, false, m_is_redundant);
s().add_clause(1, &lit, st); s().add_clause(1, &lit, st);
eqs.push_back(m.mk_eq(fapp, a)); eqs.push_back(mk_eq(fapp, a));
} }
pb_util pb(m); pb_util pb(m);
expr_ref at_least2(pb.mk_at_least_k(eqs.size(), eqs.c_ptr(), 2), m); expr_ref at_least2(pb.mk_at_least_k(eqs.size(), eqs.c_ptr(), 2), m);
@ -191,7 +191,7 @@ namespace euf {
if (sz <= distinct_max_args) { if (sz <= distinct_max_args) {
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) { for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq(m.mk_eq(args[i]->get_expr(), args[j]->get_expr()), m); expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr());
sat::literal lit = internalize(eq, true, false, m_is_redundant); sat::literal lit = internalize(eq, true, false, m_is_redundant);
s().add_clause(1, &lit, st); s().add_clause(1, &lit, st);
} }
@ -208,7 +208,7 @@ namespace euf {
expr_ref fresh(m.mk_fresh_const("dist-value", u), m); expr_ref fresh(m.mk_fresh_const("dist-value", u), m);
enode* n = m_egraph.mk(fresh, 0, nullptr); enode* n = m_egraph.mk(fresh, 0, nullptr);
n->mark_interpreted(); n->mark_interpreted();
expr_ref eq(m.mk_eq(fapp, fresh), m); expr_ref eq = mk_eq(fapp, fresh);
sat::literal lit = internalize(eq, false, false, m_is_redundant); sat::literal lit = internalize(eq, false, false, m_is_redundant);
s().add_clause(1, &lit, st); s().add_clause(1, &lit, st);
} }
@ -221,23 +221,30 @@ namespace euf {
expr* c = nullptr, * th = nullptr, * el = nullptr; expr* c = nullptr, * th = nullptr, * el = nullptr;
if (!m.is_bool(e) && m.is_ite(e, c, th, el)) { if (!m.is_bool(e) && m.is_ite(e, c, th, el)) {
app* a = to_app(e); app* a = to_app(e);
sat::bool_var v = si.to_bool_var(c); expr_ref eq_th = mk_eq(a, th);
SASSERT(v != sat::null_bool_var);
expr_ref eq_th(m.mk_eq(a, th), m);
expr_ref eq_el(m.mk_eq(a, el), m);
sat::literal lit_th = internalize(eq_th, false, false, m_is_redundant); sat::literal lit_th = internalize(eq_th, false, false, m_is_redundant);
sat::literal lit_el = internalize(eq_el, false, false, m_is_redundant); if (th == el) {
literal lits1[2] = { literal(v, true), lit_th }; s().add_clause(1, &lit_th, st);
literal lits2[2] = { literal(v, false), lit_el }; }
s().add_clause(2, lits1, st); else {
s().add_clause(2, lits2, st); sat::bool_var v = si.to_bool_var(c);
SASSERT(v != sat::null_bool_var);
expr_ref eq_el = mk_eq(a, el);
sat::literal lit_el = internalize(eq_el, false, false, m_is_redundant);
literal lits1[2] = { literal(v, true), lit_th };
literal lits2[2] = { literal(v, false), lit_el };
s().add_clause(2, lits1, st);
s().add_clause(2, lits2, st);
}
} }
else if (m.is_distinct(e)) { else if (m.is_distinct(e)) {
expr_ref_vector eqs(m); expr_ref_vector eqs(m);
unsigned sz = n->num_args(); unsigned sz = n->num_args();
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) { for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq(m.mk_eq(n->get_arg(i)->get_expr(), n->get_arg(j)->get_expr()), m); expr_ref eq = mk_eq(n->get_arg(i)->get_expr(), n->get_arg(j)->get_expr());
eqs.push_back(eq); eqs.push_back(eq);
} }
} }
@ -308,4 +315,13 @@ namespace euf {
// TODO // TODO
// return get_theory(th_id)->is_shared(l->get_var()); // return get_theory(th_id)->is_shared(l->get_var());
} }
expr_ref solver::mk_eq(expr* e1, expr* e2) {
if (e1 == e2)
return expr_ref(m.mk_true(), m);
expr_ref r(m.mk_eq(e2, e1), m);
if (!m_egraph.find(r))
r = m.mk_eq(e1, e2);
return r;
}
} }

View file

@ -27,19 +27,19 @@ namespace euf {
void solver::check_eqc_bool_assignment() const { void solver::check_eqc_bool_assignment() const {
for (enode* n : m_egraph.nodes()) { for (enode* n : m_egraph.nodes()) {
VERIFY(!m.is_bool(n->get_expr()) || VERIFY(!m.is_bool(n->get_expr()) ||
s().value(get_literal(n)) == s().value(get_literal(n->get_root()))); s().value(enode2literal(n)) == s().value(enode2literal(n->get_root())));
} }
} }
void solver::check_missing_bool_enode_propagation() const { void solver::check_missing_bool_enode_propagation() const {
for (enode* n : m_egraph.nodes()) for (enode* n : m_egraph.nodes())
if (m.is_bool(n->get_expr()) && l_undef == s().value(get_literal(n))) { if (m.is_bool(n->get_expr()) && l_undef == s().value(enode2literal(n))) {
if (!n->is_root()) { if (!n->is_root()) {
VERIFY(l_undef == s().value(get_literal(n->get_root()))); VERIFY(l_undef == s().value(enode2literal(n->get_root())));
} }
else else
for (enode* o : enode_class(n)) { for (enode* o : enode_class(n)) {
VERIFY(l_undef == s().value(get_literal(o))); VERIFY(l_undef == s().value(enode2literal(o)));
} }
} }
} }

View file

@ -42,8 +42,15 @@ namespace euf {
updt_params(p); updt_params(p);
std::function<void(std::ostream&, void*)> disp = std::function<void(std::ostream&, void*)> disp =
[&](std::ostream& out, void* j) { display_justification_ptr(out, reinterpret_cast<size_t*>(j)); }; [&](std::ostream& out, void* j) {
display_justification_ptr(out, reinterpret_cast<size_t*>(j));
};
std::function<lbool(enode* n)> eval = [&](enode* n) {
sat::literal lit = expr2literal(n->get_expr());
return (lit == sat::null_literal) ? l_undef : s().value(lit);
};
m_egraph.set_display_justification(disp); m_egraph.set_display_justification(disp);
m_egraph.set_eval(eval);
} }
void solver::updt_params(params_ref const& p) { void solver::updt_params(params_ref const& p) {
@ -140,8 +147,8 @@ namespace euf {
ext->get_antecedents(l, idx, r, probing); ext->get_antecedents(l, idx, r, probing);
for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) { for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) {
size_t* e = m_explain[qhead]; size_t* e = m_explain[qhead];
if (is_literal(e)) if (is_literal(e))
r.push_back(get_literal(e)); r.push_back(get_literal(e));
else { else {
size_t idx = get_justification(e); size_t idx = get_justification(e);
auto* ext = sat::constraint_base::to_extension(idx); auto* ext = sat::constraint_base::to_extension(idx);
@ -150,9 +157,14 @@ namespace euf {
ext->get_antecedents(lit, idx, r, probing); ext->get_antecedents(lit, idx, r, probing);
} }
} }
m_egraph.end_explain(); m_egraph.end_explain();
unsigned j = 0;
for (sat::literal lit : r)
if (s().lvl(lit) > 0) r[j++] = lit;
r.shrink(j);
TRACE("euf", tout << "eplain " << l << " <- " << r << " " << probing << "\n";); TRACE("euf", tout << "eplain " << l << " <- " << r << " " << probing << "\n";);
DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true);); DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true););
if (!probing) if (!probing)
log_antecedents(l, r); log_antecedents(l, r);
} }
@ -204,13 +216,13 @@ namespace euf {
void solver::asserted(literal l) { void solver::asserted(literal l) {
expr* e = m_var2expr.get(l.var(), nullptr); expr* e = m_var2expr.get(l.var(), nullptr);
if (!e) { if (!e)
return; return;
}
bool sign = l.sign(); bool sign = l.sign();
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << "\n";);
euf::enode* n = m_egraph.find(e); euf::enode* n = m_egraph.find(e);
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << "\n";);
if (!n) if (!n)
return; return;
for (auto th : enode_th_vars(n)) for (auto th : enode_th_vars(n))
@ -220,32 +232,17 @@ namespace euf {
size_t* c = to_ptr(l); size_t* c = to_ptr(l);
SASSERT(is_literal(c)); SASSERT(is_literal(c));
SASSERT(l == get_literal(c)); SASSERT(l == get_literal(c));
if (m.is_eq(e) && n->num_args() == 2) { if (m.is_eq(e) && n->num_args() == 2 && !sign) {
euf::enode* na = n->get_arg(0); euf::enode* na = n->get_arg(0);
euf::enode* nb = n->get_arg(1); euf::enode* nb = n->get_arg(1);
if (!sign) { m_egraph.merge(na, nb, c);
m_egraph.merge(na, nb, c); }
return; else {
} euf::enode* nb = sign ? mk_false() : mk_true();
else m_egraph.merge(n, nb, c);
new_diseq(na, nb, l);
} }
euf::enode* nb = sign ? mk_false() : mk_true();
m_egraph.merge(n, nb, c);
} }
void solver::new_diseq(enode* n1, enode* n2, literal lit) {
enode * r1 = n1->get_root();
enode * r2 = n2->get_root();
if (r1 == r2)
return;
if (r1->has_one_th_var() && r2->has_one_th_var() && r1->get_first_th_id() == r2->get_first_th_id()) {
theory_id id = r1->get_first_th_id();
theory_var v1 = r1->get_th_var(id);
theory_var v2 = r2->get_th_var(id);
fid2solver(id)->new_diseq_eh(r1, r2);
}
}
bool solver::unit_propagate() { bool solver::unit_propagate() {
bool propagated = false; bool propagated = false;
@ -284,7 +281,7 @@ namespace euf {
bool_var v = si.to_bool_var(e); bool_var v = si.to_bool_var(e);
SASSERT(m.is_bool(e)); SASSERT(m.is_bool(e));
size_t cnstr; size_t cnstr;
literal lit; literal lit;
if (is_eq) { if (is_eq) {
VERIFY(m.is_eq(e, a, b)); VERIFY(m.is_eq(e, a, b));
cnstr = eq_constraint().to_index(); cnstr = eq_constraint().to_index();
@ -315,7 +312,10 @@ namespace euf {
void solver::propagate_th_eqs() { void solver::propagate_th_eqs() {
for (; m_egraph.has_th_eq() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_th_eq()) { for (; m_egraph.has_th_eq() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_th_eq()) {
th_eq eq = m_egraph.get_th_eq(); th_eq eq = m_egraph.get_th_eq();
m_id2solver[eq.m_id]->new_eq_eh(eq); if (eq.is_eq())
m_id2solver[eq.id()]->new_eq_eh(eq);
else
m_id2solver[eq.id()]->new_diseq_eh(eq);
} }
} }
@ -379,6 +379,7 @@ namespace euf {
m_scopes.shrink(m_scopes.size() - n); m_scopes.shrink(m_scopes.size() - n);
si.pop(n); si.pop(n);
SASSERT(m_egraph.num_scopes() == m_scopes.size()); SASSERT(m_egraph.num_scopes() == m_scopes.size());
TRACE("euf", tout << "pop to: " << m_scopes.size() << "\n";);
} }
void solver::start_reinit(unsigned n) { void solver::start_reinit(unsigned n) {
@ -405,9 +406,19 @@ namespace euf {
if (expr2var_replay.empty()) if (expr2var_replay.empty())
return; return;
si.set_expr2var_replay(&expr2var_replay); si.set_expr2var_replay(&expr2var_replay);
for (auto const& kv : expr2var_replay) TRACE("euf", for (auto const& kv : expr2var_replay) tout << "replay: " << kv.m_value << " " << mk_bounded_pp(kv.m_key, m) << "\n";);
attach_lit(si.internalize(kv.m_key, true), kv.m_key); for (auto const& kv : expr2var_replay) {
si.set_expr2var_replay(nullptr); sat::literal lit;
expr* e = kv.m_key;
if (si.is_bool_op(e))
lit = literal(expr2var_replay[e], false);
else
lit = si.internalize(kv.m_key, true);
VERIFY(lit.var() == kv.m_value);
attach_lit(lit, kv.m_key);
}
si.set_expr2var_replay(nullptr);
TRACE("euf", tout << "replay done\n";);
} }
void solver::pre_simplify() { void solver::pre_simplify() {

View file

@ -49,8 +49,6 @@ namespace euf {
size_t to_index() const { return sat::constraint_base::mem2base(this); } size_t to_index() const { return sat::constraint_base::mem2base(this); }
}; };
class solver : public sat::extension, public th_internalizer, public th_decompile { class solver : public sat::extension, public th_internalizer, public th_decompile {
typedef top_sort<euf::enode> deps_t; typedef top_sort<euf::enode> deps_t;
friend class ackerman; friend class ackerman;
@ -194,10 +192,11 @@ namespace euf {
sat::sat_internalizer& get_si() { return si; } sat::sat_internalizer& get_si() { return si; }
ast_manager& get_manager() { return m; } ast_manager& get_manager() { return m; }
enode* get_enode(expr* e) { return m_egraph.find(e); } enode* get_enode(expr* e) { return m_egraph.find(e); }
sat::literal get_literal(expr* e) const { return literal(si.to_bool_var(e), false); } sat::literal expr2literal(expr* e) const { return literal(si.to_bool_var(e), false); }
sat::literal get_literal(enode* e) const { return get_literal(e->get_expr()); } sat::literal enode2literal(enode* e) const { return expr2literal(e->get_expr()); }
smt_params const& get_config() { return m_config; } smt_params const& get_config() { return m_config; }
region& get_region() { return m_trail.get_region(); } region& get_region() { return m_trail.get_region(); }
egraph& get_egraph() { return m_egraph; }
template <typename C> template <typename C>
void push(C const& c) { m_trail.push(c); } void push(C const& c) { m_trail.push(c); }
euf_trail_stack& get_trail_stack() { return m_trail; } euf_trail_stack& get_trail_stack() { return m_trail; }
@ -254,6 +253,8 @@ namespace euf {
void internalize(expr* e, bool learned) override; void internalize(expr* e, bool learned) override;
void attach_th_var(enode* n, th_solver* th, theory_var v) { m_egraph.add_th_var(n, v, th->get_id()); } void attach_th_var(enode* n, th_solver* th, theory_var v) { m_egraph.add_th_var(n, v, th->get_id()); }
void attach_node(euf::enode* n); void attach_node(euf::enode* n);
expr_ref mk_eq(expr* e1, expr* e2);
expr_ref mk_eq(euf::enode* n1, euf::enode* n2) { return mk_eq(n1->get_expr(), n2->get_expr()); }
euf::enode* mk_enode(expr* e, unsigned n, enode* const* args) { return m_egraph.mk(e, n, args); } euf::enode* mk_enode(expr* e, unsigned n, enode* const* args) { return m_egraph.mk(e, n, args); }
expr* bool_var2expr(sat::bool_var v) { return m_var2expr.get(v, nullptr); } expr* bool_var2expr(sat::bool_var v) { return m_var2expr.get(v, nullptr); }
void unhandled_function(func_decl* f); void unhandled_function(func_decl* f);

View file

@ -74,7 +74,7 @@ namespace euf {
} }
sat::literal th_euf_solver::expr2literal(expr* e) const { sat::literal th_euf_solver::expr2literal(expr* e) const {
return ctx.get_literal(e); return ctx.expr2literal(e);
} }
expr* th_euf_solver::bool_var2expr(sat::bool_var v) const { expr* th_euf_solver::bool_var2expr(sat::bool_var v) const {
@ -98,26 +98,23 @@ namespace euf {
return get_th_var(ctx.get_enode(e)); return get_th_var(ctx.get_enode(e));
} }
void th_euf_solver::push() { void th_euf_solver::push_core() {
TRACE("euf", tout << "push-core\n";);
m_var2enode_lim.push_back(m_var2enode.size()); m_var2enode_lim.push_back(m_var2enode.size());
} }
void th_euf_solver::pop(unsigned num_scopes) { void th_euf_solver::pop_core(unsigned num_scopes) {
unsigned new_lvl = m_var2enode_lim.size() - num_scopes; unsigned new_lvl = m_var2enode_lim.size() - num_scopes;
m_var2enode.shrink(m_var2enode_lim[new_lvl]); m_var2enode.shrink(m_var2enode_lim[new_lvl]);
m_var2enode_lim.shrink(new_lvl); m_var2enode_lim.shrink(new_lvl);
} }
unsigned th_euf_solver::lazy_pop(unsigned n) { void th_euf_solver::pop(unsigned n) {
if (n <= m_num_scopes) { unsigned k = std::min(m_num_scopes, n);
m_num_scopes -= n; m_num_scopes -= k;
return 0; n -= k;
} if (n > 0)
else { pop_core(n);
n -= m_num_scopes;
pop(n);
return n;
}
} }
bool th_euf_solver::add_unit(sat::literal lit) { bool th_euf_solver::add_unit(sat::literal lit) {
@ -154,4 +151,9 @@ namespace euf {
void th_euf_solver::rewrite(expr_ref& a) { void th_euf_solver::rewrite(expr_ref& a) {
ctx.get_rewriter()(a); ctx.get_rewriter()(a);
} }
expr_ref th_euf_solver::mk_eq(expr* e1, expr* e2) {
return ctx.mk_eq(e1, e2);
}
} }

View file

@ -95,13 +95,15 @@ namespace euf {
virtual bool use_diseqs() const { return false; } virtual bool use_diseqs() const { return false; }
virtual void new_diseq_eh(euf::enode* a, euf::enode* b) {} virtual void new_diseq_eh(euf::th_eq const& eq) {}
/** /**
\brief Parametric theories (e.g. Arrays) should implement this method. \brief Parametric theories (e.g. Arrays) should implement this method.
*/ */
virtual bool is_shared(theory_var v) const { return false; } virtual bool is_shared(theory_var v) const { return false; }
}; };
class th_euf_solver : public th_solver { class th_euf_solver : public th_solver {
@ -129,9 +131,15 @@ namespace euf {
euf::enode* e_internalize(expr* e) { internalize(e, m_is_redundant); return expr2enode(e); } euf::enode* e_internalize(expr* e) { internalize(e, m_is_redundant); return expr2enode(e); }
euf::enode* mk_enode(expr* e, bool suppress_args); euf::enode* mk_enode(expr* e, bool suppress_args);
expr_ref mk_eq(expr* e1, expr* e2);
expr_ref mk_var_eq(theory_var v1, theory_var v2) { return mk_eq(var2expr(v1), var2expr(v2)); }
void rewrite(expr_ref& a); void rewrite(expr_ref& a);
virtual void push_core();
virtual void pop_core(unsigned n);
void force_push() { for (; m_num_scopes > 0; --m_num_scopes) push_core(); }
public: public:
th_euf_solver(euf::solver& ctx, euf::theory_id id); th_euf_solver(euf::solver& ctx, euf::theory_id id);
virtual ~th_euf_solver() {} virtual ~th_euf_solver() {}
@ -147,12 +155,9 @@ namespace euf {
trail_stack<euf::solver> & get_trail_stack(); trail_stack<euf::solver> & get_trail_stack();
bool is_attached_to_var(enode* n) const; bool is_attached_to_var(enode* n) const;
bool is_root(theory_var v) const { return var2enode(v)->is_root(); } bool is_root(theory_var v) const { return var2enode(v)->is_root(); }
void push() override; void push() override { m_num_scopes++; }
void pop(unsigned n) override; void pop(unsigned n) override;
void lazy_push() { ++m_num_scopes; }
void force_push() { for (; m_num_scopes > 0; --m_num_scopes) push(); }
unsigned lazy_pop(unsigned n);
}; };

View file

@ -155,7 +155,10 @@ struct goal2sat::imp : public sat::sat_internalizer {
} }
sat::bool_var add_var(bool is_ext, expr* n) { sat::bool_var add_var(bool is_ext, expr* n) {
auto v = m_solver.add_var(is_ext); sat::bool_var v;
if (m_expr2var_replay && m_expr2var_replay->find(n, v))
return v;
v = m_solver.add_var(is_ext);
log_node(n); log_node(n);
log_def(v, n); log_def(v, n);
return v; return v;
@ -298,18 +301,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
} }
} }
bool process_cached(app * t, bool root, bool sign) { bool process_cached(app* t, bool root, bool sign) {
sat::literal l; sat::literal l = sat::null_literal;
if (m_cache.find(t, l)) { if (!m_cache.find(t, l))
if (sign) return false;
l.neg(); if (sign)
if (root) l.neg();
mk_root_clause(l); if (root)
else mk_root_clause(l);
m_result_stack.push_back(l); else
return true; m_result_stack.push_back(l);
} return true;
return false;
} }
bool visit(expr * t, bool root, bool sign) { bool visit(expr * t, bool root, bool sign) {
@ -321,7 +323,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (process_cached(to_app(t), root, sign)) if (process_cached(to_app(t), root, sign))
return true; return true;
if (to_app(t)->get_family_id() != m.get_basic_family_id()) if (to_app(t)->get_family_id() != m.get_basic_family_id())
return convert_app(to_app(t), root, sign); return convert_app(to_app(t), root, sign);
switch (to_app(t)->get_decl_kind()) { switch (to_app(t)->get_decl_kind()) {
case OP_NOT: case OP_NOT:
case OP_OR: case OP_OR:

View file

@ -128,7 +128,7 @@ class smt_checker {
} }
m_lemma_solver->pop(1); m_lemma_solver->pop(1);
std::cout << "smt\n"; std::cout << "smt\n";
check_assertion_redundant(lits); // check_assertion_redundant(lits);
} }
public: public:
@ -205,9 +205,6 @@ public:
case sexpr::kind_t::BV_NUMERAL: { case sexpr::kind_t::BV_NUMERAL: {
std::cout << "bv numeral\n"; std::cout << "bv numeral\n";
goto bail; goto bail;
unsigned sz = sexpr->get_bv_size();
rational r = sexpr->get_numeral();
break;
} }
case sexpr::kind_t::STRING: case sexpr::kind_t::STRING:
case sexpr::kind_t::KEYWORD: case sexpr::kind_t::KEYWORD:
@ -314,6 +311,9 @@ static void verify_smt(char const* drat_file, char const* smt_file) {
bool_var2expr.reserve(r.m_node_id+1); bool_var2expr.reserve(r.m_node_id+1);
bool_var2expr.set(r.m_node_id, exprs.get(r.m_args[0])); bool_var2expr.set(r.m_node_id, exprs.get(r.m_args[0]));
break; break;
case dimacs::drat_record::tag_t::is_var_gc:
drat_checker.gc_var(r.m_node_id);
break;
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;

View file

@ -2704,84 +2704,6 @@ namespace smt {
SASSERT(check_clauses(m_lemmas) && check_clauses(m_aux_clauses)); SASSERT(check_clauses(m_lemmas) && check_clauses(m_aux_clauses));
} }
void context::log_stats() {
size_t bin_clauses = 0, bin_lemmas = 0;
for (watch_list const& w : m_watches) {
bin_clauses += w.end_literals() - w.begin_literals();
}
bin_clauses /= 2;
for (clause* cp : m_lemmas)
if (cp->get_num_literals() == 2)
++bin_lemmas;
std::stringstream strm;
strm << "(smt.stats "
<< std::setw(4) << m_stats.m_num_restarts << " "
<< std::setw(6) << m_stats.m_num_conflicts << " "
<< std::setw(6) << m_stats.m_num_decisions << " "
<< std::setw(6) << m_stats.m_num_propagations << " "
<< std::setw(5) << (m_aux_clauses.size() + bin_clauses) << "/" << bin_clauses << " "
<< std::setw(5) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " ";
strm << std::setw(5) << m_stats.m_num_simplifications << " "
<< std::setw(4) << m_stats.m_num_del_clauses << " "
<< std::setw(7) << mem_stat() << ")\n";
std::string str(strm.str());
svector<size_t> offsets;
for (size_t i = 0; i < str.size(); ++i) {
while (i < str.size() && str[i] != ' ') ++i;
while (i < str.size() && str[i] == ' ') ++i;
// position of first character after space
if (i < str.size()) {
offsets.push_back(i);
}
}
bool same = m_last_positions.size() == offsets.size();
size_t diff = 0;
for (unsigned i = 0; i < offsets.size() && same; ++i) {
if (m_last_positions[i] > offsets[i]) diff += m_last_positions[i] - offsets[i];
if (m_last_positions[i] < offsets[i]) diff += offsets[i] - m_last_positions[i];
}
if (m_last_positions.empty() ||
m_stats.m_num_restarts >= 20 + m_last_position_log ||
(m_stats.m_num_restarts >= 6 + m_last_position_log && (!same || diff > 3))) {
m_last_position_log = m_stats.m_num_restarts;
// restarts decisions clauses simplifications memory
// conflicts propagations lemmas deletions
int adjust[9] = { -3, -3, -3, -3, -3, -3, -4, -4, -1 };
char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin ", ":lemmas ", ":simplify ", ":deletions", ":memory" };
std::stringstream l1, l2;
l1 << "(smt.stats ";
l2 << "(smt.stats ";
size_t p1 = 11, p2 = 11;
SASSERT(offsets.size() == 9);
for (unsigned i = 0; i < offsets.size(); ++i) {
size_t p = offsets[i];
if (i & 0x1) {
// odd positions
for (; p2 < p + adjust[i]; ++p2) l2 << " ";
p2 += strlen(tag[i]);
l2 << tag[i];
}
else {
// even positions
for (; p1 < p + adjust[i]; ++p1) l1 << " ";
p1 += strlen(tag[i]);
l1 << tag[i];
}
}
for (; p1 + 2 < str.size(); ++p1) l1 << " ";
for (; p2 + 2 < str.size(); ++p2) l2 << " ";
l1 << ")\n";
l2 << ")\n";
IF_VERBOSE(1, verbose_stream() << l1.str() << l2.str());
m_last_positions.reset();
m_last_positions.append(offsets);
}
IF_VERBOSE(1, verbose_stream() << str);
}
struct clause_lt { struct clause_lt {
bool operator()(clause * cls1, clause * cls2) const { return cls1->get_activity() > cls2->get_activity(); } bool operator()(clause * cls1, clause * cls2) const { return cls1->get_activity() > cls2->get_activity(); }
}; };

View file

@ -661,7 +661,83 @@ namespace smt {
return out << enode_pp(p.p.first, p.ctx) << " = " << enode_pp(p.p.second, p.ctx) << "\n"; return out << enode_pp(p.p.first, p.ctx) << " = " << enode_pp(p.p.second, p.ctx) << "\n";
} }
void context::log_stats() {
size_t bin_clauses = 0, bin_lemmas = 0;
for (watch_list const& w : m_watches) {
bin_clauses += w.end_literals() - w.begin_literals();
}
bin_clauses /= 2;
for (clause* cp : m_lemmas)
if (cp->get_num_literals() == 2)
++bin_lemmas;
std::stringstream strm;
strm << "(smt.stats "
<< std::setw(4) << m_stats.m_num_restarts << " "
<< std::setw(6) << m_stats.m_num_conflicts << " "
<< std::setw(6) << m_stats.m_num_decisions << " "
<< std::setw(6) << m_stats.m_num_propagations << " "
<< std::setw(5) << (m_aux_clauses.size() + bin_clauses) << "/" << bin_clauses << " "
<< std::setw(5) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " ";
strm << std::setw(5) << m_stats.m_num_simplifications << " "
<< std::setw(4) << m_stats.m_num_del_clauses << " "
<< std::setw(7) << mem_stat() << ")\n";
std::string str(strm.str());
svector<size_t> offsets;
for (size_t i = 0; i < str.size(); ++i) {
while (i < str.size() && str[i] != ' ') ++i;
while (i < str.size() && str[i] == ' ') ++i;
// position of first character after space
if (i < str.size()) {
offsets.push_back(i);
}
}
bool same = m_last_positions.size() == offsets.size();
size_t diff = 0;
for (unsigned i = 0; i < offsets.size() && same; ++i) {
if (m_last_positions[i] > offsets[i]) diff += m_last_positions[i] - offsets[i];
if (m_last_positions[i] < offsets[i]) diff += offsets[i] - m_last_positions[i];
}
if (m_last_positions.empty() ||
m_stats.m_num_restarts >= 20 + m_last_position_log ||
(m_stats.m_num_restarts >= 6 + m_last_position_log && (!same || diff > 3))) {
m_last_position_log = m_stats.m_num_restarts;
// restarts decisions clauses simplifications memory
// conflicts propagations lemmas deletions
int adjust[9] = { -3, -3, -3, -3, -3, -3, -4, -4, -1 };
char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin ", ":lemmas ", ":simplify ", ":deletions", ":memory" };
std::stringstream l1, l2;
l1 << "(smt.stats ";
l2 << "(smt.stats ";
size_t p1 = 11, p2 = 11;
SASSERT(offsets.size() == 9);
for (unsigned i = 0; i < offsets.size(); ++i) {
size_t p = offsets[i];
if (i & 0x1) {
// odd positions
for (; p2 < p + adjust[i]; ++p2) l2 << " ";
p2 += strlen(tag[i]);
l2 << tag[i];
}
else {
// even positions
for (; p1 < p + adjust[i]; ++p1) l1 << " ";
p1 += strlen(tag[i]);
l1 << tag[i];
}
}
for (; p1 + 2 < str.size(); ++p1) l1 << " ";
for (; p2 + 2 < str.size(); ++p2) l2 << " ";
l1 << ")\n";
l2 << ")\n";
IF_VERBOSE(1, verbose_stream() << l1.str() << l2.str());
m_last_positions.reset();
m_last_positions.append(offsets);
}
IF_VERBOSE(1, verbose_stream() << str);
}
}; };

View file

@ -152,8 +152,8 @@ class bv1_blaster_tactic : public tactic {
SASSERT(t_bits.size() == e_bits.size()); SASSERT(t_bits.size() == e_bits.size());
bit_buffer new_ites; bit_buffer new_ites;
unsigned num = t_bits.size(); unsigned num = t_bits.size();
for (unsigned i = 0; i < num; i++) for (unsigned i = 0; i < num; i++)
new_ites.push_back(m().mk_ite(c, t_bits[i], e_bits[i])); new_ites.push_back(t_bits[i] == e_bits[i] ? t_bits[i] : m().mk_ite(c, t_bits[i], e_bits[i]));
result = butil().mk_concat(new_ites.size(), new_ites.c_ptr()); result = butil().mk_concat(new_ites.size(), new_ites.c_ptr());
} }

View file

@ -81,7 +81,7 @@ static void test2() {
g.propagate(); g.propagate();
std::cout << "propagated " << t.get_seconds() << "\n"; std::cout << "propagated " << t.get_seconds() << "\n";
for (euf::enode* n : top_nodes) { for (euf::enode* n : top_nodes) {
SASSERT(n->get_root() == top_nodes[0]->get_root()); VERIFY(n->get_root() == top_nodes[0]->get_root());
} }
} }