3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-06 14:13:23 +00:00

prepare for subsumption

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2023-11-14 10:56:01 -08:00
parent 616d00409f
commit d5315e2283
2 changed files with 132 additions and 67 deletions

View file

@ -48,6 +48,9 @@ TODOs:
- For example, rules are currently not re-oriented if there is a merge. The rules could be re-oriented during propagation. - For example, rules are currently not re-oriented if there is a merge. The rules could be re-oriented during propagation.
- Elimination of redundant rules. - Elimination of redundant rules.
- superposition uses a stop-gap to avoid some steps. It should be revisited. - superposition uses a stop-gap to avoid some steps. It should be revisited.
-> forward and backward subsumption
- apply forward subsumption when simplifying equality using processed
- apply backward subsumption when simplifying processed and to_simplify
- Efficiency of handling shared terms. - Efficiency of handling shared terms.
- The shared terms hash table is not incremental. - The shared terms hash table is not incremental.
It could be made incremental by updating it on every merge similar to how the egraph handles it. It could be made incremental by updating it on every merge similar to how the egraph handles it.
@ -91,9 +94,9 @@ namespace euf {
case is_add_eq: { case is_add_eq: {
auto const& eq = m_eqs.back(); auto const& eq = m_eqs.back();
for (auto* n : monomial(eq.l)) for (auto* n : monomial(eq.l))
n->lhs.pop_back(); n->eqs.pop_back();
for (auto* n : monomial(eq.r)) for (auto* n : monomial(eq.r))
n->rhs.pop_back(); n->eqs.pop_back();
m_eqs.pop_back(); m_eqs.pop_back();
break; break;
} }
@ -109,13 +112,13 @@ namespace euf {
break; break;
} }
case is_merge_node: { case is_merge_node: {
auto [other, old_shared, old_lhs, old_rhs] = m_merge_trail.back(); auto [other, old_shared, old_eqs] = m_merge_trail.back();
auto* root = other->root; auto* root = other->root;
std::swap(other->next, root->next); std::swap(other->next, root->next);
root->shared.shrink(old_shared); root->shared.shrink(old_shared);
root->lhs.shrink(old_lhs); root->eqs.shrink(old_eqs);
root->rhs.shrink(old_rhs);
m_merge_trail.pop_back(); m_merge_trail.pop_back();
++m_tick;
break; break;
} }
case is_update_eq: { case is_update_eq: {
@ -176,16 +179,17 @@ namespace euf {
for (auto n : m_nodes) { for (auto n : m_nodes) {
if (!n) if (!n)
continue; continue;
out << g.bpp(n->n) << " r: " << n->root_id() << " - "; out << g.bpp(n->n) << " r: " << n->root_id() << " ";
out << "lhs "; if (!n->eqs.empty()) {
for (auto l : n->lhs) out << "eqs ";
out << l << " "; for (auto l : n->eqs)
out << "rhs "; out << l << " ";
for (auto r : n->rhs) }
out << r << " "; if (!n->shared.empty()) {
out << "shared "; out << "shared ";
for (auto s : n->shared) for (auto s : n->shared)
out << s << " "; out << s << " ";
}
out << "\n"; out << "\n";
} }
return out; return out;
@ -197,7 +201,7 @@ namespace euf {
if (!is_op(l) && !is_op(r)) if (!is_op(l) && !is_op(r))
merge(mk_node(l), mk_node(r), j); merge(mk_node(l), mk_node(r), j);
else else
init_equation({ to_monomial(l), to_monomial(r), false, j }); init_equation(eq(to_monomial(l), to_monomial(r), j));
} }
void ac_plugin::init_equation(eq const& e) { void ac_plugin::init_equation(eq const& e) {
@ -207,9 +211,9 @@ namespace euf {
push_undo(is_add_eq); push_undo(is_add_eq);
unsigned eq_id = m_eqs.size() - 1; unsigned eq_id = m_eqs.size() - 1;
for (auto n : monomial(eq.l)) for (auto n : monomial(eq.l))
n->lhs.push_back(eq_id); n->eqs.push_back(eq_id);
for (auto n : monomial(eq.r)) for (auto n : monomial(eq.r))
n->rhs.push_back(eq_id); n->eqs.push_back(eq_id);
m_to_simplify_todo.insert(eq_id); m_to_simplify_todo.insert(eq_id);
} }
else else
@ -244,27 +248,46 @@ namespace euf {
} }
bool ac_plugin::is_sorted(monomial_t const& m) const { bool ac_plugin::is_sorted(monomial_t const& m) const {
if (m.m_filter.m_tick == m_tick)
return true;
for (unsigned i = m.size(); i-- > 1; ) for (unsigned i = m.size(); i-- > 1; )
if (m[i-1]->root_id() > m[i]->root_id()) if (m[i-1]->root_id() > m[i]->root_id())
return false; return false;
return true; return true;
} }
uint64_t ac_plugin::filter(monomial_t& m) {
auto& filter = m.m_filter;
if (filter.m_tick == m_tick)
return filter.m_filter;
filter.m_filter = 0;
for (auto n : m)
filter.m_filter |= (1ull << (n->root_id() % 64ull));
if (!is_sorted(m))
sort(m);
filter.m_tick = m_tick;
return filter.m_filter;
}
bool ac_plugin::can_be_subset(monomial_t& subset, monomial_t& superset) {
auto f1 = filter(subset);
auto f2 = filter(superset);
return (f1 | f2) == f2;
}
void ac_plugin::merge(node* root, node* other, justification j) { void ac_plugin::merge(node* root, node* other, justification j) {
for (auto n : equiv(other)) for (auto n : equiv(other))
n->root = root; n->root = root;
m_merge_trail.push_back({ other, root->shared.size(), root->lhs.size(), root->rhs.size()}); m_merge_trail.push_back({ other, root->shared.size(), root->eqs.size()});
for (auto eq_id : other->lhs) for (auto eq_id : other->eqs)
set_processed(eq_id, false);
for (auto eq_id : other->rhs)
set_processed(eq_id, false); set_processed(eq_id, false);
for (auto m : other->shared) for (auto m : other->shared)
m_shared_todo.insert(m); m_shared_todo.insert(m);
root->shared.append(other->shared); root->shared.append(other->shared);
root->lhs.append(other->lhs); root->eqs.append(other->eqs);
root->rhs.append(other->rhs);
std::swap(root->next, other->next); std::swap(root->next, other->next);
push_undo(is_merge_node); push_undo(is_merge_node);
++m_tick;
} }
void ac_plugin::push_undo(undo_kind k) { void ac_plugin::push_undo(undo_kind k) {
@ -276,7 +299,7 @@ namespace euf {
unsigned ac_plugin::to_monomial(enode* n) { unsigned ac_plugin::to_monomial(enode* n) {
enode_vector& ns = m_todo; enode_vector& ns = m_todo;
ns.reset(); ns.reset();
ptr_vector<node> ms; ptr_vector<node> m;
ns.push_back(n); ns.push_back(n);
for (unsigned i = 0; i < ns.size(); ++i) { for (unsigned i = 0; i < ns.size(); ++i) {
n = ns[i]; n = ns[i];
@ -287,15 +310,15 @@ namespace euf {
--i; --i;
} }
else { else {
ms.push_back(mk_node(n)); m.push_back(mk_node(n));
} }
} }
return to_monomial(n, ms); return to_monomial(n, m);
} }
unsigned ac_plugin::to_monomial(enode* e, monomial_t const& ms) { unsigned ac_plugin::to_monomial(enode* e, ptr_vector<node> const& ms) {
unsigned id = m_monomials.size(); unsigned id = m_monomials.size();
m_monomials.push_back(ms); m_monomials.push_back({ ms, bloom() });
push_undo(is_add_monomial); push_undo(is_add_monomial);
return id; return id;
} }
@ -333,7 +356,7 @@ namespace euf {
if (is_processed(other_eq)) if (is_processed(other_eq))
backward_simplify(eq_id, other_eq); backward_simplify(eq_id, other_eq);
if (m_backward_simplified) if (m_backward_simplified)
continue; continue;
// simplify processed using eq // simplify processed using eq
for (auto other_eq : forward_iterator(eq_id)) for (auto other_eq : forward_iterator(eq_id))
@ -384,10 +407,10 @@ namespace euf {
unsigned_vector const& ac_plugin::superpose_iterator(unsigned eq_id) { unsigned_vector const& ac_plugin::superpose_iterator(unsigned eq_id) {
auto const& eq = m_eqs[eq_id]; auto const& eq = m_eqs[eq_id];
m_src_r.reset(); m_src_r.reset();
m_src_r.append(monomial(eq.r)); m_src_r.append(monomial(eq.r).m_nodes);
init_ids_counts(monomial(eq.l), m_src_ids, m_src_count); init_ids_counts(monomial(eq.l), m_src_ids, m_src_count);
init_overlap_iterator(eq_id, monomial(eq.l)); init_overlap_iterator(eq_id, monomial(eq.l));
return m_lhs_eqs; return m_eq_occurs;
} }
// //
@ -399,28 +422,28 @@ namespace euf {
init_ids_counts(monomial(eq.r), m_dst_ids, m_dst_count); init_ids_counts(monomial(eq.r), m_dst_ids, m_dst_count);
init_overlap_iterator(eq_id, monomial(eq.r)); init_overlap_iterator(eq_id, monomial(eq.r));
m_backward_simplified = false; m_backward_simplified = false;
return m_lhs_eqs; return m_eq_occurs;
} }
void ac_plugin::init_overlap_iterator(unsigned eq_id, ptr_vector<node> const& m) { void ac_plugin::init_overlap_iterator(unsigned eq_id, monomial_t const& m) {
m_lhs_eqs.reset(); m_eq_occurs.reset();
for (auto n : m) for (auto n : m)
m_lhs_eqs.append(n->root->lhs); m_eq_occurs.append(n->root->eqs);
// prune m_lhs_eqs to single occurrences // prune m_eq_occurs to single occurrences
unsigned j = 0; unsigned j = 0;
for (unsigned i = 0; i < m_lhs_eqs.size(); ++i) { for (unsigned i = 0; i < m_eq_occurs.size(); ++i) {
unsigned id = m_lhs_eqs[i]; unsigned id = m_eq_occurs[i];
m_eq_seen.reserve(id + 1, false); m_eq_seen.reserve(id + 1, false);
if (m_eq_seen[id]) if (m_eq_seen[id])
continue; continue;
if (id == eq_id) if (id == eq_id)
continue; continue;
m_lhs_eqs[j++] = id; m_eq_occurs[j++] = id;
m_eq_seen[id] = true; m_eq_seen[id] = true;
} }
m_lhs_eqs.shrink(j); m_eq_occurs.shrink(j);
for (auto id : m_lhs_eqs) for (auto id : m_eq_occurs)
m_eq_seen[id] = false; m_eq_seen[id] = false;
} }
@ -430,19 +453,19 @@ namespace euf {
unsigned_vector const& ac_plugin::forward_iterator(unsigned eq_id) { unsigned_vector const& ac_plugin::forward_iterator(unsigned eq_id) {
auto& eq = m_eqs[eq_id]; auto& eq = m_eqs[eq_id];
m_src_r.reset(); m_src_r.reset();
m_src_r.append(monomial(eq.r)); m_src_r.append(monomial(eq.r).m_nodes);
init_ids_counts(monomial(eq.l), m_src_ids, m_src_count); init_ids_counts(monomial(eq.l), m_src_ids, m_src_count);
unsigned min_r = UINT_MAX; unsigned min_r = UINT_MAX;
node* min_n = nullptr; node* min_n = nullptr;
for (auto n : monomial(eq.l)) for (auto n : monomial(eq.l))
if (n->root->rhs.size() < min_r) if (n->root->eqs.size() < min_r)
min_n = n, min_r = n->root->rhs.size(); min_n = n, min_r = n->root->eqs.size();
// found node that occurs in fewest rhs // found node that occurs in fewest rhs
VERIFY(min_n); VERIFY(min_n);
return min_n->rhs; return min_n->eqs;
} }
void ac_plugin::init_ids_counts(ptr_vector<node> const& monomial, unsigned_vector& ids, unsigned_vector& counts) { void ac_plugin::init_ids_counts(monomial_t const& monomial, unsigned_vector& ids, unsigned_vector& counts) {
reset_ids_counts(ids, counts); reset_ids_counts(ids, counts);
for (auto n : monomial) { for (auto n : monomial) {
unsigned id = n->root_id(); unsigned id = n->root_id();
@ -469,6 +492,9 @@ namespace euf {
auto& src = m_eqs[src_eq]; auto& src = m_eqs[src_eq];
auto& dst = m_eqs[dst_eq]; auto& dst = m_eqs[dst_eq];
if (!can_be_subset(monomial(src.l), monomial(dst.r)))
return;
reset_ids_counts(m_dst_ids, m_dst_count); reset_ids_counts(m_dst_ids, m_dst_count);
unsigned src_l_size = monomial(src.l).size(); unsigned src_l_size = monomial(src.l).size();
@ -509,9 +535,11 @@ namespace euf {
auto& dst = m_eqs[dst_eq]; auto& dst = m_eqs[dst_eq];
// //
// dst_ids, dst_count contain rhs of dst_eq // dst_ids, dst_count contain rhs of dst_eq
// //
// check that src.l is a subset of dst.r // check that src.l is a subset of dst.r
if (!can_be_subset(monomial(src.l), monomial(dst.r)))
return;
if (!is_subset(monomial(src.l))) if (!is_subset(monomial(src.l)))
return; return;
@ -524,11 +552,24 @@ namespace euf {
m_backward_simplified = true; m_backward_simplified = true;
} }
bool ac_plugin::subsumes(unsigned src_eq, unsigned dst_eq) {
auto& src = m_eqs[src_eq];
auto& dst = m_eqs[dst_eq];
if (!can_be_subset(monomial(src.l), monomial(dst.l)))
return false;
if (!can_be_subset(monomial(src.r), monomial(dst.r)))
return false;
NOT_IMPLEMENTED_YET();
// dst.l \ src.l = dst.r \ dst.r
return false;
}
unsigned ac_plugin::rewrite(monomial_t const& src_r, monomial_t const& dst_r) { unsigned ac_plugin::rewrite(monomial_t const& src_r, monomial_t const& dst_r) {
// pre-condition: is-subset is invoked so that m_src_count is initialized. // pre-condition: is-subset is invoked so that m_src_count is initialized.
// pre-condition: m_dst_count is also initialized (once). // pre-condition: m_dst_count is also initialized (once).
m_src_r.reset(); m_src_r.reset();
m_src_r.append(src_r); m_src_r.append(src_r.m_nodes);
// add to m_src_r elements of dst.r that are not in src.l // add to m_src_r elements of dst.r that are not in src.l
for (auto n : dst_r) { for (auto n : dst_r) {
unsigned id = n->root_id(); unsigned id = n->root_id();
@ -571,7 +612,7 @@ namespace euf {
reset_ids_counts(m_dst_ids, m_dst_count); reset_ids_counts(m_dst_ids, m_dst_count);
m_dst_r.reset(); m_dst_r.reset();
m_dst_r.append(monomial(dst.r)); m_dst_r.append(monomial(dst.r).m_nodes);
unsigned src_r_size = m_src_r.size(); unsigned src_r_size = m_src_r.size();
unsigned dst_r_size = m_dst_r.size(); unsigned dst_r_size = m_dst_r.size();
SASSERT(src_r_size == monomial(src.r).size()); SASSERT(src_r_size == monomial(src.r).size());
@ -633,7 +674,7 @@ namespace euf {
if (m_src_r.size() == 1 && m_dst_r.size() == 1) if (m_src_r.size() == 1 && m_dst_r.size() == 1)
push_merge(m_src_r[0]->n, m_dst_r[0]->n, j); push_merge(m_src_r[0]->n, m_dst_r[0]->n, j);
else else
init_equation({ to_monomial(nullptr, m_src_r), to_monomial(nullptr, m_dst_r), false, j }); init_equation(eq(to_monomial(nullptr, m_src_r), to_monomial(nullptr, m_dst_r), j));
m_src_r.shrink(src_r_size); m_src_r.shrink(src_r_size);
} }
@ -670,11 +711,13 @@ namespace euf {
bool change = true; bool change = true;
while (change) { while (change) {
change = false; change = false;
auto const& m = monomial(s.m); auto & m = monomial(s.m);
init_ids_counts(m, m_dst_ids, m_dst_count); init_ids_counts(m, m_dst_ids, m_dst_count);
init_overlap_iterator(UINT_MAX, m); init_overlap_iterator(UINT_MAX, m);
for (auto eq : m_lhs_eqs) { for (auto eq : m_eq_occurs) {
auto& src = m_eqs[eq]; auto& src = m_eqs[eq];
if (!can_be_subset(monomial(src.l), m))
continue;
if (!is_subset(monomial(src.l))) if (!is_subset(monomial(src.l)))
continue; continue;
m_update_shared_trail.push_back({ idx, s }); m_update_shared_trail.push_back({ idx, s });

View file

@ -43,9 +43,8 @@ namespace euf {
justification j; // justification for equality justification j; // justification for equality
node* target = nullptr; // justified next node* target = nullptr; // justified next
unsigned_vector shared; // shared occurrences unsigned_vector shared; // shared occurrences
unsigned_vector lhs; // left hand side of equalities unsigned_vector eqs; // equality occurrences
unsigned_vector rhs; // left side of equalities
unsigned root_id() const { return root->n->get_id(); } unsigned root_id() const { return root->n->get_id(); }
~node() {} ~node() {}
static node* mk(region& r, enode* n); static node* mk(region& r, enode* n);
@ -71,10 +70,18 @@ namespace euf {
iterator end() const { return iterator(&n, &n); } iterator end() const { return iterator(&n, &n); }
}; };
struct bloom {
uint64_t m_tick = 0;
uint64_t m_filter = 0;
};
// represent equalities added by merge_eh and by superposition // represent equalities added by merge_eh and by superposition
struct eq { struct eq {
eq(unsigned l, unsigned r, justification j):
l(l), r(r), j(j) {}
unsigned l, r; // refer to monomials unsigned l, r; // refer to monomials
bool is_processed = false; // true if the equality is in the processed set bool is_processed = false; // true if the equality is in the processed set
bool is_alive = true;
justification j; // justification for equality justification j; // justification for equality
}; };
@ -85,7 +92,17 @@ namespace euf {
justification j; // justification for current simplification of monomial justification j; // justification for current simplification of monomial
}; };
using monomial_t = ptr_vector<node>; struct monomial_t {
ptr_vector<node> m_nodes;
bloom m_filter;
node* operator[](unsigned i) const { return m_nodes[i]; }
unsigned size() const { return m_nodes.size(); }
node* const* begin() const { return m_nodes.begin(); }
node* const* end() const { return m_nodes.end(); }
node* * begin() { return m_nodes.begin(); }
node* * end() { return m_nodes.end(); }
};
struct monomial_hash { struct monomial_hash {
ac_plugin& p; ac_plugin& p;
@ -124,6 +141,8 @@ namespace euf {
justification::dependency_manager m_dep_manager; justification::dependency_manager m_dep_manager;
tracked_uint_set m_to_simplify_todo; tracked_uint_set m_to_simplify_todo;
tracked_uint_set m_shared_todo; tracked_uint_set m_shared_todo;
uint64_t m_tick = 1;
monomial_hash m_hash; monomial_hash m_hash;
@ -146,7 +165,7 @@ namespace euf {
ptr_vector<node> m_node_trail; ptr_vector<node> m_node_trail;
svector<std::pair<unsigned, shared>> m_update_shared_trail; svector<std::pair<unsigned, shared>> m_update_shared_trail;
svector<std::tuple<node*, unsigned, unsigned, unsigned>> m_merge_trail; svector<std::tuple<node*, unsigned, unsigned>> m_merge_trail;
svector<std::pair<unsigned, eq>> m_update_eq_trail; svector<std::pair<unsigned, eq>> m_update_eq_trail;
@ -160,11 +179,14 @@ namespace euf {
void push_undo(undo_kind k); void push_undo(undo_kind k);
enode_vector m_todo; enode_vector m_todo;
unsigned to_monomial(enode* n); unsigned to_monomial(enode* n);
unsigned to_monomial(enode* n, monomial_t const& ms); unsigned to_monomial(enode* n, ptr_vector<node> const& ms);
monomial_t const& monomial(unsigned i) const { return m_monomials[i]; } monomial_t const& monomial(unsigned i) const { return m_monomials[i]; }
monomial_t& monomial(unsigned i) { return m_monomials[i]; } monomial_t& monomial(unsigned i) { return m_monomials[i]; }
void sort(monomial_t& monomial); void sort(monomial_t& monomial);
bool is_sorted(monomial_t const& monomial) const; bool is_sorted(monomial_t const& monomial) const;
uint64_t filter(monomial_t& m);
bool can_be_subset(monomial_t& subset, monomial_t& superset);
bool subsumes(unsigned src_eq, unsigned dst_eq);
void init_equation(eq const& e); void init_equation(eq const& e);
bool orient_equation(eq& e); bool orient_equation(eq& e);
@ -174,10 +196,10 @@ namespace euf {
void forward_simplify(unsigned eq_id, unsigned using_eq); void forward_simplify(unsigned eq_id, unsigned using_eq);
void backward_simplify(unsigned eq_id, unsigned using_eq); void backward_simplify(unsigned eq_id, unsigned using_eq);
void superpose(unsigned src_eq, unsigned dst_eq); void superpose(unsigned src_eq, unsigned dst_eq);
monomial_t m_src_r, m_src_l, m_dst_r; ptr_vector<node> m_src_r, m_src_l, m_dst_r;
unsigned_vector m_src_ids, m_src_count, m_dst_ids, m_dst_count; unsigned_vector m_src_ids, m_src_count, m_dst_ids, m_dst_count;
unsigned_vector m_lhs_eqs; unsigned_vector m_eq_occurs;
bool_vector m_eq_seen; bool_vector m_eq_seen;
bool m_backward_simplified = false; bool m_backward_simplified = false;
@ -190,17 +212,17 @@ namespace euf {
bool is_subset(monomial_t const& dst); bool is_subset(monomial_t const& dst);
unsigned rewrite(monomial_t const& src_r, monomial_t const& dst_r); unsigned rewrite(monomial_t const& src_r, monomial_t const& dst_r);
bool is_to_simplify(unsigned eq) const { return !m_eqs[eq].is_processed; } bool is_to_simplify(unsigned eq) const { return !m_eqs[eq].is_processed && m_eqs[eq].is_alive; }
bool is_processed(unsigned eq) const { return m_eqs[eq].is_processed; } bool is_processed(unsigned eq) const { return m_eqs[eq].is_processed && m_eqs[eq].is_alive; }
justification justify_rewrite(unsigned eq1, unsigned eq2); justification justify_rewrite(unsigned eq1, unsigned eq2);
justification::dependency* justify_equation(unsigned eq); justification::dependency* justify_equation(unsigned eq);
justification::dependency* justify_monomial(justification::dependency* d, ptr_vector<node> const& m); justification::dependency* justify_monomial(justification::dependency* d, monomial_t const& m);
void propagate_shared(); void propagate_shared();
void simplify_shared(unsigned idx, shared s); void simplify_shared(unsigned idx, shared s);
std::ostream& display_monomial(std::ostream& out, ptr_vector<node> const& m) const; std::ostream& display_monomial(std::ostream& out, monomial_t const& m) const;
std::ostream& display_equation(std::ostream& out, eq const& e) const; std::ostream& display_equation(std::ostream& out, eq const& e) const;
public: public: