mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
optimizations to bv-solver and euf-egraph (#4698)
* additional bit-vector propagators Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * rename restrict (not a keyword, but well) #4694, tune euf Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add pb rewriting to pb2bv #4697 Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
ed44a44579
commit
6f63f8761c
24 changed files with 206 additions and 116 deletions
|
@ -1825,7 +1825,7 @@ ast * ast_manager::register_node_core(ast * n) {
|
|||
n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk();
|
||||
|
||||
// track_id(*this, n, 3);
|
||||
|
||||
|
||||
TRACE("ast", tout << (s_count++) << " Object " << n->m_id << " was created.\n";);
|
||||
TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";);
|
||||
// increment reference counters
|
||||
|
|
|
@ -27,12 +27,16 @@ namespace euf {
|
|||
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)
|
||||
m_table.erase(*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)
|
||||
m_table.insert(*it);
|
||||
if ((*it)->merge_enabled())
|
||||
m_table.insert(*it);
|
||||
|
||||
r2->m_parents.shrink(r2_num_parents);
|
||||
unmerge_justification(n1);
|
||||
}
|
||||
|
@ -48,33 +52,22 @@ namespace euf {
|
|||
return n;
|
||||
}
|
||||
|
||||
void egraph::reinsert(enode* n) {
|
||||
unsigned num_parents = n->m_parents.size();
|
||||
for (unsigned i = 0; i < num_parents; ++i) {
|
||||
enode* p = n->m_parents[i];
|
||||
if (is_equality(p)) {
|
||||
reinsert_equality(p);
|
||||
}
|
||||
else {
|
||||
auto rc = m_table.insert(p);
|
||||
merge(rc.first, p, justification::congruence(rc.second));
|
||||
if (inconsistent())
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void egraph::reinsert_equality(enode* p) {
|
||||
SASSERT(is_equality(p));
|
||||
if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root() && m_value(p) != l_true) {
|
||||
SASSERT(p->is_equality());
|
||||
if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) {
|
||||
add_literal(p, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool egraph::is_equality(enode* p) const {
|
||||
return m.is_eq(p->get_expr());
|
||||
}
|
||||
|
||||
void egraph::force_push() {
|
||||
if (m_num_scopes == 0)
|
||||
return;
|
||||
|
@ -103,7 +96,8 @@ namespace euf {
|
|||
n->mark_interpreted();
|
||||
if (num_args == 0)
|
||||
return n;
|
||||
if (is_equality(n)) {
|
||||
if (m.is_eq(f)) {
|
||||
n->set_is_equality();
|
||||
update_children(n);
|
||||
reinsert_equality(n);
|
||||
return n;
|
||||
|
@ -150,13 +144,15 @@ namespace euf {
|
|||
}
|
||||
|
||||
void egraph::new_diseq(enode* n1) {
|
||||
SASSERT(m.is_eq(n1->get_expr()));
|
||||
SASSERT(n1->is_equality());
|
||||
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)
|
||||
if (r1 == r2) {
|
||||
add_literal(n1, true);
|
||||
return;
|
||||
}
|
||||
if (!r1->has_th_vars())
|
||||
return;
|
||||
if (!r2->has_th_vars())
|
||||
|
@ -189,7 +185,7 @@ namespace euf {
|
|||
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())) {
|
||||
if (p->is_equality() && p->value() == l_false) {
|
||||
enode* n = nullptr;
|
||||
n = (r == p->get_arg(0)->get_root()) ? p->get_arg(1) : p->get_arg(0);
|
||||
n = n->get_root();
|
||||
|
@ -254,6 +250,13 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
void egraph::set_value(enode* n, lbool value) {
|
||||
force_push();
|
||||
VERIFY(n->value() == l_undef);
|
||||
n->set_value(value);
|
||||
m_updates.push_back(update_record(n, update_record::value_assignment()));
|
||||
}
|
||||
|
||||
void egraph::pop(unsigned num_scopes) {
|
||||
if (num_scopes <= m_num_scopes) {
|
||||
m_num_scopes -= num_scopes;
|
||||
|
@ -309,6 +312,10 @@ namespace euf {
|
|||
case update_record::tag_t::is_inconsistent:
|
||||
m_inconsistent = p.m_inconsistent;
|
||||
break;
|
||||
case update_record::tag_t::is_value_assignment:
|
||||
VERIFY(p.r1->value() != l_undef);
|
||||
p.r1->set_value(l_undef);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
@ -324,12 +331,16 @@ namespace euf {
|
|||
}
|
||||
|
||||
void egraph::merge(enode* n1, enode* n2, justification j) {
|
||||
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";);
|
||||
force_push();
|
||||
SASSERT(m_num_scopes == 0);
|
||||
++m_stats.m_num_merge;
|
||||
|
@ -337,18 +348,29 @@ namespace euf {
|
|||
set_conflict(n1, n2, j);
|
||||
return;
|
||||
}
|
||||
if ((r1->class_size() > r2->class_size() && !r2->interpreted()) || r1->interpreted()) {
|
||||
if ((r1->class_size() > r2->class_size() && !r2->interpreted()) || r1->interpreted() || r1->value() != l_undef) {
|
||||
std::swap(r1, r2);
|
||||
std::swap(n1, n2);
|
||||
}
|
||||
if ((m.is_true(r2->get_expr()) || m.is_false(r2->get_expr())) && j.is_congruence())
|
||||
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))
|
||||
m_table.erase(p);
|
||||
for (enode* p : enode_parents(n2))
|
||||
m_table.erase(p);
|
||||
if (r1->value() != l_undef)
|
||||
return;
|
||||
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr()))) {
|
||||
add_literal(n1, false);
|
||||
}
|
||||
if (n1->is_equality() && r2->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;
|
||||
}
|
||||
}
|
||||
push_eq(r1, n1, r2->num_parents());
|
||||
merge_justification(n1, n2, j);
|
||||
for (enode* c : enode_class(n1))
|
||||
|
@ -357,7 +379,6 @@ namespace euf {
|
|||
r2->inc_class_size(r1->class_size());
|
||||
r2->m_parents.append(r1->m_parents);
|
||||
merge_th_eq(r1, r2);
|
||||
m_worklist.push_back(r2);
|
||||
}
|
||||
|
||||
void egraph::merge_th_eq(enode* n, enode* root) {
|
||||
|
@ -383,14 +404,13 @@ namespace euf {
|
|||
unsigned head = 0, tail = m_worklist.size();
|
||||
while (head < tail && m.limit().inc() && !inconsistent()) {
|
||||
for (unsigned i = head; i < tail && !inconsistent(); ++i) {
|
||||
enode* n = m_worklist[i]->get_root();
|
||||
enode* n = m_worklist[i];
|
||||
if (!n->is_marked1()) {
|
||||
n->mark1();
|
||||
m_worklist[i] = n;
|
||||
reinsert(n);
|
||||
}
|
||||
}
|
||||
for (unsigned i = head; i < tail; ++i)
|
||||
for (unsigned i = head; i < tail; ++i)
|
||||
m_worklist[i]->unmark1();
|
||||
head = tail;
|
||||
tail = m_worklist.size();
|
||||
|
@ -460,7 +480,7 @@ namespace euf {
|
|||
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)
|
||||
if (r && r->get_root()->value() == l_false)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -90,9 +90,10 @@ namespace euf {
|
|||
struct new_th_eq_qhead {};
|
||||
struct new_lits_qhead {};
|
||||
struct inconsistent {};
|
||||
struct value_assignment {};
|
||||
enum class tag_t { is_set_parent, is_add_node, is_toggle_merge,
|
||||
is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq,
|
||||
is_new_th_eq_qhead, is_new_lits_qhead, is_inconsistent };
|
||||
is_new_th_eq_qhead, is_new_lits_qhead, is_inconsistent, is_value_assignment };
|
||||
tag_t tag;
|
||||
enode* r1;
|
||||
enode* n1;
|
||||
|
@ -124,7 +125,9 @@ namespace euf {
|
|||
update_record(unsigned qh, new_lits_qhead):
|
||||
tag(tag_t::is_new_lits_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
|
||||
update_record(bool inc, inconsistent) :
|
||||
tag(tag_t::is_inconsistent), m_inconsistent(inc) {}
|
||||
tag(tag_t::is_inconsistent), r1(nullptr), n1(nullptr), m_inconsistent(inc) {}
|
||||
update_record(enode* n, value_assignment) :
|
||||
tag(tag_t::is_value_assignment), r1(n), n1(nullptr), qhead(0) {}
|
||||
};
|
||||
ast_manager& m;
|
||||
enode_vector m_worklist;
|
||||
|
@ -151,7 +154,6 @@ namespace euf {
|
|||
std::function<void(expr*,expr*,expr*)> m_used_eq;
|
||||
std::function<void(app*,app*)> m_used_cc;
|
||||
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) {
|
||||
m_updates.push_back(update_record(r1, n1, r2_num_parents));
|
||||
|
@ -160,7 +162,6 @@ namespace euf {
|
|||
|
||||
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);
|
||||
|
@ -202,12 +203,12 @@ namespace euf {
|
|||
void push() { ++m_num_scopes; }
|
||||
void pop(unsigned num_scopes);
|
||||
|
||||
bool is_equality(enode* n) const;
|
||||
|
||||
/**
|
||||
\brief merge nodes, all effects are deferred to the propagation step.
|
||||
*/
|
||||
void merge(enode* n1, enode* n2, void* reason) { merge(n1, n2, justification::external(reason)); }
|
||||
void new_diseq(enode* n1);
|
||||
|
||||
|
||||
/**
|
||||
\brief propagate set of merges.
|
||||
|
@ -243,11 +244,12 @@ namespace euf {
|
|||
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_value(enode* n, lbool value);
|
||||
void set_bool_var(enode* n, unsigned v) { n->set_bool_var(v); }
|
||||
|
||||
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_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 end_explain();
|
||||
|
|
|
@ -17,6 +17,7 @@ Author:
|
|||
|
||||
#include "util/vector.h"
|
||||
#include "util/id_var_list.h"
|
||||
#include "util/lbool.h"
|
||||
#include "ast/ast.h"
|
||||
#include "ast/euf/euf_justification.h"
|
||||
|
||||
|
@ -43,6 +44,9 @@ namespace euf {
|
|||
bool m_update_children{ false };
|
||||
bool m_interpreted{ false };
|
||||
bool m_merge_enabled{ true };
|
||||
bool m_is_equality{ false };
|
||||
lbool m_value;
|
||||
unsigned m_bool_var { UINT_MAX };
|
||||
unsigned m_class_size{ 1 };
|
||||
unsigned m_table_id{ UINT_MAX };
|
||||
enode_vector m_parents;
|
||||
|
@ -104,6 +108,9 @@ namespace euf {
|
|||
void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); }
|
||||
void del_th_var(theory_id id) { m_th_vars.del_var(id); }
|
||||
void set_merge_enabled(bool m) { m_merge_enabled = m; }
|
||||
void set_value(lbool v) { m_value = v; }
|
||||
void set_is_equality() { m_is_equality = true; }
|
||||
void set_bool_var(unsigned v) { m_bool_var = v; }
|
||||
|
||||
public:
|
||||
~enode() {
|
||||
|
@ -121,6 +128,10 @@ namespace euf {
|
|||
unsigned num_args() const { return m_num_args; }
|
||||
unsigned num_parents() const { return m_parents.size(); }
|
||||
bool interpreted() const { return m_interpreted; }
|
||||
bool is_equality() const { return m_is_equality; }
|
||||
lbool value() const { return m_value; }
|
||||
unsigned bool_var() const { return m_bool_var; }
|
||||
|
||||
bool commutative() const { return m_commutative; }
|
||||
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
|
||||
bool merge_enabled() { return m_merge_enabled; }
|
||||
|
|
|
@ -40,6 +40,6 @@ public:
|
|||
void pop(unsigned num_scopes);
|
||||
void flush_side_constraints(expr_ref_vector& side_constraints);
|
||||
unsigned num_translated() const;
|
||||
void collect_statistics(statistics & st) const;
|
||||
void collect_statistics(::statistics & st) const;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue