diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index e63b50700..e26a59b6d 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -513,4 +513,39 @@ namespace recfun { } } + + + case_expansion::case_expansion(recfun::util& u, app * n) : + m_lhs(n, u.m()), m_def(nullptr), m_args(u.m()) { + SASSERT(u.is_defined(n)); + func_decl * d = n->get_decl(); + m_def = &u.get_def(d); + m_args.append(n->get_num_args(), n->get_args()); + } + + case_expansion::case_expansion(case_expansion const & from) + : m_lhs(from.m_lhs), + m_def(from.m_def), + m_args(from.m_args) {} + case_expansion::case_expansion(case_expansion && from) + : m_lhs(from.m_lhs), + m_def(from.m_def), + m_args(std::move(from.m_args)) {} + + std::ostream& case_expansion::display(std::ostream & out) const { + return out << "case_exp(" << m_lhs << ")"; + } + + std::ostream& body_expansion::display(std::ostream & out) const { + ast_manager& m = m_pred.m(); + out << "body_exp(" << m_cdef->get_decl()->get_name(); + for (auto* t : m_args) + out << " " << mk_pp(t, m); + return out << ")"; + } + + + } + + diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index dec589bd1..fad0fe23e 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -20,6 +20,7 @@ Revision History: #pragma once #include "ast/ast.h" +#include "ast/ast_pp.h" #include "util/obj_hashtable.h" namespace recfun { @@ -74,7 +75,7 @@ namespace recfun { public: func_decl* get_decl() const { return m_pred; } - app_ref apply_case_predicate(ptr_vector const & args) const { + app_ref apply_case_predicate(expr_ref_vector const & args) const { ast_manager& m = m_pred.get_manager(); return app_ref(m.mk_app(m_pred, args.size(), args.c_ptr()), m); } @@ -258,6 +259,10 @@ namespace recfun { return mk_fun_defined(d, args.size(), args.c_ptr()); } + app* mk_fun_defined(def const & d, expr_ref_vector const & args) { + return mk_fun_defined(d, args.size(), args.c_ptr()); + } + func_decl_ref_vector get_rec_funs() { return m_plugin->get_rec_funs(); } @@ -265,5 +270,50 @@ namespace recfun { app_ref mk_num_rounds_pred(unsigned d); }; + + + // one case-expansion of `f(t1...tn)` + struct case_expansion { + app_ref m_lhs; // the term to expand + recfun::def * m_def; + expr_ref_vector m_args; + case_expansion(recfun::util& u, app * n); + case_expansion(case_expansion const & from); + case_expansion(case_expansion && from); + std::ostream& display(std::ostream& out) const; + }; + + inline std::ostream& operator<<(std::ostream& out, case_expansion const & e) { + return e.display(out); + } + + // one body-expansion of `f(t1...tn)` using a `C_f_i(t1...tn)` + struct body_expansion { + app_ref m_pred; + recfun::case_def const * m_cdef; + expr_ref_vector m_args; + + body_expansion(recfun::util& u, app * n) : + m_pred(n, u.m()), m_cdef(nullptr), m_args(u.m()) { + m_cdef = &u.get_case_def(n); + m_args.append(n->get_num_args(), n->get_args()); + } + body_expansion(app_ref & pred, recfun::case_def const & d, expr_ref_vector & args) : + m_pred(pred), m_cdef(&d), m_args(args) {} + body_expansion(body_expansion const & from): + m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(from.m_args) {} + body_expansion(body_expansion && from) : + m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(std::move(from.m_args)) {} + + std::ostream& display(std::ostream& out) const; + }; + + inline std::ostream& operator<<(std::ostream& out, body_expansion const& e) { + return e.display(out); + } + + + } + diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index f499de627..9a9f895c9 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -387,4 +387,25 @@ namespace euf { r = m.mk_eq(e1, e2); return r; } + + unsigned solver::get_max_generation(expr* e) const { + unsigned g = 0; + expr_fast_mark1 mark; + m_todo.push_back(e); + while (!m_todo.empty()) { + e = m_todo.back(); + m_todo.pop_back(); + if (mark.is_marked(e)) + continue; + mark.mark(e); + euf::enode* n = m_egraph.find(e); + if (n) + g = std::max(g, n->generation()); + else if (is_app(e)) + for (expr* arg : *to_app(e)) + m_todo.push_back(arg); + } + return g; + } + } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 2f38c7638..aff9d6b55 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -96,6 +96,7 @@ namespace euf { user::solver* m_user_propagator{ nullptr }; th_solver* m_qsolver { nullptr }; unsigned m_generation { 0 }; + mutable ptr_vector m_todo; ptr_vector m_bool_var2expr; ptr_vector m_explain; @@ -228,6 +229,7 @@ namespace euf { s.m_generation = m_g; } }; + unsigned get_max_generation(expr* e) const; // accessors diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 90c74089c..bbaeb0ddf 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -213,7 +213,7 @@ namespace q { qlit.neg(); TRACE("q", tout << "project: " << proj << "\n";); ++m_stats.m_num_instantiations; - unsigned generation = m_qs.get_max_generation(proj); + unsigned generation = ctx.get_max_generation(proj); m_instantiations.push_back(instantiation_t(qlit, proj, generation)); } diff --git a/src/sat/smt/q_solver.cpp b/src/sat/smt/q_solver.cpp index c2139993e..7d6aa3fdc 100644 --- a/src/sat/smt/q_solver.cpp +++ b/src/sat/smt/q_solver.cpp @@ -207,26 +207,6 @@ namespace q { return val; } - unsigned solver::get_max_generation(expr* e) const { - unsigned g = 0; - expr_fast_mark1 mark; - m_todo.push_back(e); - while (!m_todo.empty()) { - e = m_todo.back(); - m_todo.pop_back(); - if (mark.is_marked(e)) - continue; - mark.mark(e); - euf::enode* n = ctx.get_egraph().find(e); - if (n) - g = std::max(g, n->generation()); - else if (is_app(e)) - for (expr* arg : *to_app(e)) - m_todo.push_back(arg); - } - return g; - } - expr_ref_vector const& solver::expand(quantifier* q) { m_expanded.reset(); if (is_forall(q)) diff --git a/src/sat/smt/q_solver.h b/src/sat/smt/q_solver.h index 6f1077dc9..9b0301775 100644 --- a/src/sat/smt/q_solver.h +++ b/src/sat/smt/q_solver.h @@ -46,7 +46,6 @@ namespace q { flat_table m_flat; sat::literal_vector m_universal; obj_map m_unit_table; - mutable ptr_vector m_todo; expr_ref_vector m_expanded; sat::literal instantiate(quantifier* q, bool negate, std::function& mk_var); @@ -85,6 +84,5 @@ namespace q { sat::literal_vector const& universal() const { return m_universal; } quantifier* flatten(quantifier* q); - unsigned get_max_generation(expr* e) const; }; } diff --git a/src/sat/smt/recfun_solver.cpp b/src/sat/smt/recfun_solver.cpp index 2a6626382..eec8ea3c8 100644 --- a/src/sat/smt/recfun_solver.cpp +++ b/src/sat/smt/recfun_solver.cpp @@ -15,10 +15,13 @@ Author: --*/ +#include "ast/rewriter/var_subst.h" #include "sat/smt/recfun_solver.h" #include "sat/smt/euf_solver.h" +#define TRACEFN(x) TRACE("recfun", tout << x << '\n';) + namespace recfun { @@ -29,11 +32,7 @@ namespace recfun { m_util(m_plugin.u()), m_disabled_guards(m), m_enabled_guards(m), - m_preds(m), - m_num_rounds(0), - m_q_case_expand(), - m_q_body_expand() { - m_num_rounds = 0; + m_preds(m) { } solver::~solver() { @@ -41,34 +40,140 @@ namespace recfun { } void solver::reset() { - reset_queues(); + m_propagation_queue.reset(); m_stats.reset(); m_disabled_guards.reset(); m_enabled_guards.reset(); - m_q_guards.reset(); + m_propagation_queue.reset(); for (auto & kv : m_guard2pending) dealloc(kv.m_value); m_guard2pending.reset(); } - void solver::reset_queues() { - for (auto* e : m_q_case_expand) { - dealloc(e); + expr_ref solver::apply_args(vars const & vars, expr_ref_vector const & args, expr * e) { + SASSERT(is_standard_order(vars)); + var_subst subst(m, true); + expr_ref new_body = subst(e, args); + ctx.get_rewriter()(new_body); + return new_body; + } + + /** + * For functions f(args) that are given as macros f(vs) = rhs + * + * 1. substitute `e.args` for `vs` into the macro rhs + * 2. add unit clause `f(args) = rhs` + */ + void solver::assert_macro_axiom(case_expansion & e) { + m_stats.m_macro_expansions++; + SASSERT(e.m_def->is_fun_macro()); + auto & vars = e.m_def->get_vars(); + auto lhs = e.m_lhs; + auto rhs = apply_args(vars, e.m_args, e.m_def->get_rhs()); + unsigned generation = std::max(ctx.get_max_generation(lhs), ctx.get_max_generation(rhs)); + euf::solver::scoped_generation _sgen(ctx, generation + 1); + auto eq = eq_internalize(lhs, rhs); + add_unit(eq); + } + + /** + * Add case axioms for every case expansion path. + * + * assert `p(args) <=> And(guards)` (with CNF on the fly) + * + * also body-expand paths that do not depend on any defined fun + */ + void solver::assert_case_axioms(case_expansion & e) { + SASSERT(e.m_def->is_fun_defined()); + // add case-axioms for all case-paths + // assert this was not defined before. + sat::literal_vector preds; + auto & vars = e.m_def->get_vars(); + + for (case_def const & c : e.m_def->get_cases()) { + // applied predicate to `args` + app_ref pred_applied = c.apply_case_predicate(e.m_args); + SASSERT(u().owns_app(pred_applied)); + preds.push_back(mk_literal(pred_applied)); + expr_ref_vector guards(m); + for (auto & g : c.get_guards()) + guards.push_back(apply_args(vars, e.m_args, g)); + if (c.is_immediate()) { + body_expansion be(pred_applied, c, e.m_args); + assert_body_axiom(be); + } + else if (!is_enabled_guard(pred_applied)) { + disable_guard(pred_applied, guards); + continue; + } + activate_guard(pred_applied, guards); } - m_q_case_expand.reset(); - for (auto* e : m_q_body_expand) { - dealloc(e); - } - m_q_body_expand.reset(); - m_q_clauses.clear(); + add_clause(preds); + } + + void solver::activate_guard(expr* pred_applied, expr_ref_vector const& guards) { + sat::literal_vector lguards; + for (expr* ga : guards) + lguards.push_back(mk_literal(ga)); + add_equiv_and(mk_literal(pred_applied), lguards); + } + + /** + * make clause `depth_limit => ~guard` + * the guard appears at a depth below the current cutoff. + */ + void solver::disable_guard(expr* guard, expr_ref_vector const& guards) { + expr_ref nguard(m.mk_not(guard), m); + if (is_disabled_guard(nguard)) + return; + SASSERT(!is_enabled_guard(nguard)); + sat::literal_vector c; + app_ref dlimit = m_util.mk_num_rounds_pred(m_num_rounds); + c.push_back(~mk_literal(dlimit)); + c.push_back(~mk_literal(guard)); + m_disabled_guards.push_back(nguard); + SASSERT(!m_guard2pending.contains(nguard)); + m_guard2pending.insert(nguard, alloc(expr_ref_vector, guards)); + TRACEFN("add clause\n" << c); + m_propagation_queue.push_back(alloc(propagation_item, c)); + } + + /** + * For a guarded definition guards => f(vars) = rhs + * and occurrence f(args) + * + * substitute `args` for `vars` in guards, and rhs + * add axiom guards[args/vars] => f(args) = rhs[args/vars] + * + */ + void solver::assert_body_axiom(body_expansion & e) { + recfun::def & d = *e.m_cdef->get_def(); + auto & vars = d.get_vars(); + auto & args = e.m_args; + SASSERT(is_standard_order(vars)); + sat::literal_vector clause; + for (auto & g : e.m_cdef->get_guards()) { + expr_ref guard = apply_args(vars, args, g); + if (m.is_false(guard)) + return; + if (m.is_true(guard)) + continue; + clause.push_back(~mk_literal(guard)); + } + expr_ref lhs(u().mk_fun_defined(d, args), m); + expr_ref rhs = apply_args(vars, args, e.m_cdef->get_rhs()); + clause.push_back(eq_internalize(lhs, rhs)); + add_clause(clause); } void solver::get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector& r, bool probing) { - + UNREACHABLE(); } void solver::asserted(sat::literal l) { - + expr* e = ctx.bool_var2expr(l.var()); + if (!l.sign() && u().is_case_pred(e)) + push_body_expand(e); } sat::check_result solver::check() { @@ -76,15 +181,18 @@ namespace recfun { } std::ostream& solver::display(std::ostream& out) const { - return out; + return out << "disabled guards:\n" << m_disabled_guards << "\n"; } std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { + UNREACHABLE(); return out; } void solver::collect_statistics(statistics& st) const { - + st.update("recfun macro expansion", m_stats.m_macro_expansions); + st.update("recfun case expansion", m_stats.m_case_expansions); + st.update("recfun body expansion", m_stats.m_body_expansions); } euf::th_solver* solver::clone(euf::solver& ctx) { @@ -92,13 +200,107 @@ namespace recfun { } bool solver::unit_propagate() { - return false; + if (m_qhead == m_propagation_queue.size()) + return false; + ctx.push(value_trail(m_qhead)); + for (; m_qhead < m_propagation_queue.size() && !s().inconsistent(); ++m_qhead) { + auto& p = *m_propagation_queue[m_qhead]; + if (p.is_guard()) { + expr* ng = nullptr; + VERIFY(m.is_not(p.m_guard, ng)); + activate_guard(ng, *m_guard2pending[p.m_guard]); + } + else if (p.is_clause()) { + add_clause(p.m_clause); + } + else if (p.is_case()) { + recfun::case_expansion& e = *p.m_case; + if (e.m_def->is_fun_macro()) + assert_macro_axiom(e); + else + assert_case_axioms(e); + ++m_stats.m_case_expansions; + } + else { + SASSERT(p.is_body()); + assert_body_axiom(*p.m_body); + ++m_stats.m_body_expansions; + } + } + return true; } - sat::literal solver::internalize(expr* e, bool sign, bool root, bool learned) { - return sat::null_literal; + void solver::push_body_expand(expr* e) { + auto* b = alloc(body_expansion, u(), to_app(e)); + m_propagation_queue.push_back(alloc(propagation_item, b)); + ctx.push(push_back_vector>(m_propagation_queue)); } + void solver::push_case_expand(expr* e) { + auto* c = alloc(case_expansion, u(), to_app(e)); + m_propagation_queue.push_back(alloc(propagation_item, c)); + ctx.push(push_back_vector>(m_propagation_queue)); + } + + sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { + SASSERT(m.is_bool(e)); + if (!visit_rec(m, e, sign, root, redundant)) { + TRACE("array", tout << mk_pp(e, m) << "\n";); + return sat::null_literal; + } + auto lit = expr2literal(e); + if (sign) + lit.neg(); + return lit; + } + + void solver::internalize(expr* e, bool redundant) { + visit_rec(m, e, false, false, redundant); + } + + bool solver::visited(expr* e) { + euf::enode* n = expr2enode(e); + return n && n->is_attached_to(get_id()); + } + + bool solver::visit(expr* e) { + if (visited(e)) + return true; + if (!is_app(e) || to_app(e)->get_family_id() != get_id()) { + ctx.internalize(e, m_is_redundant); + euf::enode* n = expr2enode(e); + // TODO ensure_var(n); + return true; + } + m_stack.push_back(sat::eframe(e)); + return false; + } + + bool solver::post_visit(expr* e, bool sign, bool root) { + euf::enode* n = expr2enode(e); + app* a = to_app(e); + SASSERT(!n || !n->is_attached_to(get_id())); + if (!n) + n = mk_enode(e, false); + SASSERT(!n->is_attached_to(get_id())); + mk_var(n); +#if 0 + for (auto* arg : euf::enode_args(n)) + ensure_var(arg); + switch (a->get_decl_kind()) { + default: + UNREACHABLE(); + break; + } +#endif + if (u().is_defined(e) && u().has_defs()) + push_case_expand(e); + + return true; + } + + + euf::theory_var solver::mk_var(euf::enode* n) { return euf::null_theory_var; } diff --git a/src/sat/smt/recfun_solver.h b/src/sat/smt/recfun_solver.h index 06bf3a1c4..4cc0ef46f 100644 --- a/src/sat/smt/recfun_solver.h +++ b/src/sat/smt/recfun_solver.h @@ -35,62 +35,6 @@ namespace recfun { void reset() { memset(this, 0, sizeof(stats)); } stats() { reset(); } }; - - // one case-expansion of `f(t1...tn)` - struct case_expansion { - app * m_lhs; // the term to expand - recfun::def * m_def; - ptr_vector m_args; - case_expansion(recfun::util& u, app * n) : - m_lhs(n), m_def(nullptr), m_args() { - SASSERT(u.is_defined(n)); - func_decl * d = n->get_decl(); - m_def = &u.get_def(d); - m_args.append(n->get_num_args(), n->get_args()); - } - case_expansion(case_expansion const & from) - : m_lhs(from.m_lhs), - m_def(from.m_def), - m_args(from.m_args) {} - case_expansion(case_expansion && from) - : m_lhs(from.m_lhs), - m_def(from.m_def), - m_args(std::move(from.m_args)) {} - }; - - struct pp_case_expansion { - case_expansion & e; - ast_manager & m; - pp_case_expansion(case_expansion & e, ast_manager & m) : e(e), m(m) {} - }; - - friend std::ostream& operator<<(std::ostream&, pp_case_expansion const &); - - // one body-expansion of `f(t1...tn)` using a `C_f_i(t1...tn)` - struct body_expansion { - app* m_pred; - recfun::case_def const * m_cdef; - ptr_vector m_args; - - body_expansion(recfun::util& u, app * n) : m_pred(n), m_cdef(nullptr), m_args() { - m_cdef = &u.get_case_def(n); - m_args.append(n->get_num_args(), n->get_args()); - } - body_expansion(app* pred, recfun::case_def const & d, ptr_vector & args) : - m_pred(pred), m_cdef(&d), m_args(args) {} - body_expansion(body_expansion const & from): - m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(from.m_args) {} - body_expansion(body_expansion && from) : - m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(std::move(from.m_args)) {} - }; - - struct pp_body_expansion { - body_expansion & e; - ast_manager & m; - pp_body_expansion(body_expansion & e, ast_manager & m) : e(e), m(m) {} - }; - - friend std::ostream& operator<<(std::ostream&, pp_body_expansion const &); recfun::decl::plugin& m_plugin; recfun::util& m_util; @@ -103,12 +47,44 @@ namespace recfun { obj_map m_pred_depth; expr_ref_vector m_preds; unsigned_vector m_preds_lim; - unsigned m_num_rounds; + unsigned m_num_rounds { 0 }; - ptr_vector m_q_case_expand; - ptr_vector m_q_body_expand; - vector m_q_clauses; - ptr_vector m_q_guards; + struct propagation_item { + case_expansion* m_case { nullptr }; + body_expansion* m_body { nullptr }; + sat::literal_vector m_clause; + expr* m_guard { nullptr }; + + ~propagation_item() { + dealloc(m_case); + dealloc(m_body); + } + + propagation_item(expr* guard): + m_guard(guard) + {} + + propagation_item(sat::literal_vector const& clause): + m_clause(clause) + {} + + propagation_item(body_expansion* b): + m_body(b) + {} + propagation_item(case_expansion* c): + m_case(c) + {} + + bool is_guard() const { return m_guard != nullptr; } + bool is_clause() const { return !m_clause.empty(); } + bool is_case() const { return m_case != nullptr; } + bool is_body() const { return m_body != nullptr; } + }; + scoped_ptr_vector m_propagation_queue; + unsigned m_qhead { 0 }; + + void push_body_expand(expr* e); + void push_case_expand(expr* e); bool is_enabled_guard(expr* guard) { expr_ref ng(m.mk_not(guard), m); return m_enabled_guards.contains(ng); } bool is_disabled_guard(expr* guard) { return m_disabled_guards.contains(guard); } @@ -122,12 +98,10 @@ namespace recfun { void activate_guard(expr* guard, expr_ref_vector const& guards); - void reset_queues(); - expr_ref apply_args(unsigned depth, recfun::vars const & vars, ptr_vector const & args, expr * e); //!< substitute variables by args + expr_ref apply_args(vars const & vars, expr_ref_vector const & args, expr * e); //!< substitute variables by args void assert_macro_axiom(case_expansion & e); void assert_case_axioms(case_expansion & e); void assert_body_axiom(body_expansion & e); - sat::literal mk_literal(expr* e); void add_induction_lemmas(unsigned depth); void disable_guard(expr* guard, expr_ref_vector const& guards); @@ -141,6 +115,10 @@ namespace recfun { } void reset(); + bool visit(expr* e) override; + bool visited(expr* e) override; + bool post_visit(expr* e, bool sign, bool root) override; + public: @@ -157,7 +135,7 @@ namespace recfun { euf::th_solver* clone(euf::solver& ctx) override; bool unit_propagate() override; sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; - void internalize(expr* e, bool redundant) override { UNREACHABLE(); } + void internalize(expr* e, bool redundant) override; euf::theory_var mk_var(euf::enode* n) override; void init_search() override; void finalize_model(model& mdl) override; diff --git a/src/smt/theory_recfun.cpp b/src/smt/theory_recfun.cpp index 1348da985..a935b5454 100644 --- a/src/smt/theory_recfun.cpp +++ b/src/smt/theory_recfun.cpp @@ -34,10 +34,8 @@ namespace smt { m_disabled_guards(m), m_enabled_guards(m), m_preds(m), - m_num_rounds(0), m_q_case_expand(), m_q_body_expand() { - m_num_rounds = 0; } theory_recfun::~theory_recfun() { @@ -54,7 +52,6 @@ namespace smt { } bool theory_recfun::internalize_atom(app * atom, bool gate_ctx) { - force_push(); TRACEFN(mk_pp(atom, m)); if (!u().has_defs()) { return false; @@ -70,13 +67,12 @@ namespace smt { ctx.set_var_theory(v, get_id()); } if (!ctx.relevancy() && u().is_defined(atom)) { - push_case_expand(alloc(case_expansion, u(), atom)); + push_case_expand(alloc(recfun::case_expansion, u(), atom)); } return true; } bool theory_recfun::internalize_term(app * term) { - force_push(); if (!u().has_defs()) { return false; } @@ -87,7 +83,7 @@ namespace smt { if (!ctx.e_internalized(term)) { ctx.mk_enode(term, false, false, true); if (!ctx.relevancy() && u().is_defined(term)) { - push_case_expand(alloc(case_expansion, u(), term)); + push_case_expand(alloc(recfun::case_expansion, u(), term)); } } @@ -128,20 +124,16 @@ namespace smt { SASSERT(ctx.relevancy()); TRACEFN("relevant_eh: (defined) " << u().is_defined(n) << " " << mk_pp(n, m)); if (u().is_defined(n) && u().has_defs()) { - push_case_expand(alloc(case_expansion, u(), n)); + push_case_expand(alloc(recfun::case_expansion, u(), n)); } } void theory_recfun::push_scope_eh() { - if (lazy_push()) - return; theory::push_scope_eh(); m_preds_lim.push_back(m_preds.size()); } void theory_recfun::pop_scope_eh(unsigned num_scopes) { - if (lazy_pop(num_scopes)) - return; theory::pop_scope_eh(num_scopes); reset_queues(); @@ -189,7 +181,7 @@ namespace smt { m_q_clauses.clear(); for (unsigned i = 0; i < m_q_case_expand.size(); ++i) { - case_expansion* e = m_q_case_expand[i]; + recfun::case_expansion* e = m_q_case_expand[i]; if (e->m_def->is_fun_macro()) { // body expand immediately assert_macro_axiom(*e); @@ -276,7 +268,7 @@ namespace smt { if (is_true && u().is_case_pred(e)) { TRACEFN("assign_case_pred_true " << mk_pp(e, m)); // body-expand - push_body_expand(alloc(body_expansion, u(), to_app(e))); + push_body_expand(alloc(recfun::body_expansion, u(), to_app(e))); } } @@ -284,12 +276,12 @@ namespace smt { expr_ref theory_recfun::apply_args( unsigned depth, recfun::vars const & vars, - ptr_vector const & args, + expr_ref_vector const & args, expr * e) { SASSERT(is_standard_order(vars)); var_subst subst(m, true); expr_ref new_body(m); - new_body = subst(e, args.size(), args.c_ptr()); + new_body = subst(e, args); ctx.get_rewriter()(new_body); // simplify set_depth_rec(depth + 1, new_body); return new_body; @@ -329,9 +321,9 @@ namespace smt { * 1. substitute `e.args` for `vs` into the macro rhs * 2. add unit clause `f(args) = rhs` */ - void theory_recfun::assert_macro_axiom(case_expansion & e) { + void theory_recfun::assert_macro_axiom(recfun::case_expansion & e) { m_stats.m_macro_expansions++; - TRACEFN("case expansion " << pp_case_expansion(e, m)); + TRACEFN("case expansion " << e); SASSERT(e.m_def->is_fun_macro()); auto & vars = e.m_def->get_vars(); expr_ref lhs(e.m_lhs, m); @@ -351,8 +343,8 @@ namespace smt { * * also body-expand paths that do not depend on any defined fun */ - void theory_recfun::assert_case_axioms(case_expansion & e) { - TRACEFN("assert_case_axioms "<< pp_case_expansion(e,m) + void theory_recfun::assert_case_axioms(recfun::case_expansion & e) { + TRACEFN("assert_case_axioms " << e << " with " << e.m_def->get_cases().size() << " cases"); SASSERT(e.m_def->is_fun_defined()); // add case-axioms for all case-paths @@ -375,7 +367,7 @@ namespace smt { guards.push_back(apply_args(depth, vars, e.m_args, g)); } if (c.is_immediate()) { - body_expansion be(pred_applied, c, e.m_args); + recfun::body_expansion be(pred_applied, c, e.m_args); assert_body_axiom(be); } else if (!is_enabled_guard(pred_applied)) { @@ -392,13 +384,6 @@ namespace smt { ctx.mk_th_axiom(get_id(), preds); } (void)max_depth; - // add_induction_lemmas(max_depth); - } - - void theory_recfun::add_induction_lemmas(unsigned depth) { - if (depth > 4 && ctx.get_fparams().m_induction && induction::should_try(ctx)) { - ctx.get_induction()(); - } } void theory_recfun::activate_guard(expr* pred_applied, expr_ref_vector const& guards) { @@ -426,7 +411,7 @@ namespace smt { * add axiom guards[args/vars] => f(args) = rhs[args/vars] * */ - void theory_recfun::assert_body_axiom(body_expansion & e) { + void theory_recfun::assert_body_axiom(recfun::body_expansion & e) { recfun::def & d = *e.m_cdef->get_def(); auto & vars = d.get_vars(); auto & args = e.m_args; @@ -439,7 +424,7 @@ namespace smt { expr_ref guard = apply_args(depth, vars, args, g); clause.push_back(~mk_literal(guard)); if (clause.back() == true_literal) { - TRACEFN("body " << pp_body_expansion(e,m) << "\n" << clause << "\n" << guard); + TRACEFN("body " << e << "\n" << clause << "\n" << guard); return; } if (clause.back() == false_literal) { @@ -450,7 +435,7 @@ namespace smt { std::function fn = [&]() { return clause; }; scoped_trace_stream _tr(*this, fn); ctx.mk_th_axiom(get_id(), clause); - TRACEFN("body " << pp_body_expansion(e,m)); + TRACEFN("body " << e); TRACEFN(pp_lits(ctx, clause)); } @@ -521,15 +506,4 @@ namespace smt { st.update("recfun body expansion", m_stats.m_body_expansions); } - std::ostream& operator<<(std::ostream & out, theory_recfun::pp_case_expansion const & e) { - return out << "case_exp(" << mk_pp(e.e.m_lhs, e.m) << ")"; - } - - std::ostream& operator<<(std::ostream & out, theory_recfun::pp_body_expansion const & e) { - out << "body_exp(" << e.e.m_cdef->get_decl()->get_name(); - for (auto* t : e.e.m_args) { - out << " " << mk_pp(t,e.m); - } - return out << ")"; - } } diff --git a/src/smt/theory_recfun.h b/src/smt/theory_recfun.h index 4ee626434..57e163fc1 100644 --- a/src/smt/theory_recfun.h +++ b/src/smt/theory_recfun.h @@ -31,62 +31,6 @@ namespace smt { void reset() { memset(this, 0, sizeof(stats)); } stats() { reset(); } }; - - // one case-expansion of `f(t1...tn)` - struct case_expansion { - app * m_lhs; // the term to expand - recfun::def * m_def; - ptr_vector m_args; - case_expansion(recfun::util& u, app * n) : - m_lhs(n), m_def(nullptr), m_args() { - SASSERT(u.is_defined(n)); - func_decl * d = n->get_decl(); - m_def = &u.get_def(d); - m_args.append(n->get_num_args(), n->get_args()); - } - case_expansion(case_expansion const & from) - : m_lhs(from.m_lhs), - m_def(from.m_def), - m_args(from.m_args) {} - case_expansion(case_expansion && from) - : m_lhs(from.m_lhs), - m_def(from.m_def), - m_args(std::move(from.m_args)) {} - }; - - struct pp_case_expansion { - case_expansion & e; - ast_manager & m; - pp_case_expansion(case_expansion & e, ast_manager & m) : e(e), m(m) {} - }; - - friend std::ostream& operator<<(std::ostream&, pp_case_expansion const &); - - // one body-expansion of `f(t1...tn)` using a `C_f_i(t1...tn)` - struct body_expansion { - app* m_pred; - recfun::case_def const * m_cdef; - ptr_vector m_args; - - body_expansion(recfun::util& u, app * n) : m_pred(n), m_cdef(nullptr), m_args() { - m_cdef = &u.get_case_def(n); - m_args.append(n->get_num_args(), n->get_args()); - } - body_expansion(app* pred, recfun::case_def const & d, ptr_vector & args) : - m_pred(pred), m_cdef(&d), m_args(args) {} - body_expansion(body_expansion const & from): - m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(from.m_args) {} - body_expansion(body_expansion && from) : - m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(std::move(from.m_args)) {} - }; - - struct pp_body_expansion { - body_expansion & e; - ast_manager & m; - pp_body_expansion(body_expansion & e, ast_manager & m) : e(e), m(m) {} - }; - - friend std::ostream& operator<<(std::ostream&, pp_body_expansion const &); recfun::decl::plugin& m_plugin; recfun::util& m_util; @@ -99,10 +43,10 @@ namespace smt { obj_map m_pred_depth; expr_ref_vector m_preds; unsigned_vector m_preds_lim; - unsigned m_num_rounds; + unsigned m_num_rounds { 0 }; - ptr_vector m_q_case_expand; - ptr_vector m_q_body_expand; + ptr_vector m_q_case_expand; + ptr_vector m_q_body_expand; vector m_q_clauses; ptr_vector m_q_guards; @@ -119,13 +63,12 @@ namespace smt { void activate_guard(expr* guard, expr_ref_vector const& guards); void reset_queues(); - expr_ref apply_args(unsigned depth, recfun::vars const & vars, ptr_vector const & args, expr * e); //!< substitute variables by args - void assert_macro_axiom(case_expansion & e); - void assert_case_axioms(case_expansion & e); - void assert_body_axiom(body_expansion & e); + expr_ref apply_args(unsigned depth, recfun::vars const & vars, expr_ref_vector const & args, expr * e); //!< substitute variables by args + void assert_macro_axiom(recfun::case_expansion & e); + void assert_case_axioms(recfun::case_expansion & e); + void assert_body_axiom(recfun::body_expansion & e); literal mk_literal(expr* e); - void add_induction_lemmas(unsigned depth); void disable_guard(expr* guard, expr_ref_vector const& guards); unsigned get_depth(expr* e); void set_depth(unsigned d, expr* e); @@ -136,8 +79,8 @@ namespace smt { return vars.empty() || vars[vars.size()-1]->get_idx() == 0; } protected: - void push_case_expand(case_expansion* e) { m_q_case_expand.push_back(e); } - void push_body_expand(body_expansion* e) { m_q_body_expand.push_back(e); } + void push_case_expand(recfun::case_expansion* e) { m_q_case_expand.push_back(e); } + void push_body_expand(recfun::body_expansion* e) { m_q_body_expand.push_back(e); } bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override;