mirror of
https://github.com/Z3Prover/z3
synced 2026-06-22 16:40:29 +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).
333 lines
11 KiB
C++
333 lines
11 KiB
C++
/*++
|
|
Copyright (c) 2015 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
|
|
mpb_plugin.cpp
|
|
|
|
Abstract:
|
|
|
|
Model-based projection plugin utilities
|
|
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2015-5-29
|
|
|
|
--*/
|
|
|
|
#include "ast/rewriter/expr_safe_replace.h"
|
|
#include "ast/ast_pp.h"
|
|
#include "ast/ast_util.h"
|
|
#include "ast/occurs.h"
|
|
#include "ast/rewriter/th_rewriter.h"
|
|
#include "ast/expr_functors.h"
|
|
#include "ast/for_each_expr.h"
|
|
#include "ast/scoped_proof.h"
|
|
#include "qe/mbp/mbp_plugin.h"
|
|
#include "model/model_pp.h"
|
|
#include "model/model_evaluator.h"
|
|
|
|
|
|
namespace mbp {
|
|
|
|
struct noop_op_proc {
|
|
void operator()(expr*) {}
|
|
};
|
|
|
|
void project_plugin::mark_rec(expr_mark& visited, expr* e) {
|
|
for_each_expr_proc<noop_op_proc> fe;
|
|
for_each_expr(fe, visited, e);
|
|
}
|
|
|
|
void project_plugin::mark_rec(expr_mark& visited, expr_ref_vector const& es) {
|
|
for (expr* e : es)
|
|
mark_rec(visited, e);
|
|
}
|
|
|
|
/**
|
|
\brief return two terms that are equal in the model.
|
|
The distinct term t is false in model, so there
|
|
are at least two arguments of t that are equal in the model.
|
|
*/
|
|
expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) {
|
|
SASSERT(m.is_distinct(t));
|
|
expr_ref val(m);
|
|
expr_ref_vector vals(m);
|
|
obj_map<expr, expr*> val2expr;
|
|
app* alit = to_app(t);
|
|
SASSERT(alit->get_num_args() > 1);
|
|
if (alit->get_num_args() == 2)
|
|
return expr_ref(m.mk_eq(alit->get_arg(0), alit->get_arg(1)), m);
|
|
for (expr* e1 : *alit) {
|
|
expr* e2;
|
|
val = model(e1);
|
|
TRACE(qe, tout << mk_pp(e1, m) << " |-> " << val << "\n";);
|
|
if (val2expr.find(val, e2)) {
|
|
return expr_ref(m.mk_eq(e1, e2), m);
|
|
}
|
|
val2expr.insert(val, e1);
|
|
vals.push_back(val);
|
|
}
|
|
for (unsigned i = 0; i < alit->get_num_args(); ++i) {
|
|
for (unsigned j = i + 1; j < alit->get_num_args(); ++j) {
|
|
expr* e1 = alit->get_arg(i);
|
|
expr* e2 = alit->get_arg(j);
|
|
val = m.mk_eq(e1, e2);
|
|
if (!model.is_false(val))
|
|
return expr_ref(m.mk_eq(e1, e2), m);
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
return expr_ref(nullptr, m);
|
|
}
|
|
|
|
|
|
void project_plugin::erase(expr_ref_vector& lits, unsigned& i) {
|
|
lits[i] = lits.back();
|
|
lits.pop_back();
|
|
--i;
|
|
}
|
|
|
|
void project_plugin::push_back(expr_ref_vector& lits, expr* e) {
|
|
if (!m.is_true(e))
|
|
lits.push_back(e);
|
|
}
|
|
|
|
bool project_plugin::reduce(model_evaluator& eval, model& model, expr* fml, expr_ref_vector& fmls) {
|
|
expr* nfml, * f1, * f2, * f3;
|
|
expr_ref val(m);
|
|
model.set_inline();
|
|
if (m.is_not(fml, nfml) && m.is_distinct(nfml))
|
|
push_back(fmls, pick_equality(m, model, nfml));
|
|
else if (m.is_or(fml)) {
|
|
for (expr* arg : *to_app(fml)) {
|
|
val = eval(arg);
|
|
if (m.is_true(val)) {
|
|
fmls.push_back(arg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (m.is_and(fml)) {
|
|
fmls.append(to_app(fml)->get_num_args(), to_app(fml)->get_args());
|
|
}
|
|
else if (m.is_iff(fml, f1, f2) || (m.is_not(fml, nfml) && m.is_xor(nfml, f1, f2))) {
|
|
val = eval(f1);
|
|
if (m.is_false(val)) {
|
|
push_back(fmls, mk_not(m, f1));
|
|
push_back(fmls, mk_not(m, f2));
|
|
}
|
|
else {
|
|
push_back(fmls, f1);
|
|
push_back(fmls, f2);
|
|
}
|
|
}
|
|
else if (m.is_implies(fml, f1, f2)) {
|
|
val = eval(f2);
|
|
if (m.is_true(val))
|
|
push_back(fmls, f2);
|
|
else
|
|
push_back(fmls, mk_not(m, f1));
|
|
}
|
|
else if (m.is_ite(fml, f1, f2, f3)) {
|
|
val = eval(f1);
|
|
if (m.is_true(val)) {
|
|
push_back(fmls, f1);
|
|
push_back(fmls, f2);
|
|
}
|
|
else {
|
|
push_back(fmls, mk_not(m, f1));
|
|
push_back(fmls, f3);
|
|
}
|
|
}
|
|
else if (m.is_not(fml, nfml) && m.is_not(nfml, nfml)) {
|
|
push_back(fmls, nfml);
|
|
}
|
|
else if (m.is_not(fml, nfml) && m.is_and(nfml)) {
|
|
for (expr* arg : *to_app(nfml)) {
|
|
val = eval(arg);
|
|
if (m.is_false(val)) {
|
|
push_back(fmls, mk_not(m, arg));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (m.is_not(fml, nfml) && m.is_or(nfml)) {
|
|
for (expr* arg : *to_app(nfml))
|
|
push_back(fmls, mk_not(m, arg));
|
|
}
|
|
else if ((m.is_not(fml, nfml) && m.is_iff(nfml, f1, f2)) || m.is_xor(fml, f1, f2)) {
|
|
val = eval(f1);
|
|
if (m.is_true(val))
|
|
f2 = mk_not(m, f2);
|
|
else
|
|
f1 = mk_not(m, f1);
|
|
push_back(fmls, f1);
|
|
push_back(fmls, f2);
|
|
}
|
|
else if (m.is_not(fml, nfml) && m.is_implies(nfml, f1, f2)) {
|
|
push_back(fmls, f1);
|
|
push_back(fmls, mk_not(m, f2));
|
|
}
|
|
else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) {
|
|
val = eval(f1);
|
|
if (m.is_true(val)) {
|
|
push_back(fmls, f1);
|
|
push_back(fmls, mk_not(m, f2));
|
|
}
|
|
else {
|
|
push_back(fmls, mk_not(m, f1));
|
|
push_back(fmls, mk_not(m, f3));
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void project_plugin::extract_literals(model& model, app_ref_vector const& vars, expr_ref_vector& fmls) {
|
|
m_cache.reset();
|
|
m_bool_visited.reset();
|
|
expr_ref val(m);
|
|
model_evaluator eval(model);
|
|
eval.set_expand_array_equalities(true);
|
|
TRACE(qe, tout << fmls << "\n";);
|
|
DEBUG_CODE(for (expr* fml : fmls) { CTRACE(qe, m.is_false(eval(fml)), tout << mk_pp(fml, m) << " is false\n" << model;); SASSERT(!m.is_false(eval(fml))); });
|
|
|
|
for (unsigned i = 0; i < fmls.size(); ++i) {
|
|
expr* nfml, * fml = fmls.get(i);
|
|
if (reduce(eval, model, fml, fmls))
|
|
erase(fmls, i);
|
|
else if (m.is_not(fml, nfml))
|
|
extract_bools(eval, fmls, i, nfml, false);
|
|
else
|
|
extract_bools(eval, fmls, i, fml, true);
|
|
}
|
|
TRACE(qe, tout << "fmls: " << fmls << "\n";);
|
|
}
|
|
|
|
void project_plugin::extract_bools(model_evaluator& eval, expr_ref_vector& fmls, unsigned idx, expr* fml, bool is_true) {
|
|
TRACE(qe, tout << "extract bools: " << mk_pp(fml, m) << "\n";);
|
|
if (!is_app(fml))
|
|
return;
|
|
m_to_visit.reset();
|
|
m_to_visit.append(to_app(fml)->get_num_args(), to_app(fml)->get_args());
|
|
while (!m_to_visit.empty()) {
|
|
if (!m.inc())
|
|
return;
|
|
expr* e = m_to_visit.back();
|
|
if (m_cache.get(e->get_id(), nullptr)) {
|
|
m_to_visit.pop_back();
|
|
}
|
|
else if (!is_app(e)) {
|
|
m_cache.setx(e->get_id(), e);
|
|
m_to_visit.pop_back();
|
|
}
|
|
else if (visit_ite(eval, e, fmls))
|
|
continue;
|
|
else if (visit_bool(eval, e, fmls))
|
|
continue;
|
|
else
|
|
visit_app(e);
|
|
}
|
|
|
|
SASSERT(m_to_visit.empty());
|
|
m_to_visit.push_back(fml);
|
|
visit_app(fml);
|
|
SASSERT(m_to_visit.empty());
|
|
expr* new_fml = m_cache.get(fml->get_id(), nullptr);
|
|
SASSERT(new_fml);
|
|
if (new_fml != fml)
|
|
fmls[idx] = is_true ? new_fml : mk_not(m, new_fml);
|
|
}
|
|
|
|
bool project_plugin::is_true(model_evaluator& eval, expr* e) {
|
|
expr_ref val = eval(e);
|
|
bool tt = m.is_true(val);
|
|
if (!tt && !m.is_false(val) && contains_uninterpreted(val)) {
|
|
IF_VERBOSE(1, verbose_stream() << "mbp failed on " << mk_pp(e, m) << " := " << val << "\n");
|
|
throw default_exception("could not evaluate Boolean in model");
|
|
}
|
|
SASSERT(tt || m.is_false(val));
|
|
return tt;
|
|
}
|
|
|
|
bool project_plugin::visit_ite(model_evaluator& eval, expr* e, expr_ref_vector& fmls) {
|
|
expr* c = nullptr, * th = nullptr, * el = nullptr;
|
|
if (m.is_ite(e, c, th, el)) {
|
|
bool tt = is_true(eval, c);
|
|
if (!m_bool_visited.is_marked(c))
|
|
fmls.push_back(tt ? c : mk_not(m, c));
|
|
m_bool_visited.mark(c);
|
|
expr* s = tt ? th : el;
|
|
expr* t = m_cache.get(s->get_id(), nullptr);
|
|
if (t) {
|
|
m_to_visit.pop_back();
|
|
m_cache.setx(e->get_id(), t);
|
|
}
|
|
else
|
|
m_to_visit.push_back(s);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool project_plugin::visit_bool(model_evaluator& eval, expr* e, expr_ref_vector& fmls) {
|
|
if (m.is_bool(e) && !m.is_true(e) && !m.is_false(e)) {
|
|
bool tt = is_true(eval, e);
|
|
if (!m_bool_visited.is_marked(e))
|
|
fmls.push_back(tt ? e : mk_not(m, e));
|
|
m_bool_visited.mark(e);
|
|
m_cache.setx(e->get_id(), m.mk_bool_val(tt));
|
|
m_to_visit.pop_back();
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void project_plugin::visit_app(expr* e) {
|
|
unsigned sz = m_to_visit.size();
|
|
m_args.reset();
|
|
bool diff = false;
|
|
for (expr* arg : *to_app(e)) {
|
|
expr* new_arg = m_cache.get(arg->get_id(), nullptr);
|
|
diff |= new_arg != arg;
|
|
if (new_arg == nullptr)
|
|
m_to_visit.push_back(arg);
|
|
else
|
|
m_args.push_back(new_arg);
|
|
}
|
|
if (sz == m_to_visit.size()) {
|
|
m_cache.setx(e->get_id(), diff ? m.mk_app(to_app(e)->get_decl(), m_args) : e);
|
|
m_to_visit.pop_back();
|
|
}
|
|
}
|
|
|
|
void project_plugin::mark_non_ground(expr* e) {
|
|
m_to_visit.push_back(e);
|
|
while (!m_to_visit.empty()) {
|
|
expr* e = m_to_visit.back();
|
|
if (!is_app(e)) {
|
|
m_visited.mark(e);
|
|
m_to_visit.pop_back();
|
|
continue;
|
|
}
|
|
unsigned n = m_to_visit.size();
|
|
for (expr* arg : *to_app(e)) {
|
|
if (!m_visited.is_marked(arg))
|
|
m_to_visit.push_back(arg);
|
|
else if (m_non_ground.is_marked(arg))
|
|
m_non_ground.mark(e);
|
|
}
|
|
if (m_to_visit.size() == n) {
|
|
m_visited.mark(e);
|
|
m_to_visit.pop_back();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|