3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

introduce notion of beta redex to deal with lambdas in non-extensional positions

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2022-06-10 17:35:01 -07:00
parent b9b5377c69
commit 8efa3c8ade
10 changed files with 65 additions and 51 deletions

View file

@ -1677,6 +1677,7 @@ quantifier* ast_manager::is_lambda_def(func_decl* f) {
return nullptr;
}
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
SASSERT(m_plugins.get(id, 0) == 0);
m_plugins.setx(id, plugin, 0);

View file

@ -1618,7 +1618,7 @@ public:
bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; }
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()); }
symbol const& lambda_def_qid() const { return m_lambda_def; }

View file

@ -3737,15 +3737,12 @@ namespace smt {
reset_model();
if (m_last_search_failure != OK) {
if (m_last_search_failure != OK)
return false;
}
if (status == l_false) {
if (status == l_false)
return false;
}
if (status == l_true && !m_qmanager->has_quantifiers() && m_lambdas.empty()) {
if (status == l_true && !m_qmanager->has_quantifiers() && !has_lambda())
return false;
}
if (status == l_true && m_qmanager->has_quantifiers()) {
// possible outcomes DONE l_true, DONE l_undef, CONTINUE
mk_proto_model();
@ -3766,7 +3763,7 @@ namespace smt {
break;
}
}
if (status == l_true && !m_lambdas.empty()) {
if (status == l_true && has_lambda()) {
m_last_search_failure = LAMBDAS;
status = l_undef;
return false;
@ -4010,7 +4007,7 @@ namespace smt {
TRACE("final_check_step", tout << "RESULT final_check: " << result << "\n";);
if (result == FC_GIVEUP && f != OK)
m_last_search_failure = f;
if (result == FC_DONE && !m_lambdas.empty()) {
if (result == FC_DONE && has_lambda()) {
m_last_search_failure = LAMBDAS;
result = FC_GIVEUP;
}
@ -4468,9 +4465,8 @@ namespace smt {
return false;
}
case 1: {
if (m_qmanager->is_shared(n)) {
if (m_qmanager->is_shared(n) && !m.is_lambda_def(n->get_expr()) && !m_lambdas.contains(n))
return true;
}
// the variable is shared if the equivalence class of n
// contains a parent application.
@ -4482,6 +4478,8 @@ namespace smt {
app* p = parent->get_expr();
family_id fid = p->get_family_id();
if (fid != th_id && fid != m.get_basic_family_id()) {
if (is_beta_redex(parent, n))
continue;
TRACE("is_shared", tout << enode_pp(n, *this)
<< "\nis shared because of:\n"
<< enode_pp(parent, *this) << "\n";);
@ -4522,6 +4520,12 @@ namespace smt {
}
}
bool context::is_beta_redex(enode* p, enode* n) const {
family_id th_id = p->get_expr()->get_family_id();
theory * th = get_theory(th_id);
return th && th->is_beta_redex(p, n);
}
bool context::get_value(enode * n, expr_ref & value) {
sort * s = n->get_sort();
family_id fid = s->get_family_id();

View file

@ -773,7 +773,12 @@ namespace smt {
void internalize_quantifier(quantifier * q, bool gate_ctx);
obj_hashtable<quantifier> m_lambdas, m_non_lambdas;
obj_map<enode, quantifier*> m_lambdas;
bool has_lambda();
bool is_beta_redex(enode* p, enode* n) const;
void internalize_lambda(quantifier * q);
void internalize_formula_core(app * n, bool gate_ctx);
@ -783,8 +788,6 @@ namespace smt {
friend class set_enode_flag_trail;
public:
void add_non_lambda(quantifier* q);
void set_enode_flag(bool_var v, bool is_new_var);

View file

@ -578,20 +578,19 @@ namespace smt {
m_qmanager->add(q, generation);
}
void context::internalize_lambda(quantifier * q) {
TRACE("internalize_quantifier", tout << mk_pp(q, m) << "\n";);
SASSERT(is_lambda(q));
if (e_internalized(q)) {
if (e_internalized(q))
return;
}
app_ref lam_name(m.mk_fresh_const("lambda", q->get_sort()), m);
app_ref eq(m), lam_app(m);
expr_ref_vector vars(m);
vars.push_back(lam_name);
unsigned sz = q->get_num_decls();
for (unsigned i = 0; i < sz; ++i) {
for (unsigned i = 0; i < sz; ++i)
vars.push_back(m.mk_var(sz - i - 1, q->get_decl_sort(i)));
}
array_util autil(m);
lam_app = autil.mk_select(vars.size(), vars.data());
eq = m.mk_eq(lam_app, q->get_expr());
@ -599,28 +598,28 @@ namespace smt {
expr * patterns[1] = { m.mk_pattern(lam_app) };
fa = m.mk_forall(sz, q->get_decl_sorts(), q->get_decl_names(), eq, 0, m.lambda_def_qid(), symbol::null, 1, patterns);
internalize_quantifier(fa, true);
if (!e_internalized(lam_name)) internalize_uninterpreted(lam_name);
m_app2enode.setx(q->get_id(), get_enode(lam_name), nullptr);
if (!e_internalized(lam_name))
internalize_uninterpreted(lam_name);
enode* lam_node = get_enode(lam_name);
push_trail(insert_obj_map<enode, quantifier*>(m_lambdas, lam_node));
m_lambdas.insert(lam_node, q);
m_app2enode.setx(q->get_id(), lam_node, nullptr);
m_l_internalized_stack.push_back(q);
m_trail_stack.push_back(&m_mk_lambda_trail);
bool_var bv = get_bool_var(fa);
assign(literal(bv, false), nullptr);
mark_as_relevant(bv);
if (m_non_lambdas.contains(q))
return;
push_trail(insert_obj_trail<quantifier>(m_lambdas, q));
m_lambdas.insert(q);
}
void context::add_non_lambda(quantifier* q) {
if (m_non_lambdas.contains(q))
return;
m_non_lambdas.insert(q);
push_trail(insert_obj_trail<quantifier>(m_non_lambdas, q));
if (m_lambdas.contains(q)) {
m_lambdas.remove(q);
push_trail(remove_obj_trail<quantifier>(m_lambdas, q));
bool context::has_lambda() {
for (auto const & [n, q] : m_lambdas) {
if (n->get_class_size() != 1)
return true;
for (enode* p : enode::parents(n))
if (!is_beta_redex(p, n))
return true;
}
return false;
}
/**

View file

@ -321,6 +321,13 @@ namespace smt {
virtual bool is_shared(theory_var v) const {
return false;
}
/**
\brief Determine if node \c n under parent \c p is in a beta redex position.
*/
virtual bool is_beta_redex(enode* p, enode* n) const {
return false;
}
/**
\brief Return true if the theory has something to propagate

View file

@ -473,6 +473,15 @@ namespace smt {
return false;
}
bool theory_array_base::is_beta_redex(enode* p, enode* n) const {
if (is_select(p))
return p->get_arg(0)->get_root() == n->get_root();
if (is_map(p))
return true;
return false;
}
bool theory_array_base::is_select_arg(enode* r) {
for (enode* n : r->get_parents())
if (is_select(n))

View file

@ -142,6 +142,7 @@ namespace smt {
//
// --------------------------------------------------
bool is_shared(theory_var v) const override;
bool is_beta_redex(enode* p, enode* n) const override;
void collect_shared_vars(sbuffer<theory_var> & result);
unsigned mk_interface_eqs();

View file

@ -1457,8 +1457,6 @@ bool theory_seq::internalize_term(app* term) {
return true;
}
suppress_lambda(term);
if (m.is_bool(term) &&
(m_util.str.is_in_re(term) || m_sk.is_skolem(term))) {
bool_var bv = ctx.mk_bool_var(term);
@ -1486,26 +1484,18 @@ bool theory_seq::internalize_term(app* term) {
mk_var(e);
if (!ctx.relevancy()) {
relevant_eh(term);
}
}
return true;
}
void theory_seq::suppress_lambda(app* term) {
bool theory_seq::is_beta_redex(enode* p, enode* n) const {
expr* term = p->get_expr();
if (!m_util.str.is_map(term) && !m_util.str.is_mapi(term) &&
!m_util.str.is_foldl(term) && !m_util.str.is_foldli(term))
return;
expr* fn = to_app(term)->get_arg(0);
quantifier* q = nullptr;
if (is_lambda(fn))
q = to_quantifier(fn);
else if (is_app(fn))
q = m.is_lambda_def(to_app(fn)->get_decl());
if (q)
ctx.add_non_lambda(q);
return false;
if (p->get_arg(0)->get_root() == n->get_root())
return true;
return false;
}
@ -3292,7 +3282,7 @@ bool theory_seq::should_research(expr_ref_vector & unsat_core) {
k_min *= 2;
if (m_util.is_seq(s_min))
k_min = std::max(m_util.str.min_length(s_min), k_min);
IF_VERBOSE(1, verbose_stream() << "(smt.seq :increase-length " << mk_pp(s_min, m) << " " << k_min << ")\n");
IF_VERBOSE(1, verbose_stream() << "(smt.seq :increase-length " << mk_bounded_pp(s_min, m, 3) << " " << k_min << ")\n");
add_length_limit(s_min, k_min, false);
return true;
}

View file

@ -388,7 +388,6 @@ namespace smt {
bool internalize_atom(app* atom, bool) override;
bool internalize_term(app*) override;
void internalize_eq_eh(app * atom, bool_var v) override;
void suppress_lambda(app* term);
void new_eq_eh(theory_var, theory_var) override;
void new_diseq_eh(theory_var, theory_var) override;
void assign_eh(bool_var v, bool is_true) override;
@ -413,6 +412,7 @@ namespace smt {
void finalize_model(model_generator & mg) override;
void init_search_eh() override;
void validate_model(model& mdl) override;
bool is_beta_redex(enode* p, enode* n) const override;
void init_model(expr_ref_vector const& es);
app* get_ite_value(expr* a);