mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
arrays (#4684)
* arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fill Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * update drat and fix euf bugs Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * const qualifiers Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorg ba Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorg Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * build warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
d56dd1db7b
commit
796e2fd9eb
79 changed files with 2571 additions and 1850 deletions
|
@ -1785,13 +1785,12 @@ bool ast_manager::slow_not_contains(ast const * n) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static unsigned s_count = 0;
|
||||
#if 0
|
||||
static void track_id(ast_manager& m, ast* n, unsigned id) {
|
||||
if (n->get_id() != id) return;
|
||||
++s_count;
|
||||
std::cout << &m << " " << s_count << "\n";
|
||||
SASSERT(s_count != 240);
|
||||
TRACE("ast", tout << s_count << "\n";);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1825,9 +1824,9 @@ 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, 254);
|
||||
// track_id(*this, n, 3);
|
||||
|
||||
TRACE("ast", tout << "Object " << n->m_id << " was created.\n";);
|
||||
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
|
||||
switch (n->get_kind()) {
|
||||
|
|
|
@ -81,8 +81,10 @@ namespace euf {
|
|||
m_scopes.push_back(m_updates.size());
|
||||
m_region.push_scope();
|
||||
}
|
||||
m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead()));
|
||||
m_updates.push_back(update_record(m_new_th_eqs_qhead, update_record::new_th_eq_qhead()));
|
||||
m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead()));
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
|
||||
}
|
||||
|
||||
void egraph::update_children(enode* n) {
|
||||
|
@ -107,17 +109,23 @@ namespace euf {
|
|||
return n;
|
||||
}
|
||||
enode_bool_pair p = m_table.insert(n);
|
||||
enode* r = p.first;
|
||||
if (r == n) {
|
||||
enode* n2 = p.first;
|
||||
if (n2 == n) {
|
||||
update_children(n);
|
||||
}
|
||||
else {
|
||||
SASSERT(r->get_expr() != n->get_expr());
|
||||
merge_justification(n, r, justification::congruence(p.second));
|
||||
std::swap(n->m_next, r->m_next);
|
||||
n->m_root = r;
|
||||
r->inc_class_size(n->class_size());
|
||||
push_eq(n, n, r->num_parents());
|
||||
merge(n, n2, justification::congruence(p.second));
|
||||
#if 0
|
||||
SASSERT(n2->get_expr() != n->get_expr());
|
||||
SASSERT(n->class_size() == 1);
|
||||
SASSERT(n->is_root());
|
||||
merge_justification(n, n2, justification::congruence(p.second));
|
||||
enode* r2 = n2->get_root();
|
||||
std::swap(n->m_next, r2->m_next);
|
||||
n->m_root = r2;
|
||||
r2->inc_class_size(n->class_size());
|
||||
push_eq(n, n, r2->num_parents());
|
||||
#endif
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
@ -128,12 +136,14 @@ namespace euf {
|
|||
}
|
||||
|
||||
void egraph::add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r) {
|
||||
TRACE("euf_verbose", tout << "eq: " << v1 << " == " << v2 << "\n";);
|
||||
m_new_th_eqs.push_back(th_eq(id, v1, v2, c, r));
|
||||
m_updates.push_back(update_record(update_record::new_th_eq()));
|
||||
++m_stats.m_num_th_eqs;
|
||||
}
|
||||
|
||||
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));
|
||||
m_updates.push_back(update_record(update_record::new_lit()));
|
||||
if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
|
||||
|
@ -186,6 +196,9 @@ namespace euf {
|
|||
return;
|
||||
}
|
||||
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;
|
||||
unsigned num_updates = m_scopes[old_lim];
|
||||
auto undo_node = [&](enode* n) {
|
||||
|
@ -240,16 +253,19 @@ namespace euf {
|
|||
m_scopes.shrink(old_lim);
|
||||
m_region.pop_scope(num_scopes);
|
||||
m_worklist.reset();
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
|
||||
}
|
||||
|
||||
void egraph::merge(enode* n1, enode* n2, justification j) {
|
||||
void egraph::merge(enode* n1, enode* n2, justification j) {
|
||||
SASSERT(m.get_sort(n1->get_expr()) == m.get_sort(n2->get_expr()));
|
||||
TRACE("euf", tout << n1->get_expr_id() << " == " << n2->get_expr_id() << "\n";);
|
||||
force_push();
|
||||
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";);
|
||||
force_push();
|
||||
SASSERT(m_num_scopes == 0);
|
||||
++m_stats.m_num_merge;
|
||||
if (r1->interpreted() && r2->interpreted()) {
|
||||
set_conflict(n1, n2, j);
|
||||
|
@ -294,6 +310,7 @@ namespace euf {
|
|||
}
|
||||
|
||||
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();
|
||||
while (head < tail && m.limit().inc() && !inconsistent()) {
|
||||
|
@ -311,6 +328,7 @@ namespace euf {
|
|||
tail = m_worklist.size();
|
||||
}
|
||||
m_worklist.reset();
|
||||
force_push();
|
||||
return
|
||||
(m_new_lits_qhead < m_new_lits.size()) ||
|
||||
(m_new_th_eqs_qhead < m_new_th_eqs.size()) ||
|
||||
|
@ -329,17 +347,27 @@ namespace euf {
|
|||
}
|
||||
|
||||
void egraph::merge_justification(enode* n1, enode* n2, justification j) {
|
||||
SASSERT(!n1->get_root()->m_target);
|
||||
SASSERT(!n2->get_root()->m_target);
|
||||
SASSERT(n1->reaches(n1->get_root()));
|
||||
SASSERT(!n2->reaches(n1->get_root()));
|
||||
SASSERT(!n2->reaches(n1));
|
||||
n1->reverse_justification();
|
||||
n1->m_target = n2;
|
||||
n1->m_justification = j;
|
||||
SASSERT(n1->acyclic());
|
||||
SASSERT(n2->acyclic());
|
||||
SASSERT(n1->get_root()->reaches(n1));
|
||||
SASSERT(!n2->get_root()->m_target);
|
||||
TRACE("euf_verbose", tout << "merge " << n1->get_expr_id() << " " << n2->get_expr_id() << " updates: " << m_updates.size() << "\n";);
|
||||
}
|
||||
|
||||
void egraph::unmerge_justification(enode* n1) {
|
||||
TRACE("euf_verbose", tout << "unmerge " << n1->get_expr_id() << " " << n1->m_target->get_expr_id() << "\n";);
|
||||
// r1 -> .. -> n1 -> n2 -> ... -> r2
|
||||
// where n2 = n1->m_target
|
||||
SASSERT(n1->get_root()->reaches(n1));
|
||||
SASSERT(n1->m_target);
|
||||
n1->m_target = nullptr;
|
||||
n1->m_justification = justification::axiom();
|
||||
n1->get_root()->reverse_justification();
|
||||
|
@ -347,7 +375,7 @@ namespace euf {
|
|||
// n1 -> ... -> r1
|
||||
// n2 -> ... -> r2
|
||||
SASSERT(n1->reaches(n1->get_root()));
|
||||
SASSERT(n1->get_root()->m_target == nullptr);
|
||||
SASSERT(!n1->get_root()->m_target);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -359,8 +387,9 @@ namespace euf {
|
|||
void egraph::push_congruence(enode* n1, enode* n2, bool comm) {
|
||||
SASSERT(is_app(n1->get_expr()));
|
||||
SASSERT(n1->get_decl() == n2->get_decl());
|
||||
if (m_used_cc)
|
||||
if (m_used_cc && !comm) {
|
||||
m_used_cc(to_app(n1->get_expr()), to_app(n2->get_expr()));
|
||||
}
|
||||
if (comm &&
|
||||
n1->get_arg(0)->get_root() == n2->get_arg(1)->get_root() &&
|
||||
n1->get_arg(1)->get_root() == n2->get_arg(0)->get_root()) {
|
||||
|
@ -409,6 +438,7 @@ namespace euf {
|
|||
void egraph::end_explain() {
|
||||
for (enode* n : m_todo)
|
||||
n->unmark1();
|
||||
DEBUG_CODE(for (enode* n : m_nodes) SASSERT(!n->is_marked1()););
|
||||
m_todo.reset();
|
||||
}
|
||||
|
||||
|
@ -424,7 +454,12 @@ namespace euf {
|
|||
template <typename T>
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) {
|
||||
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";);
|
||||
push_to_lca(a, lca);
|
||||
push_to_lca(b, lca);
|
||||
if (m_used_eq)
|
||||
|
@ -438,6 +473,7 @@ namespace euf {
|
|||
enode* n = m_todo[i];
|
||||
if (n->m_target && !n->is_marked1()) {
|
||||
n->mark1();
|
||||
CTRACE("euf", m_display_justification, n->m_justification.display(tout << n->get_expr_id() << " = " << n->m_target->get_expr_id() << " ", m_display_justification) << "\n";);
|
||||
explain_eq(justifications, n, n->m_target, n->m_justification);
|
||||
}
|
||||
}
|
||||
|
@ -461,21 +497,26 @@ namespace euf {
|
|||
out << "v:" << f->get_id();
|
||||
out << "\n";
|
||||
if (!n->m_parents.empty()) {
|
||||
out << " ";
|
||||
out << " parents ";
|
||||
for (enode* p : enode_parents(n))
|
||||
out << p->get_expr_id() << " ";
|
||||
out << "\n";
|
||||
}
|
||||
if (n->has_th_vars()) {
|
||||
out << " ";
|
||||
out << " theories ";
|
||||
for (auto v : enode_th_vars(n))
|
||||
out << v.get_id() << ":" << v.get_var() << " ";
|
||||
out << "\n";
|
||||
}
|
||||
if (n->m_target && m_display_justification)
|
||||
n->m_justification.display(out << " = " << n->m_target->get_expr_id() << " j: ", m_display_justification) << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& egraph::display(std::ostream& out) const {
|
||||
out << "updates " << m_updates.size() << "\n";
|
||||
out << "newlits " << m_new_lits.size() << " qhead: " << m_new_lits_qhead << "\n";
|
||||
out << "neweqs " << m_new_th_eqs.size() << " qhead: " << m_new_th_eqs_qhead << "\n";
|
||||
m_table.display(out);
|
||||
unsigned max_args = 0;
|
||||
for (enode* n : m_nodes)
|
||||
|
|
|
@ -125,7 +125,8 @@ namespace euf {
|
|||
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(app*,app*)> m_used_cc;
|
||||
std::function<void(std::ostream&, void*)> m_display_justification;
|
||||
|
||||
void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
|
||||
m_updates.push_back(update_record(r1, n1, r2_num_parents));
|
||||
|
@ -151,6 +152,7 @@ namespace euf {
|
|||
void push_to_lca(enode* a, enode* lca);
|
||||
void push_congruence(enode* n1, enode* n2, bool commutative);
|
||||
void push_todo(enode* n);
|
||||
|
||||
template <typename T>
|
||||
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, justification const& j) {
|
||||
if (j.is_external())
|
||||
|
@ -200,8 +202,8 @@ namespace euf {
|
|||
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]; }
|
||||
th_eq get_th_eq() const { return m_new_th_eqs[m_new_th_eqs_qhead]; }
|
||||
void next_literal() { SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; }
|
||||
void next_th_eq() { SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; }
|
||||
void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; }
|
||||
void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; }
|
||||
|
||||
|
||||
void add_th_var(enode* n, theory_var v, theory_id id);
|
||||
|
@ -209,7 +211,8 @@ namespace euf {
|
|||
|
||||
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 begin_explain();
|
||||
void end_explain();
|
||||
template <typename T>
|
||||
|
@ -228,6 +231,8 @@ namespace euf {
|
|||
e_pp pp(enode* n) const { return e_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; }
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, egraph const& g) { return g.display(out); }
|
||||
|
|
|
@ -31,7 +31,8 @@ namespace euf {
|
|||
VERIFY(found_root);
|
||||
VERIFY(found_this);
|
||||
VERIFY(this != m_root || class_size == m_class_size);
|
||||
if (this == m_root) {
|
||||
if (is_root()) {
|
||||
VERIFY(!m_target);
|
||||
for (enode* p : enode_parents(this)) {
|
||||
bool found = false;
|
||||
for (enode* arg : enode_args(p)) {
|
||||
|
@ -69,8 +70,24 @@ namespace euf {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool enode::acyclic() const {
|
||||
enode const* n = this;
|
||||
enode const* p = this;
|
||||
while (n) {
|
||||
n = n->m_target;
|
||||
if (n) {
|
||||
p = p->m_target;
|
||||
n = n->m_target;
|
||||
}
|
||||
if (n == p)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool enode::reaches(enode* n) const {
|
||||
enode const* r = this;
|
||||
SASSERT(acyclic());
|
||||
while (r) {
|
||||
if (r == n)
|
||||
return true;
|
||||
|
|
|
@ -156,6 +156,7 @@ namespace euf {
|
|||
|
||||
void reverse_justification();
|
||||
bool reaches(enode* n) const;
|
||||
bool acyclic() const;
|
||||
|
||||
enode* const* begin_parents() const { return m_parents.begin(); }
|
||||
enode* const* end_parents() const { return m_parents.end(); }
|
||||
|
|
|
@ -215,8 +215,6 @@ namespace euf {
|
|||
return enode_bool_pair(n_prime, false);
|
||||
case BINARY:
|
||||
n_prime = UNTAG(binary_table*, t)->insert_if_not_there(n);
|
||||
TRACE("euf", tout << "insert: " << n->get_expr_id() << " " << cg_binary_hash()(n) << " inserted: " << (n == n_prime) << " " << n_prime->get_expr_id() << "\n";
|
||||
display_binary(tout, t); tout << "contains_ptr: " << contains_ptr(n) << "\n";);
|
||||
return enode_bool_pair(n_prime, false);
|
||||
case BINARY_COMM:
|
||||
m_commutativity = false;
|
||||
|
@ -236,7 +234,6 @@ namespace euf {
|
|||
UNTAG(unary_table*, t)->erase(n);
|
||||
break;
|
||||
case BINARY:
|
||||
TRACE("euf", tout << "erase: " << n->get_expr_id() << " " << cg_binary_hash()(n) << " contains: " << contains_ptr(n) << "\n";);
|
||||
UNTAG(binary_table*, t)->erase(n);
|
||||
break;
|
||||
case BINARY_COMM:
|
||||
|
|
|
@ -20,7 +20,7 @@ Author:
|
|||
namespace euf {
|
||||
|
||||
class justification {
|
||||
enum kind_t {
|
||||
enum class kind_t {
|
||||
axiom_t,
|
||||
congruence_t,
|
||||
external_t
|
||||
|
@ -29,20 +29,20 @@ namespace euf {
|
|||
bool m_comm;
|
||||
void* m_external;
|
||||
justification(bool comm):
|
||||
m_kind(congruence_t),
|
||||
m_kind(kind_t::congruence_t),
|
||||
m_comm(comm),
|
||||
m_external(nullptr)
|
||||
{}
|
||||
|
||||
justification(void* ext):
|
||||
m_kind(external_t),
|
||||
m_kind(kind_t::external_t),
|
||||
m_comm(false),
|
||||
m_external(ext)
|
||||
{}
|
||||
|
||||
public:
|
||||
justification():
|
||||
m_kind(axiom_t),
|
||||
m_kind(kind_t::axiom_t),
|
||||
m_comm(false),
|
||||
m_external(nullptr)
|
||||
{}
|
||||
|
@ -51,24 +51,43 @@ namespace euf {
|
|||
static justification congruence(bool c) { return justification(c); }
|
||||
static justification external(void* ext) { return justification(ext); }
|
||||
|
||||
bool is_external() const { return m_kind == external_t; }
|
||||
bool is_congruence() const { return m_kind == congruence_t; }
|
||||
bool is_external() const { return m_kind == kind_t::external_t; }
|
||||
bool is_congruence() const { return m_kind == kind_t::congruence_t; }
|
||||
bool is_commutative() const { return m_comm; }
|
||||
template <typename T>
|
||||
T* ext() const { SASSERT(is_external()); return static_cast<T*>(m_external); }
|
||||
|
||||
justification copy(std::function<void*(void*)>& copy_justification) const {
|
||||
switch (m_kind) {
|
||||
case external_t:
|
||||
case kind_t::external_t:
|
||||
return external(copy_justification(m_external));
|
||||
case axiom_t:
|
||||
case kind_t::axiom_t:
|
||||
return axiom();
|
||||
case congruence_t:
|
||||
case kind_t::congruence_t:
|
||||
return congruence(m_comm);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return axiom();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& display(std::ostream& out, std::function<void (std::ostream&, void*)> const& ext) const {
|
||||
switch (m_kind) {
|
||||
case kind_t::external_t:
|
||||
if (ext)
|
||||
ext(out, m_external);
|
||||
else
|
||||
out << "external";
|
||||
return out;
|
||||
case kind_t::axiom_t:
|
||||
return out << "axiom";
|
||||
case kind_t::congruence_t:
|
||||
return out << "congruence";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue