mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	wreckfun
This commit is contained in:
		
							parent
							
								
									612cc5cfba
								
							
						
					
					
						commit
						83f4a006c6
					
				
					 11 changed files with 402 additions and 219 deletions
				
			
		|  | @ -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 << ")"; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<expr> 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); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -96,6 +96,7 @@ namespace euf { | |||
|         user::solver*          m_user_propagator{ nullptr }; | ||||
|         th_solver*             m_qsolver { nullptr }; | ||||
|         unsigned               m_generation { 0 }; | ||||
|         mutable ptr_vector<expr> m_todo; | ||||
| 
 | ||||
|         ptr_vector<expr>                                m_bool_var2expr; | ||||
|         ptr_vector<size_t>                              m_explain; | ||||
|  | @ -228,6 +229,7 @@ namespace euf { | |||
|                 s.m_generation = m_g; | ||||
|             } | ||||
|         }; | ||||
|         unsigned get_max_generation(expr* e) const; | ||||
| 
 | ||||
|         // accessors
 | ||||
|          | ||||
|  |  | |||
|  | @ -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)); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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))  | ||||
|  |  | |||
|  | @ -46,7 +46,6 @@ namespace q { | |||
|         flat_table             m_flat; | ||||
|         sat::literal_vector    m_universal; | ||||
|         obj_map<sort, expr*>   m_unit_table; | ||||
|         mutable ptr_vector<expr> m_todo; | ||||
|         expr_ref_vector        m_expanded; | ||||
| 
 | ||||
|         sat::literal instantiate(quantifier* q, bool negate, std::function<expr* (quantifier*, unsigned)>& 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; | ||||
|     }; | ||||
| } | ||||
|  |  | |||
|  | @ -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<unsigned>(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<scoped_ptr_vector<propagation_item>>(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<scoped_ptr_vector<propagation_item>>(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; | ||||
|     } | ||||
|  |  | |||
|  | @ -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<expr>    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<expr>         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<expr> & 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<expr, unsigned>  m_pred_depth; | ||||
|         expr_ref_vector          m_preds; | ||||
|         unsigned_vector          m_preds_lim; | ||||
|         unsigned                 m_num_rounds; | ||||
|         unsigned                 m_num_rounds { 0 }; | ||||
| 
 | ||||
|         ptr_vector<case_expansion>  m_q_case_expand; | ||||
|         ptr_vector<body_expansion>  m_q_body_expand; | ||||
|         vector<sat::literal_vector> m_q_clauses; | ||||
|         ptr_vector<expr>            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<propagation_item> 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<expr> 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; | ||||
|  |  | |||
|  | @ -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<expr> 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<literal_vector(void)> 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 << ")"; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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<expr>    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<expr>        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<expr> & 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<expr, unsigned>  m_pred_depth; | ||||
|         expr_ref_vector          m_preds; | ||||
|         unsigned_vector          m_preds_lim; | ||||
|         unsigned                 m_num_rounds; | ||||
|         unsigned                 m_num_rounds { 0 }; | ||||
| 
 | ||||
|         ptr_vector<case_expansion> m_q_case_expand; | ||||
|         ptr_vector<body_expansion> m_q_body_expand; | ||||
|         ptr_vector<recfun::case_expansion> m_q_case_expand; | ||||
|         ptr_vector<recfun::body_expansion> m_q_body_expand; | ||||
|         vector<literal_vector>     m_q_clauses; | ||||
|         ptr_vector<expr>           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<expr> 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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue