mirror of
https://github.com/Z3Prover/z3
synced 2025-06-25 23:33:41 +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).
335 lines
9.2 KiB
C++
335 lines
9.2 KiB
C++
/*++
|
|
Copyright (c) 2013 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pb_rewriter.cpp
|
|
|
|
Abstract:
|
|
|
|
Basic rewriting rules for PB constraints.
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2013-14-12
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#include "ast/rewriter/pb_rewriter.h"
|
|
#include "ast/rewriter/pb_rewriter_def.h"
|
|
#include "ast/ast_pp.h"
|
|
#include "ast/ast_util.h"
|
|
#include "ast/ast_smt_pp.h"
|
|
|
|
|
|
class pb_ast_rewriter_util {
|
|
ast_manager& m;
|
|
expr_ref_vector m_refs;
|
|
public:
|
|
|
|
typedef std::pair<expr*, rational> arg_t;
|
|
typedef vector<arg_t> args_t;
|
|
typedef rational numeral;
|
|
|
|
pb_ast_rewriter_util(ast_manager& m): m(m), m_refs(m) {}
|
|
|
|
expr* negate(expr* e) {
|
|
if (m.is_true(e)) {
|
|
return m.mk_false();
|
|
}
|
|
if (m.is_false(e)) {
|
|
return m.mk_true();
|
|
}
|
|
if (m.is_not(e, e)) {
|
|
return e;
|
|
}
|
|
m_refs.push_back(m.mk_not(e));
|
|
return m_refs.back();
|
|
}
|
|
|
|
void display(std::ostream& out, expr* e) {
|
|
out << mk_pp(e, m);
|
|
}
|
|
|
|
bool is_negated(expr* e) const {
|
|
return m.is_not(e);
|
|
}
|
|
|
|
bool is_true(expr* e) const {
|
|
return m.is_true(e);
|
|
}
|
|
|
|
bool is_false(expr* e) const {
|
|
return m.is_false(e);
|
|
}
|
|
|
|
struct compare {
|
|
bool operator()(std::pair<expr*,rational> const& a,
|
|
std::pair<expr*,rational> const& b) {
|
|
return a.first->get_id() < b.first->get_id();
|
|
}
|
|
|
|
};
|
|
};
|
|
|
|
expr_ref pb_rewriter::translate_pb2lia(obj_map<expr,expr*>& vars, expr* fml) {
|
|
pb_util util(m());
|
|
arith_util a(m());
|
|
expr_ref result(m()), tmp(m());
|
|
expr_ref_vector es(m());
|
|
expr*const* args = to_app(fml)->get_args();
|
|
unsigned sz = to_app(fml)->get_num_args();
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
expr* e = args[i];
|
|
if (m().is_not(e, e)) {
|
|
es.push_back(a.mk_sub(a.mk_numeral(rational(1),true),vars.find(e)));
|
|
}
|
|
else {
|
|
es.push_back(vars.find(e));
|
|
}
|
|
}
|
|
|
|
if (util.is_at_most_k(fml) || util.is_at_least_k(fml)) {
|
|
if (es.empty()) {
|
|
tmp = a.mk_numeral(rational(0), true);
|
|
}
|
|
else {
|
|
tmp = a.mk_add(es.size(), es.data());
|
|
}
|
|
if (util.is_at_most_k(fml)) {
|
|
result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false));
|
|
}
|
|
else {
|
|
result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false));
|
|
}
|
|
}
|
|
else if (util.is_le(fml) || util.is_ge(fml) || util.is_eq(fml)) {
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
es[i] = a.mk_mul(a.mk_numeral(util.get_coeff(fml, i),false), es[i].get());
|
|
}
|
|
if (es.empty()) {
|
|
tmp = a.mk_numeral(rational(0), true);
|
|
}
|
|
else {
|
|
tmp = a.mk_add(es.size(), es.data());
|
|
}
|
|
rational k = util.get_k(fml);
|
|
if (util.is_le(fml)) {
|
|
result = a.mk_le(tmp, a.mk_numeral(k, false));
|
|
}
|
|
else if (util.is_ge(fml)) {
|
|
result = a.mk_ge(tmp, a.mk_numeral(k, false));
|
|
}
|
|
else {
|
|
result = m().mk_eq(tmp, a.mk_numeral(k, false));
|
|
}
|
|
}
|
|
else {
|
|
result = fml;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
expr_ref pb_rewriter::mk_validate_rewrite(app_ref& e1, app_ref& e2) {
|
|
ast_manager& m = e1.get_manager();
|
|
arith_util a(m);
|
|
symbol name;
|
|
obj_map<expr,expr*> vars;
|
|
expr_ref_vector trail(m), fmls(m);
|
|
unsigned sz = to_app(e1)->get_num_args();
|
|
expr*const*args = to_app(e1)->get_args();
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
expr* e = args[i];
|
|
if (m.is_true(e)) {
|
|
if (!vars.contains(e)) {
|
|
trail.push_back(a.mk_numeral(rational(1), true));
|
|
vars.insert(e, trail.back());
|
|
}
|
|
continue;
|
|
}
|
|
if (m.is_false(e)) {
|
|
if (!vars.contains(e)) {
|
|
trail.push_back(a.mk_numeral(rational(0), true));
|
|
vars.insert(e, trail.back());
|
|
}
|
|
continue;
|
|
}
|
|
|
|
name = symbol('x' + std::to_string(i));
|
|
trail.push_back(m.mk_const(name, a.mk_int()));
|
|
expr* x = trail.back();
|
|
m.is_not(e,e);
|
|
vars.insert(e, x);
|
|
fmls.push_back(a.mk_le(a.mk_numeral(rational(0), true), x));
|
|
fmls.push_back(a.mk_le(x, a.mk_numeral(rational(1), true)));
|
|
}
|
|
expr_ref tmp(m);
|
|
expr_ref fml1 = translate_pb2lia(vars, e1);
|
|
expr_ref fml2 = translate_pb2lia(vars, e2);
|
|
tmp = m.mk_not(m.mk_eq(fml1, fml2));
|
|
fmls.push_back(tmp);
|
|
tmp = m.mk_and(fmls.size(), fmls.data());
|
|
return tmp;
|
|
}
|
|
|
|
static unsigned s_lemma = 0;
|
|
|
|
void pb_rewriter::validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml) {
|
|
ast_manager& m = fml.get_manager();
|
|
app_ref tmp1(m), tmp2(m);
|
|
tmp1 = m.mk_app(f, sz, args);
|
|
tmp2 = to_app(fml);
|
|
expr_ref tmp = mk_validate_rewrite(tmp1, tmp2);
|
|
dump_pb_rewrite(tmp);
|
|
}
|
|
|
|
void pb_rewriter::dump_pb_rewrite(expr* fml) {
|
|
std::ofstream out("pb_rewrite_" + std::to_string(s_lemma++) + ".smt2");
|
|
ast_smt_pp pp(m());
|
|
pp.display_smt2(out, fml);
|
|
out.close();
|
|
}
|
|
|
|
br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
|
if (m_util.is_aux_bool(f)) return BR_FAILED;
|
|
ast_manager& m = result.get_manager();
|
|
rational sum(0), maxsum(0);
|
|
for (unsigned i = 0; i < num_args; ++i) {
|
|
if (m.is_true(args[i])) {
|
|
sum += m_util.get_coeff(f, i);
|
|
maxsum += m_util.get_coeff(f, i);
|
|
}
|
|
else if (!m.is_false(args[i])) {
|
|
maxsum += m_util.get_coeff(f, i);
|
|
}
|
|
}
|
|
rational k = m_util.get_k(f);
|
|
|
|
vector<std::pair<expr*,rational> > vec;
|
|
for (unsigned i = 0; i < num_args; ++i) {
|
|
vec.push_back(std::make_pair(args[i], m_util.get_coeff(f, i)));
|
|
}
|
|
|
|
switch(f->get_decl_kind()) {
|
|
case OP_AT_MOST_K:
|
|
case OP_PB_LE:
|
|
for (unsigned i = 0; i < num_args; ++i) {
|
|
vec[i].second.neg();
|
|
}
|
|
k.neg();
|
|
break;
|
|
case OP_AT_LEAST_K:
|
|
case OP_PB_GE:
|
|
case OP_PB_EQ:
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
return BR_FAILED;
|
|
}
|
|
|
|
bool is_eq = f->get_decl_kind() == OP_PB_EQ;
|
|
br_status st = BR_DONE;
|
|
|
|
pb_ast_rewriter_util pbu(m);
|
|
pb_rewriter_util<pb_ast_rewriter_util> util(pbu);
|
|
|
|
util.unique(vec, k, is_eq);
|
|
lbool is_sat = util.normalize(vec, k, is_eq);
|
|
util.prune(vec, k, is_eq);
|
|
switch (is_sat) {
|
|
case l_true:
|
|
result = m.mk_true();
|
|
break;
|
|
case l_false:
|
|
result = m.mk_false();
|
|
break;
|
|
default: {
|
|
bool all_unit = true;
|
|
unsigned sz = vec.size();
|
|
rational slack(0);
|
|
m_args.reset();
|
|
m_coeffs.reset();
|
|
for (auto const& kv : vec) {
|
|
m_args.push_back(kv.first);
|
|
m_coeffs.push_back(kv.second);
|
|
SASSERT(kv.second.is_pos());
|
|
slack += kv.second;
|
|
all_unit &= m_coeffs.back().is_one();
|
|
}
|
|
if (is_eq) {
|
|
if (sz == 0) {
|
|
result = k.is_zero()?m.mk_true():m.mk_false();
|
|
}
|
|
else if (k.is_zero()) {
|
|
result = mk_not(m, mk_or(m, sz, m_args.data()));
|
|
}
|
|
else if (k.is_one() && all_unit && m_args.size() == 1) {
|
|
result = m_args.back();
|
|
}
|
|
else if (slack == k) {
|
|
result = mk_and(m, sz, m_args.data());
|
|
}
|
|
else {
|
|
result = m_util.mk_eq(sz, m_coeffs.data(), m_args.data(), k);
|
|
}
|
|
}
|
|
else if (all_unit && k.is_one() && sz < 10) {
|
|
result = mk_or(m, sz, m_args.data());
|
|
}
|
|
else if (all_unit && k == rational(sz)) {
|
|
result = mk_and(m, sz, m_args.data());
|
|
}
|
|
else {
|
|
expr_ref_vector conj(m), disj(m);
|
|
unsigned j = 0;
|
|
sz = m_args.size();
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
rational& c = m_coeffs[i];
|
|
if (slack < c + k) {
|
|
conj.push_back(m_args[i]);
|
|
slack -= c;
|
|
k -= c;
|
|
}
|
|
else if (c >= k && k.is_pos()) {
|
|
disj.push_back(m_args[i]);
|
|
}
|
|
else {
|
|
m_args[j] = m_args[i];
|
|
m_coeffs[j] = m_coeffs[i];
|
|
++j;
|
|
}
|
|
}
|
|
m_args.shrink(j);
|
|
m_coeffs.shrink(j);
|
|
sz = j;
|
|
if (sz > 0) {
|
|
disj.push_back(m_util.mk_ge(sz, m_coeffs.data(), m_args.data(), k));
|
|
}
|
|
if (!disj.empty()) {
|
|
conj.push_back(mk_or(disj));
|
|
}
|
|
result = mk_and(conj);
|
|
|
|
if (disj.size() > 1 || conj.size() > 1) {
|
|
st = BR_REWRITE3;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
TRACE(pb_verbose,
|
|
expr_ref tmp(m);
|
|
tmp = m.mk_app(f, num_args, args);
|
|
tout << tmp << "\n";
|
|
tout << result << "\n";
|
|
);
|
|
|
|
TRACE(pb_validate,
|
|
validate_rewrite(f, num_args, args, result););
|
|
|
|
return st;
|
|
}
|
|
|
|
|