mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
delay internalize (#4714)
* adding array solver Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use default in model construction Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * debug delay internalization Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * bv Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * get rid of implied values and bounds Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * redo egraph * remove out Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove files Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
25724401cf
commit
367e5fdd52
60 changed files with 1343 additions and 924 deletions
|
@ -811,6 +811,14 @@ bool bv_recognizers::is_zero(expr const * n) const {
|
|||
return decl->get_parameter(0).get_rational().is_zero();
|
||||
}
|
||||
|
||||
bool bv_recognizers::is_one(expr const* n) const {
|
||||
if (!is_app_of(n, get_fid(), OP_BV_NUM)) {
|
||||
return false;
|
||||
}
|
||||
func_decl* decl = to_app(n)->get_decl();
|
||||
return decl->get_parameter(0).get_rational().is_one();
|
||||
}
|
||||
|
||||
bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, expr*& b) const {
|
||||
if (!is_extract(e)) return false;
|
||||
low = get_extract_low(e);
|
||||
|
|
|
@ -298,6 +298,7 @@ public:
|
|||
bool is_numeral(expr const * n) const { return is_app_of(n, get_fid(), OP_BV_NUM); }
|
||||
bool is_allone(expr const * e) const;
|
||||
bool is_zero(expr const * e) const;
|
||||
bool is_one(expr const* e) const;
|
||||
bool is_bv_sort(sort const * s) const;
|
||||
bool is_bv(expr const* e) const { return is_bv_sort(get_sort(e)); }
|
||||
|
||||
|
@ -349,6 +350,7 @@ public:
|
|||
bool is_bv_lshr(expr const * e) const { return is_app_of(e, get_fid(), OP_BLSHR); }
|
||||
bool is_bv_shl(expr const * e) const { return is_app_of(e, get_fid(), OP_BSHL); }
|
||||
bool is_sign_ext(expr const * e) const { return is_app_of(e, get_fid(), OP_SIGN_EXT); }
|
||||
bool is_bv_umul_no_ovfl(expr const* e) const { return is_app_of(e, get_fid(), OP_BUMUL_NO_OVFL); }
|
||||
|
||||
MATCH_BINARY(is_bv_add);
|
||||
MATCH_BINARY(is_bv_mul);
|
||||
|
@ -407,10 +409,16 @@ public:
|
|||
return m_manager.mk_app(get_fid(), OP_EXTRACT, 2, params, 1, &n);
|
||||
}
|
||||
app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); }
|
||||
app * mk_concat(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_concat(2, args); }
|
||||
app * mk_bv_or(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BOR, num, args); }
|
||||
app * mk_bv_not(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNOT, arg); }
|
||||
app * mk_bv_and(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BAND, num, args); }
|
||||
app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); }
|
||||
|
||||
app * mk_concat(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_concat(2, args); }
|
||||
app * mk_bv_and(expr* x, expr* y) { expr* args[2] = { x, y }; return mk_bv_and(2, args); }
|
||||
app * mk_bv_or(expr* x, expr* y) { expr* args[2] = { x, y }; return mk_bv_or(2, args); }
|
||||
app * mk_bv_xor(expr* x, expr* y) { expr* args[2] = { x, y }; return mk_bv_xor(2, args); }
|
||||
|
||||
app * mk_bv_not(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNOT, arg); }
|
||||
app * mk_bv_neg(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNEG, arg); }
|
||||
app * mk_bv_urem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUREM, arg1, arg2); }
|
||||
app * mk_bv_srem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); }
|
||||
|
@ -418,6 +426,18 @@ public:
|
|||
app * mk_bv_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); }
|
||||
app * mk_bv_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); }
|
||||
app * mk_bv_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); }
|
||||
app * mk_bv_udiv(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV, arg1, arg2); }
|
||||
app * mk_bv_udiv_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); }
|
||||
app * mk_bv_udiv0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BUDIV0, arg); }
|
||||
app * mk_bv_sdiv(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSDIV, arg1, arg2); }
|
||||
app * mk_bv_sdiv_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSDIV_I, arg1, arg2); }
|
||||
app * mk_bv_sdiv0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BSDIV0, arg); }
|
||||
app * mk_bv_srem_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSREM_I, arg1, arg2); }
|
||||
app * mk_bv_srem0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BSREM0, arg); }
|
||||
app * mk_bv_urem_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUREM_I, arg1, arg2); }
|
||||
app * mk_bv_urem0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BUREM0, arg); }
|
||||
app * mk_bv_smod_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSMOD_I, arg1, arg2); }
|
||||
app * mk_bv_smod0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BSMOD0, arg); }
|
||||
app * mk_zero_extend(unsigned n, expr* e) {
|
||||
parameter p(n);
|
||||
return m_manager.mk_app(get_fid(), OP_ZERO_EXT, 1, &p, 1, &e);
|
||||
|
|
|
@ -13,35 +13,69 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2020-08-23
|
||||
|
||||
Notes:
|
||||
|
||||
Each node has a congruence closure root, cg.
|
||||
cg is set to the representative in the cc table
|
||||
(first insertion of congruent node).
|
||||
Each node n has a set of parents, denoted n.P.
|
||||
|
||||
set r2 to the root of r1:
|
||||
|
||||
Merge: Erase:
|
||||
for each p r1.P such that p.cg == p:
|
||||
erase from table
|
||||
Update root:
|
||||
r1.root := r2
|
||||
Insert:
|
||||
for each p in r1.P:
|
||||
p.cg = insert p in table
|
||||
if p.cg == p:
|
||||
append p to r2.P
|
||||
else
|
||||
add p.cg, p to worklist
|
||||
|
||||
Unmerge: Erase:
|
||||
for each p in added nodes:
|
||||
erase p from table
|
||||
Revert root:
|
||||
r1.root := r1
|
||||
Insert:
|
||||
for each p in r1.P:
|
||||
insert p if n was cc root before merge
|
||||
|
||||
condition for being cc root before merge:
|
||||
p->cg == p
|
||||
congruent(p, p->cg)
|
||||
|
||||
congruent(p,q) := roots of p.children = roots of q.children
|
||||
|
||||
Example:
|
||||
|
||||
Initially:
|
||||
n1 := f(a,b) has root n1
|
||||
n2 := f(a',b) has root n2
|
||||
table = [f(a,b) |-> n1, f(a',b) |-> n2]
|
||||
|
||||
merge(a,a') (a' becomes root)
|
||||
table = [f(a',b) |-> n2]
|
||||
n1.cg = n2
|
||||
a'.P = [n2]
|
||||
n1 is not added as parent because it is not a cc root after the assignment a.root := a'
|
||||
|
||||
unmerge(a,a')
|
||||
- nothing is erased
|
||||
- n1 is reinserted. It used to be a root.
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/euf/euf_egraph.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "ast/ast_translation.h"
|
||||
|
||||
namespace euf {
|
||||
|
||||
|
||||
void egraph::undo_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
|
||||
enode* r2 = r1->get_root();
|
||||
r2->dec_class_size(r1->class_size());
|
||||
std::swap(r1->m_next, r2->m_next);
|
||||
auto begin = r2->begin_parents() + r2_num_parents, end = r2->end_parents();
|
||||
// DEBUG_CODE(for (auto it = begin; it != end; ++it) VERIFY(((*it)->merge_enabled()) == m_table.contains(*it)););
|
||||
for (auto it = begin; it != end; ++it)
|
||||
if ((*it)->merge_enabled())
|
||||
m_table.erase(*it);
|
||||
for (enode* c : enode_class(r1))
|
||||
c->m_root = r1;
|
||||
for (auto it = begin; it != end; ++it)
|
||||
if ((*it)->merge_enabled())
|
||||
m_table.insert(*it);
|
||||
|
||||
r2->m_parents.shrink(r2_num_parents);
|
||||
unmerge_justification(n1);
|
||||
}
|
||||
|
||||
enode* egraph::mk_enode(expr* f, unsigned num_args, enode * const* args) {
|
||||
enode* n = enode::mk(m_region, f, num_args, args);
|
||||
m_nodes.push_back(n);
|
||||
|
@ -53,13 +87,11 @@ namespace euf {
|
|||
return n;
|
||||
}
|
||||
|
||||
void egraph::reinsert(enode* p) {
|
||||
if (p->merge_enabled()) {
|
||||
auto rc = m_table.insert(p);
|
||||
merge(rc.first, p, justification::congruence(rc.second));
|
||||
}
|
||||
else if (p->is_equality())
|
||||
reinsert_equality(p);
|
||||
enode_bool_pair egraph::insert_table(enode* p) {
|
||||
auto rc = m_table.insert(p);
|
||||
enode* p_other = rc.first;
|
||||
p->m_cg = rc.first;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void egraph::reinsert_equality(enode* p) {
|
||||
|
@ -72,6 +104,7 @@ namespace euf {
|
|||
void egraph::force_push() {
|
||||
if (m_num_scopes == 0)
|
||||
return;
|
||||
// DEBUG_CODE(invariant(););
|
||||
for (; m_num_scopes > 0; --m_num_scopes) {
|
||||
m_scopes.push_back(m_updates.size());
|
||||
m_region.push_scope();
|
||||
|
@ -103,7 +136,7 @@ namespace euf {
|
|||
reinsert_equality(n);
|
||||
return n;
|
||||
}
|
||||
enode_bool_pair p = m_table.insert(n);
|
||||
enode_bool_pair p = insert_table(n);
|
||||
enode* n2 = p.first;
|
||||
if (n2 == n)
|
||||
update_children(n);
|
||||
|
@ -151,7 +184,7 @@ namespace euf {
|
|||
enode* arg1 = n->get_arg(0), * arg2 = n->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";);
|
||||
TRACE("euf", tout << "new-diseq: " << bpp(r1) << " " << bpp(r2) << ": " << r1->has_th_vars() << " " << r2->has_th_vars() << "\n";);
|
||||
if (r1 == r2) {
|
||||
add_literal(n, true);
|
||||
return;
|
||||
|
@ -208,8 +241,6 @@ namespace euf {
|
|||
return m_th_propagates_diseqs.get(id, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void egraph::add_th_var(enode* n, theory_var v, theory_id id) {
|
||||
force_push();
|
||||
theory_var w = n->get_th_var(id);
|
||||
|
@ -267,6 +298,7 @@ namespace euf {
|
|||
}
|
||||
num_scopes -= m_num_scopes;
|
||||
m_num_scopes = 0;
|
||||
|
||||
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
unsigned old_lim = m_scopes.size() - num_scopes;
|
||||
|
@ -274,8 +306,9 @@ namespace euf {
|
|||
auto undo_node = [&]() {
|
||||
enode* n = m_nodes.back();
|
||||
expr* e = m_exprs.back();
|
||||
if (n->num_args() > 0)
|
||||
if (n->num_args() > 0 && n->is_cgr())
|
||||
m_table.erase(n);
|
||||
|
||||
m_expr2enode[e->get_id()] = nullptr;
|
||||
n->~enode();
|
||||
m_nodes.pop_back();
|
||||
|
@ -328,21 +361,24 @@ namespace euf {
|
|||
m_updates.shrink(num_updates);
|
||||
m_scopes.shrink(old_lim);
|
||||
m_region.pop_scope(num_scopes);
|
||||
m_worklist.reset();
|
||||
m_to_merge.reset();
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
|
||||
// DEBUG_CODE(invariant(););
|
||||
}
|
||||
|
||||
void egraph::merge(enode* n1, enode* n2, justification j) {
|
||||
if (!n1->merge_enabled() && !n2->merge_enabled())
|
||||
return;
|
||||
|
||||
if (!n1->merge_enabled() && !n2->merge_enabled())
|
||||
return;
|
||||
SASSERT(m.get_sort(n1->get_expr()) == m.get_sort(n2->get_expr()));
|
||||
enode* r1 = n1->get_root();
|
||||
enode* r2 = n2->get_root();
|
||||
if (r1 == r2)
|
||||
return;
|
||||
TRACE("euf", j.display(tout << "merge: " << mk_bounded_pp(n1->get_expr(), m) << " == " << mk_bounded_pp(n2->get_expr(), m) << " ", m_display_justification) << "\n";);
|
||||
IF_VERBOSE(20, j.display(verbose_stream() << "merge: " << mk_bounded_pp(n1->get_expr(), m) << " == " << mk_bounded_pp(n2->get_expr(), m) << " ", m_display_justification) << "\n";);
|
||||
|
||||
TRACE("euf", j.display(tout << "merge: " << bpp(n1) << " == " << bpp(n2) << " ", m_display_justification) << "\n";);
|
||||
IF_VERBOSE(20, j.display(verbose_stream() << "merge: " << bpp(n1) << " == " << bpp(n2) << " ", m_display_justification) << "\n";);
|
||||
force_push();
|
||||
SASSERT(m_num_scopes == 0);
|
||||
++m_stats.m_num_merge;
|
||||
|
@ -356,31 +392,57 @@ namespace euf {
|
|||
}
|
||||
if (r1->value() != l_undef)
|
||||
return;
|
||||
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr()))) {
|
||||
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
|
||||
add_literal(n1, false);
|
||||
}
|
||||
if (n1->is_equality() && n1->value() == l_false)
|
||||
new_diseq(n1);
|
||||
unsigned num_merge = 0, num_eqs = 0;
|
||||
for (enode* p : enode_parents(n1)) {
|
||||
if (p->merge_enabled()) {
|
||||
m_table.erase(p);
|
||||
m_worklist.push_back(p);
|
||||
++num_merge;
|
||||
}
|
||||
else if (p->is_equality()) {
|
||||
m_worklist.push_back(p);
|
||||
++num_eqs;
|
||||
}
|
||||
}
|
||||
new_diseq(n1);
|
||||
remove_parents(r1, r2);
|
||||
push_eq(r1, n1, r2->num_parents());
|
||||
merge_justification(n1, n2, j);
|
||||
for (enode* c : enode_class(n1))
|
||||
c->m_root = r2;
|
||||
std::swap(r1->m_next, r2->m_next);
|
||||
r2->inc_class_size(r1->class_size());
|
||||
r2->m_parents.append(r1->m_parents);
|
||||
merge_th_eq(r1, r2);
|
||||
reinsert_parents(r1, r2);
|
||||
}
|
||||
|
||||
void egraph::remove_parents(enode* r1, enode* r2) {
|
||||
for (enode* p : enode_parents(r1)) {
|
||||
if (p->is_marked1())
|
||||
continue;
|
||||
if (p->merge_enabled()) {
|
||||
if (!p->is_cgr())
|
||||
continue;
|
||||
SASSERT(m_table.contains_ptr(p));
|
||||
p->mark1();
|
||||
m_table.erase(p);
|
||||
SASSERT(!m_table.contains_ptr(p));
|
||||
}
|
||||
else if (p->is_equality())
|
||||
p->mark1();
|
||||
}
|
||||
}
|
||||
|
||||
void egraph::reinsert_parents(enode* r1, enode* r2) {
|
||||
for (enode* p : enode_parents(r1)) {
|
||||
if (!p->is_marked1())
|
||||
continue;
|
||||
p->unmark1();
|
||||
if (p->merge_enabled()) {
|
||||
auto rc = insert_table(p);
|
||||
enode* p_other = rc.first;
|
||||
SASSERT(m_table.contains_ptr(p) == (p_other == p));
|
||||
if (p_other != p)
|
||||
m_to_merge.push_back(to_merge(p_other, p, rc.second));
|
||||
else
|
||||
r2->m_parents.push_back(p);
|
||||
}
|
||||
else if (p->is_equality()) {
|
||||
r2->m_parents.push_back(p);
|
||||
reinsert_equality(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void egraph::merge_th_eq(enode* n, enode* root) {
|
||||
|
@ -400,24 +462,45 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
void egraph::undo_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
|
||||
enode* r2 = r1->get_root();
|
||||
TRACE("euf", tout << "undo-eq old-root: " << bpp(r1) << " current-root " << bpp(r2) << " node: " << bpp(n1) << "\n";);
|
||||
r2->dec_class_size(r1->class_size());
|
||||
std::swap(r1->m_next, r2->m_next);
|
||||
auto begin = r2->begin_parents() + r2_num_parents, end = r2->end_parents();
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
enode* p = *it;
|
||||
TRACE("euf", tout << "erase " << bpp(p) << "\n";);
|
||||
SASSERT(!p->merge_enabled() || m_table.contains_ptr(p));
|
||||
SASSERT(!p->merge_enabled() || p->is_cgr());
|
||||
if (p->merge_enabled())
|
||||
m_table.erase(p);
|
||||
}
|
||||
|
||||
for (enode* c : enode_class(r1))
|
||||
c->m_root = r1;
|
||||
|
||||
for (enode* p : enode_parents(r1))
|
||||
if (p->merge_enabled() && (p->is_cgr() || !p->congruent(p->m_cg)))
|
||||
insert_table(p);
|
||||
r2->m_parents.shrink(r2_num_parents);
|
||||
unmerge_justification(n1);
|
||||
}
|
||||
|
||||
|
||||
bool egraph::propagate() {
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
SASSERT(m_num_scopes == 0 || m_worklist.empty());
|
||||
unsigned head = 0, tail = m_worklist.size();
|
||||
SASSERT(m_num_scopes == 0 || m_to_merge.empty());
|
||||
unsigned head = 0, tail = m_to_merge.size();
|
||||
while (head < tail && m.limit().inc() && !inconsistent()) {
|
||||
for (unsigned i = head; i < tail && !inconsistent(); ++i) {
|
||||
enode* n = m_worklist[i];
|
||||
if (!n->is_marked1()) {
|
||||
n->mark1();
|
||||
reinsert(n);
|
||||
}
|
||||
auto const& w = m_to_merge[i];
|
||||
merge(w.a, w.b, justification::congruence(w.commutativity));
|
||||
}
|
||||
for (unsigned i = head; i < tail; ++i)
|
||||
m_worklist[i]->unmark1();
|
||||
head = tail;
|
||||
tail = m_worklist.size();
|
||||
tail = m_to_merge.size();
|
||||
}
|
||||
m_worklist.reset();
|
||||
m_to_merge.reset();
|
||||
force_push();
|
||||
return
|
||||
(m_new_lits_qhead < m_new_lits.size()) ||
|
||||
|
@ -565,10 +648,7 @@ namespace euf {
|
|||
SASSERT(a->get_root() == b->get_root());
|
||||
|
||||
enode* lca = find_lca(a, b);
|
||||
TRACE("euf_verbose", tout << "explain-eq: " << a->get_expr_id() << " = " << b->get_expr_id()
|
||||
<< ": " << mk_bounded_pp(a->get_expr(), m)
|
||||
<< " == " << mk_bounded_pp(b->get_expr(), m)
|
||||
<< " lca: " << mk_bounded_pp(lca->get_expr(), m) << "\n";);
|
||||
TRACE("euf_verbose", tout << "explain-eq: " << bpp(a) << " == " << bpp(b) << " lca: " << bpp(lca) << "\n";);
|
||||
push_to_lca(a, lca);
|
||||
push_to_lca(b, lca);
|
||||
if (m_used_eq)
|
||||
|
@ -590,7 +670,16 @@ namespace euf {
|
|||
|
||||
void egraph::invariant() {
|
||||
for (enode* n : m_nodes)
|
||||
n->invariant();
|
||||
n->invariant(*this);
|
||||
for (enode* n : m_nodes)
|
||||
if (n->merge_enabled() && n->num_args() > 0 && (!m_table.find(n) || n->get_root() != m_table.find(n)->get_root())) {
|
||||
CTRACE("euf", !m_table.find(n), tout << "node is not in table\n";);
|
||||
CTRACE("euf", m_table.find(n), tout << "root " << bpp(n->get_root()) << " table root " << bpp(m_table.find(n)->get_root()) << "\n";);
|
||||
TRACE("euf", display(tout << bpp(n) << " is not closed under congruence\n"););
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const {
|
||||
|
@ -653,13 +742,14 @@ namespace euf {
|
|||
for (unsigned i = 0; i < src.m_nodes.size(); ++i) {
|
||||
enode* n1 = src.m_nodes[i];
|
||||
expr* e1 = src.m_exprs[i];
|
||||
SASSERT(!n1->has_th_vars());
|
||||
args.reset();
|
||||
for (unsigned j = 0; j < n1->num_args(); ++j)
|
||||
args.push_back(old_expr2new_enode[n1->get_arg(j)->get_expr_id()]);
|
||||
expr* e2 = tr(e1);
|
||||
enode* n2 = mk(e2, args.size(), args.c_ptr());
|
||||
old_expr2new_enode.setx(e1->get_id(), n2, nullptr);
|
||||
n2->set_value(n2->value());
|
||||
n2->m_bool_var = n1->m_bool_var;
|
||||
}
|
||||
for (unsigned i = 0; i < src.m_nodes.size(); ++i) {
|
||||
enode* n1 = src.m_nodes[i];
|
||||
|
|
|
@ -18,9 +18,11 @@ Notes:
|
|||
It relies on
|
||||
- data structures form the (legacy) SMT solver.
|
||||
- it still uses eager path compression.
|
||||
- delayed congruence table reconstruction from egg.
|
||||
- it does not deduplicate parents.
|
||||
|
||||
NB. The worklist is in reality inheritied from the legacy SMT solver.
|
||||
It is claimed to have the same effect as delayed congruence table reconstruction from egg.
|
||||
Similar to the legacy solver, parents are partially deduplicated.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
@ -29,6 +31,7 @@ Notes:
|
|||
#include "util/lbool.h"
|
||||
#include "ast/euf/euf_enode.h"
|
||||
#include "ast/euf/euf_etable.h"
|
||||
#include "ast/ast_ll_pp.h"
|
||||
|
||||
namespace euf {
|
||||
|
||||
|
@ -70,7 +73,15 @@ namespace euf {
|
|||
};
|
||||
|
||||
class egraph {
|
||||
|
||||
typedef ptr_vector<trail<egraph> > trail_stack;
|
||||
|
||||
struct to_merge {
|
||||
enode* a, * b;
|
||||
bool commutativity;
|
||||
to_merge(enode* a, enode* b, bool c) : a(a), b(b), commutativity(c) {}
|
||||
};
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_merge;
|
||||
unsigned m_num_th_eqs;
|
||||
|
@ -130,7 +141,7 @@ namespace euf {
|
|||
tag(tag_t::is_value_assignment), r1(n), n1(nullptr), qhead(0) {}
|
||||
};
|
||||
ast_manager& m;
|
||||
enode_vector m_worklist;
|
||||
svector<to_merge> m_to_merge;
|
||||
etable m_table;
|
||||
region m_region;
|
||||
svector<update_record> m_updates;
|
||||
|
@ -168,12 +179,13 @@ namespace euf {
|
|||
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
|
||||
void undo_add_th_var(enode* n, theory_id id);
|
||||
enode* mk_enode(expr* f, unsigned num_args, enode * const* args);
|
||||
void reinsert(enode* n);
|
||||
void force_push();
|
||||
void set_conflict(enode* n1, enode* n2, justification j);
|
||||
void merge(enode* n1, enode* n2, justification j);
|
||||
void merge_th_eq(enode* n, enode* root);
|
||||
void merge_justification(enode* n1, enode* n2, justification j);
|
||||
void reinsert_parents(enode* r1, enode* r2);
|
||||
void remove_parents(enode* r1, enode* r2);
|
||||
void unmerge_justification(enode* n1);
|
||||
void reinsert_equality(enode* p);
|
||||
void update_children(enode* n);
|
||||
|
@ -183,6 +195,8 @@ namespace euf {
|
|||
void push_congruence(enode* n1, enode* n2, bool commutative);
|
||||
void push_todo(enode* n);
|
||||
|
||||
enode_bool_pair insert_table(enode* p);
|
||||
|
||||
template <typename T>
|
||||
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, justification const& j) {
|
||||
if (j.is_external())
|
||||
|
@ -267,7 +281,15 @@ namespace euf {
|
|||
std::ostream& display(std::ostream& out) const { return g.display(out, 0, n); }
|
||||
};
|
||||
e_pp pp(enode* n) const { return e_pp(*this, n); }
|
||||
struct b_pp {
|
||||
egraph const& g;
|
||||
enode* n;
|
||||
b_pp(egraph const& g, enode* n) : g(g), n(n) {}
|
||||
std::ostream& display(std::ostream& out) const { return out << n->get_expr_id() << ": " << mk_bounded_pp(n->get_expr(), g.m); }
|
||||
};
|
||||
b_pp bpp(enode* n) const { return b_pp(*this, n); }
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
unsigned num_scopes() const { return m_scopes.size() + m_num_scopes; }
|
||||
|
@ -275,4 +297,5 @@ namespace euf {
|
|||
|
||||
inline std::ostream& operator<<(std::ostream& out, egraph const& g) { return g.display(out); }
|
||||
inline std::ostream& operator<<(std::ostream& out, egraph::e_pp const& p) { return p.display(out); }
|
||||
inline std::ostream& operator<<(std::ostream& out, egraph::b_pp const& p) { return p.display(out); }
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@ Author:
|
|||
--*/
|
||||
|
||||
#include "ast/euf/euf_enode.h"
|
||||
#include "ast/euf/euf_egraph.h"
|
||||
|
||||
namespace euf {
|
||||
|
||||
void enode::invariant() {
|
||||
void enode::invariant(egraph& g) {
|
||||
unsigned class_size = 0;
|
||||
bool found_root = false;
|
||||
bool found_this = false;
|
||||
|
@ -27,6 +28,7 @@ namespace euf {
|
|||
VERIFY(c->m_root == m_root);
|
||||
found_root |= c == m_root;
|
||||
found_this |= c == this;
|
||||
++class_size;
|
||||
}
|
||||
VERIFY(found_root);
|
||||
VERIFY(found_this);
|
||||
|
@ -34,20 +36,26 @@ namespace euf {
|
|||
if (is_root()) {
|
||||
VERIFY(!m_target);
|
||||
for (enode* p : enode_parents(this)) {
|
||||
if (!p->merge_enabled())
|
||||
continue;
|
||||
bool found = false;
|
||||
for (enode* arg : enode_args(p)) {
|
||||
found |= arg->get_root() == this;
|
||||
}
|
||||
CTRACE("euf", !found, tout << g.bpp(p) << " does not have a child with root: " << g.bpp(this) << "\n";);
|
||||
VERIFY(found);
|
||||
}
|
||||
for (enode* c : enode_class(this)) {
|
||||
if (c == this)
|
||||
continue;
|
||||
for (enode* p : enode_parents(c)) {
|
||||
if (!p->merge_enabled())
|
||||
continue;
|
||||
bool found = false;
|
||||
for (enode* q : enode_parents(this)) {
|
||||
found |= p->congruent(q);
|
||||
}
|
||||
CTRACE("euf", !found, tout << "parent " << g.bpp(p) << " of " << g.bpp(c) << " is not congruent to a parent of " << g.bpp(this) << "\n";);
|
||||
VERIFY(found);
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +126,6 @@ namespace euf {
|
|||
prev->m_target = nullptr;
|
||||
prev->m_justification = justification::axiom();
|
||||
while (curr != nullptr) {
|
||||
|
||||
enode* new_curr = curr->m_target;
|
||||
justification new_js = curr->m_justification;
|
||||
curr->m_target = prev;
|
||||
|
@ -128,4 +135,11 @@ namespace euf {
|
|||
curr = new_curr;
|
||||
}
|
||||
}
|
||||
|
||||
bool enode::children_are_roots() const {
|
||||
for (auto* child : enode_args(this))
|
||||
if (!child->is_root())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace euf {
|
|||
typedef ptr_vector<enode> enode_vector;
|
||||
typedef std::pair<enode*,enode*> enode_pair;
|
||||
typedef svector<enode_pair> enode_pair_vector;
|
||||
typedef std::pair<enode*,bool> enode_bool_pair;
|
||||
typedef svector<enode_bool_pair> enode_bool_pair_vector;
|
||||
typedef id_var_list<> th_var_list;
|
||||
typedef int theory_var;
|
||||
typedef int theory_id;
|
||||
|
@ -48,11 +50,12 @@ namespace euf {
|
|||
lbool m_value;
|
||||
unsigned m_bool_var { UINT_MAX };
|
||||
unsigned m_class_size{ 1 };
|
||||
unsigned m_table_id{ UINT_MAX };
|
||||
unsigned m_table_id{ UINT_MAX };
|
||||
enode_vector m_parents;
|
||||
enode* m_next{ nullptr };
|
||||
enode* m_root{ nullptr };
|
||||
enode* m_target{ nullptr };
|
||||
enode* m_cg { nullptr };
|
||||
th_var_list m_th_vars;
|
||||
justification m_justification;
|
||||
unsigned m_num_args{ 0 };
|
||||
|
@ -102,6 +105,7 @@ namespace euf {
|
|||
|
||||
void set_update_children() { m_update_children = true; }
|
||||
|
||||
|
||||
friend class add_th_var_trail;
|
||||
friend class replace_th_var_trail;
|
||||
void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); }
|
||||
|
@ -131,7 +135,7 @@ namespace euf {
|
|||
bool is_equality() const { return m_is_equality; }
|
||||
lbool value() const { return m_value; }
|
||||
unsigned bool_var() const { return m_bool_var; }
|
||||
|
||||
bool is_cgr() const { return this == m_cg; }
|
||||
bool commutative() const { return m_commutative; }
|
||||
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
|
||||
bool merge_enabled() { return m_merge_enabled; }
|
||||
|
@ -172,6 +176,8 @@ namespace euf {
|
|||
func_decl* get_decl() const { return is_app(m_expr) ? to_app(m_expr)->get_decl() : nullptr; }
|
||||
unsigned get_expr_id() const { return m_expr->get_id(); }
|
||||
unsigned get_root_id() const { return m_root->m_expr->get_id(); }
|
||||
bool children_are_roots() const;
|
||||
|
||||
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; }
|
||||
|
@ -190,15 +196,15 @@ namespace euf {
|
|||
enode* const* begin_parents() const { return m_parents.begin(); }
|
||||
enode* const* end_parents() const { return m_parents.end(); }
|
||||
|
||||
void invariant();
|
||||
void invariant(class egraph& g);
|
||||
bool congruent(enode* n) const;
|
||||
};
|
||||
|
||||
class enode_args {
|
||||
enode& n;
|
||||
enode const& n;
|
||||
public:
|
||||
enode_args(enode& _n):n(_n) {}
|
||||
enode_args(enode* _n):n(*_n) {}
|
||||
enode_args(enode const& _n):n(_n) {}
|
||||
enode_args(enode const* _n):n(*_n) {}
|
||||
enode* const* begin() const { return n.m_args; }
|
||||
enode* const* end() const { return n.m_args + n.num_args(); }
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace euf {
|
|||
|
||||
// one table per func_decl implementation
|
||||
unsigned etable::cg_hash::operator()(enode * n) const {
|
||||
SASSERT(n->get_decl()->is_flat_associative() || n->num_args() >= 3);
|
||||
SASSERT(decl(n)->is_flat_associative() || num_args(n) >= 3);
|
||||
unsigned a, b, c;
|
||||
a = b = 0x9e3779b9;
|
||||
c = 11;
|
||||
|
@ -30,33 +30,33 @@ namespace euf {
|
|||
unsigned i = n->num_args();
|
||||
while (i >= 3) {
|
||||
i--;
|
||||
a += n->get_arg(i)->get_root()->hash();
|
||||
a += get_root(n, i)->hash();
|
||||
i--;
|
||||
b += n->get_arg(i)->get_root()->hash();
|
||||
b += get_root(n, i)->hash();
|
||||
i--;
|
||||
c += n->get_arg(i)->get_root()->hash();
|
||||
c += get_root(n, i)->hash();
|
||||
mix(a, b, c);
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case 2:
|
||||
b += n->get_arg(1)->get_root()->hash();
|
||||
b += get_root(n, 1)->hash();
|
||||
Z3_fallthrough;
|
||||
case 1:
|
||||
c += n->get_arg(0)->get_root()->hash();
|
||||
c += get_root(n, 0)->hash();
|
||||
}
|
||||
mix(a, b, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
bool etable::cg_eq::operator()(enode * n1, enode * n2) const {
|
||||
SASSERT(n1->get_decl() == n2->get_decl());
|
||||
unsigned num = n1->num_args();
|
||||
if (num != n2->num_args()) {
|
||||
SASSERT(decl(n1) == decl(n2));
|
||||
unsigned num = num_args(n1);
|
||||
if (num != num_args(n2)) {
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root())
|
||||
if (get_root(n1, i) != get_root(n2, i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -69,31 +69,25 @@ namespace euf {
|
|||
reset();
|
||||
}
|
||||
|
||||
void * etable::mk_table_for(func_decl * d) {
|
||||
void * etable::mk_table_for(unsigned arity, func_decl * d) {
|
||||
void * r;
|
||||
SASSERT(d->get_arity() >= 1);
|
||||
switch (d->get_arity()) {
|
||||
SASSERT(arity >= d->get_arity());
|
||||
switch (arity) {
|
||||
case 1:
|
||||
r = TAG(void*, alloc(unary_table), UNARY);
|
||||
SASSERT(GET_TAG(r) == UNARY);
|
||||
return r;
|
||||
case 2:
|
||||
if (d->is_flat_associative()) {
|
||||
// applications of declarations that are flat-assoc (e.g., +) may have many arguments.
|
||||
r = TAG(void*, alloc(table), NARY);
|
||||
SASSERT(GET_TAG(r) == NARY);
|
||||
return r;
|
||||
}
|
||||
else if (d->is_commutative()) {
|
||||
if (d->is_commutative()) {
|
||||
r = TAG(void*, alloc(comm_table, cg_comm_hash(), cg_comm_eq(m_commutativity)), BINARY_COMM);
|
||||
SASSERT(GET_TAG(r) == BINARY_COMM);
|
||||
return r;
|
||||
}
|
||||
else {
|
||||
r = TAG(void*, alloc(binary_table), BINARY);
|
||||
SASSERT(GET_TAG(r) == BINARY);
|
||||
return r;
|
||||
}
|
||||
return r;
|
||||
default:
|
||||
r = TAG(void*, alloc(table), NARY);
|
||||
SASSERT(GET_TAG(r) == NARY);
|
||||
|
@ -104,18 +98,20 @@ namespace euf {
|
|||
unsigned etable::set_table_id(enode * n) {
|
||||
func_decl * f = n->get_decl();
|
||||
unsigned tid;
|
||||
if (!m_func_decl2id.find(f, tid)) {
|
||||
decl_info d(f, n->num_args());
|
||||
if (!m_func_decl2id.find(d, tid)) {
|
||||
tid = m_tables.size();
|
||||
m_func_decl2id.insert(f, tid);
|
||||
m_func_decl2id.insert(d, tid);
|
||||
m_manager.inc_ref(f);
|
||||
SASSERT(tid <= m_tables.size());
|
||||
m_tables.push_back(mk_table_for(f));
|
||||
m_tables.push_back(mk_table_for(n->num_args(), f));
|
||||
}
|
||||
SASSERT(tid < m_tables.size());
|
||||
n->set_table_id(tid);
|
||||
DEBUG_CODE({
|
||||
unsigned tid_prime;
|
||||
SASSERT(m_func_decl2id.find(n->get_decl(), tid_prime) && tid == tid_prime);
|
||||
decl_info d(n->get_decl(), n->num_args());
|
||||
SASSERT(m_func_decl2id.contains(d));
|
||||
SASSERT(m_func_decl2id[d] == tid);
|
||||
});
|
||||
return tid;
|
||||
}
|
||||
|
@ -139,7 +135,7 @@ namespace euf {
|
|||
}
|
||||
m_tables.reset();
|
||||
for (auto const& kv : m_func_decl2id) {
|
||||
m_manager.dec_ref(kv.m_key);
|
||||
m_manager.dec_ref(kv.m_key.first);
|
||||
}
|
||||
m_func_decl2id.reset();
|
||||
}
|
||||
|
@ -147,7 +143,7 @@ namespace euf {
|
|||
void etable::display(std::ostream & out) const {
|
||||
for (auto const& kv : m_func_decl2id) {
|
||||
void * t = m_tables[kv.m_value];
|
||||
out << mk_pp(kv.m_key, m_manager) << ": ";
|
||||
out << mk_pp(kv.m_key.first, m_manager) << ": ";
|
||||
switch (GET_TAG(t)) {
|
||||
case UNARY:
|
||||
display_unary(out, t);
|
||||
|
@ -245,5 +241,40 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
bool etable::contains(enode* n) const {
|
||||
SASSERT(n->num_args() > 0);
|
||||
void* t = const_cast<etable*>(this)->get_table(n);
|
||||
switch (static_cast<table_kind>(GET_TAG(t))) {
|
||||
case UNARY:
|
||||
return UNTAG(unary_table*, t)->contains(n);
|
||||
case BINARY:
|
||||
return UNTAG(binary_table*, t)->contains(n);
|
||||
case BINARY_COMM:
|
||||
return UNTAG(comm_table*, t)->contains(n);
|
||||
default:
|
||||
return UNTAG(table*, t)->contains(n);
|
||||
}
|
||||
}
|
||||
|
||||
enode* etable::find(enode* n) const {
|
||||
SASSERT(n->num_args() > 0);
|
||||
enode* r = nullptr;
|
||||
void* t = const_cast<etable*>(this)->get_table(n);
|
||||
switch (static_cast<table_kind>(GET_TAG(t))) {
|
||||
case UNARY:
|
||||
return UNTAG(unary_table*, t)->find(n, r) ? r : nullptr;
|
||||
case BINARY:
|
||||
return UNTAG(binary_table*, t)->find(n, r) ? r : nullptr;
|
||||
case BINARY_COMM:
|
||||
return UNTAG(comm_table*, t)->find(n, r) ? r : nullptr;
|
||||
default:
|
||||
return UNTAG(table*, t)->find(n, r) ? r : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool etable::contains_ptr(enode* n) const {
|
||||
return find(n) == n;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
/*++ Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
|
@ -22,28 +21,33 @@ Revision History:
|
|||
#include "util/chashtable.h"
|
||||
|
||||
namespace euf {
|
||||
|
||||
typedef std::pair<enode *, bool> enode_bool_pair;
|
||||
|
||||
// one table per function symbol
|
||||
|
||||
static unsigned num_args(enode* n) { return n->num_args(); }
|
||||
static func_decl* decl(enode* n) { return n->get_decl(); }
|
||||
|
||||
|
||||
/**
|
||||
\brief Congruence table.
|
||||
*/
|
||||
class etable {
|
||||
static enode* get_root(enode* n, unsigned idx) { return n->get_arg(idx)->get_root(); }
|
||||
|
||||
struct cg_unary_hash {
|
||||
unsigned operator()(enode * n) const {
|
||||
SASSERT(n->num_args() == 1);
|
||||
return n->get_arg(0)->get_root()->hash();
|
||||
SASSERT(num_args(n) == 1);
|
||||
return get_root(n, 0)->hash();
|
||||
}
|
||||
};
|
||||
|
||||
struct cg_unary_eq {
|
||||
|
||||
bool operator()(enode * n1, enode * n2) const {
|
||||
SASSERT(n1->num_args() == 1);
|
||||
SASSERT(n2->num_args() == 1);
|
||||
SASSERT(n1->get_decl() == n2->get_decl());
|
||||
return n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root();
|
||||
SASSERT(num_args(n1) == 1);
|
||||
SASSERT(num_args(n2) == 1);
|
||||
SASSERT(decl(n1) == decl(n2));
|
||||
return get_root(n1, 0) == get_root(n2, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -51,19 +55,19 @@ namespace euf {
|
|||
|
||||
struct cg_binary_hash {
|
||||
unsigned operator()(enode * n) const {
|
||||
SASSERT(n->num_args() == 2);
|
||||
return combine_hash(n->get_arg(0)->get_root()->hash(), n->get_arg(1)->get_root()->hash());
|
||||
SASSERT(num_args(n) == 2);
|
||||
return combine_hash(get_root(n, 0)->hash(), get_root(n, 1)->hash());
|
||||
}
|
||||
};
|
||||
|
||||
struct cg_binary_eq {
|
||||
bool operator()(enode * n1, enode * n2) const {
|
||||
SASSERT(n1->num_args() == 2);
|
||||
SASSERT(n2->num_args() == 2);
|
||||
SASSERT(n1->get_decl() == n2->get_decl());
|
||||
return
|
||||
n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root() &&
|
||||
n1->get_arg(1)->get_root() == n2->get_arg(1)->get_root();
|
||||
SASSERT(num_args(n1) == 2);
|
||||
SASSERT(num_args(n2) == 2);
|
||||
SASSERT(decl(n1) == decl(n2));
|
||||
return
|
||||
get_root(n1, 0) == get_root(n2, 0) &&
|
||||
get_root(n1, 1) == get_root(n2, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -71,9 +75,9 @@ namespace euf {
|
|||
|
||||
struct cg_comm_hash {
|
||||
unsigned operator()(enode * n) const {
|
||||
SASSERT(n->num_args() == 2);
|
||||
unsigned h1 = n->get_arg(0)->get_root()->hash();
|
||||
unsigned h2 = n->get_arg(1)->get_root()->hash();
|
||||
SASSERT(num_args(n) == 2);
|
||||
unsigned h1 = get_root(n, 0)->hash();
|
||||
unsigned h2 = get_root(n, 1)->hash();
|
||||
if (h1 > h2)
|
||||
std::swap(h1, h2);
|
||||
return hash_u((h1 << 16) | (h2 & 0xFFFF));
|
||||
|
@ -82,15 +86,16 @@ namespace euf {
|
|||
|
||||
struct cg_comm_eq {
|
||||
bool & m_commutativity;
|
||||
cg_comm_eq(bool & c):m_commutativity(c) {}
|
||||
cg_comm_eq( bool & c): m_commutativity(c) {}
|
||||
bool operator()(enode * n1, enode * n2) const {
|
||||
SASSERT(n1->num_args() == 2);
|
||||
SASSERT(n2->num_args() == 2);
|
||||
SASSERT(n1->get_decl() == n2->get_decl());
|
||||
enode * c1_1 = n1->get_arg(0)->get_root();
|
||||
enode * c1_2 = n1->get_arg(1)->get_root();
|
||||
enode * c2_1 = n2->get_arg(0)->get_root();
|
||||
enode * c2_2 = n2->get_arg(1)->get_root();
|
||||
SASSERT(num_args(n1) == 2);
|
||||
SASSERT(num_args(n2) == 2);
|
||||
|
||||
SASSERT(decl(n1) == decl(n2));
|
||||
enode* c1_1 = get_root(n1, 0);
|
||||
enode* c1_2 = get_root(n1, 1);
|
||||
enode* c2_1 = get_root(n2, 0);
|
||||
enode* c2_2 = get_root(n2, 1);
|
||||
if (c1_1 == c2_1 && c1_2 == c2_2) {
|
||||
return true;
|
||||
}
|
||||
|
@ -113,11 +118,19 @@ namespace euf {
|
|||
};
|
||||
|
||||
typedef chashtable<enode*, cg_hash, cg_eq> table;
|
||||
typedef std::pair<func_decl*, unsigned> decl_info;
|
||||
struct decl_hash {
|
||||
unsigned operator()(decl_info const& d) const { return d.first->hash(); }
|
||||
};
|
||||
struct decl_eq {
|
||||
bool operator()(decl_info const& a, decl_info const& b) const { return a == b; }
|
||||
};
|
||||
|
||||
|
||||
ast_manager & m_manager;
|
||||
bool m_commutativity; //!< true if the last found congruence used commutativity
|
||||
bool m_commutativity{ false }; //!< true if the last found congruence used commutativity
|
||||
ptr_vector<void> m_tables;
|
||||
obj_map<func_decl, unsigned> m_func_decl2id;
|
||||
map<decl_info, unsigned, decl_hash, decl_eq> m_func_decl2id;
|
||||
|
||||
enum table_kind {
|
||||
UNARY,
|
||||
|
@ -126,7 +139,7 @@ namespace euf {
|
|||
NARY
|
||||
};
|
||||
|
||||
void * mk_table_for(func_decl * d);
|
||||
void * mk_table_for(unsigned n, func_decl * d);
|
||||
unsigned set_table_id(enode * n);
|
||||
|
||||
void * get_table(enode * n) {
|
||||
|
@ -157,52 +170,11 @@ namespace euf {
|
|||
|
||||
void erase(enode * n);
|
||||
|
||||
bool contains(enode * n) const {
|
||||
SASSERT(n->num_args() > 0);
|
||||
void * t = const_cast<etable*>(this)->get_table(n);
|
||||
switch (static_cast<table_kind>(GET_TAG(t))) {
|
||||
case UNARY:
|
||||
return UNTAG(unary_table*, t)->contains(n);
|
||||
case BINARY:
|
||||
return UNTAG(binary_table*, t)->contains(n);
|
||||
case BINARY_COMM:
|
||||
return UNTAG(comm_table*, t)->contains(n);
|
||||
default:
|
||||
return UNTAG(table*, t)->contains(n);
|
||||
}
|
||||
}
|
||||
bool contains(enode* n) const;
|
||||
|
||||
enode * find(enode * n) const {
|
||||
SASSERT(n->num_args() > 0);
|
||||
enode * r = nullptr;
|
||||
void * t = const_cast<etable*>(this)->get_table(n);
|
||||
switch (static_cast<table_kind>(GET_TAG(t))) {
|
||||
case UNARY:
|
||||
return UNTAG(unary_table*, t)->find(n, r) ? r : nullptr;
|
||||
case BINARY:
|
||||
return UNTAG(binary_table*, t)->find(n, r) ? r : nullptr;
|
||||
case BINARY_COMM:
|
||||
return UNTAG(comm_table*, t)->find(n, r) ? r : nullptr;
|
||||
default:
|
||||
return UNTAG(table*, t)->find(n, r) ? r : nullptr;
|
||||
}
|
||||
}
|
||||
enode* find(enode* n) const;
|
||||
|
||||
bool contains_ptr(enode * n) const {
|
||||
enode * r;
|
||||
SASSERT(n->num_args() > 0);
|
||||
void * t = const_cast<etable*>(this)->get_table(n);
|
||||
switch (static_cast<table_kind>(GET_TAG(t))) {
|
||||
case UNARY:
|
||||
return UNTAG(unary_table*, t)->find(n, r) && n == r;
|
||||
case BINARY:
|
||||
return UNTAG(binary_table*, t)->find(n, r) && n == r;
|
||||
case BINARY_COMM:
|
||||
return UNTAG(comm_table*, t)->find(n, r) && n == r;
|
||||
default:
|
||||
return UNTAG(table*, t)->find(n, r) && n == r;
|
||||
}
|
||||
}
|
||||
bool contains_ptr(enode* n) const;
|
||||
|
||||
void reset();
|
||||
|
||||
|
|
|
@ -1012,7 +1012,7 @@ br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
r2 = m_util.norm(r2, bv_size, true);
|
||||
if (r2.is_zero()) {
|
||||
if (!hi_div0) {
|
||||
result = m().mk_app(get_fid(), OP_BSDIV0, arg1);
|
||||
result = m_util.mk_bv_sdiv0(arg1);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
else {
|
||||
|
@ -1035,19 +1035,19 @@ br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
return BR_DONE;
|
||||
}
|
||||
|
||||
result = m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2);
|
||||
result = m_util.mk_bv_sdiv_i(arg1, arg2);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (hi_div0) {
|
||||
result = m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2);
|
||||
result = m_util.mk_bv_sdiv_i(arg1, arg2);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
bv_size = get_bv_size(arg2);
|
||||
result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)),
|
||||
m().mk_app(get_fid(), OP_BSDIV0, arg1),
|
||||
m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2));
|
||||
m_util.mk_bv_sdiv0(arg1),
|
||||
m_util.mk_bv_sdiv_i(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1061,7 @@ br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
r2 = m_util.norm(r2, bv_size);
|
||||
if (r2.is_zero()) {
|
||||
if (!hi_div0) {
|
||||
result = m().mk_app(get_fid(), OP_BUDIV0, arg1);
|
||||
result = m_util.mk_bv_udiv0(arg1);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
else {
|
||||
|
@ -1090,19 +1090,19 @@ br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
}
|
||||
|
||||
|
||||
result = m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2);
|
||||
result = m_util.mk_bv_udiv_i(arg1, arg2);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (hi_div0) {
|
||||
result = m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2);
|
||||
result = m_util.mk_bv_udiv_i(arg1, arg2);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
bv_size = get_bv_size(arg2);
|
||||
result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)),
|
||||
m().mk_app(get_fid(), OP_BUDIV0, arg1),
|
||||
m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2));
|
||||
m_util.mk_bv_udiv0(arg1),
|
||||
m_util.mk_bv_udiv_i(arg1, arg2));
|
||||
|
||||
TRACE("bv_udiv", tout << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n---->\n" << mk_ismt2_pp(result, m()) << "\n";);
|
||||
return BR_REWRITE2;
|
||||
|
@ -1201,7 +1201,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
r2 = m_util.norm(r2, bv_size);
|
||||
if (r2.is_zero()) {
|
||||
if (!hi_div0) {
|
||||
result = m().mk_app(get_fid(), OP_BUREM0, arg1);
|
||||
result = m_util.mk_bv_urem0(arg1);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
else {
|
||||
|
@ -1233,7 +1233,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
result = m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2);
|
||||
result = m_util.mk_bv_urem_i(arg1, arg2);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
@ -1242,7 +1242,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
if (is_num1 && r1.is_zero()) {
|
||||
expr * zero = arg1;
|
||||
result = m().mk_ite(m().mk_eq(arg2, zero),
|
||||
m().mk_app(get_fid(), OP_BUREM0, zero),
|
||||
m_util.mk_bv_urem0(zero),
|
||||
zero);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
@ -1254,7 +1254,7 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
expr * x_minus_1 = arg1;
|
||||
expr * minus_one = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size);
|
||||
result = m().mk_ite(m().mk_eq(x, mk_numeral(0, bv_size)),
|
||||
m().mk_app(get_fid(), OP_BUREM0, minus_one),
|
||||
m_util.mk_bv_urem0(minus_one),
|
||||
x_minus_1);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
@ -1278,14 +1278,14 @@ br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
}
|
||||
|
||||
if (hi_div0) {
|
||||
result = m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2);
|
||||
result = m_util.mk_bv_urem_i(arg1, arg2);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
bv_size = get_bv_size(arg2);
|
||||
result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)),
|
||||
m().mk_app(get_fid(), OP_BUREM0, arg1),
|
||||
m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2));
|
||||
m_util.mk_bv_urem0(arg1),
|
||||
m_util.mk_bv_urem_i(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -1297,7 +1297,7 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
if (is_num1) {
|
||||
r1 = m_util.norm(r1, bv_size, true);
|
||||
if (r1.is_zero()) {
|
||||
result = m().mk_app(get_fid(), OP_BUREM, arg1, arg2);
|
||||
result = m_util.mk_bv_urem(arg1, arg2);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
@ -1306,7 +1306,7 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e
|
|||
r2 = m_util.norm(r2, bv_size, true);
|
||||
if (r2.is_zero()) {
|
||||
if (!hi_div0)
|
||||
result = m().mk_app(get_fid(), OP_BSMOD0, arg1);
|
||||
result = m_util.mk_bv_smod0(arg1);
|
||||
else
|
||||
result = arg1;
|
||||
return BR_DONE;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue