diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index 70434435b..bff1e7dae 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -298,104 +298,6 @@ struct macro_manager::macro_expander_rw : public rewriter_tpl<macro_manager::mac {} }; -macro_manager::macro_expander::macro_expander(ast_manager & m, macro_manager & mm): - simplifier(m), - m_macro_manager(mm) { - // REMARK: theory simplifier should not be used by macro_expander... - // is_arith_macro rewrites a quantifer such as: - // forall (x Int) (= (+ x (+ (f x) 1)) 2) - // into - // forall (x Int) (= (f x) (+ 1 (* -1 x))) - // The goal is to make simple macro detection detect the arith macro. - // The arith simplifier will undo this transformation. - // borrow_plugins(s); - enable_ac_support(false); -} - -macro_manager::macro_expander::~macro_expander() { - // release_plugins(); -} - -void macro_manager::macro_expander::reduce1_quantifier(quantifier * q) { - simplifier::reduce1_quantifier(q); - // If a macro was expanded in a pattern, we must erase it since it may not be a valid pattern anymore. - // The MAM assumes valid patterns, and it crashes if invalid patterns are provided. - // For example, it will crash if the pattern does not contain all variables. - // - // Alternative solution: use pattern_validation to check if the pattern is still valid. - // I'm not sure if this is a good solution, since the pattern may be meaningless after the macro expansion. - // So, I'm just erasing them. - expr * new_q_expr = 0; - proof * new_q_pr = 0; - get_cached(q, new_q_expr, new_q_pr); - if (!is_quantifier(new_q_expr)) - return; - quantifier * new_q = to_quantifier(new_q_expr); - bool erase_patterns = false; - if (q->get_num_patterns() != new_q->get_num_patterns() || - q->get_num_no_patterns() != new_q->get_num_no_patterns()) { - erase_patterns = true; - } - else { - for (unsigned i = 0; !erase_patterns && i < q->get_num_patterns(); i++) { - if (q->get_pattern(i) != new_q->get_pattern(i)) - erase_patterns = true; - } - for (unsigned i = 0; !erase_patterns && i < q->get_num_no_patterns(); i++) { - if (q->get_no_pattern(i) != new_q->get_no_pattern(i)) - erase_patterns = true; - } - } - if (erase_patterns) { - ast_manager & m = get_manager(); - expr * new_new_q = m.update_quantifier(new_q, 0, 0, 0, 0, new_q->get_expr()); - // we can use the same proof since new_new_q and new_q are identical modulo patterns/annotations - cache_result(q, new_new_q, new_q_pr); - } -} - -bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref & p) { - if (!is_app(_n)) - return false; - app * n = to_app(_n); - quantifier * q = 0; - func_decl * d = n->get_decl(); - TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";); - if (m_macro_manager.m_decl2macro.find(d, q)) { - TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";); - app * head = 0; - expr * def = 0; - m_macro_manager.get_head_def(q, d, head, def); - unsigned num = n->get_num_args(); - SASSERT(head && def); - ptr_buffer<expr> subst_args; - subst_args.resize(num, 0); - for (unsigned i = 0; i < num; i++) { - var * v = to_var(head->get_arg(i)); - SASSERT(v->get_idx() < num); - unsigned nidx = num - v->get_idx() - 1; - SASSERT(subst_args[nidx] == 0); - subst_args[nidx] = n->get_arg(i); - } - var_subst s(m); - s(def, num, subst_args.c_ptr(), r); - if (m.proofs_enabled()) { - expr_ref instance(m); - s(q->get_expr(), num, subst_args.c_ptr(), instance); - proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr()); - proof * q_pr = 0; - m_macro_manager.m_decl2macro_pr.find(d, q_pr); - SASSERT(q_pr != 0); - proof * prs[2] = { qi_pr, q_pr }; - p = m.mk_unit_resolution(2, prs); - } - else { - p = 0; - } - return true; - } - return false; -} void macro_manager::expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr) { if (has_macros()) { diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h index 98e242cd8..71864a699 100644 --- a/src/ast/macros/macro_manager.h +++ b/src/ast/macros/macro_manager.h @@ -60,17 +60,6 @@ class macro_manager { struct macro_expander_cfg; struct macro_expander_rw; - class macro_expander : public simplifier { - protected: - macro_manager & m_macro_manager; - virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p); - virtual void reduce1_quantifier(quantifier * q); - public: - macro_expander(ast_manager & m, macro_manager & mm); - ~macro_expander(); - }; - friend class macro_expander; - public: macro_manager(ast_manager & m); ~macro_manager(); diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index d97c2f094..94a90d829 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -16,15 +16,17 @@ Author: Revision History: --*/ + +#include "util/warning.h" #include "ast/pattern/pattern_inference.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" -#include "util/warning.h" #include "ast/arith_decl_plugin.h" #include "ast/normal_forms/pull_quant.h" #include "ast/well_sorted.h" #include "ast/for_each_expr.h" +#include "ast/rewriter/rewriter_def.h" void smaller_pattern::save(expr * p1, expr * p2) { expr_pair e(p1, p2); @@ -87,7 +89,7 @@ bool smaller_pattern::operator()(unsigned num_bindings, expr * p1, expr * p2) { return process(p1, p2); } -pattern_inference::pattern_inference(ast_manager & m, pattern_inference_params & params): +pattern_inference_old::pattern_inference_old(ast_manager & m, pattern_inference_params & params): simplifier(m), m_params(params), m_bfid(m.get_basic_family_id()), @@ -105,7 +107,7 @@ pattern_inference::pattern_inference(ast_manager & m, pattern_inference_params & enable_ac_support(false); } -void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) { +void pattern_inference_old::collect::operator()(expr * n, unsigned num_bindings) { SASSERT(m_info.empty()); SASSERT(m_todo.empty()); SASSERT(m_cache.empty()); @@ -125,7 +127,7 @@ void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) { reset(); } -inline void pattern_inference::collect::visit(expr * n, unsigned delta, bool & visited) { +inline void pattern_inference_old::collect::visit(expr * n, unsigned delta, bool & visited) { entry e(n, delta); if (!m_cache.contains(e)) { m_todo.push_back(e); @@ -133,7 +135,7 @@ inline void pattern_inference::collect::visit(expr * n, unsigned delta, bool & v } } -bool pattern_inference::collect::visit_children(expr * n, unsigned delta) { +bool pattern_inference_old::collect::visit_children(expr * n, unsigned delta) { bool visited = true; unsigned i; switch (n->get_kind()) { @@ -153,13 +155,13 @@ bool pattern_inference::collect::visit_children(expr * n, unsigned delta) { return visited; } -inline void pattern_inference::collect::save(expr * n, unsigned delta, info * i) { +inline void pattern_inference_old::collect::save(expr * n, unsigned delta, info * i) { m_cache.insert(entry(n, delta), i); if (i != 0) m_info.push_back(i); } -void pattern_inference::collect::save_candidate(expr * n, unsigned delta) { +void pattern_inference_old::collect::save_candidate(expr * n, unsigned delta) { switch (n->get_kind()) { case AST_VAR: { unsigned idx = to_var(n)->get_idx(); @@ -247,14 +249,14 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) { } -void pattern_inference::collect::reset() { +void pattern_inference_old::collect::reset() { m_cache.reset(); std::for_each(m_info.begin(), m_info.end(), delete_proc<info>()); m_info.reset(); SASSERT(m_todo.empty()); } -void pattern_inference::add_candidate(app * n, uint_set const & free_vars, unsigned size) { +void pattern_inference_old::add_candidate(app * n, uint_set const & free_vars, unsigned size) { for (unsigned i = 0; i < m_num_no_patterns; i++) { if (n == m_no_patterns[i]) return; @@ -271,7 +273,7 @@ void pattern_inference::add_candidate(app * n, uint_set const & free_vars, unsig \brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true. Otherwise, copy m_candidates to result. */ -void pattern_inference::filter_looping_patterns(ptr_vector<app> & result) { +void pattern_inference_old::filter_looping_patterns(ptr_vector<app> & result) { unsigned num = m_candidates.size(); for (unsigned i1 = 0; i1 < num; i1++) { app * n1 = m_candidates.get(i1); @@ -310,7 +312,7 @@ void pattern_inference::filter_looping_patterns(ptr_vector<app> & result) { -inline void pattern_inference::contains_subpattern::save(expr * n) { +inline void pattern_inference_old::contains_subpattern::save(expr * n) { unsigned id = n->get_id(); m_already_processed.assure_domain(id); if (!m_already_processed.contains(id)) { @@ -319,7 +321,7 @@ inline void pattern_inference::contains_subpattern::save(expr * n) { } } -bool pattern_inference::contains_subpattern::operator()(expr * n) { +bool pattern_inference_old::contains_subpattern::operator()(expr * n) { m_already_processed.reset(); m_todo.reset(); expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n); @@ -360,7 +362,7 @@ bool pattern_inference::contains_subpattern::operator()(expr * n) { Return true if n contains a direct/indirect child that is also a pattern, and contains the same number of free variables. */ -inline bool pattern_inference::contains_subpattern(expr * n) { +inline bool pattern_inference_old::contains_subpattern(expr * n) { return m_contains_subpattern(n); } @@ -372,18 +374,15 @@ inline bool pattern_inference::contains_subpattern(expr * n) { Remark: Every pattern p in patterns is also a member of m_pattern_map. */ -void pattern_inference::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) { - ptr_vector<app>::const_iterator it = patterns.begin(); - ptr_vector<app>::const_iterator end = patterns.end(); - for (; it != end; ++it) { - app * curr = *it; +void pattern_inference_old::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) { + for (app * curr : patterns) { if (!contains_subpattern(curr)) result.push_back(curr); } } -bool pattern_inference::pattern_weight_lt::operator()(expr * n1, expr * n2) const { +bool pattern_inference_old::pattern_weight_lt::operator()(expr * n1, expr * n2) const { expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1); expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2); SASSERT(e1 != 0); @@ -401,13 +400,10 @@ bool pattern_inference::pattern_weight_lt::operator()(expr * n1, expr * n2) cons variables, then it is copied to remaining_candidate_patterns. The new patterns are stored in result. */ -void pattern_inference::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns, +void pattern_inference_old::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns, ptr_vector<app> & remaining_candidate_patterns, app_ref_buffer & result) { - ptr_vector<app>::const_iterator it = candidate_patterns.begin(); - ptr_vector<app>::const_iterator end = candidate_patterns.end(); - for (; it != end; ++it) { - app * candidate = *it; + for (app * candidate : candidate_patterns) { expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { @@ -425,7 +421,7 @@ void pattern_inference::candidates2unary_patterns(ptr_vector<app> const & candid // HACK: limit the number of case-splits: #define MAX_SPLITS 32 -void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns, +void pattern_inference_old::candidates2multi_patterns(unsigned max_num_patterns, ptr_vector<app> const & candidate_patterns, app_ref_buffer & result) { SASSERT(!candidate_patterns.empty()); @@ -469,21 +465,19 @@ void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns, } } -void pattern_inference::reset_pre_patterns() { +void pattern_inference_old::reset_pre_patterns() { std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc<pre_pattern>()); m_pre_patterns.reset(); } #ifdef _TRACE static void dump_app_vector(std::ostream & out, ptr_vector<app> const & v, ast_manager & m) { - ptr_vector<app>::const_iterator it = v.begin(); - ptr_vector<app>::const_iterator end = v.end(); - for (; it != end; ++it) - out << mk_pp(*it, m) << "\n"; + for (app * e : v) + out << mk_pp(e, m) << "\n"; } #endif -bool pattern_inference::is_forbidden(app * n) const { +bool pattern_inference_old::is_forbidden(app * n) const { func_decl const * decl = n->get_decl(); if (is_ground(n)) return false; @@ -499,14 +493,11 @@ bool pattern_inference::is_forbidden(app * n) const { return false; } -bool pattern_inference::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) { +bool pattern_inference_old::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) { if (m_preferred.empty()) return false; bool found = false; - ptr_vector<app>::const_iterator it = candidate_patterns.begin(); - ptr_vector<app>::const_iterator end = candidate_patterns.end(); - for (; it != end; ++it) { - app * candidate = *it; + for (app * candidate : candidate_patterns) { if (m_preferred.contains(to_app(candidate)->get_decl())) { expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; @@ -521,7 +512,7 @@ bool pattern_inference::has_preferred_patterns(ptr_vector<app> & candidate_patte return found; } -void pattern_inference::mk_patterns(unsigned num_bindings, +void pattern_inference_old::mk_patterns(unsigned num_bindings, expr * n, unsigned num_no_patterns, expr * const * no_patterns, @@ -578,7 +569,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings, #include "ast/pattern/database.h" -void pattern_inference::reduce1_quantifier(quantifier * q) { +void pattern_inference_old::reduce1_quantifier(quantifier * q) { TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";); if (!q->is_forall()) { simplifier::reduce1_quantifier(q); @@ -739,13 +730,626 @@ void pattern_inference::reduce1_quantifier(quantifier * q) { } -#if 0 -// unused -static void dump_expr_vector(std::ostream & out, ptr_vector<expr> const & v, ast_manager & m) { - ptr_vector<expr>::const_iterator it = v.begin(); - ptr_vector<expr>::const_iterator end = v.end(); - for (; it != end; ++it) - out << mk_pp(*it, m) << "\n"; -} -#endif +pattern_inference_cfg::pattern_inference_cfg(ast_manager & m, pattern_inference_params & params): + m(m), + m_params(params), + m_bfid(m.get_basic_family_id()), + m_afid(m.mk_family_id("arith")), + m_le(m), + m_nested_arith_only(true), + m_block_loop_patterns(params.m_pi_block_loop_patterns), + m_candidates(m), + m_pattern_weight_lt(m_candidates_info), + m_collect(m, *this), + m_contains_subpattern(*this), + m_database(m) { + if (params.m_pi_arith == AP_NO) + register_forbidden_family(m_afid); +} + +void pattern_inference_cfg::collect::operator()(expr * n, unsigned num_bindings) { + SASSERT(m_info.empty()); + SASSERT(m_todo.empty()); + SASSERT(m_cache.empty()); + m_num_bindings = num_bindings; + m_todo.push_back(entry(n, 0)); + while (!m_todo.empty()) { + entry & e = m_todo.back(); + n = e.m_node; + unsigned delta = e.m_delta; + TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";); + TRACE("collect_info", tout << mk_pp(n, m) << "\n";); + if (visit_children(n, delta)) { + m_todo.pop_back(); + save_candidate(n, delta); + } + } + reset(); +} + +inline void pattern_inference_cfg::collect::visit(expr * n, unsigned delta, bool & visited) { + entry e(n, delta); + if (!m_cache.contains(e)) { + m_todo.push_back(e); + visited = false; + } +} + +bool pattern_inference_cfg::collect::visit_children(expr * n, unsigned delta) { + bool visited = true; + unsigned i; + switch (n->get_kind()) { + case AST_APP: + i = to_app(n)->get_num_args(); + while (i > 0) { + --i; + visit(to_app(n)->get_arg(i), delta, visited); + } + break; + case AST_QUANTIFIER: + visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited); + break; + default: + break; + } + return visited; +} + +inline void pattern_inference_cfg::collect::save(expr * n, unsigned delta, info * i) { + m_cache.insert(entry(n, delta), i); + if (i != 0) + m_info.push_back(i); +} + +void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { + switch (n->get_kind()) { + case AST_VAR: { + unsigned idx = to_var(n)->get_idx(); + if (idx >= delta) { + idx = idx - delta; + uint_set free_vars; + if (idx < m_num_bindings) + free_vars.insert(idx); + info * i = 0; + if (delta == 0) + i = alloc(info, m, n, free_vars, 1); + else + i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1); + save(n, delta, i); + } + else { + save(n, delta, 0); + } + return; + } + case AST_APP: { + app * c = to_app(n); + func_decl * decl = c->get_decl(); + if (m_owner.is_forbidden(c)) { + save(n, delta, 0); + return; + } + + if (c->get_num_args() == 0) { + save(n, delta, alloc(info, m, n, uint_set(), 1)); + return; + } + + ptr_buffer<expr> buffer; + bool changed = false; // false if none of the children is mapped to a node different from itself. + uint_set free_vars; + unsigned size = 1; + unsigned num = c->get_num_args(); + for (unsigned i = 0; i < num; i++) { + expr * child = c->get_arg(i); + info * child_info = 0; +#ifdef Z3DEBUG + bool found = +#endif + m_cache.find(entry(child, delta), child_info); + SASSERT(found); + if (child_info == 0) { + save(n, delta, 0); + return; + } + buffer.push_back(child_info->m_node.get()); + free_vars |= child_info->m_free_vars; + size += child_info->m_size; + if (child != child_info->m_node.get()) + changed = true; + } + + app * new_node = 0; + if (changed) + new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr()); + else + new_node = to_app(n); + save(n, delta, alloc(info, m, new_node, free_vars, size)); + // Remark: arithmetic patterns are only used if they are nested inside other terms. + // That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern + // if arithmetic is not in the forbidden list. + // + // Remark: The rule above has an exception. The operators (div, idiv, mod) are allowed to be + // used as patterns even when they are not nested in other terms. The motivation is that + // Z3 currently doesn't implement them (i.e., they are uninterpreted). So, some users add axioms + // stating properties about these operators. + family_id fid = c->get_family_id(); + decl_kind k = c->get_decl_kind(); + if (!free_vars.empty() && + (fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) { + TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";); + m_owner.add_candidate(new_node, free_vars, size); + } + return; + } + default: + save(n, delta, 0); + return; + } +} + + +void pattern_inference_cfg::collect::reset() { + m_cache.reset(); + std::for_each(m_info.begin(), m_info.end(), delete_proc<info>()); + m_info.reset(); + SASSERT(m_todo.empty()); +} + +void pattern_inference_cfg::add_candidate(app * n, uint_set const & free_vars, unsigned size) { + for (unsigned i = 0; i < m_num_no_patterns; i++) { + if (n == m_no_patterns[i]) + return; + } + + if (!m_candidates_info.contains(n)) { + m_candidates_info.insert(n, info(free_vars, size)); + m_candidates.push_back(n); + } +} + + +/** + \brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true. + Otherwise, copy m_candidates to result. +*/ +void pattern_inference_cfg::filter_looping_patterns(ptr_vector<app> & result) { + unsigned num = m_candidates.size(); + for (unsigned i1 = 0; i1 < num; i1++) { + app * n1 = m_candidates.get(i1); + expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1); + SASSERT(e1); + uint_set const & s1 = e1->get_data().m_value.m_free_vars; + if (m_block_loop_patterns) { + bool smaller = false; + for (unsigned i2 = 0; i2 < num; i2++) { + if (i1 != i2) { + app * n2 = m_candidates.get(i2); + expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2); + if (e2) { + uint_set const & s2 = e2->get_data().m_value.m_free_vars; + // Remark: the comparison operator only makes sense if both AST nodes + // contain the same number of variables. + // Example: + // (f X Y) <: (f (g X Z W) Y) + if (s1 == s2 && m_le(m_num_bindings, n1, n2) && !m_le(m_num_bindings, n2, n1)) { + smaller = true; + break; + } + } + } + } + if (!smaller) + result.push_back(n1); + else + m_candidates_info.erase(n1); + } + else { + result.push_back(n1); + } + } +} + + + +inline void pattern_inference_cfg::contains_subpattern::save(expr * n) { + unsigned id = n->get_id(); + m_already_processed.assure_domain(id); + if (!m_already_processed.contains(id)) { + m_todo.push_back(n); + m_already_processed.insert(id); + } +} + +bool pattern_inference_cfg::contains_subpattern::operator()(expr * n) { + m_already_processed.reset(); + m_todo.reset(); + expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n); + SASSERT(_e); + uint_set const & s1 = _e->get_data().m_value.m_free_vars; + save(n); + unsigned num; + while (!m_todo.empty()) { + expr * curr = m_todo.back(); + m_todo.pop_back(); + switch (curr->get_kind()) { + case AST_APP: + if (curr != n) { + expr2info::obj_map_entry * e = m_owner.m_candidates_info.find_core(curr); + if (e) { + uint_set const & s2 = e->get_data().m_value.m_free_vars; + SASSERT(s2.subset_of(s1)); + if (s1 == s2) { + TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";); + return true; + } + } + } + num = to_app(curr)->get_num_args(); + for (unsigned i = 0; i < num; i++) + save(to_app(curr)->get_arg(i)); + break; + case AST_VAR: + break; + default: + UNREACHABLE(); + } + } + return false; +} + +/** + Return true if n contains a direct/indirect child that is also a + pattern, and contains the same number of free variables. +*/ +inline bool pattern_inference_cfg::contains_subpattern(expr * n) { + return m_contains_subpattern(n); +} + +/** + \brief Copy a pattern p in patterns to result, if there is no + direct/indirect child of p in patterns which contains the same set + of variables. + + Remark: Every pattern p in patterns is also a member of + m_pattern_map. +*/ +void pattern_inference_cfg::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) { + for (app * curr : patterns) { + if (!contains_subpattern(curr)) + result.push_back(curr); + } +} + + +bool pattern_inference_cfg::pattern_weight_lt::operator()(expr * n1, expr * n2) const { + expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1); + expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2); + SASSERT(e1 != 0); + SASSERT(e2 != 0); + info const & i1 = e1->get_data().m_value; + info const & i2 = e2->get_data().m_value; + unsigned num_free_vars1 = i1.m_free_vars.num_elems(); + unsigned num_free_vars2 = i2.m_free_vars.num_elems(); + return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size); +} + +/** + \brief Create unary patterns (single expressions that contain all + bound variables). If a candidate does not contain all bound + variables, then it is copied to remaining_candidate_patterns. The + new patterns are stored in result. +*/ +void pattern_inference_cfg::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns, + ptr_vector<app> & remaining_candidate_patterns, + app_ref_buffer & result) { + for (app * candidate : candidate_patterns) { + expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); + info const & i = e->get_data().m_value; + if (i.m_free_vars.num_elems() == m_num_bindings) { + app * new_pattern = m.mk_pattern(candidate); + result.push_back(new_pattern); + } + else { + remaining_candidate_patterns.push_back(candidate); + } + } +} + +// TODO: this code is too inefficient when the number of candidate +// patterns is too big. +// HACK: limit the number of case-splits: +#define MAX_SPLITS 32 + +void pattern_inference_cfg::candidates2multi_patterns(unsigned max_num_patterns, + ptr_vector<app> const & candidate_patterns, + app_ref_buffer & result) { + SASSERT(!candidate_patterns.empty()); + m_pre_patterns.push_back(alloc(pre_pattern)); + unsigned sz = candidate_patterns.size(); + unsigned num_splits = 0; + for (unsigned j = 0; j < m_pre_patterns.size(); j++) { + pre_pattern * curr = m_pre_patterns[j]; + if (curr->m_free_vars.num_elems() == m_num_bindings) { + app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr()); + result.push_back(new_pattern); + if (result.size() >= max_num_patterns) + return; + } + else if (curr->m_idx < sz) { + app * n = candidate_patterns[curr->m_idx]; + expr2info::obj_map_entry * e = m_candidates_info.find_core(n); + uint_set const & s = e->get_data().m_value.m_free_vars; + if (!s.subset_of(curr->m_free_vars)) { + pre_pattern * new_p = alloc(pre_pattern,*curr); + new_p->m_exprs.push_back(n); + new_p->m_free_vars |= s; + new_p->m_idx++; + m_pre_patterns.push_back(new_p); + + if (num_splits < MAX_SPLITS) { + m_pre_patterns[j] = 0; + curr->m_idx++; + m_pre_patterns.push_back(curr); + num_splits++; + } + } + else { + m_pre_patterns[j] = 0; + curr->m_idx++; + m_pre_patterns.push_back(curr); + } + } + TRACE("pattern_inference", tout << "m_pre_patterns.size(): " << m_pre_patterns.size() << + "\nnum_splits: " << num_splits << "\n";); + } +} + +void pattern_inference_cfg::reset_pre_patterns() { + std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc<pre_pattern>()); + m_pre_patterns.reset(); +} + + +bool pattern_inference_cfg::is_forbidden(app * n) const { + func_decl const * decl = n->get_decl(); + if (is_ground(n)) + return false; + // Remark: skolem constants should not be used in patterns, since they do not + // occur outside of the quantifier. That is, Z3 will never match this kind of + // pattern. + if (m_params.m_pi_avoid_skolems && decl->is_skolem()) { + CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";); + return true; + } + if (is_forbidden(decl)) + return true; + return false; +} + +bool pattern_inference_cfg::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) { + if (m_preferred.empty()) + return false; + bool found = false; + for (app * candidate : candidate_patterns) { + if (m_preferred.contains(to_app(candidate)->get_decl())) { + expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); + info const & i = e->get_data().m_value; + if (i.m_free_vars.num_elems() == m_num_bindings) { + TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";); + app * p = m.mk_pattern(candidate); + result.push_back(p); + found = true; + } + } + } + return found; +} + +void pattern_inference_cfg::mk_patterns(unsigned num_bindings, + expr * n, + unsigned num_no_patterns, + expr * const * no_patterns, + app_ref_buffer & result) { + m_num_bindings = num_bindings; + m_num_no_patterns = num_no_patterns; + m_no_patterns = no_patterns; + + m_collect(n, num_bindings); + + TRACE("pattern_inference", + tout << mk_pp(n, m); + tout << "\ncandidates:\n"; + unsigned num = m_candidates.size(); + for (unsigned i = 0; i < num; i++) { + tout << mk_pp(m_candidates.get(i), m) << "\n"; + }); + + if (!m_candidates.empty()) { + m_tmp1.reset(); + filter_looping_patterns(m_tmp1); + TRACE("pattern_inference", + tout << "candidates after removing looping-patterns:\n"; + dump_app_vector(tout, m_tmp1, m);); + SASSERT(!m_tmp1.empty()); + if (!has_preferred_patterns(m_tmp1, result)) { + // continue if there are no preferred patterns + m_tmp2.reset(); + filter_bigger_patterns(m_tmp1, m_tmp2); + SASSERT(!m_tmp2.empty()); + TRACE("pattern_inference", + tout << "candidates after removing bigger patterns:\n"; + dump_app_vector(tout, m_tmp2, m);); + m_tmp1.reset(); + candidates2unary_patterns(m_tmp2, m_tmp1, result); + unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns; + if (result.empty()) + num_extra_multi_patterns++; + if (num_extra_multi_patterns > 0 && !m_tmp1.empty()) { + // m_pattern_weight_lt is not a total order + std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt); + TRACE("pattern_inference", + tout << "candidates after sorting:\n"; + dump_app_vector(tout, m_tmp1, m);); + candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result); + } + } + } + + reset_pre_patterns(); + m_candidates_info.reset(); + m_candidates.reset(); +} + + +bool pattern_inference_cfg::reduce_quantifier( + quantifier * q, + expr * new_body, + expr * const *, // new_patterns + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + + TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";); + if (!q->is_forall()) { + return false; + } + + int weight = q->get_weight(); + + if (m_params.m_pi_use_database) { + app_ref_vector new_patterns(m); + m_database.initialize(g_pattern_database); + unsigned new_weight; + if (m_database.match_quantifier(q, new_patterns, new_weight)) { + DEBUG_CODE(for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); }); + quantifier_ref new_q(m); + if (q->get_num_patterns() > 0) { + // just update the weight... + TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";); + result = m.update_quantifier_weight(q, new_weight); + } + else { + quantifier_ref tmp(m); + tmp = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr()); + result = m.update_quantifier_weight(tmp, new_weight); + TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";); + } + if (m.fine_grain_proofs()) + result_pr = m.mk_rewrite(q, new_q); + return true; + } + } + + if (q->get_num_patterns() > 0) { + return false; + } + + if (m_params.m_pi_nopat_weight >= 0) + weight = m_params.m_pi_nopat_weight; + + SASSERT(q->get_num_patterns() == 0); + + if (m_params.m_pi_arith == AP_CONSERVATIVE) + m_forbidden.push_back(m_afid); + + app_ref_buffer new_patterns(m); + unsigned num_no_patterns = q->get_num_no_patterns(); + mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns); + + if (new_patterns.empty() && num_no_patterns > 0) { + if (new_patterns.empty()) { + mk_patterns(q->get_num_decls(), new_body, 0, 0, new_patterns); + if (m_params.m_pi_warnings && !new_patterns.empty()) { + warning_msg("ignoring nopats annotation because Z3 couldn't find any other pattern (quantifier id: %s)", q->get_qid().str().c_str()); + } + } + } + + if (m_params.m_pi_arith == AP_CONSERVATIVE) { + m_forbidden.pop_back(); + if (new_patterns.empty()) { + flet<bool> l1(m_block_loop_patterns, false); // allow looping patterns + mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns); + if (!new_patterns.empty()) { + weight = std::max(weight, static_cast<int>(m_params.m_pi_arith_weight)); + if (m_params.m_pi_warnings) { + warning_msg("using arith. in pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_ARITH_WEIGHT=<val>).", + q->get_qid().str().c_str(), weight); + } + } + } + } + + if (m_params.m_pi_arith != AP_NO && new_patterns.empty()) { + if (new_patterns.empty()) { + flet<bool> l1(m_nested_arith_only, false); // try to find a non-nested arith pattern + flet<bool> l2(m_block_loop_patterns, false); // allow looping patterns + mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns); + if (!new_patterns.empty()) { + weight = std::max(weight, static_cast<int>(m_params.m_pi_non_nested_arith_weight)); + if (m_params.m_pi_warnings) { + warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=<val>).", + q->get_qid().str().c_str(), weight); + } + // verbose_stream() << mk_pp(q, m) << "\n"; + } + } + } + + quantifier_ref new_q(m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body), m); + if (weight != q->get_weight()) + new_q = m.update_quantifier_weight(new_q, weight); + if (m.fine_grain_proofs()) { + proof* new_body_pr = m.mk_reflexivity(new_body); + result_pr = m.mk_quant_intro(q, new_q, new_body_pr); + } + + if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) { + pull_quant pull(m); + expr_ref new_expr(m); + proof_ref new_pr(m); + pull(new_q, new_expr, new_pr); + quantifier * result2 = to_quantifier(new_expr); + if (result2 != new_q) { + mk_patterns(result2->get_num_decls(), result2->get_expr(), 0, 0, new_patterns); + if (!new_patterns.empty()) { + if (m_params.m_pi_warnings) { + warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str()); + } + new_q = m.update_quantifier(result2, new_patterns.size(), (expr**) new_patterns.c_ptr(), result2->get_expr()); + if (m.fine_grain_proofs()) { + result_pr = m.mk_transitivity(new_pr, m.mk_quant_intro(result2, new_q, m.mk_reflexivity(new_q->get_expr()))); + } + TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";); + } + } + } + + if (new_patterns.empty()) { + if (m_params.m_pi_warnings) { + warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str()); + } + TRACE("pi_failed", tout << mk_pp(q, m) << "\n";); + } + + if (new_patterns.empty() && new_body == q->get_expr()) { + return false; + } + + result = new_q; + + IF_IVERBOSE(10, + verbose_stream() << "(smt.inferred-patterns :qid " << q->get_qid() << "\n"; + for (unsigned i = 0; i < new_patterns.size(); i++) + verbose_stream() << " " << mk_ismt2_pp(new_patterns[i], m, 2) << "\n"; + verbose_stream() << ")\n"; ); + + return true; +} + +pattern_inference_rw::pattern_inference_rw(ast_manager& m, pattern_inference_params & params): + rewriter_tpl<pattern_inference_cfg>(m, m.proofs_enabled(), m_cfg), + m_cfg(m, params) +{} diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index a138d1033..7b0ec9cfe 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -21,6 +21,7 @@ Revision History: #include "ast/ast.h" #include "ast/simplifier/simplifier.h" +#include "ast/rewriter/rewriter.h" #include "ast/pattern/pattern_inference_params.h" #include "util/vector.h" #include "util/uint_set.h" @@ -60,7 +61,7 @@ public: bool operator()(unsigned num_bindings, expr * p1, expr * p2); }; -class pattern_inference : public simplifier { +class pattern_inference_old : public simplifier { pattern_inference_params & m_params; family_id m_bfid; family_id m_afid; @@ -88,7 +89,7 @@ class pattern_inference : public simplifier { typedef obj_map<expr, info> expr2info; - expr2info m_candidates_info; // candidate -> set of free vars + size + expr2info m_candidates_info; // candidate -> set of free vars + size app_ref_vector m_candidates; ptr_vector<app> m_tmp1; @@ -136,7 +137,7 @@ class pattern_inference : public simplifier { }; ast_manager & m; - pattern_inference & m_owner; + pattern_inference_old & m_owner; family_id m_afid; unsigned m_num_bindings; typedef map<entry, info *, obj_hash<entry>, default_eq<entry> > cache; @@ -150,7 +151,7 @@ class pattern_inference : public simplifier { void save_candidate(expr * n, unsigned delta); void reset(); public: - collect(ast_manager & m, pattern_inference & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {} + collect(ast_manager & m, pattern_inference_old & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {} void operator()(expr * n, unsigned num_bindings); }; @@ -165,12 +166,12 @@ class pattern_inference : public simplifier { void filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result); class contains_subpattern { - pattern_inference & m_owner; + pattern_inference_old & m_owner; nat_set m_already_processed; ptr_vector<expr> m_todo; void save(expr * n); public: - contains_subpattern(pattern_inference & owner): + contains_subpattern(pattern_inference_old & owner): m_owner(owner) {} bool operator()(expr * n); }; @@ -217,7 +218,7 @@ class pattern_inference : public simplifier { virtual void reduce1_quantifier(quantifier * q); public: - pattern_inference(ast_manager & m, pattern_inference_params & params); + pattern_inference_old(ast_manager & m, pattern_inference_params & params); void register_forbidden_family(family_id fid) { SASSERT(fid != m_bfid); @@ -244,5 +245,201 @@ public: bool is_forbidden(app * n) const; }; +class pattern_inference_cfg : public default_rewriter_cfg { + ast_manager& m; + pattern_inference_params & m_params; + family_id m_bfid; + family_id m_afid; + svector<family_id> m_forbidden; + obj_hashtable<func_decl> m_preferred; + smaller_pattern m_le; + unsigned m_num_bindings; + unsigned m_num_no_patterns; + expr * const * m_no_patterns; + bool m_nested_arith_only; + bool m_block_loop_patterns; + + struct info { + uint_set m_free_vars; + unsigned m_size; + info(uint_set const & vars, unsigned size): + m_free_vars(vars), + m_size(size) { + } + info(): + m_free_vars(), + m_size(0) { + } + }; + + typedef obj_map<expr, info> expr2info; + + expr2info m_candidates_info; // candidate -> set of free vars + size + app_ref_vector m_candidates; + + ptr_vector<app> m_tmp1; + ptr_vector<app> m_tmp2; + ptr_vector<app> m_todo; + + // Compare candidates patterns based on their usefulness + // p1 < p2 if + // - p1 has more free variables than p2 + // - p1 and p2 has the same number of free variables, + // and p1 is smaller than p2. + struct pattern_weight_lt { + expr2info & m_candidates_info; + pattern_weight_lt(expr2info & i): + m_candidates_info(i) { + } + bool operator()(expr * n1, expr * n2) const; + }; + + pattern_weight_lt m_pattern_weight_lt; + + // + // Functor for collecting candidates. + // + class collect { + struct entry { + expr * m_node; + unsigned m_delta; + entry():m_node(0), m_delta(0) {} + entry(expr * n, unsigned d):m_node(n), m_delta(d) {} + unsigned hash() const { + return hash_u_u(m_node->get_id(), m_delta); + } + bool operator==(entry const & e) const { + return m_node == e.m_node && m_delta == e.m_delta; + } + }; + + struct info { + expr_ref m_node; + uint_set m_free_vars; + unsigned m_size; + info(ast_manager & m, expr * n, uint_set const & vars, unsigned sz): + m_node(n, m), m_free_vars(vars), m_size(sz) {} + }; + + ast_manager & m; + pattern_inference_cfg & m_owner; + family_id m_afid; + unsigned m_num_bindings; + typedef map<entry, info *, obj_hash<entry>, default_eq<entry> > cache; + cache m_cache; + ptr_vector<info> m_info; + svector<entry> m_todo; + + void visit(expr * n, unsigned delta, bool & visited); + bool visit_children(expr * n, unsigned delta); + void save(expr * n, unsigned delta, info * i); + void save_candidate(expr * n, unsigned delta); + void reset(); + public: + collect(ast_manager & m, pattern_inference_cfg & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {} + void operator()(expr * n, unsigned num_bindings); + }; + + collect m_collect; + + void add_candidate(app * n, uint_set const & s, unsigned size); + + void filter_looping_patterns(ptr_vector<app> & result); + + bool has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result); + + void filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result); + + class contains_subpattern { + pattern_inference_cfg & m_owner; + nat_set m_already_processed; + ptr_vector<expr> m_todo; + void save(expr * n); + public: + contains_subpattern(pattern_inference_cfg & owner): + m_owner(owner) {} + bool operator()(expr * n); + }; + + contains_subpattern m_contains_subpattern; + + bool contains_subpattern(expr * n); + + struct pre_pattern { + ptr_vector<app> m_exprs; // elements of the pattern. + uint_set m_free_vars; // set of free variables in m_exprs + unsigned m_idx; // idx of the next candidate to process. + pre_pattern(): + m_idx(0) { + } + }; + + ptr_vector<pre_pattern> m_pre_patterns; + expr_pattern_match m_database; + + void candidates2unary_patterns(ptr_vector<app> const & candidate_patterns, + ptr_vector<app> & remaining_candidate_patterns, + app_ref_buffer & result); + + void candidates2multi_patterns(unsigned max_num_patterns, + ptr_vector<app> const & candidate_patterns, + app_ref_buffer & result); + + void reset_pre_patterns(); + + /** + \brief All minimal unary patterns (i.e., expressions that + contain all bound variables) are copied to result. If there + are unary patterns, then at most num_extra_multi_patterns multi + patterns are created. If there are no unary pattern, then at + most 1 + num_extra_multi_patterns multi_patterns are created. + */ + void mk_patterns(unsigned num_bindings, // IN number of bindings. + expr * n, // IN node where the patterns are going to be extracted. + unsigned num_no_patterns, // IN num. patterns that should not be used. + expr * const * no_patterns, // IN patterns that should not be used. + app_ref_buffer & result); // OUT result + +public: + pattern_inference_cfg(ast_manager & m, pattern_inference_params & params); + + void register_forbidden_family(family_id fid) { + SASSERT(fid != m_bfid); + m_forbidden.push_back(fid); + } + + /** + \brief Register f as a preferred function symbol. The inference algorithm + gives preference to patterns rooted by this kind of function symbol. + */ + void register_preferred(func_decl * f) { + m_preferred.insert(f); + } + + bool reduce_quantifier(quantifier * old_q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr); + + void register_preferred(unsigned num, func_decl * const * fs) { for (unsigned i = 0; i < num; i++) register_preferred(fs[i]); } + + bool is_forbidden(func_decl const * decl) const { + family_id fid = decl->get_family_id(); + if (fid == m_bfid && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) + return true; + return std::find(m_forbidden.begin(), m_forbidden.end(), fid) != m_forbidden.end(); + } + + bool is_forbidden(app * n) const; +}; + +class pattern_inference_rw : public rewriter_tpl<pattern_inference_cfg> { + pattern_inference_cfg m_cfg; +public: + pattern_inference_rw(ast_manager& m, pattern_inference_params & params); +}; + #endif /* PATTERN_INFERENCE_H_ */ diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index 7eb2284c0..cfb998a32 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -70,7 +70,7 @@ namespace datalog { if (q->get_num_patterns() == 0) { proof_ref new_pr(m); pattern_inference_params params; - pattern_inference infer(m, params); + pattern_inference_old infer(m, params); infer(q, qe, new_pr); q = to_quantifier(qe); } diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 7eb189ebf..26265a6f8 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -21,6 +21,7 @@ Revision History: #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" +#include "ast/rewriter/rewriter_def.h" #include "ast/simplifier/arith_simplifier_plugin.h" #include "ast/simplifier/array_simplifier_plugin.h" #include "ast/simplifier/datatype_simplifier_plugin.h" @@ -517,7 +518,7 @@ void asserted_formulas::reduce_and_solve() { void asserted_formulas::infer_patterns() { IF_IVERBOSE(10, verbose_stream() << "(smt.pattern-inference)\n";); TRACE("before_pattern_inference", display(tout);); - pattern_inference infer(m, m_params); + pattern_inference_rw infer(m, m_params); expr_ref_vector new_exprs(m); proof_ref_vector new_prs(m); unsigned i = m_asserted_qhead; diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 725c9fc51..0a75ff700 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -207,8 +207,10 @@ void proto_model::remove_aux_decls_not_in_set(ptr_vector<func_decl> & decls, fun by their interpretations. */ void proto_model::cleanup() { + TRACE("model_bug", model_v2_pp(tout, *this);); func_decl_set found_aux_fs; for (auto const& kv : m_finterp) { + TRACE("model_bug", tout << kv.m_key->get_name() << "\n";); func_interp * fi = kv.m_value; cleanup_func_interp(fi, found_aux_fs); } @@ -365,7 +367,7 @@ void proto_model::complete_partial_funcs() { } model * proto_model::mk_model() { - TRACE("proto_model", tout << "mk_model\n"; model_v2_pp(tout, *this);); + TRACE("proto_model", model_v2_pp(tout << "mk_model\n", *this);); model * m = alloc(model, m_manager); for (auto const& kv : m_interp) { diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 953afd4b7..5848fac62 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -19,7 +19,6 @@ Revision History: #include "util/ref_util.h" #include "ast/for_each_expr.h" -#include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_context.h"