From 4fcc4d07aea07ca7fda1f5eb8bfa87985a8c173a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 May 2019 19:05:40 +0200 Subject: [PATCH] fix #2277 fix #2221 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 2 + src/ast/ast.cpp | 43 ++++++++++++---- src/ast/ast.h | 7 +++ src/ast/normal_forms/defined_names.cpp | 8 +-- src/ast/normal_forms/nnf.cpp | 19 ++----- src/smt/smt_internalizer.cpp | 12 +++-- src/smt/smt_model_generator.cpp | 8 +-- src/smt/theory_array.cpp | 10 ++-- src/smt/theory_array.h | 1 + src/smt/theory_array_base.cpp | 71 +++++++++++++++++++++++++- src/smt/theory_array_base.h | 5 ++ src/smt/theory_lra.cpp | 2 +- 12 files changed, 139 insertions(+), 49 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index edbc11e7d..60d5afecd 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1573,12 +1573,14 @@ namespace z3 { else { r = Z3_mk_fpa_abs(a.ctx(), a); } + a.check_error(); return expr(a.ctx(), r); } inline expr sqrt(expr const & a, expr const& rm) { check_context(a, rm); assert(a.is_fpa()); Z3_ast r = Z3_mk_fpa_sqrt(a.ctx(), rm, a); + a.check_error(); return expr(a.ctx(), r); } inline expr operator~(expr const & a) { Z3_ast r = Z3_mk_bvnot(a.ctx(), a); return expr(a.ctx(), r); } diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index e358c5fe1..4b79edf7f 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -269,7 +269,8 @@ func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_pa m_pairwise(false), m_injective(false), m_idempotent(false), - m_skolem(false) { + m_skolem(false), + m_lambda(false) { } bool func_decl_info::operator==(func_decl_info const & info) const { @@ -281,20 +282,22 @@ bool func_decl_info::operator==(func_decl_info const & info) const { m_chainable == info.m_chainable && m_pairwise == info.m_pairwise && m_injective == info.m_injective && - m_skolem == info.m_skolem; + m_skolem == info.m_skolem && + m_lambda == info.m_lambda; } std::ostream & operator<<(std::ostream & out, func_decl_info const & info) { operator<<(out, static_cast(info)); - out << " :left-assoc " << info.is_left_associative(); - out << " :right-assoc " << info.is_right_associative(); - out << " :flat-associative " << info.is_flat_associative(); - out << " :commutative " << info.is_commutative(); - out << " :chainable " << info.is_chainable(); - out << " :pairwise " << info.is_pairwise(); - out << " :injective " << info.is_injective(); - out << " :idempotent " << info.is_idempotent(); - out << " :skolem " << info.is_skolem(); + if (info.is_left_associative()) out << " :left-assoc "; + if (info.is_right_associative()) out << " :right-assoc "; + if (info.is_flat_associative()) out << " :flat-associative "; + if (info.is_commutative()) out << " :commutative "; + if (info.is_chainable()) out << " :chainable "; + if (info.is_pairwise()) out << " :pairwise "; + if (info.is_injective()) out << " :injective "; + if (info.is_idempotent()) out << " :idempotent "; + if (info.is_skolem()) out << " :skolem "; + if (info.is_lambda()) out << " :lambda "; return out; } @@ -1713,6 +1716,20 @@ bool ast_manager::are_distinct(expr* a, expr* b) const { return false; } +void ast_manager::add_lambda_def(func_decl* f, quantifier* q) { + m_lambda_defs.insert(f, q); + f->get_info()->set_lambda(true); + inc_ref(q); +} + +quantifier* ast_manager::is_lambda_def(func_decl* f) { + if (f->get_info() && f->get_info()->is_lambda()) { + return m_lambda_defs[f]; + } + return nullptr; +} + + func_decl* ast_manager::get_rec_fun_decl(quantifier* q) const { SASSERT(is_rec_fun_def(q)); return to_app(to_app(q->get_pattern(0))->get_arg(0))->get_decl(); @@ -1901,6 +1918,10 @@ void ast_manager::delete_node(ast * n) { func_decl* f = to_func_decl(n); if (f->m_info != nullptr && !m_debug_ref_count) { func_decl_info * info = f->get_info(); + if (info->is_lambda()) { + push_dec_ref(m_lambda_defs[f]); + m_lambda_defs.remove(f); + } info->del_eh(*this); dealloc(info); } diff --git a/src/ast/ast.h b/src/ast/ast.h index 5ba3a900e..e654ef1d6 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -398,6 +398,7 @@ struct func_decl_info : public decl_info { bool m_injective:1; bool m_idempotent:1; bool m_skolem:1; + bool m_lambda:1; func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr); ~func_decl_info() {} @@ -412,6 +413,7 @@ struct func_decl_info : public decl_info { bool is_injective() const { return m_injective; } bool is_idempotent() const { return m_idempotent; } bool is_skolem() const { return m_skolem; } + bool is_lambda() const { return m_lambda; } void set_associative(bool flag = true) { m_left_assoc = flag; m_right_assoc = flag; } void set_left_associative(bool flag = true) { m_left_assoc = flag; } @@ -423,6 +425,7 @@ struct func_decl_info : public decl_info { void set_injective(bool flag = true) { m_injective = flag; } void set_idempotent(bool flag = true) { m_idempotent = flag; } void set_skolem(bool flag = true) { m_skolem = flag; } + void set_lambda(bool flag = true) { m_lambda = flag; } bool operator==(func_decl_info const & info) const; @@ -641,6 +644,7 @@ public: bool is_pairwise() const { return get_info() != nullptr && get_info()->is_pairwise(); } bool is_injective() const { return get_info() != nullptr && get_info()->is_injective(); } bool is_skolem() const { return get_info() != nullptr && get_info()->is_skolem(); } + bool is_lambda() const { return get_info() != nullptr && get_info()->is_lambda(); } bool is_idempotent() const { return get_info() != nullptr && get_info()->is_idempotent(); } unsigned get_arity() const { return m_arity; } sort * get_domain(unsigned idx) const { SASSERT(idx < get_arity()); return m_domain[idx]; } @@ -1522,6 +1526,7 @@ protected: family_id m_user_sort_family_id; family_id m_arith_family_id; ast_table m_ast_table; + obj_map m_lambda_defs; id_gen m_expr_id_gen; id_gen m_decl_id_gen; sort * m_bool_sort; @@ -1651,6 +1656,8 @@ public: bool is_rec_fun_def(quantifier* q) const { return q->get_qid() == m_rec_fun; } 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); func_decl* get_rec_fun_decl(quantifier* q) const; symbol const& rec_fun_qid() const { return m_rec_fun; } diff --git a/src/ast/normal_forms/defined_names.cpp b/src/ast/normal_forms/defined_names.cpp index 0dcdd9597..1a5ffb4da 100644 --- a/src/ast/normal_forms/defined_names.cpp +++ b/src/ast/normal_forms/defined_names.cpp @@ -122,9 +122,9 @@ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffe sort * range = m.get_sort(e); func_decl * new_skolem_decl = m.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.c_ptr(), range); app * n = m.mk_app(new_skolem_decl, new_args.size(), new_args.c_ptr()); - TRACE("mk_definition_bug", tout << "gen_name: " << mk_ismt2_pp(n, m) << "\n"; - for (unsigned i = 0; i < var_sorts.size(); i++) tout << mk_pp(var_sorts[i], m) << " "; - tout << "\n";); + if (is_lambda(e)) { + m.add_lambda_def(new_skolem_decl, to_quantifier(e)); + } return n; } @@ -203,7 +203,7 @@ void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var // NB. The pattern is incomplete. // consider store(a, i, v) == \lambda j . if i = j then v else a[j] // the instantiation rules for store(a, i, v) are: - // sotre(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] } + // store(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] } // The first pattern is not included. // TBD use a model-based scheme for exracting instantiations instead of // using multi-patterns. diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index c03ff1d0f..26ffc1709 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -27,7 +27,6 @@ Notes: #include "ast/act_cache.h" #include "ast/rewriter/var_subst.h" #include "ast/normal_forms/name_exprs.h" - #include "ast/ast_smt2_pp.h" /** @@ -754,8 +753,7 @@ struct nnf::imp { if (fr.m_i == 0) { fr.m_i = 1; if (is_lambda(q)) { - if (!visit(q->get_expr(), fr.m_pol, true)) - return false; + // skip } else if (is_forall(q) == fr.m_pol) { if (!visit(q->get_expr(), fr.m_pol, true)) @@ -769,19 +767,9 @@ struct nnf::imp { } if (is_lambda(q)) { - expr * new_expr = m_result_stack.back(); - quantifier * new_q = m.update_quantifier(q, new_expr); - proof * new_q_pr = nullptr; + m_result_stack.push_back(q); if (proofs_enabled()) { - // proof * new_expr_pr = m_result_pr_stack.back(); - new_q_pr = m.mk_rewrite(q, new_q); // TBD use new_expr_pr - } - - m_result_stack.pop_back(); - m_result_stack.push_back(new_q); - if (proofs_enabled()) { - m_result_pr_stack.pop_back(); - m_result_pr_stack.push_back(new_q_pr); + m_result_pr_stack.push_back(nullptr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; @@ -949,7 +937,6 @@ void nnf::get_param_descrs(param_descrs & r) { imp::get_param_descrs(r); } - void nnf::reset() { m_imp->reset(); } diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index eb2673da9..ac4f2e1b5 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -535,14 +535,14 @@ namespace smt { } void context::internalize_lambda(quantifier * q) { - UNREACHABLE(); - #if 0 + UNREACHABLE(); +#else TRACE("internalize_quantifier", tout << mk_pp(q, m_manager) << "\n";); SASSERT(is_lambda(q)); app_ref lam_name(m_manager.mk_fresh_const("lambda", m_manager.get_sort(q)), m_manager); enode * e = mk_enode(lam_name, true, false, false); - expr_ref eq(m_manager), lam_app(m_manager); + app_ref eq(m_manager), lam_app(m_manager); expr_ref_vector vars(m_manager); vars.push_back(lam_name); unsigned sz = q->get_num_decls(); @@ -553,9 +553,11 @@ namespace smt { lam_app = autil.mk_select(vars.size(), vars.c_ptr()); eq = m_manager.mk_eq(lam_app, q->get_expr()); quantifier_ref fa(m_manager); - expr * patterns[1] = { m_manager.mk_pattern(lam_name) }; + expr * patterns[1] = { m_manager.mk_pattern(lam_app) }; fa = m_manager.mk_forall(sz, q->get_decl_sorts(), q->get_decl_names(), eq, 0, m_manager.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); #endif } @@ -938,7 +940,7 @@ namespace smt { e->mark_as_interpreted(); TRACE("mk_var_bug", tout << "mk_enode: " << id << "\n";); TRACE("generation", tout << "mk_enode: " << id << " " << generation << "\n";); - m_app2enode.setx(id, e, 0); + m_app2enode.setx(id, e, nullptr); m_e_internalized_stack.push_back(n); m_trail_stack.push_back(&m_mk_enode_trail); m_enodes.push_back(e); diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 767b9e06e..43c89d074 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -272,9 +272,7 @@ namespace smt { } // traverse all enodes that are associated with fresh values... - unsigned sz = roots.size(); - for (unsigned i = 0; i < sz; i++) { - enode * r = roots[i]; + for (enode* r : roots) { model_value_proc * proc = root2proc[r]; SASSERT(proc); if (!proc->is_fresh()) @@ -282,9 +280,7 @@ namespace smt { process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources); } - sz = roots.size(); - for (unsigned i = 0; i < sz; i++) { - enode * r = roots[i]; + for (enode * r : roots) { process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources); } } diff --git a/src/smt/theory_array.cpp b/src/smt/theory_array.cpp index d9e0b1995..f3420df45 100644 --- a/src/smt/theory_array.cpp +++ b/src/smt/theory_array.cpp @@ -230,6 +230,7 @@ namespace smt { m_stats.m_num_extensionality++; } + bool theory_array::internalize_atom(app * atom, bool) { return internalize_term(atom); } @@ -297,12 +298,11 @@ namespace smt { void theory_array::new_eq_eh(theory_var v1, theory_var v2) { m_find.merge(v1, v2); -#if 0 - if (is_lambda(get_enode(v1)->get_owner()) || - is_lambda(get_enode(v2)->get_owner())) { - instantiate_extensionality(get_enode(v1), get_enode(v2)); + enode* n1 = get_enode(v1), *n2 = get_enode(v2); + if (n1->get_owner()->get_decl()->is_lambda() || + n2->get_owner()->get_decl()->is_lambda()) { + assert_congruent(n1, n2); } -#endif } void theory_array::new_diseq_eh(theory_var v1, theory_var v2) { diff --git a/src/smt/theory_array.h b/src/smt/theory_array.h index 26d59c7bf..187978012 100644 --- a/src/smt/theory_array.h +++ b/src/smt/theory_array.h @@ -88,6 +88,7 @@ namespace smt { bool instantiate_axiom2b(enode * select, enode * store); void instantiate_axiom1(enode * store); void instantiate_extensionality(enode * a1, enode * a2); + void instantiate_congruent(enode * a1, enode * a2); bool instantiate_axiom2b_for(theory_var v); virtual final_check_status assert_delayed_axioms(); diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index bb3ed9992..6d9969e8d 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -300,6 +300,20 @@ namespace smt { m_extensionality_todo.push_back(std::make_pair(n1, n2)); return true; } + + void theory_array_base::assert_congruent(enode * a1, enode * a2) { + TRACE("array", tout << "congruent: #" << a1->get_owner_id() << " #" << a2->get_owner_id() << "\n";); + SASSERT(is_array_sort(a1)); + SASSERT(is_array_sort(a2)); + context & ctx = get_context(); + if (a1->get_owner_id() > a2->get_owner_id()) + std::swap(a1, a2); + enode * nodes[2] = { a1, a2 }; + if (!ctx.add_fingerprint(this, 1, 2, nodes)) + return; // axiom was already instantiated + m_congruent_todo.push_back(std::make_pair(a1, a2)); + } + void theory_array_base::assert_extensionality_core(enode * n1, enode * n2) { app * e1 = n1->get_owner(); @@ -338,8 +352,59 @@ namespace smt { if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } + /** + \brief assert n1 = n2 => forall vars . (n1 vars) = (n2 vars) + */ + void theory_array_base::assert_congruent_core(enode * n1, enode * n2) { + app * e1 = n1->get_owner(); + app * e2 = n2->get_owner(); + context & ctx = get_context(); + ast_manager & m = get_manager(); + sort* s = m.get_sort(e1); + unsigned dimension = get_array_arity(s); + literal n1_eq_n2 = mk_eq(e1, e2, true); + ctx.mark_as_relevant(n1_eq_n2); + expr_ref_vector args1(m), args2(m); + expr_ref f1 = instantiate_lambda(e1); + expr_ref f2 = instantiate_lambda(e2); + args1.push_back(f1); + args2.push_back(f2); + svector names; + sort_ref_vector sorts(m); + for (unsigned i = 0; i < dimension; i++) { + sort * srt = get_array_domain(s, i); + sorts.push_back(srt); + names.push_back(symbol(i)); + expr * k = m.mk_var(dimension - i - 1, srt); + args1.push_back(k); + args2.push_back(k); + } + expr * sel1 = mk_select(dimension+1, args1.c_ptr()); + expr * sel2 = mk_select(dimension+1, args2.c_ptr()); + expr * eq = m.mk_eq(sel1, sel2); + expr_ref q(m.mk_forall(dimension, sorts.c_ptr(), names.c_ptr(), eq), m); + ctx.get_rewriter()(q); + if (!ctx.b_internalized(q)) { + ctx.internalize(q, true); + } + literal fa_eq = ctx.get_literal(q); + ctx.mark_as_relevant(fa_eq); + assert_axiom(~n1_eq_n2, fa_eq); + } + + expr_ref theory_array_base::instantiate_lambda(app* e) { + ast_manager& m = get_manager(); + quantifier * q = m.is_lambda_def(e->get_decl()); + expr_ref f(e, m); + if (q) { + var_subst sub(m, false); + f = sub(q, e->get_num_args(), e->get_args()); + } + return f; + } + bool theory_array_base::can_propagate() { - return !m_axiom1_todo.empty() || !m_axiom2_todo.empty() || !m_extensionality_todo.empty(); + return !m_axiom1_todo.empty() || !m_axiom2_todo.empty() || !m_extensionality_todo.empty() || !m_congruent_todo.empty(); } void theory_array_base::propagate() { @@ -352,7 +417,10 @@ namespace smt { m_axiom2_todo.reset(); for (unsigned i = 0; i < m_extensionality_todo.size(); i++) assert_extensionality_core(m_extensionality_todo[i].first, m_extensionality_todo[i].second); + for (unsigned i = 0; i < m_congruent_todo.size(); i++) + assert_congruent_core(m_congruent_todo[i].first, m_congruent_todo[i].second); m_extensionality_todo.reset(); + m_congruent_todo.reset(); } } @@ -522,6 +590,7 @@ namespace smt { m_axiom1_todo.reset(); m_axiom2_todo.reset(); m_extensionality_todo.reset(); + m_congruent_todo.reset(); } diff --git a/src/smt/theory_array_base.h b/src/smt/theory_array_base.h index bf35bdd1b..75de7d7e9 100644 --- a/src/smt/theory_array_base.h +++ b/src/smt/theory_array_base.h @@ -66,6 +66,7 @@ namespace smt { ptr_vector m_axiom1_todo; enode_pair_vector m_axiom2_todo; enode_pair_vector m_extensionality_todo; + enode_pair_vector m_congruent_todo; scoped_ptr m_bapa; void assert_axiom(unsigned num_lits, literal * lits); @@ -79,6 +80,10 @@ namespace smt { void assert_extensionality_core(enode * a1, enode * a2); bool assert_extensionality(enode * a1, enode * a2); + expr_ref instantiate_lambda(app* e); + void assert_congruent_core(enode * a1, enode * a2); + void assert_congruent(enode * a1, enode * a2); + // -------------------------------------------------- // Array sort -> extensionality skolems // diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 11a9fa6f7..77815ceda 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3429,7 +3429,7 @@ public: } app_ref mk_obj(theory_var v) { - lp::var_index vi = m_theory_var2var_index[v]; + lp::var_index vi = get_var_index(v); bool is_int = a.is_int(get_enode(v)->get_owner()); if (m_solver->is_term(vi)) { return mk_term(m_solver->get_term(vi), is_int);