mirror of
https://github.com/Z3Prover/z3
synced 2025-04-12 04:03:39 +00:00
the utility that computes case analysis is brittle when the body of a function contains ite expressions that are not relevant to recursive unfolding. The fold-rec occurrences that get inserted to harness large case splits work against throttling case generation: they get treated as recursive functions that have to be guarded.
This commit is contained in:
parent
972d35204c
commit
81ebd52f61
|
@ -90,17 +90,21 @@ namespace recfun {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool def::contains_def(util& u, expr * e) {
|
bool util::contains_def(expr * e) {
|
||||||
struct def_find_p : public i_expr_pred {
|
struct def_find_p : public i_expr_pred {
|
||||||
util& u;
|
util& u;
|
||||||
def_find_p(util& u): u(u) {}
|
def_find_p(util& u): u(u) {}
|
||||||
bool operator()(expr* a) override { return is_app(a) && u.is_defined(to_app(a)->get_decl()); }
|
bool operator()(expr* a) override { return is_app(a) && u.is_defined(to_app(a)->get_decl()); }
|
||||||
};
|
};
|
||||||
def_find_p p(u);
|
def_find_p p(*this);
|
||||||
check_pred cp(p, m, false);
|
check_pred cp(p, m(), false);
|
||||||
return cp(e);
|
return cp(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool def::contains_def(util& u, expr * e) {
|
||||||
|
return u.contains_def(e);
|
||||||
|
}
|
||||||
|
|
||||||
// does `e` contain any `ite` construct?
|
// does `e` contain any `ite` construct?
|
||||||
// subject to the then/else branch using a recursive call,
|
// subject to the then/else branch using a recursive call,
|
||||||
// but the guard does not.
|
// but the guard does not.
|
||||||
|
@ -293,7 +297,11 @@ namespace recfun {
|
||||||
if (m.is_ite(e, cond, th, el) && contains_def(u, cond)) {
|
if (m.is_ite(e, cond, th, el) && contains_def(u, cond)) {
|
||||||
// skip
|
// skip
|
||||||
}
|
}
|
||||||
|
if (m.is_ite(e, cond, th, el) && !contains_def(u, th) && !contains_def(u, el)) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
else if (m.is_ite(e)) {
|
else if (m.is_ite(e)) {
|
||||||
|
verbose_stream() << "unfold " << mk_pp(e, m) << "\n";
|
||||||
// need to do a case split on `e`, forking the search space
|
// need to do a case split on `e`, forking the search space
|
||||||
b.to_split = st.cons_ite(to_app(e), b.to_split);
|
b.to_split = st.cons_ite(to_app(e), b.to_split);
|
||||||
}
|
}
|
||||||
|
@ -558,15 +566,16 @@ namespace recfun {
|
||||||
|
|
||||||
expr_ref plugin::redirect_ite(replace& subst, unsigned n, var * const* vars, expr * e) {
|
expr_ref plugin::redirect_ite(replace& subst, unsigned n, var * const* vars, expr * e) {
|
||||||
expr_ref result(e, m());
|
expr_ref result(e, m());
|
||||||
|
util u(m());
|
||||||
while (true) {
|
while (true) {
|
||||||
obj_map<expr, unsigned> scores;
|
obj_map<expr, unsigned> scores;
|
||||||
compute_scores(result, scores);
|
compute_scores(result, scores);
|
||||||
unsigned max_score = 0;
|
unsigned max_score = 0;
|
||||||
expr* max_expr = nullptr;
|
expr* max_expr = nullptr;
|
||||||
for (auto const& kv : scores) {
|
for (auto const& [k, v] : scores) {
|
||||||
if (m().is_ite(kv.m_key) && kv.m_value > max_score) {
|
if (m().is_ite(k) && v > max_score && u.contains_def(k)) {
|
||||||
max_expr = kv.m_key;
|
max_expr = k;
|
||||||
max_score = kv.m_value;
|
max_score = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (max_score <= 4)
|
if (max_score <= 4)
|
||||||
|
|
|
@ -251,6 +251,8 @@ namespace recfun {
|
||||||
bool is_macro(func_decl* f) { return is_defined(f) && get_def(f).is_macro(); }
|
bool is_macro(func_decl* f) { return is_defined(f) && get_def(f).is_macro(); }
|
||||||
bool is_num_rounds(expr * e) const { return is_app_of(e, m_fid, OP_NUM_ROUNDS); }
|
bool is_num_rounds(expr * e) const { return is_app_of(e, m_fid, OP_NUM_ROUNDS); }
|
||||||
bool owns_app(app * e) const { return e->get_family_id() == m_fid; }
|
bool owns_app(app * e) const { return e->get_family_id() == m_fid; }
|
||||||
|
bool contains_def(expr* e); // expression contains a def
|
||||||
|
|
||||||
|
|
||||||
//<! don't use native theory if recursive function declarations are not populated with defs
|
//<! 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_defs() const { return m_plugin->has_defs(); }
|
||||||
|
|
|
@ -271,7 +271,8 @@ namespace recfun {
|
||||||
SASSERT(!n || !n->is_attached_to(get_id()));
|
SASSERT(!n || !n->is_attached_to(get_id()));
|
||||||
if (!n)
|
if (!n)
|
||||||
n = mk_enode(e, false);
|
n = mk_enode(e, false);
|
||||||
SASSERT(!n->is_attached_to(get_id()));
|
if (n->is_attached_to(get_id()))
|
||||||
|
return true;
|
||||||
euf::theory_var w = mk_var(n);
|
euf::theory_var w = mk_var(n);
|
||||||
ctx.attach_th_var(n, this, w);
|
ctx.attach_th_var(n, this, w);
|
||||||
if (u().is_defined(e) && u().has_defs())
|
if (u().is_defined(e) && u().has_defs())
|
||||||
|
|
|
@ -342,7 +342,7 @@ namespace smt {
|
||||||
activate_guard(pred_applied, guards);
|
activate_guard(pred_applied, guards);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACEFN("assert core " << preds);
|
TRACEFN("assert cases " << preds);
|
||||||
// the disjunction of branches is asserted
|
// the disjunction of branches is asserted
|
||||||
// to close the available cases.
|
// to close the available cases.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue