mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
bv and gc of literals (#4692)
* bv and gc of literals Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * overload Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * diseq Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * diseq Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
2d52367368
commit
549753845e
34 changed files with 1480 additions and 854 deletions
|
@ -41,6 +41,7 @@ namespace euf {
|
|||
enode* n = enode::mk(m_region, f, num_args, args);
|
||||
m_nodes.push_back(n);
|
||||
m_exprs.push_back(f);
|
||||
m_expr2enode.setx(f->get_id(), n, nullptr);
|
||||
push_node(n);
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
set_merge_enabled(args[i], true);
|
||||
|
@ -65,7 +66,7 @@ namespace euf {
|
|||
|
||||
void egraph::reinsert_equality(enode* p) {
|
||||
SASSERT(is_equality(p));
|
||||
if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) {
|
||||
if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root() && m_value(p) != l_true) {
|
||||
add_literal(p, true);
|
||||
}
|
||||
}
|
||||
|
@ -97,8 +98,7 @@ namespace euf {
|
|||
SASSERT(!find(f));
|
||||
force_push();
|
||||
enode *n = mk_enode(f, num_args, args);
|
||||
SASSERT(n->class_size() == 1);
|
||||
m_expr2enode.setx(f->get_id(), n, nullptr);
|
||||
SASSERT(n->class_size() == 1);
|
||||
if (num_args == 0 && m.is_unique_value(f))
|
||||
n->mark_interpreted();
|
||||
if (num_args == 0)
|
||||
|
@ -110,26 +110,17 @@ namespace euf {
|
|||
}
|
||||
enode_bool_pair p = m_table.insert(n);
|
||||
enode* n2 = p.first;
|
||||
if (n2 == n) {
|
||||
update_children(n);
|
||||
}
|
||||
else {
|
||||
merge(n, n2, justification::congruence(p.second));
|
||||
#if 0
|
||||
SASSERT(n2->get_expr() != n->get_expr());
|
||||
SASSERT(n->class_size() == 1);
|
||||
SASSERT(n->is_root());
|
||||
merge_justification(n, n2, justification::congruence(p.second));
|
||||
enode* r2 = n2->get_root();
|
||||
std::swap(n->m_next, r2->m_next);
|
||||
n->m_root = r2;
|
||||
r2->inc_class_size(n->class_size());
|
||||
push_eq(n, n, r2->num_parents());
|
||||
#endif
|
||||
}
|
||||
if (n2 == n)
|
||||
update_children(n);
|
||||
else
|
||||
merge(n, n2, justification::congruence(p.second));
|
||||
return n;
|
||||
}
|
||||
|
||||
egraph::egraph(ast_manager& m) : m(m), m_table(m), m_exprs(m) {
|
||||
m_tmp_eq = enode::mk_tmp(m_region, 2);
|
||||
}
|
||||
|
||||
egraph::~egraph() {
|
||||
for (enode* n : m_nodes)
|
||||
n->m_parents.finalize();
|
||||
|
@ -142,6 +133,15 @@ namespace euf {
|
|||
++m_stats.m_num_th_eqs;
|
||||
}
|
||||
|
||||
void egraph::add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq) {
|
||||
if (!th_propagates_diseqs(id))
|
||||
return;
|
||||
TRACE("euf_verbose", tout << "eq: " << v1 << " != " << v2 << "\n";);
|
||||
m_new_th_eqs.push_back(th_eq(id, v1, v2, eq));
|
||||
m_updates.push_back(update_record(update_record::new_th_eq()));
|
||||
++m_stats.m_num_th_diseqs;
|
||||
}
|
||||
|
||||
void egraph::add_literal(enode* n, bool is_eq) {
|
||||
TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";);
|
||||
m_new_lits.push_back(enode_bool_pair(n, is_eq));
|
||||
|
@ -149,6 +149,68 @@ namespace euf {
|
|||
if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
|
||||
}
|
||||
|
||||
void egraph::new_diseq(enode* n1) {
|
||||
SASSERT(m.is_eq(n1->get_expr()));
|
||||
enode* arg1 = n1->get_arg(0), * arg2 = n1->get_arg(1);
|
||||
enode* r1 = arg1->get_root();
|
||||
enode* r2 = arg2->get_root();
|
||||
TRACE("euf", tout << "new-diseq: " << mk_pp(r1->get_expr(), m) << " " << mk_pp(r2->get_expr(), m) << ": " << r1->has_th_vars() << " " << r2->has_th_vars() << "\n";);
|
||||
if (r1 == r2)
|
||||
return;
|
||||
if (!r1->has_th_vars())
|
||||
return;
|
||||
if (!r2->has_th_vars())
|
||||
return;
|
||||
if (r1->has_one_th_var() && r2->has_one_th_var() && r1->get_first_th_id() == r2->get_first_th_id()) {
|
||||
theory_id id = r1->get_first_th_id();
|
||||
if (!th_propagates_diseqs(id))
|
||||
return;
|
||||
theory_var v1 = arg1->get_closest_th_var(id);
|
||||
theory_var v2 = arg2->get_closest_th_var(id);
|
||||
add_th_diseq(id, v1, v2, n1->get_expr());
|
||||
return;
|
||||
}
|
||||
for (auto p : euf::enode_th_vars(r1)) {
|
||||
if (!th_propagates_diseqs(p.get_id()))
|
||||
continue;
|
||||
for (auto q : euf::enode_th_vars(r2))
|
||||
if (p.get_id() == q.get_id())
|
||||
add_th_diseq(p.get_id(), p.get_var(), q.get_var(), n1->get_expr());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Propagate disequalities over equality atoms that are assigned to false.
|
||||
*/
|
||||
|
||||
void egraph::add_th_diseqs(theory_id id, theory_var v1, enode* r) {
|
||||
SASSERT(r->is_root());
|
||||
if (!th_propagates_diseqs(id))
|
||||
return;
|
||||
for (enode* p : enode_parents(r)) {
|
||||
if (m.is_eq(p->get_expr()) && m.is_false(p->get_root()->get_expr())) {
|
||||
enode* n = nullptr;
|
||||
n = (r == p->get_arg(0)->get_root()) ? p->get_arg(1) : p->get_arg(0);
|
||||
n = n->get_root();
|
||||
theory_var v2 = n->get_closest_th_var(id);
|
||||
if (v2 != null_theory_var)
|
||||
add_th_diseq(id, v1, v2, p->get_expr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void egraph::set_th_propagates_diseqs(theory_id id) {
|
||||
m_th_propagates_diseqs.reserve(id + 1, false);
|
||||
m_th_propagates_diseqs[id] = true;
|
||||
}
|
||||
|
||||
bool egraph::th_propagates_diseqs(theory_id id) const {
|
||||
return m_th_propagates_diseqs.get(id, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void egraph::add_th_var(enode* n, theory_var v, theory_id id) {
|
||||
force_push();
|
||||
theory_var w = n->get_th_var(id);
|
||||
|
@ -159,10 +221,12 @@ namespace euf {
|
|||
m_updates.push_back(update_record(n, id, update_record::add_th_var()));
|
||||
if (r != n) {
|
||||
theory_var u = r->get_th_var(id);
|
||||
if (u == null_theory_var)
|
||||
if (u == null_theory_var) {
|
||||
r->add_th_var(v, id, m_region);
|
||||
else
|
||||
add_th_eq(id, v, u, n, r);
|
||||
add_th_diseqs(id, v, r);
|
||||
}
|
||||
else
|
||||
add_th_eq(id, v, u, n, r);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -201,10 +265,12 @@ namespace euf {
|
|||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
unsigned old_lim = m_scopes.size() - num_scopes;
|
||||
unsigned num_updates = m_scopes[old_lim];
|
||||
auto undo_node = [&](enode* n) {
|
||||
if (n->num_args() > 1)
|
||||
auto undo_node = [&]() {
|
||||
enode* n = m_nodes.back();
|
||||
expr* e = m_exprs.back();
|
||||
if (n->num_args() > 0)
|
||||
m_table.erase(n);
|
||||
m_expr2enode[n->get_expr_id()] = nullptr;
|
||||
m_expr2enode[e->get_id()] = nullptr;
|
||||
n->~enode();
|
||||
m_nodes.pop_back();
|
||||
m_exprs.pop_back();
|
||||
|
@ -213,7 +279,7 @@ namespace euf {
|
|||
auto const& p = m_updates[i];
|
||||
switch (p.tag) {
|
||||
case update_record::tag_t::is_add_node:
|
||||
undo_node(p.r1);
|
||||
undo_node();
|
||||
break;
|
||||
case update_record::tag_t::is_toggle_merge:
|
||||
p.r1->set_merge_enabled(!p.r1->merge_enabled());
|
||||
|
@ -257,13 +323,13 @@ namespace euf {
|
|||
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
|
||||
}
|
||||
|
||||
void egraph::merge(enode* n1, enode* n2, justification j) {
|
||||
void egraph::merge(enode* n1, enode* n2, justification j) {
|
||||
SASSERT(m.get_sort(n1->get_expr()) == m.get_sort(n2->get_expr()));
|
||||
enode* r1 = n1->get_root();
|
||||
enode* r2 = n2->get_root();
|
||||
if (r1 == r2)
|
||||
return;
|
||||
TRACE("euf", j.display(tout << n1->get_expr_id() << " == " << n2->get_expr_id() << " ", m_display_justification) << "\n";);
|
||||
TRACE("euf", j.display(tout << "merge: " << mk_bounded_pp(n1->get_expr(), m) << " == " << mk_bounded_pp(n2->get_expr(), m) << " ", m_display_justification) << "\n";);
|
||||
force_push();
|
||||
SASSERT(m_num_scopes == 0);
|
||||
++m_stats.m_num_merge;
|
||||
|
@ -275,9 +341,10 @@ namespace euf {
|
|||
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_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))
|
||||
|
@ -301,6 +368,7 @@ namespace euf {
|
|||
if (v == null_theory_var) {
|
||||
root->add_th_var(iv.get_var(), id, m_region);
|
||||
m_updates.push_back(update_record(root, id, update_record::add_th_var()));
|
||||
add_th_diseqs(id, iv.get_var(), root);
|
||||
}
|
||||
else {
|
||||
SASSERT(v != iv.get_var());
|
||||
|
@ -378,6 +446,25 @@ namespace euf {
|
|||
SASSERT(!n1->get_root()->m_target);
|
||||
}
|
||||
|
||||
bool egraph::are_diseq(enode* a, enode* b) const {
|
||||
enode* ra = a->get_root(), * rb = b->get_root();
|
||||
if (ra == rb)
|
||||
return false;
|
||||
if (ra->interpreted() && rb->interpreted())
|
||||
return true;
|
||||
if (m.get_sort(ra->get_expr()) != m.get_sort(rb->get_expr()))
|
||||
return true;
|
||||
expr_ref eq(m.mk_eq(a->get_expr(), b->get_expr()), m);
|
||||
m_tmp_eq->m_args[0] = a;
|
||||
m_tmp_eq->m_args[1] = b;
|
||||
m_tmp_eq->m_expr = eq;
|
||||
SASSERT(m_tmp_eq->num_args() == 2);
|
||||
enode* r = m_table.find(m_tmp_eq);
|
||||
if (r && m_value(r->get_root()) == l_false)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief generate an explanation for a congruence.
|
||||
Each pair of children under a congruence have the same roots
|
||||
|
@ -529,9 +616,10 @@ namespace euf {
|
|||
void egraph::collect_statistics(statistics& st) const {
|
||||
st.update("euf merge", m_stats.m_num_merge);
|
||||
st.update("euf conflicts", m_stats.m_num_conflicts);
|
||||
st.update("euf equality propagations", m_stats.m_num_eqs);
|
||||
st.update("euf theory equality propagations", m_stats.m_num_th_eqs);
|
||||
st.update("euf literal propagations", m_stats.m_num_lits);
|
||||
st.update("euf propagations eqs", m_stats.m_num_eqs);
|
||||
st.update("euf propagations theory eqs", m_stats.m_num_th_eqs);
|
||||
st.update("euf propagations theory diseqs", m_stats.m_num_th_diseqs);
|
||||
st.update("euf propagations literal", m_stats.m_num_lits);
|
||||
}
|
||||
|
||||
void egraph::copy_from(egraph const& src, std::function<void*(void*)>& copy_justification) {
|
||||
|
|
|
@ -26,26 +26,47 @@ Notes:
|
|||
#pragma once
|
||||
#include "util/statistics.h"
|
||||
#include "util/trail.h"
|
||||
#include "util/lbool.h"
|
||||
#include "ast/euf/euf_enode.h"
|
||||
#include "ast/euf/euf_etable.h"
|
||||
|
||||
namespace euf {
|
||||
|
||||
/***
|
||||
\brief store derived theory equalities.
|
||||
Theory 'id' is notified with the equality of theory variables v1, v2
|
||||
that are merged into the common root of child and root (their roots may
|
||||
\brief store derived theory equalities and disequalities
|
||||
Theory 'id' is notified with the equality/disequality of theory variables v1, v2.
|
||||
For equalities, v1 and v2 are merged into the common root of child and root (their roots may
|
||||
have been updated since the equality was derived, but the explanation for
|
||||
v1 == v2 is provided by explaining the equality child == root.
|
||||
For disequalities, m_child refers to an equality atom of the form e1 == e2.
|
||||
It is equal to false under the current context.
|
||||
The explanation for the disequality v1 != v2 is derived from explaining the
|
||||
equality between the expression for v1 and e1, and the expression for v2 and e2
|
||||
and the equality of m_eq and false: the literal corresponding to m_eq is false in the
|
||||
current assignment stack, or m_child is congruent to false in the egraph.
|
||||
*/
|
||||
struct th_eq {
|
||||
class th_eq {
|
||||
|
||||
theory_id m_id;
|
||||
theory_var m_v1;
|
||||
theory_var m_v2;
|
||||
enode* m_child;
|
||||
union {
|
||||
enode* m_child;
|
||||
expr* m_eq;
|
||||
};
|
||||
enode* m_root;
|
||||
public:
|
||||
bool is_eq() const { return m_root != nullptr; }
|
||||
theory_id id() const { return m_id; }
|
||||
theory_var v1() const { return m_v1; }
|
||||
theory_var v2() const { return m_v2; }
|
||||
enode* child() const { SASSERT(is_eq()); return m_child; }
|
||||
enode* root() const { SASSERT(is_eq()); return m_root; }
|
||||
expr* eq() const { SASSERT(!is_eq()); return m_eq; }
|
||||
th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r) :
|
||||
m_id(id), m_v1(v1), m_v2(v2), m_child(c), m_root(r) {}
|
||||
th_eq(theory_id id, theory_var v1, theory_var v2, expr* eq) :
|
||||
m_id(id), m_v1(v1), m_v2(v2), m_eq(eq), m_root(nullptr) {}
|
||||
};
|
||||
|
||||
class egraph {
|
||||
|
@ -53,6 +74,7 @@ namespace euf {
|
|||
struct stats {
|
||||
unsigned m_num_merge;
|
||||
unsigned m_num_th_eqs;
|
||||
unsigned m_num_th_diseqs;
|
||||
unsigned m_num_lits;
|
||||
unsigned m_num_eqs;
|
||||
unsigned m_num_conflicts;
|
||||
|
@ -111,6 +133,7 @@ namespace euf {
|
|||
svector<update_record> m_updates;
|
||||
unsigned_vector m_scopes;
|
||||
enode_vector m_expr2enode;
|
||||
enode* m_tmp_eq { nullptr };
|
||||
enode_vector m_nodes;
|
||||
expr_ref_vector m_exprs;
|
||||
unsigned m_num_scopes { 0 };
|
||||
|
@ -122,11 +145,13 @@ namespace euf {
|
|||
unsigned m_new_th_eqs_qhead { 0 };
|
||||
svector<enode_bool_pair> m_new_lits;
|
||||
svector<th_eq> m_new_th_eqs;
|
||||
bool_vector m_th_propagates_diseqs;
|
||||
enode_vector m_todo;
|
||||
stats m_stats;
|
||||
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));
|
||||
|
@ -134,6 +159,10 @@ namespace euf {
|
|||
void push_node(enode* n) { m_updates.push_back(update_record(n)); }
|
||||
|
||||
void add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r);
|
||||
|
||||
void new_diseq(enode* n1);
|
||||
void add_th_diseqs(theory_id id, theory_var v1, enode* r);
|
||||
bool th_propagates_diseqs(theory_id id) const;
|
||||
void add_literal(enode* n, bool is_eq);
|
||||
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
|
||||
void undo_add_th_var(enode* n, theory_id id);
|
||||
|
@ -166,7 +195,7 @@ namespace euf {
|
|||
std::ostream& display(std::ostream& out, unsigned max_args, enode* n) const;
|
||||
|
||||
public:
|
||||
egraph(ast_manager& m): m(m), m_table(m), m_exprs(m) {}
|
||||
egraph(ast_manager& m);
|
||||
~egraph();
|
||||
enode* find(expr* f) { return m_expr2enode.get(f->get_id(), nullptr); }
|
||||
enode* mk(expr* f, unsigned n, enode *const* args);
|
||||
|
@ -192,12 +221,18 @@ namespace euf {
|
|||
bool propagate();
|
||||
bool inconsistent() const { return m_inconsistent; }
|
||||
|
||||
/**
|
||||
* \brief check if two nodes are known to be disequal.
|
||||
*/
|
||||
bool are_diseq(enode* a, enode* b) const;
|
||||
|
||||
/**
|
||||
\brief Maintain and update cursor into propagated consequences.
|
||||
The result of get_literal() is a pair (n, is_eq)
|
||||
where \c n is an enode and \c is_eq indicates whether the enode
|
||||
is an equality consequence.
|
||||
*/
|
||||
void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq);
|
||||
bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); }
|
||||
bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); }
|
||||
enode_bool_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; }
|
||||
|
@ -205,13 +240,14 @@ namespace euf {
|
|||
void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; }
|
||||
void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; }
|
||||
|
||||
|
||||
void add_th_var(enode* n, theory_var v, theory_id id);
|
||||
void set_th_propagates_diseqs(theory_id id);
|
||||
void set_merge_enabled(enode* n, bool enable_merge);
|
||||
|
||||
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
|
||||
void set_used_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();
|
||||
|
|
|
@ -70,6 +70,21 @@ namespace euf {
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the theory var that that is heuristically
|
||||
* linked with the fewest equality justifications.
|
||||
*/
|
||||
theory_var enode::get_closest_th_var(theory_id id) const {
|
||||
enode const* n = this;
|
||||
while (n) {
|
||||
theory_var v = n->get_th_var(id);
|
||||
if (v != null_theory_var)
|
||||
return v;
|
||||
n = n->m_target;
|
||||
}
|
||||
return null_theory_var;
|
||||
}
|
||||
|
||||
bool enode::acyclic() const {
|
||||
enode const* n = this;
|
||||
enode const* p = this;
|
||||
|
|
|
@ -36,23 +36,23 @@ namespace euf {
|
|||
const theory_id null_theory_id = -1;
|
||||
|
||||
class enode {
|
||||
expr* m_expr{ nullptr };
|
||||
bool m_mark1 { false };
|
||||
bool m_mark2 { false };
|
||||
bool m_commutative { false };
|
||||
bool m_update_children { false };
|
||||
bool m_interpreted { false };
|
||||
bool m_merge_enabled { true };
|
||||
unsigned m_class_size { 1 };
|
||||
unsigned m_table_id { UINT_MAX };
|
||||
expr* m_expr{ nullptr };
|
||||
bool m_mark1{ false };
|
||||
bool m_mark2{ false };
|
||||
bool m_commutative{ false };
|
||||
bool m_update_children{ false };
|
||||
bool m_interpreted{ false };
|
||||
bool m_merge_enabled{ true };
|
||||
unsigned m_class_size{ 1 };
|
||||
unsigned m_table_id{ UINT_MAX };
|
||||
enode_vector m_parents;
|
||||
enode* m_next{ nullptr };
|
||||
enode* m_root{ nullptr };
|
||||
enode* m_target { nullptr };
|
||||
enode* m_next{ nullptr };
|
||||
enode* m_root{ nullptr };
|
||||
enode* m_target{ nullptr };
|
||||
th_var_list m_th_vars;
|
||||
justification m_justification;
|
||||
unsigned m_num_args { 0 };
|
||||
enode* m_args[0];
|
||||
unsigned m_num_args{ 0 };
|
||||
enode* m_args[0];
|
||||
|
||||
friend class enode_args;
|
||||
friend class enode_parents;
|
||||
|
@ -81,6 +81,20 @@ namespace euf {
|
|||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static enode* mk_tmp(region& r, unsigned num_args) {
|
||||
void* mem = r.allocate(get_enode_size(num_args));
|
||||
enode* n = new (mem) enode();
|
||||
n->m_expr = nullptr;
|
||||
n->m_next = n;
|
||||
n->m_root = n;
|
||||
n->m_commutative = true;
|
||||
n->m_num_args = 2;
|
||||
n->m_merge_enabled = true;
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
n->m_args[i] = nullptr;
|
||||
return n;
|
||||
}
|
||||
|
||||
void set_update_children() { m_update_children = true; }
|
||||
|
||||
|
@ -148,10 +162,12 @@ namespace euf {
|
|||
unsigned get_expr_id() const { return m_expr->get_id(); }
|
||||
unsigned get_root_id() const { return m_root->m_expr->get_id(); }
|
||||
theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); }
|
||||
theory_var get_closest_th_var(theory_id id) const;
|
||||
bool is_attached_to(theory_id id) const { return get_th_var(id) != null_theory_var; }
|
||||
bool has_th_vars() const { return !m_th_vars.empty(); }
|
||||
bool has_one_th_var() const { return !m_th_vars.empty() && !m_th_vars.get_next();}
|
||||
theory_var get_first_th_id() const { SASSERT(has_th_vars()); return m_th_vars.get_id(); }
|
||||
|
||||
|
||||
void inc_class_size(unsigned n) { m_class_size += n; }
|
||||
void dec_class_size(unsigned n) { m_class_size -= n; }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue