mirror of
https://github.com/Z3Prover/z3
synced 2025-06-29 09:28:45 +00:00
n/a
This commit is contained in:
parent
bf5e6936c0
commit
108275dcd9
2 changed files with 69 additions and 15 deletions
|
@ -41,16 +41,20 @@ More notes:
|
||||||
Shared occurrences are rewritten modulo completion.
|
Shared occurrences are rewritten modulo completion.
|
||||||
When equal to a different shared occurrence, propagate equality.
|
When equal to a different shared occurrence, propagate equality.
|
||||||
|
|
||||||
TODOs:
|
|
||||||
- Establish / fix formal termination properties in the presence of union-find.
|
|
||||||
The formal interactions between union find and orienting the equations need to be established.
|
|
||||||
- Union-find merges can change the orientation of an equation and break termination.
|
|
||||||
- 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.
|
|
||||||
-> forward and backward subsumption
|
-> forward and backward subsumption
|
||||||
- apply forward subsumption when simplifying equality using processed
|
- apply forward subsumption when simplifying equality using processed
|
||||||
- apply backward subsumption when simplifying processed and to_simplify
|
- apply backward subsumption when simplifying processed and to_simplify
|
||||||
|
|
||||||
|
Rewrite rules are reoriented after a merge of enodes.
|
||||||
|
It simulates creating a critical pair:
|
||||||
|
n -> n'
|
||||||
|
n + k = j + k
|
||||||
|
after merge
|
||||||
|
n' + k = j + k, could be that n' + k < j + k < n + k in term ordering because n' < j, m < n
|
||||||
|
|
||||||
|
TODOs:
|
||||||
|
|
||||||
- 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.
|
||||||
|
@ -350,10 +354,10 @@ namespace euf {
|
||||||
while (true) {
|
while (true) {
|
||||||
loop_start:
|
loop_start:
|
||||||
unsigned eq_id = pick_next_eq();
|
unsigned eq_id = pick_next_eq();
|
||||||
TRACE("plugin", tout << "propagate " << eq_id << "\n");
|
|
||||||
if (eq_id == UINT_MAX)
|
if (eq_id == UINT_MAX)
|
||||||
break;
|
break;
|
||||||
eq& eq = m_eqs[eq_id];
|
|
||||||
|
TRACE("plugin", tout << "propagate " << eq_id << ": " << eq_pp(*this, m_eqs[eq_id]) << "\n");
|
||||||
|
|
||||||
// simplify eq using processed
|
// simplify eq using processed
|
||||||
for (auto other_eq : backward_iterator(eq_id))
|
for (auto other_eq : backward_iterator(eq_id))
|
||||||
|
@ -390,6 +394,7 @@ namespace euf {
|
||||||
return UINT_MAX;
|
return UINT_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reorient equations when the status of equations are set to to_simplify.
|
||||||
void ac_plugin::set_status(unsigned id, eq_status s) {
|
void ac_plugin::set_status(unsigned id, eq_status s) {
|
||||||
auto& eq = m_eqs[id];
|
auto& eq = m_eqs[id];
|
||||||
if (eq.status == eq_status::is_dead)
|
if (eq.status == eq_status::is_dead)
|
||||||
|
@ -434,7 +439,7 @@ namespace euf {
|
||||||
auto const& eq = m_eqs[eq_id];
|
auto const& eq = m_eqs[eq_id];
|
||||||
init_ref_counts(monomial(eq.r), m_dst_r_counts);
|
init_ref_counts(monomial(eq.r), m_dst_r_counts);
|
||||||
init_ref_counts(monomial(eq.l), m_dst_l_counts);
|
init_ref_counts(monomial(eq.l), m_dst_l_counts);
|
||||||
init_overlap_iterator(eq_id, monomial(eq.r));
|
init_subset_iterator(eq_id, monomial(eq.r));
|
||||||
return m_eq_occurs;
|
return m_eq_occurs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,12 +447,37 @@ namespace euf {
|
||||||
m_eq_occurs.reset();
|
m_eq_occurs.reset();
|
||||||
for (auto n : m)
|
for (auto n : m)
|
||||||
m_eq_occurs.append(n->root->eqs);
|
m_eq_occurs.append(n->root->eqs);
|
||||||
|
compress_eq_occurs(eq_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// add all but one of the use lists. Identify the largest use list and skip it.
|
||||||
|
// The rationale is that [a, b] is a subset of [a, b, c, d, e] if
|
||||||
|
// it has at least two elements (otherwise it would not apply as a rewrite over AC).
|
||||||
|
// then one of the two elements has to be in the set of [a, b, c, d, e] \ { x }
|
||||||
|
// where x is an arbitrary value from a, b, c, d, e. Not a two-element watch list, but still.
|
||||||
|
//
|
||||||
|
void ac_plugin::init_subset_iterator(unsigned eq_id, monomial_t const& m) {
|
||||||
|
unsigned max_use = 0;
|
||||||
|
node* max_n = nullptr;
|
||||||
|
for (auto n : m)
|
||||||
|
if (n->root->eqs.size() >= max_use)
|
||||||
|
max_n = n, max_use = n->root->eqs.size();
|
||||||
|
// found node that occurs in fewest rhs
|
||||||
|
VERIFY(max_n);
|
||||||
|
m_eq_occurs.reset();
|
||||||
|
for (auto n : m)
|
||||||
|
if (n != max_n)
|
||||||
|
m_eq_occurs.append(n->root->eqs);
|
||||||
|
compress_eq_occurs(eq_id);
|
||||||
|
}
|
||||||
|
|
||||||
// prune m_eq_occurs to single occurrences
|
// prune m_eq_occurs to single occurrences
|
||||||
|
void ac_plugin::compress_eq_occurs(unsigned eq_id) {
|
||||||
unsigned j = 0;
|
unsigned j = 0;
|
||||||
|
m_eq_seen.reserve(m_eqs.size() + 1, false);
|
||||||
for (unsigned i = 0; i < m_eq_occurs.size(); ++i) {
|
for (unsigned i = 0; i < m_eq_occurs.size(); ++i) {
|
||||||
unsigned id = m_eq_occurs[i];
|
unsigned id = m_eq_occurs[i];
|
||||||
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)
|
||||||
|
@ -497,6 +527,8 @@ 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];
|
||||||
|
|
||||||
|
TRACE("plugin", tout << "forward simplify " << eq_pp(*this, src) << " " << eq_pp(*this, dst) << "\n");
|
||||||
|
|
||||||
if (!can_be_subset(monomial(src.l), monomial(dst.r)))
|
if (!can_be_subset(monomial(src.l), monomial(dst.r)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -530,6 +562,7 @@ namespace euf {
|
||||||
m_eqs[dst_eq].r = new_r;
|
m_eqs[dst_eq].r = new_r;
|
||||||
m_eqs[dst_eq].j = justify_rewrite(src_eq, dst_eq);
|
m_eqs[dst_eq].j = justify_rewrite(src_eq, dst_eq);
|
||||||
push_undo(is_update_eq);
|
push_undo(is_update_eq);
|
||||||
|
TRACE("plugin", tout << "rewritten to " << m_pp(*this, monomial(new_r)) << "\n");
|
||||||
}
|
}
|
||||||
m_src_r.shrink(src_r_size);
|
m_src_r.shrink(src_r_size);
|
||||||
}
|
}
|
||||||
|
@ -543,13 +576,14 @@ namespace euf {
|
||||||
//
|
//
|
||||||
// dst_ids, dst_count contain rhs of dst_eq
|
// dst_ids, dst_count contain rhs of dst_eq
|
||||||
//
|
//
|
||||||
|
TRACE("plugin", tout << "backward simplify " << eq_pp(*this, src) << " " << eq_pp(*this, dst) << "\n");
|
||||||
// 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)))
|
if (!can_be_subset(monomial(src.l), monomial(dst.r)))
|
||||||
return false;
|
return false;
|
||||||
if (!is_subset(m_dst_l_counts, m_src_l_counts, monomial(src.l)))
|
if (!is_subset(m_dst_l_counts, m_src_l_counts, monomial(src.l)))
|
||||||
return false;
|
return false;
|
||||||
if (backward_subsumes(src_eq, dst_eq)) {
|
if (backward_subsumes(src_eq, dst_eq)) {
|
||||||
|
|
||||||
set_status(dst_eq, eq_status::is_dead);
|
set_status(dst_eq, eq_status::is_dead);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -558,6 +592,7 @@ namespace euf {
|
||||||
m_update_eq_trail.push_back({ dst_eq, m_eqs[dst_eq] });
|
m_update_eq_trail.push_back({ dst_eq, m_eqs[dst_eq] });
|
||||||
m_eqs[dst_eq].r = new_r;
|
m_eqs[dst_eq].r = new_r;
|
||||||
m_eqs[dst_eq].j = justify_rewrite(src_eq, dst_eq);
|
m_eqs[dst_eq].j = justify_rewrite(src_eq, dst_eq);
|
||||||
|
TRACE("plugin", tout << "rewritten to " << m_pp(*this, monomial(new_r)) << "\n");
|
||||||
push_undo(is_update_eq);
|
push_undo(is_update_eq);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -769,7 +804,7 @@ namespace euf {
|
||||||
change = false;
|
change = false;
|
||||||
auto & m = monomial(s.m);
|
auto & m = monomial(s.m);
|
||||||
init_ref_counts(m, m_dst_l_counts);
|
init_ref_counts(m, m_dst_l_counts);
|
||||||
init_overlap_iterator(UINT_MAX, m);
|
init_subset_iterator(UINT_MAX, m);
|
||||||
for (auto eq : m_eq_occurs) {
|
for (auto eq : m_eq_occurs) {
|
||||||
auto& src = m_eqs[eq];
|
auto& src = m_eqs[eq];
|
||||||
if (!can_be_subset(monomial(src.l), m))
|
if (!can_be_subset(monomial(src.l), m))
|
||||||
|
|
|
@ -29,6 +29,7 @@ for p in parents(xyzz):
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include "ast/euf/euf_plugin.h"
|
#include "ast/euf/euf_plugin.h"
|
||||||
|
|
||||||
namespace euf {
|
namespace euf {
|
||||||
|
@ -223,6 +224,8 @@ namespace euf {
|
||||||
unsigned_vector const& backward_iterator(unsigned eq);
|
unsigned_vector const& backward_iterator(unsigned eq);
|
||||||
void init_ref_counts(monomial_t const& monomial, ref_counts& counts);
|
void init_ref_counts(monomial_t const& monomial, ref_counts& counts);
|
||||||
void init_overlap_iterator(unsigned eq, monomial_t const& m);
|
void init_overlap_iterator(unsigned eq, monomial_t const& m);
|
||||||
|
void init_subset_iterator(unsigned eq, monomial_t const& m);
|
||||||
|
void compress_eq_occurs(unsigned eq_id);
|
||||||
// check that src is a subset of dst, where dst_counts are precomputed
|
// check that src is a subset of dst, where dst_counts are precomputed
|
||||||
bool is_subset(ref_counts const& dst_counts, ref_counts& src_counts, monomial_t const& src);
|
bool is_subset(ref_counts const& dst_counts, ref_counts& src_counts, monomial_t const& src);
|
||||||
|
|
||||||
|
@ -244,6 +247,7 @@ namespace euf {
|
||||||
std::ostream& display_monomial(std::ostream& out, monomial_t 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:
|
||||||
|
|
||||||
ac_plugin(egraph& g, unsigned fid, unsigned op);
|
ac_plugin(egraph& g, unsigned fid, unsigned op);
|
||||||
|
@ -267,5 +271,20 @@ namespace euf {
|
||||||
std::ostream& display(std::ostream& out) const override;
|
std::ostream& display(std::ostream& out) const override;
|
||||||
|
|
||||||
void set_undo(std::function<void(void)> u) { m_undo_notify = u; }
|
void set_undo(std::function<void(void)> u) { m_undo_notify = u; }
|
||||||
|
|
||||||
|
struct eq_pp {
|
||||||
|
ac_plugin& p; eq const& e;
|
||||||
|
eq_pp(ac_plugin& p, eq const& e) : p(p), e(e) {};
|
||||||
|
std::ostream& display(std::ostream& out) const { return p.display_equation(out, e); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct m_pp {
|
||||||
|
ac_plugin& p; monomial_t const& m;
|
||||||
|
m_pp(ac_plugin& p, monomial_t const& m) : p(p), m(m) {}
|
||||||
|
std::ostream& display(std::ostream& out) const { return p.display_monomial(out, m); }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, ac_plugin::eq_pp const& d) { return d.display(out); }
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, ac_plugin::m_pp const& d) { return d.display(out); }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue