mirror of
https://github.com/Z3Prover/z3
synced 2025-04-28 19:35:50 +00:00
Add EUF (congruence closure) proof hints and checker to the new core
EUF proofs are checked modulo union-find. Equalities are added to to union-find if they are assumptions or if they can be derived using congruence closure. The congruence closure assumptions are added as proof-hints. Note that this proof format does not track equality inferences, symmetry and transitivity. Instead they are handled by assuming a union-find based checker.
This commit is contained in:
parent
6f2fde87d1
commit
9be8fc7857
11 changed files with 315 additions and 57 deletions
|
@ -126,7 +126,7 @@ namespace euf {
|
|||
if (n2 == n)
|
||||
update_children(n);
|
||||
else
|
||||
merge(n, n2, justification::congruence(comm));
|
||||
merge(n, n2, justification::congruence(comm, m_congruence_timestamp++));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
@ -554,7 +554,7 @@ namespace euf {
|
|||
force_push();
|
||||
for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) {
|
||||
auto const& w = m_to_merge[i];
|
||||
merge(w.a, w.b, justification::congruence(w.commutativity));
|
||||
merge(w.a, w.b, justification::congruence(w.commutativity, m_congruence_timestamp++));
|
||||
}
|
||||
m_to_merge.reset();
|
||||
return
|
||||
|
@ -707,25 +707,28 @@ namespace euf {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain(ptr_vector<T>& justifications) {
|
||||
void egraph::explain(ptr_vector<T>& justifications, cc_justification* cc) {
|
||||
SASSERT(m_inconsistent);
|
||||
push_todo(m_n1);
|
||||
push_todo(m_n2);
|
||||
explain_eq(justifications, m_n1, m_n2, m_justification);
|
||||
explain_todo(justifications);
|
||||
explain_eq(justifications, cc, m_n1, m_n2, m_justification);
|
||||
explain_todo(justifications, cc);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, justification const& j) {
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b, justification const& j) {
|
||||
TRACE("euf_verbose", tout << "explain-eq: " << bpp(a) << " == " << bpp(b) << " jst: " << j << "\n";);
|
||||
if (j.is_external())
|
||||
justifications.push_back(j.ext<T>());
|
||||
else if (j.is_congruence())
|
||||
push_congruence(a, b, j.is_commutative());
|
||||
if (cc && j.is_congruence())
|
||||
cc->push_back(std::tuple(a, b, j.timestamp(), j.is_commutative()));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) {
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b) {
|
||||
SASSERT(a->get_root() == b->get_root());
|
||||
|
||||
enode* lca = find_lca(a, b);
|
||||
|
@ -734,27 +737,27 @@ namespace euf {
|
|||
push_to_lca(b, lca);
|
||||
if (m_used_eq)
|
||||
m_used_eq(a->get_expr(), b->get_expr(), lca->get_expr());
|
||||
explain_todo(justifications);
|
||||
explain_todo(justifications, cc);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unsigned egraph::explain_diseq(ptr_vector<T>& justifications, enode* a, enode* b) {
|
||||
unsigned egraph::explain_diseq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b) {
|
||||
enode* ra = a->get_root(), * rb = b->get_root();
|
||||
SASSERT(ra != rb);
|
||||
if (ra->interpreted() && rb->interpreted()) {
|
||||
explain_eq(justifications, a, ra);
|
||||
explain_eq(justifications, b, rb);
|
||||
explain_eq(justifications, cc, a, ra);
|
||||
explain_eq(justifications, cc, b, rb);
|
||||
return sat::null_bool_var;
|
||||
}
|
||||
enode* r = tmp_eq(ra, rb);
|
||||
SASSERT(r && r->get_root()->value() == l_false);
|
||||
explain_eq(justifications, r, r->get_root());
|
||||
explain_eq(justifications, cc, r, r->get_root());
|
||||
return r->get_root()->bool_var();
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain_todo(ptr_vector<T>& justifications) {
|
||||
void egraph::explain_todo(ptr_vector<T>& justifications, cc_justification* cc) {
|
||||
for (unsigned i = 0; i < m_todo.size(); ++i) {
|
||||
enode* n = m_todo[i];
|
||||
if (n->is_marked1())
|
||||
|
@ -762,7 +765,7 @@ namespace euf {
|
|||
if (n->m_target) {
|
||||
n->mark1();
|
||||
CTRACE("euf_verbose", 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);
|
||||
explain_eq(justifications, cc, n, n->m_target, n->m_justification);
|
||||
}
|
||||
else if (!n->is_marked1() && n->value() != l_undef) {
|
||||
n->mark1();
|
||||
|
@ -890,15 +893,15 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
template void euf::egraph::explain(ptr_vector<int>& justifications);
|
||||
template void euf::egraph::explain_todo(ptr_vector<int>& justifications);
|
||||
template void euf::egraph::explain_eq(ptr_vector<int>& justifications, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<int>& justifications, enode* a, enode* b);
|
||||
template void euf::egraph::explain(ptr_vector<int>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_todo(ptr_vector<int>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_eq(ptr_vector<int>& justifications, cc_justification*, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<int>& justifications, cc_justification*, enode* a, enode* b);
|
||||
|
||||
template void euf::egraph::explain(ptr_vector<size_t>& justifications);
|
||||
template void euf::egraph::explain_todo(ptr_vector<size_t>& justifications);
|
||||
template void euf::egraph::explain_eq(ptr_vector<size_t>& justifications, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<size_t>& justifications, enode* a, enode* b);
|
||||
template void euf::egraph::explain(ptr_vector<size_t>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_todo(ptr_vector<size_t>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_eq(ptr_vector<size_t>& justifications, cc_justification*, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<size_t>& justifications, cc_justification*, enode* a, enode* b);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -72,6 +72,13 @@ namespace euf {
|
|||
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) {}
|
||||
};
|
||||
|
||||
// cc_justification contains the uses of congruence closure
|
||||
// It is the only information collected from justifications in order to
|
||||
// reconstruct EUF proofs. Transitivity, Symmetry of equality are not
|
||||
// tracked.
|
||||
typedef std::tuple<enode*,enode*,uint64_t, bool> cc_justification_record;
|
||||
typedef svector<cc_justification_record> cc_justification;
|
||||
|
||||
class egraph {
|
||||
|
||||
|
@ -186,6 +193,8 @@ namespace euf {
|
|||
stats m_stats;
|
||||
bool m_uses_congruence = false;
|
||||
bool m_default_relevant = true;
|
||||
uint64_t m_congruence_timestamp = 0;
|
||||
|
||||
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
|
||||
std::function<void(enode*)> m_on_make;
|
||||
std::function<void(expr*,expr*,expr*)> m_used_eq;
|
||||
|
@ -226,10 +235,10 @@ namespace euf {
|
|||
void erase_from_table(enode* p);
|
||||
|
||||
template <typename T>
|
||||
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, justification const& j);
|
||||
void explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b, justification const& j);
|
||||
|
||||
template <typename T>
|
||||
void explain_todo(ptr_vector<T>& justifications);
|
||||
void explain_todo(ptr_vector<T>& justifications, cc_justification* cc);
|
||||
|
||||
std::ostream& display(std::ostream& out, unsigned max_args, enode* n) const;
|
||||
|
||||
|
@ -306,11 +315,11 @@ namespace euf {
|
|||
void end_explain();
|
||||
bool uses_congruence() const { return m_uses_congruence; }
|
||||
template <typename T>
|
||||
void explain(ptr_vector<T>& justifications);
|
||||
void explain(ptr_vector<T>& justifications, cc_justification* cc);
|
||||
template <typename T>
|
||||
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b);
|
||||
void explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b);
|
||||
template <typename T>
|
||||
unsigned explain_diseq(ptr_vector<T>& justifications, enode* a, enode* b);
|
||||
unsigned explain_diseq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b);
|
||||
enode_vector const& nodes() const { return m_nodes; }
|
||||
|
||||
ast_manager& get_manager() { return m; }
|
||||
|
|
|
@ -13,6 +13,11 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2020-08-23
|
||||
|
||||
Notes:
|
||||
|
||||
- congruence closure justifications are given a timestamp so it is easy to sort them.
|
||||
See the longer descriptoin in euf_proof_checker.cpp
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
@ -27,11 +32,15 @@ namespace euf {
|
|||
};
|
||||
kind_t m_kind;
|
||||
bool m_comm;
|
||||
void* m_external;
|
||||
justification(bool comm):
|
||||
union {
|
||||
void* m_external;
|
||||
uint64_t m_timestamp;
|
||||
};
|
||||
|
||||
justification(bool comm, uint64_t ts):
|
||||
m_kind(kind_t::congruence_t),
|
||||
m_comm(comm),
|
||||
m_external(nullptr)
|
||||
m_timestamp(ts)
|
||||
{}
|
||||
|
||||
justification(void* ext):
|
||||
|
@ -48,12 +57,13 @@ namespace euf {
|
|||
{}
|
||||
|
||||
static justification axiom() { return justification(); }
|
||||
static justification congruence(bool c) { return justification(c); }
|
||||
static justification congruence(bool c, uint64_t ts) { return justification(c, ts); }
|
||||
static justification external(void* ext) { return justification(ext); }
|
||||
|
||||
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; }
|
||||
uint64_t timestamp() const { SASSERT(is_congruence()); return m_timestamp; }
|
||||
template <typename T>
|
||||
T* ext() const { SASSERT(is_external()); return static_cast<T*>(m_external); }
|
||||
|
||||
|
@ -64,7 +74,7 @@ namespace euf {
|
|||
case kind_t::axiom_t:
|
||||
return axiom();
|
||||
case kind_t::congruence_t:
|
||||
return congruence(m_comm);
|
||||
return congruence(m_comm, m_timestamp);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return axiom();
|
||||
|
@ -90,4 +100,8 @@ namespace euf {
|
|||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, justification const& j) {
|
||||
return j.display(out, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue