3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-10-03 14:33:56 +00:00

Merge branch 'master' into polysat

This commit is contained in:
Jakob Rath 2023-02-01 16:28:57 +01:00
commit 20b5455d08
669 changed files with 26145 additions and 20652 deletions

View file

@ -447,12 +447,17 @@ public:
app * mk_add(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(arith_family_id, OP_ADD, arg1, arg2, arg3); }
app * mk_add(expr_ref_vector const& args) const { return mk_add(args.size(), args.data()); }
app * mk_add(expr_ref_buffer const& args) const { return mk_add(args.size(), args.data()); }
app * mk_add(ptr_buffer<expr> const& args) const { return mk_add(args.size(), args.data()); }
app * mk_add(ptr_vector<expr> const& args) const { return mk_add(args.size(), args.data()); }
app * mk_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(arith_family_id, OP_SUB, arg1, arg2); }
app * mk_sub(unsigned num_args, expr * const * args) const { return m_manager.mk_app(arith_family_id, OP_SUB, num_args, args); }
app * mk_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(arith_family_id, OP_MUL, arg1, arg2); }
app * mk_mul(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(arith_family_id, OP_MUL, arg1, arg2, arg3); }
app * mk_mul(unsigned num_args, expr * const * args) const { return num_args == 1 && is_app(args[0]) ? to_app(args[0]) : m_manager.mk_app(arith_family_id, OP_MUL, num_args, args); }
app * mk_mul(ptr_buffer<expr> const& args) const { return mk_mul(args.size(), args.data()); }
app * mk_mul(ptr_vector<expr> const& args) const { return mk_mul(args.size(), args.data()); }
app * mk_mul(expr_ref_vector const& args) const { return mk_mul(args.size(), args.data()); }
app * mk_uminus(expr * arg) const { return m_manager.mk_app(arith_family_id, OP_UMINUS, arg); }
app * mk_div(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_DIV, arg1, arg2); }
app * mk_idiv(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_IDIV, arg1, arg2); }

View file

@ -529,19 +529,6 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
return nullptr;
}
return mk_array_ext(arity, domain, parameters[0].get_int());
case OP_ARRAY_MAXDIFF:
case OP_ARRAY_MINDIFF: {
if (num_parameters != 0)
m_manager->raise_exception("min/maxdiff don't take any parameters");
if (arity != 2 || domain[0] != domain[1] || !is_array_sort(domain[0]) || 1 != get_array_arity(domain[0]))
m_manager->raise_exception("min/maxdiff don't take two arrays of same sort and with integer index");
sort* idx = get_array_domain(domain[0], 0);
arith_util arith(*m_manager);
if (!arith.is_int(idx))
m_manager->raise_exception("min/maxdiff take integer index domain");
return m_manager->mk_func_decl(k == OP_ARRAY_MAXDIFF ? symbol("maxdiff") : symbol("mindiff"),
arity, domain, arith.mk_int(), func_decl_info(m_family_id, k));
}
case OP_ARRAY_DEFAULT:
return mk_default(arity, domain);
case OP_SET_UNION:
@ -603,9 +590,6 @@ void array_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol con
op_names.push_back(builtin_name("as-array", OP_AS_ARRAY));
op_names.push_back(builtin_name("array-ext", OP_ARRAY_EXT));
op_names.push_back(builtin_name("mindiff", OP_ARRAY_MINDIFF));
op_names.push_back(builtin_name("maxdiff", OP_ARRAY_MAXDIFF));
#if 0
op_names.push_back(builtin_name("set-has-size", OP_SET_HAS_SIZE));
op_names.push_back(builtin_name("card", OP_SET_CARD));

View file

@ -45,8 +45,6 @@ enum array_op_kind {
OP_ARRAY_EXT,
OP_ARRAY_DEFAULT,
OP_ARRAY_MAP,
OP_ARRAY_MAXDIFF,
OP_ARRAY_MINDIFF,
OP_SET_UNION,
OP_SET_INTERSECT,
OP_SET_DIFFERENCE,
@ -161,8 +159,6 @@ public:
bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); }
bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); }
bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); }
bool is_maxdiff(expr const* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAXDIFF); }
bool is_mindiff(expr const* n) const { return is_app_of(n, m_fid, OP_ARRAY_MINDIFF); }
bool is_set_has_size(expr* e) const { return is_app_of(e, m_fid, OP_SET_HAS_SIZE); }
bool is_set_card(expr* e) const { return is_app_of(e, m_fid, OP_SET_CARD); }
bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); }
@ -189,8 +185,6 @@ public:
bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value);
MATCH_BINARY(is_subset);
MATCH_BINARY(is_maxdiff);
MATCH_BINARY(is_mindiff);
};
class array_util : public array_recognizers {
@ -213,6 +207,10 @@ public:
return mk_store(args.size(), args.data());
}
app* mk_store(ptr_buffer<expr> const& args) const {
return mk_store(args.size(), args.data());
}
app * mk_select(unsigned num_args, expr * const * args) const {
return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args);
}

View file

@ -856,11 +856,11 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_paren
case PR_MODUS_PONENS_OEQ: return mk_proof_decl("mp~", k, 2, m_mp_oeq_decl);
case PR_TH_LEMMA: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_decls);
case PR_HYPER_RESOLVE: return mk_proof_decl("hyper-res", k, num_parents, m_hyper_res_decl0);
case PR_ASSUMPTION_ADD: return mk_proof_decl("add-assume", k, num_parents, m_assumption_add_decl);
case PR_LEMMA_ADD: return mk_proof_decl("add-lemma", k, num_parents, m_lemma_add_decl);
case PR_TH_ASSUMPTION_ADD: return mk_proof_decl("add-th-assume", k, num_parents, m_th_assumption_add_decl);
case PR_TH_LEMMA_ADD: return mk_proof_decl("add-th-lemma", k, num_parents, m_th_lemma_add_decl);
case PR_REDUNDANT_DEL: return mk_proof_decl("del-redundant", k, num_parents, m_redundant_del_decl);
case PR_ASSUMPTION_ADD: return mk_proof_decl("assume", k, num_parents, m_assumption_add_decl);
case PR_LEMMA_ADD: return mk_proof_decl("infer", k, num_parents, m_lemma_add_decl);
case PR_TH_ASSUMPTION_ADD: return mk_proof_decl("th-assume", k, num_parents, m_th_assumption_add_decl);
case PR_TH_LEMMA_ADD: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_add_decl);
case PR_REDUNDANT_DEL: return mk_proof_decl("del", k, num_parents, m_redundant_del_decl);
case PR_CLAUSE_TRAIL: return mk_proof_decl("proof-trail", k, num_parents, false);
default:
UNREACHABLE();
@ -1673,6 +1673,7 @@ bool ast_manager::are_distinct(expr* a, expr* b) const {
}
void ast_manager::add_lambda_def(func_decl* f, quantifier* q) {
TRACE("model", tout << "add lambda def " << mk_pp(q, *this) << "\n");
m_lambda_defs.insert(f, q);
f->get_info()->set_lambda(true);
inc_ref(q);
@ -1969,6 +1970,14 @@ app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2,
return mk_app(fid, k, 0, nullptr, 3, args);
}
app * ast_manager::mk_app(symbol const& name, unsigned n, expr* const* args, sort* range) {
ptr_buffer<sort> sorts;
for (unsigned i = 0; i < n; ++i)
sorts.push_back(args[i]->get_sort());
return mk_app(mk_func_decl(name, n, sorts.data(), range), n, args);
}
sort * ast_manager::mk_sort(symbol const & name, sort_info * info) {
unsigned sz = sort::get_obj_size();
void * mem = allocate_node(sz);
@ -2242,7 +2251,9 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
if (type_error) {
std::ostringstream buffer;
buffer << "Wrong number of arguments (" << num_args
<< ") passed to function " << mk_pp(decl, *this);
<< ") passed to function " << mk_pp(decl, *this) << " ";
for (unsigned i = 0; i < num_args; ++i)
buffer << "\narg: " << mk_pp(args[i], *this) << "\n";
throw ast_exception(std::move(buffer).str());
}
app * r = nullptr;

View file

@ -731,6 +731,8 @@ public:
unsigned get_num_args() const { return m_num_args; }
expr * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; }
expr * const * get_args() const { return m_args; }
std::tuple<expr*,expr*> args2() const { SASSERT(m_num_args == 2); return {get_arg(0), get_arg(1)}; }
std::tuple<expr*,expr*,expr*> args3() const { SASSERT(m_num_args == 3); return {get_arg(0), get_arg(1), get_arg(2)}; }
unsigned get_size() const { return get_obj_size(get_num_args()); }
expr * const * begin() const { return m_args; }
expr * const * end() const { return m_args + m_num_args; }
@ -1385,6 +1387,7 @@ inline bool is_app_of(expr const * n, family_id fid, decl_kind k) { return n->ge
inline bool is_sort_of(sort const * s, family_id fid, decl_kind k) { return s->is_sort_of(fid, k); }
inline bool is_uninterp_const(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; }
inline bool is_uninterp(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_family_id() == null_family_id; }
inline bool is_uninterp(func_decl const * n) { return n->get_family_id() == null_family_id; }
inline bool is_decl_of(func_decl const * d, family_id fid, decl_kind k) { return d->get_family_id() == fid && d->get_decl_kind() == k; }
inline bool is_ground(expr const * n) { return is_app(n) && to_app(n)->is_ground(); }
inline bool is_non_ground(expr const * n) { return ( ! is_ground(n)); }
@ -1628,6 +1631,7 @@ public:
void add_lambda_def(func_decl* f, quantifier* q);
quantifier* is_lambda_def(func_decl* f);
quantifier* is_lambda_def(app* e) { return is_lambda_def(e->get_decl()); }
obj_map<func_decl, quantifier*> const& lambda_defs() const { return m_lambda_defs; }
symbol const& lambda_def_qid() const { return m_lambda_def; }
@ -1881,6 +1885,8 @@ public:
return mk_app(decl, 3, args);
}
app * mk_app(symbol const& name, unsigned n, expr* const* args, sort* range);
app * mk_const(func_decl * decl) {
SASSERT(decl->get_arity() == 0);
return mk_app(decl, static_cast<unsigned>(0), static_cast<expr**>(nullptr));

View file

@ -86,6 +86,7 @@ class ll_printer {
default:
display_child_ref(n);
}
}
template<typename T>

View file

@ -64,6 +64,17 @@ void ast_pp_util::display_decls(std::ostream& out) {
m_rec_decls = n;
}
void ast_pp_util::reset() {
coll.reset();
m_removed.reset();
m_sorts.clear(0u);
m_decls.clear(0u);
m_rec_decls.clear(0u);
m_is_defined.reset();
m_defined.reset();
m_defined_lim.reset();
}
void ast_pp_util::display_skolem_decls(std::ostream& out) {
ast_smt_pp pp(m);
unsigned n = coll.get_num_decls();

View file

@ -40,8 +40,7 @@ class ast_pp_util {
ast_pp_util(ast_manager& m): m(m), m_env(m), m_rec_decls(0), m_decls(0), m_sorts(0), m_defined(m), coll(m) {}
void reset() { coll.reset(); m_removed.reset(); m_sorts.clear(0u); m_decls.clear(0u); m_rec_decls.clear(0u);
m_is_defined.reset(); m_defined.reset(); m_defined_lim.reset(); }
void reset();
void collect(expr* e);

View file

@ -561,15 +561,18 @@ class smt2_printer {
void pp_var(var * v) {
format * f;
if (v->get_idx() < m_var_names.size()) {
symbol s = m_var_names[m_var_names.size() - v->get_idx() - 1];
unsigned idx = v->get_idx();
if (idx < m_var_names.size()) {
symbol s;
if (m_reverse && idx < m_arity)
s = m_var_names[m_var_names.size() - m_arity + idx];
else
s = m_var_names[m_var_names.size() - idx - 1];
std::string vname;
if (is_smt2_quoted_symbol (s)) {
vname = mk_smt2_quoted_symbol (s);
}
else {
vname = s.str();
}
if (is_smt2_quoted_symbol (s))
vname = mk_smt2_quoted_symbol (s);
else
vname = s.str();
f = mk_string(m(), vname);
}
else {
@ -1139,9 +1142,13 @@ public:
r = mk_seq1<format**, f2f>(m(), args, args+3, f2f(), cmd);
}
bool m_reverse = false;
unsigned m_arity = 0;
void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd) {
void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd, bool reverse) {
unsigned len;
flet<bool> _reverse(m_reverse, reverse);
m_arity = f->get_arity();
format * fname = m_env.pp_fdecl_name(f, len);
register_var_names(f->get_arity());
format * args[4];
@ -1202,9 +1209,9 @@ void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const &
pr(f, r, cmd);
}
void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) {
void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd, bool reverse) {
smt2_printer pr(env, p);
pr(f, e, r, cmd);
pr(f, e, r, cmd, reverse);
}
void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p,
@ -1251,7 +1258,6 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen
if (!f) return out << "null";
ast_manager & m = env.get_manager();
format_ref r(fm(m));
sbuffer<symbol> var_names;
mk_smt2_format(f, env, p, r, cmd);
if (indent > 0)
r = mk_indent(m, indent, r.get());
@ -1259,18 +1265,25 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen
return out;
}
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) {
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd, bool reverse) {
if (!f) return out << "null";
ast_manager & m = env.get_manager();
format_ref r(fm(m));
sbuffer<symbol> var_names;
mk_smt2_format(f, e, env, p, r, cmd);
mk_smt2_format(f, e, env, p, r, cmd, reverse);
if (indent > 0)
r = mk_indent(m, indent, r.get());
pp(out, r.get(), m, p);
return out;
}
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) {
return ast_smt2_pp(out, f, e, env, p, indent, cmd, false);
}
std::ostream & ast_smt2_pp_rev(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) {
return ast_smt2_pp(out, f, e, env, p, indent, cmd, true);
}
std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned indent,
unsigned num_vars, char const * var_prefix) {

View file

@ -104,7 +104,8 @@ std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & e
unsigned num_vars = 0, char const * var_prefix = nullptr);
std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0);
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun");
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun");
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun", bool reverse = false);
std::ostream & ast_smt2_pp_rev(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun");
std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref());
std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector<std::pair<func_decl*, expr*>> const& funs, smt2_pp_environment & env, params_ref const & p = params_ref());

View file

@ -101,6 +101,8 @@ expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx);
*/
expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args);
app * mk_and(ast_manager & m, unsigned num_args, app * const * args);
inline expr * mk_and(ast_manager & m, ptr_vector<expr> const& args) { return mk_and(m, args.size(), args.data()); }
inline expr * mk_and(ast_manager & m, ptr_buffer<expr> const& args) { return mk_and(m, args.size(), args.data()); }
inline expr * mk_and(ast_manager & m, expr* a, expr* b) { expr* args[2] = { a, b }; return mk_and(m, 2, args); }
inline app_ref mk_and(app_ref_vector const& args) { return app_ref(mk_and(args.get_manager(), args.size(), args.data()), args.get_manager()); }
inline expr_ref mk_and(expr_ref_vector const& args) { return expr_ref(mk_and(args.get_manager(), args.size(), args.data()), args.get_manager()); }

View file

@ -411,6 +411,11 @@ public:
app * mk_numeral(rational const & val, sort* s) const;
app * mk_numeral(rational const & val, unsigned bv_size) const;
app * mk_numeral(uint64_t u, unsigned bv_size) const { return mk_numeral(rational(u, rational::ui64()), bv_size); }
app * mk_zero(sort* s) const { return mk_numeral(rational::zero(), s); }
app * mk_zero(unsigned bv_size) const { return mk_numeral(rational::zero(), bv_size); }
app * mk_one(sort* s) const { return mk_numeral(rational::one(), s); }
app * mk_one(unsigned bv_size) const { return mk_numeral(rational::one(), bv_size); }
sort * mk_sort(unsigned bv_size);
unsigned get_bv_size(sort const * s) const {
@ -430,6 +435,9 @@ public:
}
app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); }
app * mk_concat(expr_ref_vector const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
app * mk_concat(expr_ref_buffer const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
app * mk_concat(ptr_buffer<expr> const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
app * mk_concat(ptr_vector<expr> const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
app * mk_bv_or(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BOR, num, args); }
app * mk_bv_and(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BAND, num, args); }
app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); }
@ -445,8 +453,17 @@ public:
app * mk_bv_srem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); }
app * mk_bv_smod(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSMOD, arg1, arg2); }
app * mk_bv_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); }
app * mk_bv_add(ptr_buffer<expr> const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
app * mk_bv_add(ptr_vector<expr> const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
app * mk_bv_add(expr_ref_vector const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
app * mk_bv_add(expr_ref_buffer const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
app * mk_bv_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); }
app * mk_bv_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); }
app * mk_bv_mul(unsigned n, expr* const* args) const { return m_manager.mk_app(get_fid(), OP_BMUL, n, args); }
app* mk_bv_mul(ptr_buffer<expr> const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
app* mk_bv_mul(ptr_vector<expr> const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
app* mk_bv_mul(expr_ref_vector const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
app* mk_bv_mul(expr_ref_buffer const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
app * mk_bv_udiv(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV, arg1, arg2); }
app * mk_bv_udiv_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); }
app * mk_bv_udiv0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BUDIV0, arg); }

View file

@ -0,0 +1,12 @@
z3_add_component(converters
SOURCES
expr_inverter.cpp
equiv_proof_converter.cpp
generic_model_converter.cpp
horn_subsume_model_converter.cpp
model_converter.cpp
proof_converter.cpp
replace_proof_converter.cpp
COMPONENT_DEPENDENCIES
model
)

View file

@ -0,0 +1,124 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
converter.h
Abstract:
Abstract class and templates for proof and model converters.
Author:
Leonardo (leonardo) 2011-11-14
Notes:
--*/
#pragma once
#include "util/vector.h"
#include "util/ref.h"
#include "ast/ast_translation.h"
class converter {
unsigned m_ref_count;
public:
converter():m_ref_count(0) {}
virtual ~converter() = default;
void inc_ref() { ++m_ref_count; }
void dec_ref() {
--m_ref_count;
if (m_ref_count == 0) {
dealloc(this);
}
}
virtual void cancel() {}
virtual void display(std::ostream & out) = 0;
};
template<typename T>
class concat_converter : public T {
protected:
ref<T> m_c1;
ref<T> m_c2;
template<typename T2>
T * translate_core(ast_translation & translator) {
T * t1 = m_c1->translate(translator);
T * t2 = m_c2->translate(translator);
return alloc(T2, t1, t2);
}
public:
concat_converter(T * c1, T * c2):m_c1(c1), m_c2(c2) {}
void cancel() override {
m_c2->cancel();
m_c1->cancel();
}
virtual char const * get_name() const = 0;
void display(std::ostream & out) override {
m_c1->display(out);
m_c2->display(out);
}
};
template<typename T>
class concat_star_converter : public T {
protected:
ref<T> m_c1;
ptr_vector<T> m_c2s;
unsigned_vector m_szs;
template<typename T2>
T * translate_core(ast_translation & translator) {
T * t1 = m_c1 ? m_c1->translate(translator) : nullptr;
ptr_buffer<T> t2s;
for (T* c : m_c2s)
t2s.push_back(c ? c->translate(translator) : nullptr);
return alloc(T2, t1, m_c2s.size(), t2s.data(), m_szs.data());
}
public:
concat_star_converter(T * c1, unsigned num, T * const * c2s, unsigned * szs):
m_c1(c1) {
for (unsigned i = 0; i < num; i++) {
T * c2 = c2s[i];
if (c2)
c2->inc_ref();
m_c2s.push_back(c2);
m_szs.push_back(szs[i]);
}
}
~concat_star_converter() override {
for (T* c : m_c2s)
if (c) c->dec_ref();
}
void cancel() override {
if (m_c1)
m_c1->cancel();
for (T* c : m_c2s)
if (c) c->cancel();
}
virtual char const * get_name() const = 0;
void display(std::ostream & out) override {
if (m_c1)
m_c1->display(out);
for (T* c : m_c2s)
if (c) c->display(out);
}
};

View file

@ -0,0 +1,35 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
equiv_proof_converter.cpp
Abstract:
Proof converter that applies equivalence rule to leaves.
Author:
Nikolaj Bjorner (nbjorner) 2012-11-23
Revision History:
--*/
#include "ast/converters/equiv_proof_converter.h"
#include "ast/ast_pp.h"
#include "ast/scoped_proof.h"
void equiv_proof_converter::insert(expr* fml1, expr* fml2) {
if (fml1 != fml2) {
scoped_proof _sp(m);
proof_ref p1(m), p2(m), p3(m);
p1 = m.mk_asserted(fml1);
p2 = m.mk_rewrite(fml1, fml2);
p3 = m.mk_modus_ponens(p1, p2);
TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";);
SASSERT(m.has_fact(p3));
m_replace.insert(p3);
}
}

View file

@ -0,0 +1,49 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
equiv_proof_converter.h
Abstract:
Proof converter that applies equivalence rule to leaves.
Given a proof P with occurrences of [asserted fml]
replace [asserted fml] by a proof of the form
[mp [asserted fml'] [~ fml fml']]
Author:
Nikolaj Bjorner (nbjorner) 2012-11-23
Revision History:
--*/
#pragma once
#include "ast/converters/replace_proof_converter.h"
class equiv_proof_converter : public proof_converter {
ast_manager& m;
replace_proof_converter m_replace;
public:
equiv_proof_converter(ast_manager& m): m(m), m_replace(m) {}
proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override {
return m_replace(m, num_source, source);
}
proof_converter * translate(ast_translation & translator) override {
return m_replace.translate(translator);
}
void insert(expr* fml1, expr* fml2);
ast_manager& get_manager() { return m; }
void display(std::ostream & out) override {}
};

View file

@ -0,0 +1,858 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
expr_inverter.cpp
Abstract:
inverter interface and instance
Author:
Nikolaj Bjorner (nbjorner) 2022-10-11
--*/
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_util.h"
#include "ast/arith_decl_plugin.h"
#include "ast/converters/expr_inverter.h"
class basic_expr_inverter : public iexpr_inverter {
iexpr_inverter& inv;
bool process_eq(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) {
expr* v;
expr* t;
if (uncnstr(arg1))
v = arg1, t = arg2;
else if (uncnstr(arg2))
v = arg2, t = arg1;
else
return false;
expr_ref d(m);
if (!inv.mk_diff(t, d))
return false;
mk_fresh_uncnstr_var_for(f, r);
if (m_mc)
add_def(v, m.mk_ite(r, t, d));
return true;
}
public:
basic_expr_inverter(ast_manager& m, iexpr_inverter& inv) : iexpr_inverter(m), inv(inv) {}
family_id get_fid() const override { return m.get_basic_family_id(); }
/**
* if (c, x, x') -> fresh
* x := fresh
* x' := fresh
*
* if (x, x', e) -> fresh
* x := true
* x' := fresh
*
* if (x, t, x') -> fresh
* x := false
* x' := fresh
*
* not x -> fresh
* x := not fresh
*
* x & x' -> fresh
* x := fresh
* x' := true
*
* x or x' -> fresh
* x := fresh
* x' := false
*
* x = t -> fresh
* x := if(fresh, t, diff(t))
* where diff is a diagnonalization function available in domains of size > 1.
*
*/
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
SASSERT(f->get_family_id() == m.get_basic_family_id());
switch (f->get_decl_kind()) {
case OP_ITE:
SASSERT(num == 3);
if (uncnstr(args[1]) && uncnstr(args[2])) {
mk_fresh_uncnstr_var_for(f, r);
add_def(args[1], r);
add_def(args[2], r);
return true;
}
if (uncnstr(args[0]) && uncnstr(args[1])) {
mk_fresh_uncnstr_var_for(f, r);
add_def(args[0], m.mk_true());
add_def(args[1], r);
return true;
}
if (uncnstr(args[0]) && uncnstr(args[2])) {
mk_fresh_uncnstr_var_for(f, r);
add_def(args[0], m.mk_false());
add_def(args[2], r);
return true;
}
return false;
case OP_NOT:
SASSERT(num == 1);
if (uncnstr(args[0])) {
mk_fresh_uncnstr_var_for(f, r);
add_def(args[0], m.mk_not(r));
return true;
}
return false;
case OP_AND:
if (num > 0 && uncnstr(num, args)) {
mk_fresh_uncnstr_var_for(f, r);
add_defs(num, args, r, m.mk_true());
return true;
}
return false;
case OP_OR:
if (num > 0 && uncnstr(num, args)) {
mk_fresh_uncnstr_var_for(f, r);
add_defs(num, args, r, m.mk_false());
return true;
}
return false;
case OP_EQ:
SASSERT(num == 2);
return process_eq(f, args[0], args[1], r);
default:
return false;
}
return false;
}
bool mk_diff(expr* t, expr_ref& r) override {
SASSERT(m.is_bool(t));
r = mk_not(m, t);
return true;
}
};
class arith_expr_inverter : public iexpr_inverter {
arith_util a;
public:
arith_expr_inverter(ast_manager& m) : iexpr_inverter(m), a(m) {}
family_id get_fid() const override { return a.get_family_id(); }
bool process_le_ge(func_decl* f, expr* arg1, expr* arg2, bool le, expr_ref& r) {
expr* v;
expr* t;
if (uncnstr(arg1)) {
v = arg1;
t = arg2;
}
else if (uncnstr(arg2)) {
v = arg2;
t = arg1;
le = !le;
}
else
return false;
mk_fresh_uncnstr_var_for(f, r);
if (m_mc) {
// v = ite(u, t, t + 1) if le
// v = ite(u, t, t - 1) if !le
add_def(v, m.mk_ite(r, t, a.mk_add(t, a.mk_numeral(rational(le ? 1 : -1), arg1->get_sort()))));
}
return true;
}
bool process_add(unsigned num, expr* const* args, expr_ref& u) {
if (num == 0)
return false;
unsigned i;
expr* v = nullptr;
for (i = 0; i < num; i++) {
expr* arg = args[i];
if (uncnstr(arg)) {
v = arg;
break;
}
}
if (v == nullptr)
return false;
mk_fresh_uncnstr_var_for(v->get_sort(), u);
if (!m_mc)
return true;
ptr_buffer<expr> new_args;
for (unsigned j = 0; j < num; j++)
if (j != i)
new_args.push_back(args[j]);
if (new_args.empty())
add_def(v, u);
else {
expr* rest = a.mk_add(new_args);
add_def(v, a.mk_sub(u, rest));
}
return true;
}
bool process_arith_mul(unsigned num, expr* const* args, expr_ref & u) {
if (num == 0)
return false;
sort* s = args[0]->get_sort();
if (uncnstr(num, args)) {
mk_fresh_uncnstr_var_for(s, u);
if (m_mc)
add_defs(num, args, u, a.mk_numeral(rational(1), s));
return true;
}
// c * v case for reals
bool is_int;
rational val;
if (num == 2 && uncnstr(args[1]) && a.is_numeral(args[0], val, is_int) && !is_int) {
if (val.is_zero())
return false;
mk_fresh_uncnstr_var_for(s, u);
if (m_mc) {
val = rational(1) / val;
add_def(args[1], a.mk_mul(a.mk_numeral(val, false), u));
}
return true;
}
return false;
}
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
SASSERT(f->get_family_id() == a.get_family_id());
switch (f->get_decl_kind()) {
case OP_ADD:
return process_add(num, args, r);
case OP_MUL:
return process_arith_mul(num, args, r);
case OP_LE:
SASSERT(num == 2);
return process_le_ge(f, args[0], args[1], true, r);
case OP_GE:
SASSERT(num == 2);
return process_le_ge(f, args[0], args[1], false, r);
default:
return false;
}
}
bool mk_diff(expr* t, expr_ref& r) override {
SASSERT(a.is_int_real(t));
r = a.mk_add(t, a.mk_numeral(rational(1), a.is_int(t)));
return true;
}
};
class bv_expr_inverter : public iexpr_inverter {
bv_util bv;
bool process_add(unsigned num, expr* const* args, expr_ref& u) {
if (num == 0)
return false;
unsigned i;
expr* v = nullptr;
for (i = 0; i < num; i++) {
expr* arg = args[i];
if (uncnstr(arg)) {
v = arg;
break;
}
}
if (!v)
return false;
mk_fresh_uncnstr_var_for(v->get_sort(), u);
if (!m_mc)
return true;
ptr_buffer<expr> new_args;
for (unsigned j = 0; j < num; j++)
if (j != i)
new_args.push_back(args[j]);
if (new_args.empty())
add_def(v, u);
else {
expr* rest = bv.mk_bv_add(new_args);
add_def(v, bv.mk_bv_sub(u, rest));
}
return true;
}
bool process_bv_mul(func_decl* f, unsigned num, expr* const* args, expr_ref& r) {
if (num == 0)
return false;
if (uncnstr(num, args)) {
sort* s = args[0]->get_sort();
mk_fresh_uncnstr_var_for(f, r);
if (m_mc)
add_defs(num, args, r, bv.mk_one(s));
return true;
}
// c * v (c is odd) case
unsigned sz;
rational val;
rational inv;
if (num == 2 &&
uncnstr(args[1]) &&
bv.is_numeral(args[0], val, sz) &&
val.mult_inverse(sz, inv)) {
mk_fresh_uncnstr_var_for(f, r);
if (m_mc)
add_def(args[1], bv.mk_bv_mul(bv.mk_numeral(inv, sz), r));
return true;
}
//
// x * K -> fresh[hi-sh-1:0] ++ 0...0
// where sh = parity of K
// then x -> J^-1*fresh
// where J = K >> sh
// Because x * K = fresh * K * J^-1 = fresh * 2^sh = fresh[hi-sh-1:0] ++ 0...0
//
if (num == 2 &&
uncnstr(args[1]) &&
bv.is_numeral(args[0], val, sz) &&
val.is_pos()) {
unsigned sh = 0;
while (val.is_even()) {
val /= rational(2);
++sh;
}
mk_fresh_uncnstr_var_for(f, r);
if (sh > 0)
r = bv.mk_concat(bv.mk_extract(sz - sh - 1, 0, r), bv.mk_zero(sh));
if (m_mc) {
rational inv_r;
VERIFY(val.mult_inverse(sz, inv_r));
add_def(args[1], bv.mk_bv_mul(bv.mk_numeral(inv_r, sz), r));
}
return true;
}
//
// assume x is unconstrained, we can handle general multiplication as follows:
// x * y -> if y = 0 then y else fresh << parity(y)
// and x -> if y = 0 then y else (y >> parity(y))^-1
// parity can be defined using a "giant" ite expression.
//
#if 0
for (unsigned i = 0; i < num; ++i)
if (uncnstr(args[i]))
IF_VERBOSE(11, verbose_stream() << "MISSED mult-unconstrained " << mk_bounded_pp(args[i], m) << "\n");
#endif
return false;
}
bool process_extract(func_decl* f, expr* arg, expr_ref& r) {
if (!uncnstr(arg))
return false;
mk_fresh_uncnstr_var_for(f, r);
if (!m_mc)
return true;
unsigned high = bv.get_extract_high(f);
unsigned low = bv.get_extract_low(f);
unsigned bv_size = bv.get_bv_size(arg->get_sort());
if (bv_size == high - low + 1)
add_def(arg, r);
else {
ptr_buffer<expr> args;
if (high < bv_size - 1)
args.push_back(bv.mk_zero(bv_size - high - 1));
args.push_back(r);
if (low > 0)
args.push_back(bv.mk_zero(low));
add_def(arg, bv.mk_concat(args.size(), args.data()));
}
return true;
}
bool process_bv_div(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) {
if (uncnstr(arg1) && uncnstr(arg2)) {
sort* s = arg1->get_sort();
mk_fresh_uncnstr_var_for(f, r);
if (m_mc) {
add_def(arg1, r);
add_def(arg2, bv.mk_one(s));
}
return true;
}
return false;
}
bool process_concat(func_decl* f, unsigned num, expr* const* args, expr_ref& r) {
if (num == 0)
return false;
if (!uncnstr(num, args))
return false;
mk_fresh_uncnstr_var_for(f, r);
if (m_mc) {
unsigned i = num;
unsigned low = 0;
while (i > 0) {
--i;
expr* arg = args[i];
unsigned sz = bv.get_bv_size(arg);
add_def(arg, bv.mk_extract(low + sz - 1, low, r));
low += sz;
}
}
return true;
}
bool process_bv_le(func_decl* f, expr* arg1, expr* arg2, bool is_signed, expr_ref& r) {
unsigned bv_sz = bv.get_bv_size(arg1);
if (uncnstr(arg1) && uncnstr(arg2)) {
mk_fresh_uncnstr_var_for(f, r);
if (m_mc) {
add_def(arg1, m.mk_ite(r, bv.mk_zero(bv_sz), bv.mk_one(bv_sz)));
add_def(arg2, bv.mk_zero(bv_sz));
}
return true;
}
if (uncnstr(arg1)) {
// v <= t
expr* v = arg1;
expr* t = arg2;
// v <= t ---> (u or t == MAX) u is fresh
// add definition v = ite(u or t == MAX, t, t+1)
rational MAX;
if (is_signed)
MAX = rational::power_of_two(bv_sz - 1) - rational(1);
else
MAX = rational::power_of_two(bv_sz) - rational(1);
mk_fresh_uncnstr_var_for(f, r);
r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MAX, bv_sz)));
if (m_mc)
add_def(v, m.mk_ite(r, t, bv.mk_bv_add(t, bv.mk_one(bv_sz))));
return true;
}
if (uncnstr(arg2)) {
// v >= t
expr* v = arg2;
expr* t = arg1;
// v >= t ---> (u ot t == MIN) u is fresh
// add definition v = ite(u or t == MIN, t, t-1)
rational MIN;
if (is_signed)
MIN = -rational::power_of_two(bv_sz - 1);
else
MIN = rational(0);
mk_fresh_uncnstr_var_for(f, r);
r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MIN, bv_sz)));
if (m_mc)
add_def(v, m.mk_ite(r, t, bv.mk_bv_sub(t, bv.mk_one(bv_sz))));
return true;
}
return false;
}
bool process_bvnot(expr* e, expr_ref& r) {
if (!uncnstr(e))
return false;
mk_fresh_uncnstr_var_for(e->get_sort(), r);
if (m_mc)
add_def(e, bv.mk_bv_not(r));
return true;
}
bool process_shift(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) {
if (uncnstr(arg1) && uncnstr(arg2)) {
mk_fresh_uncnstr_var_for(f, r);
if (m_mc) {
add_def(arg1, r);
add_def(arg2, bv.mk_zero(arg2->get_sort()));
}
return true;
}
return false;
}
public:
bv_expr_inverter(ast_manager& m) : iexpr_inverter(m), bv(m) {}
family_id get_fid() const override { return bv.get_family_id(); }
/**
* x + t -> fresh
* x := fresh - t
*
* x * x' * x'' -> fresh
* x := fresh
* x', x'' := 1
*
* c * x -> fresh, c is odd
* x := fresh*c^-1
*
* x[sz-1:0] -> fresh
* x := fresh
*
* x[hi:lo] -> fresh
* x := fresh1 ++ fresh ++ fresh2
*
* x udiv x', x sdiv x' -> fresh
* x' := 1
* x := fresh
*
* x ++ x' ++ x'' -> fresh
* x := fresh[hi1:lo1]
* x' := fresh[hi2:lo2]
* x'' := fresh[hi3:lo3]
*
* x <= t -> fresh or t == MAX
* x := if(fresh, t, t + 1)
* t <= x -> fresh or t == MIN
* x := if(fresh, t, t - 1)
*
* ~x -> fresh
* x := ~fresh
*
* x | y -> fresh
* x := fresh
* y := 0
*
*/
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
SASSERT(f->get_family_id() == bv.get_family_id());
switch (f->get_decl_kind()) {
case OP_BADD:
return process_add(num, args, r);
case OP_BMUL:
return process_bv_mul(f, num, args, r);
case OP_BSDIV:
case OP_BUDIV:
case OP_BSDIV_I:
case OP_BUDIV_I:
SASSERT(num == 2);
return process_bv_div(f, args[0], args[1], r);
case OP_SLEQ:
SASSERT(num == 2);
return process_bv_le(f, args[0], args[1], true, r);
case OP_ULEQ:
SASSERT(num == 2);
return process_bv_le(f, args[0], args[1], false, r);
case OP_CONCAT:
return process_concat(f, num, args, r);
case OP_EXTRACT:
SASSERT(num == 1);
return process_extract(f, args[0], r);
case OP_BNOT:
SASSERT(num == 1);
return process_bvnot(args[0], r);
case OP_BOR:
if (num > 0 && uncnstr(num, args)) {
sort* s = args[0]->get_sort();
mk_fresh_uncnstr_var_for(f, r);
if (m_mc)
add_defs(num, args, r, bv.mk_zero(s));
return true;
}
return false;
case OP_BAND:
if (num > 0 && uncnstr(num, args)) {
sort* s = args[0]->get_sort();
mk_fresh_uncnstr_var_for(f, r);
if (m_mc)
add_defs(num, args, r, bv.mk_numeral(rational::power_of_two(bv.get_bv_size(s)) - 1, s));
return true;
}
return false;
case OP_BSHL:
case OP_BASHR:
case OP_BLSHR:
return process_shift(f, args[0], args[1], r);
default:
return false;
}
}
bool mk_diff(expr* t, expr_ref& r) override {
SASSERT(bv.is_bv(t));
r = bv.mk_bv_not(t);
return true;
}
};
/**
* F[select(x, i)] -> F[fresh]
* x := const(fresh)
* F[store(x, ..., x')] -> F[fresh]
* x' := select(x, ...)
* x := fresh
*/
class array_expr_inverter : public iexpr_inverter {
array_util a;
iexpr_inverter& inv;
public:
array_expr_inverter(ast_manager& m, iexpr_inverter& s) : iexpr_inverter(m), a(m), inv(s) {}
family_id get_fid() const override { return a.get_family_id(); }
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
SASSERT(f->get_family_id() == a.get_family_id());
switch (f->get_decl_kind()) {
case OP_SELECT:
if (uncnstr(args[0])) {
mk_fresh_uncnstr_var_for(f, r);
sort* s = args[0]->get_sort();
if (m_mc)
add_def(args[0], a.mk_const_array(s, r));
return true;
}
return false;
case OP_STORE:
if (uncnstr(args[0]) && uncnstr(args[num - 1])) {
mk_fresh_uncnstr_var_for(f, r);
if (m_mc) {
add_def(args[num - 1], m.mk_app(a.get_family_id(), OP_SELECT, num - 1, args));
add_def(args[0], r);
}
return true;
}
return false;
default:
return false;
}
}
bool mk_diff(expr* t, expr_ref& r) override {
sort* s = t->get_sort();
SASSERT(a.is_array(s));
if (m.is_uninterp(get_array_range(s)))
return false;
unsigned arity = get_array_arity(s);
for (unsigned i = 0; i < arity; i++)
if (m.is_uninterp(get_array_domain(s, i)))
return false;
// building
// r = (store t i1 ... in d)
// where i1 ... in are arbitrary values
// and d is a term different from (select t i1 ... in)
expr_ref_vector new_args(m);
new_args.push_back(t);
for (unsigned i = 0; i < arity; i++)
new_args.push_back(m.get_some_value(get_array_domain(s, i)));
expr_ref sel(m);
sel = a.mk_select(new_args);
expr_ref diff_sel(m);
if (!inv.mk_diff(sel, diff_sel))
return false;
new_args.push_back(diff_sel);
r = a.mk_store(new_args);
return true;
}
};
class dt_expr_inverter : public iexpr_inverter {
datatype_util dt;
public:
dt_expr_inverter(ast_manager& m) : iexpr_inverter(m), dt(m) {}
family_id get_fid() const override { return dt.get_family_id(); }
/**
* head(x) -> fresh
* x := cons(fresh, arb)
*/
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
if (dt.is_accessor(f)) {
SASSERT(num == 1);
if (uncnstr(args[0])) {
if (!m_mc) {
mk_fresh_uncnstr_var_for(f, r);
return true;
}
func_decl* c = dt.get_accessor_constructor(f);
for (unsigned i = 0; i < c->get_arity(); i++)
if (!m.is_fully_interp(c->get_domain(i)))
return false;
mk_fresh_uncnstr_var_for(f, r);
ptr_vector<func_decl> const& accs = *dt.get_constructor_accessors(c);
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < accs.size(); i++) {
if (accs[i] == f)
new_args.push_back(r);
else
new_args.push_back(m.get_some_value(c->get_domain(i)));
}
add_def(args[0], m.mk_app(c, new_args));
return true;
}
}
return false;
}
bool mk_diff(expr* t, expr_ref& r) override {
// In the current implementation, I only handle the case where
// the datatype has a recursive constructor.
sort* s = t->get_sort();
ptr_vector<func_decl> const& constructors = *dt.get_datatype_constructors(s);
for (func_decl* constructor : constructors) {
unsigned num = constructor->get_arity();
unsigned target = UINT_MAX;
for (unsigned i = 0; i < num; i++) {
sort* s_arg = constructor->get_domain(i);
if (s == s_arg) {
target = i;
continue;
}
if (m.is_uninterp(s_arg))
break;
}
if (target == UINT_MAX)
continue;
// use the constructor the distinct term constructor(...,t,...)
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < num; i++) {
if (i == target)
new_args.push_back(t);
else
new_args.push_back(m.get_some_value(constructor->get_domain(i)));
}
r = m.mk_app(constructor, new_args);
return true;
}
return false;
}
};
expr_inverter::~expr_inverter() {
for (auto* v : m_inverters)
dealloc(v);
}
bool iexpr_inverter::uncnstr(unsigned num, expr * const * args) const {
for (unsigned i = 0; i < num; i++)
if (!m_is_var(args[i]))
return false;
return true;
}
/**
\brief Create a fresh variable for abstracting (f args[0] ... args[num-1])
Return true if it a new variable was created, and false if the variable already existed for this
application. Store the variable in v
*/
void iexpr_inverter::mk_fresh_uncnstr_var_for(sort * s, expr_ref & v) {
v = m.mk_fresh_const(nullptr, s);
if (m_mc)
m_mc->hide(v);
}
void iexpr_inverter::add_def(expr * v, expr * def) {
expr_ref _v(v, m);
expr_ref _d(def, m);
if (!m_mc)
return;
SASSERT(uncnstr(v));
SASSERT(to_app(v)->get_num_args() == 0);
m_mc->add(v, def);
}
void iexpr_inverter::add_defs(unsigned num, expr* const* args, expr* u, expr* identity) {
expr_ref _id(identity, m);
if (!m_mc)
return;
add_def(args[0], u);
for (unsigned i = 1; i < num; i++)
add_def(args[i], identity);
}
expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) {
auto add = [&](iexpr_inverter* i) {
m_inverters.setx(i->get_fid(), i, nullptr);
};
add(alloc(arith_expr_inverter, m));
add(alloc(bv_expr_inverter, m));
add(alloc(array_expr_inverter, m, *this));
add(alloc(dt_expr_inverter, m));
add(alloc(basic_expr_inverter, m, *this));
}
bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr) {
if (num == 0)
return false;
for (unsigned i = 0; i < num; i++)
if (!is_ground(args[i]))
return false;
family_id fid = f->get_family_id();
if (fid == null_family_id)
return false;
auto* p = m_inverters.get(fid, nullptr);
return p && (*p)(f, num, args, new_expr);
}
bool expr_inverter::mk_diff(expr* t, expr_ref& r) {
sort * s = t->get_sort();
if (!m.is_fully_interp(s))
return false;
// If the interpreted sort has only one element,
// then it is unsound to eliminate the unconstrained variable in the equality
sort_size sz = s->get_num_elements();
if (sz.is_finite() && sz.size() <= 1)
return false;
if (!m_mc) {
// easy case, model generation is disabled.
mk_fresh_uncnstr_var_for(s, r);
return true;
}
family_id fid = s->get_family_id();
auto* p = m_inverters.get(fid, nullptr);
return p && p->mk_diff(t, r);
}
void expr_inverter::set_is_var(std::function<bool(expr*)>& is_var) {
for (auto* p : m_inverters)
if (p)
p->set_is_var(is_var);
}
void expr_inverter::set_model_converter(generic_model_converter* mc) {
m_mc = mc;
for (auto* p : m_inverters)
if (p)
p->set_model_converter(mc);
}
void expr_inverter::set_produce_proofs(bool pr) {
m_produce_proofs = pr;
for (auto* p : m_inverters)
if (p)
p->set_produce_proofs(pr);
}

View file

@ -0,0 +1,60 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
expr_inverter.h
Abstract:
inverter interface and instance
Author:
Nikolaj Bjorner (nbjorner) 2022-10-11
--*/
#pragma once
#include "ast/converters/generic_model_converter.h"
class iexpr_inverter {
protected:
ast_manager& m;
std::function<bool(expr*)> m_is_var;
generic_model_converter_ref m_mc;
bool m_produce_proofs = false;
bool uncnstr(expr* e) const { return m_is_var(e); }
bool uncnstr(unsigned num, expr * const * args) const;
void mk_fresh_uncnstr_var_for(sort* s, expr_ref& v);
void mk_fresh_uncnstr_var_for(func_decl* f, expr_ref& v) { mk_fresh_uncnstr_var_for(f->get_range(), v); }
void add_def(expr * v, expr * def);
void add_defs(unsigned num, expr * const * args, expr * u, expr * identity);
public:
iexpr_inverter(ast_manager& m): m(m) {}
virtual ~iexpr_inverter() {}
virtual void set_is_var(std::function<bool(expr*)>& is_var) { m_is_var = is_var; }
virtual void set_model_converter(generic_model_converter* mc) { m_mc = mc; }
virtual void set_produce_proofs(bool p) { m_produce_proofs = true; }
virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr) = 0;
virtual bool mk_diff(expr* t, expr_ref& r) = 0;
virtual family_id get_fid() const = 0;
};
class expr_inverter : public iexpr_inverter {
ptr_vector<iexpr_inverter> m_inverters;
public:
expr_inverter(ast_manager& m);
~expr_inverter() override;
bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr) override;
bool mk_diff(expr* t, expr_ref& r) override;
void set_is_var(std::function<bool(expr*)>& is_var) override;
void set_model_converter(generic_model_converter* mc) override;
void set_produce_proofs(bool p) override;
family_id get_fid() const override { return null_family_id; }
};

View file

@ -0,0 +1,212 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
generic_model_converter.cpp
Abstract:
Generic model converter that hides and adds entries.
It subsumes filter_model_converter and extension_model_converter.
Author:
Nikolaj Bjorner (nbjorner) 2017-10-29
Notes:
--*/
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/for_each_expr.h"
#include "ast/ast_util.h"
#include "ast/occurs.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/converters/generic_model_converter.h"
#include "model/model_v2_pp.h"
#include "model/model_evaluator.h"
void generic_model_converter::add(func_decl * d, expr* e) {
VERIFY(e);
VERIFY(d->get_range() == e->get_sort());
m_entries.push_back(entry(d, e, m, ADD));
}
void generic_model_converter::operator()(model_ref & md) {
TRACE("model_converter", tout << "before generic_model_converter\n"; model_v2_pp(tout, *md); display(tout););
model_evaluator ev(*(md.get()));
ev.set_model_completion(m_completion);
ev.set_expand_array_equalities(false);
expr_ref val(m);
unsigned arity;
bool reset_ev = false;
for (unsigned i = m_entries.size(); i-- > 0; ) {
entry const& e = m_entries[i];
switch (e.m_instruction) {
case instruction::HIDE:
md->unregister_decl(e.m_f);
break;
case instruction::ADD:
ev(e.m_def, val);
TRACE("model_converter", tout << e.m_f->get_name() << " ->\n" << e.m_def << "\n==>\n" << val << "\n";);
arity = e.m_f->get_arity();
reset_ev = false;
if (arity == 0) {
expr* old_val = md->get_const_interp(e.m_f);
if (old_val && old_val == val) {
// skip
}
else {
reset_ev = old_val != nullptr;
md->register_decl(e.m_f, val);
}
}
else {
func_interp * old_val = md->get_func_interp(e.m_f);
if (old_val && old_val->get_else() == val) {
// skip
}
else {
reset_ev = old_val != nullptr;
func_interp * new_fi = alloc(func_interp, m, arity);
new_fi->set_else(val);
md->register_decl(e.m_f, new_fi);
}
}
if (reset_ev) {
ev.reset();
ev.set_model_completion(m_completion);
ev.set_expand_array_equalities(false);
}
break;
}
}
TRACE("model_converter", tout << "after generic_model_converter\n"; model_v2_pp(tout, *md););
}
void generic_model_converter::display(std::ostream & out) {
for (entry const& e : m_entries) {
switch (e.m_instruction) {
case instruction::HIDE:
display_del(out, e.m_f);
break;
case instruction::ADD:
display_add(out, m, e.m_f, e.m_def);
break;
}
}
}
generic_model_converter * generic_model_converter::copy(ast_translation & translator) {
ast_manager& to = translator.to();
generic_model_converter * res = alloc(generic_model_converter, to, m_orig.c_str());
for (entry const& e : m_entries) {
func_decl_ref d(translator(e.m_f.get()), to);
switch (e.m_instruction) {
case instruction::HIDE:
res->hide(d);
break;
case instruction::ADD: {
expr_ref def(translator(e.m_def.get()), to);
res->add(d, def);
break;
}
}
}
return res;
}
void generic_model_converter::set_env(ast_pp_util* visitor) {
if (!visitor) {
m_env = nullptr;
}
else {
m_env = &visitor->env();
for (entry const& e : m_entries) {
visitor->coll.visit_func(e.m_f);
if (e.m_def) visitor->coll.visit(e.m_def);
}
}
}
void generic_model_converter::get_units(obj_map<expr, bool>& units) {
th_rewriter rw(m);
expr_safe_replace rep(m);
expr_ref tmp(m);
for (auto const& kv : units) {
rep.insert(kv.m_key, kv.m_value ? m.mk_true() : m.mk_false());
}
for (unsigned i = m_entries.size(); i-- > 0;) {
entry const& e = m_entries[i];
switch (e.m_instruction) {
case HIDE:
tmp = m.mk_const(e.m_f);
if (units.contains(tmp)) {
m.dec_ref(tmp);
units.remove(tmp);
}
break;
case ADD:
if (e.m_f->get_arity() == 0 && m.is_bool(e.m_f->get_range())) {
tmp = m.mk_const(e.m_f);
if (units.contains(tmp)) {
break;
}
tmp = e.m_def;
rep(tmp);
rw(tmp);
if (m.is_true(tmp)) {
tmp = m.mk_const(e.m_f);
m.inc_ref(tmp);
units.insert(tmp, true);
rep.insert(tmp, m.mk_true());
}
else if (m.is_false(tmp)) {
tmp = m.mk_const(e.m_f);
m.inc_ref(tmp);
units.insert(tmp, false);
rep.insert(tmp, m.mk_false());
}
}
break;
}
}
}
/*
\brief simplify definition expansion from model converter in the case they come from blocked clauses.
In this case the definitions are of the form:
x <=> x or not (C)
or dually,
x <=> not (not x or not C)
in either case the definitions simplify to
x or C
*/
expr_ref generic_model_converter::simplify_def(entry const& e) {
expr_ref c(m.mk_const(e.m_f), m);
if (m.is_bool(c) && occurs(c, e.m_def)) {
expr_safe_replace rep(m);
expr_ref result1 = e.m_def;
expr_ref result2 = e.m_def;
rep.apply_substitution(c, m.mk_true(), result1);
rep.apply_substitution(c, m.mk_false(), result2);
th_rewriter rw(m);
expr_ref result(m.mk_and(m.mk_implies(result2, c), m.mk_implies(c, result1)), m);
rw(result);
return result;
}
else {
return expr_ref(m.mk_eq(c, e.m_def), m);
}
}

View file

@ -0,0 +1,74 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
generic_model_converter.h
Abstract:
Generic model converter that hides and adds entries.
It subsumes filter_model_converter and extension_model_converter.
Author:
Nikolaj Bjorner (nbjorner) 2017-10-29
Notes:
--*/
#pragma once
#include "ast/converters/model_converter.h"
class generic_model_converter : public model_converter {
public:
enum instruction { HIDE, ADD };
struct entry {
func_decl_ref m_f;
expr_ref m_def;
instruction m_instruction;
entry(func_decl* f, expr* d, ast_manager& m, instruction i):
m_f(f, m), m_def(d, m), m_instruction(i) {}
};
private:
ast_manager& m;
std::string m_orig;
vector<entry> m_entries;
expr_ref simplify_def(entry const& e);
public:
generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {}
void hide(expr* e) { SASSERT(is_app(e) && to_app(e)->get_num_args() == 0); hide(to_app(e)->get_decl()); }
void hide(func_decl * f) { m_entries.push_back(entry(f, nullptr, m, HIDE)); }
void add(func_decl * d, expr* e);
void add(expr * d, expr* e) { SASSERT(is_app(d) && to_app(d)->get_num_args() == 0); add(to_app(d)->get_decl(), e); }
void operator()(labels_vec & labels) override {}
void operator()(model_ref & md) override;
void operator()(expr_ref& fml) override { UNREACHABLE(); }
void cancel() override {}
void display(std::ostream & out) override;
model_converter * translate(ast_translation & translator) override { return copy(translator); }
generic_model_converter* copy(ast_translation & translator);
void set_env(ast_pp_util* visitor) override;
void get_units(obj_map<expr, bool>& units) override;
vector<entry> const& entries() const { return m_entries; }
};
typedef ref<generic_model_converter> generic_model_converter_ref;

View file

@ -0,0 +1,228 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
horn_subsume_model_converter.cpp
Abstract:
Model converter for redundant Horn clauses.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
--*/
#include "ast/ast_pp.h"
#include "ast/for_each_expr.h"
#include "ast/well_sorted.h"
#include "ast/rewriter/var_subst.h"
#include "ast/rewriter/bool_rewriter.h"
#include "ast/rewriter/th_rewriter.h"
#include "model/model_smt2_pp.h"
#include "ast/converters/horn_subsume_model_converter.h"
void horn_subsume_model_converter::insert(app* head, expr* body) {
m_delay_head.push_back(head);
m_delay_body.push_back(body);
}
void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) {
expr_ref b(m);
bool_rewriter(m).mk_and(sz, body, b);
insert(head, b.get());
}
bool horn_subsume_model_converter::mk_horn(
app* head, expr* body, func_decl_ref& pred, expr_ref& body_res) {
expr_ref_vector conjs(m), subst(m);
ptr_vector<sort> sorts2;
var_subst vs(m, false);
if (!is_uninterp(head)) {
return false;
}
pred = head->get_decl();
unsigned arity = head->get_num_args();
expr_free_vars fv;
fv(head);
fv.accumulate(body);
if (arity == 0 && fv.empty()) {
body_res = body;
return true;
}
fv.set_default_sort(m.mk_bool_sort());
svector<symbol> names;
for (unsigned i = 0; i < fv.size(); ++i) {
names.push_back(symbol(i));
}
names.reverse();
fv.reverse();
conjs.push_back(body);
for (unsigned i = 0; i < arity; ++i) {
expr* arg = head->get_arg(i);
var_ref v(m);
v = m.mk_var(fv.size()+i, arg->get_sort());
if (is_var(arg)) {
unsigned w = to_var(arg)->get_idx();
if (w >= subst.size()) {
subst.resize(w+1);
}
if (subst.get(w)) {
conjs.push_back(m.mk_eq(v, subst.get(w)));
}
else {
subst[w] = v;
}
}
else {
conjs.push_back(m.mk_eq(v, arg));
}
}
expr_ref body_expr(m);
body_expr = m.mk_and(conjs);
// substitute variables directly.
if (!subst.empty()) {
body_expr = vs(body_expr, subst);
}
if (fv.empty()) {
SASSERT(subst.empty());
body_res = body_expr;
}
else {
body_res = m.mk_exists(fv.size(), fv.data(), names.data(), body_expr.get());
m_rewrite(body_res);
}
TRACE("mc",
tout << mk_pp(head, m) << " :- " << mk_pp(body, m) << "\n";
tout << pred->get_name() << " :- " << mk_pp(body_res.get(), m) << "\n";);
return true;
}
bool horn_subsume_model_converter::mk_horn(
expr* clause, func_decl_ref& pred, expr_ref& body) {
// formula is closed.
DEBUG_CODE(expr_free_vars fv; fv(clause); SASSERT(fv.empty()););
while (is_quantifier(clause) && to_quantifier(clause)->get_kind() == forall_k) {
quantifier* q = to_quantifier(clause);
clause = q->get_expr();
}
expr* e1, *e2;
if (m.is_implies(clause, e1, e2)) {
if (!is_uninterp(e2)) {
return false;
}
return mk_horn(to_app(e2), e1, pred, body);
}
else if (m.is_or(clause)) {
// todo?
return false;
}
else {
return false;
}
}
void horn_subsume_model_converter::add_default_proc::operator()(app* n) {
//
// predicates that have not been assigned values
// in the Horn model are assumed false.
//
if (m.is_bool(n) &&
!m_md->has_interpretation(n->get_decl()) &&
(n->get_family_id() == null_family_id)) {
TRACE("mc", tout << "adding: " << n->get_decl()->get_name() << "\n";);
if (n->get_decl()->get_arity() == 0) {
m_md->register_decl(n->get_decl(), m.mk_false());
}
else {
func_interp* fi = alloc(func_interp, m, n->get_decl()->get_arity());
fi->set_else(m.mk_false());
m_md->register_decl(n->get_decl(), fi);
}
}
}
void horn_subsume_model_converter::add_default_false_interpretation(expr* e, model_ref& md) {
add_default_proc proc(m, md);
for_each_expr(proc, e);
}
void horn_subsume_model_converter::operator()(model_ref& mr) {
func_decl_ref pred(m);
expr_ref body_res(m);
for (unsigned i = 0; i < m_delay_head.size(); ++i) {
VERIFY(mk_horn(m_delay_head.get(i), m_delay_body.get(i), pred, body_res));
insert(pred.get(), body_res.get());
}
m_delay_head.reset();
m_delay_body.reset();
TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0););
for (unsigned i = m_funcs.size(); i-- > 0; ) {
func_decl* h = m_funcs.get(i);
expr_ref body(m_bodies.get(i), m);
unsigned arity = h->get_arity();
add_default_false_interpretation(body, mr);
SASSERT(m.is_bool(body));
TRACE("mc", tout << "eval: " << h->get_name() << "\n" << body << "\n";);
body = (*mr)(body);
TRACE("mc", tout << "to:\n" << body << "\n";);
if (arity == 0) {
expr* e = mr->get_const_interp(h);
if (e) {
body = m.mk_or(e, body);
}
m_rewrite(body);
mr->register_decl(h, body);
}
else {
func_interp* f = mr->get_func_interp(h);
if (f) {
expr* e = f->get_else();
body = m.mk_or(e, body);
}
else {
f = alloc(func_interp, m, arity);
mr->register_decl(h, f);
}
m_rewrite(body);
f->set_else(body);
}
}
}
model_converter* horn_subsume_model_converter::translate(ast_translation & translator) {
horn_subsume_model_converter* mc = alloc(horn_subsume_model_converter, translator.to());
for (unsigned i = 0; i < m_funcs.size(); ++i) {
mc->insert(translator(m_funcs[i].get()), translator(m_bodies[i].get()));
}
return mc;
}

View file

@ -0,0 +1,84 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
horn_subsume_model_converter.h
Abstract:
Model converter for redundant Horn clauses.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
Notes:
Subsumption transformation (remove Horn clause):
P(x) :- Body(x,y) Rules
----------------------------
Rules
Model converter:
P(x) := P(x) or (exists y. Body(x,y))
--*/
#pragma once
#include "ast/converters/model_converter.h"
#include "ast/rewriter/th_rewriter.h"
class horn_subsume_model_converter : public model_converter {
ast_manager& m;
func_decl_ref_vector m_funcs;
expr_ref_vector m_bodies;
th_rewriter m_rewrite;
app_ref_vector m_delay_head;
expr_ref_vector m_delay_body;
void add_default_false_interpretation(expr* e, model_ref& md);
struct add_default_proc {
ast_manager& m;
model_ref& m_md;
add_default_proc(ast_manager& m, model_ref& md): m(m), m_md(md) {}
void operator()(app* n);
void operator()(expr* n) {}
};
public:
horn_subsume_model_converter(ast_manager& m):
m(m), m_funcs(m), m_bodies(m), m_rewrite(m), m_delay_head(m), m_delay_body(m) {}
bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body);
bool mk_horn(app* head, expr* body, func_decl_ref& pred, expr_ref& body_res);
void insert(app* head, expr* body);
void insert(app* head, unsigned sz, expr* const* body);
void insert(func_decl* p, expr* body) { m_funcs.push_back(p); m_bodies.push_back(body); }
void operator()(model_ref& _m) override;
model_converter * translate(ast_translation & translator) override;
ast_manager& get_manager() { return m; }
void display(std::ostream & out) override {}
void get_units(obj_map<expr, bool>& units) override { units.reset(); }
};

View file

@ -0,0 +1,201 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
model_converter.h
Abstract:
Abstract interface for converting models.
Author:
Leonardo (leonardo) 2011-04-21
Notes:
--*/
#include "ast/converters/model_converter.h"
#include "model/model_v2_pp.h"
#include "ast/ast_smt2_pp.h"
/*
* Add or overwrite value in model.
*/
void model_converter::display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e) {
VERIFY(e);
VERIFY(f->get_range() == e->get_sort());
ast_smt2_pp_rev(out, f, e, env, params_ref(), 0, "model-add") << "\n";
}
void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const {
smt2_pp_environment_dbg dbgenv(m);
smt2_pp_environment& env = m_env ? *m_env : dbgenv;
display_add(out, env, m, f, e);
}
/*
* A value is removed from the model.
*/
void model_converter::display_del(std::ostream& out, func_decl* f) const {
if (m_env) {
ast_smt2_pp(out << "(model-del ", f->get_name(), f->is_skolem(), *m_env) << ")\n";
}
else {
out << "(model-del " << f->get_name() << ")\n";
}
}
void model_converter::set_env(ast_pp_util* visitor) {
if (visitor) {
m_env = &visitor->env();
}
else {
m_env = nullptr;
}
}
void model_converter::display_add(std::ostream& out, ast_manager& m) {
// default printer for converter that adds entries
model_ref mdl = alloc(model, m);
(*this)(mdl);
smt2_pp_environment_dbg dbgenv(m);
smt2_pp_environment& env = m_env ? *m_env : dbgenv;
display_add(out, env, *mdl);
}
void model_converter::display_add(std::ostream& out, smt2_pp_environment& env, model& mdl) {
ast_manager& m = mdl.get_manager();
for (unsigned i = 0, sz = mdl.get_num_constants(); i < sz; ++i) {
func_decl* f = mdl.get_constant(i);
display_add(out, env, m, f, mdl.get_const_interp(f));
}
for (unsigned i = 0, sz = mdl.get_num_functions(); i < sz; ++i) {
func_decl* f = mdl.get_function(i);
func_interp* fi = mdl.get_func_interp(f);
display_add(out, env, m, f, fi->get_interp());
}
}
class concat_model_converter : public concat_converter<model_converter> {
public:
concat_model_converter(model_converter * mc1, model_converter * mc2): concat_converter<model_converter>(mc1, mc2) {
VERIFY(m_c1 && m_c2);
}
void operator()(model_ref & m) override {
this->m_c2->operator()(m);
this->m_c1->operator()(m);
}
void operator()(expr_ref & fml) override {
this->m_c2->operator()(fml);
this->m_c1->operator()(fml);
}
void operator()(labels_vec & r) override {
this->m_c2->operator()(r);
this->m_c1->operator()(r);
}
void get_units(obj_map<expr, bool>& fmls) override {
m_c2->get_units(fmls);
m_c1->get_units(fmls);
}
char const * get_name() const override { return "concat-model-converter"; }
model_converter * translate(ast_translation & translator) override {
return this->translate_core<concat_model_converter>(translator);
}
void set_env(ast_pp_util* visitor) override {
this->m_c1->set_env(visitor);
this->m_c2->set_env(visitor);
}
};
model_converter * concat(model_converter * mc1, model_converter * mc2) {
if (mc1 == nullptr)
return mc2;
if (mc2 == nullptr)
return mc1;
return alloc(concat_model_converter, mc1, mc2);
}
class model2mc : public model_converter {
model_ref m_model;
labels_vec m_labels;
public:
model2mc(model * m):m_model(m) {}
model2mc(model * m, labels_vec const & r):m_model(m), m_labels(r) {}
void operator()(model_ref & m) override {
if (!m || !m_model) {
m = m_model;
return;
}
m->copy_const_interps(*m_model.get());
m->copy_func_interps(*m_model.get());
m->copy_usort_interps(*m_model.get());
}
void operator()(labels_vec & r) override {
r.append(m_labels.size(), m_labels.data());
}
void operator()(expr_ref& fml) override {
model::scoped_model_completion _scm(m_model, false);
fml = (*m_model)(fml);
}
void get_units(obj_map<expr, bool>& fmls) override {
// no-op
}
void cancel() override {
}
void display(std::ostream & out) override {
ast_manager& m = m_model->get_manager();
smt2_pp_environment_dbg dbgenv(m);
smt2_pp_environment& env = m_env ? *m_env : dbgenv;
model_converter::display_add(out, env, *m_model);
}
model_converter * translate(ast_translation & translator) override {
model * m = m_model->translate(translator);
return alloc(model2mc, m, m_labels);
}
};
model_converter * model2model_converter(model * m) {
if (!m) return nullptr;
return alloc(model2mc, m);
}
model_converter * model_and_labels2model_converter(model * m, labels_vec const & r) {
if (!m) return nullptr;
return alloc(model2mc, m, r);
}
void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m) {
if (mc) {
m = alloc(model, mng);
(*mc)(m);
}
}
void apply(model_converter_ref & mc, model_ref & m) {
if (mc) {
(*mc)(m);
}
}

View file

@ -0,0 +1,119 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
model_converter.h
Abstract:
Abstract interface for converting models.
Author:
Leonardo (leonardo) 2011-04-21
Notes:
A model converter, mc, can be used to convert a model for one
of a generated subgoal into a model for an initial goal or solver state.
For a goal or solver state that is decided, a model converter can be
a simple wrapper around a model.
Logically, given a formula F and subgoal formula F_s a model converter mc
for F_s relative to F has the property:
m |= F_s iff mc(m) |= F for every model m
For the evaluator associated with models, m, we expect
eval(m)(F_s) <=> eval(mc(m))(F)
This property holds for both eval, that decides on a fixed value
for constants that have no interpretation in m and for 'peval'
(partial eval) that returns just the constants that are unfixed.
(in the model evaluator one can control this behavior using a
configuration flag)
and more generally over the eval method have:
G => F_s iff peval(mc(e))(G) => F for every formula G
where e is the empty model (a model that does not evaluate any
When a model converter supports application to a formula it satisfies
the following property:
mc(G) & F_s is SAT iff G & F is SAT
For a model converter that is a sequence of definitions and removals
of functions we can obtain mc(G) by adding back or expanding definitions
that are required to interpret G fully in the context of F_s.
--*/
#pragma once
#include "util/ref.h"
#include "ast/ast_pp_util.h"
#include "model/model.h"
#include "ast/converters/converter.h"
class labels_vec : public svector<symbol> {};
class smt2_pp_environment;
class model_converter : public converter {
protected:
smt2_pp_environment* m_env = nullptr;
bool m_completion = true;
static void display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e);
void display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const;
void display_del(std::ostream& out, func_decl* f) const;
void display_add(std::ostream& out, ast_manager& m);
public:
model_converter() {}
void set_completion(bool f) { m_completion = f; }
virtual void operator()(model_ref & m) = 0;
virtual void operator()(labels_vec & r) {}
virtual void operator()(expr_ref& fml) { UNREACHABLE(); }
virtual model_converter * translate(ast_translation & translator) = 0;
virtual void set_env(ast_pp_util* visitor);
/**
\brief we are adding a formula to the context of the model converter.
The operator has as side effect of adding definitions as assertions to the
formula and removing these definitions from the model converter.
*/
virtual void get_units(obj_map<expr, bool>& fmls) { UNREACHABLE(); }
static void display_add(std::ostream& out, smt2_pp_environment& env, model& mdl);
};
typedef ref<model_converter> model_converter_ref;
typedef sref_vector<model_converter> model_converter_ref_vector;
typedef sref_buffer<model_converter> model_converter_ref_buffer;
model_converter * concat(model_converter * mc1, model_converter * mc2);
inline model_converter * concat(model_converter * mc1, model_converter * mc2, model_converter* mc3) {
return concat(mc1, concat(mc2, mc3));
}
model_converter * model2model_converter(model * m);
model_converter * model_and_labels2model_converter(model * m, labels_vec const &r);
void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m);
void apply(model_converter_ref & mc, model_ref & m);

View file

@ -0,0 +1,100 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
proof_converter.cpp
Abstract:
Abstract interface for converting proofs, and basic combinators
Author:
Leonardo (leonardo) 2011-11-14
Notes:
--*/
#include "ast/converters/proof_converter.h"
#include "ast/ast_smt2_pp.h"
class concat_proof_converter : public concat_converter<proof_converter> {
public:
concat_proof_converter(proof_converter * pc1, proof_converter * pc2):concat_converter<proof_converter>(pc1, pc2) {}
char const * get_name() const override { return "concat-proof-converter"; }
proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override {
proof_ref tmp(m);
tmp = this->m_c2->operator()(m, num_source, source);
proof * new_source = tmp.get();
return this->m_c1->operator()(m, 1, &new_source);
}
proof_converter * translate(ast_translation & translator) override {
return this->translate_core<concat_proof_converter>(translator);
}
};
proof_converter * concat(proof_converter * pc1, proof_converter * pc2) {
if (pc1 == nullptr)
return pc2;
if (pc2 == nullptr)
return pc1;
return alloc(concat_proof_converter, pc1, pc2);
}
class proof2pc : public proof_converter {
proof_ref m_pr;
public:
proof2pc(ast_manager & m, proof * pr):m_pr(pr, m) {}
proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override {
SASSERT(num_source == 0);
return m_pr;
}
proof_converter * translate(ast_translation & translator) override {
return alloc(proof2pc, translator.to(), translator(m_pr.get()));
}
void display(std::ostream & out) override {
out << "(proof->proof-converter-wrapper\n" << mk_ismt2_pp(m_pr.get(), m_pr.get_manager()) << ")\n";
}
};
proof_converter * proof2proof_converter(ast_manager & m, proof * pr) {
if (pr == nullptr)
return nullptr;
return alloc(proof2pc, m, pr);
}
void apply(ast_manager & m, proof_converter * pc, proof_ref & pr) {
if (pc) {
proof * _pr = pr.get();
pr = (*pc)(m, 1, &_pr);
}
}
/**
Let pc2s be a buffer of proof converters that are wrappers for proofs.
That is, they are functors of the form: unit -> Proof
Then, this function applies pc1 to the proofs produced by pc2s's and store
the resultant proof in result.
pc1 and pc2s must be different from 0.
*/
proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s) {
SASSERT(pc1);
proof_ref_buffer prs(m);
unsigned sz = pc2s.size();
for (unsigned i = 0; i < sz; i++) {
proof_ref pr(m);
SASSERT(pc2s[i]); // proof production is enabled
pr = pc2s[i]->operator()(m, 0, nullptr);
prs.push_back(pr);
}
return (*pc1)(m, sz, prs.data());
}

View file

@ -0,0 +1,43 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
proof_converter.h
Abstract:
Abstract interface for converting proofs, and basic combinators.
Author:
Leonardo (leonardo) 2011-04-26
Notes:
--*/
#pragma once
#include "ast/ast.h"
#include "util/ref.h"
#include "ast/converters/converter.h"
class proof_converter : public converter {
public:
virtual proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) = 0;
virtual proof_converter * translate(ast_translation & translator) = 0;
};
typedef ref<proof_converter> proof_converter_ref;
typedef sref_vector<proof_converter> proof_converter_ref_vector;
typedef sref_buffer<proof_converter> proof_converter_ref_buffer;
proof_converter * concat(proof_converter * pc1, proof_converter * pc2);
proof_converter * proof2proof_converter(ast_manager & m, proof * pr);
void apply(ast_manager & m, proof_converter * pc, proof_ref & pr);
proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s);

View file

@ -0,0 +1,83 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
replace_proof_converter.cpp
Abstract:
Proof converter that replaces asserted by sub-proof.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
--*/
#include "ast/converters/replace_proof_converter.h"
#include "ast/expr_functors.h"
#include "ast/ast_pp.h"
#include "ast/for_each_expr.h"
/**
\brief Replace expressions by other expressions.
replace_map is caching, so inserting src |-> dst has no effect if
src is a sub-expression of something that has already been visited.
The assumption is that proof replacements are inserted into
the replace_proof_converter in the order that they are introduced, so
there are no such clashes.
map_proc is used as expr_replacer behaves differently
when proof mode is turned on.
*/
class replace_map : public map_proc {
public:
replace_map(ast_manager& m): map_proc(m) {}
void insert(expr* src, expr* dst) {
m_map.insert(src, dst, nullptr);
}
void operator()(var* v) { visit(v); }
void operator()(app* a) { if (!get_expr(a)) { reconstruct(a); } }
void operator()(quantifier* q) { visit(q); }
void apply(expr_ref& e) {
for_each_expr(*this, e);
e = get_expr(e);
}
};
proof_ref replace_proof_converter::operator()(ast_manager & m, unsigned num_source, proof * const * source) {
SASSERT(num_source == 1);
replace_map replace(m);
proof_ref p(m);
expr_ref tmp(source[0], m), e(m), f(m);
// apply the substitution to the prefix before inserting it.
for (unsigned i = 0; i < m_proofs.size(); ++i) {
p = m_proofs[i].get();
e = p;
replace.apply(e);
f = m.mk_asserted(m.get_fact(p));
replace.insert(f, e);
TRACE("proof_converter", tout << f->get_id() << " " << mk_pp(f, m) <<
"\n|-> " << mk_pp(e, m) << "\n";);
}
replace.apply(tmp);
TRACE("proof_converter", tout << mk_pp(source[0], m) << "\n";
tout << mk_pp(tmp.get(), m) << "\n";);
return proof_ref(to_app(tmp), m);
}
proof_converter * replace_proof_converter::translate(ast_translation & translator) {
replace_proof_converter* rp = alloc(replace_proof_converter, m);
for (proof* p : m_proofs) rp->insert(translator(p));
return rp;
}

View file

@ -0,0 +1,48 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
replace_proof_converter.h
Abstract:
Proof converter to replace asserted leaves by proofs.
Given a proof P with occurrences of [asserted fml]
Replace [asserted fml] by proofs whose conclusions are fml.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
--*/
#pragma once
#include "ast/converters/proof_converter.h"
class replace_proof_converter : public proof_converter {
ast_manager& m;
proof_ref_vector m_proofs;
public:
replace_proof_converter(ast_manager& _m): m(_m), m_proofs(m) {}
proof_ref operator()(ast_manager & _m, unsigned num_source, proof * const * source) override;
proof_converter * translate(ast_translation & translator) override;
void insert(proof* p) { m_proofs.push_back(p); }
ast_manager& get_manager() { return m; }
// run the replacements the inverse direction.
void invert() { m_proofs.reverse(); }
void display(std::ostream & out) override {}
};

View file

@ -832,6 +832,10 @@ namespace datatype {
bool util::is_declared(sort* s) const {
return plugin().is_declared(s);
}
bool util::is_declared(symbol const& n) const {
return plugin().is_declared(n);
}
void util::compute_datatype_size_functions(svector<symbol> const& names) {
map<symbol, status, symbol_hash_proc, symbol_eq_proc> already_found;
@ -1087,11 +1091,9 @@ namespace datatype {
sort * datatype = con->get_range();
def const& dd = get_def(datatype);
symbol r;
for (constructor const* c : dd) {
if (c->name() == con->get_name()) {
r = c->recognizer();
}
}
for (constructor const* c : dd)
if (c->name() == con->get_name())
r = c->recognizer();
parameter ps[2] = { parameter(con), parameter(r) };
d = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype);
SASSERT(d);
@ -1142,17 +1144,15 @@ namespace datatype {
}
bool util::is_enum_sort(sort* s) {
if (!is_datatype(s)) {
return false;
}
if (!is_datatype(s))
return false;
bool r = false;
if (m_is_enum.find(s, r))
return r;
ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
r = true;
for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
r = cnstrs[i]->get_arity() == 0;
}
for (unsigned i = 0; r && i < cnstrs.size(); ++i)
r = cnstrs[i]->get_arity() == 0;
m_is_enum.insert(s, r);
m_asts.push_back(s);
return r;
@ -1284,11 +1284,14 @@ namespace datatype {
unsigned idx = 0;
def const& d = get_def(f->get_range());
for (constructor* c : d) {
if (c->name() == f->get_name()) {
return idx;
}
if (c->name() == f->get_name())
return idx;
++idx;
}
IF_VERBOSE(0, verbose_stream() << f->get_name() << "\n");
for (constructor* c : d)
IF_VERBOSE(0, verbose_stream() << "!= " << c->name() << "\n");
SASSERT(false);
UNREACHABLE();
return 0;
}

View file

@ -253,6 +253,7 @@ namespace datatype {
ptr_vector<constructor> get_constructors(symbol const& s) const;
ptr_vector<accessor> get_accessors(symbol const& s) const;
bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); }
bool is_declared(symbol const& n) const { return m_defs.contains(n); }
unsigned get_axiom_base_id(symbol const& s) { return m_axiom_bases[s]; }
util & u() const;
@ -375,6 +376,7 @@ namespace datatype {
bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
void reset();
bool is_declared(sort* s) const;
bool is_declared(symbol const& n) const;
void display_datatype(sort *s, std::ostream& strm);
bool is_fully_interp(sort * s) const;
sort_ref_vector datatype_params(sort * s) const;

View file

@ -32,9 +32,8 @@ void decl_collector::visit_sort(sort * n) {
m_todo.push_back(cnstr);
ptr_vector<func_decl> const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr);
unsigned num_cas = cnstr_acc.size();
for (unsigned j = 0; j < num_cas; j++) {
m_todo.push_back(cnstr_acc.get(j));
}
for (unsigned j = 0; j < num_cas; j++)
m_todo.push_back(cnstr_acc.get(j));
}
}
for (unsigned i = n->get_num_parameters(); i-- > 0; ) {
@ -49,14 +48,19 @@ bool decl_collector::is_bool(sort * s) {
void decl_collector::visit_func(func_decl * n) {
func_decl* g;
if (!m_visited.is_marked(n)) {
family_id fid = n->get_family_id();
if (fid == null_family_id)
m_decls.push_back(n);
else if (fid == m_rec_fid) {
m_rec_decls.push_back(n);
recfun::util u(m());
m_todo.push_back(u.get_def(n).get_rhs());
if (u.has_def(n)) {
m_rec_decls.push_back(n);
m_todo.push_back(u.get_def(n).get_rhs());
}
else
m_decls.push_back(n);
}
else if (m_ar_util.is_as_array(n, g))
m_todo.push_back(g);
@ -97,13 +101,11 @@ void decl_collector::visit(ast* n) {
case AST_QUANTIFIER: {
quantifier * q = to_quantifier(n);
unsigned num_decls = q->get_num_decls();
for (unsigned i = 0; i < num_decls; ++i) {
m_todo.push_back(q->get_decl_sort(i));
}
for (unsigned i = 0; i < num_decls; ++i)
m_todo.push_back(q->get_decl_sort(i));
m_todo.push_back(q->get_expr());
for (unsigned i = 0; i < q->get_num_patterns(); ++i) {
m_todo.push_back(q->get_pattern(i));
}
for (unsigned i = 0; i < q->get_num_patterns(); ++i)
m_todo.push_back(q->get_pattern(i));
break;
}
case AST_SORT:
@ -111,9 +113,8 @@ void decl_collector::visit(ast* n) {
break;
case AST_FUNC_DECL: {
func_decl * d = to_func_decl(n);
for (sort* srt : *d) {
m_todo.push_back(srt);
}
for (sort* srt : *d)
m_todo.push_back(srt);
m_todo.push_back(d->get_range());
visit_func(d);
break;

View file

@ -36,8 +36,8 @@ namespace euf {
}
m_expr2enode.setx(f->get_id(), n, nullptr);
push_node(n);
for (unsigned i = 0; i < num_args; ++i)
set_merge_enabled(args[i], true);
for (unsigned i = 0; i < num_args; ++i)
set_cgc_enabled(args[i], true);
return n;
}
@ -78,9 +78,8 @@ namespace euf {
void egraph::reinsert_equality(enode* p) {
SASSERT(p->is_equality());
if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) {
add_literal(p, true);
}
if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root())
add_literal(p, nullptr);
}
void egraph::force_push() {
@ -92,9 +91,7 @@ namespace euf {
m_scopes.push_back(m_updates.size());
m_region.push_scope();
m_updates.push_back(update_record(m_new_th_eqs_qhead, update_record::new_th_eq_qhead()));
m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead()));
}
SASSERT(m_new_lits_qhead <= m_new_lits.size());
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
}
@ -116,18 +113,16 @@ namespace euf {
m_on_make(n);
if (num_args == 0)
return n;
if (m.is_eq(f)) {
if (m.is_eq(f) && !m.is_iff(f)) {
n->set_is_equality();
update_children(n);
reinsert_equality(n);
}
else {
auto [n2, comm] = insert_table(n);
if (n2 == n)
update_children(n);
else
merge(n, n2, justification::congruence(comm));
}
auto [n2, comm] = insert_table(n);
if (n2 == n)
update_children(n);
else
merge(n, n2, justification::congruence(comm, m_congruence_timestamp++));
return n;
}
@ -158,11 +153,28 @@ namespace euf {
++m_stats.m_num_th_diseqs;
}
void egraph::add_literal(enode* n, bool is_eq) {
TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";);
m_new_lits.push_back(enode_bool_pair(n, is_eq));
m_updates.push_back(update_record(update_record::new_lit()));
if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
void egraph::add_literal(enode* n, enode* ante) {
if (!m_on_propagate_literal)
return;
if (!ante) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
if (!ante)
m_on_propagate_literal(n, ante);
else if (m.is_true(ante->get_expr()) || m.is_false(ante->get_expr())) {
for (enode* k : enode_class(n)) {
if (k != ante) {
//verbose_stream() << "eq: " << k->value() << " " <<ante->value() << "\n";
m_on_propagate_literal(k, ante);
}
}
}
else {
for (enode* k : enode_class(n)) {
if (k->value() != ante->value()) {
//verbose_stream() << "eq: " << k->value() << " " <<ante->value() << "\n";
m_on_propagate_literal(k, ante);
}
}
}
}
void egraph::new_diseq(enode* n) {
@ -173,7 +185,7 @@ namespace euf {
enode* r2 = arg2->get_root();
TRACE("euf", tout << "new-diseq: " << bpp(r1) << " " << bpp(r2) << ": " << r1->has_th_vars() << " " << r2->has_th_vars() << "\n";);
if (r1 == r2) {
add_literal(n, true);
add_literal(n, nullptr);
return;
}
if (!r1->has_th_vars())
@ -264,10 +276,19 @@ namespace euf {
root->del_th_var(tid);
}
void egraph::set_merge_enabled(enode* n, bool enable_merge) {
if (enable_merge != n->merge_enabled()) {
toggle_merge_enabled(n, false);
m_updates.push_back(update_record(n, update_record::toggle_merge()));
void egraph::set_merge_tf_enabled(enode* n, bool enable_merge_tf) {
if (!m.is_bool(n->get_sort()))
return;
if (enable_merge_tf != n->merge_tf()) {
n->set_merge_tf(enable_merge_tf);
m_updates.push_back(update_record(n, update_record::toggle_merge_tf()));
}
}
void egraph::set_cgc_enabled(enode* n, bool enable_merge) {
if (enable_merge != n->cgc_enabled()) {
toggle_cgc_enabled(n, false);
m_updates.push_back(update_record(n, update_record::toggle_cgc()));
}
}
@ -278,9 +299,9 @@ namespace euf {
m_updates.push_back(update_record(n, update_record::set_relevant()));
}
void egraph::toggle_merge_enabled(enode* n, bool backtracking) {
bool enable_merge = !n->merge_enabled();
n->set_merge_enabled(enable_merge);
void egraph::toggle_cgc_enabled(enode* n, bool backtracking) {
bool enable_merge = !n->cgc_enabled();
n->set_cgc_enabled(enable_merge);
if (n->num_args() > 0) {
if (enable_merge) {
auto [n2, comm] = insert_table(n);
@ -290,7 +311,7 @@ namespace euf {
else if (n->is_cgr())
erase_from_table(n);
}
VERIFY(n->num_args() == 0 || !n->merge_enabled() || m_table.contains(n));
VERIFY(n->num_args() == 0 || !n->cgc_enabled() || m_table.contains(n));
}
void egraph::set_value(enode* n, lbool value, justification j) {
@ -300,6 +321,8 @@ namespace euf {
n->set_value(value);
n->m_lit_justification = j;
m_updates.push_back(update_record(n, update_record::value_assignment()));
if (n->is_equality() && n->value() == l_false)
new_diseq(n);
}
}
@ -329,7 +352,6 @@ namespace euf {
num_scopes -= m_num_scopes;
m_num_scopes = 0;
SASSERT(m_new_lits_qhead <= m_new_lits.size());
unsigned old_lim = m_scopes.size() - num_scopes;
unsigned num_updates = m_scopes[old_lim];
auto undo_node = [&]() {
@ -352,8 +374,11 @@ namespace euf {
case update_record::tag_t::is_add_node:
undo_node();
break;
case update_record::tag_t::is_toggle_merge:
toggle_merge_enabled(p.r1, true);
case update_record::tag_t::is_toggle_cgc:
toggle_cgc_enabled(p.r1, true);
break;
case update_record::tag_t::is_toggle_merge_tf:
p.r1->set_merge_tf(!p.r1->merge_tf());
break;
case update_record::tag_t::is_set_parent:
undo_eq(p.r1, p.n1, p.r2_num_parents);
@ -365,18 +390,12 @@ namespace euf {
SASSERT(p.r1->get_th_var(p.m_th_id) != null_theory_var);
p.r1->replace_th_var(p.m_old_th_var, p.m_th_id);
break;
case update_record::tag_t::is_new_lit:
m_new_lits.pop_back();
break;
case update_record::tag_t::is_new_th_eq:
m_new_th_eqs.pop_back();
break;
case update_record::tag_t::is_new_th_eq_qhead:
m_new_th_eqs_qhead = p.qhead;
break;
case update_record::tag_t::is_new_lits_qhead:
m_new_lits_qhead = p.qhead;
break;
case update_record::tag_t::is_inconsistent:
m_inconsistent = p.m_inconsistent;
break;
@ -411,7 +430,6 @@ namespace euf {
m_region.pop_scope(num_scopes);
m_to_merge.reset();
SASSERT(m_new_lits_qhead <= m_new_lits.size());
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
// DEBUG_CODE(invariant(););
@ -419,7 +437,7 @@ namespace euf {
void egraph::merge(enode* n1, enode* n2, justification j) {
if (!n1->merge_enabled() && !n2->merge_enabled())
if (!n1->cgc_enabled() && !n2->cgc_enabled())
return;
SASSERT(n1->get_sort() == n2->get_sort());
enode* r1 = n1->get_root();
@ -436,6 +454,7 @@ namespace euf {
set_conflict(n1, n2, j);
return;
}
if (r1->value() != r2->value() && r1->value() != l_undef && r2->value() != l_undef) {
SASSERT(m.is_bool(r1->get_expr()));
set_conflict(n1, n2, j);
@ -447,11 +466,7 @@ namespace euf {
std::swap(n1, n2);
}
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
add_literal(n1, false);
if (n1->is_equality() && n1->value() == l_false)
new_diseq(n1);
remove_parents(r1, r2);
remove_parents(r1);
push_eq(r1, n1, r2->num_parents());
merge_justification(n1, n2, j);
for (enode* c : enode_class(n1))
@ -460,15 +475,22 @@ namespace euf {
r2->inc_class_size(r1->class_size());
merge_th_eq(r1, r2);
reinsert_parents(r1, r2);
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
add_literal(n1, r2);
else if (n2->value() != l_undef && n1->value() != n2->value())
add_literal(n1, n2);
else if (n1->value() != l_undef && n2->value() != n1->value())
add_literal(n2, n1);
for (auto& cb : m_on_merge)
cb(r2, r1);
}
void egraph::remove_parents(enode* r1, enode* r2) {
for (enode* p : enode_parents(r1)) {
void egraph::remove_parents(enode* r) {
for (enode* p : enode_parents(r)) {
if (p->is_marked1())
continue;
if (p->merge_enabled()) {
if (p->cgc_enabled()) {
if (!p->is_cgr())
continue;
SASSERT(m_table.contains_ptr(p));
@ -486,8 +508,8 @@ namespace euf {
if (!p->is_marked1())
continue;
p->unmark1();
TRACE("euf", tout << "reinsert " << bpp(r1) << " " << bpp(r2) << " " << bpp(p) << " " << p->merge_enabled() << "\n";);
if (p->merge_enabled()) {
TRACE("euf", tout << "reinsert " << bpp(r1) << " " << bpp(r2) << " " << bpp(p) << " " << p->cgc_enabled() << "\n";);
if (p->cgc_enabled()) {
auto [p_other, comm] = insert_table(p);
SASSERT(m_table.contains_ptr(p) == (p_other == p));
TRACE("euf", tout << "other " << bpp(p_other) << "\n";);
@ -531,9 +553,9 @@ namespace euf {
for (auto it = begin; it != end; ++it) {
enode* p = *it;
TRACE("euf", tout << "erase " << bpp(p) << "\n";);
SASSERT(!p->merge_enabled() || m_table.contains_ptr(p));
SASSERT(!p->merge_enabled() || p->is_cgr());
if (p->merge_enabled())
SASSERT(!p->cgc_enabled() || m_table.contains_ptr(p));
SASSERT(!p->cgc_enabled() || p->is_cgr());
if (p->cgc_enabled())
erase_from_table(p);
}
@ -541,7 +563,7 @@ namespace euf {
c->m_root = r1;
for (enode* p : enode_parents(r1))
if (p->merge_enabled() && (p->is_cgr() || !p->congruent(p->m_cg)))
if (p->cgc_enabled() && (p->is_cgr() || !p->congruent(p->m_cg)))
insert_table(p);
r2->m_parents.shrink(r2_num_parents);
unmerge_justification(n1);
@ -549,16 +571,14 @@ namespace euf {
bool egraph::propagate() {
SASSERT(m_new_lits_qhead <= m_new_lits.size());
SASSERT(m_num_scopes == 0 || m_to_merge.empty());
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
(m_new_lits_qhead < m_new_lits.size()) ||
(m_new_th_eqs_qhead < m_new_th_eqs.size()) ||
inconsistent();
}
@ -571,6 +591,7 @@ namespace euf {
m_updates.push_back(update_record(false, update_record::inconsistent()));
m_n1 = n1;
m_n2 = n2;
TRACE("euf", tout << "conflict " << bpp(n1) << " " << bpp(n2) << " " << j << "\n");
m_justification = j;
}
@ -649,7 +670,7 @@ namespace euf {
SASSERT(n1->get_decl() == n2->get_decl());
m_uses_congruence = true;
if (m_used_cc && !comm) {
m_used_cc(to_app(n1->get_expr()), to_app(n2->get_expr()));
m_used_cc(n1->get_app(), n2->get_app());
}
if (comm &&
n1->get_arg(0)->get_root() == n2->get_arg(1)->get_root() &&
@ -707,25 +728,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->get_app(), b->get_app(), 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 +758,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 +786,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();
@ -779,7 +803,7 @@ namespace euf {
for (enode* n : m_nodes)
n->invariant(*this);
for (enode* n : m_nodes)
if (n->merge_enabled() && n->num_args() > 0 && (!m_table.find(n) || n->get_root() != m_table.find(n)->get_root())) {
if (n->cgc_enabled() && n->num_args() > 0 && (!m_table.find(n) || n->get_root() != m_table.find(n)->get_root())) {
CTRACE("euf", !m_table.find(n), tout << "node is not in table\n";);
CTRACE("euf", m_table.find(n), tout << "root " << bpp(n->get_root()) << " table root " << bpp(m_table.find(n)->get_root()) << "\n";);
TRACE("euf", display(tout << bpp(n) << " is not closed under congruence\n"););
@ -814,7 +838,7 @@ namespace euf {
}
};
if (n->bool_var() != sat::null_bool_var)
out << "[b" << n->bool_var() << " := " << value_of() << (n->merge_tf() ? "" : " no merge") << "] ";
out << "[b" << n->bool_var() << " := " << value_of() << (n->cgc_enabled() ? "" : " no-cgc") << (n->merge_tf()? " merge-tf" : "") << "] ";
if (n->has_th_vars()) {
out << "[t";
for (auto const& v : enode_th_vars(n))
@ -831,7 +855,6 @@ namespace euf {
std::ostream& egraph::display(std::ostream& out) const {
out << "updates " << m_updates.size() << "\n";
out << "newlits " << m_new_lits.size() << " qhead: " << m_new_lits_qhead << "\n";
out << "neweqs " << m_new_th_eqs.size() << " qhead: " << m_new_th_eqs_qhead << "\n";
m_table.display(out);
unsigned max_args = 0;
@ -869,7 +892,8 @@ namespace euf {
n2->set_value(n1->value());
n2->m_bool_var = n1->m_bool_var;
n2->m_commutative = n1->m_commutative;
n2->m_merge_enabled = n1->m_merge_enabled;
n2->m_cgc_enabled = n1->m_cgc_enabled;
n2->m_merge_tf_enabled = n1->m_merge_tf_enabled;
n2->m_is_equality = n1->m_is_equality;
}
for (unsigned i = 0; i < src.m_nodes.size(); ++i) {
@ -890,16 +914,20 @@ 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);
template void euf::egraph::explain(ptr_vector<expr_dependency>& justifications, cc_justification*);
template void euf::egraph::explain_todo(ptr_vector<expr_dependency>& justifications, cc_justification*);
template void euf::egraph::explain_eq(ptr_vector<expr_dependency>& justifications, cc_justification*, enode* a, enode* b);
template unsigned euf::egraph::explain_diseq(ptr_vector<expr_dependency>& justifications, cc_justification*, enode* a, enode* b);
#if 0

View file

@ -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<app*,app*,uint64_t, bool> cc_justification_record;
typedef svector<cc_justification_record> cc_justification;
class egraph {
@ -94,22 +101,21 @@ namespace euf {
void reset() { memset(this, 0, sizeof(*this)); }
};
struct update_record {
struct toggle_merge {};
struct toggle_cgc {};
struct toggle_merge_tf {};
struct add_th_var {};
struct replace_th_var {};
struct new_lit {};
struct new_th_eq {};
struct new_th_eq_qhead {};
struct new_lits_qhead {};
struct inconsistent {};
struct value_assignment {};
struct lbl_hash {};
struct lbl_set {};
struct update_children {};
struct set_relevant {};
enum class tag_t { is_set_parent, is_add_node, is_toggle_merge, is_update_children,
is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq,
is_lbl_hash, is_new_th_eq_qhead, is_new_lits_qhead,
enum class tag_t { is_set_parent, is_add_node, is_toggle_cgc, is_toggle_merge_tf, is_update_children,
is_add_th_var, is_replace_th_var, is_new_th_eq,
is_lbl_hash, is_new_th_eq_qhead,
is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant };
tag_t tag;
enode* r1;
@ -129,20 +135,18 @@ namespace euf {
tag(tag_t::is_set_parent), r1(r1), n1(n1), r2_num_parents(r2_num_parents) {}
update_record(enode* n) :
tag(tag_t::is_add_node), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, toggle_merge) :
tag(tag_t::is_toggle_merge), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, toggle_cgc) :
tag(tag_t::is_toggle_cgc), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, toggle_merge_tf) :
tag(tag_t::is_toggle_merge_tf), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, unsigned id, add_th_var) :
tag(tag_t::is_add_th_var), r1(n), n1(nullptr), r2_num_parents(id) {}
update_record(enode* n, theory_id id, theory_var v, replace_th_var) :
tag(tag_t::is_replace_th_var), r1(n), n1(nullptr), m_th_id(id), m_old_th_var(v) {}
update_record(new_lit) :
tag(tag_t::is_new_lit), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
update_record(new_th_eq) :
tag(tag_t::is_new_th_eq), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
update_record(unsigned qh, new_th_eq_qhead):
tag(tag_t::is_new_th_eq_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
update_record(unsigned qh, new_lits_qhead):
tag(tag_t::is_new_lits_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
update_record(bool inc, inconsistent) :
tag(tag_t::is_inconsistent), r1(nullptr), n1(nullptr), m_inconsistent(inc) {}
update_record(enode* n, value_assignment) :
@ -177,16 +181,17 @@ namespace euf {
enode *m_n1 = nullptr;
enode *m_n2 = nullptr;
justification m_justification;
unsigned m_new_lits_qhead = 0;
unsigned m_new_th_eqs_qhead = 0;
svector<enode_bool_pair> m_new_lits;
svector<th_eq> m_new_th_eqs;
bool_vector m_th_propagates_diseqs;
enode_vector m_todo;
stats m_stats;
bool m_uses_congruence = false;
bool m_default_relevant = true;
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
uint64_t m_congruence_timestamp = 0;
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
std::function<void(enode*, enode*)> m_on_propagate_literal;
std::function<void(enode*)> m_on_make;
std::function<void(expr*,expr*,expr*)> m_used_eq;
std::function<void(app*,app*)> m_used_cc;
@ -201,7 +206,7 @@ namespace euf {
void add_th_diseqs(theory_id id, theory_var v1, enode* r);
bool th_propagates_diseqs(theory_id id) const;
void add_literal(enode* n, bool is_eq);
void add_literal(enode* n, enode* ante);
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
void undo_add_th_var(enode* n, theory_id id);
enode* mk_enode(expr* f, unsigned generation, unsigned num_args, enode * const* args);
@ -211,7 +216,7 @@ namespace euf {
void merge_th_eq(enode* n, enode* root);
void merge_justification(enode* n1, enode* n2, justification j);
void reinsert_parents(enode* r1, enode* r2);
void remove_parents(enode* r1, enode* r2);
void remove_parents(enode* r);
void unmerge_justification(enode* n1);
void reinsert_equality(enode* p);
void update_children(enode* n);
@ -220,16 +225,16 @@ namespace euf {
void push_to_lca(enode* a, enode* lca);
void push_congruence(enode* n1, enode* n2, bool commutative);
void push_todo(enode* n);
void toggle_merge_enabled(enode* n, bool backtracking);
void toggle_cgc_enabled(enode* n, bool backtracking);
enode_bool_pair insert_table(enode* p);
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;
@ -278,11 +283,8 @@ namespace euf {
is an equality consequence.
*/
void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq);
bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); }
bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); }
enode_bool_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; }
th_eq get_th_eq() const { return m_new_th_eqs[m_new_th_eqs_qhead]; }
void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; }
void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; }
void set_lbl_hash(enode* n);
@ -290,13 +292,16 @@ namespace euf {
void add_th_var(enode* n, theory_var v, theory_id id);
void set_th_propagates_diseqs(theory_id id);
void set_merge_enabled(enode* n, bool enable_merge);
void set_cgc_enabled(enode* n, bool enable_cgc);
void set_merge_tf_enabled(enode* n, bool enable_merge_tf);
void set_value(enode* n, lbool value, justification j);
void set_bool_var(enode* n, unsigned v) { n->set_bool_var(v); }
void set_relevant(enode* n);
void set_default_relevant(bool b) { m_default_relevant = b; }
void set_on_merge(std::function<void(enode* root,enode* other)>& on_merge) { m_on_merge.push_back(on_merge); }
void set_on_propagate(std::function<void(enode* lit,enode* ante)>& on_propagate) { m_on_propagate_literal = on_propagate; }
void set_on_make(std::function<void(enode* n)>& on_make) { m_on_make = on_make; }
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }
@ -306,11 +311,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; }

View file

@ -36,7 +36,7 @@ namespace euf {
if (is_root()) {
VERIFY(!m_target);
for (enode* p : enode_parents(this)) {
if (!p->merge_enabled())
if (!p->cgc_enabled())
continue;
bool found = false;
for (enode* arg : enode_args(p)) {
@ -49,7 +49,7 @@ namespace euf {
if (c == this)
continue;
for (enode* p : enode_parents(c)) {
if (!p->merge_enabled())
if (!p->cgc_enabled())
continue;
bool found = false;
for (enode* q : enode_parents(this)) {

View file

@ -48,7 +48,8 @@ namespace euf {
bool m_mark3 = false;
bool m_commutative = false;
bool m_interpreted = false;
bool m_merge_enabled = true;
bool m_cgc_enabled = true;
bool m_merge_tf_enabled = false;
bool m_is_equality = false; // Does the expression represent an equality
bool m_is_relevant = false;
lbool m_value = l_undef; // Assignment by SAT solver for Boolean node
@ -91,7 +92,7 @@ namespace euf {
n->m_generation = generation,
n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative();
n->m_num_args = num_args;
n->m_merge_enabled = true;
n->m_cgc_enabled = true;
for (unsigned i = 0; i < num_args; ++i) {
SASSERT(to_app(f)->get_arg(i) == args[i]->get_expr());
n->m_args[i] = args[i];
@ -107,7 +108,7 @@ namespace euf {
n->m_root = n;
n->m_commutative = true;
n->m_num_args = 2;
n->m_merge_enabled = true;
n->m_cgc_enabled = true;
for (unsigned i = 0; i < num_args; ++i)
n->m_args[i] = nullptr;
return n;
@ -121,7 +122,7 @@ namespace euf {
n->m_root = n;
n->m_commutative = true;
n->m_num_args = 2;
n->m_merge_enabled = true;
n->m_cgc_enabled = true;
for (unsigned i = 0; i < num_args; ++i)
n->m_args[i] = nullptr;
return n;
@ -132,7 +133,8 @@ namespace euf {
void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); }
void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); }
void del_th_var(theory_id id) { m_th_vars.del_var(id); }
void set_merge_enabled(bool m) { m_merge_enabled = m; }
void set_cgc_enabled(bool m) { m_cgc_enabled = m; }
void set_merge_tf(bool m) { m_merge_tf_enabled = m; }
void set_value(lbool v) { m_value = v; }
void set_justification(justification j) { m_justification = j; }
void set_is_equality() { m_is_equality = true; }
@ -152,14 +154,13 @@ namespace euf {
bool is_relevant() const { return m_is_relevant; }
void set_relevant(bool b) { m_is_relevant = b; }
lbool value() const { return m_value; }
bool value_conflict() const { return value() != l_undef && get_root()->value() != l_undef && value() != get_root()->value(); }
sat::bool_var bool_var() const { return m_bool_var; }
bool is_cgr() const { return this == m_cg; }
enode* get_cg() const { return m_cg; }
bool commutative() const { return m_commutative; }
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
bool merge_enabled() const { return m_merge_enabled; }
bool merge_tf() const { return merge_enabled() && (class_size() > 1 || num_parents() > 0 || num_args() > 0); }
bool cgc_enabled() const { return m_cgc_enabled; }
bool merge_tf() const { return m_merge_tf_enabled && (class_size() > 1 || num_parents() > 0 || num_args() > 0); }
enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }
unsigned hash() const { return m_expr->get_id(); }

View file

@ -201,8 +201,6 @@ namespace euf {
enode_bool_pair etable::insert(enode * n) {
// it doesn't make sense to insert a constant.
SASSERT(n->num_args() > 0);
SASSERT(!m_manager.is_and(n->get_expr()));
SASSERT(!m_manager.is_or(n->get_expr()));
enode * n_prime;
void * t = get_table(n);
switch (static_cast<table_kind>(GET_TAG(t))) {

View file

@ -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);
}
}

View file

@ -44,7 +44,10 @@ public:
bool empty() const { return m_subst.empty(); }
unsigned size() const { return m_subst.size(); }
void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr);
void insert(expr* s, expr* def, expr_dependency* def_dep) { insert(s, def, nullptr, def_dep); }
void erase(expr * s);
expr* find(expr* s) { return m_subst[s]; }
expr_dependency* dep(expr* s) { return (*m_subst_dep)[s]; }
bool find(expr * s, expr * & def, proof * & def_pr);
bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep);
bool contains(expr * s);
@ -56,6 +59,10 @@ public:
std::ostream& display(std::ostream& out);
};
inline std::ostream& operator<<(std::ostream& out, expr_substitution& s) {
return s.display(out);
}
class scoped_expr_substitution {
expr_substitution& m_subst;
expr_ref_vector m_trail;

View file

@ -64,15 +64,22 @@ bool has_skolem_functions(expr * n) {
return false;
}
subterms::subterms(expr_ref_vector const& es, bool include_bound): m_include_bound(include_bound), m_es(es) {}
subterms::subterms(expr_ref const& e, bool include_bound) : m_include_bound(include_bound), m_es(e.m()) {if (e) m_es.push_back(e); }
subterms::iterator subterms::begin() { return iterator(*this, true); }
subterms::iterator subterms::end() { return iterator(*this, false); }
subterms::iterator::iterator(subterms& f, bool start): m_include_bound(f.m_include_bound), m_es(f.m_es) {
if (!start) m_es.reset();
subterms::subterms(expr_ref_vector const& es, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp): m_include_bound(include_bound), m_es(es), m_esp(esp), m_vp(vp) {}
subterms::subterms(expr_ref const& e, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp) : m_include_bound(include_bound), m_es(e.m()), m_esp(esp), m_vp(vp) { if (e) m_es.push_back(e); }
subterms::iterator subterms::begin() { return iterator(* this, m_esp, m_vp, true); }
subterms::iterator subterms::end() { return iterator(*this, nullptr, nullptr, false); }
subterms::iterator::iterator(subterms& f, ptr_vector<expr>* esp, expr_mark* vp, bool start): m_include_bound(f.m_include_bound), m_esp(esp), m_visitedp(vp) {
if (!esp)
m_esp = &m_es;
else
m_esp->reset();
if (!m_visitedp)
m_visitedp = &m_visited;
if (start)
m_esp->append(f.m_es.size(), f.m_es.data());
}
expr* subterms::iterator::operator*() {
return m_es.back();
return m_esp->back();
}
subterms::iterator subterms::iterator::operator++(int) {
iterator tmp = *this;
@ -80,27 +87,29 @@ subterms::iterator subterms::iterator::operator++(int) {
return tmp;
}
subterms::iterator& subterms::iterator::operator++() {
expr* e = m_es.back();
m_visited.mark(e, true);
expr* e = m_esp->back();
// IF_VERBOSE(0, verbose_stream() << e->get_ref_count() << "\n");
SASSERT(e->get_ref_count() > 0);
m_visitedp->mark(e, true);
if (is_app(e))
for (expr* arg : *to_app(e))
m_es.push_back(arg);
m_esp->push_back(arg);
else if (is_quantifier(e) && m_include_bound)
m_es.push_back(to_quantifier(e)->get_expr());
m_esp->push_back(to_quantifier(e)->get_expr());
while (!m_es.empty() && m_visited.is_marked(m_es.back()))
m_es.pop_back();
while (!m_esp->empty() && m_visitedp->is_marked(m_esp->back()))
m_esp->pop_back();
return *this;
}
bool subterms::iterator::operator==(iterator const& other) const {
// ignore state of visited
if (other.m_es.size() != m_es.size()) {
if (other.m_esp->size() != m_esp->size()) {
return false;
}
for (unsigned i = m_es.size(); i-- > 0; ) {
if (m_es.get(i) != other.m_es.get(i))
for (unsigned i = m_esp->size(); i-- > 0; ) {
if (m_esp->get(i) != other.m_esp->get(i))
return false;
}
return true;
@ -111,11 +120,11 @@ bool subterms::iterator::operator!=(iterator const& other) const {
}
subterms_postorder::subterms_postorder(expr_ref_vector const& es): m_es(es) {}
subterms_postorder::subterms_postorder(expr_ref const& e) : m_es(e.m()) { if (e) m_es.push_back(e); }
subterms_postorder::subterms_postorder(expr_ref_vector const& es, bool include_bound): m_include_bound(include_bound), m_es(es) {}
subterms_postorder::subterms_postorder(expr_ref const& e, bool include_bound) : m_include_bound(include_bound), m_es(e.m()) { if (e) m_es.push_back(e); }
subterms_postorder::iterator subterms_postorder::begin() { return iterator(*this, true); }
subterms_postorder::iterator subterms_postorder::end() { return iterator(*this, false); }
subterms_postorder::iterator::iterator(subterms_postorder& f, bool start): m_es(f.m_es) {
subterms_postorder::iterator::iterator(subterms_postorder& f, bool start): m_include_bound(f.m_include_bound), m_es(f.m_es) {
if (!start) m_es.reset();
next();
}
@ -144,6 +153,13 @@ void subterms_postorder::iterator::next() {
}
}
}
else if (is_quantifier(e) && m_include_bound) {
expr* body = to_quantifier(e)->get_expr();
if (!m_visited.is_marked(body)) {
m_es.push_back(body);
all_visited = false;
}
}
if (all_visited) {
m_visited.mark(e, true);
break;

View file

@ -170,15 +170,20 @@ bool has_skolem_functions(expr * n);
class subterms {
bool m_include_bound = false;
expr_ref_vector m_es;
subterms(expr_ref const& e, bool include_bound);
subterms(expr_ref_vector const& es, bool include_bound);
ptr_vector<expr>* m_esp = nullptr;
expr_mark* m_vp = nullptr;
subterms(expr_ref const& e, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp);
subterms(expr_ref_vector const& es, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp);
public:
~subterms() { if (m_vp) m_vp->reset(); }
class iterator {
bool m_include_bound = false;
expr_ref_vector m_es;
expr_mark m_visited;
bool m_include_bound = false;
ptr_vector<expr> m_es;
ptr_vector<expr>* m_esp = nullptr;
expr_mark m_visited;
expr_mark* m_visitedp = nullptr;
public:
iterator(subterms& f, bool start);
iterator(subterms& f, ptr_vector<expr>* esp, expr_mark* vp, bool start);
expr* operator*();
iterator operator++(int);
iterator& operator++();
@ -186,19 +191,24 @@ public:
bool operator!=(iterator const& other) const;
};
static subterms all(expr_ref const& e) { return subterms(e, true); }
static subterms ground(expr_ref const& e) { return subterms(e, false); }
static subterms all(expr_ref_vector const& e) { return subterms(e, true); }
static subterms ground(expr_ref_vector const& e) { return subterms(e, false); }
static subterms all(expr_ref const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, true, esp, vp); }
static subterms ground(expr_ref const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); }
static subterms all(expr_ref_vector const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, true, esp, vp); }
static subterms ground(expr_ref_vector const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); }
iterator begin();
iterator end();
};
class subterms_postorder {
bool m_include_bound;
expr_ref_vector m_es;
subterms_postorder(expr_ref_vector const& es, bool include_bound);
subterms_postorder(expr_ref const& e, bool include_bound);
public:
class iterator {
bool m_include_bound = false;
expr_ref_vector m_es;
expr_mark m_visited, m_seen;
void next();
@ -210,8 +220,10 @@ public:
bool operator==(iterator const& other) const;
bool operator!=(iterator const& other) const;
};
subterms_postorder(expr_ref_vector const& es);
subterms_postorder(expr_ref const& e);
static subterms_postorder all(expr_ref_vector const& es) { return subterms_postorder(es, true); }
static subterms_postorder ground(expr_ref_vector const& es) { return subterms_postorder(es, false); }
static subterms_postorder all(expr_ref const& e) { return subterms_postorder(e, true); }
static subterms_postorder ground(expr_ref const& e) { return subterms_postorder(e, false); }
iterator begin();
iterator end();
};

View file

@ -147,17 +147,13 @@ namespace format_ns {
parameter p(s);
return fm(m).mk_app(fid(m), OP_STRING, 1, &p, 0, nullptr);
}
format * mk_int(ast_manager & m, int i) {
char buffer[128];
SPRINTF_D(buffer, i);
return mk_string(m, buffer);
return mk_string(m, std::to_string(i));
}
format * mk_unsigned(ast_manager & m, unsigned u) {
char buffer[128];
SPRINTF_U(buffer, u);
return mk_string(m, buffer);
return mk_string(m, std::to_string(u));
}
format * mk_indent(ast_manager & m, unsigned i, format * f) {

View file

@ -324,8 +324,8 @@ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl *
expr_ref else_value(m.mk_app(to_bv_i, dom.size(), dom.data()), m);
result->set_else(else_value);
}
else if (m_fpa_util.is_to_real(f)) {
expr_ref_vector dom(m);
else if (m_fpa_util.is_to_real(f)) {
SASSERT(dom.size() == 1);
func_decl_ref to_real_i(m.mk_func_decl(fid, OP_FPA_TO_REAL_I, 0, nullptr, dom.size(), dom.data()), m);
expr_ref else_value(m.mk_app(to_real_i, dom.size(), dom.data()), m);
result->set_else(else_value);

View file

@ -2809,6 +2809,8 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr *
expr * e = m.mk_eq(m_util.mk_to_real(result), x);
m_extra_assertions.push_back(e);
// x = 0 -> result = 0+
m_extra_assertions.push_back(m.mk_implies(m.mk_eq(x, zero), m.mk_eq(result, m_util.mk_pzero(result->get_sort()))));
}
SASSERT(is_well_sorted(m, result));
@ -3288,7 +3290,7 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex
void fpa2bv_converter::mk_to_ieee_bv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result)
{
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_IEEE_BV, 0, nullptr, num, args), m);
mk_to_bv(f, num, args, true, result);
mk_to_ieee_bv(fu, num, args, result);
}
void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) {
@ -3475,12 +3477,12 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg
void fpa2bv_converter::mk_to_ubv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_UBV, 0, nullptr, num, args), m);
mk_to_bv(f, num, args, false, result);
mk_to_bv(fu, num, args, false, result);
}
void fpa2bv_converter::mk_to_sbv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_SBV, 0, nullptr, num, args), m);
mk_to_bv(f, num, args, true, result);
mk_to_bv(fu, num, args, true, result);
}
expr_ref fpa2bv_converter::nan_wrap(expr * n) {
@ -3529,7 +3531,7 @@ void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr
void fpa2bv_converter::mk_to_real_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_REAL, 0, nullptr, num, args), m);
mk_to_real(f, num, args, result);
mk_to_real(fu, num, args, result);
}
void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {

View file

@ -33,8 +33,7 @@ public:
justified_expr(justified_expr const& other):
m(other.m),
m_fml(other.m_fml),
m_proof(other.m_proof)
{
m_proof(other.m_proof) {
m.inc_ref(m_fml);
m.inc_ref(m_proof);
}
@ -42,8 +41,7 @@ public:
justified_expr(justified_expr && other) noexcept :
m(other.m),
m_fml(nullptr),
m_proof(nullptr)
{
m_proof(nullptr) {
std::swap(m_fml, other.m_fml);
std::swap(m_proof, other.m_proof);
}
@ -51,10 +49,11 @@ public:
~justified_expr() {
m.dec_ref(m_fml);
m.dec_ref(m_proof);
m_fml = nullptr;
m_proof = nullptr;
m_fml = nullptr;
m_proof = nullptr;
}
expr* get_fml() const { return m_fml; }
proof* get_proof() const { return m_proof; }
};

View file

@ -175,12 +175,6 @@ namespace macro_manager_ns {
/**
\brief Mark all func_decls used in exprs as forbidden.
*/
void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) {
expr_mark visited;
macro_manager_ns::proc p(m_forbidden_set, m_forbidden);
for (unsigned i = 0; i < n; i++)
for_each_expr(p, visited, exprs[i]);
}
void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) {
expr_mark visited;

View file

@ -73,9 +73,7 @@ public:
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
void mark_forbidden(unsigned n, expr * const * exprs);
void mark_forbidden(unsigned n, justified_expr const * exprs);
void mark_forbidden(expr * e) { mark_forbidden(1, &e); }
bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); }
obj_hashtable<func_decl> const & get_forbidden_set() const { return m_forbidden_set; }
void display(std::ostream & out);

View file

@ -28,7 +28,7 @@ Revision History:
#include "ast/rewriter/bool_rewriter.h"
macro_util::macro_util(ast_manager & m):
m_manager(m),
m(m),
m_bv(m),
m_arith(m),
m_arith_rw(m),
@ -176,7 +176,7 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const {
*/
bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
expr * lhs = nullptr, * rhs = nullptr;
if (m_manager.is_eq(n, lhs, rhs) &&
if (m.is_eq(n, lhs, rhs) &&
is_macro_head(lhs, num_decls) &&
!is_forbidden(to_app(lhs)->get_decl()) &&
!occurs(to_app(lhs)->get_decl(), rhs)) {
@ -184,13 +184,13 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he
def = rhs;
return true;
}
if (m_manager.is_not(n, lhs) && m_manager.is_eq(lhs, lhs, rhs) &&
m_manager.is_bool(lhs) &&
if (m.is_not(n, lhs) && m.is_eq(lhs, lhs, rhs) &&
m.is_bool(lhs) &&
is_macro_head(lhs, num_decls) &&
!is_forbidden(to_app(lhs)->get_decl()) &&
!occurs(to_app(lhs)->get_decl(), rhs)) {
head = to_app(lhs);
def = m_manager.mk_not(rhs);
def = m.mk_not(rhs);
return true;
}
return false;
@ -216,7 +216,7 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he
*/
bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
expr * lhs = nullptr, * rhs = nullptr;
if (m_manager.is_eq(n, lhs, rhs) &&
if (m.is_eq(n, lhs, rhs) &&
is_macro_head(rhs, num_decls) &&
!is_forbidden(to_app(rhs)->get_decl()) &&
!occurs(to_app(rhs)->get_decl(), lhs)) {
@ -224,13 +224,13 @@ bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & h
def = lhs;
return true;
}
if (m_manager.is_not(n, n) && m_manager.is_eq(n, lhs, rhs) &&
m_manager.is_bool(lhs) &&
if (m.is_not(n, n) && m.is_eq(n, lhs, rhs) &&
m.is_bool(lhs) &&
is_macro_head(rhs, num_decls) &&
!is_forbidden(to_app(rhs)->get_decl()) &&
!occurs(to_app(rhs)->get_decl(), lhs)) {
head = to_app(rhs);
def = m_manager.mk_not(lhs);
def = m.mk_not(lhs);
return true;
}
return false;
@ -262,7 +262,7 @@ bool macro_util::poly_contains_head(expr * n, func_decl * f, expr * exception) c
bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const {
// TODO: obsolete... we should move to collect_arith_macro_candidates
if (!m_manager.is_eq(n) && !m_arith.is_le(n) && !m_arith.is_ge(n))
if (!m.is_eq(n) && !m_arith.is_le(n) && !m_arith.is_ge(n))
return false;
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
@ -306,7 +306,7 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex
if (h == nullptr)
return false;
head = to_app(h);
expr_ref tmp(m_manager);
expr_ref tmp(m);
tmp = m_arith.mk_add(args.size(), args.data());
if (inv)
mk_sub(tmp, rhs, def);
@ -321,12 +321,12 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex
*/
bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, app_ref & t) {
expr* lhs = nullptr, *rhs = nullptr;
if (!m_manager.is_eq(n, lhs, rhs))
if (!m.is_eq(n, lhs, rhs))
return false;
if (!is_ground(lhs) && !is_ground(rhs))
return false;
sort * s = lhs->get_sort();
if (m_manager.is_uninterp(s))
if (m.is_uninterp(s))
return false;
sort_size sz = s->get_num_elements();
if (sz.is_finite() && sz.size() == 1)
@ -351,11 +351,11 @@ bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, ap
bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t, expr_ref & def) {
if (!is_forall(n))
return false;
TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";);
TRACE("macro_util", tout << "processing: " << mk_pp(n, m) << "\n";);
expr * body = to_quantifier(n)->get_expr();
unsigned num_decls = to_quantifier(n)->get_num_decls();
expr * lhs, *rhs;
if (!m_manager.is_iff(body, lhs, rhs))
if (!m.is_iff(body, lhs, rhs))
return false;
if (is_pseudo_head(lhs, num_decls, head, t) &&
!is_forbidden(head->get_decl()) &&
@ -417,13 +417,11 @@ bool macro_util::is_quasi_macro_ok(expr * n, unsigned num_decls, expr * def) con
if (is_app(n) &&
to_app(n)->get_family_id() == null_family_id &&
to_app(n)->get_num_args() >= num_decls) {
unsigned num_args = to_app(n)->get_num_args();
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
unsigned num_found_vars = 0;
expr_free_vars fv;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(n)->get_arg(i);
for (expr* arg : *to_app(n)) {
if (occurs(to_app(n)->get_decl(), arg))
return false;
else
@ -466,14 +464,14 @@ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decl
continue;
}
}
var * new_var = m_manager.mk_var(next_var_idx, arg->get_sort());
var * new_var = m.mk_var(next_var_idx, arg->get_sort());
next_var_idx++;
expr * new_cond = m_manager.mk_eq(new_var, arg);
expr * new_cond = m.mk_eq(new_var, arg);
new_args.push_back(new_var);
new_conds.push_back(new_cond);
}
bool_rewriter(m_manager).mk_and(new_conds.size(), new_conds.data(), cond);
head = m_manager.mk_app(qhead->get_decl(), new_args.size(), new_args.data());
bool_rewriter(m).mk_and(new_conds.size(), new_conds.data(), cond);
head = m.mk_app(qhead->get_decl(), new_args.size(), new_args.data());
num_decls = next_var_idx;
}
@ -485,7 +483,7 @@ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decl
See normalize_expr
*/
void macro_util::mk_macro_interpretation(app * head, unsigned num_decls, expr * def, expr_ref & interp) const {
TRACE("macro_util", tout << mk_pp(head, m_manager) << "\n" << mk_pp(def, m_manager) << "\n";);
TRACE("macro_util", tout << mk_pp(head, m) << "\n" << mk_pp(def, m) << "\n";);
SASSERT(is_macro_head(head, head->get_num_args()) ||
is_quasi_macro_ok(head, head->get_num_args(), def));
SASSERT(!occurs(head->get_decl(), def));
@ -503,20 +501,20 @@ void macro_util::mk_macro_interpretation(app * head, unsigned num_decls, expr *
f(x_3, x_2) --> f(x_0, x_1)
*/
void macro_util::normalize_expr(app * head, unsigned num_decls, expr * t, expr_ref & norm_t) const {
expr_ref_buffer var_mapping(m_manager);
expr_ref_buffer var_mapping(m);
var_mapping.resize(num_decls);
bool changed = false;
unsigned num_args = head->get_num_args();
TRACE("macro_util",
tout << "head: " << mk_pp(head, m_manager) << "\n";
tout << "applying substitution to:\n" << mk_bounded_pp(t, m_manager) << "\n";);
tout << "head: " << mk_pp(head, m) << "\n";
tout << "applying substitution to:\n" << mk_bounded_pp(t, m) << "\n";);
for (unsigned i = 0; i < num_args; i++) {
var * v = to_var(head->get_arg(i));
unsigned vi = v->get_idx();
SASSERT(vi < num_decls);
if (vi != i) {
changed = true;
var_ref new_var(m_manager.mk_var(i, v->get_sort()), m_manager);
var_ref new_var(m.mk_var(i, v->get_sort()), m);
var_mapping.setx(num_decls - vi - 1, new_var);
}
else
@ -525,13 +523,13 @@ void macro_util::normalize_expr(app * head, unsigned num_decls, expr * t, expr_r
if (changed) {
// REMARK: t may have nested quantifiers... So, I must use the std order for variable substitution.
var_subst subst(m_manager, true);
var_subst subst(m, true);
TRACE("macro_util",
tout << "head: " << mk_pp(head, m_manager) << "\n";
tout << "applying substitution to:\n" << mk_ll_pp(t, m_manager) << "\nsubstitution:\n";
tout << "head: " << mk_pp(head, m) << "\n";
tout << "applying substitution to:\n" << mk_ll_pp(t, m) << "\nsubstitution:\n";
for (unsigned i = 0; i < var_mapping.size(); i++) {
if (var_mapping[i] != 0)
tout << "#" << i << " -> " << mk_ll_pp(var_mapping[i], m_manager);
tout << "#" << i << " -> " << mk_ll_pp(var_mapping[i], m);
});
norm_t = subst(t, var_mapping.size(), var_mapping.data());
}
@ -553,12 +551,9 @@ bool is_hint_head(expr * n, ptr_buffer<var> & vars) {
return false;
if (to_app(n)->get_decl()->is_associative() || to_app(n)->get_family_id() != null_family_id)
return false;
unsigned num_args = to_app(n)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(n)->get_arg(i);
for (expr * arg : *to_app(n))
if (is_var(arg))
vars.push_back(to_var(arg));
}
return !vars.empty();
}
@ -579,9 +574,7 @@ bool vars_of_is_subset(expr * n, ptr_buffer<var> const & vars) {
return false;
}
else if (is_app(curr)) {
unsigned num_args = to_app(curr)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(curr)->get_arg(i);
for (expr * arg : *to_app(curr)) {
if (is_ground(arg))
continue;
if (visited.contains(arg))
@ -611,13 +604,11 @@ bool is_hint_atom(expr * lhs, expr * rhs) {
}
void hint_to_macro_head(ast_manager & m, app * head, unsigned & num_decls, app_ref & new_head) {
unsigned num_args = head->get_num_args();
ptr_buffer<expr> new_args;
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
unsigned next_var_idx = num_decls;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = head->get_arg(i);
for (expr * arg : *head) {
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
SASSERT(idx < num_decls);
@ -642,8 +633,8 @@ void hint_to_macro_head(ast_manager & m, app * head, unsigned & num_decls, app_r
is_hint_head(head, vars) must also return true
*/
bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) {
TRACE("macro_util", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n";
if (exception) tout << mk_pp(exception, m_manager); else tout << "<null>";
TRACE("macro_util", tout << "is_poly_hint n:\n" << mk_pp(n, m) << "\nhead:\n" << mk_pp(head, m) << "\nexception:\n";
if (exception) tout << mk_pp(exception, m); else tout << "<null>";
tout << "\n";);
ptr_buffer<var> vars;
if (!is_hint_head(head, vars)) {
@ -664,7 +655,7 @@ bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) {
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (arg != exception && (occurs(f, arg) || !vars_of_is_subset(arg, vars))) {
TRACE("macro_util", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";);
TRACE("macro_util", tout << "failed because of:\n" << mk_pp(arg, m) << "\n";);
return false;
}
}
@ -710,36 +701,36 @@ void macro_util::macro_candidates::insert(func_decl * f, expr * def, expr * cond
// -----------------------------
void macro_util::insert_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) {
expr_ref norm_def(m_manager);
expr_ref norm_cond(m_manager);
expr_ref norm_def(m);
expr_ref norm_cond(m);
normalize_expr(head, num_decls, def, norm_def);
if (cond != nullptr)
normalize_expr(head, num_decls, cond, norm_cond);
else if (!hint)
norm_cond = m_manager.mk_true();
norm_cond = m.mk_true();
SASSERT(!hint || norm_cond.get() == 0);
r.insert(head->get_decl(), norm_def.get(), norm_cond.get(), ineq, satisfy_atom, hint);
}
void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom,
bool hint, macro_candidates & r) {
TRACE("macro_util", tout << expr_ref(head, m_manager) << "\n";);
TRACE("macro_util", tout << expr_ref(head, m) << "\n";);
if (!is_macro_head(head, head->get_num_args())) {
app_ref new_head(m_manager);
expr_ref extra_cond(m_manager);
expr_ref new_cond(m_manager);
app_ref new_head(m);
expr_ref extra_cond(m);
expr_ref new_cond(m);
if (!hint) {
quasi_macro_head_to_macro_head(head, num_decls, new_head, extra_cond);
if (cond == nullptr)
new_cond = extra_cond;
else
bool_rewriter(m_manager).mk_and(cond, extra_cond, new_cond);
bool_rewriter(m).mk_and(cond, extra_cond, new_cond);
}
else {
hint_to_macro_head(m_manager, head, num_decls, new_head);
hint_to_macro_head(m, head, num_decls, new_head);
TRACE("macro_util",
tout << "hint macro head: " << mk_ismt2_pp(new_head, m_manager) << std::endl;
tout << "hint macro def: " << mk_ismt2_pp(def, m_manager) << std::endl; );
tout << "hint macro head: " << mk_ismt2_pp(new_head, m) << std::endl;
tout << "hint macro def: " << mk_ismt2_pp(def, m) << std::endl; );
}
insert_macro(new_head, num_decls, def, new_cond, ineq, satisfy_atom, hint, r);
}
@ -751,10 +742,10 @@ void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def,
bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) {
if (m_curr_clause == nullptr)
return false;
SASSERT(is_clause(m_manager, m_curr_clause));
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
SASSERT(is_clause(m, m_curr_clause));
unsigned num_lits = get_clause_num_literals(m, m_curr_clause);
for (unsigned i = 0; i < num_lits; i++) {
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
expr * l = get_clause_literal(m, m_curr_clause, i);
if (l != except_lit && occurs(f, l))
return true;
}
@ -764,20 +755,20 @@ bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) {
void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond) {
if (m_curr_clause == nullptr)
return;
SASSERT(is_clause(m_manager, m_curr_clause));
expr_ref_buffer neg_other_lits(m_manager);
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
SASSERT(is_clause(m, m_curr_clause));
expr_ref_buffer neg_other_lits(m);
unsigned num_lits = get_clause_num_literals(m, m_curr_clause);
for (unsigned i = 0; i < num_lits; i++) {
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
expr * l = get_clause_literal(m, m_curr_clause, i);
if (l != except_lit) {
expr_ref neg_l(m_manager);
bool_rewriter(m_manager).mk_not(l, neg_l);
expr_ref neg_l(m);
bool_rewriter(m).mk_not(l, neg_l);
neg_other_lits.push_back(neg_l);
}
}
if (neg_other_lits.empty())
return;
bool_rewriter(m_manager).mk_and(neg_other_lits.size(), neg_other_lits.data(), extra_cond);
bool_rewriter(m).mk_and(neg_other_lits.size(), neg_other_lits.data(), extra_cond);
}
void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer<expr> & args) {
@ -800,14 +791,14 @@ void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer<expr>
}
void macro_util::add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r) {
expr_ref cond(m_manager);
expr_ref cond(m);
if (!hint)
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(head, num_decls, def, cond, ineq, true, hint, r);
}
void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool is_ineq, macro_candidates & r) {
if (!is_add(lhs) && m_manager.is_eq(atom)) // this case is a simple macro.
if (!is_add(lhs) && m.is_eq(atom)) // this case is a simple macro.
return;
ptr_buffer<expr> args;
unsigned lhs_num_args;
@ -837,9 +828,9 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a
if (_is_arith_macro || _is_poly_hint) {
collect_poly_args(lhs, arg, args);
expr_ref rest(m_manager);
expr_ref rest(m);
mk_add(args.size(), args.data(), arg->get_sort(), rest);
expr_ref def(m_manager);
expr_ref def(m);
mk_sub(rhs, rest, def);
// If is_poly_hint, rhs may contain variables that do not occur in to_app(arg).
// So, we should re-check.
@ -858,9 +849,9 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a
if (_is_arith_macro || _is_poly_hint) {
collect_poly_args(lhs, arg, args);
expr_ref rest(m_manager);
expr_ref rest(m);
mk_add(args.size(), args.data(), arg->get_sort(), rest);
expr_ref def(m_manager);
expr_ref def(m);
mk_sub(rest, rhs, def);
// If is_poly_hint, rhs may contain variables that do not occur in to_app(neg_arg).
// So, we should re-check.
@ -872,12 +863,12 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a
}
void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) {
TRACE("macro_util", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m_manager) << "\n";);
if (!m_manager.is_eq(atom) && !is_le_ge(atom))
TRACE("macro_util", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m) << "\n";);
if (!m.is_eq(atom) && !is_le_ge(atom))
return;
expr * lhs = to_app(atom)->get_arg(0);
expr * rhs = to_app(atom)->get_arg(1);
bool is_ineq = !m_manager.is_eq(atom);
bool is_ineq = !m.is_eq(atom);
collect_arith_macro_candidates(lhs, rhs, atom, num_decls, is_ineq, r);
collect_arith_macro_candidates(rhs, lhs, atom, num_decls, is_ineq, r);
}
@ -921,32 +912,40 @@ void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls,
void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r) {
expr* lhs, *rhs;
TRACE("macro_util", tout << "Candidate check for: " << mk_ismt2_pp(atom, m_manager) << std::endl;);
TRACE("macro_util", tout << "Candidate check for: " << mk_ismt2_pp(atom, m) << std::endl;);
if (m_manager.is_eq(atom, lhs, rhs)) {
auto insert_quasi = [&](expr* lhs, expr* rhs) {
if (is_quasi_macro_head(lhs, num_decls) &&
!is_forbidden(to_app(lhs)->get_decl()) &&
!occurs(to_app(lhs)->get_decl(), rhs) &&
!rest_contains_decl(to_app(lhs)->get_decl(), atom)) {
expr_ref cond(m_manager);
expr_ref cond(m);
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(to_app(lhs), num_decls, rhs, cond, false, true, false, r);
return true;
}
else if (is_hint_atom(lhs, rhs)) {
insert_quasi_macro(to_app(lhs), num_decls, rhs, nullptr, false, true, true, r);
}
return false;
};
if (is_quasi_macro_head(rhs, num_decls) &&
!is_forbidden(to_app(rhs)->get_decl()) &&
!occurs(to_app(rhs)->get_decl(), lhs) &&
!rest_contains_decl(to_app(rhs)->get_decl(), atom)) {
expr_ref cond(m_manager);
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(to_app(rhs), num_decls, lhs, cond, false, true, false, r);
}
else if (is_hint_atom(rhs, lhs)) {
insert_quasi_macro(to_app(rhs), num_decls, lhs, nullptr, false, true, true, r);
}
auto insert_hint = [&](expr* lhs, expr* rhs) {
if (is_hint_atom(lhs, rhs))
insert_quasi_macro(to_app(lhs), num_decls, rhs, nullptr, false, true, true, r);
};
if (m.is_eq(atom, lhs, rhs)) {
if (!insert_quasi(lhs, rhs))
insert_hint(lhs, rhs);
if (!insert_quasi(rhs, lhs))
insert_hint(rhs, lhs);
}
expr* atom2;
if (m.is_not(atom, atom2) && m.is_eq(atom2, lhs, rhs) && m.is_bool(lhs)) {
expr_ref nlhs(m.mk_not(lhs), m);
expr_ref nrhs(m.mk_not(rhs), m);
if (!insert_quasi(lhs, nrhs))
insert_hint(lhs, nrhs);
if (!insert_quasi(rhs, nlhs))
insert_hint(rhs, nlhs);
}
collect_arith_macro_candidates(atom, num_decls, r);
@ -965,11 +964,11 @@ void macro_util::collect_macro_candidates(quantifier * q, macro_candidates & r)
return;
unsigned num_decls = q->get_num_decls();
SASSERT(m_curr_clause == 0);
if (is_clause(m_manager, n)) {
if (is_clause(m, n)) {
m_curr_clause = n;
unsigned num_lits = get_clause_num_literals(m_manager, n);
unsigned num_lits = get_clause_num_literals(m, n);
for (unsigned i = 0; i < num_lits; i++)
collect_macro_candidates_core(get_clause_literal(m_manager, n, i), num_decls, r);
collect_macro_candidates_core(get_clause_literal(m, n, i), num_decls, r);
m_curr_clause = nullptr;
}
else {

View file

@ -56,7 +56,7 @@ public:
};
private:
ast_manager & m_manager;
ast_manager & m;
bv_util m_bv;
arith_util m_arith;
mutable arith_rewriter m_arith_rw;

View file

@ -114,10 +114,9 @@ bool quasi_macros::fully_depends_on(app * a, quantifier * q) const {
if (is_var(arg))
bitset.set(to_var(arg)->get_idx(), true);
for (unsigned i = 0; i < bitset.size() ; i++) {
for (unsigned i = 0; i < bitset.size() ; i++)
if (!bitset.get(i))
return false;
}
return false;
return true;
}
@ -167,18 +166,18 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
quantifier * q = to_quantifier(e);
expr * qe = q->get_expr(), *lhs = nullptr, *rhs = nullptr;
if (m.is_eq(qe, lhs, rhs)) {
if (is_quasi_def(q, lhs, rhs)) {
if (is_quasi_def(q, lhs, rhs)) {
a = to_app(lhs);
t = rhs;
return true;
} else if (is_quasi_def(q, rhs, lhs)) {
} else if (is_quasi_def(q, rhs, lhs)) {
a = to_app(rhs);
t = lhs;
return true;
}
}
else if (m.is_not(qe, lhs) && is_non_ground_uninterp(lhs) &&
is_unique(to_app(lhs)->get_decl())) { // this is like f(...) = false
is_unique(to_app(lhs)->get_decl())) { // this is like f(...) = false
a = to_app(lhs);
t = m.mk_false();
return true;
@ -189,8 +188,8 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
return true;
}
else if (m.is_not(qe, lhs) && m.is_eq(lhs, lhs, rhs) && m.is_bool(lhs)) {
if (is_quasi_def(q, lhs, rhs)) {
a = to_app(lhs);
if (is_quasi_def(q, lhs, rhs)) {
a = to_app(lhs);
t = m.mk_not(rhs);
return true;
} else if (is_quasi_def(q, rhs, lhs)) {

View file

@ -22,6 +22,7 @@ Notes:
#include "ast/normal_forms/nnf.h"
#include "ast/normal_forms/nnf_params.hpp"
#include "ast/used_vars.h"
#include "ast/ast_util.h"
#include "ast/well_sorted.h"
#include "ast/act_cache.h"
#include "ast/rewriter/var_subst.h"
@ -137,7 +138,7 @@ class skolemizer {
if (is_sk_hack(p)) {
expr * sk_hack = to_app(p)->get_arg(0);
if (q->get_kind() == forall_k) // check whether is in negative/positive context.
tmp = m.mk_or(body, m.mk_not(sk_hack)); // negative context
tmp = m.mk_or(body, mk_not(m, sk_hack)); // negative context
else
tmp = m.mk_and(body, sk_hack); // positive context
body = tmp;
@ -148,7 +149,7 @@ class skolemizer {
p = nullptr;
if (m_proofs_enabled) {
if (q->get_kind() == forall_k)
p = m.mk_skolemization(m.mk_not(q), m.mk_not(r));
p = m.mk_skolemization(mk_not(m, q), mk_not(m, r));
else
p = m.mk_skolemization(q, r);
}
@ -388,7 +389,7 @@ struct nnf::imp {
}
void skip(expr * t, bool pol) {
expr * r = pol ? t : m.mk_not(t);
expr * r = pol ? t : mk_not(m, t);
m_result_stack.push_back(r);
if (proofs_enabled()) {
m_result_pr_stack.push_back(m.mk_oeq_reflexivity(r));
@ -639,7 +640,7 @@ struct nnf::imp {
m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2);
if (!fr.m_pol)
n2 = m.mk_not(n2);
n2 = mk_not(m, n2);
m_result_stack.push_back(n2);
if (proofs_enabled()) {
if (!fr.m_pol) {

View file

@ -74,3 +74,46 @@ bool occurs(func_decl * d, expr * n) {
return false;
}
void mark_occurs(ptr_vector<expr>& to_check, expr* v, expr_mark& occ) {
expr_fast_mark2 visited;
occ.mark(v, true);
visited.mark(v, true);
while (!to_check.empty()) {
expr* e = to_check.back();
if (visited.is_marked(e)) {
to_check.pop_back();
continue;
}
if (is_app(e)) {
bool does_occur = false;
bool all_visited = true;
for (expr* arg : *to_app(e)) {
if (!visited.is_marked(arg)) {
to_check.push_back(arg);
all_visited = false;
}
else
does_occur |= occ.is_marked(arg);
}
if (all_visited) {
occ.mark(e, does_occur);
visited.mark(e, true);
to_check.pop_back();
}
}
else if (is_quantifier(e)) {
expr* body = to_quantifier(e)->get_expr();
if (visited.is_marked(body)) {
visited.mark(e, true);
occ.mark(e, occ.is_marked(body));
to_check.pop_back();
}
else
to_check.push_back(body);
}
else {
visited.mark(e, true);
to_check.pop_back();
}
}
}

View file

@ -18,8 +18,8 @@ Revision History:
--*/
#pragma once
class expr;
class func_decl;
#include "util/vector.h"
#include "ast/ast.h"
/**
\brief Return true if n1 occurs in n2
@ -31,4 +31,9 @@ bool occurs(expr * n1, expr * n2);
*/
bool occurs(func_decl * d, expr * n);
/**
* \brief Mark sub-expressions of to_check by whether v occurs in these.
*/
void mark_occurs(ptr_vector<expr>& to_check, expr* v, expr_mark& occurs);

View file

@ -1245,12 +1245,8 @@ void proof_checker::dump_proof(proof const* pr) {
}
void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) {
char buffer[128];
#ifdef _WINDOWS
sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id);
#else
sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id);
#endif
std::string buffer;
buffer = "proof_lemma_" + std::to_string(m_proof_lemma_id) + ".smt2";
std::ofstream out(buffer);
ast_smt_pp pp(m);
pp.set_benchmark_name("lemma");

View file

@ -106,7 +106,7 @@ public:
{
ast_manager &m = args.get_manager();
bool_rewriter brwr(m);
brwr.set_flat(false);
brwr.set_flat_and_or(false);
if (m.is_or(decl))
{ mk_or_core(args, res); }

View file

@ -36,7 +36,6 @@ namespace recfun {
ast_manager &m,
family_id fid,
def * d,
std::string & name,
unsigned case_index,
sort_ref_vector const & arg_sorts,
expr_ref_vector const& guards,
@ -44,10 +43,10 @@ namespace recfun {
: m_pred(m),
m_guards(guards),
m_rhs(expr_ref(rhs,m)),
m_def(d) {
parameter p(case_index);
func_decl_info info(fid, OP_FUN_CASE_PRED, 1, &p);
m_pred = m.mk_func_decl(symbol(name.c_str()), arg_sorts.size(), arg_sorts.data(), m.mk_bool_sort(), info);
m_def(d) {
parameter ps[2] = { parameter(case_index), parameter(d->get_decl()) };
func_decl_info info(fid, OP_FUN_CASE_PRED, 2, ps);
m_pred = m.mk_func_decl(symbol("case-def"), arg_sorts.size(), arg_sorts.data(), m.mk_bool_sort(), info);
}
def::def(ast_manager &m, family_id fid, symbol const & s,
@ -220,11 +219,10 @@ namespace recfun {
}
void def::add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) {
case_def c(m, m_fid, this, name, case_index, get_domain(), conditions, rhs);
void def::add_case(unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) {
case_def c(m, m_fid, this, case_index, get_domain(), conditions, rhs);
c.set_is_immediate(is_imm);
TRACEFN("add_case " << name
<< "\n" << mk_pp(rhs, m)
TRACEFN("add_case " << case_index << " " << mk_pp(rhs, m)
<< "\n:is_imm " << is_imm
<< "\n:guards " << conditions);
m_cases.push_back(c);
@ -261,7 +259,7 @@ namespace recfun {
// is the function a macro (unconditional body)?
if (is_macro || n_vars == 0 || !contains_ite(u, rhs)) {
// constant function or trivial control flow, only one (dummy) case
add_case(name, 0, conditions, rhs);
add_case(0, conditions, rhs);
return;
}
@ -347,7 +345,7 @@ namespace recfun {
// yield new case
bool is_imm = is_i(case_rhs);
add_case(name, case_idx++, conditions, case_rhs, is_imm);
add_case(case_idx++, conditions, case_rhs, is_imm);
}
}
@ -408,6 +406,7 @@ namespace recfun {
void promise_def::set_definition(replace& r, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) {
SASSERT(n_vars == d->get_arity());
d->m_is_macro = is_macro;
is_imm_pred is_i(*u);
d->compute_cases(*u, r, is_i, is_macro, n_vars, vars, rhs);
}
@ -435,6 +434,12 @@ namespace recfun {
return *(m_util.get());
}
void plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
op_names.push_back(builtin_name("case-def", OP_FUN_CASE_PRED));
op_names.push_back(builtin_name("recfun-num-rounds", OP_NUM_ROUNDS));
}
promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
def* d = u().decl_fun(name, n, params, range, is_generated);
SASSERT(!m_defs.contains(d->get_decl()));
@ -442,17 +447,18 @@ namespace recfun {
return promise_def(&u(), d);
}
void plugin::inherit(decl_plugin* other, ast_translation& tr) {
for (auto [k, v] : static_cast<plugin*>(other)->m_defs) {
void plugin::inherit(decl_plugin* _other, ast_translation& tr) {
plugin* other = static_cast<plugin*>(_other);
for (auto [k, v] : other->m_defs) {
func_decl_ref f(tr(k), tr.to());
if (m_defs.contains(f))
continue;
def* d = v->copy(u(), tr);
m_defs.insert(f, d);
for (case_def & c : d->get_cases())
m_case_defs.insert(c.get_decl(), &c);
m_case_defs.insert(c.get_decl(), &c);
}
m_has_rec_defs = other->m_has_rec_defs;
}
promise_def plugin::ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
@ -473,6 +479,7 @@ namespace recfun {
}
void plugin::set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) {
m_has_rec_defs |= !is_macro;
u().set_definition(r, d, is_macro, n_vars, vars, rhs);
for (case_def & c : d.get_def()->get_cases())
m_case_defs.insert(c.get_decl(), &c);
@ -495,6 +502,18 @@ namespace recfun {
func_decl * plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range)
{
func_decl_info info(get_family_id(), k, num_parameters, parameters);
switch (k) {
case OP_FUN_CASE_PRED:
SASSERT(num_parameters == 2);
return m().mk_func_decl(symbol("case-def"), arity, domain, m().mk_bool_sort(), info);
case OP_NUM_ROUNDS:
SASSERT(num_parameters == 1);
SASSERT(arity == 0);
return m().mk_const_decl(symbol("recfun-num-rounds"), m().mk_bool_sort(), info);
default:
break;
}
UNREACHABLE();
return nullptr;
}

View file

@ -61,7 +61,7 @@ namespace recfun {
expr_ref_vector m_guards; //<! conjunction that is equivalent to this case
expr_ref m_rhs; //<! if guard is true, `f(t1...tn) = rhs` holds
def * m_def; //<! definition this is a part of
bool m_immediate; //<! does `rhs` contain no defined_fun/case_pred?
bool m_immediate = false; //<! does `rhs` contain no defined_fun/case_pred?
case_def(ast_manager& m):
m_pred(m),
@ -72,7 +72,6 @@ namespace recfun {
case_def(ast_manager & m,
family_id fid,
def * d,
std::string & name,
unsigned case_index,
sort_ref_vector const & arg_sorts,
expr_ref_vector const& guards,
@ -117,13 +116,14 @@ namespace recfun {
func_decl_ref m_decl; //!< generic declaration
expr_ref m_rhs; //!< definition
family_id m_fid;
bool m_is_macro;
def(ast_manager &m, family_id fid, symbol const & s, unsigned arity, sort *const * domain, sort* range, bool is_generated);
// compute cases for a function, given its RHS (possibly containing `ite`).
void compute_cases(util& u, replace& subst, is_immediate_pred &,
bool is_macro, unsigned n_vars, var *const * vars, expr* rhs);
void add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false);
void add_case(unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false);
bool contains_ite(util& u, expr* e); // expression contains a test over a def?
bool contains_def(util& u, expr* e); // expression contains a def
public:
@ -138,6 +138,7 @@ namespace recfun {
bool is_fun_macro() const { return m_cases.size() == 1; }
bool is_fun_defined() const { return !is_fun_macro(); }
bool is_macro() const { return m_is_macro; }
def* copy(util& dst, ast_translation& tr);
@ -165,6 +166,7 @@ namespace recfun {
mutable scoped_ptr<util> m_util;
def_map m_defs; // function->def
case_def_map m_case_defs; // case_pred->def
bool m_has_rec_defs = false;
ast_manager & m() { return *m_manager; }
@ -187,6 +189,8 @@ namespace recfun {
func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) override;
void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override;
promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false);
@ -200,11 +204,13 @@ namespace recfun {
bool has_def(func_decl* f) const { return m_defs.contains(f); }
bool has_defs() const;
bool has_rec_defs() const { return m_has_rec_defs; }
def const& get_def(func_decl* f) const { return *(m_defs[f]); }
promise_def get_promise_def(func_decl* f) const { return promise_def(&u(), m_defs[f]); }
def& get_def(func_decl* f) { return *(m_defs[f]); }
bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); }
bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); }
case_def& get_case_def(func_decl* f) { SASSERT(has_case_def(f)); return *(m_case_defs[f]); }
bool is_defined(func_decl* f) {return has_case_def(f) && !get_def(f).get_cases().empty(); }
func_decl_ref_vector get_rec_funs() {
func_decl_ref_vector result(m());
@ -248,6 +254,8 @@ namespace recfun {
//<! don't use native theory if recursive function declarations are not populated with defs
bool has_defs() const { return m_plugin->has_defs(); }
bool has_rec_defs() const { return m_plugin->has_rec_defs(); }
//<! add a function declaration
def * decl_fun(symbol const & s, unsigned n_args, sort *const * args, sort * range, bool is_generated);

View file

@ -14,6 +14,7 @@ z3_add_component(rewriter
der.cpp
distribute_forall.cpp
dl_rewriter.cpp
dom_simplifier.cpp
elim_bounds.cpp
enum2bv_rewriter.cpp
expr_replacer.cpp
@ -25,6 +26,7 @@ z3_add_component(rewriter
hoist_rewriter.cpp
inj_axiom.cpp
label_rewriter.cpp
macro_replacer.cpp
maximize_ac_sharing.cpp
mk_simplified_app.cpp
pb_rewriter.cpp
@ -45,5 +47,6 @@ z3_add_component(rewriter
ast
params
automata
interval
polynomial
)

View file

@ -23,9 +23,8 @@ Notes:
#include "ast/ast_pp.h"
seq_util& arith_rewriter_core::seq() {
if (!m_seq) {
m_seq = alloc(seq_util, m());
}
if (!m_seq)
m_seq = alloc(seq_util, m);
return *m_seq;
}
@ -93,9 +92,9 @@ br_status arith_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c
case OP_TANH: SASSERT(num_args == 1); st = mk_tanh_core(args[0], result); break;
default: st = BR_FAILED; break;
}
CTRACE("arith_rewriter", st != BR_FAILED, tout << st << ": " << mk_pp(f, m());
for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m()) << " ";
tout << "\n==>\n" << mk_pp(result.get(), m()) << "\n";
CTRACE("arith_rewriter", st != BR_FAILED, tout << st << ": " << mk_pp(f, m);
for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m) << " ";
tout << "\n==>\n" << mk_pp(result.get(), m) << "\n";
if (is_app(result)) tout << "args: " << to_app(result)->get_num_args() << "\n";
);
return st;
@ -133,7 +132,7 @@ bool arith_rewriter::div_polynomial(expr * t, numeral const & g, const_treatment
SASSERT(!g.is_one());
unsigned sz;
expr * const * ms = get_monomials(t, sz);
expr_ref_buffer new_args(m());
expr_ref_buffer new_args(m);
numeral a;
for (unsigned i = 0; i < sz; i++) {
expr * arg = ms[i];
@ -196,10 +195,10 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref &
switch (kind) {
case LE: c = floor(c); break;
case GE: c = ceil(c); break;
case EQ: result = m().mk_false(); return true;
case EQ: result = m.mk_false(); return true;
}
}
expr_ref k(m_util.mk_numeral(c, is_int), m());
expr_ref k(m_util.mk_numeral(c, is_int), m);
switch (kind) {
case LE: result = m_util.mk_le(pp, k); return true;
case GE: result = m_util.mk_ge(pp, k); return true;
@ -223,24 +222,24 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref &
if (c.is_neg()) {
switch (kind) {
case EQ:
case LE: result = m().mk_false(); return true;
case GE: result = m().mk_true(); return true;
case LE: result = m.mk_false(); return true;
case GE: result = m.mk_true(); return true;
}
}
if (c.is_zero() && kind == GE) {
result = m().mk_true();
result = m.mk_true();
return true;
}
if (c.is_pos() && c >= abs(b)) {
switch (kind) {
case LE: result = m().mk_true(); return true;
case LE: result = m.mk_true(); return true;
case EQ:
case GE: result = m().mk_false(); return true;
case GE: result = m.mk_false(); return true;
}
}
// mod x b <= b - 1
if (c + rational::one() == abs(b) && kind == LE) {
result = m().mk_true();
result = m.mk_true();
return true;
}
}
@ -304,7 +303,7 @@ br_status arith_rewriter::is_separated(expr* arg1, expr* arg2, op_kind kind, exp
if (kind != LE && kind != GE)
return BR_FAILED;
rational bound(0), r1, r2;
expr_ref narg(m());
expr_ref narg(m);
bool has_bound = true;
if (!m_util.is_numeral(arg2, r2))
return BR_FAILED;
@ -335,47 +334,47 @@ br_status arith_rewriter::is_separated(expr* arg1, expr* arg2, op_kind kind, exp
if (kind == GE && r1 > r2)
return BR_FAILED;
if (kind == LE && r1 > r2) {
result = m().mk_false();
result = m.mk_false();
return BR_DONE;
}
if (kind == GE && r1 < r2) {
result = m().mk_false();
result = m.mk_false();
return BR_DONE;
}
SASSERT(r1 == r2);
expr_ref zero(m_util.mk_numeral(rational(0), arg1->get_sort()), m());
expr_ref zero(m_util.mk_numeral(rational(0), arg1->get_sort()), m);
if (r1.is_zero() && m_util.is_mul(arg1)) {
expr_ref_buffer eqs(m());
expr_ref_buffer eqs(m);
ptr_buffer<expr> args;
flat_mul(arg1, args);
for (expr* arg : args) {
if (m_util.is_numeral(arg))
continue;
eqs.push_back(m().mk_eq(arg, zero));
eqs.push_back(m.mk_eq(arg, zero));
}
result = m().mk_or(eqs);
result = m.mk_or(eqs);
return BR_REWRITE2;
}
if (kind == LE && m_util.is_add(arg1)) {
expr_ref_buffer leqs(m());
expr_ref_buffer leqs(m);
for (expr* arg : *to_app(arg1)) {
if (!m_util.is_numeral(arg))
leqs.push_back(m_util.mk_le(arg, zero));
}
result = m().mk_and(leqs);
result = m.mk_and(leqs);
return BR_REWRITE2;
}
if (kind == GE && m_util.is_add(arg1)) {
expr_ref_buffer geqs(m());
expr_ref_buffer geqs(m);
for (expr* arg : *to_app(arg1)) {
if (!m_util.is_numeral(arg))
geqs.push_back(m_util.mk_ge(arg, zero));
}
result = m().mk_and(geqs);
result = m.mk_and(geqs);
return BR_REWRITE2;
}
@ -399,8 +398,8 @@ bool arith_rewriter::elim_to_real_var(expr * var, expr_ref & new_var) {
bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial) {
if (m_util.is_mul(monomial)) {
expr_ref_buffer new_vars(m());
expr_ref new_var(m());
expr_ref_buffer new_vars(m);
expr_ref new_var(m);
unsigned num = to_app(monomial)->get_num_args();
for (unsigned i = 0; i < num; i++) {
if (!elim_to_real_var(to_app(monomial)->get_arg(i), new_var))
@ -417,8 +416,8 @@ bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial)
bool arith_rewriter::elim_to_real_pol(expr * p, expr_ref & new_p) {
if (m_util.is_add(p)) {
expr_ref_buffer new_monomials(m());
expr_ref new_monomial(m());
expr_ref_buffer new_monomials(m);
expr_ref new_monomial(m);
for (expr* arg : *to_app(p)) {
if (!elim_to_real_mon(arg, new_monomial))
return false;
@ -507,14 +506,14 @@ br_status arith_rewriter::reduce_power(expr * arg1, expr * arg2, op_kind kind, e
switch (kind) {
case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_REWRITE1;
case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_REWRITE1;
default: result = m().mk_eq(new_arg1, new_arg2); return BR_REWRITE1;
default: result = m.mk_eq(new_arg1, new_arg2); return BR_REWRITE1;
}
}
br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) {
expr *orig_arg1 = arg1, *orig_arg2 = arg2;
expr_ref new_arg1(m());
expr_ref new_arg2(m());
expr_ref new_arg1(m);
expr_ref new_arg2(m);
if ((is_zero(arg1) && is_reduce_power_target(arg2, kind == EQ)) ||
(is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ)))
return reduce_power(arg1, arg2, kind, result);
@ -524,29 +523,29 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
arg1 = new_arg1;
arg2 = new_arg2;
}
expr_ref new_new_arg1(m());
expr_ref new_new_arg2(m());
expr_ref new_new_arg1(m);
expr_ref new_new_arg2(m);
if (m_elim_to_real && elim_to_real(arg1, arg2, new_new_arg1, new_new_arg2)) {
arg1 = new_new_arg1;
arg2 = new_new_arg2;
CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";);
CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n";);
if (st == BR_FAILED)
st = BR_DONE;
}
numeral a1, a2;
if (is_numeral(arg1, a1) && is_numeral(arg2, a2)) {
switch (kind) {
case LE: result = a1 <= a2 ? m().mk_true() : m().mk_false(); return BR_DONE;
case GE: result = a1 >= a2 ? m().mk_true() : m().mk_false(); return BR_DONE;
default: result = a1 == a2 ? m().mk_true() : m().mk_false(); return BR_DONE;
case LE: result = a1 <= a2 ? m.mk_true() : m.mk_false(); return BR_DONE;
case GE: result = a1 >= a2 ? m.mk_true() : m.mk_false(); return BR_DONE;
default: result = a1 == a2 ? m.mk_true() : m.mk_false(); return BR_DONE;
}
}
#define ANUM_LE_GE_EQ() { \
switch (kind) { \
case LE: result = am.le(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \
case GE: result = am.ge(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \
default: result = am.eq(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \
case LE: result = am.le(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \
case GE: result = am.ge(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \
default: result = am.eq(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \
} \
}
@ -593,12 +592,12 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
if (!first && !g.is_one() && num_consts <= 1) {
bool is_sat = div_polynomial(arg1, g, (kind == LE ? CT_CEIL : (kind == GE ? CT_FLOOR : CT_FALSE)), new_arg1);
if (!is_sat) {
result = m().mk_false();
result = m.mk_false();
return BR_DONE;
}
is_sat = div_polynomial(arg2, g, (kind == LE ? CT_FLOOR : (kind == GE ? CT_CEIL : CT_FALSE)), new_arg2);
if (!is_sat) {
result = m().mk_false();
result = m.mk_false();
return BR_DONE;
}
arg1 = new_arg1.get();
@ -607,25 +606,25 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
}
}
expr* c = nullptr, *t = nullptr, *e = nullptr;
if (m().is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) {
if (m.is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) {
switch (kind) {
case LE: result = a1 <= a2 ? m().mk_or(c, m_util.mk_le(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2;
case GE: result = a1 >= a2 ? m().mk_or(c, m_util.mk_ge(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
case EQ: result = a1 == a2 ? m().mk_or(c, m().mk_eq(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2;
case LE: result = a1 <= a2 ? m.mk_or(c, m_util.mk_le(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2;
case GE: result = a1 >= a2 ? m.mk_or(c, m_util.mk_ge(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
case EQ: result = a1 == a2 ? m.mk_or(c, m.mk_eq(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2;
}
}
if (m().is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) {
if (m.is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) {
switch (kind) {
case LE: result = a1 <= a2 ? m().mk_or(m().mk_not(c), m_util.mk_le(t, arg2)) : m().mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2;
case GE: result = a1 >= a2 ? m().mk_or(m().mk_not(c), m_util.mk_ge(t, arg2)) : m().mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2;
case EQ: result = a1 == a2 ? m().mk_or(m().mk_not(c), m().mk_eq(t, arg2)) : m().mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2;
case LE: result = a1 <= a2 ? m.mk_or(m.mk_not(c), m_util.mk_le(t, arg2)) : m.mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2;
case GE: result = a1 >= a2 ? m.mk_or(m.mk_not(c), m_util.mk_ge(t, arg2)) : m.mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2;
case EQ: result = a1 == a2 ? m.mk_or(m.mk_not(c), m.mk_eq(t, arg2)) : m.mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2;
}
}
if (m().is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) {
if (m.is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) {
switch (kind) {
case LE: result = m().mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2;
case GE: result = m().mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
case EQ: result = m().mk_ite(c, m().mk_eq(t, arg2), m().mk_eq(e, arg2)); return BR_REWRITE2;
case LE: result = m.mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2;
case GE: result = m.mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
case EQ: result = m.mk_ite(c, m.mk_eq(t, arg2), m.mk_eq(e, arg2)); return BR_REWRITE2;
}
}
if (m_util.is_to_int(arg2) && is_numeral(arg1)) {
@ -642,7 +641,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
return BR_REWRITE1;
case EQ:
result = m_util.mk_ge(t, m_util.mk_numeral(a2, false));
result = m().mk_and(m_util.mk_lt(t, m_util.mk_numeral(a2+1, false)), result);
result = m.mk_and(m_util.mk_lt(t, m_util.mk_numeral(a2+1, false)), result);
return BR_REWRITE3;
}
}
@ -663,7 +662,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
switch (kind) {
case LE: result = m_util.mk_le(arg1, arg2); return BR_DONE;
case GE: result = m_util.mk_ge(arg1, arg2); return BR_DONE;
default: result = m().mk_eq(arg1, arg2); return BR_DONE;
default: result = m.mk_eq(arg1, arg2); return BR_DONE;
}
}
return BR_FAILED;
@ -674,7 +673,7 @@ br_status arith_rewriter::mk_le_core(expr * arg1, expr * arg2, expr_ref & result
}
br_status arith_rewriter::mk_lt_core(expr * arg1, expr * arg2, expr_ref & result) {
result = m().mk_not(m_util.mk_le(arg2, arg1));
result = m.mk_not(m_util.mk_le(arg2, arg1));
return BR_REWRITE2;
}
@ -683,7 +682,7 @@ br_status arith_rewriter::mk_ge_core(expr * arg1, expr * arg2, expr_ref & result
}
br_status arith_rewriter::mk_gt_core(expr * arg1, expr * arg2, expr_ref & result) {
result = m().mk_not(m_util.mk_le(arg1, arg2));
result = m.mk_not(m_util.mk_le(arg1, arg2));
return BR_REWRITE2;
}
@ -694,7 +693,7 @@ bool arith_rewriter::is_arith_term(expr * n) const {
br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
br_status st = BR_FAILED;
if (m_eq2ineq) {
result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
result = m.mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
st = BR_REWRITE2;
}
else if (m_arith_lhs || is_arith_term(arg1) || is_arith_term(arg2)) {
@ -724,7 +723,7 @@ br_status arith_rewriter::mk_and_core(unsigned n, expr* const* args, expr_ref& r
}
if (rest.size() < n - 1) {
rest.push_back(arg0);
result = m().mk_and(rest);
result = m.mk_and(rest);
return BR_REWRITE1;
}
}
@ -742,8 +741,8 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) {
rational a, b;
rational g = gcd(p, k, a, b);
if (g == 1) {
expr_ref nb(m_util.mk_numeral(b, true), m());
result = m().mk_eq(m_util.mk_mod(u, y),
expr_ref nb(m_util.mk_numeral(b, true), m);
result = m.mk_eq(m_util.mk_mod(u, y),
m_util.mk_mod(m_util.mk_mul(nb, arg2), y));
return true;
}
@ -752,7 +751,7 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) {
}
expr_ref arith_rewriter::neg_monomial(expr* e) const {
expr_ref_vector args(m());
expr_ref_vector args(m);
rational a1;
if (m_util.is_numeral(e, a1))
args.push_back(m_util.mk_numeral(-a1, e->get_sort()));
@ -773,10 +772,10 @@ expr_ref arith_rewriter::neg_monomial(expr* e) const {
args.push_back(e);
}
if (args.size() == 1) {
return expr_ref(args.back(), m());
return expr_ref(args.back(), m);
}
else {
return expr_ref(m_util.mk_mul(args.size(), args.data()), m());
return expr_ref(m_util.mk_mul(args.size(), args.data()), m);
}
}
@ -793,7 +792,7 @@ bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) const {
expr * t2 = to_app(t)->get_arg(0);
if (m_util.is_mul(t2) && is_numeral(to_app(t2)->get_arg(0), r) && r.is_neg()) {
expr_ref_vector args1(m());
expr_ref_vector args1(m);
for (expr* e1 : *to_app(t)) {
args1.push_back(neg_monomial(e1));
}
@ -826,7 +825,7 @@ bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args)
br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
if (is_anum_simp_target(num_args, args)) {
expr_ref_buffer new_args(m());
expr_ref_buffer new_args(m);
anum_manager & am = m_util.am();
scoped_anum r(am);
scoped_anum arg(am);
@ -864,7 +863,7 @@ br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, ex
new_args.push_back(m_util.mk_numeral(am, r, false));
br_status st = poly_rewriter<arith_rewriter_core>::mk_add_core(new_args.size(), new_args.data(), result);
if (st == BR_FAILED) {
result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
result = m.mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
return BR_DONE;
}
return st;
@ -876,7 +875,7 @@ br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, ex
br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
if (is_anum_simp_target(num_args, args)) {
expr_ref_buffer new_args(m());
expr_ref_buffer new_args(m);
anum_manager & am = m_util.am();
scoped_anum r(am);
scoped_anum arg(am);
@ -913,7 +912,7 @@ br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, ex
br_status st = poly_rewriter<arith_rewriter_core>::mk_mul_core(new_args.size(), new_args.data(), result);
if (st == BR_FAILED) {
result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
result = m.mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
return BR_DONE;
}
return st;
@ -998,7 +997,7 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul
else {
numeral k(1);
k /= v2;
result = m().mk_app(get_fid(), OP_MUL,
result = m.mk_app(get_fid(), OP_MUL,
m_util.mk_numeral(k, false),
arg1);
return BR_REWRITE1;
@ -1028,8 +1027,8 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul
v1 /= v2;
result = m_util.mk_mul(m_util.mk_numeral(v1, false),
m_util.mk_div(b, d));
expr_ref z(m_util.mk_real(0), m());
result = m().mk_ite(m().mk_eq(d, z), m_util.mk_div(arg1, z), result);
expr_ref z(m_util.mk_real(0), m);
result = m.mk_ite(m.mk_eq(d, z), m_util.mk_div(arg1, z), result);
return BR_REWRITE2;
}
}
@ -1039,7 +1038,7 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul
}
br_status arith_rewriter::mk_idivides(unsigned k, expr * arg, expr_ref & result) {
result = m().mk_eq(m_util.mk_mod(arg, m_util.mk_int(k)), m_util.mk_int(0));
result = m.mk_eq(m_util.mk_mod(arg, m_util.mk_int(k)), m_util.mk_int(0));
return BR_REWRITE2;
}
@ -1063,12 +1062,12 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu
return BR_FAILED;
}
if (arg1 == arg2) {
expr_ref zero(m_util.mk_int(0), m());
result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1));
expr_ref zero(m_util.mk_int(0), m);
result = m.mk_ite(m.mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1));
return BR_REWRITE3;
}
if (m_util.is_numeral(arg2, v2, is_int) && v2.is_pos() && m_util.is_add(arg1)) {
expr_ref_buffer args(m());
expr_ref_buffer args(m);
bool change = false;
rational add(0);
for (expr* arg : *to_app(arg1)) {
@ -1083,15 +1082,15 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu
}
}
if (change) {
result = m_util.mk_idiv(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
result = m_util.mk_idiv(m.mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
result = m_util.mk_add(m_util.mk_numeral(add, true), result);
TRACE("div_bug", tout << "mk_div result: " << result << "\n";);
return BR_REWRITE3;
}
}
if (divides(arg1, arg2, result)) {
expr_ref zero(m_util.mk_int(0), m());
result = m().mk_ite(m().mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result);
expr_ref zero(m_util.mk_int(0), m);
result = m.mk_ite(m.mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result);
return BR_REWRITE_FULL;
}
return BR_FAILED;
@ -1150,17 +1149,17 @@ expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) {
flat_mul(den, args2);
remove_divisor(arg, args1);
remove_divisor(arg, args2);
expr_ref zero(m_util.mk_int(0), m());
expr_ref zero(m_util.mk_int(0), m);
num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.data());
den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.data());
expr_ref d(m_util.mk_idiv(num, den), m());
expr_ref nd(m_util.mk_idiv(m_util.mk_uminus(num), m_util.mk_uminus(den)), m());
return expr_ref(m().mk_ite(m().mk_eq(zero, arg),
expr_ref d(m_util.mk_idiv(num, den), m);
expr_ref nd(m_util.mk_idiv(m_util.mk_uminus(num), m_util.mk_uminus(den)), m);
return expr_ref(m.mk_ite(m.mk_eq(zero, arg),
m_util.mk_idiv(zero, zero),
m().mk_ite(m_util.mk_ge(arg, zero),
m.mk_ite(m_util.mk_ge(arg, zero),
d,
nd)),
m());
m);
}
void arith_rewriter::flat_mul(expr* e, ptr_buffer<expr>& args) {
@ -1208,8 +1207,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
}
if (arg1 == arg2 && !m_util.is_numeral(arg2)) {
expr_ref zero(m_util.mk_int(0), m());
result = m().mk_ite(m().mk_eq(arg2, zero), m_util.mk_mod(zero, zero), zero);
expr_ref zero(m_util.mk_int(0), m);
result = m.mk_ite(m.mk_eq(arg2, zero), m_util.mk_mod(zero, zero), zero);
return BR_DONE;
}
@ -1222,8 +1221,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
// propagate mod inside only if there is something to reduce.
if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) {
TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";);
expr_ref_buffer args(m());
TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n";);
expr_ref_buffer args(m);
bool change = false;
for (expr* arg : *to_app(arg1)) {
rational arg_v;
@ -1246,8 +1245,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
if (!change) {
return BR_FAILED; // did not find any target for applying simplification
}
result = m_util.mk_mod(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m()) << "\n";);
result = m_util.mk_mod(m.mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m) << "\n";);
return BR_REWRITE3;
}
@ -1290,10 +1289,10 @@ br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & resul
}
else if (m_elim_rem) {
expr * mod = m_util.mk_mod(arg1, arg2);
result = m().mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)),
result = m.mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)),
mod,
m_util.mk_uminus(mod));
TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m()) << "\n";);
TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m) << "\n";);
return BR_REWRITE3;
}
return BR_FAILED;
@ -1322,7 +1321,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res
bool is_num_y = m_util.is_numeral(arg2, y);
auto ensure_real = [&](expr* e) { return m_util.is_int(e) ? m_util.mk_to_real(e) : e; };
TRACE("arith", tout << mk_pp(arg1, m()) << " " << mk_pp(arg2, m()) << "\n";);
TRACE("arith", tout << mk_bounded_pp(arg1, m) << " " << mk_bounded_pp(arg2, m) << "\n";);
if (is_num_x && x.is_one()) {
result = m_util.mk_numeral(x, false);
return BR_DONE;
@ -1377,7 +1376,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res
if (is_num_y && y.is_minus_one()) {
result = m_util.mk_div(m_util.mk_real(1), ensure_real(arg1));
result = m().mk_ite(m().mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
result = m.mk_ite(m.mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
m_util.mk_real(0),
result);
return BR_REWRITE2;
@ -1387,7 +1386,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res
// (^ t -k) --> (^ (/ 1 t) k)
result = m_util.mk_power(m_util.mk_div(m_util.mk_numeral(rational(1), false), arg1),
m_util.mk_numeral(-y, false));
result = m().mk_ite(m().mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
result = m.mk_ite(m.mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
m_util.mk_real(0),
result);
return BR_REWRITE3;
@ -1504,7 +1503,7 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) {
// Try to apply simplifications such as:
// (to_int (+ 1.0 (to_real x)) y) --> (+ 1 x (to_int y))
expr_ref_buffer int_args(m()), real_args(m());
expr_ref_buffer int_args(m), real_args(m);
for (expr* c : *to_app(arg)) {
if (m_util.is_numeral(c, a) && a.is_int()) {
int_args.push_back(m_util.mk_numeral(a, true));
@ -1520,17 +1519,17 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) {
return BR_FAILED;
if (real_args.empty()) {
result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.data());
result = m.mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.data());
return BR_REWRITE1;
}
if (!int_args.empty() && m_util.is_add(arg)) {
decl_kind k = to_app(arg)->get_decl()->get_decl_kind();
expr_ref t1(m().mk_app(get_fid(), k, int_args.size(), int_args.data()), m());
expr_ref t2(m().mk_app(get_fid(), k, real_args.size(), real_args.data()), m());
expr_ref t1(m.mk_app(get_fid(), k, int_args.size(), int_args.data()), m);
expr_ref t2(m.mk_app(get_fid(), k, real_args.size(), real_args.data()), m);
int_args.reset();
int_args.push_back(t1);
int_args.push_back(m_util.mk_to_int(t2));
result = m().mk_app(get_fid(), k, int_args.size(), int_args.data());
result = m.mk_app(get_fid(), k, int_args.size(), int_args.data());
return BR_REWRITE3;
}
}
@ -1550,9 +1549,9 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) {
for (expr* e : *to_app(arg))
new_args.push_back(m_util.mk_to_real(e));
if (m_util.is_add(arg))
result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
result = m.mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
else
result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
result = m.mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
return BR_REWRITE2;
}
}
@ -1562,23 +1561,23 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) {
br_status arith_rewriter::mk_is_int(expr * arg, expr_ref & result) {
numeral a;
if (m_util.is_numeral(arg, a)) {
result = a.is_int() ? m().mk_true() : m().mk_false();
result = a.is_int() ? m.mk_true() : m.mk_false();
return BR_DONE;
}
else if (m_util.is_to_real(arg)) {
result = m().mk_true();
result = m.mk_true();
return BR_DONE;
}
else {
result = m().mk_eq(m().mk_app(get_fid(), OP_TO_REAL,
m().mk_app(get_fid(), OP_TO_INT, arg)),
result = m.mk_eq(m.mk_app(get_fid(), OP_TO_REAL,
m.mk_app(get_fid(), OP_TO_INT, arg)),
arg);
return BR_REWRITE3;
}
}
br_status arith_rewriter::mk_abs_core(expr * arg, expr_ref & result) {
result = m().mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg));
result = m.mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg));
return BR_REWRITE2;
}
@ -1647,9 +1646,9 @@ bool arith_rewriter::is_pi_integer(expr * t) {
a = c;
b = d;
}
TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m()) << "\n";
tout << "a: " << mk_ismt2_pp(a, m()) << "\n";
tout << "b: " << mk_ismt2_pp(b, m()) << "\n";);
TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m) << "\n";
tout << "a: " << mk_ismt2_pp(a, m) << "\n";
tout << "b: " << mk_ismt2_pp(b, m) << "\n";);
return
(m_util.is_pi(a) && m_util.is_to_real(b)) ||
(m_util.is_to_real(a) && m_util.is_pi(b));
@ -1861,7 +1860,7 @@ br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) {
}
if (is_pi_multiple(arg, k)) {
expr_ref n(m()), d(m());
expr_ref n(m), d(m);
n = mk_sin_value(k);
if (n.get() == nullptr)
goto end;

View file

@ -25,13 +25,13 @@ Notes:
class arith_rewriter_core {
protected:
typedef rational numeral;
ast_manager& m;
arith_util m_util;
scoped_ptr<seq_util> m_seq;
bool m_expand_power{ false };
bool m_mul2power{ false };
bool m_expand_tan{ false };
bool m_expand_power = false;
bool m_mul2power = false;
bool m_expand_tan = false;
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
seq_util& seq();
@ -47,7 +47,7 @@ protected:
app* mk_power(expr* x, rational const& r, sort* s);
expr* coerce(expr* x, sort* s);
public:
arith_rewriter_core(ast_manager & m):m_util(m) {}
arith_rewriter_core(ast_manager & m):m(m), m_util(m) {}
bool is_zero(expr * n) const { return m_util.is_zero(n); }
};
@ -120,7 +120,7 @@ public:
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
result = m().mk_app(f, num_args, args);
result = m.mk_app(f, num_args, args);
}
br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
@ -159,30 +159,30 @@ public:
br_status mk_power_core(expr* arg1, expr* arg2, expr_ref & result);
void mk_div(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_div_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_DIV, arg1, arg2);
result = m.mk_app(get_fid(), OP_DIV, arg1, arg2);
}
void mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_idiv_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_IDIV, arg1, arg2);
result = m.mk_app(get_fid(), OP_IDIV, arg1, arg2);
}
void mk_mod(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_mod_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_MOD, arg1, arg2);
result = m.mk_app(get_fid(), OP_MOD, arg1, arg2);
}
void mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_rem_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_REM, arg1, arg2);
result = m.mk_app(get_fid(), OP_REM, arg1, arg2);
}
br_status mk_to_int_core(expr * arg, expr_ref & result);
br_status mk_to_real_core(expr * arg, expr_ref & result);
void mk_to_int(expr * arg, expr_ref & result) {
if (mk_to_int_core(arg, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_TO_INT, 1, &arg);
result = m.mk_app(get_fid(), OP_TO_INT, 1, &arg);
}
void mk_to_real(expr * arg, expr_ref & result) {
if (mk_to_real_core(arg, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_TO_REAL, 1, &arg);
result = m.mk_app(get_fid(), OP_TO_REAL, 1, &arg);
}
br_status mk_is_int(expr * arg, expr_ref & result);

View file

@ -196,15 +196,103 @@ bool array_rewriter::squash_store(unsigned n, expr* const* args, expr_ref& resul
}
br_status array_rewriter::mk_select_same_store(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref tmp(m());
expr *arg0 = args[0];
bool first = true;
#define RET(x, status) \
tmp = x; \
if (first || tmp == result) { \
result = std::move(tmp); \
return status; \
} \
return BR_FAILED;
while (true) {
if (m_util.is_store(arg0)) {
SASSERT(to_app(arg0)->get_num_args() == num_args+1);
switch (compare_args(num_args - 1, args+1, to_app(arg0)->get_args()+1)) {
case l_true:
// select(store(a, I, v), I) --> v
RET(to_app(arg0)->get_arg(num_args), BR_DONE);
case l_false:
// select(store(a, I, v), J) --> select(a, J) if I != J
arg0 = to_app(arg0)->get_arg(0);
continue;
case l_undef:
// check if loading from subsequent arrays yields the same value
if (first) {
result = to_app(arg0)->get_arg(num_args);
first = false;
}
else if (result != to_app(arg0)->get_arg(num_args))
return BR_FAILED;
arg0 = to_app(arg0)->get_arg(0);
continue;
}
}
if (m_util.is_const(arg0)) {
// select(const(v), I) --> v
RET(to_app(arg0)->get_arg(0), BR_DONE);
}
if (is_lambda(arg0)) {
// anywhere lambda reduction as opposed to whnf
// select(lambda(X) M, N) -> M[N/X]
quantifier* q = to_quantifier(arg0);
SASSERT(q->get_num_decls() == num_args - 1);
var_subst subst(m());
expr_ref_vector _args(m());
var_shifter sh(m());
for (unsigned i = 1; i < num_args; ++i) {
sh(args[i], num_args-1, result);
_args.push_back(result);
}
expr_ref tmp2 = subst(q->get_expr(), _args.size(), _args.data());
inv_var_shifter invsh(m());
invsh(tmp2, _args.size(), tmp2);
RET(std::move(tmp2), BR_REWRITE_FULL);
}
if (m_util.is_map(arg0)) {
app* a = to_app(arg0);
func_decl* f0 = m_util.get_map_func_decl(a);
expr_ref_vector args0(m());
for (expr* arg : *a) {
ptr_vector<expr> args1;
args1.push_back(arg);
args1.append(num_args-1, args + 1);
args0.push_back(m_util.mk_select(args1.size(), args1.data()));
}
RET(m().mk_app(f0, args0.size(), args0.data()), BR_REWRITE2);
}
if (m_util.is_as_array(arg0)) {
// select(as-array[f], I) --> f(I)
func_decl * f = m_util.get_as_array_func_decl(to_app(arg0));
RET(m().mk_app(f, num_args - 1, args + 1), BR_REWRITE1);
}
break;
}
return BR_FAILED;
}
br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 2);
br_status st = mk_select_same_store(num_args, args, result);
if (st != BR_FAILED)
return st;
result.reset();
if (m_util.is_store(args[0])) {
SASSERT(to_app(args[0])->get_num_args() == num_args+1);
switch (compare_args(num_args - 1, args+1, to_app(args[0])->get_args()+1)) {
case l_true:
// select(store(a, I, v), I) --> v
result = to_app(args[0])->get_arg(num_args);
return BR_DONE;
UNREACHABLE();
case l_false: {
expr* arg0 = to_app(args[0])->get_arg(0);
while (m_util.is_store(arg0) && compare_args(num_args-1, args + 1, to_app(arg0)->get_args() + 1) == l_false) {
@ -228,14 +316,17 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args,
}
return true;
};
expr *array = to_app(args[0])->get_arg(0);
bool is_leaf = m_util.is_const(array);
bool should_expand =
m_blast_select_store ||
is_leaf ||
are_values() ||
(m_expand_select_store && to_app(args[0])->get_arg(0)->get_ref_count() == 1);
(m_expand_select_store && array->get_ref_count() == 1);
if (should_expand) {
// select(store(a, I, v), J) --> ite(I=J, v, select(a, J))
ptr_buffer<expr> new_args;
new_args.push_back(to_app(args[0])->get_arg(0));
new_args.push_back(array);
new_args.append(num_args-1, args+1);
expr * sel_a_j = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.data());
expr * v = to_app(args[0])->get_arg(num_args);
@ -258,51 +349,6 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args,
}
}
if (m_util.is_const(args[0])) {
// select(const(v), I) --> v
result = to_app(args[0])->get_arg(0);
return BR_DONE;
}
if (is_lambda(args[0])) {
// anywhere lambda reduction as opposed to whnf
// select(lambda(X) M, N) -> M[N/X]
quantifier* q = to_quantifier(args[0]);
SASSERT(q->get_num_decls() == num_args - 1);
var_subst subst(m());
expr_ref_vector _args(m());
var_shifter sh(m());
for (unsigned i = 1; i < num_args; ++i) {
sh(args[i], num_args-1, result);
_args.push_back(result);
}
result = subst(q->get_expr(), _args.size(), _args.data());
inv_var_shifter invsh(m());
invsh(result, _args.size(), result);
return BR_REWRITE_FULL;
}
if (m_util.is_map(args[0])) {
app* a = to_app(args[0]);
func_decl* f0 = m_util.get_map_func_decl(a);
expr_ref_vector args0(m());
for (expr* arg : *a) {
ptr_vector<expr> args1;
args1.push_back(arg);
args1.append(num_args-1, args + 1);
args0.push_back(m_util.mk_select(args1.size(), args1.data()));
}
result = m().mk_app(f0, args0.size(), args0.data());
return BR_REWRITE2;
}
if (m_util.is_as_array(args[0])) {
// select(as-array[f], I) --> f(I)
func_decl * f = m_util.get_as_array_func_decl(to_app(args[0]));
result = m().mk_app(f, num_args - 1, args + 1);
return BR_REWRITE1;
}
expr* c, *th, *el;
if (m().is_ite(args[0], c, th, el) && (m_expand_select_ite || (th->get_ref_count() == 1 || el->get_ref_count() == 1))) {
ptr_vector<expr> args1, args2;

View file

@ -46,6 +46,11 @@ class array_rewriter {
expr_ref expand_store(expr* s);
bool squash_store(unsigned n, expr* const* args, expr_ref& result);
br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_select_same_store(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
public:
array_rewriter(ast_manager & m, params_ref const & p = params_ref()):
@ -63,10 +68,6 @@ public:
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_store(unsigned num_args, expr * const * args, expr_ref & result);
void mk_select(unsigned num_args, expr * const * args, expr_ref & result);
void mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);

View file

@ -59,7 +59,7 @@ class bit_blaster : public bit_blaster_tpl<bit_blaster_cfg> {
public:
bit_blaster(ast_manager & m, bit_blaster_params const & params);
bit_blaster_params const & get_params() const { return this->m_params; }
void set_flat(bool f) { m_rw.set_flat(f); }
void set_flat_and_or(bool f) { m_rw.set_flat_and_or(f); }
};

View file

@ -75,7 +75,7 @@ public:
bit_blaster_tpl<blaster_cfg>(blaster_cfg(m_rewriter, m_util)),
m_rewriter(m),
m_util(m) {
m_rewriter.set_flat(false);
m_rewriter.set_flat_and_or(false);
m_rewriter.set_elim_and(true);
}

View file

@ -27,7 +27,7 @@ Revision History:
template<typename Cfg>
void bit_blaster_tpl<Cfg>::checkpoint() {
if (memory::get_allocation_size() > m_max_memory)
if (memory::get_allocation_size() > m_max_memory || memory::above_high_watermark())
throw rewriter_exception(Z3_MAX_MEMORY_MSG);
if (!m().inc())
throw rewriter_exception(m().limit().get_cancel_msg());

View file

@ -24,7 +24,7 @@ Notes:
void bool_rewriter::updt_params(params_ref const & _p) {
bool_rewriter_params p(_p);
m_flat = p.flat();
m_flat_and_or = p.flat_and_or();
m_elim_and = p.elim_and();
m_elim_ite = p.elim_ite();
m_local_ctx = p.local_ctx();
@ -244,10 +244,38 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args
result = buffer.back();
return BR_DONE;
default:
#if 0
// stupid or removal. A very special case of circuit optimization.
expr* x, * y, * z, * u;
auto is_complement = [&](expr* a, expr* b) {
expr* c;
if (m().is_not(a, c) && c == b)
return true;
if (m().is_not(b, c) && c == a)
return true;
return false;
};
if (sz == 2 && m().is_and(buffer[0], x, y) && m().is_and(buffer[1], z, u) && x == z && is_complement(y, u)) {
result = x;
return BR_DONE;
}
#endif
if (m_local_ctx && m_local_ctx_cost <= m_local_ctx_limit) {
if (local_ctx_simp(sz, buffer.data(), result))
return BR_DONE;
}
#if 1
br_status st;
st = m_hoist.mk_or(buffer.size(), buffer.data(), result);
if (st == BR_DONE)
return BR_REWRITE1;
if (st != BR_FAILED)
return st;
#endif
if (s) {
ast_lt lt;
std::sort(buffer.begin(), buffer.end(), lt);
@ -290,7 +318,7 @@ br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args,
ast_lt lt;
std::sort(flat_args.begin(), flat_args.end(), lt);
}
result = m().mk_or(flat_args);
result = mk_or_app(flat_args.size(), flat_args.data());
}
return BR_DONE;
}
@ -555,10 +583,8 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_
result = arg; \
return true; \
} \
if (m_flat && m().is_or(arg)) { \
unsigned sz = to_app(arg)->get_num_args(); \
for (unsigned j = 0; j < sz; j++) { \
expr * arg_arg = to_app(arg)->get_arg(j); \
if (m_flat_and_or && m().is_or(arg)) { \
for (expr * arg_arg : *to_app(arg)) { \
push_new_arg(arg_arg, new_args, neg_lits, pos_lits); \
} \
} \
@ -621,11 +647,13 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result)
SASSERT(m().is_value(val));
if (m().are_distinct(val, e)) {
result = m().mk_and(mk_eq(t, val), cond);
mk_eq(t, val, result);
result = m().mk_and(result, cond);
return BR_REWRITE2;
}
if (m().are_distinct(val, t)) {
result = m().mk_and(mk_eq(e, val), m().mk_not(cond));
mk_eq(e, val, result);
result = m().mk_and(result, m().mk_not(cond));
return BR_REWRITE2;
}
if (m().are_equal(val, t)) {
@ -634,12 +662,14 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result)
return BR_DONE;
}
else {
result = m().mk_or(mk_eq(e, val), cond);
mk_eq(e, val, result);
result = m().mk_or(result, cond);
}
return BR_REWRITE2;
}
if (m().are_equal(val, e)) {
result = m().mk_or(mk_eq(t, val), m().mk_not(cond));
mk_eq(t, val, result);
result = m().mk_or(result, m().mk_not(cond));
return BR_REWRITE2;
}
@ -660,8 +690,10 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result)
app* bool_rewriter::mk_eq(expr* lhs, expr* rhs) {
// degrades simplification
// if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs);
if (m().are_equal(lhs, rhs))
return m().mk_true();
if (m().are_distinct(lhs, rhs))
return m().mk_false();
return m().mk_eq(lhs, rhs);
}
@ -731,7 +763,7 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
}
if (unfolded) {
result = mk_eq(lhs, rhs);
result = m().mk_eq(lhs, rhs);
return BR_REWRITE1;
}
@ -748,6 +780,10 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
}
}
}
if (m_order_eq && lhs->get_id() > rhs->get_id()) {
result = m().mk_eq(rhs, lhs);
return BR_DONE;
}
return BR_FAILED;
}
@ -759,7 +795,8 @@ br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args
if (num_args == 2) {
expr_ref tmp(m());
result = m().mk_not(mk_eq(args[0], args[1]));
mk_eq(args[0], args[1], tmp);
mk_not(tmp, result);
return BR_REWRITE2; // mk_eq may be dispatched to other rewriters.
}
@ -798,10 +835,10 @@ br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args
}
if (m_blast_distinct && num_args < m_blast_distinct_threshold) {
ptr_buffer<expr> new_diseqs;
expr_ref_vector new_diseqs(m());
for (unsigned i = 0; i < num_args; i++) {
for (unsigned j = i + 1; j < num_args; j++)
new_diseqs.push_back(m().mk_not(mk_eq(args[i], args[j])));
new_diseqs.push_back(m().mk_not(m().mk_eq(args[i], args[j])));
}
result = m().mk_and(new_diseqs);
return BR_REWRITE3;
@ -820,6 +857,7 @@ br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & re
s = true;
}
// (ite c (ite c t1 t2) t3) ==> (ite c t1 t3
if (m().is_ite(t) && to_app(t)->get_arg(0) == c) {
// Remark: (ite c (ite (not c) t1 t2) t3) ==> (ite c t2 t3) does not happen if applying rewrites bottom up
@ -906,23 +944,24 @@ br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & re
}
#if 0
expr* t1, *t2;
// (ite c (not (= t1 t2)) t1) ==> (not (= t1 (and c t2)))
if (m().is_not(t, t1) && m().is_eq(t1, t1, t2) && e == t1) {
expr_ref a(m());
mk_and(c, t2, a);
result = m().mk_not(m().mk_eq(t1, a));
result = mk_not(mk_eq(t1, a));
return BR_REWRITE3;
}
if (m().is_not(t, t1) && m().is_eq(t1, t2, t1) && e == t1) {
expr_ref a(m());
mk_and(c, t2, a);
result = m().mk_eq(t1, a);
result = mk_eq(t1, a);
return BR_REWRITE3;
}
#endif
if (m().is_ite(t) && m_ite_extra_rules && m_elim_ite) {
// (ite c1 (ite c2 t1 t2) t1) ==> (ite (and c1 (not c2)) t2 t1)
if (e == to_app(t)->get_arg(1)) {

View file

@ -20,6 +20,7 @@ Notes:
#include "ast/ast.h"
#include "ast/rewriter/rewriter.h"
#include "ast/rewriter/hoist_rewriter.h"
#include "util/params.h"
/**
@ -50,10 +51,12 @@ Notes:
*/
class bool_rewriter {
ast_manager & m_manager;
bool m_flat;
bool m_local_ctx;
bool m_elim_and;
bool m_blast_distinct;
hoist_rewriter m_hoist;
bool m_flat_and_or = false;
bool m_local_ctx = false;
bool m_elim_and = false;
bool m_blast_distinct = false;
bool m_order_eq = false;
unsigned m_blast_distinct_threshold;
bool m_ite_extra_rules;
unsigned m_local_ctx_limit;
@ -78,16 +81,17 @@ class bool_rewriter {
void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits);
public:
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); }
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { updt_params(p); }
ast_manager & m() const { return m_manager; }
family_id get_fid() const { return m().get_basic_family_id(); }
bool is_eq(expr * t) const { return m().is_eq(t); }
bool flat() const { return m_flat; }
void set_flat(bool f) { m_flat = f; }
bool flat_and_or() const { return m_flat_and_or; }
void set_flat_and_or(bool f) { m_flat_and_or = f; }
bool elim_and() const { return m_elim_and; }
void set_elim_and(bool f) { m_elim_and = f; }
void reset_local_ctx_cost() { m_local_ctx_cost = 0; }
void set_order_eq(bool f) { m_order_eq = f; }
void updt_params(params_ref const & p);
@ -111,7 +115,7 @@ public:
mk_and_as_or(num_args, args, result);
return BR_DONE;
}
else if (m_flat) {
else if (m_flat_and_or) {
return mk_flat_and_core(num_args, args, result);
}
else {
@ -119,7 +123,7 @@ public:
}
}
br_status mk_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
return m_flat ?
return m_flat_and_or ?
mk_flat_or_core(num_args, args, result) :
mk_nflat_or_core(num_args, args, result);
}
@ -234,7 +238,7 @@ public:
struct bool_rewriter_cfg : public default_rewriter_cfg {
bool_rewriter m_r;
bool flat_assoc(func_decl * f) const { return m_r.flat() && (m_r.m().is_and(f) || m_r.m().is_or(f)); }
bool flat_assoc(func_decl * f) const { return m_r.flat_and_or() && (m_r.m().is_and(f) || m_r.m().is_or(f)); }
bool rewrite_patterns() const { return false; }
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = nullptr;

View file

@ -0,0 +1,349 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
bv_bounds_simplifier.h
Abstract:
Context dependent simplification for bit-vectors
Author:
Nikolaj and Nuno
--*/
#pragma once
#include "math/interval/mod_interval.h"
namespace bv {
struct undo_bound {
expr* e = nullptr;
interval b;
bool fresh = false;
undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {}
};
struct bv_bounds_base {
typedef obj_map<expr, interval> map;
typedef obj_map<expr, bool> expr_set;
typedef obj_map<expr, unsigned> expr_cnt;
ast_manager& m;
bv_util m_bv;
vector<undo_bound> m_scopes;
svector<expr_set*> m_expr_vars;
svector<expr_cnt*> m_bound_exprs;
map m_bound;
bool m_propagate_eq = false;
ptr_vector<expr> m_args;
bv_bounds_base(ast_manager& m):m(m), m_bv(m) {}
virtual ~bv_bounds_base() {
for (auto* e : m_expr_vars)
dealloc(e);
for (auto* b : m_bound_exprs)
dealloc(b);
}
bool is_bound(expr *e, expr*& v, interval& b) const {
rational r;
expr *lhs = nullptr, *rhs = nullptr;
unsigned sz;
if (m_bv.is_bv_ule(e, lhs, rhs)) {
if (m_bv.is_numeral(lhs, r, sz)) { // C ule x <=> x uge C
if (m_bv.is_numeral(rhs))
return false;
b = interval(r, rational::power_of_two(sz) - 1, sz, true);
v = rhs;
return true;
}
if (m_bv.is_numeral(rhs, r, sz)) { // x ule C
b = interval(rational::zero(), r, sz, true);
v = lhs;
return true;
}
// TBD: x + s <= x + q
// x + s <= x
// x <= x + q
}
else if (m_bv.is_bv_sle(e, lhs, rhs)) {
if (m_bv.is_numeral(lhs, r, sz)) { // C sle x <=> x sge C
if (m_bv.is_numeral(rhs))
return false;
b = interval(r, rational::power_of_two(sz-1) - 1, sz, true);
v = rhs;
return true;
}
if (m_bv.is_numeral(rhs, r, sz)) { // x sle C
b = interval(rational::power_of_two(sz-1), r, sz, true);
v = lhs;
return true;
}
// TBD: other cases for forbidden intervals
}
else if (m.is_eq(e, lhs, rhs)) {
if (m_bv.is_numeral(rhs))
std::swap(lhs, rhs);
if (m_bv.is_numeral(rhs))
return false;
if (m_bv.is_numeral(lhs, r, sz)) {
unsigned lo, hi;
expr* rhs2;
if (m_bv.is_extract(rhs, lo, hi, rhs2) && r == 0) {
unsigned sz2 = m_bv.get_bv_size(rhs2);
if (sz2 - 1 == hi) {
b = interval(rational::zero(), rational::power_of_two(lo) - 1, sz2, false);
v = rhs2;
return true;
}
}
b = interval(r, r, sz, true);
v = rhs;
return true;
}
}
return false;
}
bool assert_expr_core(expr * t, bool sign) {
while (m.is_not(t, t))
sign = !sign;
interval b;
expr* t1;
if (is_bound(t, t1, b)) {
SASSERT(m_bv.get_bv_size(t1) == b.size());
SASSERT(!m_bv.is_numeral(t1));
if (sign && !b.negate(b))
return false;
TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";);
map::obj_map_entry* e = m_bound.find_core(t1);
if (e) {
interval& old = e->get_data().m_value;
interval intr;
if (!old.intersect(b, intr))
return false;
if (old == intr)
return true;
m_scopes.push_back(undo_bound(t1, old, false));
old = intr;
SASSERT(old.size() == m_bv.get_bv_size(t1));
}
else {
SASSERT(b.size() == m_bv.get_bv_size(t1));
m_bound.insert(t1, b);
m_scopes.push_back(undo_bound(t1, interval(), true));
}
}
return true;
}
//
// x + q <= s <=> x not in [s - q + 1, -q[
// <=> x in [-q, s - q], s != -1
//
// x in [lo, hi]
// q = -lo
// hi = s + lo => s = hi - lo
// hi - lo != -1
//
expr_ref mk_bound(expr* t, rational const& lo, rational const& hi) {
sort* s = t->get_sort();
if (lo == hi + 1)
return expr_ref(m.mk_true(), m);
else
return expr_ref(m_bv.mk_ule(m_bv.mk_bv_add(t, m_bv.mk_numeral(-lo, s)), m_bv.mk_numeral(hi - lo, s)), m);
}
//
// use interval information to rewrite sub-terms x to (0 ++ x[hi:0])
// in other words, identify leading 0s.
//
bool zero_patch(expr* t, expr_ref& result) {
if (!is_app(t))
return false;
if (m_bv.is_extract(t))
return false;
m_args.reset();
bool simplified = false;
interval b;
for (expr* arg : *to_app(t)) {
if (!m_bv.is_bv(arg)) {
m_args.push_back(arg);
continue;
}
if (!m_bv.is_extract(arg) && m_bound.find(arg, b)) {
unsigned num_bits = b.hi().get_num_bits();
unsigned bv_size = m_bv.get_bv_size(arg);
if (0 < num_bits && num_bits < bv_size) {
m_args.push_back(m_bv.mk_concat(m_bv.mk_zero(bv_size - num_bits),
m_bv.mk_extract(num_bits - 1, 0, arg)));
simplified = true;
}
else
m_args.push_back(arg);
}
else
m_args.push_back(arg);
}
if (simplified) {
result = m.mk_app(to_app(t)->get_decl(), m_args);
return true;
}
return false;
}
bool simplify_core(expr* t, expr_ref& result) {
expr* t1;
interval b;
if (m_bound.find(t, b) && b.is_singleton()) {
result = m_bv.mk_numeral(b.lo(), m_bv.get_bv_size(t));
return true;
}
if (zero_patch(t, result))
return result;
if (!m.is_bool(t))
return false;
bool sign = false;
while (m.is_not(t, t))
sign = !sign;
if (!is_bound(t, t1, b))
return false;
if (sign && b.tight()) {
sign = false;
if (!b.negate(b)) {
result = m.mk_false();
return true;
}
}
interval ctx, intr;
result = nullptr;
if (b.is_full() && b.tight())
result = m.mk_true();
else if (!m_bound.find(t1, ctx)) {
}
else if (ctx.implies(b))
result = m.mk_true();
else if (!b.intersect(ctx, intr))
result = m.mk_false();
else if (m_propagate_eq && intr.is_singleton())
result = m.mk_eq(t1, m_bv.mk_numeral(intr.lo(), t1->get_sort()));
else if (false && intr != b)
result = mk_bound(t1, intr.lo(), intr.hi());
else {
TRACE("bv", tout << mk_pp(t, m) << " b: " << b << " ctx: " << ctx << " intr " << intr << "\n");
}
CTRACE("bv", result, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";);
if (sign && result)
result = m.mk_not(result);
return result != nullptr;
}
// check if t contains v
ptr_vector<expr> todo;
bool contains(expr* t, expr* v) {
ast_fast_mark1 mark;
todo.push_back(t);
while (!todo.empty()) {
t = todo.back();
todo.pop_back();
if (mark.is_marked(t))
continue;
if (t == v) {
todo.reset();
return true;
}
mark.mark(t);
if (!is_app(t))
continue;
app* a = to_app(t);
todo.append(a->get_num_args(), a->get_args());
}
return false;
}
bool contains_bound(expr* t) {
ast_fast_mark1 mark1;
ast_fast_mark2 mark2;
todo.push_back(t);
while (!todo.empty()) {
t = todo.back();
todo.pop_back();
if (mark1.is_marked(t)) {
continue;
}
mark1.mark(t);
if (!is_app(t)) {
continue;
}
interval b;
expr* e;
if (is_bound(t, e, b)) {
if (mark2.is_marked(e)) {
todo.reset();
return true;
}
mark2.mark(e);
if (m_bound.contains(e)) {
todo.reset();
return true;
}
}
app* a = to_app(t);
todo.append(a->get_num_args(), a->get_args());
}
return false;
}
void pop_core(unsigned num_scopes) {
TRACE("bv", tout << "pop: " << num_scopes << "\n";);
if (m_scopes.empty())
return;
unsigned target = m_scopes.size() - num_scopes;
if (target == 0) {
m_bound.reset();
m_scopes.reset();
return;
}
for (unsigned i = m_scopes.size(); i-- > target; ) {
undo_bound& undo = m_scopes[i];
SASSERT(m_bound.contains(undo.e));
if (undo.fresh)
m_bound.erase(undo.e);
else
m_bound.insert(undo.e, undo.b);
}
m_scopes.shrink(target);
}
};
}

File diff suppressed because it is too large Load diff

View file

@ -25,10 +25,11 @@ Notes:
class bv_rewriter_core {
protected:
ast_manager& m;
typedef rational numeral;
bv_util m_util;
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
expr_ref m_bit1;
bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
bool is_numeral(expr * n, numeral & r) const { unsigned sz; return m_util.is_numeral(n, r, sz); }
@ -44,7 +45,7 @@ protected:
decl_kind power_decl_kind() const { UNREACHABLE(); return static_cast<decl_kind>(UINT_MAX); }
public:
bv_rewriter_core(ast_manager & m):m_util(m) {}
bv_rewriter_core(ast_manager & m):m(m), m_util(m), m_bit1(m) {}
};
class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
@ -100,6 +101,7 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
br_status mk_bv_mul(expr* a, expr* b, expr_ref& result) { expr* args[2] = { a, b }; return mk_bv_mul(2, args, result); }
br_status mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_mul_hoist(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result);
@ -171,11 +173,15 @@ public:
bool is_bv(expr * t) const { return m_util.is_bv(t); }
expr * mk_numeral(numeral const & v, unsigned sz) { return m_util.mk_numeral(v, sz); }
expr * mk_numeral(unsigned v, unsigned sz) { return m_util.mk_numeral(numeral(v), sz); }
app * mk_zero(sort* s) { return m_util.mk_zero(s); }
app * mk_one(sort* s) { return m_util.mk_one(s); }
app * mk_zero(unsigned sz) { return m_util.mk_zero(sz); }
app * mk_one(unsigned sz) { return m_util.mk_one(sz); }
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
result = m().mk_app(f, num_args, args);
result = m.mk_app(f, num_args, args);
}
bool is_urem_any(expr * e, expr * & dividend, expr * & divisor);
@ -187,16 +193,26 @@ public:
bv_util & get_util() { return m_util; }
// Return true if t is of the form
// (= t #b0)
// (= t #b1)
// (= #b0 t)
// (= #b1 t)
bool is_eq_bit(expr* t, expr*& x, unsigned& val);
// return true if t is #b0 or #b1
bool is_bit(expr* t, unsigned& val);
#define MK_BV_BINARY(OP) \
expr_ref OP(expr* a, expr* b) { \
expr_ref result(m()); \
expr_ref result(m); \
if (BR_FAILED == OP(a, b, result)) \
result = m_util.OP(a, b); \
return result; \
} \
expr_ref mk_zero_extend(unsigned n, expr * arg) {
expr_ref result(m());
expr_ref result(m);
if (BR_FAILED == mk_zero_extend(n, arg, result))
result = m_util.mk_zero_extend(n, arg);
return result;
@ -210,7 +226,7 @@ public:
expr_ref mk_bv2int(expr* a) {
expr_ref result(m());
expr_ref result(m);
if (BR_FAILED == mk_bv2int(a, result))
result = m_util.mk_bv2int(a);
return result;

View file

@ -81,14 +81,54 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) {
}
// VAR
if (is_var(e, num_decls)) {
if (is_var(e, num_decls))
return set_result(to_var(e), m.mk_false());
}
// (not VAR)
if (is_neg_var(m, e, v, num_decls)) {
if (is_neg_var(m, e, v, num_decls))
return set_result(v, m.mk_true());
return false;
}
bool der::is_var_eq(expr* e, unsigned num_decls, var*& v, expr_ref& t) {
expr* lhs, * rhs;
auto set_result = [&](var* w, expr* s) {
v = w;
t = s;
TRACE("der", tout << mk_pp(e, m) << "\n";);
return true;
};
// (= VAR t)
if (m.is_eq(e, lhs, rhs)) {
if (!is_var(lhs, num_decls))
std::swap(lhs, rhs);
if (!is_var(lhs, num_decls))
return false;
return set_result(to_var(lhs), rhs);
}
if (m.is_eq(e, lhs, rhs) && m.is_bool(lhs)) {
// (iff VAR t) case
if (!is_var(lhs, num_decls))
std::swap(lhs, rhs);
if (is_var(lhs, num_decls)) {
m_new_exprs.push_back(rhs);
return set_result(to_var(lhs), rhs);
}
return false;
}
// VAR
if (is_var(e, num_decls))
return set_result(to_var(e), m.mk_true());
// (not VAR)
if (is_neg_var(m, e, v, num_decls))
return set_result(v, m.mk_false());
return false;
}
@ -99,6 +139,7 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) {
TRACE("der", tout << mk_pp(q, m) << "\n";);
auto k = q->get_kind();
// Keep applying it until r doesn't change anymore
do {
proof_ref curr_pr(m);
@ -106,14 +147,13 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) {
reduce1(q, r, curr_pr);
if (q != r)
reduced = true;
if (m.proofs_enabled()) {
pr = m.mk_transitivity(pr, curr_pr);
}
if (m.proofs_enabled())
pr = m.mk_transitivity(pr, curr_pr);
}
while (q != r && is_quantifier(r));
// Eliminate variables that have become unused
if (reduced && is_forall(r)) {
if (reduced && is_quantifier(r) && k == to_quantifier(r)->get_kind()) {
quantifier * q = to_quantifier(r);
r = elim_unused_vars(m, q, params_ref());
if (m.proofs_enabled()) {
@ -125,7 +165,7 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) {
}
void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
if (!is_forall(q)) {
if (!is_forall(q) && !is_exists(q)) {
pr = nullptr;
r = q;
return;
@ -136,14 +176,20 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
var * v = nullptr;
expr_ref t(m);
if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t))
if (is_forall(q) && is_var_diseq(e, num_decls, v, t) && !occurs(v, t))
r = m.mk_false();
else if (is_exists(q) && is_var_eq(e, num_decls, v, t) && !occurs(v, t))
r = m.mk_true();
else {
expr_ref_vector ors(m);
flatten_or(e, ors);
unsigned num_args = ors.size();
expr_ref_vector literals(m);
if (is_forall(q))
flatten_or(e, literals);
else
flatten_and(e, literals);
unsigned num_args = literals.size();
unsigned diseq_count = 0;
unsigned largest_vinx = 0;
bool is_eq = false;
m_map.reset();
m_pos2var.reset();
@ -153,7 +199,8 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
// Find all disequalities
for (unsigned i = 0; i < num_args; i++) {
if (is_var_diseq(ors.get(i), num_decls, v, t)) {
is_eq = is_forall(q) ? is_var_diseq(literals.get(i), num_decls, v, t) : is_var_eq(literals.get(i), num_decls, v, t);
if (is_eq) {
unsigned idx = v->get_idx();
if (m_map.get(idx, nullptr) == nullptr) {
m_map.reserve(idx + 1);
@ -174,7 +221,7 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
if (!m_order.empty()) {
create_substitution(largest_vinx + 1);
apply_substitution(q, ors, r);
apply_substitution(q, literals, is_forall(q), r);
}
}
else {
@ -185,9 +232,9 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
// Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses.
// So, we must perform a occurs check here.
if (m.proofs_enabled()) {
if (m.proofs_enabled())
pr = r == q ? nullptr : m.mk_der(q, r);
}
}
static void der_sort_vars(ptr_vector<var> & vars, expr_ref_vector & definitions, unsigned_vector & order) {
@ -326,20 +373,20 @@ void der::create_substitution(unsigned sz) {
}
}
void der::apply_substitution(quantifier * q, expr_ref_vector& ors, expr_ref & r) {
unsigned num_args = ors.size();
void der::apply_substitution(quantifier * q, expr_ref_vector& literals, bool is_or, expr_ref & r) {
unsigned num_args = literals.size();
// get a new expression
m_new_args.reset();
for(unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; i++) {
int x = m_pos2var[i];
if (x != -1 && m_map.get(x) != nullptr)
continue; // this is a disequality with definition (vanishes)
m_new_args.push_back(ors.get(i));
m_new_args.push_back(literals.get(i));
}
expr_ref t(mk_or(m, m_new_args.size(), m_new_args.data()), m);
expr_ref t(is_or ? mk_or(m_new_args) : mk_and(m_new_args), m);
expr_ref new_e = m_subst(t, m_subst_map);
// don't forget to update the quantifier patterns

View file

@ -131,7 +131,7 @@ class der {
ptr_vector<var> m_inx2var;
unsigned_vector m_order;
expr_ref_vector m_subst_map;
expr_ref_buffer m_new_args;
expr_ref_vector m_new_args;
/**
\brief Return true if e can be viewed as a variable disequality.
@ -145,9 +145,11 @@ class der {
*/
bool is_var_diseq(expr * e, unsigned num_decls, var *& v, expr_ref & t);
bool is_var_eq(expr* e, unsigned num_decls, var*& v, expr_ref& t);
void get_elimination_order();
void create_substitution(unsigned sz);
void apply_substitution(quantifier * q, expr_ref_vector& ors, expr_ref & r);
void apply_substitution(quantifier * q, expr_ref_vector& lits, bool is_or, expr_ref & r);
void reduce1(quantifier * q, expr_ref & r, proof_ref & pr);

View file

@ -0,0 +1,325 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
dom_simplifier.cpp
Abstract:
Dominator-based context simplifer.
Author:
Nikolaj and Nuno
--*/
#include "ast/ast_util.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/rewriter/dom_simplifier.h"
/**
\brief compute a post-order traversal for e.
Also populate the set of parents
*/
void expr_dominators::compute_post_order() {
unsigned post_num = 0;
SASSERT(m_post2expr.empty());
SASSERT(m_expr2post.empty());
ast_mark mark;
ptr_vector<expr> todo;
todo.push_back(m_root);
while (!todo.empty()) {
expr* e = todo.back();
if (mark.is_marked(e)) {
todo.pop_back();
continue;
}
if (is_app(e)) {
app* a = to_app(e);
bool done = true;
for (expr* arg : *a) {
if (!mark.is_marked(arg)) {
todo.push_back(arg);
done = false;
}
}
if (done) {
mark.mark(e, true);
m_expr2post.insert(e, post_num++);
m_post2expr.push_back(e);
todo.pop_back();
for (expr* arg : *a) {
add_edge(m_parents, arg, a);
}
}
}
else {
mark.mark(e, true);
todo.pop_back();
}
}
}
expr* expr_dominators::intersect(expr* x, expr * y) {
unsigned n1 = m_expr2post[x];
unsigned n2 = m_expr2post[y];
while (n1 != n2) {
if (n1 < n2) {
x = m_doms[x];
n1 = m_expr2post[x];
}
else if (n1 > n2) {
y = m_doms[y];
n2 = m_expr2post[y];
}
}
SASSERT(x == y);
return x;
}
bool expr_dominators::compute_dominators() {
expr * e = m_root;
SASSERT(m_doms.empty());
m_doms.insert(e, e);
bool change = true;
unsigned iterations = 1;
while (change) {
change = false;
TRACE("simplify",
for (auto & kv : m_doms) {
tout << mk_bounded_pp(kv.m_key, m) << " |-> " << mk_bounded_pp(kv.m_value, m) << "\n";
});
SASSERT(m_post2expr.empty() || m_post2expr.back() == e);
for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) {
expr * child = m_post2expr[i];
ptr_vector<expr> const& p = m_parents[child];
expr * new_idom = nullptr, *idom2 = nullptr;
for (expr * pred : p) {
if (m_doms.contains(pred)) {
new_idom = !new_idom ? pred : intersect(new_idom, pred);
}
}
if (!new_idom) {
m_doms.insert(child, p[0]);
change = true;
}
else if (!m_doms.find(child, idom2) || idom2 != new_idom) {
m_doms.insert(child, new_idom);
change = true;
}
}
iterations *= 2;
if (change && iterations > m_post2expr.size()) {
return false;
}
}
return true;
}
void expr_dominators::extract_tree() {
for (auto const& kv : m_doms) {
add_edge(m_tree, kv.m_value, kv.m_key);
}
}
bool expr_dominators::compile(expr * e) {
reset();
m_root = e;
compute_post_order();
if (!compute_dominators()) return false;
extract_tree();
TRACE("simplify", display(tout););
return true;
}
bool expr_dominators::compile(unsigned sz, expr * const* es) {
expr_ref e(m.mk_and(sz, es), m);
return compile(e);
}
void expr_dominators::reset() {
m_expr2post.reset();
m_post2expr.reset();
m_parents.reset();
m_doms.reset();
m_tree.reset();
m_root.reset();
}
std::ostream& expr_dominators::display(std::ostream& out) {
return display(out, 0, m_root);
}
std::ostream& expr_dominators::display(std::ostream& out, unsigned indent, expr* r) {
for (unsigned i = 0; i < indent; ++i) out << " ";
out << r->get_id() << ": " << mk_bounded_pp(r, m, 1) << "\n";
if (m_tree.contains(r)) {
for (expr* child : m_tree[r]) {
if (child != r)
display(out, indent + 1, child);
}
}
return out;
}
// ---------------------
// expr_substitution_simplifier
namespace {
class expr_substitution_simplifier : public dom_simplifier {
ast_manager& m;
expr_substitution m_subst;
scoped_expr_substitution m_scoped_substitution;
obj_map<expr, unsigned> m_expr2depth;
expr_ref_vector m_trail;
// move from asserted_formulas to here..
void compute_depth(expr* e) {
ptr_vector<expr> todo;
todo.push_back(e);
while (!todo.empty()) {
e = todo.back();
unsigned d = 0;
if (m_expr2depth.contains(e)) {
todo.pop_back();
continue;
}
if (is_app(e)) {
app* a = to_app(e);
bool visited = true;
for (expr* arg : *a) {
unsigned d1 = 0;
if (m_expr2depth.find(arg, d1)) {
d = std::max(d, d1);
}
else {
visited = false;
todo.push_back(arg);
}
}
if (!visited) {
continue;
}
}
todo.pop_back();
m_expr2depth.insert(e, d + 1);
}
}
bool is_gt(expr* lhs, expr* rhs) {
if (lhs == rhs) {
return false;
}
if (m.is_value(rhs)) {
return true;
}
SASSERT(is_ground(lhs) && is_ground(rhs));
if (depth(lhs) > depth(rhs)) {
return true;
}
if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) {
app* l = to_app(lhs);
app* r = to_app(rhs);
if (l->get_decl()->get_id() != r->get_decl()->get_id()) {
return l->get_decl()->get_id() > r->get_decl()->get_id();
}
if (l->get_num_args() != r->get_num_args()) {
return l->get_num_args() > r->get_num_args();
}
for (unsigned i = 0; i < l->get_num_args(); ++i) {
if (l->get_arg(i) != r->get_arg(i)) {
return is_gt(l->get_arg(i), r->get_arg(i));
}
}
UNREACHABLE();
}
return false;
}
unsigned depth(expr* e) { return m_expr2depth[e]; }
public:
expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {}
void updt_params(params_ref const & p) override {}
void collect_param_descrs(param_descrs& r) override {}
bool assert_expr(expr * t, bool sign) override {
expr* tt;
if (m.is_not(t, tt))
return assert_expr(tt, !sign);
if (m.is_false(t))
return sign;
if (m.is_true(t))
return !sign;
TRACE("simplify", tout << t->get_id() << ": " << mk_bounded_pp(t, m) << " " << (sign?" - neg":" - pos") << "\n";);
m_scoped_substitution.push();
if (!sign) {
update_substitution(t, nullptr);
}
else {
expr_ref nt(m.mk_not(t), m);
update_substitution(nt, nullptr);
}
return true;
}
void update_substitution(expr* n, proof* pr) {
expr* lhs, *rhs, *n1;
if (is_ground(n) && m.is_eq(n, lhs, rhs)) {
compute_depth(lhs);
compute_depth(rhs);
m_trail.push_back(lhs);
m_trail.push_back(rhs);
if (is_gt(lhs, rhs)) {
TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";);
m_scoped_substitution.insert(lhs, rhs, pr);
return;
}
if (is_gt(rhs, lhs)) {
TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";);
m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr));
return;
}
TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";);
}
if (m.is_not(n, n1)) {
m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr));
}
else {
m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr));
}
}
void operator()(expr_ref& r) override { r = m_scoped_substitution.find(r); }
void pop(unsigned num_scopes) override { m_scoped_substitution.pop(num_scopes); }
unsigned scope_level() const override { return m_scoped_substitution.scope_level(); }
dom_simplifier * translate(ast_manager & m) override {
SASSERT(m_subst.empty());
return alloc(expr_substitution_simplifier, m);
}
};
}
dom_simplifier* mk_expr_substitution_simplifier(ast_manager& m) {
return alloc(expr_substitution_simplifier, m);
}

View file

@ -0,0 +1,86 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
dom_simplifier.h
Abstract:
Dominator-based context simplifer.
Author:
Nikolaj and Nuno
--*/
#pragma once
#include "ast/ast.h"
#include "ast/expr_substitution.h"
#include "util/obj_pair_hashtable.h"
class expr_dominators {
public:
typedef obj_map<expr, ptr_vector<expr>> tree_t;
private:
ast_manager& m;
expr_ref m_root;
obj_map<expr, unsigned> m_expr2post; // reverse post-order number
ptr_vector<expr> m_post2expr;
tree_t m_parents;
obj_map<expr, expr*> m_doms;
tree_t m_tree;
void add_edge(tree_t& tree, expr * src, expr* dst) {
tree.insert_if_not_there(src, ptr_vector<expr>()).push_back(dst);
}
void compute_post_order();
expr* intersect(expr* x, expr * y);
bool compute_dominators();
void extract_tree();
std::ostream& display(std::ostream& out, unsigned indent, expr* r);
public:
expr_dominators(ast_manager& m): m(m), m_root(m) {}
bool compile(expr * e);
bool compile(unsigned sz, expr * const* es);
tree_t const& get_tree() { return m_tree; }
void reset();
expr* idom(expr *e) const { return m_doms[e]; }
std::ostream& display(std::ostream& out);
};
class dom_simplifier {
public:
virtual ~dom_simplifier() = default;
/**
\brief assert_expr performs an implicit push
*/
virtual bool assert_expr(expr * t, bool sign) = 0;
/**
\brief apply simplification.
*/
virtual void operator()(expr_ref& r) = 0;
/**
\brief pop scopes accumulated from assertions.
*/
virtual void pop(unsigned num_scopes) = 0;
virtual dom_simplifier * translate(ast_manager & m) = 0;
virtual unsigned scope_level() const = 0;
virtual void updt_params(params_ref const & p) = 0;
virtual void collect_param_descrs(param_descrs& r) = 0;
};
dom_simplifier* mk_expr_substitution_simplifier(ast_manager& m);

View file

@ -25,6 +25,11 @@ void expr_replacer::operator()(expr * t, expr_ref & result, proof_ref & result_p
operator()(t, result, result_pr, result_dep);
}
void expr_replacer::operator()(expr* t, expr_ref& result, expr_dependency_ref& result_dep) {
proof_ref result_pr(m());
operator()(t, result, result_pr, result_dep);
}
void expr_replacer::operator()(expr * t, expr_ref & result) {
proof_ref pr(m());
operator()(t, result, pr);

View file

@ -34,9 +34,12 @@ public:
virtual void set_substitution(expr_substitution * s) = 0;
virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & deps) = 0;
virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
virtual void operator()(expr * t, expr_ref & result);
virtual void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); }
void operator()(expr* t, expr_ref& result, expr_dependency_ref& deps);
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
void operator()(expr * t, expr_ref & result);
void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); }
void operator()(expr_ref_vector& v) { expr_ref t(m()); for (unsigned i = 0; i < v.size(); ++i) (*this)(v.get(i), t), v[i] = t; }
std::pair<expr_ref, expr_dependency_ref> replace_with_dep(expr* t) { expr_ref r(m()); expr_dependency_ref d(m()); (*this)(t, r, d); return { r, d }; }
virtual unsigned get_num_steps() const { return 0; }
virtual void reset() = 0;

View file

@ -13,8 +13,6 @@ Author:
Nikolaj Bjorner (nbjorner) 2019-2-4
Notes:
--*/
@ -25,19 +23,17 @@ Notes:
hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p):
m_manager(m), m_args1(m), m_args2(m), m_subst(m) {
m(m), m_args1(m), m_args2(m), m_subst(m) {
updt_params(p);
}
br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) {
if (num_args < 2) {
if (num_args < 2)
return BR_FAILED;
}
for (unsigned i = 0; i < num_args; ++i) {
if (!is_and(es[i], nullptr)) {
for (unsigned i = 0; i < num_args; ++i)
if (!is_and(es[i], nullptr))
return BR_FAILED;
}
}
bool turn = false;
m_preds1.reset();
@ -52,12 +48,10 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref &
VERIFY(is_and(es[0], args[turn]));
expr* e1, *e2;
for (expr* e : *(args[turn])) {
if (m().is_eq(e, e1, e2)) {
if (m.is_eq(e, e1, e2))
(*uf)[turn].merge(mk_var(e1), mk_var(e2));
}
else {
else
(*preds)[turn].insert(e);
}
}
unsigned round = 0;
for (unsigned j = 1; j < num_args; ++j) {
@ -72,44 +66,39 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref &
VERIFY(is_and(es[j], args[turn]));
for (expr* e : *args[turn]) {
if (m().is_eq(e, e1, e2)) {
if (m.is_eq(e, e1, e2)) {
m_es.push_back(e1);
m_uf0.merge(mk_var(e1), mk_var(e2));
}
else if ((*preds)[last].contains(e)) {
else if ((*preds)[last].contains(e))
(*preds)[turn].insert(e);
}
}
if ((*preds)[turn].empty() && m_es.empty()) {
if ((*preds)[turn].empty() && m_es.empty())
return BR_FAILED;
}
m_eqs.reset();
for (expr* e : m_es) {
if (m_mark.is_marked(e)) {
if (m_mark.is_marked(e))
continue;
}
unsigned u = mk_var(e);
unsigned v = u;
m_roots.reset();
do {
m_mark.mark(e);
unsigned r = (*uf)[last].find(v);
if (m_roots.find(r, e2)) {
m_eqs.push_back(std::make_pair(e, e2));
}
else {
if (m_roots.find(r, e2))
m_eqs.push_back({e, e2});
else
m_roots.insert(r, e);
}
v = m_uf0.next(v);
e = mk_expr(v);
}
while (u != v);
}
reset((*uf)[turn]);
for (auto const& p : m_eqs)
(*uf)[turn].merge(mk_var(p.first), mk_var(p.second));
for (auto const& [e1, e2] : m_eqs)
(*uf)[turn].merge(mk_var(e1), mk_var(e2));
if ((*preds)[turn].empty() && m_eqs.empty())
return BR_FAILED;
}
@ -118,25 +107,23 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref &
return BR_DONE;
}
// p & eqs & (or fmls)
expr_ref_vector fmls(m());
expr_ref_vector fmls(m);
m_subst.reset();
for (expr * p : (*preds)[turn]) {
expr* q = nullptr;
if (m().is_not(p, q)) {
m_subst.insert(q, m().mk_false());
}
else {
m_subst.insert(p, m().mk_true());
}
if (m.is_not(p, q))
m_subst.insert(q, m.mk_false());
else
m_subst.insert(p, m.mk_true());
fmls.push_back(p);
}
for (auto& p : m_eqs) {
if (m().is_value(p.first))
if (m.is_value(p.first))
std::swap(p.first, p.second);
m_subst.insert(p.first, p.second);
fmls.push_back(m().mk_eq(p.first, p.second));
fmls.push_back(m.mk_eq(p.first, p.second));
}
expr_ref ors(::mk_or(m(), num_args, es), m());
expr_ref ors(::mk_or(m, num_args, es), m);
m_subst(ors);
fmls.push_back(ors);
result = mk_and(fmls);
@ -146,9 +133,8 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref &
unsigned hoist_rewriter::mk_var(expr* e) {
unsigned v = 0;
if (m_expr2var.find(e, v)) {
if (m_expr2var.find(e, v))
return v;
}
m_uf1.mk_var();
v = m_uf2.mk_var();
SASSERT(v == m_var2expr.size());
@ -158,15 +144,14 @@ unsigned hoist_rewriter::mk_var(expr* e) {
}
expr_ref hoist_rewriter::hoist_predicates(obj_hashtable<expr> const& preds, unsigned num_args, expr* const* es) {
expr_ref result(m());
expr_ref_vector args(m()), fmls(m());
expr_ref result(m);
expr_ref_vector args(m), fmls(m);
for (unsigned i = 0; i < num_args; ++i) {
VERIFY(is_and(es[i], &m_args1));
fmls.reset();
for (expr* e : m_args1) {
for (expr* e : m_args1)
if (!preds.contains(e))
fmls.push_back(e);
}
args.push_back(::mk_and(fmls));
}
fmls.reset();
@ -188,19 +173,18 @@ br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c
}
bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) {
if (m().is_and(e)) {
if (m.is_and(e)) {
if (args) {
args->reset();
args->append(to_app(e)->get_num_args(), to_app(e)->get_args());
}
return true;
}
if (m().is_not(e, e) && m().is_or(e)) {
if (m.is_not(e, e) && m.is_or(e)) {
if (args) {
args->reset();
for (expr* arg : *to_app(e)) {
args->push_back(::mk_not(m(), arg));
}
for (expr* arg : *to_app(e))
args->push_back(::mk_not(m, arg));
}
return true;
}

View file

@ -26,7 +26,7 @@ Notes:
#include "util/obj_hashtable.h"
class hoist_rewriter {
ast_manager & m_manager;
ast_manager & m;
expr_ref_vector m_args1, m_args2;
obj_hashtable<expr> m_preds1, m_preds2;
basic_union_find m_uf1, m_uf2, m_uf0;
@ -34,11 +34,9 @@ class hoist_rewriter {
svector<std::pair<expr*,expr*>> m_eqs;
u_map<expr*> m_roots;
expr_safe_replace m_subst;
obj_map<expr, unsigned> m_expr2var;
ptr_vector<expr> m_var2expr;
expr_mark m_mark;
br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result);
obj_map<expr, unsigned> m_expr2var;
ptr_vector<expr> m_var2expr;
expr_mark m_mark;
bool is_and(expr* e, expr_ref_vector* args);
@ -52,12 +50,12 @@ class hoist_rewriter {
public:
hoist_rewriter(ast_manager & m, params_ref const & p = params_ref());
ast_manager& m() const { return m_manager; }
family_id get_fid() const { return m().get_basic_family_id(); }
bool is_eq(expr * t) const { return m().is_eq(t); }
family_id get_fid() const { return m.get_basic_family_id(); }
bool is_eq(expr * t) const { return m.is_eq(t); }
void updt_params(params_ref const & p) {}
static void get_param_descrs(param_descrs & r) {}
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result);
};
struct hoist_rewriter_cfg : public default_rewriter_cfg {

View file

@ -0,0 +1,160 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
macro_replacer.cpp
Abstract:
Abstract (functor) for applying macro replacement.
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
Notes:
--*/
#include "ast/rewriter/macro_replacer.h"
#include "ast/rewriter/rewriter_def.h"
#include "ast/rewriter/var_subst.h"
/**
* Rewriting formulas using macro definitions.
*/
struct macro_replacer::macro_replacer_cfg : public default_rewriter_cfg {
ast_manager& m;
macro_replacer& ep;
expr_dependency_ref& m_used_macro_dependencies;
expr_ref_vector m_trail;
macro_replacer_cfg(ast_manager& m, macro_replacer& ep, expr_dependency_ref& deps) :
m(m),
ep(ep),
m_used_macro_dependencies(deps),
m_trail(m)
{}
bool rewrite_patterns() const { return false; }
bool flat_assoc(func_decl* f) const { return false; }
br_status reduce_app(func_decl* f, unsigned num, expr* const* args, expr_ref& result, proof_ref& result_pr) {
result_pr = nullptr;
return BR_FAILED;
}
/**
* adapted from macro_manager.cpp
* Perhaps hoist and combine?
*/
bool reduce_quantifier(quantifier* old_q,
expr* new_body,
expr* const* new_patterns,
expr* const* new_no_patterns,
expr_ref& result,
proof_ref& result_pr) {
bool erase_patterns = false;
for (unsigned i = 0; !erase_patterns && i < old_q->get_num_patterns(); i++)
if (old_q->get_pattern(i) != new_patterns[i])
erase_patterns = true;
for (unsigned i = 0; !erase_patterns && i < old_q->get_num_no_patterns(); i++)
if (old_q->get_no_pattern(i) != new_no_patterns[i])
erase_patterns = true;
if (erase_patterns)
result = m.update_quantifier(old_q, 0, nullptr, 0, nullptr, new_body);
if (erase_patterns && m.proofs_enabled())
result_pr = m.mk_rewrite(old_q, result);
return erase_patterns;
}
bool get_subst(expr* _n, expr*& r, proof*& p) {
if (!is_app(_n))
return false;
p = nullptr;
app* n = to_app(_n);
func_decl* d = n->get_decl();
app_ref head(m);
expr_ref def(m);
expr_dependency_ref dep(m);
if (ep.has_macro(d, head, def, dep)) {
unsigned num = head->get_num_args();
ptr_buffer<expr> subst_args;
subst_args.resize(num, 0);
for (unsigned i = 0; i < num; i++) {
var* v = to_var(head->get_arg(i));
VERIFY(v->get_idx() < num);
unsigned nidx = num - v->get_idx() - 1;
SASSERT(!subst_args[nidx]);
subst_args[nidx] = n->get_arg(i);
}
var_subst s(m);
expr_ref rr = s(def, num, subst_args.data());
r = rr;
m_trail.push_back(rr);
m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, dep);
// skip proof terms for simplifiers
return true;
}
return false;
}
};
struct macro_replacer::macro_replacer_rw : public rewriter_tpl<macro_replacer::macro_replacer_cfg> {
macro_replacer::macro_replacer_cfg m_cfg;
macro_replacer_rw(ast_manager& m, macro_replacer& ep, expr_dependency_ref& deps) :
rewriter_tpl<macro_replacer::macro_replacer_cfg>(m, false, m_cfg),
m_cfg(m, ep, deps)
{}
};
void macro_replacer::insert(app* head, expr* def, expr_dependency* dep) {
func_decl* f = head->get_decl();
m_trail.push_back(head);
m_trail.push_back(def);
m_deps.push_back(dep);
m_map.insert(f, std::tuple(head, def, dep));
}
void macro_replacer::operator()(expr* t, expr_dependency* dep_in, expr_ref& result, expr_dependency_ref& dep_out) {
expr_dependency_ref _dep_in(dep_in, m);
macro_replacer_rw exp(m, *this, dep_out);
exp(t, result);
if (!dep_in)
return;
// update dependencies if needed
m_dep_exprs.reset();
m.linearize(dep_in, m_dep_exprs);
unsigned sz = m_trail.size();
for (expr*& d : m_dep_exprs) {
exp(d, result);
if (result != d) {
d = result.get();
m_trail.push_back(result);
}
}
if (sz != m_trail.size()) {
dep_in = m.mk_join(m_dep_exprs.size(), m_dep_exprs.data());
m_trail.shrink(sz);
}
dep_out = m.mk_join(dep_in, dep_out);
}
bool macro_replacer::has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& dep) {
std::tuple<app*,expr*,expr_dependency*> v;
if (!m_map.find(f, v))
return false;
auto const& [h, d, dp] = v;
head = h;
def = d;
dep = dp;
return true;
}

View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
macro_replacer.h
Abstract:
Abstract (functor) for applying macro replacement.
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
Notes:
--*/
#pragma once
#include "ast/ast.h"
#include "util/obj_hashtable.h"
class macro_replacer {
ast_manager& m;
ast_ref_vector m_trail;
expr_dependency_ref_vector m_deps;
ptr_vector<expr> m_dep_exprs;
obj_map<func_decl, std::tuple<app*, expr*, expr_dependency*>> m_map;
struct macro_replacer_cfg;
struct macro_replacer_rw;
public:
macro_replacer(ast_manager& m): m(m), m_trail(m), m_deps(m) {}
void insert(app* head, expr* def, expr_dependency* dep);
void operator()(expr* t, expr_dependency* d, expr_ref& result, expr_dependency_ref& dep);
void operator()(expr* t, expr_ref & result) { expr_dependency_ref dep(m); (*this)(t, nullptr, result, dep); }
void operator()(expr_ref & t) { expr_ref s(t, m); (*this)(s, t); }
bool has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& d);
};

View file

@ -32,8 +32,15 @@ mk_extract_proc::~mk_extract_proc() {
}
app * mk_extract_proc::operator()(unsigned high, unsigned low, expr * arg) {
unsigned l, h;
while (m_util.is_extract(arg, l, h, arg)) {
low += l;
high += l;
}
ast_manager & m = m_util.get_manager();
sort * s = arg->get_sort();
if (low == 0 && high + 1 == m_util.get_bv_size(arg) && is_app(arg))
return to_app(arg);
if (m_low == low && m_high == high && m_domain == s)
return m.mk_app(m_f_cached, arg);
// m_f_cached has a reference to m_domain, so, I don't need to inc_ref m_domain

View file

@ -200,7 +200,7 @@ struct pb2bv_rewriter::imp {
}
if (m_pb_solver == "segmented") {
throw default_exception("segmented encoding is disabled, use a different value for pb.solver");
throw default_exception("segmented encoding is disabled, use a different value for pb.solver");
switch (is_le) {
case l_true: return mk_seg_le(k);
case l_false: return mk_seg_ge(k);
@ -1077,9 +1077,9 @@ struct pb2bv_rewriter::imp {
}
void collect_param_descrs(param_descrs& r) const {
r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: false) retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver");
r.insert("pb.solver", CPK_SYMBOL, "(default: solver) retain pb constraints (don't bit-blast them) and use built-in pb solver");
r.insert("cardinality.encoding", CPK_SYMBOL, "(default: none) grouped, bimander, ordered, unate, circuit");
r.insert("keep_cardinality_constraints", CPK_BOOL, "retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver", "false");
r.insert("pb.solver", CPK_SYMBOL, "encoding used for Pseudo-Boolean constraints: totalizer, sorting, binary_merge, bv, solver. PB constraints are retained if set to 'solver'", "solver");
r.insert("cardinality.encoding", CPK_SYMBOL, "encoding used for cardinality constraints: grouped, bimander, ordered, unate, circuit", "none");
}
unsigned get_num_steps() const { return m_rw.get_num_steps(); }

View file

@ -36,6 +36,7 @@ protected:
bool m_hoist_mul;
bool m_ast_order;
bool m_hoist_ite;
ast_manager& M() { return Config::m; }
bool is_numeral(expr * n) const { return Config::is_numeral(n); }
bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); }
@ -106,7 +107,6 @@ public:
SASSERT(!m_som || !m_hoist_mul); // som is mutually exclusive with hoisting multiplication.
}
ast_manager & m() const { return Config::m(); }
family_id get_fid() const { return Config::get_fid(); }
void updt_params(params_ref const & p);

View file

@ -51,7 +51,7 @@ expr * poly_rewriter<Config>::mk_add_app(unsigned num_args, expr * const * args)
switch (num_args) {
case 0: return mk_numeral(numeral(0));
case 1: return args[0];
default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args);
default: return M().mk_app(get_fid(), add_decl_kind(), num_args, args);
}
}
@ -119,7 +119,7 @@ expr * poly_rewriter<Config>::mk_mul_app(unsigned num_args, expr * const * args)
if (new_args.size() > 2 && is_numeral(new_args.get(0), a)) {
return mk_mul_app(a, mk_mul_app(new_args.size() - 1, new_args.data() + 1));
}
return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.data());
return M().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.data());
}
}
else {
@ -127,7 +127,7 @@ expr * poly_rewriter<Config>::mk_mul_app(unsigned num_args, expr * const * args)
if (num_args > 2 && is_numeral(args[0], a)) {
return mk_mul_app(a, mk_mul_app(num_args - 1, args + 1));
}
return m().mk_app(get_fid(), mul_decl_kind(), num_args, args);
return M().mk_app(get_fid(), mul_decl_kind(), num_args, args);
}
}
}
@ -189,9 +189,9 @@ br_status poly_rewriter<Config>::mk_flat_mul_core(unsigned num_args, expr * cons
br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.data(), result);
TRACE("poly_rewriter",
tout << "flat mul:\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], M()) << "\n";
tout << "---->\n";
for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";
for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], M()) << "\n";
tout << st << "\n";
);
if (st == BR_FAILED) {
@ -292,7 +292,7 @@ br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * con
new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i)));
}
result = mk_add_app(new_add_args.size(), new_add_args.data());
TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";);
TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, M(), 5) << "\n";);
return BR_REWRITE2;
}
}
@ -328,7 +328,7 @@ br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * con
for (unsigned i = 0; i < new_args.size(); i++) {
if (i > 0)
tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< ");
tout << mk_ismt2_pp(new_args[i], m());
tout << mk_ismt2_pp(new_args[i], M());
}
tout << "\nordered: " << ordered << "\n";);
if (ordered && num_coeffs == 0 && !use_power())
@ -340,7 +340,7 @@ br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * con
for (unsigned i = 0; i < new_args.size(); i++) {
if (i > 0)
tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< ");
tout << mk_ismt2_pp(new_args[i], m());
tout << mk_ismt2_pp(new_args[i], M());
}
tout << "\n";);
}
@ -349,8 +349,8 @@ br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * con
result = mk_mul_app(c, result);
TRACE("poly_rewriter",
for (unsigned i = 0; i < num_args; ++i)
tout << mk_ismt2_pp(args[i], m()) << " ";
tout << "\nmk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";);
tout << mk_ismt2_pp(args[i], M()) << " ";
tout << "\nmk_nflat_mul_core result:\n" << mk_ismt2_pp(result, M()) << "\n";);
return BR_DONE;
}
@ -373,9 +373,9 @@ br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * con
}
}
unsigned orig_size = sums.size();
expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception
expr_ref_buffer sum(M()); // must be ref_buffer because we may throw an exception
ptr_buffer<expr> m_args;
TRACE("som", tout << "starting som...\n";);
TRACE("som", tout << "starting soM()...\n";);
do {
TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " ";
tout << "\n";);
@ -566,7 +566,7 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
SASSERT(m_sort_sums || ordered);
TRACE("rewriter",
tout << "ordered: " << ordered << " sort sums: " << m_sort_sums << "\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";);
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], M()) << "\n";);
if (has_multiple) {
// expensive case
@ -589,7 +589,7 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
coeffs.push_back(a);
}
}
expr_ref_buffer new_args(m());
expr_ref_buffer new_args(M());
if (!c.is_zero()) {
new_args.push_back(mk_numeral(c));
}
@ -639,7 +639,7 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero())
return BR_FAILED;
}
expr_ref_buffer new_args(m());
expr_ref_buffer new_args(M());
if (!c.is_zero())
new_args.push_back(mk_numeral(c));
for (unsigned i = 0; i < num_args; i++) {
@ -690,8 +690,8 @@ br_status poly_rewriter<Config>::mk_sub(unsigned num_args, expr * const * args,
return BR_DONE;
}
set_curr_sort(args[0]->get_sort());
expr_ref minus_one(mk_numeral(numeral(-1)), m());
expr_ref_buffer new_args(m());
expr_ref minus_one(mk_numeral(numeral(-1)), M());
expr_ref_buffer new_args(M());
new_args.push_back(args[0]);
for (unsigned i = 1; i < num_args; i++) {
if (is_zero(args[i])) continue;
@ -984,11 +984,11 @@ bool poly_rewriter<Config>::hoist_ite(expr_ref& e) {
return false;
obj_hashtable<expr> shared;
ptr_buffer<expr> adds;
expr_ref_vector bs(m()), pinned(m());
expr_ref_vector bs(M()), pinned(M());
TO_BUFFER(is_add, adds, e);
unsigned i = 0;
for (expr* a : adds) {
if (m().is_ite(a)) {
if (M().is_ite(a)) {
shared.reset();
numeral g(0);
if (hoist_ite(a, shared, g) && (is_nontrivial_gcd(g) || !shared.empty())) {
@ -1026,7 +1026,7 @@ bool poly_rewriter<Config>::hoist_ite(expr_ref& e) {
template<typename Config>
bool poly_rewriter<Config>::hoist_ite(expr* a, obj_hashtable<expr>& shared, numeral& g) {
expr* c = nullptr, *t = nullptr, *e = nullptr;
if (m().is_ite(a, c, t, e)) {
if (M().is_ite(a, c, t, e)) {
return hoist_ite(t, shared, g) && hoist_ite(e, shared, g);
}
rational k, g1;
@ -1064,8 +1064,8 @@ bool poly_rewriter<Config>::hoist_ite(expr* a, obj_hashtable<expr>& shared, nume
template<typename Config>
expr* poly_rewriter<Config>::apply_hoist(expr* a, numeral const& g, obj_hashtable<expr> const& shared) {
expr* c = nullptr, *t = nullptr, *e = nullptr;
if (m().is_ite(a, c, t, e)) {
return m().mk_ite(c, apply_hoist(t, g, shared), apply_hoist(e, g, shared));
if (M().is_ite(a, c, t, e)) {
return M().mk_ite(c, apply_hoist(t, g, shared), apply_hoist(e, g, shared));
}
rational k;
if (is_nontrivial_gcd(g) && is_int_numeral(a, k)) {

View file

@ -354,7 +354,7 @@ class seq_rewriter {
public:
seq_rewriter(ast_manager & m, params_ref const & p = params_ref()):
m_util(m), m_autil(m), m_br(m), m_re2aut(m), m_op_cache(m), m_es(m),
m_util(m), m_autil(m), m_br(m, p), m_re2aut(m), m_op_cache(m), m_es(m),
m_lhs(m), m_rhs(m), m_coalesce_chars(true) {
}
ast_manager & m() const { return m_util.get_manager(); }

View file

@ -125,36 +125,6 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
return num_steps > m_max_steps;
}
// Return true if t is of the form
// (= t #b0)
// (= t #b1)
// (= #b0 t)
// (= #b1 t)
bool is_eq_bit(expr * t, expr * & x, unsigned & val) {
if (!m().is_eq(t))
return false;
expr * lhs = to_app(t)->get_arg(0);
if (!m_bv_rw.is_bv(lhs))
return false;
if (m_bv_rw.get_bv_size(lhs) != 1)
return false;
expr * rhs = to_app(t)->get_arg(1);
rational v;
unsigned sz;
if (m_bv_rw.is_numeral(lhs, v, sz)) {
x = rhs;
val = v.get_unsigned();
SASSERT(val == 0 || val == 1);
return true;
}
if (m_bv_rw.is_numeral(rhs, v, sz)) {
x = lhs;
val = v.get_unsigned();
SASSERT(val == 0 || val == 1);
return true;
}
return false;
}
// (iff (= x bit1) A)
// --->
@ -162,11 +132,11 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
br_status apply_tamagotchi(expr * lhs, expr * rhs, expr_ref & result) {
expr * x;
unsigned val;
if (is_eq_bit(lhs, x, val)) {
if (m_bv_rw.is_eq_bit(lhs, x, val)) {
result = m().mk_eq(x, m().mk_ite(rhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1)));
return BR_REWRITE2;
}
if (is_eq_bit(rhs, x, val)) {
if (m_bv_rw.is_eq_bit(rhs, x, val)) {
result = m().mk_eq(x, m().mk_ite(lhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1)));
return BR_REWRITE2;
}
@ -183,22 +153,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
if (k == OP_EQ) {
// theory dispatch for =
SASSERT(num == 2);
family_id s_fid = args[0]->get_sort()->get_family_id();
if (s_fid == m_a_rw.get_fid())
st = m_a_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_bv_rw.get_fid())
st = m_bv_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_dt_rw.get_fid())
st = m_dt_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_f_rw.get_fid())
st = m_f_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_ar_rw.get_fid())
st = m_ar_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_seq_rw.get_fid())
st = m_seq_rw.mk_eq_core(args[0], args[1], result);
if (st != BR_FAILED)
return st;
st = apply_tamagotchi(args[0], args[1], result);
st = reduce_eq(args[0], args[1], result);
if (st != BR_FAILED)
return st;
}
@ -695,9 +650,38 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args) {
expr_ref result(m());
proof_ref pr(m());
if (BR_FAILED == reduce_app(f, num_args, args, result, pr)) {
if (BR_FAILED == reduce_app(f, num_args, args, result, pr))
result = m().mk_app(f, num_args, args);
}
return result;
}
br_status reduce_eq(expr* a, expr* b, expr_ref& result) {
family_id s_fid = a->get_sort()->get_family_id();
br_status st = BR_FAILED;
if (s_fid == m_a_rw.get_fid())
st = m_a_rw.mk_eq_core(a, b, result);
else if (s_fid == m_bv_rw.get_fid())
st = m_bv_rw.mk_eq_core(a, b, result);
else if (s_fid == m_dt_rw.get_fid())
st = m_dt_rw.mk_eq_core(a, b, result);
else if (s_fid == m_f_rw.get_fid())
st = m_f_rw.mk_eq_core(a, b, result);
else if (s_fid == m_ar_rw.get_fid())
st = m_ar_rw.mk_eq_core(a, b, result);
else if (s_fid == m_seq_rw.get_fid())
st = m_seq_rw.mk_eq_core(a, b, result);
if (st != BR_FAILED)
return st;
return apply_tamagotchi(a, b, result);
}
expr_ref mk_eq(expr* a, expr* b) {
expr_ref result(m());
br_status st = reduce_eq(a, b, result);
if (BR_FAILED == st)
st = m_b_rw.mk_eq_core(a, b, result);
if (BR_FAILED == st)
result = m().mk_eq(a, b);
return result;
}
@ -826,7 +810,6 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
result = elim_unused_vars(m(), q1, params_ref());
TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << "\n";);
result_pr = nullptr;
if (m().proofs_enabled()) {
@ -835,6 +818,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
p2 = m().mk_elim_unused_vars(q1, result);
result_pr = m().mk_transitivity(p1, p2);
}
TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;);
SASSERT(old_q->get_sort() == result->get_sort());
return true;
}
@ -848,7 +834,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
m_f_rw(m, p),
m_dl_rw(m),
m_pb_rw(m),
m_seq_rw(m),
m_seq_rw(m, p),
m_char_rw(m),
m_rec_rw(m),
m_a_util(m),
@ -895,6 +881,10 @@ struct th_rewriter::imp : public rewriter_tpl<th_rewriter_cfg> {
return m_cfg.mk_app(f, sz, args);
}
expr_ref mk_eq(expr* a, expr* b) {
return m_cfg.mk_eq(a, b);
}
void set_solver(expr_solver* solver) {
m_cfg.m_seq_rw.set_solver(solver);
}
@ -922,6 +912,14 @@ void th_rewriter::get_param_descrs(param_descrs & r) {
rewriter_params::collect_param_descrs(r);
}
void th_rewriter::set_flat_and_or(bool f) {
m_imp->cfg().m_b_rw.set_flat_and_or(f);
}
void th_rewriter::set_order_eq(bool f) {
m_imp->cfg().m_b_rw.set_order_eq(f);
}
th_rewriter::~th_rewriter() {
dealloc(m_imp);
}
@ -934,7 +932,6 @@ unsigned th_rewriter::get_num_steps() const {
return m_imp->get_num_steps();
}
void th_rewriter::cleanup() {
ast_manager & m = m_imp->m();
m_imp->~imp();
@ -984,6 +981,10 @@ expr_ref th_rewriter::mk_app(func_decl* f, unsigned num_args, expr* const* args)
return m_imp->mk_app(f, num_args, args);
}
expr_ref th_rewriter::mk_eq(expr* a, expr* b) {
return m_imp->mk_eq(a, b);
}
void th_rewriter::set_solver(expr_solver* solver) {
m_imp->set_solver(solver);
}

View file

@ -38,6 +38,10 @@ public:
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
void set_flat_and_or(bool f);
void set_order_eq(bool f);
unsigned get_cache_size() const;
unsigned get_num_steps() const;
@ -47,6 +51,9 @@ public:
expr_ref operator()(expr * n, unsigned num_bindings, expr * const * bindings);
expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args);
expr_ref mk_app(func_decl* f, ptr_vector<expr> const& args) { return mk_app(f, args.size(), args.data()); }
expr_ref mk_app(func_decl* f, expr_ref_vector const& args) { return mk_app(f, args.size(), args.data()); }
expr_ref mk_eq(expr* a, expr* b);
bool reduce_quantifier(quantifier * old_q,
expr * new_body,

View file

@ -32,11 +32,8 @@ public:
void reset_mark(ast * n) { n->reset_mark_so(); }
void mark(ast * n) { if (is_marked(n)) return; n->mark_so(true); m_to_unmark.push_back(n); }
void reset() {
ptr_buffer<ast>::iterator it = m_to_unmark.begin();
ptr_buffer<ast>::iterator end = m_to_unmark.end();
for (; it != end; ++it) {
reset_mark(*it);
}
for (auto* t : m_to_unmark)
reset_mark(t);
m_to_unmark.reset();
}
void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); }

View file

@ -0,0 +1,41 @@
z3_add_component(simplifiers
SOURCES
bit_blaster.cpp
bound_manager.cpp
bound_propagator.cpp
bound_simplifier.cpp
bv_bounds_simplifier.cpp
bv_slice.cpp
card2bv.cpp
demodulator_simplifier.cpp
dependent_expr_state.cpp
dominator_simplifier.cpp
distribute_forall.cpp
elim_unconstrained.cpp
eliminate_predicates.cpp
euf_completion.cpp
extract_eqs.cpp
linear_equation.cpp
max_bv_sharing.cpp
model_reconstruction_trail.cpp
propagate_values.cpp
reduce_args_simplifier.cpp
solve_context_eqs.cpp
solve_eqs.cpp
COMPONENT_DEPENDENCIES
bit_blaster
euf
interval
normal_forms
rewriter
substitution
TACTIC_HEADERS
bit_blaster.h
bit2int.h
elim_bounds.h
elim_term_ite.h
pull_nested_quantifiers.h
push_ite.h
refine_inj_axiom.h
rewriter_simplifier.h
)

View file

@ -0,0 +1,47 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bit2int.h
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/rewriter/bit2int.h"
class bit2int_simplifier : public dependent_expr_simplifier {
bit2int m_rewriter;
public:
bit2int_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
m_rewriter(m) {
}
char const* name() const override { return "bit2int"; }
void reduce() override {
expr_ref r(m);
proof_ref pr(m);
for (unsigned idx : indices()) {
auto const& d = m_fmls[idx];
m_rewriter(d.fml(), r, pr);
m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep()));
}
}
bool supports_proofs() const override { return true; }
};
/*
ADD_SIMPLIFIER("bit2int", "simplify bit2int expressions.", "alloc(bit2int_simplifier, m, p, s)")
*/

View file

@ -0,0 +1,76 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster.cpp
Abstract:
Apply bit-blasting
Author:
Leonardo (leonardo) 2011-10-25
--*/
#include "ast/simplifiers/bit_blaster.h"
void bit_blaster_simplifier::updt_params(params_ref const & p) {
m_params.append(p);
m_rewriter.updt_params(m_params);
}
void bit_blaster_simplifier::collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
insert_max_steps(r);
r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders).");
r.insert("blast_add", CPK_BOOL, "(default: true) bit-blast adders.");
r.insert("blast_quant", CPK_BOOL, "(default: false) bit-blast quantified variables.");
r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms.");
}
void bit_blaster_simplifier::reduce() {
m_rewriter.start_rewrite();
expr_ref new_curr(m);
proof_ref new_pr(m);
bool change = false;
for (unsigned idx : indices()) {
auto [curr, p, d] = m_fmls[idx]();
m_rewriter(curr, new_curr, new_pr);
if (curr != new_curr) {
m_num_steps += m_rewriter.get_num_steps();
change = true;
TRACE("bit_blaster", tout << mk_pp(curr, m) << " -> " << new_curr << "\n";);
m_fmls.update(idx, dependent_expr(m, new_curr, mp(p, new_pr), d));
}
}
if (change) {
obj_map<func_decl, expr*> const2bits;
ptr_vector<func_decl> newbits;
m_rewriter.end_rewrite(const2bits, newbits);
for (auto* f : newbits)
m_fmls.model_trail().hide(f);
for (auto const& [f, v] : const2bits)
m_fmls.model_trail().push(f, v, nullptr, {});
}
m_rewriter.cleanup();
}
void bit_blaster_simplifier::collect_statistics(statistics& st) const {
st.update("bit-blaster-num-steps", m_num_steps);
}
void bit_blaster_simplifier::push() {
m_rewriter.push();
dependent_expr_simplifier::push();
}
void bit_blaster_simplifier::pop(unsigned n) {
dependent_expr_simplifier::pop(n);
m_rewriter.pop(n);
}

View file

@ -0,0 +1,54 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster.h
Abstract:
Apply bit-blasting
Author:
Leonardo (leonardo) 2011-10-25
--*/
#include "ast/rewriter/bit_blaster/bit_blaster_rewriter.h"
#include "ast/ast_pp.h"
#include "model/model_pp.h"
#include "ast/rewriter/rewriter_types.h"
#include "ast/simplifiers/dependent_expr_state.h"
class bit_blaster_simplifier : public dependent_expr_simplifier {
bit_blaster_rewriter m_rewriter;
unsigned m_num_steps = 0;
params_ref m_params;
public:
bit_blaster_simplifier(ast_manager & m, params_ref const & p, dependent_expr_state& s):
dependent_expr_simplifier(m, s),
m_rewriter(m, p) {
updt_params(p);
}
char const* name() const override { return "bit-blast"; }
void updt_params(params_ref const & p) override;
void collect_param_descrs(param_descrs & r) override;
void reduce() override;
void collect_statistics(statistics& st) const override;
void push() override;
void pop(unsigned n) override;
/*
* Expose the bit-blaster rewriter so that assumptions and implied bit-vectors can be reconstructed
* after bit-blasting.
*/
bit_blaster_rewriter& rewriter() { return m_rewriter; }
};
/*
ADD_SIMPLIFIER("bit-blast", "reduce bit-vector expressions into SAT.", "alloc(bit_blaster_simplifier, m, p, s)")
*/

View file

@ -0,0 +1,292 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bound_manager.cpp
Abstract:
Collect bounds.
Author:
Leonardo (leonardo) 2011-05-16
Notes:
--*/
#include "ast/ast_smt2_pp.h"
#include "ast/ast_pp.h"
#include "ast/ast_translation.h"
#include "ast/simplifiers/bound_manager.h"
bound_manager::bound_manager(ast_manager & m):
m_util(m),
m_bounded_vars(m) {
}
bound_manager::~bound_manager() {
reset();
}
bound_manager* bound_manager::translate(ast_manager& dst_m) {
bound_manager* result = alloc(bound_manager, dst_m);
ast_translation tr(m(), dst_m);
expr_dependency_translation edtr(tr);
for (auto& kv : m_lowers) result->m_lowers.insert(tr(kv.m_key), kv.m_value);
for (auto& kv : m_uppers) result->m_uppers.insert(tr(kv.m_key), kv.m_value);
for (auto& kv : m_lower_deps) result->m_lower_deps.insert(tr(kv.m_key), edtr(kv.m_value));
for (auto& kv : m_upper_deps) result->m_upper_deps.insert(tr(kv.m_key), edtr(kv.m_value));
for (expr* e : m_bounded_vars) result->m_bounded_vars.push_back(tr(e));
return result;
}
static decl_kind swap_decl(decl_kind k) {
switch (k) {
case OP_LE: return OP_GE;
case OP_LT: return OP_GT;
case OP_GE: return OP_LE;
case OP_GT: return OP_LT;
default:
UNREACHABLE();
return k;
}
}
decl_kind bound_manager::neg(decl_kind k) {
switch (k) {
case OP_LE: return OP_GT;
case OP_LT: return OP_GE;
case OP_GE: return OP_LT;
case OP_GT: return OP_LE;
default:
UNREACHABLE();
return k;
}
}
void bound_manager::norm(numeral & n, decl_kind & k) {
switch (k) {
case OP_LE: return;
case OP_GE: return;
case OP_LT:
// x < n --> x <= n-1
n--;
k = OP_LE;
return;
case OP_GT:
// x > n --> x >= n+1
n++;
k = OP_GE;
return;
default:
return;
}
}
static bool is_lower(decl_kind k) {
return k == OP_GT || k == OP_GE;
}
static bool is_strict(decl_kind k) {
return k == OP_LT || k == OP_GT;
}
bool bound_manager::is_numeral(expr* v, numeral& n, bool& is_int) {
expr* w;
if (m_util.is_uminus(v, w) && is_numeral(w, n, is_int)) {
n.neg();
return true;
}
return m_util.is_numeral(v, n, is_int);
}
void bound_manager::operator()(expr * f, expr_dependency * d, proof* p) {
if (p)
return;
TRACE("bound_manager", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";);
expr * v;
numeral n;
if (is_disjunctive_bound(f, d))
return;
if (is_equality_bound(f, d))
return;
bool pos = true;
while (m().is_not(f, f))
pos = !pos;
if (!is_app(f))
return;
app * t = to_app(f);
if (t->get_family_id() != m_util.get_family_id())
return;
decl_kind k = t->get_decl_kind();
if (k != OP_LE && k != OP_GE && k != OP_LT && k != OP_GT)
return;
expr * lhs = t->get_arg(0);
expr * rhs = t->get_arg(1);
bool is_int;
if (is_uninterp_const(lhs) && is_numeral(rhs, n, is_int)) {
v = lhs;
}
else if (is_uninterp_const(rhs) && is_numeral(lhs, n, is_int)) {
v = rhs;
k = swap_decl(k);
}
else {
return;
}
if (!pos)
k = neg(k);
if (is_int)
norm(n, k);
TRACE("bound_manager", tout << "found bound for:\n" << mk_ismt2_pp(v, m()) << "\n";);
bool strict = is_strict(k);
if (is_lower(k)) {
insert_lower(v, strict, n, d);
}
else {
insert_upper(v, strict, n, d);
}
}
void bound_manager::insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d) {
limit old;
if (m_uppers.find(v, old)) {
if (n < old.first || (n == old.first && strict && !old.second)) {
// improved bound
m_uppers.insert(v, limit(n, strict));
if (d)
m_upper_deps.insert(v, d);
}
}
else {
m_uppers.insert(v, limit(n, strict));
if (d)
m_upper_deps.insert(v, d);
if (!m_lowers.contains(v)) {
m_bounded_vars.push_back(v);
}
}
}
void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d) {
limit old;
if (m_lowers.find(v, old)) {
if (n > old.first || (n == old.first && strict && !old.second)) {
// improved bound
m_lowers.insert(v, limit(n, strict));
if (d)
m_lower_deps.insert(v, d);
}
}
else {
m_lowers.insert(v, limit(n, strict));
if (d)
m_lower_deps.insert(v, d);
if (!m_uppers.contains(v)) {
m_bounded_vars.push_back(v);
}
}
}
bool bound_manager::is_equality_bound(expr * f, expr_dependency * d) {
expr* x, *y, *z, *u;
if (!m().is_eq(f, x, y))
return false;
if (!is_uninterp_const(x))
std::swap(x, y);
numeral n;
bool is_int;
if (is_uninterp_const(x) && is_numeral(y, n, is_int)) {
insert_lower(x, false, n, d);
insert_upper(x, false, n, d);
return true;
}
// x = y mod n => 0 <= x < n
if (m_util.is_mod(y, z, u) && is_numeral(u, n, is_int) && n > 0) {
insert_lower(x, false, rational::zero(), d);
insert_upper(x, false, n - 1, d);
return true;
}
return false;
}
bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) {
numeral lo, hi, n;
if (!m().is_or(f)) return false;
unsigned sz = to_app(f)->get_num_args();
if (sz == 0) return false;
expr * x, * y, * v = nullptr;
bool is_int;
for (unsigned i = 0; i < sz; ++i) {
expr * e = to_app(f)->get_arg(i);
if (!m().is_eq(e, x, y)) return false;
if (is_uninterp_const(x) &&
is_numeral(y, n, is_int) && is_int &&
(x == v || v == nullptr)) {
if (v == nullptr) { v = x; lo = hi = n; }
if (n < lo) lo = n;
if (n > hi) hi = n;
}
else if (is_uninterp_const(y) &&
is_numeral(x, n, is_int) && is_int &&
(y == v || v == nullptr)) {
if (v == nullptr) { v = y; lo = hi = n; }
if (n < lo) lo = n;
if (n > hi) hi = n;
}
else {
return false;
}
}
TRACE("bound_manager", tout << "bounds: " << lo << " " << hi << "\n";);
insert_lower(v, false, lo, d);
insert_upper(v, false, hi, d);
return true;
}
void bound_manager::reset() {
m_bounded_vars.finalize();
m_lowers.finalize();
m_uppers.finalize();
m_lower_deps.finalize();
m_upper_deps.finalize();
}
bool bound_manager::inconsistent() const {
for (auto const& [k,v] : m_lowers) {
limit const& lim1 = v;
limit lim2;
if (m_uppers.find(k, lim2)) {
if (lim1.first > lim2.first)
return true;
if (lim1.first == lim2.first &&
!lim1.second && lim2.second) {
return true;
}
}
}
return false;
}
void bound_manager::display(std::ostream & out) const {
numeral n; bool strict;
for (iterator it = begin(); it != end(); ++it) {
expr * v = *it;
if (has_lower(v, n, strict))
out << n << " " << (strict ? "<" : "<=");
else
out << "-oo <";
out << " " << mk_ismt2_pp(v, m()) << " ";
if (has_upper(v, n, strict))
out << (strict ? "<" : "<=") << " " << n;
else
out << "< oo";
out << "\n";
}
}

View file

@ -0,0 +1,111 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bound_manager.h
Abstract:
Collect bounds.
Author:
Leonardo (leonardo) 2011-05-16
Notes:
--*/
#pragma once
#include "ast/ast.h"
#include "ast/arith_decl_plugin.h"
class bound_manager {
public:
typedef rational numeral;
private:
typedef std::pair<numeral, bool> limit;
arith_util m_util;
obj_map<expr, limit> m_lowers;
obj_map<expr, limit> m_uppers;
obj_map<expr, expr_dependency*> m_lower_deps;
obj_map<expr, expr_dependency*> m_upper_deps;
expr_ref_vector m_bounded_vars;
bool is_disjunctive_bound(expr * f, expr_dependency * d);
bool is_equality_bound(expr * f, expr_dependency * d);
bool is_numeral(expr* v, rational& n, bool& is_int);
void insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d);
void insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d);
public:
static decl_kind neg(decl_kind k);
static void norm(numeral & n, decl_kind & k);
bound_manager(ast_manager & m);
~bound_manager();
bound_manager* translate(ast_manager& dst_m);
ast_manager & m() const { return m_util.get_manager(); }
void operator()(expr * n, expr_dependency * d, proof* p);
bool has_lower(expr * c, numeral & v, bool & strict) const {
limit l;
if (m_lowers.find(c, l)) {
v = l.first;
strict = l.second;
return true;
}
return false;
}
bool has_upper(expr * c, numeral & v, bool & strict) const {
limit l;
if (m_uppers.find(c, l)) {
v = l.first;
strict = l.second;
return true;
}
return false;
}
expr_dependency * lower_dep(expr * c) const {
expr_dependency * d;
if (m_lower_deps.find(c, d))
return d;
return nullptr;
}
expr_dependency * upper_dep(expr * c) const {
expr_dependency * d;
if (m_upper_deps.find(c, d))
return d;
return nullptr;
}
bool inconsistent() const;
bool has_lower(expr * c) const {
return m_lowers.contains(c);
}
bool has_upper(expr * c) const {
return m_uppers.contains(c);
}
typedef ptr_vector<expr>::const_iterator iterator;
/**
\brief Iterator for all bounded constants.
*/
iterator begin() const { return m_bounded_vars.begin(); }
iterator end() const { return m_bounded_vars.end(); }
void reset();
// for debugging purposes
void display(std::ostream & out) const;
};

View file

@ -0,0 +1,942 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bound_propagator.cpp
Abstract:
Bound propagators for arithmetic.
Support class for implementing strategies and search procedures
Author:
Leonardo de Moura (leonardo) 2011-06-18.
Revision History:
--*/
#include "ast/simplifiers/bound_propagator.h"
#include<cmath>
// -------------------------------
// Bound Relaxation configuration
//
// The idea is to minimize errors in floating point computations
//
// If RELAX_BOUNDS is undefined, then bound relaxation is disabled.
// Otherwise, lower bounds l are relaxed using the formula
// PRECISION * floor(l * INV_PRECISION + TOLERANCE)
// and upper bounds u as:
// PRECISION * ceil(u * INV_PRECISION - TOLERANCE)
// In the LP literature, the suggested values are
// l := 10^-5 * floor(l*10^5 + 10^-6)
// u := 10^-5 * ceil(u*10^5 - 10^-6)
// I'm using the following values because of strict bounds
// l := 10^-6 * floor(l*10^6 + 10^-7)
// u := 10^-6 * ceil(u*10^6 - 10^-7)
#define RELAX_BOUNDS
#define TOLERANCE 0.0000001
#define PRECISION 0.000001
#define INV_PRECISION 1000000.0
// -------------------------------
bound_propagator::bound::bound(numeral_manager & m,
mpq const & k,
double approx_k,
bool lower,
bool strict,
unsigned lvl,
unsigned ts,
bkind bk,
unsigned c_idx,
assumption a,
bound * prev):
m_approx_k(approx_k),
m_lower(lower),
m_strict(strict),
m_kind(bk),
m_level(lvl),
m_timestamp(ts),
m_prev(prev) {
m.set(m_k, k);
if (bk == DERIVED)
m_constraint_idx = c_idx;
else
m_assumption = a;
}
bound_propagator::bound_propagator(numeral_manager & _m, allocator & a, params_ref const & p):
m(_m),
m_allocator(a),
m_eq_manager(m, a) {
m_timestamp = 0;
m_qhead = 0;
m_conflict = null_var;
updt_params(p);
reset_statistics();
}
bound_propagator::~bound_propagator() {
m.del(m_tmp);
reset();
}
void bound_propagator::del_constraints_core() {
for (auto & c : m_constraints) {
del_constraint(c);
}
m_constraints.reset();
}
void bound_propagator::del_constraints() {
SASSERT(scope_lvl() == 0);
if (m_constraints.empty())
return;
del_constraints_core();
m_constraints.finalize();
for (auto& wl : m_watches) {
wl.finalize();
}
}
void bound_propagator::del_constraint(constraint & c) {
switch (c.m_kind) {
case LINEAR:
m_eq_manager.del(c.m_eq);
break;
default:
UNREACHABLE();
break;
}
}
void bound_propagator::updt_params(params_ref const & p) {
m_max_refinements = p.get_uint("bound_max_refinements", 16);
m_threshold = p.get_double("bound_threshold", 0.05);
m_small_interval = p.get_double("bound_small_interval", 128);
m_strict2double = p.get_double("strict2double", 0.00001);
}
void bound_propagator::get_param_descrs(param_descrs & r) {
r.insert("bound_max_refinements", CPK_UINT, "(default: 16) maximum number of bound refinements (per round) for unbounded variables.");
r.insert("bound_threshold", CPK_DOUBLE, "(default: 0.05) bound propagation improvement threshold ratio.");
}
void bound_propagator::collect_statistics(statistics & st) const {
st.update("bound conflicts", m_conflicts);
st.update("bound propagations", m_propagations);
st.update("bound false alarms", m_false_alarms);
}
void bound_propagator::reset_statistics() {
m_conflicts = 0;
m_propagations = 0;
m_false_alarms = 0;
}
void bound_propagator::mk_var(var x, bool is_int) {
m_is_int.reserve(x+1, false);
m_dead.reserve(x+1, true);
m_lowers.reserve(x+1, 0);
m_uppers.reserve(x+1, 0);
m_lower_refinements.reserve(x+1, 0);
m_upper_refinements.reserve(x+1, 0);
m_watches.reserve(x+1);
SASSERT(m_dead[x]);
m_is_int[x] = is_int;
m_dead[x] = false;
m_lowers[x] = 0;
m_uppers[x] = 0;
m_lower_refinements[x] = 0;
m_upper_refinements[x] = 0;
m_watches[x].reset();
}
void bound_propagator::del_var(var x) {
SASSERT(!m_dead[x]);
m_dead[x] = true;
// mark constraints containing x as dead.
wlist & wl = m_watches[x];
for (auto c : wl) {
m_constraints[c].m_dead = true;
}
}
void bound_propagator::mk_eq(unsigned sz, mpq * as, var * xs) {
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
init_eq(eq);
}
void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) {
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
init_eq(eq);
}
void bound_propagator::init_eq(linear_equation * eq) {
if (eq == nullptr)
return;
unsigned c_idx = m_constraints.size();
m_constraints.push_back(constraint());
constraint & new_c = m_constraints.back();
new_c.m_kind = LINEAR;
new_c.m_dead = false;
new_c.m_timestamp = 0;
new_c.m_act = 0;
new_c.m_counter = 0;
new_c.m_eq = eq;
unsigned sz = eq->size();
for (unsigned i = 0; i < sz; i++) {
m_watches[eq->x(i)].push_back(c_idx);
}
if (propagate(c_idx) && scope_lvl() > 0)
m_reinit_stack.push_back(c_idx);
}
void bound_propagator::push() {
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_trail_limit = m_trail.size();
s.m_qhead_old = m_qhead;
s.m_reinit_stack_limit = m_reinit_stack.size();
s.m_timestamp_old = m_timestamp;
s.m_in_conflict = inconsistent();
}
void bound_propagator::undo_trail(unsigned old_sz) {
SASSERT(old_sz <= m_trail.size());
unsigned i = m_trail.size();
while (i > old_sz) {
--i;
trail_info & info = m_trail.back();
var x = info.x();
bool is_lower = info.is_lower();
m_trail.pop_back();
bound * b;
if (is_lower) {
b = m_lowers[x];
m_lowers[x] = b->m_prev;
}
else {
b = m_uppers[x];
m_uppers[x] = b->m_prev;
}
m.del(b->m_k);
b->~bound();
m_allocator.deallocate(sizeof(bound), b);
}
SASSERT(m_trail.size() == old_sz);
}
void bound_propagator::pop(unsigned num_scopes) {
unsigned lvl = scope_lvl();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
scope & s = m_scopes[new_lvl];
undo_trail(s.m_trail_limit);
m_timestamp = s.m_timestamp_old;
m_qhead = s.m_qhead_old;
if (!s.m_in_conflict)
m_conflict = null_var;
unsigned reinit_stack_sz = s.m_reinit_stack_limit;
m_scopes.shrink(new_lvl);
// reinitialize
unsigned i = reinit_stack_sz;
unsigned j = reinit_stack_sz;
unsigned sz = m_reinit_stack.size();
for (; i < sz; i++) {
unsigned c_idx = m_reinit_stack[i];
bool p = propagate(c_idx);
if (new_lvl > 0 && p) {
m_reinit_stack[j] = c_idx;
j++;
}
}
m_reinit_stack.shrink(j);
}
bool bound_propagator::assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
if (is_int(x)) {
if (m.is_int(k)) {
if (strict)
m.inc(k);
}
else {
m.ceil(k, k);
}
SASSERT(m.is_int(k));
strict = false;
}
TRACE("bound_propagator_detail", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
bound * old_lower = m_lowers[x];
if (old_lower) {
bool improves = m.gt(k, old_lower->m_k) || (!old_lower->m_strict && strict && m.eq(k, old_lower->m_k));
if (!improves) {
if (bk == DERIVED) {
TRACE("bound_propagator_detail", tout << "false alarm\n";);
m_false_alarms++;
}
return false;
}
}
if (bk == DERIVED) {
TRACE("bound_propagator_derived", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
m_propagations++;
}
if (scope_lvl() == 0 && bk == DERIVED)
bk = AXIOM; // don't need justification at level 0
double approx_k = m.get_double(k);
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " approx: " << approx_k << "\n";);
#ifdef RELAX_BOUNDS
approx_k = PRECISION*floor(approx_k*INV_PRECISION + TOLERANCE);
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
#endif
void * mem = m_allocator.allocate(sizeof(bound));
bound * new_lower = new (mem) bound(m, k, approx_k, true, strict, scope_lvl(), m_timestamp, bk, c_idx, a, old_lower);
m_timestamp++;
m_lowers[x] = new_lower;
m_trail.push_back(trail_info(x, true));
m_lower_refinements[x]++;
check_feasibility(x);
return true;
}
bool bound_propagator::assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
if (is_int(x)) {
if (m.is_int(k)) {
if (strict)
m.dec(k);
}
else {
m.floor(k, k);
}
SASSERT(m.is_int(k));
strict = false;
}
TRACE("bound_propagator_detail", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
bound * old_upper = m_uppers[x];
if (old_upper) {
bool improves = m.lt(k, old_upper->m_k) || (!old_upper->m_strict && strict && m.eq(k, old_upper->m_k));
if (!improves) {
if (bk == DERIVED) {
TRACE("bound_propagator_detail", tout << "false alarm\n";);
m_false_alarms++;
}
return false;
}
}
if (bk == DERIVED) {
m_propagations++;
TRACE("bound_propagator_derived", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
}
if (scope_lvl() == 0 && bk == DERIVED)
bk = AXIOM; // don't need justification at level 0
double approx_k = m.get_double(k);
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " approx: " << approx_k << "\n";);
#ifdef RELAX_BOUNDS
approx_k = PRECISION*ceil(approx_k*INV_PRECISION - TOLERANCE);
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
#endif
void * mem = m_allocator.allocate(sizeof(bound));
bound * new_upper = new (mem) bound(m, k, approx_k, false, strict, scope_lvl(), m_timestamp, bk, c_idx, a, m_uppers[x]);
m_timestamp++;
m_uppers[x] = new_upper;
m_trail.push_back(trail_info(x, false));
m_upper_refinements[x]++;
check_feasibility(x);
return true;
}
bool bound_propagator::get_interval_size(var x, double & r) const {
bound * l = m_lowers[x];
bound * u = m_uppers[x];
if (l && u) {
r = u->m_approx_k - l->m_approx_k;
return true;
}
return false;
}
template<bool LOWER>
bool bound_propagator::relevant_bound(var x, double new_k) const {
TRACE("bound_propagator_detail", tout << "relevant_bound x" << x << " " << new_k << " LOWER: " << LOWER << "\n";
if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n";
if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";);
bound * b = LOWER ? m_lowers[x] : m_uppers[x];
if (b == nullptr)
return true; // variable did not have a bound
double interval_size;
bool bounded = get_interval_size(x, interval_size);
if (!is_int(x)) {
// check if the improvement is significant
double improvement;
double abs_k = b->m_approx_k;
if (abs_k < 0.0)
abs_k -= abs_k;
if (bounded)
improvement = m_threshold * std::max(std::min(interval_size, abs_k), 1.0);
else
improvement = m_threshold * std::max(abs_k, 1.0);
if (LOWER) {
if (new_k <= b->m_approx_k + improvement) {
TRACE("bound_propagator", tout << "LOWER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
return false; // improvement is too small
}
}
else {
if (new_k >= b->m_approx_k - improvement) {
TRACE("bound_propagator", tout << "UPPER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
return false; // improvement is too small
}
}
}
else {
if (LOWER) {
if (new_k < b->m_approx_k + 1.0)
return false; // no improvement
}
else {
if (new_k > b->m_approx_k - 1.0)
return false; // no improvement
}
}
if (bounded && interval_size <= m_small_interval)
return true;
if (LOWER)
return m_lower_refinements[x] < m_max_refinements;
else
return m_upper_refinements[x] < m_max_refinements;
}
bool bound_propagator::relevant_lower(var x, double approx_k) const {
return relevant_bound<true>(x, approx_k);
}
bool bound_propagator::relevant_upper(var x, double approx_k) const {
return relevant_bound<false>(x, approx_k);
}
void bound_propagator::check_feasibility(var x) {
if (inconsistent())
return;
bound * l = m_lowers[x];
bound * u = m_uppers[x];
if (l && u) {
if (m.lt(l->m_k, u->m_k))
return;
if (!l->m_strict && !u->m_strict && m.eq(l->m_k, u->m_k))
return;
m_conflict = x;
m_conflicts++;
SASSERT(inconsistent());
TRACE("bound_propagator", tout << "inconsistency detected: x" << x << "\n"; display(tout););
}
}
void bound_propagator::propagate() {
m_to_reset_ts.reset();
while (m_qhead < m_trail.size()) {
if (inconsistent())
break;
trail_info & info = m_trail[m_qhead];
var x = info.x();
bool is_lower = info.is_lower();
bound * b = is_lower ? m_lowers[x] : m_uppers[x];
SASSERT(b);
unsigned ts = b->m_timestamp;
TRACE("bound_propagator_detail", tout << "propagating x" << x << "\n";);
m_qhead++;
wlist const & wl = m_watches[x];
for (unsigned c_idx : wl) {
constraint & c = m_constraints[c_idx];
// We don't need to visit c if it was already propagated using b.
// Whenever we visit c we store in c.m_timestamp the current timestamp
// So, we know that c was already propagated any bound using bounds with timestamp lower than c.m_timestamp.
if (ts >= c.m_timestamp) {
if (c.m_timestamp == 0)
m_to_reset_ts.push_back(c_idx);
c.m_timestamp = m_timestamp;
propagate(c_idx);
}
}
}
for (unsigned c : m_to_reset_ts)
m_constraints[c].m_timestamp = 0;
}
bool bound_propagator::propagate(unsigned c_idx) {
constraint const & c = m_constraints[c_idx];
if (c.m_dead)
return false;
if (c.m_kind == LINEAR)
return propagate_eq(c_idx);
return false;
}
bool bound_propagator::propagate_eq(unsigned c_idx) {
constraint const & c = m_constraints[c_idx];
linear_equation * eq = c.m_eq;
#if 0
{
static unsigned counter = 0;
static unsigned visited = 0;
counter++;
visited += eq->size();
if (counter % 1000 == 0)
verbose_stream() << "[bound-propagator] :propagate-eq " << counter << " :visited-vars " << visited << std::endl;
}
#endif
TRACE("bound_propagator_detail", tout << "propagating using eq: "; m_eq_manager.display(tout, *eq); tout << "\n";);
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
unsigned ll_i = UINT_MAX; // position of the variable that couldn't contribute to ll
unsigned uu_i = UINT_MAX; // position of the variable that coundn't contribute to uu
bool ll_failed = false;
bool uu_failed = false;
double ll = 0.0;
double uu = 0.0;
unsigned sz = eq->size();
for (unsigned i = 0; i < sz; i++) {
var x_i = eq->x(i);
double a_i = eq->approx_a(i);
bound * l_i = m_lowers[x_i];
bound * u_i = m_uppers[x_i];
if (a_i < 0.0) {
if (!ll_failed) {
if (l_i == nullptr) {
if (ll_i == UINT_MAX)
ll_i = i;
else
ll_failed = true;
}
else {
ll -= a_i * l_i->m_approx_k;
}
}
if (!uu_failed) {
if (u_i == nullptr) {
if (uu_i == UINT_MAX)
uu_i = i;
else
uu_failed = true;
}
else {
uu -= a_i * u_i->m_approx_k;
}
}
}
else {
if (!ll_failed) {
if (u_i == nullptr) {
if (ll_i == UINT_MAX)
ll_i = i;
else
ll_failed = true;
}
else {
ll -= a_i * u_i->m_approx_k;
}
}
if (!uu_failed) {
if (l_i == nullptr) {
if (uu_i == UINT_MAX)
uu_i = i;
else
uu_failed = true;
}
else {
uu -= a_i * l_i->m_approx_k;
}
}
}
if (ll_failed && uu_failed)
return false; // nothing to propagate
}
bool propagated = false;
SASSERT(!ll_failed || !uu_failed);
if (ll_i == UINT_MAX || uu_i == UINT_MAX) {
for (unsigned i = 0; i < sz; i++) {
var x_i = eq->x(i);
double a_i = eq->approx_a(i);
bound * l_i = m_lowers[x_i];
bound * u_i = m_uppers[x_i];
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
if (ll_i == UINT_MAX) {
// can propagate a lower bound for a_i*x_i
if (a_i > 0.0) {
// can propagate a lower bound for x_i
double new_k = (ll + a_i * u_i->m_approx_k)/a_i;
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
propagated = true;
}
else {
// a_i < 0.0
// can propagate a upper bound for x_i
double new_k = (ll + a_i * l_i->m_approx_k)/a_i;
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
propagated = true;
}
}
if (uu_i == UINT_MAX) {
// can propagate an upper bound for a_i*x_i
if (a_i > 0.0) {
// can propagate a upper bound for x_i
double new_k = (uu + a_i * l_i->m_approx_k)/a_i;
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
propagated = true;
}
else {
// a_i < 0.0
// can propagate a lower bound for x_i
double new_k = (uu + a_i * u_i->m_approx_k)/a_i;
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
propagated = true;
}
}
}
}
if (!ll_failed && ll_i != UINT_MAX) {
// can propagate a lower bound for the monomial at position ll_i
var x_i = eq->x(ll_i);
double a_i = eq->approx_a(ll_i);
double new_k = ll/a_i;
if (a_i > 0.0) {
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, ll_i))
propagated = true;
}
else {
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, ll_i))
propagated = true;
}
}
if (!uu_failed && uu_i != UINT_MAX) {
// can propagate a upper bound for the monomial at position uu_i
var x_i = eq->x(uu_i);
double a_i = eq->approx_a(uu_i);
double new_k = uu/a_i;
if (a_i > 0.0) {
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, uu_i))
propagated = true;
}
else {
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, uu_i))
propagated = true;
}
}
return propagated;
}
/**
\brief Try to propagate a lower bound for the variable stored at position i, using mpq's (rationals).
When this method is invoked, we know that all other variables have the "right" bounds, and
using doubles we improve the current known bound.
*/
bool bound_propagator::propagate_lower(unsigned c_idx, unsigned i) {
constraint const & c = m_constraints[c_idx];
linear_equation * eq = c.m_eq;
var x_i = eq->x(i);
mpz const & a_i = eq->a(i);
unsigned sz = eq->size();
mpq k;
bool strict = false;
bool neg_a_i = m.is_neg(a_i);
for (unsigned j = 0; j < sz; j++) {
if (i == j)
continue;
var x_j = eq->x(j);
mpz const & a_j = eq->a(j);
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_uppers[x_j] : m_lowers[x_j];
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << " b_j->m_k: " << m.to_string(b_j->m_k) <<
" a_j: " << m.to_string(a_j) << "\n";);
SASSERT(b_j);
if (b_j->m_strict)
strict = true;
m.addmul(k, a_j, b_j->m_k, k);
}
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << "\n";);
m.neg(k);
m.div(k, a_i, k);
TRACE("bound_propagator_step", tout << "propagating lower x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
bool r = assert_lower_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
m.del(k);
return r;
}
/**
\brief Try to propagate a upper bound for the variable stored at position i, using mpq's (rationals).
When this method is invoked, we know that all other variables have the "right" bounds, and
using doubles we improve the current known bound.
*/
bool bound_propagator::propagate_upper(unsigned c_idx, unsigned i) {
constraint const & c = m_constraints[c_idx];
linear_equation * eq = c.m_eq;
var x_i = eq->x(i);
mpz const & a_i = eq->a(i);
unsigned sz = eq->size();
mpq k;
bool strict = false;
bool neg_a_i = m.is_neg(a_i);
for (unsigned j = 0; j < sz; j++) {
if (i == j)
continue;
var x_j = eq->x(j);
mpz const & a_j = eq->a(j);
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_lowers[x_j] : m_uppers[x_j];
SASSERT(b_j);
if (b_j->m_strict)
strict = true;
m.addmul(k, a_j, b_j->m_k, k);
}
m.neg(k);
m.div(k, a_i, k);
TRACE("bound_propagator_step", tout << "propagating upper x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
bool r = assert_upper_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
m.del(k);
return r;
}
void bound_propagator::reset() {
undo_trail(0);
del_constraints_core();
m_constraints.finalize();
m_is_int.finalize();
m_dead.finalize();
m_lowers.finalize();
m_uppers.finalize();
m_watches.finalize();
m_trail.finalize();
m_qhead = 0;
m_reinit_stack.finalize();
m_lower_refinements.finalize();
m_upper_refinements.finalize();
m_timestamp = 0;
m_conflict = null_var;
m_scopes.finalize();
}
bool bound_propagator::lower(var x, mpq & k, bool & strict, unsigned & ts) const {
bound * b = m_lowers[x];
if (!b)
return false;
m.set(k, b->m_k);
strict = b->m_strict;
ts = b->m_timestamp;
return true;
}
bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const {
bound * b = m_uppers[x];
if (!b)
return false;
m.set(k, b->m_k);
strict = b->m_strict;
ts = b->m_timestamp;
return true;
}
bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) {
bound * r = this;
while (r != nullptr && r->m_timestamp >= timestamp)
r = r->m_prev;
return r;
}
/**
\brief Return true if the coefficient of x in eq is positive
*/
bool bound_propagator::is_a_i_pos(linear_equation const & eq, var x) const {
unsigned i = eq.pos(x);
if (i == UINT_MAX)
return false;
return m.is_pos(eq.a(i));
}
void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector & ex) const {
if (!b)
return;
b = b->at(ts);
if (!b)
return;
if (b->m_kind == AXIOM || b->m_kind == DECISION)
return;
if (b->m_kind == ASSUMPTION) {
ex.push_back(b->m_assumption);
return;
}
svector<var_bound> & todo = const_cast<bound_propagator*>(this)->m_todo;
todo.reset();
unsigned qhead = 0;
todo.push_back(var_bound(x, b));
b->m_mark = true;
while (qhead < todo.size()) {
var_bound & vb = todo[qhead];
qhead ++;
var x = vb.first;
bound * b = vb.second;
SASSERT(b->kind() == ASSUMPTION || b->kind() == DERIVED);
if (b->kind() == ASSUMPTION) {
ex.push_back(b->m_assumption);
continue;
}
SASSERT(b->kind() == DERIVED);
constraint const & c = m_constraints[b->m_constraint_idx];
switch (c.m_kind) {
case LINEAR: {
linear_equation * eq = c.m_eq;
bool is_lower = b->is_lower();
if (!is_a_i_pos(*eq, x))
is_lower = !is_lower;
unsigned sz = eq->size();
for (unsigned i = 0; i < sz; i++) {
var x_i = eq->x(i);
if (x_i == x)
continue;
bound * b = (m.is_neg(eq->a(i)) == is_lower) ? m_lowers[x_i] : m_uppers[x_i];
SASSERT(b);
if (b->kind() == DERIVED || b->kind() == ASSUMPTION) {
if (!b->m_mark) {
b->m_mark = true;
todo.push_back(var_bound(x_i, b));
}
}
}
break;
}
default:
break;
}
}
for (var_bound& vb : todo)
vb.second->m_mark = false;
todo.reset();
}
/**
\brief Compute lower (upper) bound for the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]
Return false if the lower (upper) bound is -oo (oo)
*/
template<bool LOWER, typename Numeral>
bool bound_propagator::get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const {
st = false;
m.reset(r);
for (unsigned i = 0; i < sz; i++) {
var x_i = xs[i];
Numeral const & a_i = as[i];
if (m.is_zero(a_i))
continue;
bound * b = (m.is_neg(a_i) == LOWER) ? m_uppers[x_i] : m_lowers[x_i];
if (!b) {
m.reset(r);
return false;
}
if (b->m_strict)
st = true;
m.addmul(r, a_i, b->m_k, r);
}
return true;
}
bool bound_propagator::lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
return get_bound<true, mpq>(sz, as, xs, r, st);
}
bool bound_propagator::upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
return get_bound<false, mpq>(sz, as, xs, r, st);
}
void bound_propagator::display_bounds_of(std::ostream & out, linear_equation const & eq) const {
for (unsigned i = 0; i < eq.size(); i++) {
display_var_bounds(out, eq.x(i));
out << "\n";
}
}
void bound_propagator::display_var_bounds(std::ostream & out, var x, bool approx, bool precise) const {
if (m_lowers[x]) {
if (precise)
out << m.to_string(m_lowers[x]->m_k);
if (precise && approx)
out << " | ";
if (approx)
out << m_lowers[x]->m_approx_k;
out << " " << (m_lowers[x]->m_strict ? "<" : "<=");
}
else {
out << "-oo <";
}
out << " x" << x << " ";
if (m_uppers[x]) {
out << (m_uppers[x]->m_strict ? "<" : "<=") << " ";
if (precise)
out << m.to_string(m_uppers[x]->m_k);
if (precise && approx)
out << " | ";
if (approx)
out << m_uppers[x]->m_approx_k;
}
else {
out << "< oo";
}
}
void bound_propagator::display_bounds(std::ostream & out, bool approx, bool precise) const {
unsigned num_vars = m_dead.size();
for (unsigned x = 0; x < num_vars; x++) {
if (!is_dead(x)) {
display_var_bounds(out, x, approx, precise);
out << "\n";
}
}
}
void bound_propagator::display_constraints(std::ostream & out) const {
for (constraint const& c : m_constraints) {
if (c.m_kind == LINEAR) {
m_eq_manager.display(out, *(c.m_eq));
out << "\n";
}
}
}
void bound_propagator::display(std::ostream & out) const {
display_bounds(out);
display_constraints(out);
}

View file

@ -0,0 +1,263 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bound_propagator.h
Abstract:
Bound propagators for arithmetic.
Support class for implementing strategies and search procedures
Author:
Leonardo de Moura (leonardo) 2011-06-18.
Revision History:
--*/
#pragma once
#include "util/mpq.h"
#include "util/vector.h"
#include "util/params.h"
#include "util/statistics.h"
#include "util/numeral_buffer.h"
#include "ast/simplifiers/linear_equation.h"
class bound_propagator {
public:
typedef unsigned var;
typedef unsigned assumption;
typedef unsynch_mpq_manager numeral_manager;
typedef unsigned_vector assumption_vector;
typedef unsigned constraint_id;
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
typedef svector<double> double_vector;
static const assumption null_assumption = UINT_MAX;
static const var null_var = UINT_MAX;
static const unsigned null_constraint_idx = UINT_MAX;
class trail_info {
unsigned m_x_lower;
public:
trail_info(var x, bool is_lower):m_x_lower((x << 1) + static_cast<unsigned>(is_lower)) {}
trail_info():m_x_lower(UINT_MAX) {}
var x() const { return m_x_lower >> 1; }
bool is_lower() const { return (m_x_lower & 1) != 0; }
};
protected:
enum ckind { LINEAR // only linear equalities so far.
};
/**
\brief Constraints don't need justification.
*/
class constraint {
friend class bound_propagator;
unsigned m_kind:2;
unsigned m_dead:1;
unsigned m_timestamp; // Constraint tried to propagate new bounds using bounds with timestamp < m_timestamp.
unsigned m_act; // activity
unsigned m_counter; // number of times the constraint propagated
union {
linear_equation * m_eq;
};
};
enum bkind { AXIOM, // doesn't need justification
ASSUMPTION, // aka external case-split, it is used to connect with external search engine.
DERIVED, // implied
DECISION // internal case-split
};
struct bound {
mpq m_k;
double m_approx_k;
unsigned m_lower:1;
unsigned m_strict:1;
unsigned m_mark:1;
unsigned m_kind:2;
unsigned m_level:27;
unsigned m_timestamp;
union {
assumption m_assumption;
unsigned m_constraint_idx;
};
bound * m_prev;
bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk,
unsigned c_idx, assumption a, bound * prev);
bound * at(unsigned timestamp);
bkind kind() const { return static_cast<bkind>(m_kind); }
bool is_lower() const { return m_lower != 0; }
};
typedef ptr_vector<bound> var2bound;
typedef svector<var> var_vector;
typedef svector<constraint> constraint_vector;
typedef unsigned_vector c_idx_vector;
typedef c_idx_vector wlist;
typedef small_object_allocator allocator;
typedef linear_equation_manager lin_eq_manager;
numeral_manager & m;
allocator & m_allocator;
lin_eq_manager m_eq_manager;
constraint_vector m_constraints;
char_vector m_is_int;
char_vector m_dead;
var2bound m_lowers;
var2bound m_uppers;
vector<wlist> m_watches;
svector<trail_info> m_trail;
unsigned m_qhead;
c_idx_vector m_reinit_stack;
unsigned_vector m_lower_refinements; // number of times a lower bound was propagated for each variable (loop prevention)
unsigned_vector m_upper_refinements; // number of times a upper bound was propagated for each variable (loop prevention)
unsigned m_timestamp;
var m_conflict;
mpq m_tmp;
struct scope {
unsigned m_trail_limit;
unsigned m_qhead_old;
unsigned m_reinit_stack_limit;
unsigned m_timestamp_old:31;
unsigned m_in_conflict:1;
};
svector<scope> m_scopes;
unsigned_vector m_to_reset_ts; // temp field: ids of the constraints we must reset the field m_timestamp
// config
unsigned m_max_refinements; // maximum number of refinements per round
double m_small_interval;
double m_threshold; // improvement threshold
double m_strict2double;
// statistics
unsigned m_conflicts;
unsigned m_propagations;
unsigned m_false_alarms;
void del_constraint(constraint & cnstr);
void del_constraints_core();
template<bool LOWER>
bool relevant_bound(var x, double approx_k) const;
bool relevant_lower(var x, double approx_k) const;
bool relevant_upper(var x, double approx_k) const;
bool get_interval_size(var x, double & r) const;
void check_feasibility(var x);
bool assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
bool assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
bool propagate(unsigned c_idx);
bool propagate_eq(unsigned c_idx);
bool propagate_lower(unsigned c_idx, unsigned i);
bool propagate_upper(unsigned c_idx, unsigned i);
void undo_trail(unsigned old_sz);
typedef std::pair<var, bound *> var_bound;
svector<var_bound> m_todo;
void explain(var x, bound * b, unsigned ts, assumption_vector & ex) const;
bool is_a_i_pos(linear_equation const & eq, var x) const;
template<bool LOWER, typename Numeral>
bool get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const;
void init_eq(linear_equation * eq);
public:
bound_propagator(numeral_manager & m, allocator & a, params_ref const & p);
~bound_propagator();
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
void collect_statistics(statistics & st) const;
void reset_statistics();
double strict2double() const { return m_strict2double; }
bool is_int(var x) const { return m_is_int[x] != 0; }
unsigned scope_lvl() const { return m_scopes.size(); }
void mk_var(var x, bool is_int);
void del_var(var x);
bool is_dead(var x) const { return m_dead[x] != 0; }
void mk_eq(unsigned sz, mpq * as, var * xs);
void mk_eq(unsigned sz, mpz * as, var * xs);
void del_constraints();
void assert_lower(var x, mpq const & k, bool strict, assumption a = null_assumption) {
m.set(m_tmp, k);
assert_lower_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
}
void assert_upper(var x, mpq const & k, bool strict, assumption a = null_assumption) {
m.set(m_tmp, k);
assert_upper_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
}
void assert_decided_lower(var x, mpq const & k) {
m.set(m_tmp, k);
assert_lower_core(x, m_tmp, false, DECISION, 0, null_assumption);
}
void assert_decided_upper(var x, mpq const & k) {
m.set(m_tmp, k);
assert_upper_core(x, m_tmp, false, DECISION, 0, null_assumption);
}
void propagate();
void push();
void pop(unsigned num_scopes);
void reset();
bool has_lower(var x) const { return m_lowers[x] != 0; }
bool has_upper(var x) const { return m_uppers[x] != 0; }
bool lower(var x, mpq & k, bool & strict, unsigned & ts) const;
bool upper(var x, mpq & k, bool & strict, unsigned & ts) const;
bool is_fixed(var x) const { return has_lower(x) && has_upper(x) && m.eq(m_lowers[x]->m_k, m_uppers[x]->m_k) && !inconsistent(); }
mpq const & lower(var x, bool & strict) const { SASSERT(has_lower(x)); bound * b = m_lowers[x]; strict = b->m_strict; return b->m_k; }
mpq const & upper(var x, bool & strict) const { SASSERT(has_upper(x)); bound * b = m_uppers[x]; strict = b->m_strict; return b->m_k; }
mpq const & lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_k; }
mpq const & upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_k; }
double approx_lower(var x) const {
SASSERT(has_lower(x));
return m_lowers[x]->m_strict ? m_lowers[x]->m_approx_k + m_strict2double : m_lowers[x]->m_approx_k;
}
double approx_upper(var x) const {
SASSERT(has_upper(x));
return m_uppers[x]->m_strict ? m_uppers[x]->m_approx_k - m_strict2double : m_uppers[x]->m_approx_k;
}
bool is_zero(var x) const { return has_lower(x) && has_upper(x) && m.is_zero(lower(x)) && m.is_zero(upper(x)); }
void explain_lower(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_lowers[x], ts, ex); }
void explain_upper(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_uppers[x], ts, ex); }
void explain_lower(var x, assumption_vector & ex) const { explain_lower(x, m_timestamp, ex); }
void explain_upper(var x, assumption_vector & ex) const { explain_upper(x, m_timestamp, ex); }
var conflict_var() const { return m_conflict; }
bool inconsistent() const { return m_conflict != null_var; }
unsigned trail_size() const { return m_trail.size(); }
unsigned qhead() const { return m_qhead; }
typedef svector<trail_info>::const_iterator trail_iterator;
trail_iterator begin_trail() const { return m_trail.begin(); }
trail_iterator end_trail() const { return m_trail.end(); }
bool lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
bool upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
void display(std::ostream & out) const;
void display_var_bounds(std::ostream & out, var x, bool approx = true, bool precise = true) const;
void display_bounds(std::ostream & out, bool approx = true, bool precise = true) const;
void display_precise_bounds(std::ostream & out) const { display_bounds(out, false, true); }
void display_approx_bounds(std::ostream & out) const { display_bounds(out, true, false); }
void display_constraints(std::ostream & out) const;
void display_bounds_of(std::ostream & out, linear_equation const & eq) const;
unsigned get_num_false_alarms() const { return m_false_alarms; }
unsigned get_num_propagations() const { return m_propagations; }
};

View file

@ -0,0 +1,596 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bound_simplifier.cpp
Author:
Nikolaj Bjorner (nbjorner) 2023-01-22
Description:
Extract bounds for sub-expressions and use the bounds for propagation and simplification.
It applies the simplificaitons from the bounds_propagator and it applies nested rewriting
of sub-expressions based on bounds information. Initially, rewriting amounts to eliminating
occurrences of mod N.
From the description of propagate_ineqs_tactic:
- Propagate bounds using the bound_propagator.
- Eliminate subsumed inequalities.
For example:
x - y >= 3
can be replaced with true if we know that
x >= 3 and y <= 0
- Convert inequalities of the form p <= k and p >= k into p = k,
where p is a polynomial and k is a constant.
This strategy assumes the input is in arith LHS mode.
This can be achieved by using option :arith-lhs true in the
simplifier.
--*/
#include "ast/ast_pp.h"
#include "ast/simplifiers/bound_simplifier.h"
#include "ast/rewriter/rewriter_def.h"
struct bound_simplifier::rw_cfg : public default_rewriter_cfg {
bound_simplifier& s;
rw_cfg(bound_simplifier& s): s(s) {}
br_status reduce_app(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result, proof_ref& pr) {
return s.reduce_app(f, num_args, args, result, pr);
}
};
struct bound_simplifier::rw : public rewriter_tpl<rw_cfg> {
rw_cfg m_cfg;
rw(bound_simplifier& s):
rewriter_tpl<rw_cfg>(s.m, false, m_cfg),
m_cfg(s) {
}
};
br_status bound_simplifier::reduce_app(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result, proof_ref& pr) {
rational N, hi, lo;
if (a.is_mod(f) && num_args == 2 && a.is_numeral(args[1], N)) {
expr* x = args[0];
auto& im = m_interval;
scoped_dep_interval i(im);
get_bounds(x, i);
if (im.upper_is_inf(i) || im.lower_is_inf(i))
return BR_FAILED;
if (im.upper_is_open(i) || im.lower_is_open(i))
return BR_FAILED;
lo = im.lower(i);
hi = im.upper(i);
if (hi - lo >= N)
return BR_FAILED;
if (N > hi && lo >= 0) {
result = x;
TRACE("propagate-ineqs", tout << expr_ref(m.mk_app(f, num_args, args), m) << " -> " << result << "\n");
return BR_DONE;
}
if (2 * N > hi && lo >= N) {
result = a.mk_sub(x, a.mk_int(N));
m_rewriter(result);
TRACE("propagate-ineqs", tout << expr_ref(m.mk_app(f, num_args, args), m) << " -> " << result << "\n");
return BR_DONE;
}
IF_VERBOSE(2, verbose_stream() << "potentially missed simplification: " << mk_pp(x, m) << " " << lo << " " << hi << " not reduced\n");
}
return BR_FAILED;
}
void bound_simplifier::reduce() {
bool updated = true, found_bound = false;
for (unsigned i = 0; i < 5 && updated; ++i) {
updated = false;
found_bound = false;
reset();
for (unsigned idx : indices()) {
if (insert_bound(m_fmls[idx])) {
m_fmls.update(idx, dependent_expr(m, m.mk_true(), nullptr, nullptr));
found_bound = true;
}
}
if (!found_bound)
break;
for (unsigned idx : indices())
tighten_bound(m_fmls[idx]);
bp.propagate();
proof_ref pr(m);
expr_ref r(m);
rw rw(*this);
for (unsigned idx : indices()) {
auto const& d = m_fmls[idx];
if (d.pr())
continue;
rw(d.fml(), r, pr);
if (r != d.fml()) {
m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep()));
++m_num_reduced;
updated = true;
}
}
restore_bounds();
}
}
// generalization to summations?
bool bound_simplifier::is_offset(expr* e, expr* x, rational& n) {
expr* y, *z;
if (a.is_add(e, y, z)) {
if (x != y)
std::swap(y, z);
return x == y && a.is_numeral(z, n);
}
return false;
}
bool bound_simplifier::insert_bound(dependent_expr const& de) {
if (de.pr())
return false;
if (de.dep())
return false;
rational n, n0;
expr* x, *y, *f = de.fml();
if (m.is_eq(f, x, y)) {
if (a.is_numeral(y))
std::swap(x, y);
if (a.is_numeral(x, n)) {
assert_lower(y, n, false);
assert_upper(y, n, false);
return true;
}
}
else if (a.is_le(f, x, y)) {
if (a.is_numeral(x, n))
assert_lower(y, n, false);
else if (a.is_numeral(y, n))
assert_upper(x, n, false);
else
return false;
return true;
}
else if (a.is_ge(f, x, y)) {
if (a.is_numeral(x, n))
assert_upper(y, n, false);
else if (a.is_numeral(y, n))
assert_lower(x, n, false);
else
return false;
return true;
}
else if (m.is_not(f, f)) {
if (a.is_le(f, x, y)) {
if (a.is_numeral(x, n))
assert_upper(y, n, true);
else if (a.is_numeral(y, n))
assert_lower(x, n, true);
else
return false;
return true;
}
else if (a.is_ge(f, x, y)) {
if (a.is_numeral(x, n))
assert_lower(y, n, true);
else if (a.is_numeral(y, n))
assert_upper(x, n, true);
else
return false;
return true;
}
}
return false;
}
void bound_simplifier::tighten_bound(dependent_expr const& de) {
if (de.pr())
return;
if (de.dep())
return;
rational n, k;
expr* x, *y, *f = de.fml();
expr* z, * u, * v, * w;
bool strict;
if (a.is_le(f, x, y)) {
// x <= (x + k) mod N && x >= 0 -> x + k < N
if (a.is_mod(y, z, u) && a.is_numeral(u, n) && has_lower(x, k, strict) && k >= 0 && is_offset(z, x, k) && k > 0 && k < n)
assert_upper(x, n - k, true);
// x <= (x + y) mod N && x >= 0 && 0 <= y < N => x + y < N
if (a.is_mod(y, z, u) && a.is_numeral(u, n) && n > 0) {
assert_upper(x, n, true);
if (has_lower(x, k, strict) && k >= 0 && a.is_add(z, v, w)) {
if (x == v && has_upper(w, k, strict) && k < n)
assert_upper(z, n, true);
if (x == w && has_upper(v, k, strict) && k < n)
assert_upper(z, n, true);
}
}
}
// x != k, k <= x -> k < x
if (m.is_not(f, f) && m.is_eq(f, x, y)) {
if (a.is_numeral(x))
std::swap(x, y);
if (a.is_numeral(y, n)) {
scoped_dep_interval i(m_interval);
get_bounds(x, i);
scoped_mpq k(nm);
if (!i.m().lower_is_inf(i) && !i.m().lower_is_open(i) && i.m().lower(i) == n)
assert_lower(x, n, true);
else if (!i.m().upper_is_inf(i) && !i.m().upper_is_open(i) && i.m().upper(i) == n)
assert_upper(x, n, true);
}
}
}
void bound_simplifier::assert_upper(expr* x, rational const& n, bool strict) {
scoped_mpq c(nm);
nm.set(c, n.to_mpq());
bp.assert_upper(to_var(x), c, strict);
}
void bound_simplifier::assert_lower(expr* x, rational const& n, bool strict) {
scoped_mpq c(nm);
nm.set(c, n.to_mpq());
bp.assert_lower(to_var(x), c, strict);
}
bool bound_simplifier::has_lower(expr* x, rational& n, bool& strict) {
scoped_dep_interval i(m_interval);
get_bounds(x, i);
if (m_interval.lower_is_inf(i))
return false;
strict = m_interval.lower_is_open(i);
n = m_interval.lower(i);
return true;
}
bool bound_simplifier::has_upper(expr* x, rational& n, bool& strict) {
scoped_dep_interval i(m_interval);
get_bounds(x, i);
if (m_interval.upper_is_inf(i))
return false;
strict = m_interval.upper_is_open(i);
n = m_interval.upper(i);
return true;
}
void bound_simplifier::get_bounds(expr* x, scoped_dep_interval& i) {
auto& im = m_interval;
im.reset(i);
scoped_dep_interval arg_i(im);
rational n;
if (a.is_numeral(x, n)) {
im.set_value(i, n);
return;
}
if (is_var(x)) {
unsigned v = to_var(x);
bool strict;
if (bp.has_upper(v)) {
im.set_upper(i, bp.upper(v, strict));
im.set_upper_is_inf(i, false);
im.set_upper_is_open(i, strict);
}
if (bp.has_lower(v)) {
im.set_lower(i, bp.lower(v, strict));
im.set_lower_is_inf(i, false);
im.set_lower_is_open(i, strict);
}
}
if (a.is_add(x)) {
scoped_dep_interval tmp_i(im), sum_i(im);
im.set_value(sum_i, rational::zero());
for (expr* arg : *to_app(x)) {
get_bounds(arg, arg_i);
im.add(sum_i, arg_i, tmp_i);
im.set<dep_intervals::without_deps>(sum_i, tmp_i);
}
im.intersect <dep_intervals::without_deps>(i, sum_i, i);
}
if (a.is_mul(x)) {
scoped_dep_interval tmp_i(im);
im.set_value(tmp_i, rational::one());
for (expr* arg : *to_app(x)) {
get_bounds(arg, arg_i);
im.mul(tmp_i, arg_i, tmp_i);
}
im.intersect <dep_intervals::without_deps>(i, tmp_i, i);
}
expr* y, * z, * u, * v;
if (a.is_mod(x, y, z) && a.is_numeral(z, n) && n > 0) {
scoped_dep_interval tmp_i(im);
im.set_lower_is_inf(tmp_i, false);
im.set_lower_is_open(tmp_i, false);
im.set_lower(tmp_i, mpq(0));
im.set_upper_is_inf(tmp_i, false);
im.set_upper_is_open(tmp_i, false);
im.set_upper(tmp_i, n - 1);
im.intersect <dep_intervals::without_deps>(i, tmp_i, i);
}
// x = y*(u div y), y > 0 -> x <= u
if (a.is_mul(x, y, z) && a.is_idiv(z, u, v) && v == y) {
scoped_dep_interval iy(im), iu(im), tmp_i(im);
get_bounds(y, iy);
get_bounds(u, iu);
if (!im.lower_is_inf(iy) && im.lower(iy) > 0 &&
!im.upper_is_inf(iu) && im.upper(iu) >= 0) {
im.set_upper_is_inf(tmp_i, false);
im.set_upper_is_open(tmp_i, im.upper_is_open(iu));
im.set_upper(tmp_i, im.upper(iu));
im.intersect<dep_intervals::without_deps>(i, tmp_i, i);
}
}
// x = y div z, z > 0 => x <= y
if (a.is_idiv(x, y, z)) {
scoped_dep_interval iy(im), iz(im), tmp_i(im);
get_bounds(y, iy);
get_bounds(z, iz);
if (!im.lower_is_inf(iz) && im.lower(iz) > 0 &&
!im.upper_is_inf(iy) && im.upper(iy) >= 0) {
im.set_upper_is_inf(tmp_i, false);
im.set_upper_is_open(tmp_i, im.upper_is_open(iy));
im.set_upper(tmp_i, im.upper(iy));
im.set_lower_is_inf(tmp_i, false);
im.set_lower_is_open(tmp_i, false); // TODO - could be refined
im.set_lower(tmp_i, rational::zero());
im.intersect<dep_intervals::without_deps>(i, tmp_i, i);
}
}
if (a.is_div(x, y, z)) {
scoped_dep_interval iy(im), iz(im), tmp_i(im);
get_bounds(y, iy);
get_bounds(z, iz);
im.div<dep_intervals::without_deps>(iy, iz, tmp_i);
im.intersect<dep_intervals::without_deps>(i, tmp_i, i);
}
}
void bound_simplifier::expr2linear_pol(expr* t, mpq_buffer& as, var_buffer& xs) {
scoped_mpq c_mpq_val(nm);
if (a.is_add(t)) {
rational c_val;
for (expr* mon : *to_app(t)) {
expr* c, * x;
if (a.is_mul(mon, c, x) && a.is_numeral(c, c_val)) {
nm.set(c_mpq_val, c_val.to_mpq());
as.push_back(c_mpq_val);
xs.push_back(to_var(x));
}
else {
as.push_back(mpq(1));
xs.push_back(to_var(mon));
}
}
}
else {
as.push_back(mpq(1));
xs.push_back(to_var(t));
}
}
bool bound_simplifier::lower_subsumed(expr* p, mpq const& k, bool strict) {
if (!a.is_add(p))
return false;
m_num_buffer.reset();
m_var_buffer.reset();
expr2linear_pol(p, m_num_buffer, m_var_buffer);
scoped_mpq implied_k(nm);
bool implied_strict;
return
bp.lower(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) &&
(nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
}
bool bound_simplifier::upper_subsumed(expr* p, mpq const& k, bool strict) {
if (!a.is_add(p))
return false;
m_num_buffer.reset();
m_var_buffer.reset();
expr2linear_pol(p, m_num_buffer, m_var_buffer);
scoped_mpq implied_k(nm);
bool implied_strict;
return
bp.upper(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) &&
(nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
}
void bound_simplifier::restore_bounds() {
scoped_mpq l(nm), u(nm);
bool strict_l, strict_u, has_l, has_u;
unsigned ts;
unsigned sz = m_var2expr.size();
rw rw(*this);
auto add = [&](expr* fml) {
expr_ref tmp(fml, m);
rw(tmp, tmp);
m_rewriter(tmp);
m_fmls.add(dependent_expr(m, tmp, nullptr, nullptr));
};
for (unsigned x = 0; x < sz; x++) {
expr* p = m_var2expr.get(x);
has_l = bp.lower(x, l, strict_l, ts);
has_u = bp.upper(x, u, strict_u, ts);
if (!has_l && !has_u)
continue;
if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) {
// l <= p <= l --> p = l
add(m.mk_eq(p, a.mk_numeral(rational(l), a.is_int(p))));
continue;
}
if (has_l && !lower_subsumed(p, l, strict_l)) {
if (strict_l)
add(m.mk_not(a.mk_le(p, a.mk_numeral(rational(l), a.is_int(p)))));
else
add(a.mk_ge(p, a.mk_numeral(rational(l), a.is_int(p))));
}
if (has_u && !upper_subsumed(p, u, strict_u)) {
if (strict_u)
add(m.mk_not(a.mk_ge(p, a.mk_numeral(rational(u), a.is_int(p)))));
else
add(a.mk_le(p, a.mk_numeral(rational(u), a.is_int(p))));
}
}
}
void bound_simplifier::reset() {
bp.reset();
m_var2expr.reset();
m_expr2var.reset();
m_trail.reset();
}
#if 0
void find_ite_bounds(expr* root) {
TRACE("find_ite_bounds_bug", display_bounds(tout););
expr* n = root;
expr* target = nullptr;
expr* c, * t, * e;
expr* x, * y;
bool has_l, has_u;
mpq l_min, u_max;
bool l_strict, u_strict;
mpq curr;
bool curr_strict;
while (true) {
TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";);
if (m.is_ite(n, c, t, e)) {
if (is_x_minus_y_eq_0(t, x, y))
n = e;
else if (is_x_minus_y_eq_0(e, x, y))
n = t;
else
break;
}
else if (is_x_minus_y_eq_0(n, x, y)) {
n = nullptr;
}
else {
break;
}
TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n";
if (target) {
tout << "target: " << mk_ismt2_pp(target, m) << "\n";
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n";
});
if (is_unbounded(y))
std::swap(x, y);
if (!is_unbounded(x)) {
TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";);
break;
}
if (target == nullptr) {
target = x;
if (lower(y, curr, curr_strict)) {
has_l = true;
nm.set(l_min, curr);
l_strict = curr_strict;
}
else {
has_l = false;
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
}
if (upper(y, curr, curr_strict)) {
has_u = true;
nm.set(u_max, curr);
u_strict = curr_strict;
}
else {
has_u = false;
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
}
}
else if (target == x) {
if (has_l) {
if (lower(y, curr, curr_strict)) {
if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) {
nm.set(l_min, curr);
l_strict = curr_strict;
}
}
else {
has_l = false;
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
}
}
if (has_u) {
if (upper(y, curr, curr_strict)) {
if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) {
nm.set(u_max, curr);
u_strict = curr_strict;
}
}
else {
has_u = false;
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
}
}
}
else {
break;
}
if (!has_l && !has_u)
break;
if (n == nullptr) {
TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n";
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n";
tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n";
tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";);
a_var x = mk_var(target);
if (has_l)
bp.assert_lower(x, l_min, l_strict);
if (has_u)
bp.assert_upper(x, u_max, u_strict);
break;
}
}
nm.del(l_min);
nm.del(u_max);
nm.del(curr);
}
void find_ite_bounds() {
unsigned sz = m_new_goal->size();
for (unsigned i = 0; i < sz; i++) {
expr* f = m_new_goal->form(i);
if (m.is_ite(f))
find_ite_bounds(to_app(f));
}
bp.propagate();
TRACE("find_ite_bounds", display_bounds(tout););
}
#endif

View file

@ -0,0 +1,136 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bound_simplifier.h
Author:
Nikolaj Bjorner (nbjorner) 2023-01-22
Description:
Collects bounds of sub-expressions and uses them to simplify modulus
expressions.
propagate_ineqs_tactic handles other propagations with bounds.
--*/
#pragma once
#include "ast/arith_decl_plugin.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/simplifiers/bound_propagator.h"
#include "math/interval/dep_intervals.h"
class bound_simplifier : public dependent_expr_simplifier {
typedef bound_propagator::var a_var;
typedef numeral_buffer<mpq, unsynch_mpq_manager> mpq_buffer;
typedef svector<a_var> var_buffer;
arith_util a;
params_ref m_params;
th_rewriter m_rewriter;
unsynch_mpq_manager nm;
small_object_allocator m_alloc;
bound_propagator bp;
dep_intervals m_interval;
ptr_vector<expr> m_var2expr;
unsigned_vector m_expr2var;
expr_ref_vector m_trail;
mpq_buffer m_num_buffer;
var_buffer m_var_buffer;
unsigned m_num_reduced = 0;
struct rw_cfg;
struct rw;
bool insert_bound(dependent_expr const& de);
void tighten_bound(dependent_expr const& de);
void reset();
expr* to_expr(unsigned v) const {
return m_var2expr.get(v, nullptr);
}
bool is_var(expr* e) const {
return UINT_MAX != m_expr2var.get(e->get_id(), UINT_MAX);
}
unsigned to_var(expr* e) {
unsigned v = m_expr2var.get(e->get_id(), UINT_MAX);
if (v == UINT_MAX) {
v = m_var2expr.size();
expr* core_e = e;
a.is_to_real(e, core_e);
bp.mk_var(v, a.is_int(core_e));
m_expr2var.setx(e->get_id(), v, UINT_MAX);
if (e != core_e)
m_expr2var.setx(core_e->get_id(), v, UINT_MAX);
m_var2expr.push_back(core_e);
m_trail.push_back(e);
}
return v;
}
br_status reduce_app(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result, proof_ref& pr);
void assert_lower(expr* x, rational const& n, bool strict);
void assert_upper(expr* x, rational const& n, bool strict);
bool has_upper(expr* x, rational& n, bool& strict);
bool has_lower(expr* x, rational& n, bool& strict);
void get_bounds(expr* x, scoped_dep_interval&);
void expr2linear_pol(expr* t, mpq_buffer& as, var_buffer& xs);
bool lower_subsumed(expr* p, mpq const& k, bool strict);
bool upper_subsumed(expr* p, mpq const& k, bool strict);
void restore_bounds();
// e = x + offset
bool is_offset(expr* e, expr* x, rational& offset);
public:
bound_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
a(m),
m_rewriter(m),
bp(nm, m_alloc, p),
m_interval(m.limit()),
m_trail(m),
m_num_buffer(nm) {
updt_params(p);
}
char const* name() const override { return "propagate-ineqs"; }
bool supports_proofs() const override { return false; }
void reduce() override;
void updt_params(params_ref const& p) override {
m_params.append(p);
bp.updt_params(m_params);
}
void collect_param_descrs(param_descrs & r) override {
bound_propagator::get_param_descrs(r);
}
void collect_statistics(statistics& st) const override {
st.update("bound-propagations", bp.get_num_propagations());
st.update("bound-false-alarms", bp.get_num_false_alarms());
st.update("bound-simplifications", m_num_reduced);
}
void reset_statistics() override {
m_num_reduced = 0;
bp.reset_statistics();
}
};

View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bv_bounds_simplifier.h
Author:
Nikolaj Bjorner (nbjorner) 2023-01-27
--*/
#include "ast/simplifiers/bv_bounds_simplifier.h"
#include "ast/simplifiers/dominator_simplifier.h"
#include "ast/rewriter/bv_bounds_base.h"
#include "ast/rewriter/dom_simplifier.h"
class dom_bv_bounds_simplifier : public dom_simplifier, public bv::bv_bounds_base {
params_ref m_params;
public:
dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv_bounds_base(m), m_params(p) {
updt_params(p);
}
~dom_bv_bounds_simplifier() override {
}
void updt_params(params_ref const & p) override {
m_propagate_eq = p.get_bool("propagate_eq", false);
}
void collect_param_descrs(param_descrs& r) override {
r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false");
}
bool assert_expr(expr * t, bool sign) override {
return assert_expr_core(t, sign);
}
void operator()(expr_ref& r) override {
expr_ref result(m);
simplify_core(r, result);
if (result)
r = result;
}
void pop(unsigned num_scopes) override {
pop_core(num_scopes);
}
dom_simplifier * translate(ast_manager & m) override {
return alloc(dom_bv_bounds_simplifier, m, m_params);
}
unsigned scope_level() const override {
return m_scopes.size();
}
};
dependent_expr_simplifier* mk_bv_bounds_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& s) {
return alloc(dominator_simplifier, m, s, alloc(dom_bv_bounds_simplifier, m, p), p);
}

View file

@ -0,0 +1,18 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bv_bounds_simplifier.h
Author:
Nikolaj Bjorner (nbjorner) 2023-01-27
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
dependent_expr_simplifier * mk_bv_bounds_simplifier(ast_manager & m, params_ref const & p, dependent_expr_state& fmls);

View file

@ -0,0 +1,43 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bv_elim.h
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/rewriter/bv_elim.h"
namespace bv {
class elim_simplifier : public dependent_expr_simplifier {
bv_elim_rw m_rewriter;
public:
elim_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
m_rewriter(m) {
}
char const* name() const override { return "bv-elim"; }
void reduce() override {
expr_ref r(m);
for (unsigned idx : indices()) {
auto const& d = m_fmls[idx];
if (!has_quantifiers(d.fml()))
continue;
m_rewriter(d.fml(), r);
m_fmls.update(idx, dependent_expr(m, r, nullptr, d.dep()));
}
}
};
}

Some files were not shown because too many files have changed in this diff Show more