mirror of
https://github.com/Z3Prover/z3
synced 2026-02-21 15:57:35 +00:00
Merge branch 'master' into polysat
This commit is contained in:
commit
20b5455d08
669 changed files with 26145 additions and 20652 deletions
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ class ll_printer {
|
|||
default:
|
||||
display_child_ref(n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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()); }
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
12
src/ast/converters/CMakeLists.txt
Normal file
12
src/ast/converters/CMakeLists.txt
Normal 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
|
||||
)
|
||||
124
src/ast/converters/converter.h
Normal file
124
src/ast/converters/converter.h
Normal 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
35
src/ast/converters/equiv_proof_converter.cpp
Normal file
35
src/ast/converters/equiv_proof_converter.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
49
src/ast/converters/equiv_proof_converter.h
Normal file
49
src/ast/converters/equiv_proof_converter.h
Normal 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 {}
|
||||
};
|
||||
|
||||
858
src/ast/converters/expr_inverter.cpp
Normal file
858
src/ast/converters/expr_inverter.cpp
Normal 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);
|
||||
}
|
||||
60
src/ast/converters/expr_inverter.h
Normal file
60
src/ast/converters/expr_inverter.h
Normal 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; }
|
||||
};
|
||||
212
src/ast/converters/generic_model_converter.cpp
Normal file
212
src/ast/converters/generic_model_converter.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
74
src/ast/converters/generic_model_converter.h
Normal file
74
src/ast/converters/generic_model_converter.h
Normal 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;
|
||||
|
||||
228
src/ast/converters/horn_subsume_model_converter.cpp
Normal file
228
src/ast/converters/horn_subsume_model_converter.cpp
Normal 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;
|
||||
}
|
||||
|
||||
84
src/ast/converters/horn_subsume_model_converter.h
Normal file
84
src/ast/converters/horn_subsume_model_converter.h
Normal 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(); }
|
||||
|
||||
};
|
||||
|
||||
201
src/ast/converters/model_converter.cpp
Normal file
201
src/ast/converters/model_converter.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
119
src/ast/converters/model_converter.h
Normal file
119
src/ast/converters/model_converter.h
Normal 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);
|
||||
|
||||
|
||||
100
src/ast/converters/proof_converter.cpp
Normal file
100
src/ast/converters/proof_converter.cpp
Normal 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());
|
||||
}
|
||||
43
src/ast/converters/proof_converter.h
Normal file
43
src/ast/converters/proof_converter.h
Normal 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);
|
||||
|
||||
83
src/ast/converters/replace_proof_converter.cpp
Normal file
83
src/ast/converters/replace_proof_converter.cpp
Normal 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;
|
||||
}
|
||||
|
||||
48
src/ast/converters/replace_proof_converter.h
Normal file
48
src/ast/converters/replace_proof_converter.h
Normal 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 {}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
|
|
|||
|
|
@ -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))) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
349
src/ast/rewriter/bv_bounds_base.h
Normal file
349
src/ast/rewriter/bv_bounds_base.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
325
src/ast/rewriter/dom_simplifier.cpp
Normal file
325
src/ast/rewriter/dom_simplifier.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
86
src/ast/rewriter/dom_simplifier.h
Normal file
86
src/ast/rewriter/dom_simplifier.h
Normal 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);
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
160
src/ast/rewriter/macro_replacer.cpp
Normal file
160
src/ast/rewriter/macro_replacer.cpp
Normal 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;
|
||||
}
|
||||
45
src/ast/rewriter/macro_replacer.h
Normal file
45
src/ast/rewriter/macro_replacer.h
Normal 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);
|
||||
};
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
41
src/ast/simplifiers/CMakeLists.txt
Normal file
41
src/ast/simplifiers/CMakeLists.txt
Normal 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
|
||||
)
|
||||
47
src/ast/simplifiers/bit2int.h
Normal file
47
src/ast/simplifiers/bit2int.h
Normal 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)")
|
||||
*/
|
||||
76
src/ast/simplifiers/bit_blaster.cpp
Normal file
76
src/ast/simplifiers/bit_blaster.cpp
Normal 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);
|
||||
}
|
||||
54
src/ast/simplifiers/bit_blaster.h
Normal file
54
src/ast/simplifiers/bit_blaster.h
Normal 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)")
|
||||
*/
|
||||
292
src/ast/simplifiers/bound_manager.cpp
Normal file
292
src/ast/simplifiers/bound_manager.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
111
src/ast/simplifiers/bound_manager.h
Normal file
111
src/ast/simplifiers/bound_manager.h
Normal 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;
|
||||
};
|
||||
|
||||
942
src/ast/simplifiers/bound_propagator.cpp
Normal file
942
src/ast/simplifiers/bound_propagator.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
263
src/ast/simplifiers/bound_propagator.h
Normal file
263
src/ast/simplifiers/bound_propagator.h
Normal 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; }
|
||||
};
|
||||
|
||||
596
src/ast/simplifiers/bound_simplifier.cpp
Normal file
596
src/ast/simplifiers/bound_simplifier.cpp
Normal 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
|
||||
136
src/ast/simplifiers/bound_simplifier.h
Normal file
136
src/ast/simplifiers/bound_simplifier.h
Normal 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();
|
||||
}
|
||||
};
|
||||
|
||||
65
src/ast/simplifiers/bv_bounds_simplifier.cpp
Normal file
65
src/ast/simplifiers/bv_bounds_simplifier.cpp
Normal 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);
|
||||
}
|
||||
18
src/ast/simplifiers/bv_bounds_simplifier.h
Normal file
18
src/ast/simplifiers/bv_bounds_simplifier.h
Normal 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);
|
||||
43
src/ast/simplifiers/bv_elim.h
Normal file
43
src/ast/simplifiers/bv_elim.h
Normal 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
Loading…
Add table
Add a link
Reference in a new issue