/*++ Copyright (c) 2006 Microsoft Corporation Module Name: distribute_forall.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-02. Revision History: Christoph Wintersteiger 2010-04-06: Added implementation. --*/ #include"var_subst.h" #include"ast_ll_pp.h" #include"distribute_forall.h" distribute_forall::distribute_forall(ast_manager & m, basic_simplifier_plugin & p) : m_manager(m), m_bsimp(p), m_cache(m) { } void distribute_forall::visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); visited = false; } } bool distribute_forall::visit_children(expr * n) { bool visited = true; unsigned j; switch(n->get_kind()) { case AST_VAR: break; case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j), visited); } break; case AST_QUANTIFIER: visit(to_quantifier(n)->get_expr(), visited); break; default: UNREACHABLE(); } return visited; } void distribute_forall::reduce1(expr * n) { switch (n->get_kind()) { case AST_VAR: cache_result(n, n); break; case AST_APP: reduce1_app(to_app(n)); break; case AST_QUANTIFIER: reduce1_quantifier(to_quantifier(n)); break; default: UNREACHABLE(); } } void distribute_forall::reduce1_app(app * a) { SASSERT(a); unsigned num_args = a->get_num_args(); unsigned j = num_args; bool reduced = false; m_new_args.reserve(num_args); app * na = a; while(j > 0) { --j; SASSERT(is_cached(a->get_arg(j))); expr * c = get_cached(a->get_arg(j)); SASSERT(c!=0); if (c != a->get_arg(j)) reduced = true; m_new_args[j] = c; } if (reduced) { na = m_manager.mk_app(a->get_decl(), num_args, m_new_args.c_ptr()); } cache_result(a, na); } void distribute_forall::reduce1_quantifier(quantifier * q) { // This transformation is applied after skolemization/quantifier elimination. So, all quantifiers are universal. SASSERT(q->is_forall()); // This transformation is applied after basic pre-processing steps. // So, we can assume that // 1) All (and f1 ... fn) are already encoded as (not (or (not f1 ... fn))) // 2) All or-formulas are flat (or f1 (or f2 f3)) is encoded as (or f1 f2 f3) expr * e = get_cached(q->get_expr()); if (m_manager.is_not(e) && m_manager.is_or(to_app(e)->get_arg(0))) { // found target for simplification // (forall X (not (or F1 ... Fn))) // --> // (and (forall X (not F1)) // ... // (forall X (not Fn))) app * or_e = to_app(to_app(e)->get_arg(0)); unsigned num_args = or_e->get_num_args(); expr_ref_buffer new_args(m_manager); for (unsigned i = 0; i < num_args; i++) { expr * arg = or_e->get_arg(i); expr_ref not_arg(m_manager); // m_bsimp.mk_not applies basic simplifications. For example, if arg is of the form (not a), then it will return a. m_bsimp.mk_not(arg, not_arg); quantifier_ref tmp_q(m_manager); tmp_q = m_manager.update_quantifier(q, not_arg); expr_ref new_q(m_manager); elim_unused_vars(m_manager, tmp_q, new_q); new_args.push_back(new_q); } expr_ref result(m_manager); // m_bsimp.mk_and actually constructs a (not (or ...)) formula, // it will also apply basic simplifications. m_bsimp.mk_and(new_args.size(), new_args.c_ptr(), result); cache_result(q, result); } else { cache_result(q, m_manager.update_quantifier(q, e)); } } void distribute_forall::operator()(expr * f, expr_ref & result) { m_todo.reset(); flush_cache(); m_todo.push_back(f); while (!m_todo.empty()) { expr * e = m_todo.back(); if (visit_children(e)) { m_todo.pop_back(); reduce1(e); } } result = get_cached(f); SASSERT(result!=0); TRACE("distribute_forall", tout << mk_ll_pp(f, m_manager) << "======>\n" << mk_ll_pp(result, m_manager);); } expr * distribute_forall::get_cached(expr * n) const { return const_cast(this)->m_cache.find(n); } void distribute_forall::cache_result(expr * n, expr * r) { SASSERT(r != 0); m_cache.insert(n, r); }