3
0
Fork 0
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:
Nikolaj Bjorner 2020-09-28 19:24:16 -07:00 committed by GitHub
parent 25724401cf
commit 367e5fdd52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 1343 additions and 924 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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];

View file

@ -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); }
}

View file

@ -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;
}
}

View file

@ -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(); }
};

View file

@ -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;
}
};

View file

@ -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();

View file

@ -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;