mirror of
https://github.com/Z3Prover/z3
synced 2025-04-11 11:43:36 +00:00
171 lines
4.5 KiB
C++
171 lines
4.5 KiB
C++
/*++
|
|
Copyright (c) 2006 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
distribute_forall.cpp
|
|
|
|
Abstract:
|
|
|
|
<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<distribute_forall*>(this)->m_cache.find(n);
|
|
}
|
|
|
|
void distribute_forall::cache_result(expr * n, expr * r) {
|
|
SASSERT(r != 0);
|
|
m_cache.insert(n, r);
|
|
}
|