mirror of
https://github.com/Z3Prover/z3
synced 2026-02-23 16:57:51 +00:00
* Introduce X-macro-based trace tag definition - Created trace_tags.def to centralize TRACE tag definitions - Each tag includes a symbolic name and description - Set up enum class TraceTag for type-safe usage in TRACE macros * Add script to generate Markdown documentation from trace_tags.def - Python script parses trace_tags.def and outputs trace_tags.md * Refactor TRACE_NEW to prepend TraceTag and pass enum to is_trace_enabled * trace: improve trace tag handling system with hierarchical tagging - Introduce hierarchical tag-class structure: enabling a tag class activates all child tags - Unify TRACE, STRACE, SCTRACE, and CTRACE under enum TraceTag - Implement initial version of trace_tag.def using X(tag, tag_class, description) (class names and descriptions to be refined in a future update) * trace: replace all string-based TRACE tags with enum TraceTag - Migrated all TRACE, STRACE, SCTRACE, and CTRACE macros to use enum TraceTag values instead of raw string literals * trace : add cstring header * trace : Add Markdown documentation generation from trace_tags.def via mk_api_doc.py * trace : rename macro parameter 'class' to 'tag_class' and remove Unicode comment in trace_tags.h. * trace : Add TODO comment for future implementation of tag_class activation * trace : Disable code related to tag_class until implementation is ready (#7663).
227 lines
6.5 KiB
C++
227 lines
6.5 KiB
C++
/*++
|
|
Copyright (c) 2020 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
euf_ackerman.cpp
|
|
|
|
Abstract:
|
|
|
|
Ackerman reduction plugin for EUF
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2020-08-28
|
|
|
|
--*/
|
|
|
|
#include "sat/smt/euf_solver.h"
|
|
#include "sat/smt/euf_ackerman.h"
|
|
|
|
namespace euf {
|
|
|
|
ackerman::ackerman(solver& ctx, ast_manager& m): ctx(ctx), m(m) {
|
|
new_tmp();
|
|
}
|
|
|
|
ackerman::~ackerman() {
|
|
reset();
|
|
dealloc(m_tmp_inference);
|
|
}
|
|
|
|
void ackerman::reset() {
|
|
for (inference* inf : m_table) {
|
|
m.dec_ref(inf->a);
|
|
m.dec_ref(inf->b);
|
|
m.dec_ref(inf->c);
|
|
dealloc(inf);
|
|
}
|
|
m_table.reset();
|
|
m_queue = nullptr;
|
|
}
|
|
|
|
void ackerman::insert(expr* a, expr* b, expr* lca) {
|
|
if (a->get_id() > b->get_id())
|
|
std::swap(a, b);
|
|
inference& inf = *m_tmp_inference;
|
|
inf.a = a;
|
|
inf.b = b;
|
|
inf.c = lca;
|
|
inf.is_cc = false;
|
|
inf.m_count = 0;
|
|
insert();
|
|
}
|
|
|
|
void ackerman::insert(app* a, app* b) {
|
|
if (a->get_id() > b->get_id())
|
|
std::swap(a, b);
|
|
inference& inf = *m_tmp_inference;
|
|
inf.a = a;
|
|
inf.b = b;
|
|
inf.c = nullptr;
|
|
inf.is_cc = true;
|
|
inf.m_count = 0;
|
|
insert();
|
|
}
|
|
|
|
|
|
void ackerman::insert() {
|
|
inference* inf = m_tmp_inference;
|
|
inference* other = m_table.insert_if_not_there(inf);
|
|
if (other == inf) {
|
|
m.inc_ref(inf->a);
|
|
m.inc_ref(inf->b);
|
|
m.inc_ref(inf->c);
|
|
new_tmp();
|
|
}
|
|
other->m_count++;
|
|
inference::push_to_front(m_queue, other);
|
|
}
|
|
|
|
void ackerman::remove(inference* inf) {
|
|
inference::remove_from(m_queue, inf);
|
|
m_table.erase(inf);
|
|
m.dec_ref(inf->a);
|
|
m.dec_ref(inf->b);
|
|
m.dec_ref(inf->c);
|
|
dealloc(inf);
|
|
}
|
|
|
|
void ackerman::new_tmp() {
|
|
m_tmp_inference = alloc(inference);
|
|
m_tmp_inference->init(m_tmp_inference);
|
|
}
|
|
|
|
bool ackerman::enable_cc(app* a, app* b) {
|
|
if (!ctx.enable_ackerman_axioms(a))
|
|
return false;
|
|
if (!ctx.enable_ackerman_axioms(b))
|
|
return false;
|
|
for (expr* arg : *a)
|
|
if (!ctx.enable_ackerman_axioms(arg))
|
|
return false;
|
|
for (expr* arg : *b)
|
|
if (!ctx.enable_ackerman_axioms(arg))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool ackerman::enable_eq(expr* a, expr* b, expr* c) {
|
|
return ctx.enable_ackerman_axioms(a) &&
|
|
ctx.enable_ackerman_axioms(b) &&
|
|
ctx.enable_ackerman_axioms(c);
|
|
}
|
|
|
|
void ackerman::cg_conflict_eh(expr * n1, expr * n2) {
|
|
if (!is_app(n1) || !is_app(n2))
|
|
return;
|
|
if (!ctx.enable_ackerman_axioms(n1))
|
|
return;
|
|
SASSERT(!ctx.m_drating);
|
|
app* a = to_app(n1);
|
|
app* b = to_app(n2);
|
|
if (a->get_decl() != b->get_decl() || a->get_num_args() != b->get_num_args())
|
|
return;
|
|
if (!enable_cc(a, b))
|
|
return;
|
|
TRACE(ack, tout << "conflict eh: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
|
|
insert(a, b);
|
|
gc();
|
|
}
|
|
|
|
void ackerman::used_eq_eh(expr* a, expr* b, expr* c) {
|
|
if (a == b || a == c || b == c)
|
|
return;
|
|
if (ctx.m_drating)
|
|
return;
|
|
if (!enable_eq(a, b, c))
|
|
return;
|
|
TRACE(ack, tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
|
|
insert(a, b, c);
|
|
gc();
|
|
}
|
|
|
|
void ackerman::used_cc_eh(app* a, app* b) {
|
|
if (ctx.m_drating)
|
|
return;
|
|
TRACE(ack, tout << "used cc: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
|
|
SASSERT(a->get_decl() == b->get_decl());
|
|
SASSERT(a->get_num_args() == b->get_num_args());
|
|
if (!enable_cc(a, b))
|
|
return;
|
|
insert(a, b);
|
|
gc();
|
|
}
|
|
|
|
void ackerman::gc() {
|
|
m_num_propagations_since_last_gc++;
|
|
if (m_num_propagations_since_last_gc <= ctx.m_config.m_dack_gc)
|
|
return;
|
|
m_num_propagations_since_last_gc = 0;
|
|
|
|
while (m_table.size() > m_gc_threshold)
|
|
remove(m_queue->prev());
|
|
|
|
m_gc_threshold *= 110;
|
|
m_gc_threshold /= 100;
|
|
m_gc_threshold++;
|
|
}
|
|
|
|
void ackerman::propagate() {
|
|
SASSERT(ctx.s().at_base_lvl());
|
|
auto* n = m_queue;
|
|
inference* k = nullptr;
|
|
unsigned num_prop = static_cast<unsigned>(ctx.s().get_stats().m_conflict * ctx.m_config.m_dack_factor);
|
|
num_prop = std::min(num_prop, m_table.size());
|
|
for (unsigned i = 0; i < num_prop; ++i, n = k) {
|
|
k = n->next();
|
|
if (n->m_count < ctx.m_config.m_dack_threshold)
|
|
continue;
|
|
if (n->m_count >= m_high_watermark && num_prop < m_table.size())
|
|
++num_prop;
|
|
if (n->is_cc)
|
|
add_cc(n->a, n->b);
|
|
else
|
|
add_eq(n->a, n->b, n->c);
|
|
++ctx.m_stats.m_ackerman;
|
|
remove(n);
|
|
}
|
|
}
|
|
|
|
void ackerman::add_cc(expr* _a, expr* _b) {
|
|
app* a = to_app(_a);
|
|
app* b = to_app(_b);
|
|
TRACE(ack, tout << mk_pp(a, m) << " " << mk_pp(b, m) << "\n";);
|
|
sat::literal_vector lits;
|
|
unsigned sz = a->get_num_args();
|
|
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
expr* ai = a->get_arg(i);
|
|
expr* bi = b->get_arg(i);
|
|
if (ai != bi) {
|
|
expr_ref eq = ctx.mk_eq(ai, bi);
|
|
lits.push_back(~ctx.mk_literal(eq));
|
|
}
|
|
}
|
|
expr_ref eq = ctx.mk_eq(a, b);
|
|
lits.push_back(ctx.mk_literal(eq));
|
|
th_proof_hint* ph = ctx.mk_cc_proof_hint(lits, a, b);
|
|
ctx.s().mk_clause(lits, sat::status::th(true, m.get_basic_family_id(), ph));
|
|
}
|
|
|
|
void ackerman::add_eq(expr* a, expr* b, expr* c) {
|
|
if (a == c || b == c)
|
|
return;
|
|
sat::literal lits[3];
|
|
expr_ref eq1(ctx.mk_eq(a, c), m);
|
|
expr_ref eq2(ctx.mk_eq(b, c), m);
|
|
expr_ref eq3(ctx.mk_eq(a, b), m);
|
|
TRACE(ack, tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
|
|
lits[0] = ~ctx.mk_literal(eq1);
|
|
lits[1] = ~ctx.mk_literal(eq2);
|
|
lits[2] = ctx.mk_literal(eq3);
|
|
th_proof_hint* ph = ctx.mk_tc_proof_hint(lits);
|
|
ctx.s().add_clause(3, lits, sat::status::th(true, m.get_basic_family_id(), ph));
|
|
}
|
|
|
|
}
|