/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quant_hoist.cpp Abstract: Quantifier hoisting utility. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #include "ast/rewriter/quant_hoist.h" #include "ast/expr_functors.h" #include "ast/ast_smt_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" #include "ast/rewriter/ast_counter.h" #include "ast/rewriter/expr_safe_replace.h" // // Bring quantifiers of common type into prenex form. // class quantifier_hoister::impl { ast_manager& m; bool_rewriter m_rewriter; public: impl(ast_manager& m) : m(m), m_rewriter(m) {} void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result, bool use_fresh, bool rewrite_ok) { quantifier_type qt = Q_none_pos; pull_quantifier(fml, qt, vars, result, use_fresh, rewrite_ok); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); SASSERT(is_positive(qt)); is_fa = (Q_forall_pos == qt); } void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result, bool use_fresh, bool rewrite_ok) { quantifier_type qt = Q_exists_pos; pull_quantifier(fml, qt, vars, result, use_fresh, rewrite_ok); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); } void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars, bool use_fresh, bool rewrite_ok) { quantifier_type qt = is_forall?Q_forall_pos:Q_exists_pos; expr_ref result(m); pull_quantifier(fml, qt, vars, result, use_fresh, rewrite_ok); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); fml = std::move(result); } void extract_quantifier(quantifier* q, app_ref_vector& vars, expr_ref& result, bool use_fresh) { unsigned nd = q->get_num_decls(); for (unsigned i = 0; i < nd; ++i) { sort* s = q->get_decl_sort(i); symbol const& sym = q->get_decl_name (i); app* a = use_fresh ? m.mk_fresh_const(sym.str ().c_str (), s) : m.mk_const (sym, s); vars.push_back(a); } expr * const * exprs = (expr* const*) (vars.data() + vars.size()- nd); result = instantiate(m, q, exprs); } unsigned pull_quantifier(bool _is_forall, expr_ref& fml, ptr_vector* sorts, svector* names, bool use_fresh, bool rewrite_ok) { unsigned index = var_counter().get_next_var(fml); while (is_quantifier(fml) && _is_forall == is_forall(fml)) { quantifier* q = to_quantifier(fml); index += q->get_num_decls(); if (names) { names->append(q->get_num_decls(), q->get_decl_names()); } if (sorts) { sorts->append(q->get_num_decls(), q->get_decl_sorts()); } fml = q->get_expr(); } if (!has_quantifiers(fml)) { return index; } app_ref_vector vars(m); pull_quantifier(_is_forall, fml, vars, use_fresh, rewrite_ok); if (vars.empty()) { return index; } // replace vars by de-bruijn indices expr_safe_replace rep(m); svector bound_names; ptr_vector bound_sorts; for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars[i].get(); if (names) { bound_names.push_back(v->get_decl()->get_name()); } if (sorts) { bound_sorts.push_back(v->get_sort()); } rep.insert(v, m.mk_var(index++, v->get_sort())); } if (names && !bound_names.empty()) { bound_names.reverse(); bound_names.append(*names); names->reset(); names->append(bound_names); } if (sorts && !bound_sorts.empty()) { bound_sorts.reverse(); bound_sorts.append(*sorts); sorts->reset(); sorts->append(bound_sorts); } rep(fml); return index; } private: enum quantifier_type { Q_forall_pos = 0x10, Q_exists_pos = 0x20, Q_none_pos = 0x40, Q_forall_neg = 0x11, Q_exists_neg = 0x21, Q_none_neg = 0x41 }; void display(quantifier_type qt, std::ostream& out) { switch(qt) { case Q_forall_pos: out << "Forall+"; break; case Q_exists_pos: out << "Exists+"; break; case Q_none_pos: out << "None+"; break; case Q_forall_neg: out << "Forall-"; break; case Q_exists_neg: out << "Exists-"; break; case Q_none_neg: out << "None-"; break; } } quantifier_type& negate(quantifier_type& qt) { qt = static_cast(qt ^0x1); return qt; } static bool is_negative(quantifier_type qt) { return 0 != (qt & 0x1); } static bool is_positive(quantifier_type qt) { return 0 == (qt & 0x1); } static void set_quantifier_type(quantifier_type& qt, bool is_forall) { switch(qt) { case Q_forall_pos: SASSERT(is_forall); break; case Q_forall_neg: SASSERT(!is_forall); break; case Q_exists_pos: SASSERT(!is_forall); break; case Q_exists_neg: SASSERT(is_forall); break; case Q_none_pos: qt = is_forall?Q_forall_pos:Q_exists_pos; break; case Q_none_neg: qt = is_forall?Q_exists_neg:Q_forall_neg; break; } } bool is_compatible(quantifier_type qt, bool is_forall) { switch(qt) { case Q_forall_pos: return is_forall; case Q_forall_neg: return !is_forall; case Q_exists_pos: return !is_forall; case Q_exists_neg: return is_forall; case Q_none_pos: return true; case Q_none_neg: return true; default: UNREACHABLE(); } return false; } void pull_quantifier(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result, bool use_fresh, bool rewrite_ok) { if (!has_quantifiers(fml)) { result = fml; return; } switch(fml->get_kind()) { case AST_APP: { expr_ref_vector args(m); expr_ref tmp(m); expr* t1, *t2, *t3; unsigned num_args = 0; app* a = to_app(fml); if (m.is_and(fml)) { num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { pull_quantifier(a->get_arg(i), qt, vars, tmp, use_fresh, rewrite_ok); args.push_back(tmp); } if (rewrite_ok) { m_rewriter.mk_and(args.size(), args.data(), result); } else { result = m.mk_and (args.size (), args.data ()); } } else if (m.is_or(fml)) { num_args = to_app(fml)->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { pull_quantifier(to_app(fml)->get_arg(i), qt, vars, tmp, use_fresh, rewrite_ok); args.push_back(tmp); } if (rewrite_ok) { m_rewriter.mk_or(args.size(), args.data(), result); } else { result = m.mk_or (args.size (), args.data ()); } } else if (m.is_not(fml)) { pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp, use_fresh, rewrite_ok); negate(qt); result = m.mk_not(tmp); } else if (m.is_implies(fml, t1, t2)) { pull_quantifier(t1, negate(qt), vars, tmp, use_fresh, rewrite_ok); negate(qt); pull_quantifier(t2, qt, vars, result, use_fresh, rewrite_ok); result = m.mk_implies(tmp, result); } else if (m.is_ite(fml, t1, t2, t3)) { expr_ref tt1(m), tt2(m), tt3(m), ntt1(m), nt1(m); pull_quantifier(t2, qt, vars, tt2, use_fresh, rewrite_ok); pull_quantifier(t3, qt, vars, tt3, use_fresh, rewrite_ok); if (has_quantifiers(t1)) { pull_quantifier(t1, qt, vars, tt1, use_fresh, rewrite_ok); nt1 = m.mk_not(t1); pull_quantifier(nt1, qt, vars, ntt1, use_fresh, rewrite_ok); result = m.mk_and(m.mk_or(ntt1, tt2), m.mk_or(tt1, tt3)); } else { result = m.mk_ite(t1, tt2, tt3); } } else if (m.is_eq(fml, t1, t2) && m.is_bool(t1)) { expr_ref tt1(m), tt2(m), ntt1(m), ntt2(m), nt1(m), nt2(m); pull_quantifier(t1, qt, vars, tt1, use_fresh, rewrite_ok); pull_quantifier(t2, qt, vars, tt2, use_fresh, rewrite_ok); nt1 = m.mk_not(t1); nt2 = m.mk_not(t2); pull_quantifier(nt1, qt, vars, ntt1, use_fresh, rewrite_ok); pull_quantifier(nt2, qt, vars, ntt2, use_fresh, rewrite_ok); result = m.mk_and(m.mk_or(ntt1, tt2), m.mk_or(ntt2, tt1)); } else { // the formula contains a quantifier, but it is "inaccessible" result = fml; } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(fml); if (is_lambda(q)) { result = fml; break; } expr_ref tmp(m); if (!is_compatible(qt, is_forall(q))) { result = fml; break; } set_quantifier_type(qt, is_forall(q)); extract_quantifier(q, vars, tmp, use_fresh); pull_quantifier(tmp, qt, vars, result, use_fresh, rewrite_ok); break; } case AST_VAR: result = fml; break; default: UNREACHABLE(); result = fml; break; } } }; quantifier_hoister::quantifier_hoister(ast_manager& m) { m_impl = alloc(impl, m); } quantifier_hoister::~quantifier_hoister() { dealloc(m_impl); } void quantifier_hoister::operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result, bool use_fresh, bool rewrite_ok) { (*m_impl)(fml, vars, is_fa, result, use_fresh, rewrite_ok); } void quantifier_hoister::pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result, bool use_fresh, bool rewrite_ok) { m_impl->pull_exists(fml, vars, result, use_fresh, rewrite_ok); } void quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars, bool use_fresh, bool rewrite_ok) { m_impl->pull_quantifier(is_forall, fml, vars, use_fresh, rewrite_ok); } unsigned quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names, bool use_fresh, bool rewrite_ok) { return m_impl->pull_quantifier(is_forall, fml, sorts, names, use_fresh, rewrite_ok); }