3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-03 01:40:22 +00:00

reorganizing the code

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-23 22:14:35 -07:00
parent 9e299b88c4
commit 0a4446ae26
255 changed files with 17 additions and 17 deletions

View file

@ -0,0 +1,2 @@
Simplifier module is now obsolete.
It is still being used in many places, but we will eventually replace all occurrences with the new rewriter module.

View file

@ -0,0 +1,26 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_simplifier_params.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-09.
Revision History:
--*/
#include"arith_simplifier_params.h"
void arith_simplifier_params::register_params(ini_params & p) {
p.register_bool_param("ARITH_EXPAND_EQS", m_arith_expand_eqs);
p.register_bool_param("ARITH_PROCESS_ALL_EQS", m_arith_process_all_eqs);
}

View file

@ -0,0 +1,37 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_simplifier_params.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-09.
Revision History:
--*/
#ifndef _ARITH_SIMPLIFIER_PARAMS_H_
#define _ARITH_SIMPLIFIER_PARAMS_H_
#include"ini_file.h"
struct arith_simplifier_params {
bool m_arith_expand_eqs;
bool m_arith_process_all_eqs;
arith_simplifier_params():
m_arith_expand_eqs(false),
m_arith_process_all_eqs(false) {
}
void register_params(ini_params & p);
};
#endif /* _ARITH_SIMPLIFIER_PARAMS_H_ */

View file

@ -0,0 +1,442 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
arith_simplifier_plugin.cpp
Abstract:
Simplifier for the arithmetic family.
Author:
Leonardo (leonardo) 2008-01-08
--*/
#include"arith_simplifier_plugin.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
arith_simplifier_plugin::~arith_simplifier_plugin() {
}
arith_simplifier_plugin::arith_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, arith_simplifier_params & p):
poly_simplifier_plugin(symbol("arith"), m, OP_ADD, OP_MUL, OP_UMINUS, OP_SUB, OP_NUM),
m_params(p),
m_util(m),
m_bsimp(b),
m_int_zero(m),
m_real_zero(m) {
m_int_zero = m_util.mk_numeral(rational(0), true);
m_real_zero = m_util.mk_numeral(rational(0), false);
}
/**
\brief Return true if the first monomial of t is negative.
*/
bool arith_simplifier_plugin::is_neg_poly(expr * t) const {
if (m_util.is_add(t)) {
t = to_app(t)->get_arg(0);
}
if (m_util.is_mul(t)) {
t = to_app(t)->get_arg(0);
rational r;
bool is_int;
if (m_util.is_numeral(t, r, is_int))
return r.is_neg();
}
return false;
}
void arith_simplifier_plugin::get_monomial_gcd(expr_ref_vector& monomials, numeral& g) {
g = numeral::zero();
numeral n;
for (unsigned i = 0; !g.is_one() && i < monomials.size(); ++i) {
expr* e = monomials[i].get();
if (is_numeral(e, n)) {
g = gcd(abs(n), g);
}
else if (is_mul(e) && is_numeral(to_app(e)->get_arg(0), n)) {
g = gcd(abs(n), g);
}
else {
g = numeral::one();
return;
}
}
if (g.is_zero()) {
g = numeral::one();
}
}
void arith_simplifier_plugin::div_monomial(expr_ref_vector& monomials, numeral const& g) {
numeral n;
for (unsigned i = 0; i < monomials.size(); ++i) {
expr* e = monomials[i].get();
if (is_numeral(e, n)) {
SASSERT((n/g).is_int());
monomials[i] = mk_numeral(n/g);
}
else if (is_mul(e) && is_numeral(to_app(e)->get_arg(0), n)) {
SASSERT((n/g).is_int());
monomials[i] = mk_mul(n/g, to_app(e)->get_arg(1));
}
else {
UNREACHABLE();
}
}
}
void arith_simplifier_plugin::gcd_reduce_monomial(expr_ref_vector& monomials, numeral& k) {
numeral g, n;
get_monomial_gcd(monomials, g);
g = gcd(abs(k), g);
if (g.is_one()) {
return;
}
SASSERT(g.is_pos());
k = k / g;
div_monomial(monomials, g);
}
template<arith_simplifier_plugin::op_kind Kind>
void arith_simplifier_plugin::mk_le_ge_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
set_curr_sort(arg1);
bool is_int = m_curr_sort->get_decl_kind() == INT_SORT;
expr_ref_vector monomials(m_manager);
rational k;
TRACE("arith_eq_bug", tout << mk_ismt2_pp(arg1, m_manager) << "\n" << mk_ismt2_pp(arg2, m_manager) << "\n";);
process_sum_of_monomials(false, arg1, monomials, k);
process_sum_of_monomials(true, arg2, monomials, k);
k.neg();
if (is_int) {
numeral g;
get_monomial_gcd(monomials, g);
if (!g.is_one()) {
div_monomial(monomials, g);
switch(Kind) {
case LE:
//
// g*monmials' <= k
// <=>
// monomials' <= floor(k/g)
//
k = floor(k/g);
break;
case GE:
//
// g*monmials' >= k
// <=>
// monomials' >= ceil(k/g)
//
k = ceil(k/g);
break;
case EQ:
k = k/g;
if (!k.is_int()) {
result = m_manager.mk_false();
return;
}
break;
}
}
}
expr_ref lhs(m_manager);
mk_sum_of_monomials(monomials, lhs);
if (m_util.is_numeral(lhs)) {
SASSERT(lhs == mk_zero());
if (( Kind == LE && numeral::zero() <= k) ||
( Kind == GE && numeral::zero() >= k) ||
( Kind == EQ && numeral::zero() == k))
result = m_manager.mk_true();
else
result = m_manager.mk_false();
}
else {
if (is_neg_poly(lhs)) {
expr_ref neg_lhs(m_manager);
mk_uminus(lhs, neg_lhs);
lhs = neg_lhs;
k.neg();
expr * rhs = m_util.mk_numeral(k, is_int);
switch (Kind) {
case LE:
result = m_util.mk_ge(lhs, rhs);
break;
case GE:
result = m_util.mk_le(lhs, rhs);
break;
case EQ:
result = m_manager.mk_eq(lhs, rhs);
break;
}
}
else {
expr * rhs = m_util.mk_numeral(k, is_int);
switch (Kind) {
case LE:
result = m_util.mk_le(lhs, rhs);
break;
case GE:
result = m_util.mk_ge(lhs, rhs);
break;
case EQ:
result = m_manager.mk_eq(lhs, rhs);
break;
}
}
}
}
void arith_simplifier_plugin::mk_arith_eq(expr * arg1, expr * arg2, expr_ref & result) {
mk_le_ge_eq_core<EQ>(arg1, arg2, result);
}
void arith_simplifier_plugin::mk_le(expr * arg1, expr * arg2, expr_ref & result) {
mk_le_ge_eq_core<LE>(arg1, arg2, result);
}
void arith_simplifier_plugin::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
mk_le_ge_eq_core<GE>(arg1, arg2, result);
}
void arith_simplifier_plugin::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
expr_ref tmp(m_manager);
mk_le(arg2, arg1, tmp);
m_bsimp.mk_not(tmp, result);
}
void arith_simplifier_plugin::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
expr_ref tmp(m_manager);
mk_le(arg1, arg2, tmp);
m_bsimp.mk_not(tmp, result);
}
void arith_simplifier_plugin::gcd_normalize(numeral & coeff, expr_ref& term) {
if (!abs(coeff).is_one()) {
set_curr_sort(term);
SASSERT(m_curr_sort->get_decl_kind() == INT_SORT);
expr_ref_vector monomials(m_manager);
rational k;
monomials.push_back(mk_numeral(numeral(coeff), true));
process_sum_of_monomials(false, term, monomials, k);
gcd_reduce_monomial(monomials, k);
numeral coeff1;
if (!is_numeral(monomials[0].get(), coeff1)) {
UNREACHABLE();
}
if (coeff1 == coeff) {
return;
}
monomials[0] = mk_numeral(k, true);
coeff = coeff1;
mk_sum_of_monomials(monomials, term);
}
}
void arith_simplifier_plugin::mk_div(expr * arg1, expr * arg2, expr_ref & result) {
set_curr_sort(arg1);
numeral v1, v2;
bool is_int;
if (m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) {
SASSERT(!is_int);
if (m_util.is_numeral(arg1, v1, is_int))
result = m_util.mk_numeral(v1/v2, false);
else {
numeral k(1);
k /= v2;
expr_ref inv_arg2(m_util.mk_numeral(k, false), m_manager);
mk_mul(inv_arg2, arg1, result);
}
}
else
result = m_util.mk_div(arg1, arg2);
}
void arith_simplifier_plugin::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
set_curr_sort(arg1);
numeral v1, v2;
bool is_int;
if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero())
result = m_util.mk_numeral(div(v1, v2), is_int);
else
result = m_util.mk_idiv(arg1, arg2);
}
void arith_simplifier_plugin::prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result) {
SASSERT(m_util.is_int(e));
SASSERT(k.is_int() && k.is_pos());
numeral n;
bool is_int;
if (depth == 0) {
result = e;
}
else if (is_add(e) || is_mul(e)) {
expr_ref_vector args(m_manager);
expr_ref tmp(m_manager);
app* a = to_app(e);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
prop_mod_const(a->get_arg(i), depth - 1, k, tmp);
args.push_back(tmp);
}
reduce(a->get_decl(), args.size(), args.c_ptr(), result);
}
else if (m_util.is_numeral(e, n, is_int) && is_int) {
result = mk_numeral(mod(n, k), true);
}
else {
result = e;
}
}
void arith_simplifier_plugin::mk_mod(expr * arg1, expr * arg2, expr_ref & result) {
set_curr_sort(arg1);
numeral v1, v2;
bool is_int;
if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) {
result = m_util.mk_numeral(mod(v1, v2), is_int);
}
else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) {
result = m_util.mk_numeral(numeral(0), true);
}
else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos()) {
expr_ref tmp(m_manager);
prop_mod_const(arg1, 5, v2, tmp);
result = m_util.mk_mod(tmp, arg2);
}
else {
result = m_util.mk_mod(arg1, arg2);
}
}
void arith_simplifier_plugin::mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
set_curr_sort(arg1);
numeral v1, v2;
bool is_int;
if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) {
numeral m = mod(v1, v2);
//
// rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2)
//
if (v2.is_neg()) {
m.neg();
}
result = m_util.mk_numeral(m, is_int);
}
else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) {
result = m_util.mk_numeral(numeral(0), true);
}
else if (m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) {
expr_ref tmp(m_manager);
prop_mod_const(arg1, 5, v2, tmp);
result = m_util.mk_mod(tmp, arg2);
if (v2.is_neg()) {
result = m_util.mk_uminus(result);
}
}
else {
result = m_util.mk_rem(arg1, arg2);
}
}
void arith_simplifier_plugin::mk_to_real(expr * arg, expr_ref & result) {
numeral v;
if (m_util.is_numeral(arg, v))
result = m_util.mk_numeral(v, false);
else
result = m_util.mk_to_real(arg);
}
void arith_simplifier_plugin::mk_to_int(expr * arg, expr_ref & result) {
numeral v;
if (m_util.is_numeral(arg, v))
result = m_util.mk_numeral(floor(v), true);
else if (m_util.is_to_real(arg))
result = to_app(arg)->get_arg(0);
else
result = m_util.mk_to_int(arg);
}
void arith_simplifier_plugin::mk_is_int(expr * arg, expr_ref & result) {
numeral v;
if (m_util.is_numeral(arg, v))
result = v.is_int()?m_manager.mk_true():m_manager.mk_false();
else if (m_util.is_to_real(arg))
result = m_manager.mk_true();
else
result = m_util.mk_is_int(arg);
}
bool arith_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
set_reduce_invoked();
SASSERT(f->get_family_id() == m_fid);
TRACE("arith_simplifier_plugin", tout << mk_pp(f, m_manager) << "\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_pp(args[i], m_manager) << "\n";);
arith_op_kind k = static_cast<arith_op_kind>(f->get_decl_kind());
switch (k) {
case OP_NUM: return false;
case OP_LE: if (m_presimp) return false; SASSERT(num_args == 2); mk_le(args[0], args[1], result); break;
case OP_GE: if (m_presimp) return false; SASSERT(num_args == 2); mk_ge(args[0], args[1], result); break;
case OP_LT: if (m_presimp) return false; SASSERT(num_args == 2); mk_lt(args[0], args[1], result); break;
case OP_GT: if (m_presimp) return false; SASSERT(num_args == 2); mk_gt(args[0], args[1], result); break;
case OP_ADD: mk_add(num_args, args, result); break;
case OP_SUB: mk_sub(num_args, args, result); break;
case OP_UMINUS: SASSERT(num_args == 1); mk_uminus(args[0], result); break;
case OP_MUL:
mk_mul(num_args, args, result);
TRACE("arith_simplifier_plugin", tout << mk_pp(result, m_manager) << "\n";);
break;
case OP_DIV: SASSERT(num_args == 2); mk_div(args[0], args[1], result); break;
case OP_IDIV: SASSERT(num_args == 2); mk_idiv(args[0], args[1], result); break;
case OP_REM: SASSERT(num_args == 2); mk_rem(args[0], args[1], result); break;
case OP_MOD: SASSERT(num_args == 2); mk_mod(args[0], args[1], result); break;
case OP_TO_REAL: SASSERT(num_args == 1); mk_to_real(args[0], result); break;
case OP_TO_INT: SASSERT(num_args == 1); mk_to_int(args[0], result); break;
case OP_IS_INT: SASSERT(num_args == 1); mk_is_int(args[0], result); break;
case OP_POWER: return false;
case OP_IRRATIONAL_ALGEBRAIC_NUM: return false;
default:
UNREACHABLE();
return false;
}
TRACE("arith_simplifier_plugin", tout << mk_pp(result.get(), m_manager) << "\n";);
return true;
}
bool arith_simplifier_plugin::is_arith_term(expr * n) const {
return n->get_kind() == AST_APP && to_app(n)->get_family_id() == m_fid;
}
bool arith_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
TRACE("reduce_eq_bug", tout << mk_ismt2_pp(lhs, m_manager) << "\n" << mk_ismt2_pp(rhs, m_manager) << "\n";);
set_reduce_invoked();
if (m_presimp) {
return false;
}
if (m_params.m_arith_expand_eqs) {
expr_ref le(m_manager), ge(m_manager);
mk_le_ge_eq_core<LE>(lhs, rhs, le);
mk_le_ge_eq_core<GE>(lhs, rhs, ge);
m_bsimp.mk_and(le, ge, result);
return true;
}
if (m_params.m_arith_process_all_eqs || is_arith_term(lhs) || is_arith_term(rhs)) {
mk_arith_eq(lhs, rhs, result);
return true;
}
return false;
}

View file

@ -0,0 +1,95 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
arith_simplifier_plugin.h
Abstract:
Simplifier for the arithmetic family.
Author:
Leonardo (leonardo) 2008-01-08
--*/
#ifndef _ARITH_SIMPLIFIER_PLUGIN_H_
#define _ARITH_SIMPLIFIER_PLUGIN_H_
#include"basic_simplifier_plugin.h"
#include"poly_simplifier_plugin.h"
#include"arith_decl_plugin.h"
#include"arith_simplifier_params.h"
/**
\brief Simplifier for the arith family.
*/
class arith_simplifier_plugin : public poly_simplifier_plugin {
public:
enum op_kind {
LE, GE, EQ
};
protected:
arith_simplifier_params & m_params;
arith_util m_util;
basic_simplifier_plugin & m_bsimp;
expr_ref m_int_zero;
expr_ref m_real_zero;
bool is_neg_poly(expr * t) const;
template<op_kind k>
void mk_le_ge_eq_core(expr * arg1, expr * arg2, expr_ref & result);
void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result);
void gcd_reduce_monomial(expr_ref_vector& monomials, numeral& k);
void div_monomial(expr_ref_vector& monomials, numeral const& g);
void get_monomial_gcd(expr_ref_vector& monomials, numeral& g);
public:
arith_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, arith_simplifier_params & p);
~arith_simplifier_plugin();
arith_util & get_arith_util() { return m_util; }
virtual numeral norm(const numeral & n) { return n; }
virtual bool is_numeral(expr * n, rational & val) const { bool f; return m_util.is_numeral(n, val, f); }
bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
virtual bool is_minus_one(expr * n) const { numeral tmp; return is_numeral(n, tmp) && tmp.is_minus_one(); }
virtual expr * get_zero(sort * s) const { return m_util.is_int(s) ? m_int_zero.get() : m_real_zero.get(); }
virtual app * mk_numeral(numeral const & n) { return m_util.mk_numeral(n, m_curr_sort->get_decl_kind() == INT_SORT); }
app * mk_numeral(numeral const & n, bool is_int) { return m_util.mk_numeral(n, is_int); }
bool is_int_sort(sort const * s) const { return m_util.is_int(s); }
bool is_real_sort(sort const * s) const { return m_util.is_real(s); }
bool is_arith_sort(sort const * s) const { return is_int_sort(s) || is_real_sort(s); }
bool is_int(expr const * n) const { return m_util.is_int(n); }
bool is_le(expr const * n) const { return m_util.is_le(n); }
bool is_ge(expr const * n) const { return m_util.is_ge(n); }
virtual bool is_le_ge(expr * n) const { return is_le(n) || is_ge(n); }
void mk_le(expr * arg1, expr * arg2, expr_ref & result);
void mk_ge(expr * arg1, expr * arg2, expr_ref & result);
void mk_lt(expr * arg1, expr * arg2, expr_ref & result);
void mk_gt(expr * arg1, expr * arg2, expr_ref & result);
void mk_arith_eq(expr * arg1, expr * arg2, expr_ref & result);
void mk_div(expr * arg1, expr * arg2, expr_ref & result);
void mk_idiv(expr * arg1, expr * arg2, expr_ref & result);
void mk_mod(expr * arg1, expr * arg2, expr_ref & result);
void mk_rem(expr * arg1, expr * arg2, expr_ref & result);
void mk_to_real(expr * arg, expr_ref & result);
void mk_to_int(expr * arg, expr_ref & result);
void mk_is_int(expr * arg, expr_ref & result);
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
bool is_arith_term(expr * n) const;
void gcd_normalize(numeral & coeff, expr_ref& term);
};
#endif /* _ARITH_SIMPLIFIER_PLUGIN_H_ */

View file

@ -0,0 +1,864 @@
/*++
Copyright (c) 2008 Microsoft Corporation
Module Name:
array_simplifier_plugin.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2008-05-05
Revision History:
Notes TODO:
Examine quadratic cost of simplification vs. model-based procedure.
Parameterize cache replacement strategy.
Some parameters are hard-wired.
--*/
#include "array_simplifier_plugin.h"
#include "ast_ll_pp.h"
#include "ast_pp.h"
array_simplifier_plugin::array_simplifier_plugin(
ast_manager & m,
basic_simplifier_plugin& s,
simplifier& simp,
theory_array_params const& p) :
simplifier_plugin(symbol("array"),m),
m_util(m),
m_simp(s),
m_simplifier(simp),
m_params(p),
m_store_cache_size(0)
{}
array_simplifier_plugin::~array_simplifier_plugin() {
select_cache::iterator it = m_select_cache.begin();
select_cache::iterator end = m_select_cache.end();
for ( ; it != end; ++it) {
m_manager.dec_array_ref(it->m_key->size(), it->m_key->c_ptr());
m_manager.dec_ref(it->m_value);
dealloc(it->m_key);
}
store_cache::iterator it2 = m_store_cache.begin();
store_cache::iterator end2 = m_store_cache.end();
for (; it2 != end2; ++it2) {
m_manager.dec_ref(it->m_value);
dealloc(it->m_key);
}
}
bool array_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (!m_params.m_array_simplify)
return false;
set_reduce_invoked();
if (m_presimp)
return false;
#if _DEBUG
for (unsigned i = 0; i < num_args && i < f->get_arity(); ++i) {
SASSERT(m_manager.get_sort(args[i]) == f->get_domain(i));
}
#endif
TRACE("array_simplifier", {
tout << mk_pp(f, m_manager) << " ";
for (unsigned i = 0; i < num_args; ++i) {
tout << mk_pp(args[i], m_manager) << " ";
}
tout << "\n";
}
);
SASSERT(f->get_family_id() == m_fid);
switch(f->get_decl_kind()) {
case OP_SELECT:
mk_select(num_args, args, result);
break;
case OP_STORE:
mk_store(f, num_args, args, result);
break;
case OP_SET_UNION: {
sort* s = f->get_range();
expr_ref empty(m_manager);
mk_empty_set(s, empty);
switch(num_args) {
case 0:
result = empty;
break;
case 1:
result = args[0];
break;
default: {
result = args[0];
func_decl* f_or = m_manager.mk_or_decl();
for (unsigned i = 1; i < num_args; ++i) {
mk_map(f_or, result, args[i], result);
}
break;
}
}
break;
}
case OP_SET_INTERSECT: {
expr_ref full(m_manager);
mk_full_set(f->get_range(), full);
switch(num_args) {
case 0:
result = full;
break;
case 1:
result = args[0];
break;
default: {
result = args[0];
func_decl* f_and = m_manager.mk_and_decl();
for (unsigned i = 1; i < num_args; ++i) {
mk_map(f_and, result, args[i], result);
}
break;
}
}
TRACE("array_simplifier", tout << "sort " << mk_pp(result.get(), m_manager) << "\n";);
break;
}
case OP_SET_SUBSET: {
SASSERT(num_args == 2);
expr_ref diff(m_manager), emp(m_manager);
mk_set_difference(num_args, args, diff);
mk_empty_set(m_manager.get_sort(args[0]), emp);
m_simp.mk_eq(diff.get(), emp.get(), result);
break;
}
case OP_SET_COMPLEMENT: {
SASSERT(num_args == 1);
func_decl* f_not = m_manager.mk_not_decl();
mk_map(f_not, args[0], result);
break;
}
case OP_SET_DIFFERENCE: {
SASSERT(num_args == 2);
expr_ref r1(m_manager);
mk_map(m_manager.mk_not_decl(), args[1], r1);
mk_map(m_manager.mk_and_decl(), args[0], r1, result);
break;
}
case OP_ARRAY_MAP: {
SASSERT(f->get_num_parameters() == 1);
SASSERT(f->get_parameter(0).is_ast());
SASSERT(is_func_decl(f->get_parameter(0).get_ast()));
//
// map_d (store a j v) = (store (map_f a) v (d v))
//
if (num_args == 1 && is_store(args[0])) {
app* store_expr = to_app(args[0]);
unsigned num_args = store_expr->get_num_args();
SASSERT(num_args >= 3);
parameter p = f->get_parameter(0);
func_decl* d = to_func_decl(p.get_ast());
expr* a = store_expr->get_arg(0);
expr* v = store_expr->get_arg(num_args-1);
// expr*const* args = store_expr->get_args()+1;
expr_ref r1(m_manager), r2(m_manager);
ptr_vector<expr> new_args;
reduce(f, 1, &a, r1);
m_simplifier.mk_app(d, 1, &v, r2);
new_args.push_back(r1);
for (unsigned i = 1; i + 1 < num_args; ++i) {
new_args.push_back(store_expr->get_arg(i));
}
new_args.push_back(r2);
mk_store(store_expr->get_decl(), num_args, new_args.c_ptr(), result);
break;
}
//
// map_d (store a j v) (store b j w) = (store (map_f a b) j (d v w))
//
if (num_args > 1 && same_store(num_args, args)) {
app* store_expr1 = to_app(args[0]);
unsigned num_indices = store_expr1->get_num_args();
SASSERT(num_indices >= 3);
parameter p = f->get_parameter(0);
func_decl* d = to_func_decl(p.get_ast());
ptr_vector<expr> arrays;
ptr_vector<expr> values;
for (unsigned i = 0; i < num_args; ++i) {
arrays.push_back(to_app(args[i])->get_arg(0));
values.push_back(to_app(args[i])->get_arg(num_indices-1));
}
expr_ref r1(m_manager), r2(m_manager);
reduce(f, arrays.size(), arrays.c_ptr(), r1);
m_simplifier.mk_app(d, values.size(), values.c_ptr(), r2);
ptr_vector<expr> new_args;
new_args.push_back(r1);
for (unsigned i = 1; i + 1 < num_indices; ++i) {
new_args.push_back(store_expr1->get_arg(i));
}
new_args.push_back(r2);
mk_store(store_expr1->get_decl(), new_args.size(), new_args.c_ptr(), result);
break;
}
//
// map_d (const v) = (const (d v))
//
if (num_args == 1 && is_const_array(args[0])) {
app* const_expr = to_app(args[0]);
SASSERT(const_expr->get_num_args() == 1);
parameter p = f->get_parameter(0);
func_decl* d = to_func_decl(p.get_ast());
expr* v = const_expr->get_arg(0);
expr_ref r1(m_manager);
m_simplifier.mk_app(d, 1, &v, r1);
expr* arg = r1.get();
parameter param(f->get_range());
result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, &arg);
break;
}
//
// map_d (const v) (const w) = (const (d v w))
//
if (num_args > 1 && all_const_array(num_args, args)) {
parameter p = f->get_parameter(0);
func_decl* d = to_func_decl(p.get_ast());
ptr_vector<expr> values;
for (unsigned i = 0; i < num_args; ++i) {
values.push_back(to_app(args[i])->get_arg(0));
}
expr_ref r1(m_manager);
m_simplifier.mk_app(d, values.size(), values.c_ptr(), r1);
expr* arg = r1.get();
parameter param(f->get_range());
result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, &arg);
break;
}
result = m_manager.mk_app(f, num_args, args);
break;
}
default:
result = m_manager.mk_app(f, num_args, args);
break;
}
TRACE("array_simplifier",
tout << mk_pp(result.get(), m_manager) << "\n";);
return true;
}
bool array_simplifier_plugin::same_store(unsigned num_args, expr* const* args) const {
if (num_args == 0) {
return true;
}
if (!is_store(args[0])) {
return false;
}
SASSERT(to_app(args[0])->get_num_args() >= 3);
unsigned num_indices = to_app(args[0])->get_num_args() - 2;
for (unsigned i = 1; i < num_args; ++i) {
if (!is_store(args[i])) {
return false;
}
for (unsigned j = 1; j < num_indices + 1; ++j) {
if (to_app(args[0])->get_arg(j) != to_app(args[i])->get_arg(j)) {
return false;
}
}
}
return true;
}
bool array_simplifier_plugin::all_const_array(unsigned num_args, expr* const* args) const {
bool is_const = true;
for (unsigned i = 0; is_const && i < num_args; ++i) {
is_const = is_const_array(args[i]);
}
return is_const;
}
bool array_simplifier_plugin::all_values(unsigned num_args, expr* const* args) const {
for (unsigned i = 0; i < num_args; ++i) {
if (!m_manager.is_value(args[i])) {
return false;
}
}
return true;
}
bool array_simplifier_plugin::lex_lt(unsigned num_args, expr* const* args1, expr* const* args2) {
for (unsigned i = 0; i < num_args; ++i) {
TRACE("array_simplifier",
tout << mk_pp(args1[i], m_manager) << "\n";
tout << mk_pp(args2[i], m_manager) << "\n";
tout << args1[i]->get_id() << " " << args2[i]->get_id() << "\n";
);
if (args1[i]->get_id() < args2[i]->get_id()) return true;
if (args1[i]->get_id() > args2[i]->get_id()) return false;
}
return false;
}
void array_simplifier_plugin::get_stores(expr* n, unsigned& arity, expr*& m, ptr_vector<expr*const>& stores) {
while (is_store(n)) {
app* a = to_app(n);
SASSERT(a->get_num_args() > 2);
arity = a->get_num_args()-2;
n = a->get_arg(0);
stores.push_back(a->get_args()+1);
}
m = n;
}
lbool array_simplifier_plugin::eq_default(expr* def, unsigned arity, unsigned num_st, expr*const* const* st) {
for (unsigned i = 0; i < num_st; ++i) {
if (st[i][arity] == def) {
continue;
}
if (m_manager.is_value(st[i][arity]) && m_manager.is_value(def)) {
return l_false;
}
return l_undef;
}
return l_true;
}
bool array_simplifier_plugin::insert_table(expr* def, unsigned arity, unsigned num_st, expr*const* const* st, arg_table& table) {
for (unsigned i = 0; i < num_st; ++i ) {
for (unsigned j = 0; j < arity; ++j) {
if (!m_manager.is_value(st[i][j])) {
return false;
}
}
args_entry e(arity, st[i]);
table.insert_if_not_there(e);
}
return true;
}
lbool array_simplifier_plugin::eq_stores(expr* def, unsigned arity, unsigned num_st1, expr*const* const* st1, unsigned num_st2, expr*const* const* st2) {
if (num_st1 == 0) {
return eq_default(def, arity, num_st2, st2);
}
if (num_st2 == 0) {
return eq_default(def, arity, num_st1, st1);
}
arg_table table1, table2;
if (!insert_table(def, arity, num_st1, st1, table1)) {
return l_undef;
}
if (!insert_table(def, arity, num_st2, st2, table2)) {
return l_undef;
}
arg_table::iterator it = table1.begin();
arg_table::iterator end = table1.end();
for (; it != end; ++it) {
args_entry const & e1 = *it;
args_entry e2;
expr* v1 = e1.m_args[arity];
if (table2.find(e1, e2)) {
expr* v2 = e2.m_args[arity];
if (v1 == v2) {
table2.erase(e1);
continue;
}
if (m_manager.is_value(v1) && m_manager.is_value(v2)) {
return l_false;
}
return l_undef;
}
else if (m_manager.is_value(v1) && m_manager.is_value(def) && v1 != def) {
return l_false;
}
}
it = table2.begin();
end = table2.end();
for (; it != end; ++it) {
args_entry const & e = *it;
expr* v = e.m_args[arity];
if (m_manager.is_value(v) && m_manager.is_value(def) && v != def) {
return l_false;
}
}
if (!table2.empty() || !table1.empty()) {
return l_undef;
}
return l_true;
}
bool array_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
set_reduce_invoked();
expr* c1, *c2;
ptr_vector<expr*const> st1, st2;
unsigned arity = 0;
get_stores(lhs, arity, c1, st1);
get_stores(rhs, arity, c2, st2);
if (is_const_array(c1) && is_const_array(c2)) {
c1 = to_app(c1)->get_arg(0);
c2 = to_app(c2)->get_arg(0);
if (c1 == c2) {
lbool eq = eq_stores(c1, arity, st1.size(), st1.c_ptr(), st2.size(), st2.c_ptr());
TRACE("array_simplifier",
tout << mk_pp(lhs, m_manager) << " = "
<< mk_pp(rhs, m_manager) << " := " << eq << "\n";);
switch(eq) {
case l_false:
result = m_manager.mk_false();
return true;
case l_true:
result = m_manager.mk_true();
return true;
default:
return false;
}
}
else if (m_manager.is_value(c1) && m_manager.is_value(c2)) {
result = m_manager.mk_false();
return true;
}
}
return false;
}
bool array_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
set_reduce_invoked();
return false;
}
array_simplifier_plugin::const_select_result
array_simplifier_plugin::mk_select_const(expr* m, app* index, expr_ref& result) {
store_info* info = 0;
expr* r = 0, *a = 0;
if (!is_store(m)) {
return NOT_CACHED;
}
if (!m_store_cache.find(m, info)) {
return NOT_CACHED;
}
if (info->m_map.find(index, r)) {
result = r;
return FOUND_VALUE;
}
a = info->m_default.get();
//
// Unfold and cache the store while searching for value of index.
//
while (is_store(a) && m_manager.is_value(to_app(a)->get_arg(1))) {
app* b = to_app(a);
app* c = to_app(b->get_arg(1));
if (!info->m_map.contains(c)) {
info->m_map.insert(c, b->get_arg(2));
m_manager.inc_ref(b->get_arg(2));
++m_store_cache_size;
}
a = b->get_arg(0);
info->m_default = a;
if (c == index) {
result = b->get_arg(2);
return FOUND_VALUE;
}
}
result = info->m_default.get();
return FOUND_DEFAULT;
}
void array_simplifier_plugin::cache_store(unsigned num_stores, expr* store_term)
{
if (num_stores <= m_const_store_threshold) {
return;
}
prune_store_cache();
if (!m_store_cache.contains(store_term)) {
store_info * info = alloc(store_info, m_manager, store_term);
m_manager.inc_ref(store_term);
m_store_cache.insert(store_term, info);
TRACE("cache_store", tout << m_store_cache.size() << "\n";);
++m_store_cache_size;
}
}
void array_simplifier_plugin::cache_select(unsigned num_args, expr * const * args, expr * result) {
ptr_vector<expr> * entry = alloc(ptr_vector<expr>);
entry->append(num_args, const_cast<expr**>(args));
const select_cache::key_data & kd = m_select_cache.insert_if_not_there(entry, result);
if (kd.m_key != entry) {
dealloc(entry);
return;
}
m_manager.inc_array_ref(num_args, args);
m_manager.inc_ref(result);
TRACE("cache_select", tout << m_select_cache.size() << "\n";);
}
void array_simplifier_plugin::prune_select_cache() {
if (m_select_cache.size() > m_select_cache_max_size) {
flush_select_cache();
}
}
void array_simplifier_plugin::prune_store_cache() {
if (m_store_cache_size > m_store_cache_max_size) {
flush_store_cache();
}
}
void array_simplifier_plugin::flush_select_cache() {
select_cache::iterator it = m_select_cache.begin();
select_cache::iterator end = m_select_cache.end();
for (; it != end; ++it) {
ptr_vector<expr> * e = (*it).m_key;
m_manager.dec_array_ref(e->size(), e->begin());
m_manager.dec_ref((*it).m_value);
dealloc(e);
}
m_select_cache.reset();
}
void array_simplifier_plugin::flush_store_cache() {
store_cache::iterator it = m_store_cache.begin();
store_cache::iterator end = m_store_cache.end();
for (; it != end; ++it) {
m_manager.dec_ref((*it).m_key);
const_map::iterator mit = (*it).m_value->m_map.begin();
const_map::iterator mend = (*it).m_value->m_map.end();
for (; mit != mend; ++mit) {
m_manager.dec_ref((*mit).m_value);
}
dealloc((*it).m_value);
}
m_store_cache.reset();
m_store_cache_size = 0;
}
void array_simplifier_plugin::flush_caches() {
flush_select_cache();
flush_store_cache();
}
void array_simplifier_plugin::mk_set_difference(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args == 2);
result = m_manager.mk_app(m_fid, OP_SET_DIFFERENCE, 0, 0, num_args, args);
}
void array_simplifier_plugin::mk_empty_set(sort* ty, expr_ref & result) {
parameter param(ty);
expr* args[1] = { m_manager.mk_false() };
result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, args);
}
void array_simplifier_plugin::mk_full_set(sort* ty, expr_ref & result) {
parameter param(ty);
expr* args[1] = { m_manager.mk_true() };
result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, args);
}
bool array_simplifier_plugin::same_args(unsigned num_args, expr * const * args1, expr * const * args2) {
for (unsigned i = 0; i < num_args; ++i) {
if (args1[i] != args2[i]) {
return false;
}
}
return true;
}
void array_simplifier_plugin::mk_store(func_decl* f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 3);
expr* arg0 = args[0];
expr* argn = args[num_args-1];
//
// store(store(a,i,v),i,w) = store(a,i,w)
//
if (is_store(arg0) &&
same_args(num_args-2, args+1, to_app(arg0)->get_args()+1)) {
expr_ref_buffer new_args(m_manager);
new_args.push_back(to_app(arg0)->get_arg(0));
for (unsigned i = 1; i < num_args; ++i) {
new_args.push_back(args[i]);
}
reduce(f, num_args, new_args.c_ptr(), result);
TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
return;
}
//
// store(const(v),i,v) = const(v)
//
if (is_const_array(arg0) &&
to_app(arg0)->get_arg(0) == args[num_args-1]) {
result = arg0;
TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
return;
}
//
// store(a, i, select(a, i)) = a
//
if (is_select(argn) &&
(to_app(argn)->get_num_args() == num_args - 1) &&
same_args(num_args-1, args, to_app(argn)->get_args())) {
TRACE("dummy_store", tout << "dummy store simplified mk_store(\n";
for (unsigned i = 0; i < num_args; i++) ast_ll_pp(tout, m_manager, args[i]);
tout << ") =====>\n";
ast_ll_pp(tout, m_manager, arg0););
result = arg0;
TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
return;
}
//
// store(store(a,i,v),j,w) -> store(store(a,j,w),i,v)
// if i, j are values, i->get_id() < j->get_id()
//
if (m_params.m_array_canonize_simplify &&
is_store(arg0) &&
all_values(num_args-2, args+1) &&
all_values(num_args-2, to_app(arg0)->get_args()+1) &&
lex_lt(num_args-2, args+1, to_app(arg0)->get_args()+1)) {
expr* const* args2 = to_app(arg0)->get_args();
expr_ref_buffer new_args(m_manager);
new_args.push_back(args2[0]);
for (unsigned i = 1; i < num_args; ++i) {
new_args.push_back(args[i]);
}
reduce(f, num_args, new_args.c_ptr(), result);
new_args.reset();
new_args.push_back(result);
for (unsigned i = 1; i < num_args; ++i) {
new_args.push_back(args2[i]);
}
result = m_manager.mk_app(m_fid, OP_STORE, num_args, new_args.c_ptr());
TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
return;
}
result = m_manager.mk_app(m_fid, OP_STORE, num_args, args);
TRACE("array_simplifier", tout << "default: " << mk_pp(result.get(), m_manager) << "\n";);
}
void array_simplifier_plugin::mk_select_as_array(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(is_as_array(args[0]));
func_decl * f = get_as_array_func_decl(to_app(args[0]));
result = m_manager.mk_app(f, num_args - 1, args+1);
}
void array_simplifier_plugin::mk_select_as_array_tree(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(is_as_array_tree(args[0]));
SASSERT(m_manager.is_ite(args[0]));
ptr_buffer<app, 32> todo;
obj_map<app, app *> cache;
app_ref_buffer trail(m_manager);
todo.push_back(to_app(args[0]));
while (!todo.empty()) {
app * curr = todo.back();
SASSERT(m_manager.is_ite(curr));
expr * branches[2] = {0, 0};
bool visited = true;
for (unsigned i = 0; i < 2; i++) {
expr * arg = curr->get_arg(i+1);
if (is_as_array(arg)) {
branches[i] = m_manager.mk_app(get_as_array_func_decl(to_app(arg)), num_args - 1, args+1);
}
else {
SASSERT(m_manager.is_ite(arg));
app * new_arg = 0;
if (!cache.find(to_app(arg), new_arg)) {
todo.push_back(to_app(arg));
visited = false;
}
else {
branches[i] = new_arg;
}
}
}
if (visited) {
todo.pop_back();
app * new_curr = m_manager.mk_ite(curr->get_arg(0), branches[0], branches[1]);
trail.push_back(new_curr);
cache.insert(curr, new_curr);
}
}
SASSERT(cache.contains(to_app(args[0])));
app * r = 0;
cache.find(to_app(args[0]), r);
result = r;
}
void array_simplifier_plugin::mk_select(unsigned num_args, expr * const * args, expr_ref & result) {
expr * r = 0;
if (is_as_array(args[0])) {
mk_select_as_array(num_args, args, result);
return;
}
if (is_as_array_tree(args[0])) {
mk_select_as_array_tree(num_args, args, result);
return;
}
bool is_const_select = num_args == 2 && m_manager.is_value(args[1]);
app* const_index = is_const_select?to_app(args[1]):0;
unsigned num_const_stores = 0;
expr_ref tmp(m_manager);
expr* args2[2];
if (is_const_select) {
switch(mk_select_const(args[0], const_index, tmp)) {
case NOT_CACHED:
break;
case FOUND_VALUE:
TRACE("mk_select", tout << "found value\n"; ast_ll_pp(tout, m_manager, tmp.get()); );
result = tmp.get();
// value of select is stored under result.
return;
case FOUND_DEFAULT:
args2[0] = tmp.get();
args2[1] = args[1];
args = args2;
is_const_select = false;
break;
}
}
SASSERT(num_args > 0);
ptr_vector<expr> & entry = m_tmp2;
entry.reset();
entry.append(num_args, args);
expr * entry0 = entry[0];
SASSERT(m_todo.empty());
m_todo.push_back(entry0);
while (!m_todo.empty()) {
expr * m = m_todo.back();
TRACE("array_simplifier", tout << mk_bounded_pp(m, m_manager) << "\n";);
if (is_store(m)) {
expr * nested_array = to_app(m)->get_arg(0);
expr * else_branch = 0;
entry[0] = nested_array;
if (is_const_select) {
if (m_manager.is_value(to_app(m)->get_arg(1))) {
app* const_index2 = to_app(to_app(m)->get_arg(1));
//
// we found the value, all other stores are different.
// there is no need to recurse.
//
if (const_index == const_index2) {
result = to_app(m)->get_arg(2);
cache_store(num_const_stores, args[0]);
m_todo.reset();
return;
}
++num_const_stores;
}
else {
is_const_select = false;
}
}
if (m_select_cache.find(&entry, else_branch)) {
expr_ref_buffer eqs(m_manager);
for (unsigned i = 1; i < num_args ; ++i) {
expr * a = args[i];
expr * b = to_app(m)->get_arg(i);
expr_ref eq(m_manager);
m_simp.mk_eq(a, b, eq);
eqs.push_back(eq.get());
}
expr_ref cond(m_manager);
m_simp.mk_and(eqs.size(), eqs.c_ptr(), cond);
expr * then_branch = to_app(m)->get_arg(num_args);
if (m_manager.is_true(cond.get())) {
result = then_branch;
}
else if (m_manager.is_false(cond.get())) {
result = else_branch;
}
else {
m_simp.mk_ite(cond.get(), then_branch, else_branch, result);
}
entry[0] = m;
cache_select(entry.size(), entry.c_ptr(), result.get());
m_todo.pop_back();
}
else {
m_todo.push_back(nested_array);
}
}
else if (is_const_array(m)) {
entry[0] = m;
cache_select(entry.size(), entry.c_ptr(), to_app(m)->get_arg(0));
m_todo.pop_back();
}
else {
entry[0] = m;
TRACE("array_simplifier", {
for (unsigned i = 0; i < entry.size(); ++i) {
tout << mk_bounded_pp(entry[i], m_manager) << ": "
<< mk_bounded_pp(m_manager.get_sort(entry[i]), m_manager) << "\n";
}}
);
r = m_manager.mk_app(m_fid, OP_SELECT, 0, 0, entry.size(), entry.c_ptr());
cache_select(entry.size(), entry.c_ptr(), r);
m_todo.pop_back();
}
}
cache_store(num_const_stores, args[0]);
entry[0] = entry0;
#ifdef Z3DEBUG
bool f =
#endif
m_select_cache.find(&entry, r);
SASSERT(f);
result = r;
prune_select_cache();
prune_store_cache();
TRACE("mk_select",
for (unsigned i = 0; i < num_args; i++) {
ast_ll_pp(tout, m_manager, args[i]); tout << "\n";
};
tout << "is_store: " << is_store(args[0]) << "\n";
ast_ll_pp(tout, m_manager, r););
}
void array_simplifier_plugin::mk_map(func_decl* f, expr* a, expr* b, expr_ref& result) {
expr* exprs[2] = { a, b };
parameter param(f);
result = m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, &param, 2, exprs );
}
void array_simplifier_plugin::mk_map(func_decl* f, expr* a, expr_ref& result) {
parameter param(f);
result = m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, &param, 1, &a );
}

View file

@ -0,0 +1,154 @@
/*++
Copyright (c) 2008 Microsoft Corporation
Module Name:
array_simplifier_plugin.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2008-05-05
Revision History:
--*/
#ifndef _ARRAY_SIMPLIFIER_PLUGIN_H_
#define _ARRAY_SIMPLIFIER_PLUGIN_H_
#include"ast.h"
#include"map.h"
#include"array_decl_plugin.h"
#include"simplifier_plugin.h"
#include"basic_simplifier_plugin.h"
#include"theory_array_params.h"
#include"simplifier.h"
#include"obj_hashtable.h"
#include"lbool.h"
class array_simplifier_plugin : public simplifier_plugin {
typedef ptr_vector<expr> entry;
struct entry_hash_proc {
unsigned operator()(ptr_vector<expr> * entry) const {
return get_exprs_hash(entry->size(), entry->begin(), 0xbeef1010);
}
};
struct entry_eq_proc {
bool operator()(ptr_vector<expr> * entry1, ptr_vector<expr> * entry2) const {
if (entry1->size() != entry2->size()) return false;
return compare_arrays(entry1->begin(), entry2->begin(), entry1->size());
}
};
typedef map<entry *, expr *, entry_hash_proc, entry_eq_proc> select_cache;
struct args_entry {
unsigned m_arity;
expr* const* m_args;
args_entry(unsigned a, expr* const* args) : m_arity(a), m_args(args) {}
args_entry() : m_arity(0), m_args(0) {}
};
struct args_entry_hash_proc {
unsigned operator()(args_entry const& e) const {
return get_exprs_hash(e.m_arity, e.m_args, 0xbeef1010);
}
};
struct args_entry_eq_proc {
bool operator()(args_entry const& e1, args_entry const& e2) const {
if (e1.m_arity != e2.m_arity) return false;
return compare_arrays(e1.m_args, e2.m_args, e1.m_arity);
}
};
typedef hashtable<args_entry, args_entry_hash_proc, args_entry_eq_proc> arg_table;
array_util m_util;
basic_simplifier_plugin& m_simp;
simplifier& m_simplifier;
theory_array_params const& m_params;
select_cache m_select_cache;
ptr_vector<expr> m_tmp;
ptr_vector<expr> m_tmp2;
ptr_vector<expr> m_todo;
static const unsigned m_select_cache_max_size = 100000;
typedef obj_map<expr, expr*> const_map;
class store_info {
store_info();
store_info(store_info const&);
public:
const_map m_map;
expr_ref m_default;
store_info(ast_manager& m, expr* d): m_default(d, m) {}
};
typedef obj_map<expr, store_info*> store_cache;
store_cache m_store_cache;
unsigned m_store_cache_size;
static const unsigned m_store_cache_max_size = 10000;
static const unsigned m_const_store_threshold = 5;
enum const_select_result {
NOT_CACHED,
FOUND_DEFAULT,
FOUND_VALUE
};
public:
array_simplifier_plugin(ast_manager & m, basic_simplifier_plugin& s, simplifier& simp, theory_array_params const& p);
virtual ~array_simplifier_plugin();
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);
virtual void flush_caches();
private:
bool is_select(expr* n) const { return m_util.is_select(n); }
bool is_store(expr * n) const { return m_util.is_store(n); }
bool is_const_array(expr * n) const { return m_util.is_const(n); }
bool is_as_array(expr * n) const { return m_util.is_as_array(n); }
bool is_as_array_tree(expr * n) { return m_util.is_as_array_tree(n); }
func_decl * get_as_array_func_decl(app * n) const { return m_util.get_as_array_func_decl(n); }
void mk_select_as_array(unsigned num_args, expr * const * args, expr_ref & result);
void mk_select_as_array_tree(unsigned num_args, expr * const * args, expr_ref & result);
bool is_enumerated(expr* n, expr_ref& c, ptr_vector<expr>& keys, ptr_vector<expr>& vals);
const_select_result mk_select_const(expr* m, app* index, expr_ref& result);
void cache_store(unsigned num_stores, expr* nested_store);
void cache_select(unsigned num_args, expr * const * args, expr * result);
void prune_select_cache();
void prune_store_cache();
void flush_select_cache();
void flush_store_cache();
void mk_set_difference(unsigned num_args, expr * const * args, expr_ref & result);
void mk_empty_set(sort* ty, expr_ref & result);
void mk_full_set(sort* ty, expr_ref & result);
void mk_select(unsigned num_args, expr * const * args, expr_ref & result);
void mk_store(func_decl* f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_map(func_decl* f, expr* a, expr* b, expr_ref & result);
void mk_map(func_decl* f, expr* a, expr_ref & result);
bool same_args(unsigned num_args, expr * const * args1, expr * const * args2);
void get_stores(expr* n, unsigned& arity, expr*& m, ptr_vector<expr*const>& stores);
lbool eq_default(expr* def, unsigned arity, unsigned num_st, expr*const* const* st);
bool insert_table(expr* def, unsigned arity, unsigned num_st, expr*const* const* st, arg_table& table);
lbool eq_stores(expr* def, unsigned arity, unsigned num_st1, expr*const* const* st1, unsigned num_st2, expr*const* const* st2);
bool same_store(unsigned num_args, expr* const* args) const;
bool all_const_array(unsigned num_args, expr* const* args) const;
bool all_values(unsigned num_args, expr* const* args) const;
bool lex_lt(unsigned num_args, expr* const* args1, expr* const* args2);
};
#endif /* _ARRAY_SIMPLIFIER_PLUGIN_H_ */

View file

@ -0,0 +1,54 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
base_simplifier.h
Abstract:
Base class for expression simplifier functors.
Author:
Leonardo (leonardo) 2008-01-11
Notes:
--*/
#ifndef _BASE_SIMPLIFIER_H_
#define _BASE_SIMPLIFIER_H_
#include"expr_map.h"
/**
\brief Implements basic functionality used by expression simplifiers.
*/
class base_simplifier {
protected:
ast_manager & m_manager;
expr_map m_cache;
ptr_vector<expr> m_todo;
void cache_result(expr * n, expr * r, proof * p) { m_cache.insert(n, r, p); }
void reset_cache() { m_cache.reset(); }
void flush_cache() { m_cache.flush(); }
void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); }
void visit(expr * n, bool & visited) {
if (!is_cached(n)) {
m_todo.push_back(n);
visited = false;
}
}
public:
base_simplifier(ast_manager & m):
m_manager(m),
m_cache(m, m.fine_grain_proofs()) {
}
bool is_cached(expr * n) const { return m_cache.contains(n); }
ast_manager & get_manager() { return m_manager; }
};
#endif /* _BASE_SIMPLIFIER_H_ */

View file

@ -0,0 +1,142 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
basic_simplifier_plugin.cpp
Abstract:
Simplifier for the basic family.
Author:
Leonardo (leonardo) 2008-01-07
--*/
#include"basic_simplifier_plugin.h"
#include"ast_ll_pp.h"
#include"bool_rewriter.h"
basic_simplifier_plugin::basic_simplifier_plugin(ast_manager & m):
simplifier_plugin(symbol("basic"), m),
m_rewriter(alloc(bool_rewriter, m)) {
}
basic_simplifier_plugin::~basic_simplifier_plugin() {
dealloc(m_rewriter);
}
bool basic_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
set_reduce_invoked();
SASSERT(f->get_family_id() == m_manager.get_basic_family_id());
basic_op_kind k = static_cast<basic_op_kind>(f->get_decl_kind());
switch (k) {
case OP_FALSE:
case OP_TRUE:
return false;
case OP_EQ:
SASSERT(num_args == 2);
mk_eq(args[0], args[1], result);
return true;
case OP_DISTINCT:
mk_distinct(num_args, args, result);
return true;
case OP_ITE:
SASSERT(num_args == 3);
mk_ite(args[0], args[1], args[2], result);
return true;
case OP_AND:
mk_and(num_args, args, result);
return true;
case OP_OR:
mk_or(num_args, args, result);
return true;
case OP_IMPLIES:
mk_implies(args[0], args[1], result);
return true;
case OP_IFF:
mk_iff(args[0], args[1], result);
return true;
case OP_XOR:
mk_xor(args[0], args[1], result);
return true;
case OP_NOT:
SASSERT(num_args == 1);
mk_not(args[0], result);
return true;
default:
UNREACHABLE();
return false;
}
}
/**
\brief Return true if \c rhs is of the form (ite c t1 t2) and are_distinct(lhs, t1) and are_distinct(lhs, t2).
*/
static bool is_lhs_diseq_rhs_ite_branches(ast_manager & m, expr * lhs, expr * rhs) {
return m.is_ite(rhs) && m.are_distinct(lhs, to_app(rhs)->get_arg(1)) && m.are_distinct(lhs, to_app(rhs)->get_arg(2));
}
/**
\brief Return true if \c rhs is of the form (ite c t1 t2) and lhs = t1 && are_distinct(lhs, t2)
*/
static bool is_lhs_eq_rhs_ite_then(ast_manager & m, expr * lhs, expr * rhs) {
return m.is_ite(rhs) && lhs == to_app(rhs)->get_arg(1) && m.are_distinct(lhs, to_app(rhs)->get_arg(2));
}
/**
\brief Return true if \c rhs is of the form (ite c t1 t2) and are_distinct(lhs,t1) && lhs = t2
*/
static bool is_lhs_eq_rhs_ite_else(ast_manager & m, expr * lhs, expr * rhs) {
return m.is_ite(rhs) && lhs == to_app(rhs)->get_arg(2) && m.are_distinct(lhs, to_app(rhs)->get_arg(1));
}
void basic_simplifier_plugin::mk_eq(expr * lhs, expr * rhs, expr_ref & result) {
// (= t1 (ite C t2 t3)) --> false if are_distinct(t1, t2) && are_distinct(t1, t3)
if (is_lhs_diseq_rhs_ite_branches(m_manager, lhs, rhs) || is_lhs_diseq_rhs_ite_branches(m_manager, rhs, lhs)) {
result = m_manager.mk_false();
}
// (= t1 (ite C t2 t3)) --> C if t1 = t2 && are_distinct(t1, t3)
else if (is_lhs_eq_rhs_ite_then(m_manager, lhs, rhs)) {
result = to_app(rhs)->get_arg(0);
}
// (= t1 (ite C t2 t3)) --> C if t1 = t2 && are_distinct(t1, t3)
else if (is_lhs_eq_rhs_ite_then(m_manager, rhs, lhs)) {
result = to_app(lhs)->get_arg(0);
}
// (= t1 (ite C t2 t3)) --> (not C) if t1 = t3 && are_distinct(t1, t2)
else if (is_lhs_eq_rhs_ite_else(m_manager, lhs, rhs)) {
mk_not(to_app(rhs)->get_arg(0), result);
}
// (= t1 (ite C t2 t3)) --> (not C) if t1 = t3 && are_distinct(t1, t2)
else if (is_lhs_eq_rhs_ite_else(m_manager, rhs, lhs)) {
mk_not(to_app(lhs)->get_arg(0), result);
}
else {
m_rewriter->mk_eq(lhs, rhs, result);
}
}
bool basic_simplifier_plugin::eliminate_and() const { return m_rewriter->elim_and(); }
void basic_simplifier_plugin::set_eliminate_and(bool f) { m_rewriter->set_elim_and(f); }
void basic_simplifier_plugin::mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); }
void basic_simplifier_plugin::mk_xor(expr * lhs, expr * rhs, expr_ref & result) { m_rewriter->mk_xor(lhs, rhs, result); }
void basic_simplifier_plugin::mk_implies(expr * lhs, expr * rhs, expr_ref & result) { m_rewriter->mk_implies(lhs, rhs, result); }
void basic_simplifier_plugin::mk_ite(expr * c, expr * t, expr * e, expr_ref & result) { m_rewriter->mk_ite(c, t, e, result); }
void basic_simplifier_plugin::mk_and(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_and(num_args, args, result); }
void basic_simplifier_plugin::mk_or(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_or(num_args, args, result); }
void basic_simplifier_plugin::mk_and(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_and(arg1, arg2, result); }
void basic_simplifier_plugin::mk_or(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_or(arg1, arg2, result); }
void basic_simplifier_plugin::mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_rewriter->mk_and(arg1, arg2, arg3, result); }
void basic_simplifier_plugin::mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_rewriter->mk_or(arg1, arg2, arg3, result); }
void basic_simplifier_plugin::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_nand(num_args, args, result); }
void basic_simplifier_plugin::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_nor(num_args, args, result); }
void basic_simplifier_plugin::mk_nand(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_nand(arg1, arg2, result); }
void basic_simplifier_plugin::mk_nor(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_nor(arg1, arg2, result); }
void basic_simplifier_plugin::mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_distinct(num_args, args, result); }
void basic_simplifier_plugin::mk_not(expr * n, expr_ref & result) { m_rewriter->mk_not(n, result); }
void basic_simplifier_plugin::enable_ac_support(bool flag) {
m_rewriter->set_flat(flag);
}

View file

@ -0,0 +1,78 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
basic_simplifier_plugin.h
Abstract:
Simplifier for the basic family.
Author:
Leonardo (leonardo) 2008-01-07
--*/
#ifndef _BASIC_SIMPLIFIER_PLUGIN_H_
#define _BASIC_SIMPLIFIER_PLUGIN_H_
#include"simplifier_plugin.h"
class bool_rewriter;
/**
\brief Simplifier for the basic family.
*/
class basic_simplifier_plugin : public simplifier_plugin {
bool_rewriter * m_rewriter;
public:
basic_simplifier_plugin(ast_manager & m);
virtual ~basic_simplifier_plugin();
bool_rewriter & get_rewriter() { return *m_rewriter; }
bool eliminate_and() const;
void set_eliminate_and(bool f);
void mk_eq(expr * lhs, expr * rhs, expr_ref & result);
void mk_iff(expr * lhs, expr * rhs, expr_ref & result);
void mk_xor(expr * lhs, expr * rhs, expr_ref & result);
void mk_implies(expr * lhs, expr * rhs, expr_ref & result);
void mk_ite(expr * c, expr * t, expr * e, expr_ref & result);
void mk_and(unsigned num_args, expr * const * args, expr_ref & result);
void mk_or(unsigned num_args, expr * const * args, expr_ref & result);
void mk_and(expr * arg1, expr * arg2, expr_ref & result);
void mk_or(expr * arg1, expr * arg2, expr_ref & result);
void mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
void mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
void mk_nand(unsigned num_args, expr * const * args, expr_ref & result);
void mk_nor(unsigned num_args, expr * const * args, expr_ref & result);
void mk_nand(expr * arg1, expr * arg2, expr_ref & result);
void mk_nor(expr * arg1, expr * arg2, expr_ref & result);
void mk_distinct(unsigned num_args, expr * const * args, expr_ref & result);
void mk_not(expr * n, expr_ref & result);
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
virtual void enable_ac_support(bool flag);
};
/**
\brief Functor that compares expressions, but puts the expressions e and f(e) close to each other, where
f is in family m_fid, and has kind m_dkind;
*/
struct expr_lt_proc {
family_id m_fid;
decl_kind m_dkind;
expr_lt_proc(family_id fid = null_family_id, decl_kind k = null_decl_kind):m_fid(fid), m_dkind(k) {}
unsigned get_id(expr * n) const {
if (m_fid != null_family_id && is_app_of(n, m_fid, m_dkind))
return (to_app(n)->get_arg(0)->get_id() << 1) + 1;
else
return n->get_id() << 1;
}
bool operator()(expr * n1, expr * n2) const {
return get_id(n1) < get_id(n2);
}
};
#endif /* _BASIC_SIMPLIFIER_PLUGIN_H_ */

View file

@ -0,0 +1,421 @@
/*++
Copyright (c) 2009 Microsoft Corporation
Module Name:
bit2cpp.cpp
Abstract:
Routines for simplifying bit2int expressions.
This propagates bv2int over arithmetical symbols as much as possible,
converting arithmetic operations into bit-vector operations.
Author:
Nikolaj Bjorner (nbjorner) 2009-08-28
Revision History:
--*/
#include "bit2int.h"
#include "ast_pp.h"
#include "ast_ll_pp.h"
#include "for_each_ast.h"
#define CHECK(_x_) if (!(_x_)) { UNREACHABLE(); }
bit2int::bit2int(ast_manager & m) :
m_manager(m), m_bv_util(m), m_arith_util(m), m_cache(m), m_bit0(m) {
m_bit0 = m_bv_util.mk_numeral(0,1);
}
void bit2int::operator()(expr * m, expr_ref & result, proof_ref& p) {
flush_cache();
expr_reduce emap(*this);
for_each_ast(emap, m);
result = get_cached(m);
if (m_manager.proofs_enabled() && m != result.get()) {
// TBD: rough
p = m_manager.mk_rewrite(m, result);
}
TRACE("bit2int",
tout << mk_pp(m, m_manager) << "======>\n"
<< mk_pp(result, m_manager) << "\n";);
}
unsigned bit2int::get_b2i_size(expr* n) {
SASSERT(m_bv_util.is_bv2int(n));
return m_bv_util.get_bv_size(to_app(n)->get_arg(0));
}
unsigned bit2int::get_numeral_bits(numeral const& k) {
numeral two(2);
numeral n(abs(k));
unsigned num_bits = 1;
n = div(n, two);
while (n.is_pos()) {
++num_bits;
n = div(n, two);
}
return num_bits;
}
void bit2int::align_size(expr* e, unsigned sz, expr_ref& result) {
unsigned sz1 = m_bv_util.get_bv_size(e);
SASSERT(sz1 <= sz);
m_bv_simplifier->mk_zeroext(sz-sz1, e, result);
}
void bit2int::align_sizes(expr_ref& a, expr_ref& b) {
unsigned sz1 = m_bv_util.get_bv_size(a);
unsigned sz2 = m_bv_util.get_bv_size(b);
expr_ref tmp(m_manager);
if (sz1 > sz2) {
m_bv_simplifier->mk_zeroext(sz1-sz2, b, tmp);
b = tmp;
}
else if (sz2 > sz1) {
m_bv_simplifier->mk_zeroext(sz2-sz1, a, tmp);
a = tmp;
}
}
bool bit2int::extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv) {
numeral k;
bool is_int;
if (m_bv_util.is_bv2int(n)) {
bv = to_app(n)->get_arg(0);
sz = m_bv_util.get_bv_size(bv);
sign = false;
return true;
}
else if (m_arith_util.is_numeral(n, k, is_int) && is_int) {
sz = get_numeral_bits(k);
bv = m_bv_util.mk_numeral(k, m_bv_util.mk_sort(sz));
sign = k.is_neg();
return true;
}
else {
return false;
}
}
bool bit2int::mk_add(expr* e1, expr* e2, expr_ref& result) {
unsigned sz1, sz2;
bool sign1, sign2;
expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager);
if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 &&
extract_bv(e2, sz2, sign2, tmp2) && !sign2) {
unsigned sz;
numeral k;
if (m_bv_util.is_numeral(tmp1, k, sz) && k.is_zero()) {
result = e2;
return true;
}
if (m_bv_util.is_numeral(tmp2, k, sz) && k.is_zero()) {
result = e1;
return true;
}
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_zeroext(1, tmp1, tmp1);
m_bv_simplifier->mk_zeroext(1, tmp2, tmp2);
SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
m_bv_simplifier->mk_add(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
return true;
}
return false;
}
bool bit2int::mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result) {
unsigned sz1, sz2;
bool sign1, sign2;
expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager);
if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 &&
extract_bv(e2, sz2, sign2, tmp2) && !sign2) {
align_sizes(tmp1, tmp2);
SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
switch(ty) {
case lt:
m_bv_simplifier->mk_leq_core(false, tmp2, tmp1, tmp3);
result = m_manager.mk_not(tmp3);
break;
case le:
m_bv_simplifier->mk_leq_core(false,tmp1, tmp2, result);
break;
case eq:
result = m_manager.mk_eq(tmp1,tmp2);
break;
}
return true;
}
return false;
}
bool bit2int::mk_mul(expr* e1, expr* e2, expr_ref& result) {
unsigned sz1, sz2;
bool sign1, sign2;
expr_ref tmp1(m_manager), tmp2(m_manager);
expr_ref tmp3(m_manager);
if (extract_bv(e1, sz1, sign1, tmp1) &&
extract_bv(e2, sz2, sign2, tmp2)) {
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp1), tmp1, tmp1);
m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp2), tmp2, tmp2);
SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
m_bv_simplifier->mk_mul(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
if (sign1 != sign2) {
result = m_arith_util.mk_uminus(result);
}
return true;
}
return false;
}
bool bit2int::is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg) {
ptr_vector<expr> todo;
expr_ref tmp(m_manager);
numeral k;
bool is_int;
todo.push_back(n);
m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), pos);
m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), neg);
while (!todo.empty()) {
n = todo.back();
todo.pop_back();
if (m_bv_util.is_bv2int(n)) {
CHECK(mk_add(n, pos, pos));
}
else if (m_arith_util.is_numeral(n, k, is_int) && is_int) {
if (k.is_nonneg()) {
CHECK(mk_add(n, pos, pos));
}
else {
tmp = m_arith_util.mk_numeral(-k, true);
CHECK(mk_add(tmp, neg, neg));
}
}
else if (m_arith_util.is_add(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) {
todo.push_back(to_app(n)->get_arg(i));
}
}
else if (m_arith_util.is_mul(n) &&
to_app(n)->get_num_args() == 2 &&
m_arith_util.is_numeral(to_app(n)->get_arg(0), k, is_int) && is_int && k.is_minus_one() &&
m_bv_util.is_bv2int(to_app(n)->get_arg(1))) {
CHECK(mk_add(to_app(n)->get_arg(1), neg, neg));
}
else if (m_arith_util.is_mul(n) &&
to_app(n)->get_num_args() == 2 &&
m_arith_util.is_numeral(to_app(n)->get_arg(1), k, is_int) && is_int && k.is_minus_one() &&
m_bv_util.is_bv2int(to_app(n)->get_arg(0))) {
CHECK(mk_add(to_app(n)->get_arg(0), neg, neg));
}
else if (m_arith_util.is_uminus(n) &&
m_bv_util.is_bv2int(to_app(n)->get_arg(0))) {
CHECK(mk_add(to_app(n)->get_arg(0), neg, neg));
}
else {
TRACE("bit2int", tout << "Not a poly: " << mk_pp(n, m_manager) << "\n";);
return false;
}
}
return true;
}
void bit2int::visit(quantifier* q) {
expr_ref result(m_manager);
result = get_cached(q->get_expr());
result = m_manager.update_quantifier(q, result);
cache_result(q, result);
}
void bit2int::visit(app* n) {
func_decl* f = n->get_decl();
unsigned num_args = n->get_num_args();
m_args.reset();
for (unsigned i = 0; i < num_args; ++i) {
m_args.push_back(get_cached(n->get_arg(i)));
}
expr* const* args = m_args.c_ptr();
bool has_b2i =
m_arith_util.is_le(n) || m_arith_util.is_ge(n) || m_arith_util.is_gt(n) ||
m_arith_util.is_lt(n) || m_manager.is_eq(n);
expr_ref result(m_manager);
for (unsigned i = 0; !has_b2i && i < num_args; ++i) {
has_b2i = m_bv_util.is_bv2int(args[i]);
}
if (!has_b2i) {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
return;
}
//
// bv2int(x) + bv2int(y) -> bv2int(pad(x) + pad(y))
// bv2int(x) + k -> bv2int(pad(x) + pad(k))
// bv2int(x) * bv2int(y) -> bv2int(pad(x) * pad(y))
// bv2int(x) * k -> sign(k)*bv2int(pad(x) * pad(k))
// bv2int(x) - bv2int(y) <= z -> bv2int(x) <= bv2int(y) + z
// bv2int(x) <= z - bv2int(y) -> bv2int(x) + bv2int(y) <= z
//
expr* e1, *e2;
expr_ref tmp1(m_manager), tmp2(m_manager);
expr_ref tmp3(m_manager);
expr_ref pos1(m_manager), neg1(m_manager);
expr_ref pos2(m_manager), neg2(m_manager);
expr_ref e2bv(m_manager);
bool sign2;
numeral k;
unsigned sz2;
if (num_args >= 2) {
e1 = args[0];
e2 = args[1];
}
if (m_arith_util.is_add(n) && num_args >= 1) {
result = e1;
for (unsigned i = 1; i < num_args; ++i) {
e1 = result;
e2 = args[i];
if (!mk_add(e1, e2, result)) {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
return;
}
}
cache_result(n, result);
}
else if (m_arith_util.is_mul(n) && num_args >= 1) {
result = e1;
for (unsigned i = 1; i < num_args; ++i) {
e1 = result;
e2 = args[i];
if (!mk_mul(e1, e2, result)) {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
return;
}
}
cache_result(n, result);
}
else if (m_manager.is_eq(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(eq, tmp1, tmp2, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_le(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(le, tmp1, tmp2, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_lt(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(lt, tmp1, tmp2, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_ge(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(le, tmp2, tmp1, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_gt(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(lt, tmp2, tmp1, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_mod(n) &&
is_bv_poly(e1, pos1, neg1) &&
extract_bv(e2, sz2, sign2, e2bv) && !sign2) {
//
// (pos1 - neg1) mod e2 = (pos1 + (e2 - (neg1 mod e2))) mod e2
//
unsigned sz_p, sz_n, sz;
bool sign_p, sign_n;
expr_ref tmp_p(m_manager), tmp_n(m_manager);
CHECK(extract_bv(pos1, sz_p, sign_p, tmp_p));
CHECK(extract_bv(neg1, sz_n, sign_n, tmp_n));
SASSERT(!sign_p && !sign_n);
// pos1 mod e2
if (m_bv_util.is_numeral(tmp_n, k, sz) && k.is_zero()) {
tmp1 = tmp_p;
tmp2 = e2bv;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
cache_result(n, result);
return;
}
// neg1 mod e2;
tmp1 = tmp_n;
tmp2 = e2bv;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
// e2 - (neg1 mod e2)
tmp1 = e2bv;
tmp2 = tmp3;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_sub(tmp1, tmp2, tmp3);
// pos1 + (e2 - (neg1 mod e2))
tmp1 = tmp_p;
tmp2 = tmp3;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_zeroext(1, tmp1, tmp_p);
m_bv_simplifier->mk_zeroext(1, tmp2, tmp_n);
m_bv_simplifier->mk_add(tmp_p, tmp_n, tmp1);
// (pos1 + (e2 - (neg1 mod e2))) mod e2
tmp2 = e2bv;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
cache_result(n, result);
}
else {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
}
}
expr * bit2int::get_cached(expr * n) const {
return const_cast<bit2int*>(this)->m_cache.find(n);
}
void bit2int::cache_result(expr * n, expr * r) {
TRACE("bit2int_verbose", tout << "caching:\n" << mk_ll_pp(n, m_manager) <<
"======>\n" << mk_ll_pp(r, m_manager) << "\n";);
m_cache.insert(n, r);
}

View file

@ -0,0 +1,96 @@
/*++
Copyright (c) 2009 Microsoft Corporation
Module Name:
bit2int.h
Abstract:
Routines for simplifying bit2int expressions.
Author:
Nikolaj Bjorner (nbjorner) 2009-08-28
Revision History:
--*/
#ifndef _BIT2INT_H_
#define _BIT2INT_H_
#include"bv_decl_plugin.h"
#include"arith_decl_plugin.h"
#include"act_cache.h"
#include"basic_simplifier_plugin.h"
#include"bv_simplifier_plugin.h"
class bit2int {
protected:
typedef rational numeral;
enum eq_type {
lt,
le,
eq
};
class expr_reduce {
bit2int& m_super;
public:
expr_reduce(bit2int& s) : m_super(s) {}
void operator()(var* v) {
m_super.cache_result(v, v);
}
void operator()(quantifier* q) {
m_super.visit(q);
}
void operator()(app* a) {
m_super.visit(a);
}
void operator()(ast* a) {}
};
typedef act_cache expr_map;
ast_manager & m_manager;
bv_util m_bv_util;
arith_util m_arith_util;
bv_simplifier_plugin * m_bv_simplifier;
expr_map m_cache; // map: ast -> ast ref. counters are incremented when inserted here.
expr_ref m_bit0;
ptr_vector<expr> m_args;
void visit(app* n);
void visit(quantifier* q);
unsigned get_b2i_size(expr * n);
bool extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv);
unsigned get_numeral_bits(numeral const& k);
bool is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg);
bool mk_mul(expr* a, expr* b, expr_ref& result);
bool mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result);
bool mk_add(expr* e1, expr* e2, expr_ref& result);
expr * get_cached(expr * n) const;
bool is_cached(expr * n) const { return get_cached(n) != 0; }
void cache_result(expr * n, expr * r);
void reset_cache() { m_cache.reset(); }
void flush_cache() { m_cache.cleanup(); }
void align_size(expr* e, unsigned sz, expr_ref& result);
void align_sizes(expr_ref& a, expr_ref& b);
public:
bit2int(ast_manager & m);
void set_bv_simplifier(bv_simplifier_plugin * p) { m_bv_simplifier = p; }
void operator()(expr * m, expr_ref & result, proof_ref& p);
};
#endif /* _BIT2INT_H_ */

View file

@ -0,0 +1,113 @@
#include "bv_elim.h"
#include "bv_decl_plugin.h"
#include "var_subst.h"
#include <sstream>
void bv_elim::elim(quantifier* q, quantifier_ref& r) {
svector<symbol> names, _names;
sort_ref_buffer sorts(m_manager), _sorts(m_manager);
expr_ref_buffer pats(m_manager);
expr_ref_buffer no_pats(m_manager);
expr_ref_buffer subst_map(m_manager), _subst_map(m_manager);
var_subst subst(m_manager);
bv_util bv(m_manager);
expr_ref new_body(m_manager);
expr* old_body = q->get_expr();
unsigned num_decls = q->get_num_decls();
family_id bfid = m_manager.get_family_id("bv");
//
// Traverse sequence of bound variables to eliminate
// bit-vecctor variables and replace them by
// Booleans.
//
unsigned var_idx = 0;
for (unsigned i = num_decls; i > 0; ) {
--i;
sort* s = q->get_decl_sort(i);
symbol nm = q->get_decl_name(i);
if (bv.is_bv_sort(s)) {
// convert n-bit bit-vector variable into sequence of n-Booleans.
unsigned num_bits = bv.get_bv_size(s);
expr_ref_buffer args(m_manager);
expr_ref bv(m_manager);
for (unsigned j = 0; j < num_bits; ++j) {
std::ostringstream new_name;
new_name << nm.str();
new_name << "_";
new_name << j;
var* v = m_manager.mk_var(var_idx++, m_manager.mk_bool_sort());
args.push_back(v);
_sorts.push_back(m_manager.mk_bool_sort());
_names.push_back(symbol(new_name.str().c_str()));
}
bv = m_manager.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr());
_subst_map.push_back(bv.get());
}
else {
_subst_map.push_back(m_manager.mk_var(var_idx++, s));
_sorts.push_back(s);
_names.push_back(nm);
}
}
//
// reverse the vectors.
//
SASSERT(_names.size() == _sorts.size());
for (unsigned i = _names.size(); i > 0; ) {
--i;
names.push_back(_names[i]);
sorts.push_back(_sorts[i]);
}
for (unsigned i = _subst_map.size(); i > 0; ) {
--i;
subst_map.push_back(_subst_map[i]);
}
expr* const* sub = subst_map.c_ptr();
unsigned sub_size = subst_map.size();
subst(old_body, sub_size, sub, new_body);
for (unsigned j = 0; j < q->get_num_patterns(); j++) {
expr_ref pat(m_manager);
subst(q->get_pattern(j), sub_size, sub, pat);
pats.push_back(pat);
}
for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
expr_ref nopat(m_manager);
subst(q->get_no_pattern(j), sub_size, sub, nopat);
no_pats.push_back(nopat);
}
r = m_manager.mk_quantifier(true,
names.size(),
sorts.c_ptr(),
names.c_ptr(),
new_body.get(),
q->get_weight(),
q->get_qid(),
q->get_skid(),
pats.size(), pats.c_ptr(),
no_pats.size(), no_pats.c_ptr());
}
bool bv_elim_star::visit_quantifier(quantifier* q) {
// behave like destructive resolution, do not recurse.
return true;
}
void bv_elim_star::reduce1_quantifier(quantifier* q) {
quantifier_ref r(m_manager);
proof_ref pr(m_manager);
m_bv_elim.elim(q, r);
if (m_manager.fine_grain_proofs()) {
pr = m_manager.mk_rewrite(q, r.get());
}
else {
pr = 0;
}
cache_result(q, r, pr);
}

View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bv_elim.h
Abstract:
Eliminate bit-vectors variables from clauses, by
replacing them by bound Boolean variables.
Author:
Nikolaj Bjorner (nbjorner) 2008-12-16.
Revision History:
--*/
#ifndef _BV_ELIM_H_
#define _BV_ELIM_H_
#include "ast.h"
#include "simplifier.h"
class bv_elim {
ast_manager& m_manager;
public:
bv_elim(ast_manager& m) : m_manager(m) {};
void elim(quantifier* q, quantifier_ref& r);
};
class bv_elim_star : public simplifier {
protected:
bv_elim m_bv_elim;
virtual bool visit_quantifier(quantifier* q);
virtual void reduce1_quantifier(quantifier* q);
public:
bv_elim_star(ast_manager& m) : simplifier(m), m_bv_elim(m) { enable_ac_support(false); }
virtual ~bv_elim_star() {}
};
#endif /* _BV_ELIM_H_ */

View file

@ -0,0 +1,39 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bv_simplifier_params.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-10.
Revision History:
--*/
#ifndef _BV_SIMPLIFIER_PARAMS_H_
#define _BV_SIMPLIFIER_PARAMS_H_
#include"ini_file.h"
struct bv_simplifier_params {
bool m_hi_div0; //!< if true, uses the hardware interpretation for div0, mod0, ... if false, div0, mod0, ... are considered uninterpreted.
bool m_bv2int_distribute; //!< if true allows downward propagation of bv2int.
bv_simplifier_params():
m_hi_div0(true),
m_bv2int_distribute(true) {
}
void register_params(ini_params & p) {
p.register_bool_param("HI_DIV0", m_hi_div0, "if true, then Z3 uses the usual hardware interpretation for division (rem, mod) by zero. Otherwise, these operations are considered uninterpreted.");
p.register_bool_param("BV2INT_DISTRIBUTE", m_bv2int_distribute, "if true, then int2bv is distributed over arithmetical operators.");
}
};
#endif /* _BV_SIMPLIFIER_PARAMS_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,187 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
bv_simplifier_plugin.h
Abstract:
Simplifier for the bv family.
Author:
Leonardo (leonardo) 2008-01-08
--*/
#ifndef _BV_SIMPLIFIER_PLUGIN_H_
#define _BV_SIMPLIFIER_PLUGIN_H_
#include"basic_simplifier_plugin.h"
#include"poly_simplifier_plugin.h"
#include"bv_decl_plugin.h"
#include"map.h"
#include"bv_simplifier_params.h"
#include"arith_decl_plugin.h"
/**
\brief Simplifier for the bv family.
*/
class bv_simplifier_plugin : public poly_simplifier_plugin {
typedef rational numeral;
struct extract_entry {
unsigned m_high;
unsigned m_low;
expr * m_arg;
extract_entry():m_high(0), m_low(0), m_arg(0) {}
extract_entry(unsigned h, unsigned l, expr * n):m_high(h), m_low(l), m_arg(n) {}
unsigned hash() const {
unsigned a = m_high;
unsigned b = m_low;
unsigned c = m_arg->get_id();
mix(a,b,c);
return c;
}
bool operator==(const extract_entry & e) const {
return m_high == e.m_high && m_low == e.m_low && m_arg == e.m_arg;
}
struct hash_proc {
unsigned operator()(extract_entry const& e) const { return e.hash(); }
};
struct eq_proc {
bool operator()(extract_entry const& a, extract_entry const& b) const { return a == b; }
};
};
typedef map<extract_entry, expr *, extract_entry::hash_proc , extract_entry::eq_proc > extract_cache;
protected:
ast_manager& m_manager;
bv_util m_util;
arith_util m_arith;
basic_simplifier_plugin & m_bsimp;
bv_simplifier_params & m_params;
expr_ref_vector m_zeros;
extract_cache m_extract_cache;
unsigned_vector m_lows, m_highs;
ptr_vector<expr> m_extract_args;
rational mk_bv_and(numeral const& a0, numeral const& b0, unsigned sz);
rational mk_bv_or(numeral const& a0, numeral const& b0, unsigned sz);
rational mk_bv_xor(numeral const& a0, numeral const& b0, unsigned sz);
rational mk_bv_not(numeral const& a0, unsigned sz);
rational num(expr* e);
bool has_sign_bit(numeral const& n, unsigned bv_size) { return m_util.has_sign_bit(n, bv_size); }
bool shift_shift(bv_op_kind k, expr* arg1, expr* arg2, expr_ref& result);
void bit2bool_simplify(unsigned idx, expr* e, expr_ref& result);
void mk_add_concat(expr_ref& result);
bool is_zero_bit(expr* x, unsigned idx);
void mk_bv_rotate_left_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result);
void mk_bv_rotate_right_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result);
public:
bv_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, bv_simplifier_params & p);
virtual ~bv_simplifier_plugin();
// simplifier_plugin:
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);
virtual void flush_caches();
// poly_simplifier_plugin
virtual rational norm(const rational & n);
virtual bool is_numeral(expr * n, rational & val) const;
bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
virtual bool is_minus_one(expr * n) const { return is_minus_one_core(n); }
virtual expr * get_zero(sort * s) const;
virtual app * mk_numeral(rational const & n);
bool is_bv(expr * n) const { return m_util.is_bv(n); }
bool is_bv_sort(sort * s) const { return m_util.is_bv_sort(s); }
bool is_le(expr * n) const { return m_util.is_bv_ule(n) || m_util.is_bv_sle(n); }
// REMARK: simplified bv expressions are never of the form a >= b.
virtual bool is_le_ge(expr * n) const { return is_le(n); }
uint64 to_uint64(const numeral & n, unsigned bv_size);
rational norm(rational const& n, unsigned bv_size, bool is_signed) { return m_util.norm(n, bv_size, is_signed); }
unsigned get_bv_size(expr const * n) { return get_bv_size(m_manager.get_sort(n)); }
unsigned get_bv_size(sort const * s) { return m_util.get_bv_size(s); }
void mk_leq_core(bool is_signed, expr * arg1, expr * arg2, expr_ref & result);
void mk_ule(expr* a, expr* b, expr_ref& result);
void mk_ult(expr* a, expr* b, expr_ref& result);
void mk_sle(expr* a, expr* b, expr_ref& result);
void mk_slt(expr* a, expr* b, expr_ref& result);
void mk_bv_and(unsigned num_args, expr * const* args, expr_ref & result);
void mk_bv_or(unsigned num_args, expr * const* args, expr_ref & result);
void mk_bv_xor(unsigned num_args, expr * const* args, expr_ref & result);
void mk_bv_not(expr * arg, expr_ref & result);
void mk_extract(unsigned hi,unsigned lo, expr* bv, expr_ref& result);
void mk_extract_core(unsigned high, unsigned low, expr * arg, expr_ref& result);
void cache_extract(unsigned h, unsigned l, expr * arg, expr * result);
expr* get_cached_extract(unsigned h, unsigned l, expr * arg);
bool lookup_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result);
bool try_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result);
void mk_bv_eq(expr* a1, expr* a2, expr_ref& result);
void mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
void mk_args_eq_numeral(app * app, expr * n, expr_ref & result);
void mk_concat(unsigned num_args, expr * const * args, expr_ref & result);
void mk_concat(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
mk_concat(2, args, result);
}
void mk_zeroext(unsigned n, expr * arg, expr_ref & result);
void mk_repeat(unsigned n, expr * arg, expr_ref & result);
void mk_sign_extend(unsigned n, expr * arg, expr_ref & result);
void mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result);
void mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result);
void mk_bv_ashr(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_smod(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result);
void mk_bv_srem(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_udiv(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_sdiv(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_smod_i(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_urem_i(expr * arg1, expr * arg2, expr_ref & result);
void mk_bv_srem_i(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_udiv_i(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_sdiv_i(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_nand(unsigned num_args, expr* const* args, expr_ref& result);
void mk_bv_nor(unsigned num_args, expr* const* args, expr_ref& result);
void mk_bv_xnor(unsigned num_args, expr* const* args, expr_ref& result);
void mk_bv_rotate_right(func_decl* f, expr* arg, expr_ref& result);
void mk_bv_rotate_left(func_decl* f, expr* arg, expr_ref& result);
void mk_bv_ext_rotate_right(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_ext_rotate_left(expr* arg1, expr* arg2, expr_ref& result);
void mk_bv_redor(expr* arg, expr_ref& result);
void mk_bv_redand(expr* arg, expr_ref& result);
void mk_bv_comp(expr* arg1, expr* arg2, expr_ref& result);
bool are_numerals(unsigned num_args, expr * const* args, unsigned& bv_size);
app * mk_numeral(rational const & n, unsigned bv_size);
app * mk_numeral(uint64 n, unsigned bv_size) { return mk_numeral(numeral(n, numeral::ui64()), bv_size); }
app* mk_bv0(unsigned bv_size) { return m_util.mk_numeral(numeral(0), bv_size); }
rational mk_allone(unsigned bv_size) { return m_util.power_of_two(bv_size) - numeral(1); }
bool is_minus_one_core(expr * arg) const;
bool is_x_minus_one(expr * arg, expr * & x);
void mk_int2bv(expr * arg, sort* range, expr_ref & result);
void mk_bv2int(expr * arg, sort* range, expr_ref & result);
uint64 n64(expr* e);
bool is_mul_no_overflow(expr* e);
bool is_add_no_overflow(expr* e);
unsigned num_leading_zero_bits(expr* e);
};
#endif /* _BV_SIMPLIFIER_PLUGIN_H_ */

View file

@ -0,0 +1,113 @@
/*++
Copyright (c) 2008 Microsoft Corporation
Module Name:
datatype_simplifier_plugin.cpp
Abstract:
Simplifier for algebraic datatypes.
Author:
nbjorner 2008-11-6
--*/
#include"datatype_simplifier_plugin.h"
datatype_simplifier_plugin::datatype_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b):
simplifier_plugin(symbol("datatype"), m),
m_util(m),
m_bsimp(b) {
}
datatype_simplifier_plugin::~datatype_simplifier_plugin() {
}
bool datatype_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
set_reduce_invoked();
SASSERT(f->get_family_id() == get_family_id());
switch(f->get_decl_kind()) {
case OP_DT_CONSTRUCTOR: {
return false;
}
case OP_DT_RECOGNISER: {
//
// simplify is_cons(cons(x,y)) -> true
// simplify is_cons(nil) -> false
//
SASSERT(num_args == 1);
if (!is_app_of(args[0], get_family_id(), OP_DT_CONSTRUCTOR)) {
return false;
}
app* a = to_app(args[0]);
func_decl* f1 = a->get_decl();
func_decl* f2 = m_util.get_recognizer_constructor(f);
if (f1 == f2) {
result = m_manager.mk_true();
}
else {
result = m_manager.mk_false();
}
return true;
}
case OP_DT_ACCESSOR: {
//
// simplify head(cons(x,y)) -> x
//
SASSERT(num_args == 1);
if (!is_app_of(args[0], get_family_id(), OP_DT_CONSTRUCTOR)) {
return false;
}
app* a = to_app(args[0]);
func_decl* f1 = a->get_decl();
func_decl* f2 = m_util.get_accessor_constructor(f);
if (f1 != f2) {
return false;
}
ptr_vector<func_decl> const* acc = m_util.get_constructor_accessors(f1);
SASSERT(acc && acc->size() == a->get_num_args());
for (unsigned i = 0; i < acc->size(); ++i) {
if (f == (*acc)[i]) {
// found it.
result = a->get_arg(i);
return true;
}
}
UNREACHABLE();
}
default:
UNREACHABLE();
}
return false;
}
bool datatype_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
set_reduce_invoked();
if (is_app_of(lhs, get_family_id(), OP_DT_CONSTRUCTOR) &&
is_app_of(rhs, get_family_id(), OP_DT_CONSTRUCTOR)) {
app* a = to_app(lhs);
app* b = to_app(rhs);
if (a->get_decl() != b->get_decl()) {
result = m_manager.mk_false();
return true;
}
expr_ref_vector eqs(m_manager);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
m_bsimp.mk_eq(a->get_arg(i),b->get_arg(i), result);
eqs.push_back(result);
}
m_bsimp.mk_and(eqs.size(), eqs.c_ptr(), result);
return true;
}
// TBD: occurs check, constructor check.
return false;
}

View file

@ -0,0 +1,42 @@
/*++
Copyright (c) 2008 Microsoft Corporation
Module Name:
datatype_simplifier_plugin.h
Abstract:
Simplifier for algebraic datatypes.
Author:
nbjorner 2008-11-6
--*/
#ifndef _DATATYPE_SIMPLIFIER_PLUGIN_H_
#define _DATATYPE_SIMPLIFIER_PLUGIN_H_
#include"basic_simplifier_plugin.h"
#include"datatype_decl_plugin.h"
/**
\brief Simplifier for the arith family.
*/
class datatype_simplifier_plugin : public simplifier_plugin {
datatype_util m_util;
basic_simplifier_plugin & m_bsimp;
public:
datatype_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b);
~datatype_simplifier_plugin();
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
};
#endif /* _DATATYPE_SIMPLIFIER_PLUGIN_H_ */

View file

@ -0,0 +1,170 @@
/*++
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);
}

View file

@ -0,0 +1,82 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
distribute_forall.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-02.
Revision History:
Christoph Wintersteiger 2010-04-06: Added implementation
--*/
#ifndef _DISTRIBUTE_FORALL_H_
#define _DISTRIBUTE_FORALL_H_
#include"ast.h"
#include"basic_simplifier_plugin.h"
#include"act_cache.h"
/**
\brief Apply the following transformation
(forall X (and F1 ... Fn))
-->
(and (forall X F1) ... (forall X Fn))
The actual transformation is slightly different since the "and" connective is eliminated and
replaced with a "not or".
So, the actual transformation is:
(forall X (not (or F1 ... Fn)))
-->
(not (or (not (forall X (not F1)))
...
(not (forall X (not Fn)))))
The implementation uses the visit_children/reduce1 idiom. A cache is used as usual.
*/
class distribute_forall {
typedef act_cache expr_map;
ast_manager & m_manager;
basic_simplifier_plugin & m_bsimp; // useful for constructing formulas and/or/not in simplified form.
ptr_vector<expr> m_todo;
expr_map m_cache;
ptr_vector<expr> m_new_args;
// The new expressions are stored in a mapping that increments their reference counter. So, we do not need to store them in
// m_new_exprs
// expr_ref_vector m_new_exprs;
public:
distribute_forall(ast_manager & m, basic_simplifier_plugin & p);
/**
\brief Apply the distribute_forall transformation (when possible) to all universal quantifiers in \c f.
Store the result in \c result.
*/
void operator()(expr * f, expr_ref & result);
protected:
inline void visit(expr * n, bool & visited);
bool visit_children(expr * n);
void reduce1(expr * n);
void reduce1_quantifier(quantifier * q);
void reduce1_app(app * a);
expr * get_cached(expr * n) const;
bool is_cached(expr * n) const { return get_cached(n) != 0; }
void cache_result(expr * n, expr * r);
void reset_cache() { m_cache.reset(); }
void flush_cache() { m_cache.cleanup(); }
};
#endif /* _DISTRIBUTE_FORALL_H_ */

View file

@ -0,0 +1,222 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
elim_bounds.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-28.
Revision History:
--*/
#include"elim_bounds.h"
#include"used_vars.h"
#include"obj_hashtable.h"
#include"var_subst.h"
#include"ast_pp.h"
elim_bounds::elim_bounds(ast_manager & m):
m_manager(m),
m_util(m) {
}
/**
\brief Find bounds of the form
(<= x k)
(<= (+ x (* -1 y)) k)
(<= (+ x (* -1 t)) k)
(<= (+ t (* -1 x)) k)
x and y are a bound variables, t is a ground term and k is a numeral
It also detects >=, and the atom can be negated.
*/
bool elim_bounds::is_bound(expr * n, var * & lower, var * & upper) {
upper = 0;
lower = 0;
bool neg = false;
if (m_manager.is_not(n)) {
n = to_app(n)->get_arg(0);
neg = true;
}
bool le = false;
if (m_util.is_le(n)) {
SASSERT(m_util.is_numeral(to_app(n)->get_arg(1)));
n = to_app(n)->get_arg(0);
le = true;
}
else if (m_util.is_ge(n)) {
SASSERT(m_util.is_numeral(to_app(n)->get_arg(1)));
n = to_app(n)->get_arg(0);
le = false;
}
else {
return false;
}
if (neg)
le = !le;
if (is_var(n)) {
upper = to_var(n);
}
else if (m_util.is_add(n) && to_app(n)->get_num_args() == 2) {
expr * arg1 = to_app(n)->get_arg(0);
expr * arg2 = to_app(n)->get_arg(1);
if (is_var(arg1))
upper = to_var(arg1);
else if (!is_ground(arg1))
return false;
rational k;
bool is_int;
if (m_util.is_mul(arg2) && m_util.is_numeral(to_app(arg2)->get_arg(0), k, is_int) && k.is_minus_one()) {
arg2 = to_app(arg2)->get_arg(1);
if (is_var(arg2))
lower = to_var(arg2);
else if (!is_ground(arg2))
return false; // not supported
}
else {
return false; // not supported
}
}
else {
return false;
}
if (!le)
std::swap(upper, lower);
return true;
}
bool elim_bounds::is_bound(expr * n) {
var * lower, * upper;
return is_bound(n, lower, upper);
}
void elim_bounds::operator()(quantifier * q, expr_ref & r) {
if (!q->is_forall()) {
r = q;
return;
}
expr * n = q->get_expr();
ptr_buffer<expr> atoms;
if (m_manager.is_or(n))
atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args());
else
atoms.push_back(n);
used_vars m_used_vars;
// collect non-candidates
unsigned sz = atoms.size();
for (unsigned i = 0; i < sz; i++) {
expr * a = atoms[i];
if (!is_bound(a))
m_used_vars.process(a);
}
if (m_used_vars.uses_all_vars(q->get_num_decls())) {
r = q;
return;
}
// collect candidates
obj_hashtable<var> m_lowers;
obj_hashtable<var> m_uppers;
obj_hashtable<var> m_candidate_set;
ptr_buffer<var> m_candidates;
#define ADD_CANDIDATE(V) if (!m_lowers.contains(V) && !m_uppers.contains(V)) { m_candidate_set.insert(V); m_candidates.push_back(V); }
for (unsigned i = 0; i < sz; i++) {
expr * a = atoms[i];
var * lower = 0;
var * upper = 0;
if (is_bound(a, lower, upper)) {
if (lower != 0 && !m_used_vars.contains(lower->get_idx())) {
ADD_CANDIDATE(lower);
m_lowers.insert(lower);
}
if (upper != 0 && !m_used_vars.contains(upper->get_idx())) {
ADD_CANDIDATE(upper);
m_uppers.insert(upper);
}
}
}
TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < m_candidates.size(); i++) tout << mk_pp(m_candidates[i], m_manager) << "\n";);
// remove candidates that have lower and upper bounds
for (unsigned i = 0; i < m_candidates.size(); i++) {
var * v = m_candidates[i];
if (m_lowers.contains(v) && m_uppers.contains(v))
m_candidate_set.erase(v);
}
TRACE("elim_bounds", tout << "candidates after filter:\n"; for (unsigned i = 0; i < m_candidates.size(); i++) tout << mk_pp(m_candidates[i], m_manager) << "\n";);
if (m_candidate_set.empty()) {
r = q;
return;
}
// remove bounds that contain variables in m_candidate_set
unsigned j = 0;
for (unsigned i = 0; i < sz; i++) {
expr * a = atoms[i];
var * lower = 0;
var * upper = 0;
if (is_bound(a, lower, upper) && ((lower != 0 && m_candidate_set.contains(lower)) || (upper != 0 && m_candidate_set.contains(upper))))
continue;
atoms[j] = a;
j++;
}
atoms.resize(j);
expr * new_body = 0;
switch (atoms.size()) {
case 0:
r = m_manager.mk_false();
return;
case 1:
new_body = atoms[0];
break;
default:
new_body = m_manager.mk_or(atoms.size(), atoms.c_ptr());
break;
}
quantifier_ref new_q(m_manager);
new_q = m_manager.update_quantifier(q, new_body);
elim_unused_vars(m_manager, new_q, r);
TRACE("elim_bounds", tout << mk_pp(q, m_manager) << "\n" << mk_pp(r, m_manager) << "\n";);
}
bool elim_bounds_star::visit_quantifier(quantifier * q) {
if (!q->is_forall() || q->get_num_patterns() != 0)
return true;
bool visited = true;
visit(q->get_expr(), visited);
return visited;
}
void elim_bounds_star::reduce1_quantifier(quantifier * q) {
if (!q->is_forall() || q->get_num_patterns() != 0) {
cache_result(q, q, 0);
return;
}
quantifier_ref new_q(m_manager);
expr * new_body = 0;
proof * new_pr;
get_cached(q->get_expr(), new_body, new_pr);
new_q = m_manager.update_quantifier(q, new_body);
expr_ref r(m_manager);
m_elim(new_q, r);
if (q == r.get()) {
cache_result(q, q, 0);
return;
}
proof_ref pr(m_manager);
if (m_manager.fine_grain_proofs())
pr = m_manager.mk_rewrite(q, r); // TODO: improve justification
cache_result(q, r, pr);
}

View file

@ -0,0 +1,69 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
elim_bounds.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-28.
Revision History:
--*/
#ifndef _ELIM_BOUNDS_H_
#define _ELIM_BOUNDS_H_
#include"ast.h"
#include"arith_decl_plugin.h"
#include"simplifier.h"
/**
\brief Functor for eliminating irrelevant bounds in quantified formulas.
Example:
(forall (x Int) (y Int) (or (not (>= y x) (not (>= x 0)) (= (select a x) 1))))
The bound (>= y x) is irrelevant and can be eliminated.
This can be easily proved by using Fourier-Motzkin elimination.
Limitations & Assumptions:
- It assumes the input formula was already simplified.
- It can only handle bounds in the diff-logic fragment.
\remark This operation is subsumed by Fourier-Motzkin elimination.
*/
class elim_bounds {
ast_manager & m_manager;
arith_util m_util;
bool is_bound(expr * n, var * & lower, var * & upper);
bool is_bound(expr * n);
public:
elim_bounds(ast_manager & m);
void operator()(quantifier * q, expr_ref & r);
};
/**
\brief Functor for applying elim_bounds in all
universal quantifiers in an expression.
Assumption: the formula was already skolemized.
*/
class elim_bounds_star : public simplifier {
protected:
elim_bounds m_elim;
virtual bool visit_quantifier(quantifier * q);
virtual void reduce1_quantifier(quantifier * q);
public:
elim_bounds_star(ast_manager & m):simplifier(m), m_elim(m) { enable_ac_support(false); }
virtual ~elim_bounds_star() {}
};
#endif /* _ELIM_BOUNDS_H_ */

View file

@ -0,0 +1,142 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inj_axiom.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-23.
Revision History:
--*/
#include"inj_axiom.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"has_free_vars.h"
#include"well_sorted.h"
/**
\brief Little HACK for simplifying injectivity axioms
\remark It is not covering all possible cases.
*/
bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) {
expr * n = q->get_expr();
if (q->is_forall() && m.is_or(n) && to_app(n)->get_num_args() == 2) {
expr * arg1 = to_app(n)->get_arg(0);
expr * arg2 = to_app(n)->get_arg(1);
if (m.is_not(arg2))
std::swap(arg1, arg2);
if (m.is_not(arg1) &&
m.is_eq(to_app(arg1)->get_arg(0)) &&
m.is_eq(arg2)) {
expr * app1 = to_app(to_app(arg1)->get_arg(0))->get_arg(0);
expr * app2 = to_app(to_app(arg1)->get_arg(0))->get_arg(1);
expr * var1 = to_app(arg2)->get_arg(0);
expr * var2 = to_app(arg2)->get_arg(1);
if (is_app(app1) &&
is_app(app2) &&
to_app(app1)->get_decl() == to_app(app2)->get_decl() &&
to_app(app1)->get_num_args() == to_app(app2)->get_num_args() &&
to_app(app1)->get_family_id() == null_family_id &&
to_app(app1)->get_num_args() > 0 &&
is_var(var1) &&
is_var(var2) &&
var1 != var2) {
app * f1 = to_app(app1);
app * f2 = to_app(app2);
bool found_vars = false;
unsigned num = f1->get_num_args();
unsigned idx = UINT_MAX;
unsigned num_vars = 1;
for (unsigned i = 0; i < num; i++) {
expr * c1 = f1->get_arg(i);
expr * c2 = f2->get_arg(i);
if (!is_var(c1) && !is_uninterp_const(c1))
return false;
if ((c1 == var1 && c2 == var2) || (c1 == var2 && c2 == var1)) {
if (found_vars)
return false;
found_vars = true;
idx = i;
}
else if (c1 == c2 && c1 != var1 && c1 != var2) {
if (is_var(c1)) {
++num_vars;
}
}
else {
return false;
}
}
if (found_vars && !has_free_vars(q)) {
TRACE("inj_axiom",
tout << "Cadidate for simplification:\n" << mk_ll_pp(q, m) << mk_pp(app1, m) << "\n" << mk_pp(app2, m) << "\n" <<
mk_pp(var1, m) << "\n" << mk_pp(var2, m) << "\nnum_vars: " << num_vars << "\n";);
// Building new (optimized) axiom
func_decl * decl = f1->get_decl();
unsigned var_idx = 0;
ptr_buffer<expr> f_args, inv_vars;
ptr_buffer<sort> decls;
buffer<symbol> names;
expr * var = 0;
for (unsigned i = 0; i < num; i++) {
expr * c = f1->get_arg(i);
if (is_var(c)) {
names.push_back(symbol(i));
sort * s = decl->get_domain(i);
decls.push_back(s);
expr * new_c = m.mk_var(var_idx, s);
var_idx++;
f_args.push_back(new_c);
if (i == idx) {
var = new_c;
}
else {
inv_vars.push_back(new_c);
}
}
else {
SASSERT(is_uninterp_const(c));
f_args.push_back(c);
}
}
SASSERT(var != 0);
app * f = m.mk_app(decl, f_args.size(), f_args.c_ptr());
ptr_vector<sort> domain;
inv_vars.push_back(f);
for (unsigned i = 0; i < inv_vars.size(); ++i) {
domain.push_back(m.get_sort(inv_vars[i]));
}
sort * d = decl->get_domain(idx);
func_decl * inv_decl = m.mk_fresh_func_decl("inj", domain.size(), domain.c_ptr(), d);
expr * proj = m.mk_app(inv_decl, inv_vars.size(), inv_vars.c_ptr());
expr * eq = m.mk_eq(proj, var);
expr * p = m.mk_pattern(f);
// decls are in the wrong order...
// Remark: the sort of the var 0 must be in the last position.
std::reverse(decls.begin(), decls.end());
result = m.mk_forall(decls.size(), decls.c_ptr(), names.c_ptr(), eq,
0, symbol(), symbol(), 1, &p);
TRACE("inj_axiom", tout << "new axiom:\n" << mk_pp(result, m) << "\n";);
SASSERT(is_well_sorted(m, result));
return true;
}
}
}
}
return false;
}

View file

@ -0,0 +1,27 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inj_axiom.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-23.
Revision History:
--*/
#ifndef _INJ_AXIOM_H_
#define _INJ_AXIOM_H_
#include"ast.h"
bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result);
#endif /* _INJ_AXIOM_H_ */

View file

@ -0,0 +1,192 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
maximise_ac_sharing.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-22.
Revision History:
--*/
#include"maximise_ac_sharing.h"
#include"ast_pp.h"
#include"basic_simplifier_plugin.h"
maximise_ac_sharing::ac_plugin::ac_plugin(symbol const & fname, ast_manager & m, maximise_ac_sharing & owner):
simplifier_plugin(fname, m),
m_owner(owner) {
}
void maximise_ac_sharing::ac_plugin::register_kind(decl_kind k) {
m_kinds.push_back(k);
}
maximise_ac_sharing::ac_plugin::~ac_plugin() {
}
bool maximise_ac_sharing::ac_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
decl_kind k = f->get_kind();
if (!f->is_associative())
return false;
if (num_args <= 2)
return false;
if (std::find(m_kinds.begin(), m_kinds.end(), k) == m_kinds.end())
return false;
ptr_buffer<expr, 128> _args;
expr * numeral = 0;
if (m_owner.is_numeral(args[0])) {
numeral = args[0];
for (unsigned i = 1; i < num_args; i++)
_args.push_back(args[i]);
num_args--;
}
else {
_args.append(num_args, args);
}
TRACE("ac_sharing_detail", tout << "before-reuse: num_args: " << num_args << "\n";);
#define MAX_NUM_ARGS_FOR_OPT 128
// Try to reuse already created circuits.
TRACE("ac_sharing_detail", tout << "args: "; for (unsigned i = 0; i < num_args; i++) tout << mk_pp(_args[i], m_manager) << "\n";);
try_to_reuse:
if (num_args > 1 && num_args < MAX_NUM_ARGS_FOR_OPT) {
for (unsigned i = 0; i < num_args - 1; i++) {
for (unsigned j = i + 1; j < num_args; j++) {
if (m_owner.contains(f, _args[i], _args[j])) {
TRACE("ac_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";);
_args[i] = m_manager.mk_app(f, _args[i], _args[j]);
SASSERT(num_args > 1);
for (unsigned w = j; w < num_args - 1; w++) {
_args[w] = _args[w+1];
}
num_args--;
goto try_to_reuse;
}
}
}
}
// Create "tree-like circuit"
while (true) {
TRACE("ac_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";);
unsigned j = 0;
for (unsigned i = 0; i < num_args; i += 2, j++) {
if (i == num_args - 1) {
_args[j] = _args[i];
}
else {
m_owner.insert(f, _args[i], _args[i+1]);
_args[j] = m_manager.mk_app(f, _args[i], _args[i+1]);
}
}
num_args = j;
if (num_args == 1) {
if (numeral == 0) {
result = _args[0];
}
else {
result = m_manager.mk_app(f, numeral, _args[0]);
}
TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m_manager) << "\n";);
return true;
}
}
UNREACHABLE();
return false;
}
bool maximise_ac_sharing::contains(func_decl * f, expr * arg1, expr * arg2) {
entry e(f, arg1, arg2);
return m_cache.contains(&e);
}
void maximise_ac_sharing::insert(func_decl * f, expr * arg1, expr * arg2) {
entry * e = new (m_region) entry(f, arg1, arg2);
m_entries.push_back(e);
m_cache.insert(e);
m_manager.inc_ref(arg1);
m_manager.inc_ref(arg2);
}
maximise_ac_sharing::maximise_ac_sharing(ast_manager & m):
m_manager(m),
m_simplifier(m),
m_init(false) {
basic_simplifier_plugin* basic_simp = alloc(basic_simplifier_plugin,m);
m_simplifier.register_plugin(basic_simp);
}
maximise_ac_sharing::~maximise_ac_sharing() {
restore_entries(0);
}
void maximise_ac_sharing::operator()(expr * s, expr_ref & r, proof_ref & p) {
init();
m_simplifier.operator()(s, r, p);
}
void maximise_ac_sharing::push_scope() {
init();
m_scopes.push_back(m_entries.size());
m_region.push_scope();
}
void maximise_ac_sharing::pop_scope(unsigned num_scopes) {
SASSERT(num_scopes <= m_scopes.size());
unsigned new_lvl = m_scopes.size() - num_scopes;
unsigned old_lim = m_scopes[new_lvl];
restore_entries(old_lim);
m_region.pop_scope(num_scopes);
m_scopes.shrink(new_lvl);
}
void maximise_ac_sharing::restore_entries(unsigned old_lim) {
unsigned i = m_entries.size();
while (i != old_lim) {
--i;
entry * e = m_entries[i];
m_manager.dec_ref(e->m_arg1);
m_manager.dec_ref(e->m_arg2);
}
m_entries.shrink(old_lim);
}
void maximise_ac_sharing::reset() {
restore_entries(0);
m_entries.reset();
m_cache.reset();
m_simplifier.reset();
m_region.reset();
m_scopes.reset();
}
void maximise_bv_sharing::init_core() {
maximise_ac_sharing::ac_plugin * p = alloc(maximise_ac_sharing::ac_plugin, symbol("bv"), get_manager(), *this);
p->register_kind(OP_BADD);
p->register_kind(OP_BMUL);
p->register_kind(OP_BOR);
p->register_kind(OP_BAND);
register_plugin(p);
}
bool maximise_bv_sharing::is_numeral(expr * n) const {
return m_util.is_numeral(n);
}
maximise_bv_sharing::maximise_bv_sharing(ast_manager & m):
maximise_ac_sharing(m),
m_util(m) {
}

View file

@ -0,0 +1,124 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
maximise_ac_sharing.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-22.
Revision History:
--*/
#ifndef _MAXIMISE_AC_SHARING_H_
#define _MAXIMISE_AC_SHARING_H_
#include"simplifier.h"
#include"hashtable.h"
#include"bv_decl_plugin.h"
#include"region.h"
/**
\brief Functor used to maximise the amount of shared terms in an expression.
The idea is to rewrite AC terms to maximise sharing.
Example:
(f (bvadd a (bvadd b c)) (bvadd a (bvadd b d)))
is rewritten to:
(f (bvadd (bvadd a b) c) (bvadd (bvadd a b) d))
\warning This class uses an opportunistic heuristic to maximise sharing.
There is no guarantee that the optimal expression will be produced.
*/
class maximise_ac_sharing {
struct entry {
func_decl * m_decl;
expr * m_arg1;
expr * m_arg2;
entry(func_decl * d = 0, expr * arg1 = 0, expr * arg2 = 0):m_decl(d), m_arg1(arg1), m_arg2(arg2) {
SASSERT((d == 0 && arg1 == 0 && arg2 == 0) || (d != 0 && arg1 != 0 && arg2 != 0));
if (arg1->get_id() > arg2->get_id())
std::swap(m_arg1, m_arg2);
}
unsigned hash() const {
unsigned a = m_decl->get_id();
unsigned b = m_arg1->get_id();
unsigned c = m_arg2->get_id();
mix(a,b,c);
return c;
}
bool operator==(entry const & e) const {
return m_decl == e.m_decl && m_arg1 == e.m_arg1 && m_arg2 == e.m_arg2;
}
};
typedef ptr_hashtable<entry, obj_ptr_hash<entry>, deref_eq<entry> > cache;
protected:
class ac_plugin : public simplifier_plugin {
maximise_ac_sharing & m_owner;
svector<decl_kind> m_kinds; //!< kinds to be processed
public:
ac_plugin(symbol const & fname, ast_manager & m, maximise_ac_sharing & owner);
void register_kind(decl_kind k);
virtual ~ac_plugin();
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
};
friend class ac_plugin;
private:
ast_manager & m_manager;
simplifier m_simplifier;
bool m_init;
region m_region;
cache m_cache;
ptr_vector<entry> m_entries;
unsigned_vector m_scopes;
bool contains(func_decl * f, expr * arg1, expr * arg2);
void insert(func_decl * f, expr * arg1, expr * arg2);
void restore_entries(unsigned old_lim);
void init() {
if (!m_init) {
init_core();
m_init = true;
}
}
protected:
virtual void init_core() = 0;
virtual bool is_numeral(expr * n) const = 0;
void register_plugin(ac_plugin * p) { m_simplifier.register_plugin(p); }
public:
maximise_ac_sharing(ast_manager & m);
virtual ~maximise_ac_sharing();
void operator()(expr * s, expr_ref & r, proof_ref & p);
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
ast_manager & get_manager() { return m_manager; }
};
class maximise_bv_sharing : public maximise_ac_sharing {
bv_util m_util;
protected:
virtual void init_core();
virtual bool is_numeral(expr * n) const;
public:
maximise_bv_sharing(ast_manager & m);
};
#endif /* _MAXIMISE_AC_SHARING_H_ */

View file

@ -0,0 +1,812 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
poly_simplifier_plugin.cpp
Abstract:
Abstract class for families that have polynomials.
Author:
Leonardo (leonardo) 2008-01-08
--*/
#include"poly_simplifier_plugin.h"
#include"ast_pp.h"
#include"ast_util.h"
#include"ast_smt2_pp.h"
poly_simplifier_plugin::poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub,
decl_kind num):
simplifier_plugin(fname, m),
m_ADD(add),
m_MUL(mul),
m_SUB(sub),
m_UMINUS(uminus),
m_NUM(num),
m_curr_sort(0),
m_curr_sort_zero(0) {
}
expr * poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args) {
SASSERT(num_args > 0);
#ifdef Z3DEBUG
// check for incorrect use of mk_add
for (unsigned i = 0; i < num_args; i++) {
SASSERT(!is_zero(args[i]));
}
#endif
if (num_args == 1)
return args[0];
else
return m_manager.mk_app(m_fid, m_ADD, num_args, args);
}
expr * poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args) {
SASSERT(num_args > 0);
#ifdef Z3DEBUG
// check for incorrect use of mk_mul
set_curr_sort(args[0]);
SASSERT(!is_zero(args[0]));
numeral k;
for (unsigned i = 0; i < num_args; i++) {
SASSERT(!is_numeral(args[i], k) || !k.is_one());
SASSERT(i == 0 || !is_numeral(args[i]));
}
#endif
if (num_args == 1)
return args[0];
else if (num_args == 2)
return m_manager.mk_app(m_fid, m_MUL, args[0], args[1]);
else if (is_numeral(args[0]))
return m_manager.mk_app(m_fid, m_MUL, args[0], m_manager.mk_app(m_fid, m_MUL, num_args - 1, args+1));
else
return m_manager.mk_app(m_fid, m_MUL, num_args, args);
}
expr * poly_simplifier_plugin::mk_mul(numeral const & c, expr * body) {
numeral c_prime;
c_prime = norm(c);
if (c_prime.is_zero())
return 0;
if (body == 0)
return mk_numeral(c_prime);
if (c_prime.is_one())
return body;
set_curr_sort(body);
expr * args[2] = { mk_numeral(c_prime), body };
return mk_mul(2, args);
}
/**
\brief Traverse args, and copy the non-numeral exprs to result, and accumulate the
value of the numerals in k.
*/
void poly_simplifier_plugin::process_monomial(unsigned num_args, expr * const * args, numeral & k, ptr_buffer<expr> & result) {
rational v;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (is_numeral(arg, v))
k *= v;
else
result.push_back(arg);
}
}
#ifdef Z3DEBUG
/**
\brief Return true if m is a wellformed monomial.
*/
bool poly_simplifier_plugin::wf_monomial(expr * m) const {
SASSERT(!is_add(m));
if (is_mul(m)) {
app * curr = to_app(m);
expr * pp = 0;
if (is_numeral(curr->get_arg(0)))
pp = curr->get_arg(1);
else
pp = curr;
if (is_mul(pp)) {
for (unsigned i = 0; i < to_app(pp)->get_num_args(); i++) {
expr * arg = to_app(pp)->get_arg(i);
CTRACE("wf_monomial_bug", is_mul(arg),
tout << "m: " << mk_ismt2_pp(m, m_manager) << "\n";
tout << "pp: " << mk_ismt2_pp(pp, m_manager) << "\n";
tout << "arg: " << mk_ismt2_pp(arg, m_manager) << "\n";
tout << "i: " << i << "\n";
);
SASSERT(!is_mul(arg));
SASSERT(!is_numeral(arg));
}
}
}
return true;
}
/**
\brief Return true if m is a wellformed polynomial.
*/
bool poly_simplifier_plugin::wf_polynomial(expr * m) const {
if (is_add(m)) {
for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) {
expr * arg = to_app(m)->get_arg(i);
SASSERT(!is_add(arg));
SASSERT(wf_monomial(arg));
}
}
else if (is_mul(m)) {
SASSERT(wf_monomial(m));
}
return true;
}
#endif
/**
\brief Functor used to sort the elements of a monomial.
Force numeric constants to be in the beginning.
*/
struct monomial_element_lt_proc {
poly_simplifier_plugin & m_plugin;
monomial_element_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {}
bool operator()(expr * m1, expr * m2) const {
SASSERT(!m_plugin.is_numeral(m1) || !m_plugin.is_numeral(m2));
if (m_plugin.is_numeral(m1))
return true;
if (m_plugin.is_numeral(m2))
return false;
return m1->get_id() < m2->get_id();
}
};
/**
\brief Create a monomial (* args).
*/
void poly_simplifier_plugin::mk_monomial(unsigned num_args, expr * * args, expr_ref & result) {
switch(num_args) {
case 0:
result = mk_one();
break;
case 1:
result = args[0];
break;
default:
std::sort(args, args + num_args, monomial_element_lt_proc(*this));
result = mk_mul(num_args, args);
SASSERT(wf_monomial(result));
break;
}
}
/**
\brief Return the body of the monomial. That is, the monomial without a coefficient.
Examples: (* 2 (* x y)) ==> (* x y)
(* x x) ==> (* x x)
x ==> x
10 ==> 10
*/
expr * poly_simplifier_plugin::get_monomial_body(expr * m) {
TRACE("get_monomial_body_bug", tout << mk_pp(m, m_manager) << "\n";);
SASSERT(wf_monomial(m));
if (!is_mul(m))
return m;
if (is_numeral(to_app(m)->get_arg(0)))
return to_app(m)->get_arg(1);
return m;
}
inline bool is_essentially_var(expr * n, family_id fid) {
SASSERT(is_var(n) || is_app(n));
return is_var(n) || to_app(n)->get_family_id() != fid;
}
/**
\brief Hack for ordering monomials.
We want an order << where
- (* c1 m1) << (* c2 m2) when m1->get_id() < m2->get_id(), and c1 and c2 are numerals.
- c << m when c is a numeral, and m is not.
So, this method returns -1 for numerals, and the id of the body of the monomial
*/
int poly_simplifier_plugin::get_monomial_body_order(expr * m) {
if (is_essentially_var(m, m_fid)) {
return m->get_id();
}
else if (is_mul(m)) {
if (is_numeral(to_app(m)->get_arg(0)))
return to_app(m)->get_arg(1)->get_id();
else
return m->get_id();
}
else if (is_numeral(m)) {
return -1;
}
else {
return m->get_id();
}
}
void poly_simplifier_plugin::get_monomial_coeff(expr * m, numeral & result) {
SASSERT(!is_numeral(m));
SASSERT(wf_monomial(m));
if (!is_mul(m))
result = numeral::one();
else if (is_numeral(to_app(m)->get_arg(0), result))
return;
else
result = numeral::one();
}
/**
\brief Return true if n1 and n2 can be written as k1 * t and k2 * t, where k1 and
k2 are numerals, or n1 and n2 are both numerals.
*/
bool poly_simplifier_plugin::eq_monomials_modulo_k(expr * n1, expr * n2) {
bool is_num1 = is_numeral(n1);
bool is_num2 = is_numeral(n2);
if (is_num1 != is_num2)
return false;
if (is_num1 && is_num2)
return true;
return get_monomial_body(n1) == get_monomial_body(n2);
}
/**
\brief Return (k1 + k2) * t (or (k1 - k2) * t when inv = true), where n1 = k1 * t, and n2 = k2 * t
Return false if the monomials cancel each other.
*/
bool poly_simplifier_plugin::merge_monomials(bool inv, expr * n1, expr * n2, expr_ref & result) {
numeral k1;
numeral k2;
bool is_num1 = is_numeral(n1, k1);
bool is_num2 = is_numeral(n2, k2);
SASSERT(is_num1 == is_num2);
if (!is_num1 && !is_num2) {
get_monomial_coeff(n1, k1);
get_monomial_coeff(n2, k2);
SASSERT(eq_monomials_modulo_k(n1, n2));
}
if (inv)
k1 -= k2;
else
k1 += k2;
if (k1.is_zero())
return false;
if (is_num1 && is_num2) {
result = mk_numeral(k1);
}
else {
expr * b = get_monomial_body(n1);
if (k1.is_one())
result = b;
else
result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k1), b);
}
return true;
}
/**
\brief Return a monomial equivalent to -n.
*/
void poly_simplifier_plugin::inv_monomial(expr * n, expr_ref & result) {
set_curr_sort(n);
SASSERT(wf_monomial(n));
rational v;
SASSERT(n != 0);
TRACE("inv_monomial_bug", tout << "n:\n" << mk_ismt2_pp(n, m_manager) << "\n";);
if (is_numeral(n, v)) {
TRACE("inv_monomial_bug", tout << "is numeral\n";);
v.neg();
result = mk_numeral(v);
}
else {
TRACE("inv_monomial_bug", tout << "is not numeral\n";);
numeral k;
get_monomial_coeff(n, k);
expr * b = get_monomial_body(n);
k.neg();
if (k.is_one())
result = b;
else
result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k), b);
}
}
/**
\brief Add a monomial n to result.
*/
template<bool Inv>
void poly_simplifier_plugin::add_monomial_core(expr * n, expr_ref_vector & result) {
if (is_zero(n))
return;
if (Inv) {
expr_ref n_prime(m_manager);
inv_monomial(n, n_prime);
result.push_back(n_prime);
}
else {
result.push_back(n);
}
}
void poly_simplifier_plugin::add_monomial(bool inv, expr * n, expr_ref_vector & result) {
if (inv)
add_monomial_core<true>(n, result);
else
add_monomial_core<false>(n, result);
}
/**
\brief Copy the monomials in n to result. The monomials are inverted if inv is true.
Equivalent monomials are merged.
*/
template<bool Inv>
void poly_simplifier_plugin::process_sum_of_monomials_core(expr * n, expr_ref_vector & result) {
SASSERT(wf_polynomial(n));
if (is_add(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); i++)
add_monomial_core<Inv>(to_app(n)->get_arg(i), result);
}
else {
add_monomial_core<Inv>(n, result);
}
}
void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result) {
if (inv)
process_sum_of_monomials_core<true>(n, result);
else
process_sum_of_monomials_core<false>(n, result);
}
/**
\brief Copy the (non-numeral) monomials in n to result. The monomials are inverted if inv is true.
Equivalent monomials are merged. The constant (numeral) monomials are accumulated in k.
*/
void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result, numeral & k) {
SASSERT(wf_polynomial(n));
numeral val;
if (is_add(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) {
expr * arg = to_app(n)->get_arg(i);
if (is_numeral(arg, val)) {
k += inv ? -val : val;
}
else {
add_monomial(inv, arg, result);
}
}
}
else if (is_numeral(n, val)) {
k += inv ? -val : val;
}
else {
add_monomial(inv, n, result);
}
}
/**
\brief Functor used to sort monomials.
Force numeric constants to be in the beginning of a polynomial.
*/
struct monomial_lt_proc {
poly_simplifier_plugin & m_plugin;
monomial_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {}
bool operator()(expr * m1, expr * m2) const {
return m_plugin.get_monomial_body_order(m1) < m_plugin.get_monomial_body_order(m2);
}
};
void poly_simplifier_plugin::mk_sum_of_monomials_core(unsigned sz, expr ** ms, expr_ref & result) {
switch (sz) {
case 0:
result = mk_zero();
break;
case 1:
result = ms[0];
break;
default:
result = mk_add(sz, ms);
break;
}
}
/**
\brief Return true if m is essentially a variable, or is of the form (* c x),
where c is a numeral and x is essentially a variable.
Store the "variable" in x.
*/
bool poly_simplifier_plugin::is_simple_monomial(expr * m, expr * & x) {
if (is_essentially_var(m, m_fid)) {
x = m;
return true;
}
if (is_app(m) && to_app(m)->get_num_args() == 2) {
expr * arg1 = to_app(m)->get_arg(0);
expr * arg2 = to_app(m)->get_arg(1);
if (is_numeral(arg1) && is_essentially_var(arg2, m_fid)) {
x = arg2;
return true;
}
}
return false;
}
/**
\brief Return true if all monomials are simple, and each "variable" occurs only once.
The method assumes the monomials were sorted using monomial_lt_proc.
*/
bool poly_simplifier_plugin::is_simple_sum_of_monomials(expr_ref_vector & monomials) {
expr * last_var = 0;
expr * curr_var = 0;
unsigned size = monomials.size();
for (unsigned i = 0; i < size; i++) {
expr * m = monomials.get(i);
if (!is_simple_monomial(m, curr_var))
return false;
if (curr_var == last_var)
return false;
last_var = curr_var;
}
return true;
}
/**
\brief Store in result the sum of the given monomials.
*/
void poly_simplifier_plugin::mk_sum_of_monomials(expr_ref_vector & monomials, expr_ref & result) {
switch (monomials.size()) {
case 0:
result = mk_zero();
break;
case 1:
result = monomials.get(0);
break;
default: {
std::sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this));
if (is_simple_sum_of_monomials(monomials)) {
mk_sum_of_monomials_core(monomials.size(), monomials.c_ptr(), result);
return;
}
ptr_buffer<expr> new_monomials;
expr * last_body = 0;
numeral last_coeff;
numeral coeff;
unsigned sz = monomials.size();
for (unsigned i = 0; i < sz; i++) {
expr * m = monomials.get(i);
expr * body = 0;
if (!is_numeral(m, coeff)) {
body = get_monomial_body(m);
get_monomial_coeff(m, coeff);
}
if (last_body == body) {
last_coeff += coeff;
continue;
}
expr * new_m = mk_mul(last_coeff, last_body);
if (new_m)
new_monomials.push_back(new_m);
last_body = body;
last_coeff = coeff;
}
expr * new_m = mk_mul(last_coeff, last_body);
if (new_m)
new_monomials.push_back(new_m);
TRACE("mk_sum", for (unsigned i = 0; i < monomials.size(); i++) tout << mk_pp(monomials.get(i), m_manager) << "\n";
tout << "======>\n";
for (unsigned i = 0; i < new_monomials.size(); i++) tout << mk_pp(new_monomials.get(i), m_manager) << "\n";);
mk_sum_of_monomials_core(new_monomials.size(), new_monomials.c_ptr(), result);
break;
} }
}
/**
\brief Auxiliary template for mk_add_core
*/
template<bool Inv>
void poly_simplifier_plugin::mk_add_core_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 2);
expr_ref_vector monomials(m_manager);
process_sum_of_monomials_core<false>(args[0], monomials);
for (unsigned i = 1; i < num_args; i++) {
process_sum_of_monomials_core<Inv>(args[i], monomials);
}
TRACE("mk_add_core_bug",
for (unsigned i = 0; i < monomials.size(); i++) {
SASSERT(monomials.get(i) != 0);
tout << mk_ismt2_pp(monomials.get(i), m_manager) << "\n";
});
mk_sum_of_monomials(monomials, result);
}
/**
\brief Return a sum of monomials. The method assume that each arg in args is a sum of monomials.
If inv is true, then all but the first argument in args are inverted.
*/
void poly_simplifier_plugin::mk_add_core(bool inv, unsigned num_args, expr * const * args, expr_ref & result) {
TRACE("mk_add_core_bug",
for (unsigned i = 0; i < num_args; i++) {
SASSERT(args[i] != 0);
tout << mk_ismt2_pp(args[i], m_manager) << "\n";
});
switch (num_args) {
case 0:
result = mk_zero();
break;
case 1:
result = args[0];
break;
default:
if (inv)
mk_add_core_core<true>(num_args, args, result);
else
mk_add_core_core<false>(num_args, args, result);
break;
}
}
void poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
set_curr_sort(args[0]);
mk_add_core(false, num_args, args, result);
}
void poly_simplifier_plugin::mk_add(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
mk_add(2, args, result);
}
void poly_simplifier_plugin::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
set_curr_sort(args[0]);
mk_add_core(true, num_args, args, result);
}
void poly_simplifier_plugin::mk_sub(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
mk_sub(2, args, result);
}
void poly_simplifier_plugin::mk_uminus(expr * arg, expr_ref & result) {
set_curr_sort(arg);
rational v;
if (is_numeral(arg, v)) {
v.neg();
result = mk_numeral(v);
}
else {
expr_ref zero(mk_zero(), m_manager);
mk_sub(zero.get(), arg, result);
}
}
/**
\brief Add monomial n to result, the coeff of n is stored in k.
*/
void poly_simplifier_plugin::append_to_monomial(expr * n, numeral & k, ptr_buffer<expr> & result) {
SASSERT(wf_monomial(n));
rational val;
if (is_numeral(n, val)) {
k *= val;
return;
}
get_monomial_coeff(n, val);
k *= val;
n = get_monomial_body(n);
if (is_mul(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); i++)
result.push_back(to_app(n)->get_arg(i));
}
else {
result.push_back(n);
}
}
/**
\brief Return a sum of monomials that is equivalent to (* args[0] ... args[num_args-1]).
This method assumes that each arg[i] is a sum of monomials.
*/
void poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args, expr_ref & result) {
if (num_args == 1) {
result = args[0];
return;
}
rational val;
if (num_args == 2 && is_numeral(args[0], val) && is_essentially_var(args[1], m_fid)) {
if (val.is_one())
result = args[1];
else if (val.is_zero())
result = args[0];
else
result = mk_mul(num_args, args);
return;
}
if (num_args == 2 && is_essentially_var(args[0], m_fid) && is_numeral(args[1], val)) {
if (val.is_one())
result = args[0];
else if (val.is_zero())
result = args[1];
else {
expr * inv_args[2] = { args[1], args[0] };
result = mk_mul(2, inv_args);
}
return;
}
TRACE("mk_mul_bug",
for (unsigned i = 0; i < num_args; i++) {
tout << mk_pp(args[i], m_manager) << "\n";
});
set_curr_sort(args[0]);
buffer<unsigned> szs;
buffer<unsigned> it;
vector<ptr_vector<expr> > sums;
for (unsigned i = 0; i < num_args; i ++) {
it.push_back(0);
expr * arg = args[i];
SASSERT(wf_polynomial(arg));
sums.push_back(ptr_vector<expr>());
ptr_vector<expr> & v = sums.back();
if (is_add(arg)) {
v.append(to_app(arg)->get_num_args(), to_app(arg)->get_args());
}
else {
v.push_back(arg);
}
szs.push_back(v.size());
}
expr_ref_vector monomials(m_manager);
do {
rational k(1);
ptr_buffer<expr> m;
for (unsigned i = 0; i < num_args; i++) {
ptr_vector<expr> & v = sums[i];
expr * arg = v[it[i]];
TRACE("mk_mul_bug", tout << "k: " << k << " arg: " << mk_pp(arg, m_manager) << "\n";);
append_to_monomial(arg, k, m);
TRACE("mk_mul_bug", tout << "after k: " << k << "\n";);
}
expr_ref num(m_manager);
if (!k.is_zero() && !k.is_one()) {
num = mk_numeral(k);
m.push_back(num);
// bit-vectors can normalize
// to 1 during
// internalization.
if (is_numeral(num, k) && k.is_one()) {
m.pop_back();
}
}
if (!k.is_zero()) {
expr_ref new_monomial(m_manager);
TRACE("mk_mul_bug",
for (unsigned i = 0; i < m.size(); i++) {
tout << mk_pp(m[i], m_manager) << "\n";
});
mk_monomial(m.size(), m.c_ptr(), new_monomial);
TRACE("mk_mul_bug", tout << "new_monomial:\n" << mk_pp(new_monomial, m_manager) << "\n";);
add_monomial_core<false>(new_monomial, monomials);
}
}
while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr()));
mk_sum_of_monomials(monomials, result);
}
void poly_simplifier_plugin::mk_mul(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
mk_mul(2, args, result);
}
bool poly_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
set_reduce_invoked();
unsigned i = 0;
for (; i < num_args; i++)
if (!is_numeral(args[i]))
break;
if (i == num_args) {
// all arguments are numerals
// check if arguments are different...
ptr_buffer<expr> buffer;
buffer.append(num_args, args);
std::sort(buffer.begin(), buffer.end(), ast_lt_proc());
for (unsigned i = 0; i < num_args; i++) {
if (i > 0 && buffer[i] == buffer[i-1]) {
result = m_manager.mk_false();
return true;
}
}
result = m_manager.mk_true();
return true;
}
return false;
}
bool poly_simplifier_plugin::reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result) {
set_reduce_invoked();
if (is_decl_of(f, m_fid, m_ADD)) {
SASSERT(num_args > 0);
set_curr_sort(args[0]);
expr_ref_buffer args1(m_manager);
for (unsigned i = 0; i < num_args; ++i) {
expr * arg = args[i];
rational m = norm(mults[i]);
if (m.is_zero()) {
// skip
}
else if (m.is_one()) {
args1.push_back(arg);
}
else {
expr_ref k(m_manager);
k = mk_numeral(m);
expr_ref new_arg(m_manager);
mk_mul(k, args[i], new_arg);
args1.push_back(new_arg);
}
}
if (args1.empty()) {
result = mk_zero();
}
else {
mk_add(args1.size(), args1.c_ptr(), result);
}
return true;
}
else {
return simplifier_plugin::reduce(f, num_args, mults, args, result);
}
}
/**
\brief Return true if n is can be put into the form (+ v t) or (+ (- v) t)
\c inv = true will contain true if (- v) is found, and false otherwise.
*/
bool poly_simplifier_plugin::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) {
if (!is_add(n) || is_ground(n))
return false;
ptr_buffer<expr> args;
v = 0;
expr * curr = to_app(n);
bool stop = false;
inv = false;
while (!stop) {
expr * arg;
expr * neg_arg;
if (is_add(curr)) {
arg = to_app(curr)->get_arg(0);
curr = to_app(curr)->get_arg(1);
}
else {
arg = curr;
stop = true;
}
if (is_ground(arg)) {
TRACE("model_checker_bug", tout << "pushing:\n" << mk_pp(arg, m_manager) << "\n";);
args.push_back(arg);
}
else if (is_var(arg)) {
if (v != 0)
return false; // already found variable
v = to_var(arg);
}
else if (is_times_minus_one(arg, neg_arg) && is_var(neg_arg)) {
if (v != 0)
return false; // already found variable
v = to_var(neg_arg);
inv = true;
}
else {
return false; // non ground term.
}
}
if (v == 0)
return false; // did not find variable
SASSERT(!args.empty());
mk_add(args.size(), args.c_ptr(), t);
return true;
}

View file

@ -0,0 +1,151 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
poly_simplifier_plugin.h
Abstract:
Abstract class for families that have polynomials.
Author:
Leonardo (leonardo) 2008-01-08
--*/
#ifndef _POLY_SIMPLIFIER_PLUGIN_H_
#define _POLY_SIMPLIFIER_PLUGIN_H_
#include "simplifier_plugin.h"
/**
\brief Abstract class that provides simplification functions for polynomials.
*/
class poly_simplifier_plugin : public simplifier_plugin {
protected:
typedef rational numeral;
decl_kind m_ADD;
decl_kind m_MUL;
decl_kind m_SUB;
decl_kind m_UMINUS;
decl_kind m_NUM;
sort * m_curr_sort;
expr * m_curr_sort_zero;
expr * mk_add(unsigned num_args, expr * const * args);
expr * mk_add(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_add(2, args); }
expr * mk_mul(unsigned num_args, expr * const * args);
expr * mk_mul(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_mul(2, args); }
expr * mk_sub(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_fid, m_SUB, num_args, args); }
expr * mk_uminus(expr * arg) { return m_manager.mk_app(m_fid, m_UMINUS, arg); }
void process_monomial(unsigned num_args, expr * const * args, numeral & k, ptr_buffer<expr> & result);
void mk_monomial(unsigned num_args, expr * * args, expr_ref & result);
bool eq_monomials_modulo_k(expr * n1, expr * n2);
bool merge_monomials(bool inv, expr * n1, expr * n2, expr_ref & result);
template<bool Inv>
void add_monomial_core(expr * n, expr_ref_vector & result);
void add_monomial(bool inv, expr * n, expr_ref_vector & result);
template<bool Inv>
void process_sum_of_monomials_core(expr * n, expr_ref_vector & result);
void process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result);
void process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result, numeral & k);
void mk_sum_of_monomials(expr_ref_vector & monomials, expr_ref & result);
template<bool Inv>
void mk_add_core_core(unsigned num_args, expr * const * args, expr_ref & result);
void mk_add_core(bool inv, unsigned num_args, expr * const * args, expr_ref & result);
void append_to_monomial(expr * n, numeral & k, ptr_buffer<expr> & result);
expr * mk_mul(numeral const & c, expr * body);
void mk_sum_of_monomials_core(unsigned sz, expr ** ms, expr_ref & result);
bool is_simple_sum_of_monomials(expr_ref_vector & monomials);
bool is_simple_monomial(expr * m, expr * & x);
public:
poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub, decl_kind num);
virtual ~poly_simplifier_plugin() {}
/**
\brief Return true if the given expression is a numeral, and store its value in \c val.
*/
virtual bool is_numeral(expr * n, numeral & val) const = 0;
bool is_numeral(expr * n) const { return is_app_of(n, m_fid, m_NUM); }
bool is_zero(expr * n) const {
SASSERT(m_curr_sort_zero != 0);
SASSERT(m_manager.get_sort(n) == m_manager.get_sort(m_curr_sort_zero));
return n == m_curr_sort_zero;
}
bool is_zero_safe(expr * n) {
set_curr_sort(m_manager.get_sort(n));
return is_zero(n);
}
virtual bool is_minus_one(expr * n) const = 0;
virtual expr * get_zero(sort * s) const = 0;
/**
\brief Return true if n is of the form (* -1 r)
*/
bool is_times_minus_one(expr * n, expr * & r) const {
if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) {
r = to_app(n)->get_arg(1);
return true;
}
return false;
}
/**
\brief Return true if n is of the form: a <= b or a >= b.
*/
virtual bool is_le_ge(expr * n) const = 0;
/**
\brief Return a constant representing the giving numeral and sort m_curr_sort.
*/
virtual app * mk_numeral(numeral const & n) = 0;
app * mk_zero() { return mk_numeral(numeral::zero()); }
app * mk_one() { return mk_numeral(numeral::one()); }
app * mk_minus_one() { return mk_numeral(numeral::minus_one()); }
/**
\brief Normalize the given numeral with respect to m_curr_sort
*/
virtual numeral norm(numeral const & n) = 0;
void set_curr_sort(sort * s) {
if (s != m_curr_sort) {
// avoid virtual function call
m_curr_sort = s;
m_curr_sort_zero = get_zero(m_curr_sort);
}
}
void set_curr_sort(expr * n) { set_curr_sort(m_manager.get_sort(n)); }
bool is_add(expr const * n) const { return is_app_of(n, m_fid, m_ADD); }
bool is_mul(expr const * n) const { return is_app_of(n, m_fid, m_MUL); }
void mk_add(unsigned num_args, expr * const * args, expr_ref & result);
void mk_add(expr * arg1, expr * arg2, expr_ref & result);
void mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
void mk_sub(expr * arg1, expr * arg2, expr_ref & result);
void mk_uminus(expr * arg, expr_ref & result);
void mk_mul(unsigned num_args, expr * const * args, expr_ref & result);
void mk_mul(expr * arg1, expr * arg2, expr_ref & result);
virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);
virtual bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result);
expr * get_monomial_body(expr * m);
int get_monomial_body_order(expr * m);
void get_monomial_coeff(expr * m, numeral & result);
void inv_monomial(expr * n, expr_ref & result);
bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t);
#ifdef Z3DEBUG
bool wf_monomial(expr * m) const;
bool wf_polynomial(expr * m) const;
#endif
};
#endif /* _POLY_SIMPLIFIER_PLUGIN_H_ */

View file

@ -0,0 +1,213 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
pull_ite_tree.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-22.
Revision History:
--*/
#include"pull_ite_tree.h"
#include"recurse_expr_def.h"
#include"for_each_expr.h"
#include"ast_pp.h"
pull_ite_tree::pull_ite_tree(ast_manager & m, simplifier & s):
m_manager(m),
m_simplifier(s),
m_cache(m) {
}
void pull_ite_tree::cache_result(expr * n, expr * r, proof * pr) {
m_cache.insert(n, r, pr);
}
void pull_ite_tree::visit(expr * n, bool & visited) {
if (!is_cached(n)) {
m_todo.push_back(n);
visited = false;
}
}
bool pull_ite_tree::visit_children(expr * n) {
if (m_manager.is_ite(n)) {
bool visited = true;
visit(to_app(n)->get_arg(1), visited);
visit(to_app(n)->get_arg(2), visited);
return visited;
}
else {
return true;
}
}
void pull_ite_tree::reduce(expr * n) {
// Remark: invoking the simplifier to build the new expression saves a lot of memory.
if (m_manager.is_ite(n)) {
expr * c = to_app(n)->get_arg(0);
expr * t_old = to_app(n)->get_arg(1);
expr * e_old = to_app(n)->get_arg(2);
expr * t = 0;
proof * t_pr = 0;
expr * e = 0;
proof * e_pr = 0;
get_cached(t_old, t, t_pr);
get_cached(e_old, e, e_pr);
expr_ref r(m_manager);
expr * args[3] = {c, t, e};
m_simplifier.mk_app(to_app(n)->get_decl(), 3, args, r);
if (!m_manager.proofs_enabled()) {
// expr * r = m_manager.mk_ite(c, t, e);
cache_result(n, r, 0);
}
else {
// t_pr is a proof for (m_p ... t_old ...) == t
// e_pr is a proof for (m_p ... e_old ...) == e
expr_ref old(m_manager);
expr_ref p_t_old(m_manager);
expr_ref p_e_old(m_manager);
old = mk_p_arg(n); // (m_p ... n ...) where n is (ite c t_old e_old)
p_t_old = mk_p_arg(t_old); // (m_p ... t_old ...)
p_e_old = mk_p_arg(e_old); // (m_p ... e_old ...)
expr_ref tmp1(m_manager);
tmp1 = m_manager.mk_ite(c, p_t_old, p_e_old); // (ite c (m_p ... t_old ...) (m_p ... e_old ...))
proof * pr1 = m_manager.mk_rewrite(old, tmp1); // proof for (m_p ... (ite c t_old e_old) ...) = (ite c (m_p ... t_old ...) (m_p ... e_old ...))
expr_ref tmp2(m_manager);
tmp2 = m_manager.mk_ite(c, t, e); // (ite c t e)
proof * pr2 = 0; // it will contain a proof for (ite c (m_p ... t_old ...) (m_p ... e_old ...)) = (ite c t e)
proof * pr3 = 0; // it will contain a proof for (m_p ... (ite c t_old e_old) ...) = (ite c t e)
proof * proofs[2];
unsigned num_proofs = 0;
if (t_pr != 0) {
proofs[num_proofs] = t_pr;
num_proofs++;
}
if (e_pr != 0) {
proofs[num_proofs] = e_pr;
num_proofs++;
}
if (num_proofs > 0) {
pr2 = m_manager.mk_congruence(to_app(tmp1), to_app(tmp2), num_proofs, proofs);
pr3 = m_manager.mk_transitivity(pr1, pr2);
}
else {
pr3 = pr1;
}
proof * pr4 = 0; // it will contain a proof for (ite c t e) = r
proof * pr5 = 0; // it will contain a proof for (m_p ... (ite c t_old e_old) ...) = r
if (tmp2 != r) {
pr4 = m_manager.mk_rewrite(tmp2, r);
pr5 = m_manager.mk_transitivity(pr3, pr4);
}
else {
pr5 = pr3;
}
cache_result(n, r, pr5);
}
}
else {
expr_ref r(m_manager);
m_args[m_arg_idx] = n;
m_simplifier.mk_app(m_p, m_args.size(), m_args.c_ptr(), r);
if (!m_manager.proofs_enabled()) {
// expr * r = m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr());
cache_result(n, r, 0);
}
else {
expr_ref old(m_manager);
proof * p;
old = mk_p_arg(n);
if (old == r)
p = 0;
else
p = m_manager.mk_rewrite(old, r);
cache_result(n, r, p);
}
}
}
void pull_ite_tree::operator()(app * n, app_ref & r, proof_ref & pr) {
unsigned num_args = n->get_num_args();
m_args.resize(num_args);
m_p = n->get_decl();
expr * ite = 0;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = n->get_arg(i);
if (ite) {
m_args[i] = arg;
}
else if (m_manager.is_ite(arg)) {
m_arg_idx = i;
m_args[i] = 0;
ite = arg;
}
else {
m_args[i] = arg;
}
}
if (!ite) {
r = n;
pr = 0;
return;
}
m_todo.push_back(ite);
while (!m_todo.empty()) {
expr * n = m_todo.back();
if (is_cached(n))
m_todo.pop_back();
else if (visit_children(n)) {
m_todo.pop_back();
reduce(n);
}
}
SASSERT(is_cached(ite));
expr * _r = 0;
proof * _pr = 0;
get_cached(ite, _r, _pr);
r = to_app(_r);
pr = _pr;
m_cache.reset();
m_todo.reset();
}
pull_ite_tree_star::pull_ite_tree_star(ast_manager & m, simplifier & s):
simplifier(m),
m_proc(m, s) {
borrow_plugins(s);
}
bool pull_ite_tree_star::get_subst(expr * n, expr_ref & r, proof_ref & p) {
if (is_app(n) && is_target(to_app(n))) {
app_ref tmp(m_manager);
m_proc(to_app(n), tmp, p);
r = tmp;
return true;
}
return false;
}
bool pull_cheap_ite_tree_star::is_target(app * n) const {
bool r =
n->get_num_args() == 2 &&
n->get_family_id() != null_family_id &&
m_manager.is_bool(n) &&
(m_manager.is_value(n->get_arg(0)) || m_manager.is_value(n->get_arg(1))) &&
(m_manager.is_term_ite(n->get_arg(0)) || m_manager.is_term_ite(n->get_arg(1)));
TRACE("pull_ite_target", tout << mk_pp(n, m_manager) << "\nresult: " << r << "\n";);
return r;
}

View file

@ -0,0 +1,101 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
pull_ite_tree.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-22.
Revision History:
--*/
#ifndef _PULL_ITE_TREE_H_
#define _PULL_ITE_TREE_H_
#include"ast.h"
#include"simplifier.h"
#include"recurse_expr.h"
#include"obj_hashtable.h"
#include"expr_map.h"
/**
\brief Functor for applying the following transformation
F[(p (ite c t1 t2) args)] = F'[(ite c t1 t2), p, args]
F'[(ite c t1 t2), p, args] = (ite c F'[t1, p, args] F'[t2, p, args])
F'[t, p, args] = (p t args)
*/
class pull_ite_tree {
ast_manager & m_manager;
simplifier & m_simplifier;
func_decl * m_p;
ptr_vector<expr> m_args;
unsigned m_arg_idx; //!< position of the ite argument
expr_map m_cache;
ptr_vector<expr> m_todo;
bool is_cached(expr * n) const { return m_cache.contains(n); }
void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); }
void cache_result(expr * n, expr * r, proof * pr);
void visit(expr * n, bool & visited);
bool visit_children(expr * n);
void reduce(expr * n);
/**
\brief Creante an application (m_p ... n ...) where n is the argument m_arg_idx and the other arguments
are in m_args.
*/
expr * mk_p_arg(expr * n) {
m_args[m_arg_idx] = n;
return m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr());
}
public:
pull_ite_tree(ast_manager & m, simplifier & s);
/**
\brief Apply the transformation above if n contains an ite-expression.
Store the result in r. If n does not contain an ite-expression, then
store n in r.
When proof generation is enabled, pr is a proof for n = r.
*/
void operator()(app * n, app_ref & r, proof_ref & pr);
};
/**
\brief Functor for applying the pull_ite_tree on subexpressions n that
satisfy the is_target virtual predicate.
*/
class pull_ite_tree_star : public simplifier {
protected:
pull_ite_tree m_proc;
virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
public:
pull_ite_tree_star(ast_manager & m, simplifier & s);
virtual ~pull_ite_tree_star() { release_plugins(); }
virtual bool is_target(app * n) const = 0;
};
/**
\brief Apply pull_ite_tree on predicates of the form
(p ite v) and (p v ite)
where:
- p is an interpreted predicate
- ite is an ite-term expression
- v is a value
*/
class pull_cheap_ite_tree_star : public pull_ite_tree_star {
public:
pull_cheap_ite_tree_star(ast_manager & m, simplifier & s):pull_ite_tree_star(m, s) {}
virtual ~pull_cheap_ite_tree_star() {}
virtual bool is_target(app * n) const;
};
#endif /* _PULL_ITE_TREE_H_ */

View file

@ -0,0 +1,220 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
push_app_ite.cpp
Abstract:
TODO: Write a better ite lifter
Author:
Leonardo de Moura (leonardo) 2008-05-14.
Revision History:
--*/
#include"push_app_ite.h"
#include"ast_pp.h"
push_app_ite::push_app_ite(simplifier & s, bool conservative):
simplifier(s.get_manager()),
m_conservative(conservative) {
borrow_plugins(s);
}
push_app_ite::~push_app_ite() {
// the plugins were borrowed. So, release ownership.
m_plugins.release();
}
int push_app_ite::has_ite_arg(unsigned num_args, expr * const * args) {
for (unsigned i = 0; i < num_args; i++)
if (m_manager.is_ite(args[i]))
return i;
return -1;
}
void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & r) {
TRACE("push_app_ite", tout << "pushing app...\n";);
int ite_arg_idx = has_ite_arg(num_args, args);
if (ite_arg_idx < 0) {
mk_app(decl, num_args, args, r);
return;
}
app * ite = to_app(args[ite_arg_idx]);
expr * c = ite->get_arg(0);
expr * t = ite->get_arg(1);
expr * e = ite->get_arg(2);
expr ** args_prime = const_cast<expr**>(args);
expr * old = args_prime[ite_arg_idx];
args_prime[ite_arg_idx] = t;
expr_ref t_new(m_manager);
apply(decl, num_args, args_prime, t_new);
args_prime[ite_arg_idx] = e;
expr_ref e_new(m_manager);
apply(decl, num_args, args_prime, e_new);
args_prime[ite_arg_idx] = old;
expr * new_args[3] = { c, t_new, e_new };
mk_app(ite->get_decl(), 3, new_args, r);
}
/**
\brief Default (conservative) implementation. Return true if there one and only one ite-term argument.
*/
bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
if (m_manager.is_ite(decl))
return false;
bool found_ite = false;
for (unsigned i = 0; i < num_args; i++) {
if (m_manager.is_ite(args[i]) && !m_manager.is_bool(args[i])) {
if (found_ite) {
if (m_conservative)
return false;
}
else {
found_ite = true;
}
}
}
CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n";
tout << decl->get_name();
for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m_manager);
tout << "\n";);
return found_ite;
}
void push_app_ite::operator()(expr * s, expr_ref & r, proof_ref & p) {
expr * result;
proof * result_proof;
reduce_core(s);
get_cached(s, result, result_proof);
r = result;
switch (m_manager.proof_mode()) {
case PGM_DISABLED:
p = m_manager.mk_undef_proof();
break;
case PGM_COARSE:
if (result == s)
p = m_manager.mk_reflexivity(s);
else
p = m_manager.mk_rewrite_star(s, result, 0, 0);
break;
case PGM_FINE:
if (result == s)
p = m_manager.mk_reflexivity(s);
else
p = result_proof;
break;
}
}
void push_app_ite::reduce_core(expr * n) {
if (!is_cached(n)) {
unsigned sz = m_todo.size();
m_todo.push_back(n);
while (m_todo.size() != sz) {
expr * n = m_todo.back();
if (is_cached(n))
m_todo.pop_back();
else if (visit_children(n)) {
m_todo.pop_back();
reduce1(n);
}
}
}
}
bool push_app_ite::visit_children(expr * n) {
bool visited = true;
unsigned j;
switch(n->get_kind()) {
case AST_VAR:
return true;
case AST_APP:
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
visit(to_app(n)->get_arg(j), visited);
}
return visited;
case AST_QUANTIFIER:
visit(to_quantifier(n)->get_expr(), visited);
return visited;
default:
UNREACHABLE();
return true;
}
}
void push_app_ite::reduce1(expr * n) {
switch (n->get_kind()) {
case AST_VAR:
cache_result(n, n, 0);
break;
case AST_APP:
reduce1_app(to_app(n));
break;
case AST_QUANTIFIER:
reduce1_quantifier(to_quantifier(n));
break;
default:
UNREACHABLE();
}
}
void push_app_ite::reduce1_app(app * n) {
m_args.reset();
func_decl * decl = n->get_decl();
proof_ref p1(m_manager);
get_args(n, m_args, p1);
expr_ref r(m_manager);
if (is_target(decl, m_args.size(), m_args.c_ptr()))
apply(decl, m_args.size(), m_args.c_ptr(), r);
else
mk_app(decl, m_args.size(), m_args.c_ptr(), r);
if (!m_manager.fine_grain_proofs())
cache_result(n, r, 0);
else {
expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
proof * p;
if (n == r)
p = 0;
else if (r != s)
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r));
else
p = p1;
cache_result(n, r, p);
}
}
void push_app_ite::reduce1_quantifier(quantifier * q) {
expr * new_body;
proof * new_body_pr;
get_cached(q->get_expr(), new_body, new_body_pr);
quantifier * new_q = m_manager.update_quantifier(q, new_body);
proof * p = q == new_q ? 0 : m_manager.mk_quant_intro(q, new_q, new_body_pr);
cache_result(q, new_q, p);
}
bool ng_push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
bool r = push_app_ite::is_target(decl, num_args, args);
if (!r)
return false;
for (unsigned i = 0; i < num_args; i++)
if (!is_ground(args[i]))
return true;
return false;
}
ng_push_app_ite::ng_push_app_ite(simplifier & s, bool conservative):
push_app_ite(s, conservative) {
}

View file

@ -0,0 +1,63 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
push_app_ite.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-14.
Revision History:
--*/
#ifndef _PUSH_APP_ITE_H_
#define _PUSH_APP_ITE_H_
#include"ast.h"
#include"simplifier.h"
/**
\brief Functor for applying the following transformation:
(f s (ite c t1 t2)) ==> (ite c (f s t1) (f s t2))
*/
class push_app_ite : public simplifier {
protected:
bool m_conservative;
int has_ite_arg(unsigned num_args, expr * const * args);
void apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result);
virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
void reduce_core(expr * n);
bool visit_children(expr * n);
void reduce1(expr * n);
void reduce1_app(app * n);
void reduce1_quantifier(quantifier * q);
public:
push_app_ite(simplifier & s, bool conservative = true);
virtual ~push_app_ite();
void operator()(expr * s, expr_ref & r, proof_ref & p);
};
/**
\brief Variation of push_app_ite that applies the transformation on nonground terms only.
\remark This functor uses the app::is_ground method. This method is not
completly precise, for instance, any term containing a quantifier is marked as non ground.
*/
class ng_push_app_ite : public push_app_ite {
protected:
virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
public:
ng_push_app_ite(simplifier & s, bool conservative = true);
virtual ~ng_push_app_ite() {}
};
#endif /* _PUSH_APP_ITE_H_ */

View file

@ -0,0 +1,949 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
simplifier.cpp
Abstract:
Expression simplifier.
Author:
Leonardo (leonardo) 2008-01-03
Notes:
--*/
#include"simplifier.h"
#include"var_subst.h"
#include"ast_ll_pp.h"
#include"ast_pp.h"
#include"well_sorted.h"
#include"ast_smt_pp.h"
simplifier::simplifier(ast_manager & m):
base_simplifier(m),
m_proofs(m),
m_subst_proofs(m),
m_need_reset(false),
m_use_oeq(false),
m_visited_quantifier(false),
m_ac_support(true) {
}
void simplifier::register_plugin(plugin * p) {
m_plugins.register_plugin(p);
}
simplifier::~simplifier() {
flush_cache();
}
void simplifier::enable_ac_support(bool flag) {
m_ac_support = flag;
ptr_vector<plugin>::const_iterator it = m_plugins.begin();
ptr_vector<plugin>::const_iterator end = m_plugins.end();
for (; it != end; ++it) {
if (*it != 0)
(*it)->enable_ac_support(flag);
}
}
/**
\brief External interface for the simplifier.
A client will invoke operator()(s, r, p) to simplify s.
The result is stored in r.
When proof generation is enabled, a proof for the equivalence (or equisatisfiability)
of s and r is stored in p.
When proof generation is disabled, this method stores the "undefined proof" object in p.
*/
void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
m_need_reset = true;
expr * old_s;
expr * result;
proof * result_proof;
switch (m_manager.proof_mode()) {
case PGM_DISABLED: // proof generation is disabled.
reduce_core(s);
// after executing reduce_core, the result of the simplification is in the cache
get_cached(s, result, result_proof);
r = result;
p = m_manager.mk_undef_proof();
break;
case PGM_COARSE: // coarse proofs... in this case, we do not produce a step by step (fine grain) proof to show the equivalence (or equisatisfiability) of s an r.
m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst.
reduce_core(s);
get_cached(s, result, result_proof);
r = result;
if (result == s)
p = m_manager.mk_reflexivity(s);
else {
remove_duplicates(m_subst_proofs);
p = m_manager.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr());
}
break;
case PGM_FINE: // fine grain proofs... in this mode, every proof step (or most of them) is described.
m_proofs.reset();
old_s = 0;
// keep simplyfing until no further simplifications are possible.
while (s != old_s) {
TRACE("simplifier", tout << "simplification pass... " << s->get_id() << "\n";);
TRACE("simplifier_loop", tout << mk_ll_pp(s, m_manager) << "\n";);
reduce_core(s);
get_cached(s, result, result_proof);
if (result_proof != 0)
m_proofs.push_back(result_proof);
old_s = s;
s = result;
}
SASSERT(s != 0);
r = s;
p = m_proofs.empty() ? m_manager.mk_reflexivity(s) : m_manager.mk_transitivity(m_proofs.size(), m_proofs.c_ptr());
break;
default:
UNREACHABLE();
}
}
void simplifier::flush_cache() {
m_cache.flush();
ptr_vector<plugin>::const_iterator it = m_plugins.begin();
ptr_vector<plugin>::const_iterator end = m_plugins.end();
for (; it != end; ++it) {
if (*it != 0) {
(*it)->flush_caches();
}
}
}
bool simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) {
return false;
}
void simplifier::reduce_core(expr * n) {
if (!is_cached(n)) {
// We do not assume m_todo is empty... So, we store the current size of the todo-stack.
unsigned sz = m_todo.size();
m_todo.push_back(n);
while (m_todo.size() != sz) {
expr * n = m_todo.back();
if (is_cached(n))
m_todo.pop_back();
else if (visit_children(n)) {
// if all children were already simplified, then remove n from the todo stack and apply a
// simplification step to it.
m_todo.pop_back();
reduce1(n);
}
}
}
}
/**
\brief Return true if all children of n have been already simplified.
*/
bool simplifier::visit_children(expr * n) {
switch(n->get_kind()) {
case AST_VAR:
return true;
case AST_APP:
// The simplifier has support for flattening AC (associative-commutative) operators.
// The method ast_manager::mk_app is used to create the flat version of an AC operator.
// In Z3 1.x, we used multi-ary operators. This creates problems for the superposition engine.
// So, starting at Z3 2.x, only boolean operators can be multi-ary.
// Example:
// (and (and a b) (and c d)) --> (and a b c d)
// (+ (+ a b) (+ c d)) --> (+ a (+ b (+ c d)))
// Remark: The flattening is only applied if m_ac_support is true.
if (m_ac_support && to_app(n)->get_decl()->is_associative() && to_app(n)->get_decl()->is_commutative())
return visit_ac(to_app(n));
else {
bool visited = true;
unsigned j = to_app(n)->get_num_args();
while (j > 0) {
--j;
visit(to_app(n)->get_arg(j), visited);
}
return visited;
}
case AST_QUANTIFIER:
return visit_quantifier(to_quantifier(n));
default:
UNREACHABLE();
return true;
}
}
/**
\brief Visit the children of n assuming it is an AC (associative-commutative) operator.
For example, if n is of the form (+ (+ a b) (+ c d)), this method
will return true if the nodes a, b, c and d have been already simplified.
The nodes (+ a b) and (+ c d) are not really checked.
*/
bool simplifier::visit_ac(app * n) {
bool visited = true;
func_decl * decl = n->get_decl();
SASSERT(m_ac_support);
SASSERT(decl->is_associative());
SASSERT(decl->is_commutative());
m_ac_marked.reset();
ptr_buffer<app> todo;
todo.push_back(n);
while (!todo.empty()) {
app * n = todo.back();
todo.pop_back();
if (m_ac_mark.is_marked(n))
continue;
m_ac_mark.mark(n, true);
m_ac_marked.push_back(n);
SASSERT(n->get_decl() == decl);
unsigned i = n->get_num_args();
while (i > 0) {
--i;
expr * arg = n->get_arg(i);
if (is_app_of(arg, decl))
todo.push_back(to_app(arg));
else
visit(arg, visited);
}
}
ptr_vector<expr>::const_iterator it = m_ac_marked.begin();
ptr_vector<expr>::const_iterator end = m_ac_marked.end();
for (; it != end; ++it)
m_ac_mark.mark(*it, false);
return visited;
}
bool simplifier::visit_quantifier(quantifier * n) {
m_visited_quantifier = true;
bool visited = true;
unsigned j = to_quantifier(n)->get_num_patterns();
while (j > 0) {
--j;
visit(to_quantifier(n)->get_pattern(j), visited);
}
j = to_quantifier(n)->get_num_no_patterns();
while (j > 0) {
--j;
visit(to_quantifier(n)->get_no_pattern(j), visited);
}
visit(to_quantifier(n)->get_expr(), visited);
return visited;
}
/**
\brief Simplify n and store the result in the cache.
*/
void simplifier::reduce1(expr * n) {
switch (n->get_kind()) {
case AST_VAR:
cache_result(n, n, 0);
break;
case AST_APP:
reduce1_app(to_app(n));
break;
case AST_QUANTIFIER:
reduce1_quantifier(to_quantifier(n));
break;
default:
UNREACHABLE();
}
}
/**
\brief Simplify the given application using the cached values,
associativity flattening, the given substitution, and family/theory
specific simplifications via plugins.
*/
void simplifier::reduce1_app(app * n) {
expr_ref r(m_manager);
proof_ref p(m_manager);
TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m_manager) << "\n";);
if (get_subst(n, r, p)) {
TRACE("reduce", tout << "applying substitution...\n";);
cache_result(n, r, p);
return;
}
func_decl * decl = n->get_decl();
if (m_ac_support && decl->is_associative() && decl->is_commutative())
reduce1_ac_app_core(n);
else
reduce1_app_core(n);
}
void simplifier::reduce1_app_core(app * n) {
m_args.reset();
func_decl * decl = n->get_decl();
proof_ref p1(m_manager);
// Stores the new arguments of n in m_args.
// Let n be of the form
// (decl arg_0 ... arg_{n-1})
// then
// m_args contains [arg_0', ..., arg_{n-1}'],
// where arg_i' is the simplification of arg_i
// and
// p1 is a proof for
// (decl arg_0 ... arg_{n-1}) is equivalente/equisatisfiable to (decl arg_0' ... arg_{n-1}')
// p1 is built using the congruence proof step and the proofs for arg_0' ... arg_{n-1}'.
// Of course, p1 is 0 if proofs are not enabled or coarse grain proofs are used.
bool has_new_args = get_args(n, m_args, p1);
// The following if implements a simple trick.
// If none of the arguments have been simplified, and n is not a theory symbol,
// Then no simplification is possible, and we can cache the result of the simplification of n as n.
if (has_new_args || decl->get_family_id() != null_family_id) {
expr_ref r(m_manager);
TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m_manager););
// the method mk_app invokes get_subst and plugins to simplify
// (decl arg_0' ... arg_{n-1}')
mk_app(decl, m_args.size(), m_args.c_ptr(), r);
if (!m_manager.fine_grain_proofs()) {
cache_result(n, r, 0);
}
else {
expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
proof * p;
if (n == r)
p = 0;
else if (r != s)
// we use a "theory rewrite generic proof" to justify the step
// s = (decl arg_0' ... arg_{n-1}') --> r
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r));
else
p = p1;
cache_result(n, r, p);
}
}
else {
cache_result(n, n, 0);
}
}
bool is_ac_list(app * n, ptr_vector<expr> & args) {
args.reset();
func_decl * f = n->get_decl();
app * curr = n;
while (true) {
if (curr->get_num_args() != 2)
return false;
expr * arg1 = curr->get_arg(0);
if (is_app_of(arg1, f))
return false;
args.push_back(arg1);
expr * arg2 = curr->get_arg(1);
if (!is_app_of(arg2, f)) {
args.push_back(arg2);
return true;
}
curr = to_app(arg2);
}
}
bool is_ac_vector(app * n) {
func_decl * f = n->get_decl();
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
if (is_app_of(n->get_arg(i), f))
return false;
}
return true;
}
void simplifier::reduce1_ac_app_core(app * n) {
app_ref n_c(m_manager);
proof_ref p1(m_manager);
mk_ac_congruent_term(n, n_c, p1);
TRACE("ac", tout << "expr:\n" << mk_pp(n, m_manager) << "\ncongruent term:\n" << mk_pp(n_c, m_manager) << "\n";);
expr_ref r(m_manager);
func_decl * decl = n->get_decl();
family_id fid = decl->get_family_id();
plugin * p = get_plugin(fid);
if (is_ac_vector(n_c)) {
if (p != 0 && p->reduce(decl, n_c->get_num_args(), n_c->get_args(), r)) {
// done...
}
else {
r = n_c;
}
}
else if (is_ac_list(n_c, m_args)) {
// m_args contains the arguments...
if (p != 0 && p->reduce(decl, m_args.size(), m_args.c_ptr(), r)) {
// done...
}
else {
r = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
}
}
else {
m_args.reset();
m_mults.reset();
get_ac_args(n_c, m_args, m_mults);
TRACE("ac", tout << "AC args:\n";
for (unsigned i = 0; i < m_args.size(); i++) {
tout << mk_pp(m_args[i], m_manager) << " * " << m_mults[i] << "\n";
});
if (p != 0 && p->reduce(decl, m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), r)) {
// done...
}
else {
ptr_buffer<expr> new_args;
expand_args(m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), new_args);
r = m_manager.mk_app(decl, new_args.size(), new_args.c_ptr());
}
}
TRACE("ac", tout << "AC result:\n" << mk_pp(r, m_manager) << "\n";);
if (!m_manager.fine_grain_proofs()) {
cache_result(n, r, 0);
}
else {
proof * p;
if (n == r.get())
p = 0;
else if (r.get() != n_c.get())
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(n_c, r));
else
p = p1;
cache_result(n, r, p);
}
}
static unsigned g_rewrite_lemma_id = 0;
void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result) {
expr_ref arg(m_manager);
arg = m_manager.mk_app(decl, num_args, args);
if (arg.get() != result) {
char buffer[128];
#ifdef _WINDOWS
sprintf_s(buffer, ARRAYSIZE(buffer), "lemma_%d.smt", g_rewrite_lemma_id);
#else
sprintf(buffer, "rewrite_lemma_%d.smt", g_rewrite_lemma_id);
#endif
ast_smt_pp pp(m_manager);
pp.set_benchmark_name("rewrite_lemma");
pp.set_status("unsat");
expr_ref n(m_manager);
n = m_manager.mk_not(m_manager.mk_eq(arg.get(), result));
std::ofstream out(buffer);
pp.display(out, n);
out.close();
++g_rewrite_lemma_id;
}
}
/**
\brief Return in \c result an expression \c e equivalent to <tt>(f args[0] ... args[num_args - 1])</tt>, and
store in \c pr a proof for <tt>(= (f args[0] ... args[num_args - 1]) e)</tt>
If e is identical to (f args[0] ... args[num_args - 1]), then pr is set to 0.
*/
void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result) {
m_need_reset = true;
if (m_manager.is_eq(decl)) {
sort * s = m_manager.get_sort(args[0]);
plugin * p = get_plugin(s->get_family_id());
if (p != 0 && p->reduce_eq(args[0], args[1], result))
return;
}
else if (m_manager.is_distinct(decl)) {
sort * s = m_manager.get_sort(args[0]);
plugin * p = get_plugin(s->get_family_id());
if (p != 0 && p->reduce_distinct(num_args, args, result))
return;
}
family_id fid = decl->get_family_id();
plugin * p = get_plugin(fid);
if (p != 0 && p->reduce(decl, num_args, args, result)) {
//uncomment this line if you want to trace rewrites as lemmas:
//dump_rewrite_lemma(decl, num_args, args, result.get());
return;
}
result = m_manager.mk_app(decl, num_args, args);
}
/**
\brief Create a term congruence to n (f a[0] ... a[num_args-1]) using the
cached values for the a[i]'s. Store the result in r, and the proof for (= n r) in p.
If n and r are identical, then set p to 0.
*/
void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
bool has_new_args = false;
ptr_vector<expr> args;
ptr_vector<proof> proofs;
unsigned num = n->get_num_args();
for (unsigned j = 0; j < num; j++) {
expr * arg = n->get_arg(j);
expr * new_arg;
proof * arg_proof;
get_cached(arg, new_arg, arg_proof);
CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0),
tout << mk_ll_pp(arg, m_manager) << "\n---->\n" << mk_ll_pp(new_arg, m_manager) << "\n";
tout << "#" << arg->get_id() << " #" << new_arg->get_id() << "\n";
tout << arg << " " << new_arg << "\n";);
if (arg != new_arg) {
has_new_args = true;
proofs.push_back(arg_proof);
SASSERT(arg_proof);
}
else {
SASSERT(arg_proof == 0);
}
args.push_back(new_arg);
}
if (has_new_args) {
r = m_manager.mk_app(n->get_decl(), args.size(), args.c_ptr());
if (m_use_oeq)
p = m_manager.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr());
else
p = m_manager.mk_congruence(n, r, proofs.size(), proofs.c_ptr());
}
else {
r = n;
p = 0;
}
}
/**
\brief Store the new arguments of \c n in result. Store in p a proof for
(= n (f result[0] ... result[num_args - 1])), where f is the function symbol of n.
If there are no new arguments or fine grain proofs are disabled, then p is set to 0.
Return true there are new arguments.
*/
bool simplifier::get_args(app * n, ptr_vector<expr> & result, proof_ref & p) {
bool has_new_args = false;
unsigned num = n->get_num_args();
if (m_manager.fine_grain_proofs()) {
app_ref r(m_manager);
mk_congruent_term(n, r, p);
result.append(r->get_num_args(), r->get_args());
SASSERT(n->get_num_args() == result.size());
has_new_args = r != n;
}
else {
p = 0;
for (unsigned j = 0; j < num; j++) {
expr * arg = n->get_arg(j);
expr * new_arg;
proof * arg_proof;
get_cached(arg, new_arg, arg_proof);
if (arg != new_arg) {
has_new_args = true;
}
result.push_back(new_arg);
}
}
return has_new_args;
}
/**
\brief Create a term congruence to n (where n is an expression such as: (f (f a_1 a_2) (f a_3 (f a_4 a_5))) using the
cached values for the a_i's. Store the result in r, and the proof for (= n r) in p.
If n and r are identical, then set p to 0.
*/
void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
SASSERT(m_ac_support);
func_decl * f = n->get_decl();
m_ac_cache.reset();
m_ac_pr_cache.reset();
ptr_buffer<app> todo;
ptr_buffer<expr> new_args;
ptr_buffer<proof> new_arg_prs;
todo.push_back(n);
while (!todo.empty()) {
app * curr = todo.back();
if (m_ac_cache.contains(curr)) {
todo.pop_back();
continue;
}
bool visited = true;
bool has_new_arg = false;
new_args.reset();
new_arg_prs.reset();
unsigned num_args = curr->get_num_args();
for (unsigned j = 0; j < num_args; j ++) {
expr * arg = curr->get_arg(j);
if (is_app_of(arg, f)) {
app * new_arg = 0;
if (m_ac_cache.find(to_app(arg), new_arg)) {
SASSERT(new_arg != 0);
new_args.push_back(new_arg);
if (arg != new_arg)
has_new_arg = true;
if (m_manager.fine_grain_proofs()) {
proof * pr = 0;
m_ac_pr_cache.find(to_app(arg), pr);
if (pr != 0)
new_arg_prs.push_back(pr);
}
}
else {
visited = false;
todo.push_back(to_app(arg));
}
}
else {
expr * new_arg = 0;
proof * pr;
get_cached(arg, new_arg, pr);
new_args.push_back(new_arg);
if (arg != new_arg)
has_new_arg = true;
if (m_manager.fine_grain_proofs() && pr != 0)
new_arg_prs.push_back(pr);
}
}
if (visited) {
SASSERT(new_args.size() == curr->get_num_args());
todo.pop_back();
if (!has_new_arg) {
m_ac_cache.insert(curr, curr);
if (m_manager.fine_grain_proofs())
m_ac_pr_cache.insert(curr, 0);
}
else {
app * new_curr = m_manager.mk_app(f, new_args.size(), new_args.c_ptr());
m_ac_cache.insert(curr, new_curr);
if (m_manager.fine_grain_proofs()) {
proof * p = m_manager.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr());
m_ac_pr_cache.insert(curr, p);
}
}
}
}
SASSERT(m_ac_cache.contains(n));
app * new_n = 0;
m_ac_cache.find(n, new_n);
r = new_n;
if (m_manager.fine_grain_proofs()) {
proof * new_pr = 0;
m_ac_pr_cache.find(n, new_pr);
p = new_pr;
}
}
#define White 0
#define Grey 1
#define Black 2
static int get_color(obj_map<expr, int> & colors, expr * n) {
obj_map<expr, int>::obj_map_entry * entry = colors.insert_if_not_there2(n, White);
return entry->get_data().m_value;
}
static bool visit_ac_children(func_decl * f, expr * n, obj_map<expr, int> & colors, ptr_buffer<expr> & todo, ptr_buffer<expr> & result) {
if (is_app_of(n, f)) {
unsigned num_args = to_app(n)->get_num_args();
bool visited = true;
// Put the arguments in 'result' in reverse order.
// Reason: preserve the original order of the arguments in the final result.
// Remark: get_ac_args will traverse 'result' backwards.
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(n)->get_arg(i);
obj_map<expr, int>::obj_map_entry * entry = colors.insert_if_not_there2(arg, White);
if (entry->get_data().m_value == White) {
todo.push_back(arg);
visited = false;
}
}
return visited;
}
else {
return true;
}
}
void simplifier::ac_top_sort(app * n, ptr_buffer<expr> & result) {
ptr_buffer<expr> todo;
func_decl * f = n->get_decl();
obj_map<expr, int> & colors = m_colors;
colors.reset();
todo.push_back(n);
while (!todo.empty()) {
expr * curr = todo.back();
int color;
obj_map<expr, int>::obj_map_entry * entry = colors.insert_if_not_there2(curr, White);
SASSERT(entry);
color = entry->get_data().m_value;
switch (color) {
case White:
// Remark: entry becomes invalid if an element is inserted into the hashtable.
// So, I must set Grey before executing visit_ac_children.
entry->get_data().m_value = Grey;
SASSERT(get_color(colors, curr) == Grey);
if (visit_ac_children(f, curr, colors, todo, result)) {
// If visit_ac_children succeeded, then the hashtable was not modified,
// and entry is still valid.
SASSERT(todo.back() == curr);
entry->get_data().m_value = Black;
SASSERT(get_color(colors, curr) == Black);
result.push_back(curr);
todo.pop_back();
}
break;
case Grey:
SASSERT(visit_ac_children(f, curr, colors, todo, result));
SASSERT(entry);
entry->get_data().m_value = Black;
SASSERT(get_color(colors, curr) == Black);
result.push_back(curr);
SASSERT(todo.back() == curr);
todo.pop_back();
break;
case Black:
todo.pop_back();
break;
default:
UNREACHABLE();
}
}
}
void simplifier::get_ac_args(app * n, ptr_vector<expr> & args, vector<rational> & mults) {
SASSERT(m_ac_support);
ptr_buffer<expr> sorted_exprs;
ac_top_sort(n, sorted_exprs);
SASSERT(!sorted_exprs.empty());
SASSERT(sorted_exprs[sorted_exprs.size()-1] == n);
TRACE("ac", tout << mk_ll_pp(n, m_manager, true, false) << "#" << n->get_id() << "\nsorted expressions...\n";
for (unsigned i = 0; i < sorted_exprs.size(); i++) {
tout << "#" << sorted_exprs[i]->get_id() << " ";
}
tout << "\n";);
m_ac_mults.reset();
m_ac_mults.insert(n, rational(1));
func_decl * decl = n->get_decl();
unsigned j = sorted_exprs.size();
while (j > 0) {
--j;
expr * curr = sorted_exprs[j];
rational mult;
m_ac_mults.find(curr, mult);
SASSERT(!mult.is_zero());
if (is_app_of(curr, decl)) {
unsigned num_args = to_app(curr)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(curr)->get_arg(i);
rational zero;
obj_map<expr, rational>::obj_map_entry * entry = m_ac_mults.insert_if_not_there2(arg, zero);
entry->get_data().m_value += mult;
}
}
else {
args.push_back(curr);
mults.push_back(mult);
}
}
}
void simplifier::reduce1_quantifier(quantifier * q) {
expr * new_body;
proof * new_body_pr;
SASSERT(is_well_sorted(m_manager, q));
get_cached(q->get_expr(), new_body, new_body_pr);
quantifier_ref q1(m_manager);
proof * p1 = 0;
if (is_quantifier(new_body) &&
to_quantifier(new_body)->is_forall() == q->is_forall() &&
!to_quantifier(q)->has_patterns() &&
!to_quantifier(new_body)->has_patterns()) {
quantifier * nested_q = to_quantifier(new_body);
ptr_buffer<sort> sorts;
buffer<symbol> names;
sorts.append(q->get_num_decls(), q->get_decl_sorts());
names.append(q->get_num_decls(), q->get_decl_names());
sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts());
names.append(nested_q->get_num_decls(), nested_q->get_decl_names());
q1 = m_manager.mk_quantifier(q->is_forall(),
sorts.size(),
sorts.c_ptr(),
names.c_ptr(),
nested_q->get_expr(),
std::min(q->get_weight(), nested_q->get_weight()),
q->get_qid(),
q->get_skid(),
0, 0, 0, 0);
SASSERT(is_well_sorted(m_manager, q1));
if (m_manager.fine_grain_proofs()) {
quantifier * q0 = m_manager.update_quantifier(q, new_body);
proof * p0 = q == q0 ? 0 : m_manager.mk_quant_intro(q, q0, new_body_pr);
p1 = m_manager.mk_pull_quant(q0, q1);
p1 = m_manager.mk_transitivity(p0, p1);
}
}
else {
ptr_buffer<expr> new_patterns;
ptr_buffer<expr> new_no_patterns;
expr * new_pattern;
proof * new_pattern_pr;
// Remark: we can ignore the proofs for the patterns.
unsigned num = q->get_num_patterns();
for (unsigned i = 0; i < num; i++) {
get_cached(q->get_pattern(i), new_pattern, new_pattern_pr);
if (m_manager.is_pattern(new_pattern)) {
new_patterns.push_back(new_pattern);
}
}
num = q->get_num_no_patterns();
for (unsigned i = 0; i < num; i++) {
get_cached(q->get_no_pattern(i), new_pattern, new_pattern_pr);
new_no_patterns.push_back(new_pattern);
}
remove_duplicates(new_patterns);
remove_duplicates(new_no_patterns);
q1 = m_manager.mk_quantifier(q->is_forall(),
q->get_num_decls(),
q->get_decl_sorts(),
q->get_decl_names(),
new_body,
q->get_weight(),
q->get_qid(),
q->get_skid(),
new_patterns.size(),
new_patterns.c_ptr(),
new_no_patterns.size(),
new_no_patterns.c_ptr());
SASSERT(is_well_sorted(m_manager, q1));
TRACE("simplifier", tout << mk_pp(q, m_manager) << "\n" << mk_pp(q1, m_manager) << "\n";);
if (m_manager.fine_grain_proofs()) {
if (q != q1 && !new_body_pr) {
new_body_pr = m_manager.mk_rewrite(q->get_expr(), new_body);
}
p1 = q == q1 ? 0 : m_manager.mk_quant_intro(q, q1, new_body_pr);
}
}
expr_ref r(m_manager);
elim_unused_vars(m_manager, q1, r);
proof * pr = 0;
if (m_manager.fine_grain_proofs()) {
proof * p2 = 0;
if (q1.get() != r.get())
p2 = m_manager.mk_elim_unused_vars(q1, r);
pr = m_manager.mk_transitivity(p1, p2);
}
cache_result(q, r, pr);
}
/**
\see release_plugins
*/
void simplifier::borrow_plugins(simplifier const & s) {
ptr_vector<plugin>::const_iterator it = s.begin_plugins();
ptr_vector<plugin>::const_iterator end = s.end_plugins();
for (; it != end; ++it)
register_plugin(*it);
}
/**
\brief Make the simplifier behave as a pre-simplifier: No AC, and plugins are marked in pre-simplification mode.
*/
void simplifier::enable_presimp() {
enable_ac_support(false);
ptr_vector<plugin>::const_iterator it = begin_plugins();
ptr_vector<plugin>::const_iterator end = end_plugins();
for (; it != end; ++it)
(*it)->enable_presimp(true);
}
/**
\brief This method should be invoked if the plugins of this simplifier were borrowed from a different simplifier.
*/
void simplifier::release_plugins() {
m_plugins.release();
}
void subst_simplifier::set_subst_map(expr_map * s) {
flush_cache();
m_subst_map = s;
}
bool subst_simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) {
if (m_subst_map && m_subst_map->contains(n)) {
expr * _r;
proof * _p = 0;
m_subst_map->get(n, _r, _p);
r = _r;
p = _p;
if (m_manager.coarse_grain_proofs())
m_subst_proofs.push_back(p);
return true;
}
return false;
}
static void push_core(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
SASSERT(pr == 0 || m.is_undef_proof(pr) || e == m.get_fact(pr));
TRACE("preprocessor",
tout << mk_pp(e, m) << "\n";
if (pr) tout << mk_ll_pp(pr, m) << "\n\n";);
if (m.is_true(e))
return;
result.push_back(e);
if (m.proofs_enabled())
result_prs.push_back(pr);
}
static void push_and(ast_manager & m, app * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
unsigned num = e->get_num_args();
TRACE("push_and", tout << mk_pp(e, m) << "\n";);
for (unsigned i = 0; i < num; i++)
push_assertion(m, e->get_arg(i), m.mk_and_elim(pr, i), result, result_prs);
}
static void push_not_or(ast_manager & m, app * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
unsigned num = e->get_num_args();
TRACE("push_not_or", tout << mk_pp(e, m) << "\n";);
for (unsigned i = 0; i < num; i++) {
expr * child = e->get_arg(i);
if (m.is_not(child)) {
expr * not_child = to_app(child)->get_arg(0);
push_assertion(m, not_child, m.mk_not_or_elim(pr, i), result, result_prs);
}
else {
expr_ref not_child(m);
not_child = m.mk_not(child);
push_assertion(m, not_child, m.mk_not_or_elim(pr, i), result, result_prs);
}
}
}
void push_assertion(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
CTRACE("push_assertion", !(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e),
tout << mk_pp(e, m) << "\n" << mk_pp(m.get_fact(pr), m) << "\n";);
SASSERT(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e);
if (m.is_and(e))
push_and(m, to_app(e), pr, result, result_prs);
else if (m.is_not(e) && m.is_or(to_app(e)->get_arg(0)))
push_not_or(m, to_app(to_app(e)->get_arg(0)), pr, result, result_prs);
else
push_core(m, e, pr, result, result_prs);
}

View file

@ -0,0 +1,232 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
simplifier.h
Abstract:
Generic expression simplifier with support for theory specific "plugins".
Author:
Leonardo (leonardo) 2008-01-03
Notes:
--*/
#ifndef _SIMPLIFIER_H_
#define _SIMPLIFIER_H_
#include"base_simplifier.h"
#include"simplifier_plugin.h"
#include"plugin_manager.h"
#include"ast_util.h"
#include"obj_hashtable.h"
/**
\brief Local simplifier.
Proof production can be enabled/disabled.
The simplifier can also apply substitutions during the
simplification. A substitution is a mapping from expression
to expression+proof, where for each entry e_1->(e_2,p) p is
a proof for (= e_1 e_2).
The simplifier can also generate coarse grain proofs. In a coarse
proof, local rewrite steps are omitted, and only the substitutions
used are tracked.
Example:
Consider the expression (+ a b), and the substitution b->(0, p)
When fine grain proofs are enabled, the simplifier will produce the
following proof
Assume the id of the proof object p is $0. Note that p is a proof for (= b 0).
$1: [reflexivity] |- (= a a)
$2: [congruence] $1 $0 |- (= (+ a b) (+ a 0))
$3: [plus-0] |- (= (+ a 0) a)
$4: [transitivity] $2 $3 |- (= (+ a b) a)
When coarse grain proofs are enabled, the simplifier produces the following
proof:
$1: [simplifier] $0 |- (= (+ a b) a)
*/
class simplifier : public base_simplifier {
protected:
typedef simplifier_plugin plugin;
plugin_manager<plugin> m_plugins;
ptr_vector<expr> m_args;
vector<rational> m_mults;
ptr_vector<expr> m_args2;
proof_ref_vector m_proofs; // auxiliary vector for implementing exhaustive simplification.
proof_ref_vector m_subst_proofs; // in coarse grain proof generation mode, this vector tracks the justification for substitutions (see method get_subst).
bool m_need_reset;
bool m_use_oeq;
bool m_visited_quantifier; //!< true, if the simplifier found a quantifier
bool m_ac_support;
expr_mark m_ac_mark;
ptr_vector<expr> m_ac_marked;
obj_map<app, app *> m_ac_cache; // temporary cache for ac
obj_map<app, proof *> m_ac_pr_cache; // temporary cache for ac
obj_map<expr, int> m_colors; // temporary cache for topological sort.
obj_map<expr, rational> m_ac_mults;
/*
Simplifier uses an idiom for rewriting ASTs without using recursive calls.
- It uses a cache (field m_cache in base_simplifier) and a todo-stack (field m_todo in base_simplifier).
- The cache is a mapping from AST to (AST + Proof). An entry [n -> (n',pr)] is used to store the fact
that n and n' are equivalent and pr is a proof for that. If proofs are disabled, then pr is 0.
We say n' is the result of the simplification of n.
Note: Some simplifications do not preserve equivalence, but equisatisfiability.
For saving space, we use pr = 0 also to represent the reflexivity proof [n -> (n, 0)].
- The simplifier can be extended using plugin (subclasses of the class simplifier_plugin).
Each theory has a family ID. All operators (func_decls) and sorts from a given theory have
the same family_id. Given an application (object of the class app), we use the method
get_family_id() to obtain the family id of the operator in this application.
The simplifier uses plugin to apply theory specific simplifications. The basic idea is:
whenever an AST with family_id X is found, invoke the plugin for this family_id.
A simplifier_plugin implements the following API:
1) bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result)
This method is invoked when the simplifier is trying to reduce/simplify an application
of the form (f args[0] ... args[num_args - 1]), and f has a family_id associated with
the plugin. The plugin may return false to indicate it could not simplify this application.
If it returns true (success), the result should be stored in the argument result.
2) bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result);
This method is a similar to the previous one, and it is used to handle associative operators.
A plugin does not need to implement this method, the default implementation will use the previous one.
The arguments mults indicates the multiplicity of every argument in args.
For example, suppose this reduce is invoked with the arguments (f, 2, [3, 2], [a, b], result).
This represents the application (f a a a b b).
Some theory simplifiers may have efficient ways to encode this multiplicity. For example,
the arithmetic solver, if f is "+", the multiplicity can be encoded using "*".
This optimization is used because some benchmarks can create term that are very huge when
flattened. One "real" example (that motivated this optimization) is:
let a1 = x1 + x1
let a2 = a1 + a1
...
let an = a{n-1} + a{n-1}
an
In this example, n was 32, so after AC flattening, we had an application
(+ x1 ... x1) with 2^32 arguments. Using the simple reduce would produce a stack overflow.
This class uses a topological sort for computing the multiplicities efficiently.
So, the field m_colors is used to implement the topological sort.
3) bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result)
This method is invoked when the sort of lhs and rhs has a family_id associated with the plugin.
This method allows theory specific simplifications such as:
(= (+ a b) b) --> (= a 0)
Assuming n1 is a reference to (+ a b) and n2 to b, the simplifier would invoke
reduce_eq(n1, n2, result)
Like reduce, false can be returned if a simplification could not be applied.
And if true is returned, then the result is stored in the argument result.
4) bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result)
It is similar to reduce_eq, but it used for theory specific simplifications for
(distinct args[0] ... args[num_args-1])
Example:
(distinct 0 1 ... n) --> true
- The idiom used in this class is implemented in the methdo reduce_core.
See reduce_core for more details. The basic idea is:
1) Get the next ast to be simplified from the todo-stack.
2) If it is already cached, then do nothing. That is, this expression was already simplified.
3) Otherwise, check whether all arguments already have been simplified (method visit_children).
3a) The arguments that have not been simplified are added to the todo-stack by visit_children.
In this case visit_children will return false.
3b) If all arguments have already been simplified, then invoke reduce1 to perform a reduction/simplification
step. The cache is updated with the result.
- After invoking reduce_core(n), the cache contains an entry [n -> (n', pr)].
*/
void flush_cache();
/**
\brief This method can be redefined in subclasses of simplifier to implement substitutions.
It returns true if n should be substituted by r, where the substitution is justified by the
proof p. The field m_subst_proofs is used to store these justifications when coarse proofs are used (PGM_COARSE).
This method is redefined in the class subst_simplifier. It is used in asserted_formulas
for implementing constant elimination. For example, if asserted_formulas contains the atoms
(= a (+ b 1)) (p a c), then the constant "a" can be eliminated. This is achieved by set (+ b 1) as
a substitution for "a".
*/
virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
void reduce_core(expr * n);
bool visit_children(expr * n);
bool visit_ac(app * n);
virtual bool visit_quantifier(quantifier * q);
void reduce1(expr * n);
void reduce1_app(app * n);
void reduce1_app_core(app * n);
void reduce1_ac_app_core(app * n);
void mk_congruent_term(app * n, app_ref & r, proof_ref & p);
void mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p);
bool get_args(app * n, ptr_vector<expr> & result, proof_ref & p);
void get_ac_args(app * n, ptr_vector<expr> & args, vector<rational> & mults);
virtual void reduce1_quantifier(quantifier * q);
void dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result);
void ac_top_sort(app * n, ptr_buffer<expr> & result);
public:
simplifier(ast_manager & manager);
virtual ~simplifier();
void enable_ac_support(bool flag);
/**
\brief Simplify the expression \c s. Store the result in \c r, and a proof that <tt>(= s r)</tt> in \c p.
*/
void operator()(expr * s, expr_ref & r, proof_ref & p);
void reset() { if (m_need_reset) { flush_cache(); m_need_reset = false; } }
bool visited_quantifier() const { return m_visited_quantifier; }
void mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & r);
void cache_result(expr * n, expr * r, proof * p) { m_need_reset = true; base_simplifier::cache_result(n, r, p); }
void register_plugin(plugin * p);
ptr_vector<plugin>::const_iterator begin_plugins() const { return m_plugins.begin(); }
ptr_vector<plugin>::const_iterator end_plugins() const { return m_plugins.end(); }
plugin * get_plugin(family_id fid) const { return m_plugins.get_plugin(fid); }
ast_manager & get_manager() { return m_manager; }
void borrow_plugins(simplifier const & s);
void release_plugins();
void enable_presimp();
};
class subst_simplifier : public simplifier {
protected:
expr_map * m_subst_map;
virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
public:
subst_simplifier(ast_manager & manager):simplifier(manager), m_subst_map(0) {}
void set_subst_map(expr_map * s);
};
void push_assertion(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs);
#endif

View file

@ -0,0 +1,46 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
simplifier_plugin.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-12-29.
Revision History:
--*/
#include"simplifier_plugin.h"
/**
\brief Copy every args[i] mult[i] times to new_args.
*/
void expand_args(unsigned num_args, rational const * mults, expr * const * args, ptr_buffer<expr> & new_args) {
for (unsigned i = 0; i < num_args; i++) {
rational const & c = mults[i];
SASSERT(c.is_int());
rational j(0);
while (j < c) {
new_args.push_back(args[i]);
j++;
}
}
}
bool simplifier_plugin::reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result) {
set_reduce_invoked();
if (f->is_idempotent()) {
return reduce(f, num_args, args, result);
}
else {
ptr_buffer<expr> new_args;
expand_args(num_args, mults, args, new_args);
return reduce(f, new_args.size(), new_args.c_ptr(), result);
}
}

View file

@ -0,0 +1,94 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
simplifier_plugin.h
Abstract:
Expression simplifier plugin interface.
Author:
Leonardo (leonardo) 2008-01-03
--*/
#ifndef __SIMPLIFIER_PLUGIN_H__
#define __SIMPLIFIER_PLUGIN_H__
#include"ast.h"
class simplifier;
void expand_args(unsigned num_args, rational const * mults, expr * const * args, ptr_buffer<expr> & new_args);
/**
\brief Abstract simplifier for the operators in a given family.
*/
class simplifier_plugin {
protected:
ast_manager & m_manager;
family_id m_fid;
bool m_presimp; // true if simplifier is performing pre-simplification...
bool m_reduce_invoked; // true if one of the reduce methods were invoked.
void set_reduce_invoked() { m_reduce_invoked = true; }
public:
simplifier_plugin(symbol const & fname, ast_manager & m):m_manager(m), m_fid(m.get_family_id(fname)), m_presimp(false), m_reduce_invoked(false) {}
bool reduce_invoked() const { return m_reduce_invoked; }
virtual ~simplifier_plugin() {}
virtual simplifier_plugin * mk_fresh() {
UNREACHABLE();
return 0;
}
/**
\brief Return in \c result an expression \c e equivalent to <tt>(f args[0] ... args[num_args - 1])</tt>.
Return true if succeeded.
*/
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; }
/**
\brief Return in \c result an expression \c e equivalent to <tt>(f args[0] ... args[0] ... args[num_args - 1])</tt>.
Where each args[i] occurs mults[i] times.
Return true if succeeded.
*/
virtual bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result);
/**
\brief Return in \c result an expression \c e equivalent to <tt>(= lhs rhs)</tt>.
Return true if succeeded.
*/
virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { set_reduce_invoked(); return false; }
/**
\brief Return in \c result an expression \c e equivalent to <tt>(distinct args[0] ... args[num_args-1])</tt>.
Return true if succeeded.
*/
virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; }
family_id get_family_id() const { return m_fid; }
/**
\brief Simplifiers may maintain local caches. These caches must be flushed when this method is invoked.
*/
virtual void flush_caches() { /* do nothing */ }
ast_manager & get_manager() { return m_manager; }
void enable_presimp(bool flag) { m_presimp = flag; }
virtual void enable_ac_support(bool flag) {}
};
#endif

View file

@ -0,0 +1,76 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array_params.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-01.
Revision History:
--*/
#ifndef _THEORY_ARRAY_PARAMS_H_
#define _THEORY_ARRAY_PARAMS_H_
#include"ini_file.h"
enum array_solver_id {
AR_NO_ARRAY,
AR_SIMPLE,
AR_MODEL_BASED,
AR_FULL
};
struct theory_array_params {
array_solver_id m_array_mode;
bool m_array_weak;
bool m_array_extensional;
unsigned m_array_laziness;
bool m_array_delay_exp_axiom;
bool m_array_cg;
bool m_array_always_prop_upward;
bool m_array_lazy_ieq;
unsigned m_array_lazy_ieq_delay;
bool m_array_canonize_simplify;
bool m_array_simplify; // temporary hack for disabling array simplifier plugin.
theory_array_params():
m_array_mode(AR_FULL),
m_array_weak(false),
m_array_extensional(true),
m_array_laziness(1),
m_array_delay_exp_axiom(true),
m_array_cg(false),
m_array_always_prop_upward(true), // UPWARDs filter is broken... TODO: fix it
m_array_lazy_ieq(false),
m_array_lazy_ieq_delay(10),
m_array_canonize_simplify(false),
m_array_simplify(true) {
}
void register_params(ini_params & p) {
p.register_int_param("ARRAY_SOLVER", 0, 3, reinterpret_cast<int&>(m_array_mode), "0 - no array, 1 - simple, 2 - model based, 3 - full");
p.register_bool_param("ARRAY_WEAK", m_array_weak);
p.register_bool_param("ARRAY_EXTENSIONAL", m_array_extensional);
p.register_unsigned_param("ARRAY_LAZINESS", m_array_laziness);
p.register_bool_param("ARRAY_DELAY_EXP_AXIOM", m_array_delay_exp_axiom);
p.register_bool_param("ARRAY_CG", m_array_cg);
p.register_bool_param("ARRAY_ALWAYS_PROP_UPWARD", m_array_always_prop_upward,
"Disable the built-in filter upwards propagation");
p.register_bool_param("ARRAY_LAZY_IEQ", m_array_lazy_ieq);
p.register_unsigned_param("ARRAY_LAZY_IEQ_DELAY", m_array_lazy_ieq_delay);
p.register_bool_param("ARRAY_CANONIZE", m_array_canonize_simplify,
"Normalize arrays into normal form during simplification");
}
};
#endif /* _THEORY_ARRAY_PARAMS_H_ */