3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-22 19:17:53 +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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,183 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
arith_rewriter.h
Abstract:
Basic rewriting rules for arithmetic
Author:
Leonardo (leonardo) 2011-04-10
Notes:
--*/
#ifndef _ARITH_REWRITER_H_
#define _ARITH_REWRITER_H_
#include"poly_rewriter.h"
#include"arith_decl_plugin.h"
class arith_rewriter_core {
protected:
typedef rational numeral;
arith_util m_util;
bool m_expand_power;
bool m_mul2power;
bool m_expand_tan;
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
bool is_numeral(expr * n, numeral & r) const { return m_util.is_numeral(n, r); }
bool is_zero(expr * n) const { return m_util.is_zero(n); }
bool is_minus_one(expr * n) const { return m_util.is_minus_one(n); }
void normalize(numeral & c, sort * s) {}
app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); }
decl_kind add_decl_kind() const { return OP_ADD; }
decl_kind mul_decl_kind() const { return OP_MUL; }
bool use_power() const { return m_mul2power && !m_expand_power; }
decl_kind power_decl_kind() const { return OP_POWER; }
public:
arith_rewriter_core(ast_manager & m):m_util(m) {}
};
class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
bool m_arith_lhs;
bool m_gcd_rounding;
bool m_eq2ineq;
bool m_elim_to_real;
bool m_push_to_real;
bool m_anum_simp;
bool m_elim_rem;
unsigned m_max_degree;
void get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts);
enum const_treatment { CT_FLOOR, CT_CEIL, CT_FALSE };
bool div_polynomial(expr * t, numeral const & g, const_treatment ct, expr_ref & result);
enum op_kind { LE, GE, EQ };
static op_kind inv(op_kind k) { return k == LE ? GE : (k == GE ? LE : EQ); }
bool is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result);
br_status mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result);
bool elim_to_real_var(expr * var, expr_ref & new_var);
bool elim_to_real_mon(expr * monomial, expr_ref & new_monomial);
bool elim_to_real_pol(expr * p, expr_ref & new_p);
bool elim_to_real(expr * arg1, expr * arg2, expr_ref & new_arg1, expr_ref & new_arg2);
void updt_local_params(params_ref const & p);
bool is_anum_simp_target(unsigned num_args, expr * const * args);
br_status mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_div_irrat_irrat(expr * arg1, expr * arg2, expr_ref & result);
bool is_reduce_power_target(expr * arg, bool is_eq);
expr * reduce_power(expr * arg, bool is_eq);
br_status reduce_power(expr * arg1, expr * arg2, op_kind kind, expr_ref & result);
bool is_pi_multiple(expr * t, rational & k);
bool is_pi_offset(expr * t, rational & k, expr * & m);
bool is_2_pi_integer(expr * t);
bool is_2_pi_integer_offset(expr * t, expr * & m);
bool is_pi_integer(expr * t);
bool is_pi_integer_offset(expr * t, expr * & m);
expr * mk_sin_value(rational const & k);
app * mk_sqrt(rational const & k);
public:
arith_rewriter(ast_manager & m, params_ref const & p = params_ref()):
poly_rewriter<arith_rewriter_core>(m, p) {
updt_local_params(p);
}
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
result = m().mk_app(f, num_args, args);
}
br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_le_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_lt_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_ge_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_gt_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result);
void mk_eq(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_eq_core(arg1, arg2, result) == BR_FAILED)
result = m_util.mk_le(arg1, arg2);
}
void mk_le(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_le_core(arg1, arg2, result) == BR_FAILED)
result = m_util.mk_le(arg1, arg2);
}
void mk_lt(expr * arg1, expr * arg2, expr_ref & result) { mk_lt_core(arg1, arg2, result); }
void mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_ge_core(arg1, arg2, result) == BR_FAILED)
result = m_util.mk_ge(arg1, arg2);
}
void mk_gt(expr * arg1, expr * arg2, expr_ref & result) { mk_gt_core(arg1, arg2, result); }
br_status mk_div_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_idiv_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_mod_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_rem_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_power_core(expr* arg1, expr* arg2, expr_ref & result);
void mk_div(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_div_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_DIV, arg1, arg2);
}
void mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_idiv_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_IDIV, arg1, arg2);
}
void mk_mod(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_mod_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_MOD, arg1, arg2);
}
void mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_rem_core(arg1, arg2, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_REM, arg1, arg2);
}
br_status mk_to_int_core(expr * arg, expr_ref & result);
br_status mk_to_real_core(expr * arg, expr_ref & result);
void mk_to_int(expr * arg, expr_ref & result) {
if (mk_to_int_core(arg, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_TO_INT, 1, &arg);
}
void mk_to_real(expr * arg, expr_ref & result) {
if (mk_to_real_core(arg, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_TO_REAL, 1, &arg);
}
br_status mk_is_int(expr * arg, expr_ref & result);
void set_cancel(bool f);
br_status mk_sin_core(expr * arg, expr_ref & result);
br_status mk_cos_core(expr * arg, expr_ref & result);
br_status mk_tan_core(expr * arg, expr_ref & result);
br_status mk_asin_core(expr * arg, expr_ref & result);
br_status mk_acos_core(expr * arg, expr_ref & result);
br_status mk_atan_core(expr * arg, expr_ref & result);
br_status mk_sinh_core(expr * arg, expr_ref & result);
br_status mk_cosh_core(expr * arg, expr_ref & result);
br_status mk_tanh_core(expr * arg, expr_ref & result);
};
#endif

View file

@ -0,0 +1,359 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
array_rewriter.cpp
Abstract:
Basic rewriting rules for Arrays.
Author:
Leonardo (leonardo) 2011-04-06
Notes:
--*/
#include"array_rewriter.h"
#include"ast_lt.h"
#include"ast_pp.h"
void array_rewriter::updt_params(params_ref const & p) {
m_sort_store = p.get_bool(":sort-store", false);
m_expand_select_store = p.get_bool(":expand-select-store", false);
}
void array_rewriter::get_param_descrs(param_descrs & r) {
r.insert(":expand-select-store", CPK_BOOL, "(default: false) replace a (select (store ...) ...) term by an if-then-else term.");
r.insert(":sort-store", CPK_BOOL, "(default: false) sort nested stores when the indices are known to be different.");
}
br_status array_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(f->get_family_id() == get_fid());
TRACE("array_rewriter", tout << mk_pp(f, m()) << "\n";
for (unsigned i = 0; i < num_args; ++i) {
tout << mk_pp(args[i], m()) << "\n";
});
switch (f->get_decl_kind()) {
case OP_SELECT:
return mk_select_core(num_args, args, result);
case OP_STORE:
return mk_store_core(num_args, args, result);
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()));
return mk_map_core(to_func_decl(f->get_parameter(0).get_ast()), num_args, args, result);
case OP_SET_UNION:
return mk_set_union(num_args, args, result);
case OP_SET_INTERSECT:
return mk_set_intersect(num_args, args, result);
case OP_SET_SUBSET:
SASSERT(num_args == 2);
return mk_set_subset(args[0], args[1], result);
case OP_SET_COMPLEMENT:
SASSERT(num_args == 1);
return mk_set_complement(args[0], result);
case OP_SET_DIFFERENCE:
SASSERT(num_args == 2);
return mk_set_difference(args[0], args[1], result);
default:
return BR_FAILED;
}
}
// l_true -- all equal
// l_false -- at least one disequal
// l_undef -- don't know
template<bool CHECK_DISEQ>
lbool array_rewriter::compare_args(unsigned num_args, expr * const * args1, expr * const * args2) {
for (unsigned i = 0; i < num_args; i++) {
if (args1[i] == args2[i])
continue;
if (CHECK_DISEQ && m().are_distinct(args1[i], args2[i]))
return l_false;
return l_undef;
}
return l_true;
}
br_status array_rewriter::mk_store_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 3);
if (m_util.is_store(args[0])) {
lbool r = m_sort_store ?
compare_args<true>(num_args - 2, args + 1, to_app(args[0])->get_args() + 1) :
compare_args<false>(num_args - 2, args + 1, to_app(args[0])->get_args() + 1);
switch (r) {
case l_true: {
//
// store(store(a,i,v),i,w) --> store(a,i,w)
//
ptr_buffer<expr> new_args;
new_args.push_back(to_app(args[0])->get_arg(0));
new_args.append(num_args-1, args+1);
SASSERT(new_args.size() == num_args);
result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr());
return BR_DONE;
}
case l_false:
SASSERT(m_sort_store);
//
// store(store(a,i,v),j,w) -> store(store(a,j,w),i,v)
// if i, j are different, lt(i,j)
//
if (lex_lt(num_args-2, args+1, to_app(args[0])->get_args() + 1)) {
ptr_buffer<expr> new_args;
new_args.push_back(to_app(args[0])->get_arg(0));
new_args.append(num_args-1, args+1);
expr * nested_store = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr());
new_args.reset();
new_args.push_back(nested_store);
new_args.append(num_args - 1, to_app(args[0])->get_args() + 1);
result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr());
return BR_REWRITE2;
}
break;
case l_undef:
break;
}
}
//
// store(const(v),i,v) --> const(v)
//
if (m_util.is_const(args[0]) &&
to_app(args[0])->get_arg(0) == args[num_args-1]) {
result = args[0];
return BR_DONE;
}
expr * v = args[num_args-1];
//
// store(a, i, select(a, i)) --> a
//
if (m_util.is_select(v) &&
compare_args<false>(num_args-1, args, to_app(v)->get_args())) {
result = args[0];
return BR_DONE;
}
return BR_FAILED;
}
br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 2);
if (m_util.is_store(args[0])) {
SASSERT(to_app(args[0])->get_num_args() == num_args+1);
switch (compare_args<true>(num_args - 1, args+1, to_app(args[0])->get_args()+1)) {
case l_true:
// select(store(a, I, v), I) --> v
result = to_app(args[0])->get_arg(num_args);
return BR_DONE;
case l_false: {
// select(store(a, I, v), J) --> select(a, J) if I != J
ptr_buffer<expr> new_args;
new_args.push_back(to_app(args[0])->get_arg(0));
new_args.append(num_args-1, args+1);
result = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr());
return BR_REWRITE1;
}
default:
if (m_expand_select_store) {
// select(store(a, I, v), J) --> ite(I=J, v, select(a, J))
ptr_buffer<expr> new_args;
new_args.push_back(to_app(args[0])->get_arg(0));
new_args.append(num_args-1, args+1);
expr * sel_a_j = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr());
expr * v = to_app(args[0])->get_arg(num_args);
ptr_buffer<expr> eqs;
unsigned num_indices = num_args-1;
for (unsigned i = 0; i < num_indices; i++) {
eqs.push_back(m().mk_eq(to_app(args[0])->get_arg(i+1), args[i+1]));
}
if (num_indices == 1) {
result = m().mk_ite(eqs[0], v, sel_a_j);
return BR_REWRITE2;
}
else {
result = m().mk_ite(m().mk_and(eqs.size(), eqs.c_ptr()), v, sel_a_j);
return BR_REWRITE3;
}
}
return BR_FAILED;
}
}
if (m_util.is_const(args[0])) {
// select(const(v), I) --> v
result = to_app(args[0])->get_arg(0);
return BR_DONE;
}
if (m_util.is_as_array(args[0])) {
// select(as-array[f], I) --> f(I)
func_decl * f = m_util.get_as_array_func_decl(to_app(args[0]));
result = m().mk_app(f, num_args - 1, args + 1);
return BR_REWRITE1;
}
return BR_FAILED;
}
br_status array_rewriter::mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 0);
bool is_store0 = m_util.is_store(args[0]);
bool is_const0 = m_util.is_const(args[0]);
if (num_args == 1) {
//
// map_f (store a j v) = (store (map_f a) j (f v))
//
if (is_store0) {
app * store_expr = to_app(args[0]);
unsigned num_args = store_expr->get_num_args();
SASSERT(num_args >= 3);
expr * a = store_expr->get_arg(0);
expr * v = store_expr->get_arg(num_args-1);
ptr_buffer<expr> new_args;
new_args.push_back(m_util.mk_map(f, 1, &a)); // (map_f a)
new_args.append(num_args - 2, store_expr->get_args() + 1); // j
new_args.push_back(m().mk_app(f, v)); // (f v)
result = m().mk_app(get_fid(), OP_STORE, new_args.size(), new_args.c_ptr());
return BR_REWRITE2;
}
//
// map_f (const v) = (const (f v))
//
if (is_const0) {
expr * fv = m().mk_app(f, to_app(args[0])->get_arg(0));
result = m_util.mk_const_array(m().get_sort(args[0]), fv);
return BR_REWRITE2;
}
return BR_FAILED;
}
SASSERT(num_args > 1);
if (is_store0) {
unsigned num_indices = to_app(args[0])->get_num_args() - 2;
unsigned i;
for (i = 1; i < num_args; i++) {
if (!m_util.is_store(args[i]))
break;
unsigned j;
for (j = 1; j < num_indices+1; j++) {
if (to_app(args[0])->get_arg(j) != to_app(args[i])->get_arg(j))
break;
}
if (j < num_indices+1)
break;
}
//
// map_f (store a_1 j v_1) ... (store a_n j v_n) --> (store (map_f a_1 ... a_n) j (f v_1 ... v_n))
//
if (i == num_args) {
ptr_buffer<expr> arrays;
ptr_buffer<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));
}
ptr_buffer<expr> new_args;
new_args.push_back(m_util.mk_map(f, arrays.size(), arrays.c_ptr()));
new_args.append(num_indices, to_app(args[0])->get_args() + 1);
new_args.push_back(m().mk_app(f, values.size(), values.c_ptr()));
result = m().mk_app(get_fid(), OP_STORE, new_args.size(), new_args.c_ptr());
return BR_REWRITE2;
}
return BR_FAILED;
}
if (is_const0) {
unsigned i;
for (i = 1; i < num_args; i++) {
if (!m_util.is_const(args[i]))
break;
}
if (i == num_args) {
//
// map_f (const v_1) ... (const v_n) = (const (f v_1 ... v_n))
//
ptr_buffer<expr> values;
for (unsigned i = 0; i < num_args; i++) {
values.push_back(to_app(args[i])->get_arg(0));
}
expr * fv = m().mk_app(f, values.size(), values.c_ptr());
parameter p(m().get_sort(args[0]));
result = m().mk_app(get_fid(), OP_CONST_ARRAY, 1, &p, 1, &fv);
return BR_REWRITE2;
}
return BR_FAILED;
}
return BR_FAILED;
}
void array_rewriter::mk_store(unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_store_core(num_args, args, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_STORE, num_args, args);
}
void array_rewriter::mk_select(unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_select_core(num_args, args, result) == BR_FAILED)
result = m().mk_app(get_fid(), OP_SELECT, num_args, args);
}
void array_rewriter::mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_map_core(f, num_args, args, result) == BR_FAILED)
result = m_util.mk_map(f, num_args, args);
}
br_status array_rewriter::mk_set_union(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
if (num_args == 1) {
result = args[0];
return BR_DONE;
}
SASSERT(num_args >= 2);
br_status r = unsigned2br_status(num_args - 2);
result = m_util.mk_map(m().mk_or_decl(), num_args, args);
return r;
}
br_status array_rewriter::mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
if (num_args == 1) {
result = args[0];
return BR_DONE;
}
SASSERT(num_args >= 2);
br_status r = unsigned2br_status(num_args - 2);
result = m_util.mk_map(m().mk_and_decl(), num_args, args);
return r;
}
br_status array_rewriter::mk_set_complement(expr * arg, expr_ref & result) {
return mk_map_core(m().mk_not_decl(), 1, &arg, result);
}
br_status array_rewriter::mk_set_difference(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, m_util.mk_map(m().mk_not_decl(), 1, &arg2) };
result = m_util.mk_map(m().mk_and_decl(), 2, args);
return BR_REWRITE2;
}
br_status array_rewriter::mk_set_subset(expr * arg1, expr * arg2, expr_ref & result) {
mk_set_difference(arg1, arg2, result);
result = m().mk_eq(result.get(), m_util.mk_empty_set(m().get_sort(arg1)));
return BR_REWRITE3;
}

View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
array_rewriter.h
Abstract:
Basic rewriting rules for Arrays.
Author:
Leonardo (leonardo) 2011-04-06
Notes:
--*/
#ifndef _ARRAY_REWRITER_H_
#define _ARRAY_REWRITER_H_
#include"array_decl_plugin.h"
#include"rewriter_types.h"
#include"lbool.h"
#include"params.h"
/**
\brief Cheap rewrite rules for Arrays
*/
class array_rewriter {
array_util m_util;
bool m_sort_store;
bool m_expand_select_store;
template<bool CHECK_DISEQ>
lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2);
public:
array_rewriter(ast_manager & m, params_ref const & p = params_ref()):
m_util(m) {
updt_params(p);
}
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_store(unsigned num_args, expr * const * args, expr_ref & result);
void mk_select(unsigned num_args, expr * const * args, expr_ref & result);
void mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
// The following methods never return BR_FAILED
br_status mk_set_union(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_set_complement(expr * arg, expr_ref & result);
br_status mk_set_difference(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_set_subset(expr * arg1, expr * arg2, expr_ref & result);
};
#endif

View file

@ -0,0 +1,989 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bool_rewriter.h
Abstract:
Basic rewrites for Boolean operators.
Author:
Leonardo (leonardo) 2011-04-04
Notes:
--*/
#include"bool_rewriter.h"
#include"rewriter_def.h"
void bool_rewriter::updt_params(params_ref const & p) {
m_flat = p.get_bool(":flat", true);
m_elim_and = p.get_bool(":elim-and", false);
m_local_ctx = p.get_bool(":local-ctx", false);
m_local_ctx_limit = p.get_uint(":local-ctx-limit", UINT_MAX);
m_blast_distinct = p.get_bool(":blast-distinct", false);
m_ite_extra_rules = p.get_bool(":ite-extra-rules", false);
}
void bool_rewriter::get_param_descrs(param_descrs & r) {
r.insert(":ite-extra-rules", CPK_BOOL, "(default: false) extra ite simplifications, these additional simplifications may reduce size locally but increase globally.");
r.insert(":flat", CPK_BOOL, "(default: true) create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor.");
r.insert(":elim-and", CPK_BOOL, "(default: false) conjunctions are rewritten using negation and disjunctions.");
r.insert(":local-ctx", CPK_BOOL, "(default: false) perform local (i.e., cheap) context simplifications.");
r.insert(":local-ctx-limit", CPK_UINT, "(default: inf) limit for applying local context simplifier.");
r.insert(":blast-distinct", CPK_BOOL, "(default: false) expand a distinct predicate into a quadratic number of disequalities.");
}
br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(f->get_family_id() == m().get_basic_family_id());
switch (f->get_decl_kind()) {
case OP_EQ:
case OP_IFF:
SASSERT(num_args == 2);
return mk_eq_core(args[0], args[1], result);
case OP_DISTINCT:
return mk_distinct_core(num_args, args, result);
case OP_AND:
return mk_and_core(num_args, args, result);
case OP_OR:
return mk_or_core(num_args, args, result);
case OP_NOT:
SASSERT(num_args == 1);
return mk_not_core(args[0], result);
case OP_ITE:
SASSERT(num_args == 3);
return mk_ite_core(args[0], args[1], args[2], result);
case OP_IMPLIES:
SASSERT(num_args == 2);
mk_implies(args[0], args[1], result);
return BR_DONE;
case OP_XOR:
SASSERT(num_args == 2);
mk_xor(args[0], args[1], result);
return BR_DONE;
default:
return BR_FAILED;
}
}
void bool_rewriter::mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref_buffer new_args(m());
for (unsigned i = 0; i < num_args; i++) {
expr_ref tmp(m());
mk_not(args[i], tmp);
new_args.push_back(tmp);
}
expr_ref tmp(m());
mk_or(new_args.size(), new_args.c_ptr(), tmp);
mk_not(tmp, result);
}
br_status bool_rewriter::mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
bool s = false;
ptr_buffer<expr> buffer;
expr_fast_mark1 neg_lits;
expr_fast_mark2 pos_lits;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (m().is_true(arg)) {
s = true;
continue;
}
if (m().is_false(arg)) {
result = m().mk_false();
return BR_DONE;
}
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
s = true;
continue;
}
if (pos_lits.is_marked(atom)) {
result = m().mk_false();
return BR_DONE;
}
neg_lits.mark(atom);
}
else {
if (pos_lits.is_marked(arg)) {
s = true;
continue;
}
if (neg_lits.is_marked(arg)) {
result = m().mk_false();
return BR_DONE;
}
pos_lits.mark(arg);
}
buffer.push_back(arg);
}
unsigned sz = buffer.size();
switch(sz) {
case 0:
result = m().mk_true();
return BR_DONE;
case 1:
result = buffer.back();
return BR_DONE;
default:
if (s) {
result = m().mk_and(sz, buffer.c_ptr());
return BR_DONE;
}
return BR_FAILED;
}
}
br_status bool_rewriter::mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
unsigned i;
for (i = 0; i < num_args; i++) {
if (m().is_and(args[i]))
break;
}
if (i < num_args) {
// has nested ANDs
ptr_buffer<expr> flat_args;
flat_args.append(i, args);
for (; i < num_args; i++) {
expr * arg = args[i];
// Remark: all rewrites are depth 1.
if (m().is_and(arg)) {
unsigned num = to_app(arg)->get_num_args();
for (unsigned j = 0; j < num; j++)
flat_args.push_back(to_app(arg)->get_arg(j));
}
else {
flat_args.push_back(arg);
}
}
if (mk_nflat_and_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED)
result = m().mk_and(flat_args.size(), flat_args.c_ptr());
return BR_DONE;
}
return mk_nflat_and_core(num_args, args, result);
}
br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
bool s = false;
ptr_buffer<expr> buffer;
expr_fast_mark1 neg_lits;
expr_fast_mark2 pos_lits;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (m().is_true(arg)) {
result = m().mk_true();
return BR_DONE;
}
if (m().is_false(arg)) {
s = true;
continue;
}
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
s = true;
continue;
}
if (pos_lits.is_marked(atom)) {
result = m().mk_true();
return BR_DONE;
}
neg_lits.mark(atom);
}
else {
if (pos_lits.is_marked(arg)) {
s = true;
continue;
}
if (neg_lits.is_marked(arg)) {
result = m().mk_true();
return BR_DONE;
}
pos_lits.mark(arg);
}
buffer.push_back(arg);
}
unsigned sz = buffer.size();
switch(sz) {
case 0:
result = m().mk_false();
return BR_DONE;
case 1:
result = buffer.back();
return BR_DONE;
default:
if (m_local_ctx && m_local_ctx_cost <= m_local_ctx_limit) {
neg_lits.reset();
pos_lits.reset();
if (local_ctx_simp(sz, buffer.c_ptr(), result))
return BR_DONE;
}
if (s) {
result = m().mk_or(sz, buffer.c_ptr());
return BR_DONE;
}
return BR_FAILED;
}
}
br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
unsigned i;
for (i = 0; i < num_args; i++) {
if (m().is_or(args[i]))
break;
}
if (i < num_args) {
// has nested ORs
ptr_buffer<expr> flat_args;
flat_args.append(i, args);
for (; i < num_args; i++) {
expr * arg = args[i];
// Remark: all rewrites are depth 1.
if (m().is_or(arg)) {
unsigned num = to_app(arg)->get_num_args();
for (unsigned j = 0; j < num; j++)
flat_args.push_back(to_app(arg)->get_arg(j));
}
else {
flat_args.push_back(arg);
}
}
if (mk_nflat_or_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED)
result = m().mk_or(flat_args.size(), flat_args.c_ptr());
return BR_DONE;
}
return mk_nflat_or_core(num_args, args, result);
}
expr * bool_rewriter::mk_or_app(unsigned num_args, expr * const * args) {
switch(num_args) {
case 0: return m().mk_false();
case 1: return args[0];
default: return m().mk_or(num_args, args);
}
}
/**
\brief Auxiliary method for local_ctx_simp.
Replace args[i] by true if marked in neg_lits.
Replace args[i] by false if marked in pos_lits.
*/
bool bool_rewriter::simp_nested_not_or(unsigned num_args, expr * const * args,
expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) {
ptr_buffer<expr> new_args;
bool simp = false;
m_local_ctx_cost += num_args;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (neg_lits.is_marked(arg)) {
result = m().mk_false();
return true;
}
if (pos_lits.is_marked(arg)) {
simp = true;
continue;
}
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
simp = true;
continue;
}
if (pos_lits.is_marked(atom)) {
result = m().mk_false();
return true;
}
}
new_args.push_back(arg);
}
if (simp) {
switch(new_args.size()) {
case 0:
result = m().mk_true();
return true;
case 1:
mk_not(new_args[0], result);
return true;
default:
result = m().mk_not(m().mk_or(new_args.size(), new_args.c_ptr()));
return true;
}
}
return false;
}
expr * bool_rewriter::simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified) {
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
modified = true;
return m().mk_false();
}
if (pos_lits.is_marked(atom)) {
modified = true;
return m().mk_true();
}
return arg;
}
else {
if (neg_lits.is_marked(arg)) {
modified = true;
return m().mk_true();
}
if (pos_lits.is_marked(arg)) {
modified = true;
return m().mk_false();
}
return arg;
}
}
/**
\brief Simpler version of mk_ite, that will not invoke mk_or/mk_and.
It is used byt local_ctx_simp to prevent a recursive call to local_ctx_simp.
See comment at simp_nested_eq_ite.
*/
void bool_rewriter::mk_nested_ite(expr * c, expr * t, expr * e, expr_ref & result) {
if (m().is_true(c)) {
result = t;
return;
}
if (m().is_false(c)) {
result = e;
return;
}
if (t == e) {
result = t;
return;
}
if (m().is_bool(t)) {
if (m().is_true(t)) {
if (m().is_false(e)) {
result = c;
return;
}
result = m().mk_or(c, e);
return;
}
if (m().is_false(t)) {
if (m().is_true(e)) {
mk_not(c, result);
return;
}
expr_ref tmp(m());
mk_not(e, tmp);
result = m().mk_not(m().mk_or(c, tmp));
return;
}
if (m().is_true(e)) {
expr_ref tmp(m());
mk_not(c, tmp);
result = m().mk_or(tmp, t);
return;
}
if (m().is_false(e) || c == e) {
expr_ref tmp1(m());
expr_ref tmp2(m());
mk_not(c, tmp1);
mk_not(t, tmp2);
result = m().mk_not(m().mk_or(tmp1, tmp2));
return;
}
if (c == t) {
result = m().mk_or(c, e);
return;
}
if (m().is_complement_core(t, e)) { // t = not(e)
mk_eq(c, t, result);
return;
}
if (m().is_complement_core(e, t)) { // e = not(t)
mk_eq(c, t, result);
return;
}
}
result = m().mk_ite(c, t, e);
}
bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) {
bool neg = false;
m_local_ctx_cost += 3;
if (m().is_not(t)) {
neg = true;
t = to_app(t)->get_arg(0);
}
if (m().is_iff(t) || m().is_eq(t)) {
bool modified = false;
expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified);
expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified);
if (!modified)
return false;
mk_eq(new_lhs, new_rhs, result);
if (neg)
mk_not(result, result);
return true;
}
if (m().is_ite(t)) {
bool modified = false;
expr * new_c = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified);
expr * new_t = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified);
expr * new_e = simp_arg(to_app(t)->get_arg(2), neg_lits, pos_lits, modified);
if (!modified)
return false;
// It is not safe to invoke mk_ite here, since it can recursively call
// local_ctx_simp by
// - transforming the ITE into an OR
// - and invoked mk_or, that will invoke local_ctx_simp
// mk_ite(new_c, new_t, new_e, result);
mk_nested_ite(new_c, new_t, new_e, result);
if (neg)
mk_not(result, result);
return true;
}
return false;
}
/**
\brief Apply local context simplification at (OR args[0] ... args[num_args-1])
Basic idea:
- Replace args[i] by false in the other arguments
- If args[i] is of the form (not t), then replace t by true in the other arguments.
To make sure the simplification is efficient we bound the depth.
*/
bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref_vector old_args(m());
expr_ref_vector new_args(m());
expr_ref new_arg(m());
expr_fast_mark1 neg_lits;
expr_fast_mark2 pos_lits;
bool simp = false;
bool modified = false;
bool forward = true;
unsigned rounds = 0;
while (true) {
rounds++;
#if 0
if (rounds > 10)
verbose_stream() << "rounds: " << rounds << "\n";
#endif
#define PUSH_NEW_ARG(ARG) { \
new_args.push_back(ARG); \
if (m().is_not(ARG)) \
neg_lits.mark(to_app(ARG)->get_arg(0)); \
else \
pos_lits.mark(ARG); \
}
#define PROCESS_ARG() \
{ \
expr * arg = args[i]; \
if (m().is_not(arg) && m().is_or(to_app(arg)->get_arg(0)) && \
simp_nested_not_or(to_app(to_app(arg)->get_arg(0))->get_num_args(), \
to_app(to_app(arg)->get_arg(0))->get_args(), \
neg_lits, \
pos_lits, \
new_arg)) { \
modified = true; simp = true; \
arg = new_arg; \
} \
if (simp_nested_eq_ite(arg, neg_lits, pos_lits, new_arg)) { \
modified = true; simp = true; \
arg = new_arg; \
} \
if (m().is_false(arg)) \
continue; \
if (m().is_true(arg)) { \
result = arg; \
return true; \
} \
if (m_flat && m().is_or(arg)) { \
unsigned sz = to_app(arg)->get_num_args(); \
for (unsigned j = 0; j < sz; j++) { \
expr * arg_arg = to_app(arg)->get_arg(j); \
PUSH_NEW_ARG(arg_arg); \
} \
} \
else { \
PUSH_NEW_ARG(arg); \
} \
}
m_local_ctx_cost += 2*num_args;
#if 0
static unsigned counter = 0;
counter++;
if (counter % 10000 == 0)
verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << "\n";
#endif
if (forward) {
for (unsigned i = 0; i < num_args; i++) {
PROCESS_ARG();
}
forward = false;
}
else {
unsigned i = num_args;
while (i > 0) {
--i;
PROCESS_ARG();
}
if (!modified) {
if (simp) {
result = mk_or_app(num_args, args);
return true;
}
return false; // didn't simplify
}
// preserve the original order...
std::reverse(new_args.c_ptr(), new_args.c_ptr() + new_args.size());
modified = false;
forward = true;
}
pos_lits.reset();
neg_lits.reset();
old_args.reset();
old_args.swap(new_args);
SASSERT(new_args.empty());
args = old_args.c_ptr();
num_args = old_args.size();
}
}
/**
\brief Apply simplification if ite is an if-then-else tree where every leaf is a value.
This is an efficient way to
*/
br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) {
SASSERT(m().is_ite(ite));
SASSERT(m().is_value(val));
expr * t = ite->get_arg(1);
expr * e = ite->get_arg(2);
if (!m().is_value(t) || !m().is_value(e))
return BR_FAILED;
if (t != val && e != val) {
TRACE("try_ite_value", tout << mk_ismt2_pp(t, m()) << " " << mk_ismt2_pp(e, m()) << " " << mk_ismt2_pp(val, m()) << "\n";
tout << t << " " << e << " " << val << "\n";);
result = m().mk_false();
return BR_DONE;
}
if (t == val && e == val) {
result = m().mk_true();
return BR_DONE;
}
if (t == val) {
result = ite->get_arg(0);
return BR_DONE;
}
SASSERT(e == val);
mk_not(ite->get_arg(0), result);
return BR_DONE;
}
#if 0
// Return true if ite is an if-then-else tree where the leaves are values,
// and they are all different from val
static bool is_ite_value_tree_neq_value(ast_manager & m, app * ite, app * val) {
SASSERT(m.is_ite(ite));
SASSERT(m.is_value(val));
expr_fast_mark1 visited;
ptr_buffer<app> todo;
todo.push_back(ite);
#define VISIT(ARG) { \
if (m.is_value(ARG)) { \
if (ARG == val) \
return false; \
} \
else if (m.is_ite(ARG)) { \
if (!visited.is_marked(ARG)) { \
visited.mark(ARG); \
todo.push_back(to_app(ARG)); \
} \
} \
else { \
return false; \
} \
}
while (!todo.empty()) {
app * ite = todo.back();
todo.pop_back();
SASSERT(m.is_ite(ite));
expr * t = ite->get_arg(1);
expr * e = ite->get_arg(2);
VISIT(t);
VISIT(e);
}
return true;
}
#endif
br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
if (lhs == rhs) {
result = m().mk_true();
return BR_DONE;
}
if (m().are_distinct(lhs, rhs)) {
result = m().mk_false();
return BR_DONE;
}
br_status r = BR_FAILED;
if (m().is_ite(lhs) && m().is_value(rhs)) {
// if (is_ite_value_tree_neq_value(m(), to_app(lhs), to_app(rhs))) {
// result = m().mk_false();
// return BR_DONE;
// }
r = try_ite_value(to_app(lhs), to_app(rhs), result);
CTRACE("try_ite_value", r != BR_FAILED,
tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";);
}
else if (m().is_ite(rhs) && m().is_value(lhs)) {
// if (is_ite_value_tree_neq_value(m(), to_app(rhs), to_app(lhs))) {
// result = m().mk_false();
// return BR_DONE;
// }
r = try_ite_value(to_app(rhs), to_app(lhs), result);
CTRACE("try_ite_value", r != BR_FAILED,
tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";);
}
if (r != BR_FAILED)
return r;
if (m().is_bool(lhs)) {
bool unfolded = false;
if (m().is_not(lhs) && m().is_not(rhs)) {
lhs = to_app(lhs)->get_arg(0);
rhs = to_app(rhs)->get_arg(0);
unfolded = true;
}
if (m().is_true(lhs)) {
result = rhs;
return BR_DONE;
}
if (m().is_false(lhs)) {
mk_not(rhs, result);
return BR_DONE;
}
if (m().is_true(rhs)) {
result = lhs;
return BR_DONE;
}
if (m().is_false(rhs)) {
mk_not(lhs, result);
return BR_DONE;
}
if (m().is_complement(lhs, rhs)) {
result = m().mk_false();
return BR_DONE;
}
if (unfolded) {
result = m().mk_eq(lhs, rhs);
return BR_DONE;
}
}
return BR_FAILED;
}
br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result) {
if (num_args <= 1) {
result = m().mk_true();
return BR_DONE;
}
if (num_args == 2) {
expr_ref tmp(m());
result = m().mk_not(m().mk_eq(args[0], args[1]));
return BR_REWRITE2; // mk_eq may be dispatched to other rewriters.
}
expr_fast_mark1 visited;
bool all_value = true;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (visited.is_marked(arg)) {
result = m().mk_false();
return BR_DONE;
}
visited.mark(arg);
if (!m().is_value(arg))
all_value = false;
}
if (all_value) {
result = m().mk_true();
return BR_DONE;
}
SASSERT(num_args > 2);
if (m().is_bool(args[0])) {
result = m().mk_false();
return BR_DONE;
}
if (m_blast_distinct) {
ptr_buffer<expr> new_diseqs;
for (unsigned i = 0; i < num_args; i++) {
for (unsigned j = i + 1; j < num_args; j++)
new_diseqs.push_back(m().mk_not(m().mk_eq(args[i], args[j])));
}
result = m().mk_and(new_diseqs.size(), new_diseqs.c_ptr());
return BR_REWRITE3;
}
return BR_FAILED;
}
br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) {
bool s = false;
// (ite (not c) a b) ==> (ite c b a)
if (m().is_not(c)) {
c = to_app(c)->get_arg(0);
std::swap(t, e);
s = true;
}
// (ite c (ite c t1 t2) t3) ==> (ite c t1 t3)
if (m().is_ite(t) && to_app(t)->get_arg(0) == c) {
// Remark: (ite c (ite (not c) t1 t2) t3) ==> (ite c t2 t3) does not happen if applying rewrites bottom up
t = to_app(t)->get_arg(1);
s = true;
}
// (ite c t1 (ite c t2 t3)) ==> (ite c t1 t3)
if (m().is_ite(e) && to_app(e)->get_arg(0) == c) {
// Remark: (ite c t1 (ite (not c) t2 t3)) ==> (ite c t1 t2) does not happen if applying rewrites bottom up
e = to_app(e)->get_arg(2);
s = true;
}
if (m().is_true(c)) {
result = t;
return BR_DONE;
}
if (m().is_false(c)) {
result = e;
return BR_DONE;
}
if (t == e) {
result = t;
return BR_DONE;
}
if (m().is_bool(t)) {
if (m().is_true(t)) {
if (m().is_false(e)) {
result = c;
return BR_DONE;
}
mk_or(c, e, result);
return BR_DONE;
}
if (m().is_false(t)) {
if (m().is_true(e)) {
mk_not(c, result);
return BR_DONE;
}
expr_ref tmp(m());
mk_not(c, tmp);
mk_and(tmp, e, result);
return BR_DONE;
}
if (m().is_true(e)) {
expr_ref tmp(m());
mk_not(c, tmp);
mk_or(tmp, t, result);
return BR_DONE;
}
if (m().is_false(e)) {
mk_and(c, t, result);
return BR_DONE;
}
if (c == e) {
mk_and(c, t, result);
return BR_DONE;
}
if (c == t) {
mk_or(c, e, result);
return BR_DONE;
}
if (m().is_complement_core(t, e)) { // t = not(e)
mk_eq(c, t, result);
return BR_DONE;
}
if (m().is_complement_core(e, t)) { // e = not(t)
mk_eq(c, t, result);
return BR_DONE;
}
}
if (m().is_ite(t) && m_ite_extra_rules) {
// (ite c1 (ite c2 t1 t2) t1) ==> (ite (and c1 (not c2)) t2 t1)
if (e == to_app(t)->get_arg(1)) {
expr_ref not_c2(m());
mk_not(to_app(t)->get_arg(0), not_c2);
expr_ref new_c(m());
mk_and(c, not_c2, new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(2), e);
return BR_REWRITE1;
}
// (ite c1 (ite c2 t1 t2) t2) ==> (ite (and c1 c2) t1 t2)
if (e == to_app(t)->get_arg(2)) {
expr_ref new_c(m());
mk_and(c, to_app(t)->get_arg(0), new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(1), e);
return BR_REWRITE1;
}
if (m().is_ite(e)) {
// (ite c1 (ite c2 t1 t2) (ite c3 t1 t2)) ==> (ite (or (and c1 c2) (and (not c1) c3)) t1 t2)
if (to_app(t)->get_arg(1) == to_app(e)->get_arg(1) &&
to_app(t)->get_arg(2) == to_app(e)->get_arg(2)) {
expr_ref and1(m());
expr_ref and2(m());
expr_ref notc(m());
mk_and(c, to_app(t)->get_arg(0), and1);
mk_not(c, notc);
mk_and(notc, to_app(e)->get_arg(0), and2);
expr_ref new_c(m());
mk_or(and1, and2, new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2));
return BR_REWRITE1;
}
// (ite c1 (ite c2 t1 t2) (ite c3 t2 t1)) ==> (ite (or (and c1 c2) (and (not c1) (not c3))) t1 t2)
if (to_app(t)->get_arg(1) == to_app(e)->get_arg(2) &&
to_app(t)->get_arg(2) == to_app(e)->get_arg(1)) {
expr_ref and1(m());
expr_ref and2(m());
expr_ref notc(m());
mk_and(c, to_app(t)->get_arg(0), and1);
mk_not(c, notc);
expr_ref notc3(m());
mk_not(to_app(e)->get_arg(0), notc3);
mk_and(notc, notc3, and2);
expr_ref new_c(m());
mk_or(and1, and2, new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2));
return BR_REWRITE1;
}
}
}
if (m().is_ite(e) && m_ite_extra_rules) {
// (ite c1 t1 (ite c2 t1 t2)) ==> (ite (or c1 c2) t1 t2)
if (t == to_app(e)->get_arg(1)) {
expr_ref new_c(m());
mk_or(c, to_app(e)->get_arg(0), new_c);
result = m().mk_ite(new_c, t, to_app(e)->get_arg(2));
return BR_REWRITE1;
}
// (ite c1 t1 (ite c2 t2 t1)) ==> (ite (or c1 (not c2)) t1 t2)
if (t == to_app(e)->get_arg(2)) {
expr_ref not_c2(m());
mk_not(to_app(e)->get_arg(0), not_c2);
expr_ref new_c(m());
mk_or(c, not_c2, new_c);
result = m().mk_ite(new_c, t, to_app(e)->get_arg(1));
return BR_REWRITE1;
}
}
if (s) {
result = m().mk_ite(c, t, e);
return BR_DONE;
}
return BR_FAILED;
}
br_status bool_rewriter::mk_not_core(expr * t, expr_ref & result) {
if (m().is_not(t)) {
result = to_app(t)->get_arg(0);
return BR_DONE;
}
if (m().is_true(t)) {
result = m().mk_false();
return BR_DONE;
}
if (m().is_false(t)) {
result = m().mk_true();
return BR_DONE;
}
if (is_eq(t) && m().is_bool(to_app(t)->get_arg(0))) {
expr_ref tmp(m());
mk_not(to_app(t)->get_arg(0), tmp);
mk_eq(tmp, to_app(t)->get_arg(1), result);
return BR_DONE;
}
return BR_FAILED;
}
void bool_rewriter::mk_xor(expr * lhs, expr * rhs, expr_ref & result) {
expr_ref tmp(m());
mk_not(lhs, tmp);
mk_eq(tmp, rhs, result);
}
void bool_rewriter::mk_implies(expr * lhs, expr * rhs, expr_ref & result) {
expr_ref tmp(m());
mk_not(lhs, tmp);
mk_or(tmp, rhs, result);
}
void bool_rewriter::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref tmp(m_manager);
mk_and(num_args, args, tmp);
mk_not(tmp, result);
}
void bool_rewriter::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref tmp(m_manager);
mk_or(num_args, args, tmp);
mk_not(tmp, result);
}
void bool_rewriter::mk_nand(expr * arg1, expr * arg2, expr_ref & result) {
expr_ref tmp(m_manager);
mk_and(arg1, arg2, tmp);
mk_not(tmp, result);
}
void bool_rewriter::mk_nor(expr * arg1, expr * arg2, expr_ref & result) {
expr_ref tmp(m_manager);
mk_or(arg1, arg2, tmp);
mk_not(tmp, result);
}
template class rewriter_tpl<bool_rewriter_cfg>;

View file

@ -0,0 +1,199 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bool_rewriter.h
Abstract:
Basic rewriting rules for Boolean operators.
Author:
Leonardo (leonardo) 2011-04-04
Notes:
--*/
#ifndef _BOOL_REWRITER_H_
#define _BOOL_REWRITER_H_
#include"ast.h"
#include"rewriter.h"
#include"params.h"
/**
\brief Apply basic Boolean rewriting operations.
Only depth 1 simplifications are performed.
Note: there are no recursive calls.
Note: arguments of AC operators are not sorted.
Note: arguments of = and xor are also not sorted.
Note: By default, (AND A B) is not rewritten as (NOT (OR (NOT A) (NOT B)))
Note: AND OR operators are flattened only if mk_flat_app, mk_flat_or, mk_flat_and are used.
The following operators are expanded:
- => (implies)
- xor
- nand
- nor
- iff
All methods run in time almost linear on the number of arguments.
Actually, this is not true when flattening is enabled.
A better approximation is O(Sum_{t \in args} size1(t)).
Where size1(t) = max{t->get_num_args(), 1}.
*/
class bool_rewriter {
ast_manager & m_manager;
bool m_flat;
bool m_local_ctx;
bool m_elim_and;
bool m_blast_distinct;
bool m_ite_extra_rules;
unsigned m_local_ctx_limit;
unsigned m_local_ctx_cost;
br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result);
void mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result);
expr * mk_or_app(unsigned num_args, expr * const * args);
bool simp_nested_not_or(unsigned num_args, expr * const * args, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result);
expr * simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified);
void mk_nested_ite(expr * new_c, expr * new_t, expr * new_e, expr_ref & result);
bool simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result);
bool local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result);
br_status try_ite_value(app * ite, app * val, expr_ref & result);
public:
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); }
ast_manager & m() const { return m_manager; }
family_id get_fid() const { return m().get_basic_family_id(); }
bool is_eq(expr * t) const { return m().is_eq(t) || m().is_iff(t); }
bool flat() const { return m_flat; }
void set_flat(bool f) { m_flat = f; }
bool elim_and() const { return m_elim_and; }
void set_elim_and(bool f) { m_elim_and = f; }
void reset_local_ctx_cost() { m_local_ctx_cost = 0; }
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
// The core methods return true if a rewrite-step/simplification was applied
// to the arguments, and the result is stored in 'result'. Otherwise, they return false
// and result.get == 0.
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
result = m().mk_app(f, num_args, args);
}
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
br_status mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_iff_core(expr * lhs, expr * rhs, expr_ref & result) { return mk_eq_core(lhs, rhs, result); }
br_status mk_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
if (m_elim_and) {
mk_and_as_or(num_args, args, result);
return BR_DONE;
}
else if (m_flat) {
return mk_flat_and_core(num_args, args, result);
}
else {
return mk_nflat_and_core(num_args, args, result);
}
}
br_status mk_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
return m_flat ?
mk_flat_or_core(num_args, args, result) :
mk_nflat_or_core(num_args, args, result);
}
br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result);
br_status mk_not_core(expr * t, expr_ref & result);
void mk_eq(expr * lhs, expr * rhs, expr_ref & result) {
if (mk_eq_core(lhs, rhs, result) == BR_FAILED)
result = m().mk_eq(lhs, rhs);
}
void mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); }
void mk_xor(expr * lhs, expr * rhs, expr_ref & result);
void mk_and(unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_and_core(num_args, args, result) == BR_FAILED) {
SASSERT(!m_elim_and);
result = m().mk_and(num_args, args);
}
}
void mk_or(unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_or_core(num_args, args, result) == BR_FAILED)
result = m().mk_or(num_args, args);
}
void mk_and(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = {arg1, arg2};
mk_and(2, args, result);
}
void mk_or(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = {arg1, arg2};
mk_or(2, args, result);
}
void mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
expr * args[3] = {arg1, arg2, arg3};
mk_and(3, args, result);
}
void mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
expr * args[3] = {arg1, arg2, arg3};
mk_or(3, args, result);
}
void mk_implies(expr * lhs, expr * rhs, expr_ref & result);
void mk_ite(expr * c, expr * t, expr * e, expr_ref & result) {
if (mk_ite_core(c, t, e, result) == BR_FAILED)
result = m().mk_ite(c, t, e);
}
void mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_distinct_core(num_args, args, result) == BR_FAILED)
result = m().mk_distinct(num_args, args);
}
void mk_not(expr * t, expr_ref & result) {
if (mk_not_core(t, result) == BR_FAILED)
result = m().mk_not(t);
}
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);
};
struct bool_rewriter_cfg : public default_rewriter_cfg {
bool_rewriter m_r;
bool flat_assoc(func_decl * f) const { return m_r.flat() && (m_r.m().is_and(f) || m_r.m().is_or(f)); }
bool rewrite_patterns() const { return false; }
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = 0;
if (f->get_family_id() != m_r.get_fid())
return BR_FAILED;
return m_r.mk_app_core(f, num, args, result);
}
bool_rewriter_cfg(ast_manager & m, params_ref const & p):m_r(m, p) {}
};
class bool_rewriter_star : public rewriter_tpl<bool_rewriter_cfg> {
bool_rewriter_cfg m_cfg;
public:
bool_rewriter_star(ast_manager & m, params_ref const & p):
rewriter_tpl<bool_rewriter_cfg>(m, false, m_cfg),
m_cfg(m, p) {}
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,171 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bv_rewriter.h
Abstract:
Basic rewriting rules for bit-vectors
Author:
Leonardo (leonardo) 2011-04-14
Notes:
--*/
#ifndef _BV_REWRITER_H_
#define _BV_REWRITER_H_
#include"poly_rewriter.h"
#include"bv_decl_plugin.h"
#include"arith_decl_plugin.h"
class mk_extract_proc {
bv_util & m_util;
unsigned m_high;
unsigned m_low;
sort * m_domain;
func_decl * m_f_cached;
public:
mk_extract_proc(bv_util & u);
~mk_extract_proc();
app * operator()(unsigned high, unsigned low, expr * arg);
};
class bv_rewriter_core {
protected:
typedef rational numeral;
bv_util m_util;
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
bool is_numeral(expr * n, numeral & r) const { unsigned sz; return m_util.is_numeral(n, r, sz); }
bool is_zero(expr * n) const { return m_util.is_zero(n); }
bool is_minus_one(expr * n) const { return m_util.is_allone(n); }
void normalize(numeral & c, sort * s) { unsigned bv_size = m_util.get_bv_size(s); c = m_util.norm(c, bv_size); }
app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); }
decl_kind add_decl_kind() const { return OP_BADD; }
decl_kind mul_decl_kind() const { return OP_BMUL; }
bool use_power() const { return false; }
decl_kind power_decl_kind() const { UNREACHABLE(); return static_cast<decl_kind>(UINT_MAX); }
public:
bv_rewriter_core(ast_manager & m):m_util(m) {}
};
class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
mk_extract_proc m_mk_extract;
arith_util m_autil;
bool m_hi_div0;
bool m_elim_sign_ext;
bool m_mul2concat;
bool m_bit2bool;
bool m_blast_eq_value;
bool m_mkbv2num;
bool m_split_concat_eq;
bool m_udiv2mul;
bool is_zero_bit(expr * x, unsigned idx);
br_status mk_ule(expr * a, expr * b, expr_ref & result);
br_status mk_uge(expr * a, expr * b, expr_ref & result);
br_status mk_ult(expr * a, expr * b, expr_ref & result);
br_status mk_sle(expr * a, expr * b, expr_ref & result);
br_status mk_sge(expr * a, expr * b, expr_ref & result);
br_status mk_slt(expr * a, expr * b, expr_ref & result);
br_status mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result);
br_status mk_concat(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result);
br_status mk_repeat(unsigned n, expr * arg, expr_ref & result);
br_status mk_zero_extend(unsigned n, expr * arg, expr_ref & result);
br_status mk_sign_extend(unsigned n, expr * arg, expr_ref & result);
br_status mk_bv_not(expr * arg, expr_ref & result);
br_status mk_bv_or(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bv_xor(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bv_and(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bv_nand(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bv_nor(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bv_xnor(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_bv_rotate_left(unsigned n, expr * arg, expr_ref & result);
br_status mk_bv_rotate_right(unsigned n, expr * arg, expr_ref & result);
br_status mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_bv_add(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
return mk_bv_add(2, args, result);
}
br_status mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result);
bool is_minus_one_core(expr * arg) const;
bool is_x_minus_one(expr * arg, expr * & x);
br_status mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
br_status mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
br_status mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
br_status mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
br_status mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
br_status mk_bv_sdiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, m_hi_div0, result); }
br_status mk_bv_udiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, m_hi_div0, result); }
br_status mk_bv_srem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, m_hi_div0, result); }
br_status mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, m_hi_div0, result); }
br_status mk_bv_smod(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, m_hi_div0, result); }
br_status mk_bv_sdiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, true, result); }
br_status mk_bv_udiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, true, result); }
br_status mk_bv_srem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, true, result); }
br_status mk_bv_urem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, true, result); }
br_status mk_bv_smod_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, true, result); }
br_status mk_int2bv(unsigned bv_size, expr * arg, expr_ref & result);
br_status mk_bv2int(expr * arg, expr_ref & result);
br_status mk_bv_redor(expr * arg, expr_ref & result);
br_status mk_bv_redand(expr * arg, expr_ref & result);
br_status mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result);
br_status mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result);
br_status mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result);
br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result);
bool is_minus_one_times_t(expr * arg);
void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result);
bool is_concat_split_target(expr * t) const;
br_status mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result);
void updt_local_params(params_ref const & p);
public:
bv_rewriter(ast_manager & m, params_ref const & p = params_ref()):
poly_rewriter<bv_rewriter_core>(m, p),
m_mk_extract(m_util),
m_autil(m) {
updt_local_params(p);
}
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
void set_mkbv2num(bool f) { m_mkbv2num = f; }
unsigned get_bv_size(expr * t) const {return m_util.get_bv_size(t); }
bool is_numeral(expr * t) const { return m_util.is_numeral(t); }
bool is_numeral(expr * t, numeral & r, unsigned & sz) const { return m_util.is_numeral(t, r, sz); }
bool is_bv(expr * t) const { return m_util.is_bv(t); }
expr * mk_numeral(numeral const & v, unsigned sz) { return m_util.mk_numeral(v, sz); }
expr * mk_numeral(unsigned v, unsigned sz) { return m_util.mk_numeral(numeral(v), sz); }
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
result = m().mk_app(f, num_args, args);
}
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
};
#endif

View file

@ -0,0 +1,111 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
datatype_rewriter.cpp
Abstract:
Basic rewriting rules for Datatypes.
Author:
Leonardo (leonardo) 2011-04-06
Notes:
--*/
#include"datatype_rewriter.h"
br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(f->get_family_id() == get_fid());
switch(f->get_decl_kind()) {
case OP_DT_CONSTRUCTOR: return BR_FAILED;
case OP_DT_RECOGNISER:
//
// simplify is_cons(cons(x,y)) -> true
// simplify is_cons(nil) -> false
//
SASSERT(num_args == 1);
if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0])))
return BR_FAILED;
if (to_app(args[0])->get_decl() == m_util.get_recognizer_constructor(f))
result = m().mk_true();
else
result = m().mk_false();
return BR_DONE;
case OP_DT_ACCESSOR: {
//
// simplify head(cons(x,y)) -> x
//
SASSERT(num_args == 1);
if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0])))
return BR_FAILED;
app * a = to_app(args[0]);
func_decl * c_decl = a->get_decl();
if (c_decl != m_util.get_accessor_constructor(f))
return BR_FAILED;
ptr_vector<func_decl> const * acc = m_util.get_constructor_accessors(c_decl);
SASSERT(acc && acc->size() == a->get_num_args());
unsigned num = acc->size();
for (unsigned i = 0; i < num; ++i) {
if (f == (*acc)[i]) {
// found it.
result = a->get_arg(i);
return BR_DONE;
}
}
UNREACHABLE();
break;
}
default:
UNREACHABLE();
}
return BR_FAILED;
}
br_status datatype_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
if (!is_app(lhs) || !is_app(rhs) || !m_util.is_constructor(to_app(lhs)) || !m_util.is_constructor(to_app(rhs)))
return BR_FAILED;
if (to_app(lhs)->get_decl() != to_app(rhs)->get_decl()) {
result = m().mk_false();
return BR_DONE;
}
// Remark: In datatype_simplifier_plugin, we used
// m_basic_simplifier to create '=' and 'and' applications in the
// following code. This trick not guarantee that the final expression
// will be fully simplified.
//
// Example:
// The assertion
// (assert (= (cons a1 (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil))))))
// (cons b1 (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil))))))))
//
// After applying asserted_formulas::reduce(), the following formula was generated.
//
// (= a1 b1)
// (= a2 b2)
// (= a3 b3)
// (= (+ a4 (* (- 1) b4)) (- 1))
// (= (+ c5 a5) b5) <<< NOT SIMPLIFIED WITH RESPECT TO ARITHMETIC
// (= (cons a6 nil) (cons b6 nil))) <<< NOT SIMPLIFIED WITH RESPECT TO DATATYPE theory
//
// Note that asserted_formulas::reduce() applied the simplier many times.
// After the first simplification step we had:
// (= a1 b1)
// (= (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil))))))
// (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil))))))
ptr_buffer<expr> eqs;
unsigned num = to_app(lhs)->get_num_args();
SASSERT(num == to_app(rhs)->get_num_args());
for (unsigned i = 0; i < num; ++i) {
eqs.push_back(m().mk_eq(to_app(lhs)->get_arg(i), to_app(rhs)->get_arg(i)));
}
result = m().mk_and(eqs.size(), eqs.c_ptr());
return BR_REWRITE2;
}

View file

@ -0,0 +1,35 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
datatype_rewriter.h
Abstract:
Basic rewriting rules for Datatypes.
Author:
Leonardo (leonardo) 2011-04-06
Notes:
--*/
#ifndef _DATATYPE_REWRITER_H_
#define _DATATYPE_REWRITER_H_
#include"datatype_decl_plugin.h"
#include"rewriter_types.h"
class datatype_rewriter {
datatype_util m_util;
public:
datatype_rewriter(ast_manager & m):m_util(m) {}
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
};
#endif

467
src/ast/rewriter/der.cpp Normal file
View file

@ -0,0 +1,467 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
der.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-01-27.
Revision History:
Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution
--*/
#include"der.h"
#include"occurs.h"
#include"for_each_expr.h"
#include"rewriter_def.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
static bool is_var(expr * e, unsigned num_decls) {
return is_var(e) && to_var(e)->get_idx() < num_decls;
}
static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) {
return m.is_not(e) && is_var(to_app(e)->get_arg(0)) && to_var(to_app(e)->get_arg(0))->get_idx() < num_decls;
}
/**
\brief Return true if \c e is of the form (not (= VAR t)) or (not (iff VAR t)) or (iff VAR t) or (iff (not VAR) t) or (VAR IDX) or (not (VAR IDX)).
The last case can be viewed
*/
bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) {
// (not (= VAR t)) and (not (iff VAR t)) cases
if (m_manager.is_not(e) && (m_manager.is_eq(to_app(e)->get_arg(0)) || m_manager.is_iff(to_app(e)->get_arg(0)))) {
app * eq = to_app(to_app(e)->get_arg(0));
SASSERT(m_manager.is_eq(eq) || m_manager.is_iff(eq));
expr * lhs = eq->get_arg(0);
expr * rhs = eq->get_arg(1);
if (!is_var(lhs, num_decls) && !is_var(rhs, num_decls))
return false;
if (!is_var(lhs, num_decls))
std::swap(lhs, rhs);
SASSERT(is_var(lhs, num_decls));
// Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles...
// if (occurs(lhs, rhs)) {
// return false;
// }
v = to_var(lhs);
t = rhs;
TRACE("der", tout << mk_pp(e, m_manager) << "\n";);
return true;
}
// (iff VAR t) and (iff (not VAR) t) cases
else if (m_manager.is_iff(e)) {
expr * lhs = to_app(e)->get_arg(0);
expr * rhs = to_app(e)->get_arg(1);
// (iff VAR t) case
if (is_var(lhs, num_decls) || is_var(rhs, num_decls)) {
if (!is_var(lhs, num_decls))
std::swap(lhs, rhs);
SASSERT(is_var(lhs, num_decls));
// Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles...
// if (occurs(lhs, rhs)) {
// return false;
// }
v = to_var(lhs);
t = m_manager.mk_not(rhs);
m_new_exprs.push_back(t);
TRACE("der", tout << mk_pp(e, m_manager) << "\n";);
return true;
}
// (iff (not VAR) t) case
else if (is_neg_var(m_manager, lhs, num_decls) || is_neg_var(m_manager, rhs, num_decls)) {
if (!is_neg_var(m_manager, lhs, num_decls))
std::swap(lhs, rhs);
SASSERT(is_neg_var(m_manager, lhs, num_decls));
expr * lhs_var = to_app(lhs)->get_arg(0);
// Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles...
// if (occurs(lhs_var, rhs)) {
// return false;
// }
v = to_var(lhs_var);
t = rhs;
TRACE("der", tout << mk_pp(e, m_manager) << "\n";);
return true;
}
else {
return false;
}
}
// VAR != false case
else if (is_var(e, num_decls)) {
t = m_manager.mk_false();
v = to_var(e);
TRACE("der", tout << mk_pp(e, m_manager) << "\n";);
return true;
}
// VAR != true case
else if (is_neg_var(m_manager, e, num_decls)) {
t = m_manager.mk_true();
v = to_var(to_app(e)->get_arg(0));
TRACE("der", tout << mk_pp(e, m_manager) << "\n";);
return true;
}
else {
return false;
}
}
void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) {
bool reduced = false;
pr = 0;
r = q;
TRACE("der", tout << mk_pp(q, m_manager) << "\n";);
// Keep applying it until r doesn't change anymore
do {
proof_ref curr_pr(m_manager);
q = to_quantifier(r);
reduce1(q, r, curr_pr);
if (q != r)
reduced = true;
if (m_manager.proofs_enabled()) {
pr = m_manager.mk_transitivity(pr, curr_pr);
}
} while (q != r && is_quantifier(r));
// Eliminate variables that have become unused
if (reduced && is_forall(r)) {
quantifier * q = to_quantifier(r);
elim_unused_vars(m_manager, q, r);
if (m_manager.proofs_enabled()) {
proof * p1 = m_manager.mk_elim_unused_vars(q, r);
pr = m_manager.mk_transitivity(pr, p1);
}
}
m_new_exprs.reset();
}
void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
if (!is_forall(q)) {
pr = 0;
r = q;
return;
}
expr * e = q->get_expr();
unsigned num_decls = q->get_num_decls();
var * v = 0;
expr_ref t(m_manager);
if (m_manager.is_or(e)) {
unsigned num_args = to_app(e)->get_num_args();
unsigned i = 0;
unsigned diseq_count = 0;
unsigned largest_vinx = 0;
m_map.reset();
m_pos2var.reset();
m_inx2var.reset();
m_pos2var.reserve(num_args, -1);
// Find all disequalities
for (; i < num_args; i++) {
if (is_var_diseq(to_app(e)->get_arg(i), num_decls, v, t)) {
unsigned idx = v->get_idx();
if(m_map.get(idx, 0) == 0) {
m_map.reserve(idx + 1, 0);
m_inx2var.reserve(idx + 1, 0);
m_map[idx] = t;
m_inx2var[idx] = v;
m_pos2var[i] = idx;
diseq_count++;
largest_vinx = (idx>largest_vinx) ? idx : largest_vinx;
}
}
}
if (diseq_count > 0) {
get_elimination_order();
SASSERT(m_order.size() <= diseq_count); // some might be missing because of cycles
if (!m_order.empty()) {
create_substitution(largest_vinx + 1);
apply_substitution(q, r);
}
}
else {
TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m_manager) << "\n";);
r = q;
}
}
// Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses.
// So, we must perform a occurs check here.
else if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t)) {
r = m_manager.mk_false();
}
else
r = q;
if (m_manager.proofs_enabled()) {
pr = r == q ? 0 : m_manager.mk_der(q, r);
}
}
void der_sort_vars(ptr_vector<var> & vars, ptr_vector<expr> & definitions, unsigned_vector & order) {
order.reset();
// eliminate self loops, and definitions containing quantifiers.
bool found = false;
for (unsigned i = 0; i < definitions.size(); i++) {
var * v = vars[i];
expr * t = definitions[i];
if (t == 0 || has_quantifiers(t) || occurs(v, t))
definitions[i] = 0;
else
found = true; // found at least one candidate
}
if (!found)
return;
typedef std::pair<expr *, unsigned> frame;
svector<frame> todo;
expr_fast_mark1 visiting;
expr_fast_mark2 done;
unsigned vidx, num;
for (unsigned i = 0; i < definitions.size(); i++) {
if (definitions[i] == 0)
continue;
var * v = vars[i];
SASSERT(v->get_idx() == i);
SASSERT(todo.empty());
todo.push_back(frame(v, 0));
while (!todo.empty()) {
start:
frame & fr = todo.back();
expr * t = fr.first;
if (t->get_ref_count() > 1 && done.is_marked(t)) {
todo.pop_back();
continue;
}
switch (t->get_kind()) {
case AST_VAR:
vidx = to_var(t)->get_idx();
if (fr.second == 0) {
CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";);
// Remark: The size of definitions may be smaller than the number of variables occuring in the quantified formula.
if (definitions.get(vidx, 0) != 0) {
if (visiting.is_marked(t)) {
// cycle detected: remove t
visiting.reset_mark(t);
definitions[vidx] = 0;
}
else {
visiting.mark(t);
fr.second = 1;
todo.push_back(frame(definitions[vidx], 0));
goto start;
}
}
}
else {
SASSERT(fr.second == 1);
if (definitions.get(vidx, 0) != 0) {
visiting.reset_mark(t);
order.push_back(vidx);
}
else {
// var was removed from the list of candidate vars to elim cycle
// do nothing
}
}
if (t->get_ref_count() > 1)
done.mark(t);
todo.pop_back();
break;
case AST_QUANTIFIER:
UNREACHABLE();
todo.pop_back();
break;
case AST_APP:
num = to_app(t)->get_num_args();
while (fr.second < num) {
expr * arg = to_app(t)->get_arg(fr.second);
fr.second++;
if (arg->get_ref_count() > 1 && done.is_marked(arg))
continue;
todo.push_back(frame(arg, 0));
goto start;
}
if (t->get_ref_count() > 1)
done.mark(t);
todo.pop_back();
break;
default:
UNREACHABLE();
todo.pop_back();
break;
}
}
}
}
void der::get_elimination_order() {
m_order.reset();
TRACE("top_sort",
tout << "DEFINITIONS: " << std::endl;
for(unsigned i = 0; i < m_map.size(); i++)
if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m_manager) << std::endl;
);
// der::top_sort ts(m_manager);
der_sort_vars(m_inx2var, m_map, m_order);
TRACE("der",
tout << "Elimination m_order:" << std::endl;
for(unsigned i=0; i<m_order.size(); i++)
{
if (i != 0) tout << ",";
tout << m_order[i];
}
tout << std::endl;
);
}
void der::create_substitution(unsigned sz) {
m_subst_map.reset();
m_subst_map.resize(sz, 0);
for(unsigned i = 0; i < m_order.size(); i++) {
expr_ref cur(m_map[m_order[i]], m_manager);
// do all the previous substitutions before inserting
expr_ref r(m_manager);
m_subst(cur, m_subst_map.size(), m_subst_map.c_ptr(), r);
unsigned inx = sz - m_order[i]- 1;
SASSERT(m_subst_map[inx]==0);
m_subst_map[inx] = r;
}
}
void der::apply_substitution(quantifier * q, expr_ref & r) {
expr * e = q->get_expr();
unsigned num_args=to_app(e)->get_num_args();
// get a new expression
m_new_args.reset();
for(unsigned i = 0; i < num_args; i++) {
int x = m_pos2var[i];
if (x != -1 && m_map[x] != 0)
continue; // this is a disequality with definition (vanishes)
m_new_args.push_back(to_app(e)->get_arg(i));
}
unsigned sz = m_new_args.size();
expr_ref t(m_manager);
t = (sz == 1) ? m_new_args[0] : m_manager.mk_or(sz, m_new_args.c_ptr());
expr_ref new_e(m_manager);
m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e);
// don't forget to update the quantifier patterns
expr_ref_buffer new_patterns(m_manager);
expr_ref_buffer new_no_patterns(m_manager);
for (unsigned j = 0; j < q->get_num_patterns(); j++) {
expr_ref new_pat(m_manager);
m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat);
new_patterns.push_back(new_pat);
}
for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
expr_ref new_nopat(m_manager);
m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat);
new_no_patterns.push_back(new_nopat);
}
r = m_manager.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(),
new_no_patterns.size(), new_no_patterns.c_ptr(), new_e);
}
struct der_rewriter_cfg : public default_rewriter_cfg {
der m_der;
der_rewriter_cfg(ast_manager & m):m_der(m) {}
ast_manager & m() const { return m_der.m(); }
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
quantifier_ref q1(m());
q1 = m().update_quantifier(old_q, old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns, new_body);
m_der(q1, result, result_pr);
return true;
}
};
template class rewriter_tpl<der_rewriter_cfg>;
struct der_rewriter::imp : public rewriter_tpl<der_rewriter_cfg> {
der_rewriter_cfg m_cfg;
imp(ast_manager & m):
rewriter_tpl<der_rewriter_cfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(m) {
}
};
der_rewriter::der_rewriter(ast_manager & m) {
m_imp = alloc(imp, m);
}
der_rewriter::~der_rewriter() {
dealloc(m_imp);
}
ast_manager & der_rewriter::m() const {
return m_imp->m();
}
void der_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) {
m_imp->operator()(t, result, result_pr);
}
void der_rewriter::set_cancel(bool f) {
#pragma omp critical (der_rewriter)
{
m_imp->set_cancel(f);
}
}
void der_rewriter::cleanup() {
ast_manager & m = m_imp->m();
#pragma omp critical (th_rewriter)
{
dealloc(m_imp);
m_imp = alloc(imp, m);
}
}
void der_rewriter::reset() {
m_imp->reset();
}

187
src/ast/rewriter/der.h Normal file
View file

@ -0,0 +1,187 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
der.h
Abstract:
Destructive equality resolution.
Author:
Leonardo de Moura (leonardo) 2008-01-27.
Revision History:
Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution
--*/
#ifndef _DER_H_
#define _DER_H_
#include"ast.h"
#include"var_subst.h"
/*
New DER: the class DER (above) eliminates variables one by one.
This is inefficient, and we should implement a new version that
can handle efficiently examples with hundreds of variables.
Suppose the target of the simplification is the quantifier
(FORALL (x1 T1) (x2 T2) ... (xn Tn) (or ....))
So, the variables x1 ... xn are the candidates for elimination.
First, we build a mapping of candidate substitutions
Since variables x1 ... xn have ids 0 ... n-1, we can
use an array m_map to implement this mapping.
The idea is to traverse the children of the (or ...) looking
for diseqs. The method is_var_diseq can be used for doing that.
Given a child c, if is_var_diseq(c, num_decls, v, t) returns true,
and m_map[v] is null, then we store t at m_map[v].
For performance reasons, we also store a mapping from child position (in the OR) to variable ID.
Thus, m_pos2var[pos] contains the variable that is defined by the diseq at position pos.
m_pos2var[pos] = -1, if this position does not contain a diseq.
After doing that, m_map contains the variables that can
be potentially eliminated using DER.
We say m_map[v] is the definition of variable v.
The next step is to perform a topological sort on these
variables. The result is an array (m_order) of integers (variable ids)
such that i < j implies that m_map[m_order[i]] does not depend on variable m_order[j].
For example, consider the case where m_map contains the following values
m_map[0] = (+ (VAR 2) 0)
m_map[1] = null
m_map[2] = (+ (VAR 3) 0)
m_map[3] = (+ (VAR 1) 1)
m_map[4] = (* (VAR 5) 2)
m_map[5] = null
In this example, variable 0 depends on the definition of variable 2, which
depends on the definition of variable 3, which depends on the definition of variable 0 (cycle).
On the other hand, no cycle is found when starting at variable 4.
Cycles can be broken by erasing entries from m_map. For example, the cycle above
can be removed by setting m_map[0] = null.
m_map[0] = null
m_map[1] = null
m_map[2] = (+ (VAR 3) 0)
m_map[3] = (+ (VAR 1) 1)
m_map[4] = (* (VAR 5) 2)
m_map[5] = null
The file asserted_formulas.cpp has a class top_sort for performing topological sort.
This class cannot be used here, since it is meant for eliminating constants (instead of variables).
We need to implement a new top_sort here, we do not need a separate class for doing that.
Moreover, it is much simpler, since m_map is just an array.
In the example above (after setting m_map[0] to null), top_sort will produce the following order
m_order = [3, 2, 4]
The next step is to use var_subst to update the definitions in var_subst.
The idea is to process the variables in the order specified by m_order.
When processing m_map[m_order[i]] we use the definitions of all variables in m_order[0 ... i-1].
For example:
The first variable is 3, since it is at m_order[0], nothing needs to be done.
Next we have variable 2, we use m_map[3] since 3 is before 2 in m_order. So, the new
definition for 2 is (+ (+ (VAR 1) 1) 0). That is, we update m_map[2] with (+ (+ (VAR 1) 1) 0)
Next we have variable 4, we use m_map[3] and m_map[2] since 3 and 2 are before 4 in m_order.
In this case, var_subst will not do anything since m_map[4] does not contain variables 3 or 2.
So, the new m_map is:
m_map[0] = null
m_map[1] = null
m_map[2] = (+ (+ (VAR 1) 1) 0)
m_map[3] = (+ (VAR 1) 1)
m_map[4] = (* (VAR 5) 2)
m_map[5] = null
Now, we update the body of the quantifier using var_subst and the mapping above.
The idea is to create a new set of children for the OR.
For each child at position i, we do
if m_map[m_pos2var[i]] != -1
skip this child, it is a diseq used during DER
else
apply var_subst using m_map to this child, and store the result in a new children array
Create a new OR (new body of the quantifier) using the new children
Then, we create a new quantifier using this new body, and use the function elim_unused_vars to
eliminate the ununsed variables.
Remark: let us implement the new version inside the class der.
Use #if 0 ... #endif to comment the old version.
Remark: after you are done, we can eliminate the call to occurs in is_var_diseq, since
top_sort is already performing cycle detection.
*/
/**
\brief Functor for applying Destructive Multi-Equality Resolution.
(forall (X Y) (or X /= s C[X])) --> (forall (Y) C[Y])
*/
class der {
ast_manager & m_manager;
var_subst m_subst;
expr_ref_buffer m_new_exprs;
ptr_vector<expr> m_map;
int_vector m_pos2var;
ptr_vector<var> m_inx2var;
unsigned_vector m_order;
expr_ref_vector m_subst_map;
expr_ref_buffer m_new_args;
/**
\brief Return true if e can be viewed as a variable disequality.
Store the variable id in v and the definition in t.
For example:
if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T.
if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T).
(not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))),
and can be viewed as a disequality.
*/
bool is_var_diseq(expr * e, unsigned num_decls, var *& v, expr_ref & t);
void get_elimination_order();
void create_substitution(unsigned sz);
void apply_substitution(quantifier * q, expr_ref & r);
void reduce1(quantifier * q, expr_ref & r, proof_ref & pr);
public:
der(ast_manager & m):m_manager(m),m_subst(m),m_new_exprs(m),m_subst_map(m),m_new_args(m) {}
ast_manager & m() const { return m_manager; }
void operator()(quantifier * q, expr_ref & r, proof_ref & pr);
};
/**
\brief Functor for applying Destructive Multi-Equality Resolution in all
universal quantifiers in an expression.
*/
class der_rewriter {
protected:
struct imp;
imp * m_imp;
public:
der_rewriter(ast_manager & m);
~der_rewriter();
ast_manager & m () const;
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
void set_cancel(bool f);
void cleanup();
void reset();
};
typedef der_rewriter der_star;
#endif /* _DER_H_ */

View file

@ -0,0 +1,57 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_rewriter.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2010-08-10
Revision History:
--*/
#include"dl_rewriter.h"
br_status dl_rewriter::mk_app_core(
func_decl * f, unsigned num_args, expr* const* args, expr_ref& result) {
ast_manager& m = result.get_manager();
uint64 v1, v2;
switch(f->get_decl_kind()) {
case datalog::OP_DL_LT:
if (m_util.is_numeral_ext(args[0], v1) &&
m_util.is_numeral_ext(args[1], v2)) {
result = (v1 < v2)?m.mk_true():m.mk_false();
return BR_DONE;
}
// x < x <=> false
if (args[0] == args[1]) {
result = m.mk_false();
return BR_DONE;
}
// x < 0 <=> false
if (m_util.is_numeral_ext(args[1], v2) && v2 == 0) {
result = m.mk_false();
return BR_DONE;
}
// 0 < x <=> 0 != x
if (m_util.is_numeral_ext(args[1], v1) && v1 == 0) {
result = m.mk_not(m.mk_eq(args[0], args[1]));
return BR_DONE;
}
break;
default:
break;
}
return BR_FAILED;
}

View file

@ -0,0 +1,33 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
dl_rewriter.h
Abstract:
Basic rewriting rules for atalog finite domains.
Author:
Nikolaj Bjorner (nbjorner) 2012-03-09
Notes:
--*/
#ifndef _DL_REWRITER_H_
#define _DL_REWRITER_H_
#include"dl_decl_plugin.h"
#include"rewriter_types.h"
class dl_rewriter {
datalog::dl_decl_util m_util;
public:
dl_rewriter(ast_manager & m):m_util(m) {}
family_id get_fid() const { return m_util.get_family_id(); }
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
};
#endif

View file

@ -0,0 +1,156 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
expr_replacer.cpp
Abstract:
Abstract (functor) for replacing constants with expressions.
Author:
Leonardo (leonardo) 2011-04-29
Notes:
--*/
#include"expr_replacer.h"
#include"rewriter_def.h"
#include"th_rewriter.h"
#include"cooperate.h"
void expr_replacer::operator()(expr * t, expr_ref & result, proof_ref & result_pr) {
expr_dependency_ref result_dep(m());
operator()(t, result, result_pr, result_dep);
}
void expr_replacer::operator()(expr * t, expr_ref & result) {
proof_ref pr(m());
operator()(t, result, pr);
}
struct expr_replacer::scoped_set_subst {
expr_replacer & m_r;
scoped_set_subst(expr_replacer & r, expr_substitution & s):m_r(r) { m_r.set_substitution(&s); }
~scoped_set_subst() { m_r.set_substitution(0); }
};
void expr_replacer::apply_substitution(expr * s, expr * def, proof * def_pr, expr_ref & t) {
expr_substitution sub(m());
sub.insert(s, def, def_pr);
scoped_set_subst set(*this, sub);
(*this)(t);
}
void expr_replacer::apply_substitution(expr * s, expr * def, expr_ref & t) {
expr_substitution sub(m());
sub.insert(s, def);
scoped_set_subst set(*this, sub);
(*this)(t);
}
struct default_expr_replacer_cfg : public default_rewriter_cfg {
ast_manager & m;
expr_substitution * m_subst;
expr_dependency_ref m_used_dependencies;
default_expr_replacer_cfg(ast_manager & _m):
m(_m),
m_subst(0),
m_used_dependencies(_m) {
}
bool get_subst(expr * s, expr * & t, proof * & pr) {
if (m_subst == 0)
return false;
expr_dependency * d = 0;
if (m_subst->find(s, t, pr, d)) {
m_used_dependencies = m.mk_join(m_used_dependencies, d);
return true;
}
return false;
}
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("simplifier");
return false;
}
};
template class rewriter_tpl<default_expr_replacer_cfg>;
class default_expr_replacer : public expr_replacer {
default_expr_replacer_cfg m_cfg;
rewriter_tpl<default_expr_replacer_cfg> m_replacer;
public:
default_expr_replacer(ast_manager & m):
m_cfg(m),
m_replacer(m, m.proofs_enabled(), m_cfg) {
}
virtual ast_manager & m() const { return m_replacer.m(); }
virtual void set_substitution(expr_substitution * s) {
m_replacer.cleanup();
m_replacer.cfg().m_subst = s;
}
virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) {
result_dep = 0;
m_replacer.operator()(t, result, result_pr);
if (m_cfg.m_used_dependencies != 0) {
result_dep = m_cfg.m_used_dependencies;
m_replacer.reset(); // reset cache
m_cfg.m_used_dependencies = 0;
}
}
virtual void set_cancel(bool f) {
m_replacer.set_cancel(f);
}
virtual unsigned get_num_steps() const {
return m_replacer.get_num_steps();
}
};
expr_replacer * mk_default_expr_replacer(ast_manager & m) {
return alloc(default_expr_replacer, m);
}
/**
\brief Adapter for using th_rewriter as an expr_replacer.
*/
class th_rewriter2expr_replacer : public expr_replacer {
th_rewriter m_r;
public:
th_rewriter2expr_replacer(ast_manager & m, params_ref const & p):
m_r(m, p) {
}
virtual ~th_rewriter2expr_replacer() {}
virtual ast_manager & m() const { return m_r.m(); }
virtual void set_substitution(expr_substitution * s) { m_r.set_substitution(s); }
virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) {
m_r(t, result, result_pr);
result_dep = m_r.get_used_dependencies();
m_r.reset_used_dependencies();
}
virtual void set_cancel(bool f) {
m_r.set_cancel(f);
}
virtual unsigned get_num_steps() const {
return m_r.get_num_steps();
}
};
expr_replacer * mk_expr_simp_replacer(ast_manager & m, params_ref const & p) {
return alloc(th_rewriter2expr_replacer, m, p);
}

View file

@ -0,0 +1,62 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
expr_replacer.h
Abstract:
Abstract (functor) for replacing expressions.
Author:
Leonardo (leonardo) 2011-04-29
Notes:
--*/
#ifndef _EXPR_REPLACER_H_
#define _EXPR_REPLACER_H_
#include"ast.h"
#include"expr_substitution.h"
#include"params.h"
/**
\brief Abstract interface for functors that replace constants with expressions.
*/
class expr_replacer {
struct scoped_set_subst;
public:
virtual ~expr_replacer() {}
virtual ast_manager & m() const = 0;
virtual void set_substitution(expr_substitution * s) = 0;
virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & deps) = 0;
virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
virtual void operator()(expr * t, expr_ref & result);
virtual void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); }
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
virtual void set_cancel(bool f) = 0;
virtual unsigned get_num_steps() const { return 0; }
void apply_substitution(expr * s, expr * def, proof * def_pr, expr_ref & t);
void apply_substitution(expr * s, expr * def, expr_ref & t);
};
/**
\brief Create a vanilla replacer. It just applies the substitution.
*/
expr_replacer * mk_default_expr_replacer(ast_manager & m);
/**
\brief Apply substitution and simplify.
*/
expr_replacer * mk_expr_simp_replacer(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,352 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
factor_rewriter.cpp
Abstract:
Rewriting utilities for factoring polynomials in equations,
and inequalities.
Author:
Nikolaj (nbjorner) 2011-19-05
Notes:
--*/
#include"factor_rewriter.h"
#include"ast_pp.h"
#include"rewriter_def.h"
factor_rewriter::factor_rewriter(ast_manager & m): m_manager(m), m_arith(m), m_factors(m) {
}
br_status factor_rewriter::mk_app_core(
func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (m().is_eq(f)) { SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); }
if(f->get_family_id() == a().get_family_id()) {
switch (f->get_decl_kind()) {
case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result);
case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result);
case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result);
case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result);
default: return BR_FAILED;
}
}
return BR_FAILED;
}
br_status factor_rewriter::mk_eq(expr * arg1, expr * arg2, expr_ref & result) {
if (!a().is_real(arg1) && !m_arith.is_int(arg1)) {
return BR_FAILED;
}
mk_adds(arg1, arg2);
mk_muls();
if (m_muls.empty()) {
result = m().mk_true();
return BR_DONE;
}
if (!extract_factors()) {
TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " = " << mk_pp(arg2, m()) << "\n";);
return BR_FAILED;
}
powers_t::iterator it = m_powers.begin(), end = m_powers.end();
expr_ref_vector eqs(m());
for(; it != end; ++it) {
expr* e = it->m_key;
eqs.push_back(m().mk_eq(e, a().mk_numeral(rational(0), m().get_sort(e))));
}
result = m().mk_or(eqs.size(), eqs.c_ptr());
return BR_DONE;
}
br_status factor_rewriter::mk_le(expr * arg1, expr * arg2, expr_ref & result) {
mk_adds(arg1, arg2);
mk_muls();
if (m_muls.empty()) {
result = m().mk_true();
return BR_DONE;
}
if (!extract_factors()) {
TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " <= " << mk_pp(arg2, m()) << "\n";);
return BR_FAILED;
}
// a^2 * b^3 * c <= 0 ->
// a = 0 \/ (b = 0 \/ b > 0 & c <= 0 \/ b < 0 & c >= 0)
//
expr_ref neg(m());
expr_ref_vector eqs(m());
mk_is_negative(neg, eqs);
eqs.push_back(neg);
result = m().mk_or(eqs.size(), eqs.c_ptr());
TRACE("factor_rewriter",
tout << mk_pp(arg1, m()) << " <= " << mk_pp(arg2, m()) << "\n";
tout << mk_pp(result.get(), m()) << "\n";);
return BR_DONE;
}
br_status factor_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
mk_adds(arg1, arg2);
mk_muls();
if (m_muls.empty()) {
result = m().mk_false();
return BR_DONE;
}
if (!extract_factors()) {
TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " < " << mk_pp(arg2, m()) << "\n";);
return BR_FAILED;
}
// a^2 * b^3 * c < 0 ->
// a != 0 /\ (b > 0 & c < 0 \/ b < 0 & c > 0)
//
expr_ref neg(m());
expr_ref_vector eqs(m());
mk_is_negative(neg, eqs);
for (unsigned i = 0; i < eqs.size(); ++i) {
eqs[i] = m().mk_not(eqs[i].get());
}
eqs.push_back(neg);
result = m().mk_and(eqs.size(), eqs.c_ptr());
TRACE("factor_rewriter", tout << mk_pp(result.get(), m()) << "\n";);
return BR_DONE;
}
void factor_rewriter::mk_is_negative(expr_ref& result, expr_ref_vector& eqs) {
powers_t::iterator it = m_powers.begin(), end = m_powers.end();
SASSERT(m_powers.size() >= 1);
SASSERT(it != end);
expr_ref neg0(m()), neg(m()), pos0(m()), pos(m()), tmp(m());
expr* e = it->m_key;
expr_ref zero(a().mk_numeral(rational(0), m().get_sort(e)), m());
expr_ref_vector conjs(m());
pos0 = m().mk_true();
neg0 = m().mk_false();
for(; it != end; ++it) {
e = it->m_key;
eqs.push_back(m().mk_eq(zero, e));
if (!even(it->m_value)) {
pos = a().mk_lt(zero, e);
neg = a().mk_lt(e, zero);
if (m().is_false(neg0)) {
neg0 = neg;
pos0 = pos;
}
else {
tmp = m().mk_or(m().mk_and(pos, pos0), m().mk_and(neg, neg0));
neg0 = m().mk_or(m().mk_and(neg, pos0), m().mk_and(pos, neg0));
pos0 = tmp;
}
}
}
result = neg0;
}
// convert arg1 - arg2 into
// sum of monomials
// m_adds: sum of products.
// m_muls: list of products
void factor_rewriter::mk_adds(expr* arg1, expr* arg2) {
m_adds.reset();
m_adds.push_back(std::make_pair(arg1, true));
m_adds.push_back(std::make_pair(arg2, false));
rational k;
for (unsigned i = 0; i < m_adds.size();) {
bool sign = m_adds[i].second;
expr* _e = m_adds[i].first;
TRACE("factor_rewriter", tout << i << " " << mk_pp(_e, m_manager) << "\n";);
if (!is_app(_e)) {
++i;
continue;
}
app* e = to_app(_e);
if (a().is_add(e) && e->get_num_args() > 0) {
m_adds[i].first = e->get_arg(0);
for (unsigned j = 1; j < e->get_num_args(); ++j) {
m_adds.push_back(std::make_pair(e->get_arg(j),sign));
}
}
else if (a().is_sub(e) && e->get_num_args() > 0) {
m_adds[i].first = e->get_arg(0);
for (unsigned j = 1; j < e->get_num_args(); ++j) {
m_adds.push_back(std::make_pair(e->get_arg(j),!sign));
}
}
else if (a().is_uminus(e)) {
m_adds[i].first = e->get_arg(0);
m_adds[i].second = !sign;
}
else if (a().is_numeral(e, k) && k.is_zero()) {
unsigned sz = m_adds.size();
m_adds[i] = m_adds[sz-1];
m_adds.resize(sz-1);
}
else {
++i;
}
}
TRACE("factor_rewriter",
for (unsigned i = 0; i < m_adds.size(); ++i) {
if (!m_adds[i].second) tout << "-"; else tout << "+";
tout << mk_pp(m_adds[i].first, m()) << " ";
}
tout << "\n";
);
}
void factor_rewriter::mk_muls() {
m_muls.reset();
for (unsigned i = 0; i < m_adds.size(); ++i) {
m_muls.push_back(ptr_vector<expr>());
m_muls.back().push_back(m_adds[i].first);
mk_expand_muls(m_muls.back());
if (m_muls.back().empty()) {
m_muls.pop_back();
m_adds.erase(m_adds.begin() + i);
--i;
}
}
TRACE("factor_rewriter",
for (unsigned i = 0; i < m_muls.size(); ++i) {
for (unsigned j = 0; j < m_muls[i].size(); ++j) {
tout << mk_pp(m_muls[i][j], m()) << " ";
}
tout << "\n";
}
tout << "\n";
);
}
void factor_rewriter::mk_expand_muls(ptr_vector<expr>& muls) {
for (unsigned i = 0; i < muls.size(); ) {
expr* _e = muls[i];
if (!is_app(_e)) {
++i;
continue;
}
app* e = to_app(_e);
if (a().is_mul(e) && e->get_num_args() > 0) {
muls[i] = e->get_arg(0);
for (unsigned j = 1; j < e->get_num_args(); ++j) {
muls.push_back(e->get_arg(j));
}
}
else {
++i;
}
}
}
bool factor_rewriter::extract_factors() {
m_factors.reset();
unsigned_vector pos;
expr* e;
SASSERT(!m_muls.empty());
if (m_muls.size() == 1) {
if (m_muls[0].size() > 1) {
m_factors.append(m_muls[0].size(), m_muls[0].c_ptr());
if (!m_adds[0].second) {
bool found_numeral = false;
sort* s = m().get_sort(m_muls[0][0]);
rational v;
for (unsigned i = 0; !found_numeral && i < m_factors.size(); ++i) {
if (a().is_numeral(m_factors[i].get(), v)) {
m_factors[i] = a().mk_numeral(-v, s);
found_numeral = true;
}
}
if (!found_numeral) {
m_factors.push_back(a().mk_numeral(rational(-1),s));
}
}
collect_powers();
return true;
}
return false;
}
for (unsigned i = 0; i < m_muls[0].size(); ++i) {
pos.reset();
pos.push_back(i);
e = m_muls[0][i];
bool ok = true;
for (unsigned j = 1; ok && j < m_muls.size(); ++j) {
ok = false;
unsigned k = 0;
for (k = 0; !ok && k < m_muls[j].size(); ++k) {
ok = m_muls[j][k] == e;
}
pos.push_back(k-1);
}
if (ok) {
SASSERT(pos.size() == m_muls.size());
m_factors.push_back(e);
for (unsigned j = 0; j < pos.size(); ++j) {
m_muls[j].erase(m_muls[j].begin() + pos[j]);
}
--i;
}
}
if (m_factors.empty()) {
return false;
}
SASSERT(m_muls.size() == m_adds.size());
expr_ref_vector trail(m());
sort* s = m().get_sort(m_factors[0].get());
for (unsigned i = 0; i < m_adds.size(); ++i) {
switch(m_muls[i].size()) {
case 0:
e = a().mk_numeral(rational(1), s);
break;
case 1:
e = m_muls[i][0];
break;
default:
e = a().mk_mul(m_muls[i].size(), m_muls[i].c_ptr());
break;
}
if (!m_adds[i].second) {
e = a().mk_uminus(e);
}
trail.push_back(e);
}
switch(trail.size()) {
case 0:
break;
case 1:
m_factors.push_back(trail[0].get());
break;
default:
m_factors.push_back(a().mk_add(trail.size(), trail.c_ptr()));
break;
}
TRACE("factor_rewriter",
for (unsigned i = 0; i < m_factors.size(); ++i) {
tout << mk_pp(m_factors[i].get(), m()) << " ";
}
tout << "\n";
);
collect_powers();
return true;
}
void factor_rewriter::collect_powers() {
m_powers.reset();
for (unsigned i = 0; i < m_factors.size(); ++i) {
obj_map<expr,unsigned>::obj_map_entry* entry = m_powers.insert_if_not_there2(m_factors[i].get(), 0);
if (entry) {
++(entry->get_data().m_value);
}
}
}
template class rewriter_tpl<factor_rewriter_cfg>;

View file

@ -0,0 +1,78 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
factor_rewriter.h
Abstract:
Rewriting utilities for factoring polynomials in equations,
and inequalities.
Author:
Nikolaj (nbjorner) 2011-19-05
Notes:
--*/
#ifndef _FACTOR_REWRITER_H_
#define _FACTOR_REWRITER_H_
#include"ast.h"
#include"rewriter.h"
#include"arith_decl_plugin.h"
class factor_rewriter {
typedef obj_map<expr,unsigned> powers_t;
ast_manager & m_manager;
arith_util m_arith;
powers_t m_powers;
vector<std::pair<expr*,bool> > m_adds;
vector<ptr_vector<expr> > m_muls;
expr_ref_vector m_factors;
public:
factor_rewriter(ast_manager & m);
ast_manager & m() const { return m_manager; }
arith_util & a() { return m_arith; }
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
private:
br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_le(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_ge(expr * a1, expr * a2, expr_ref & r) { return mk_le(a2,a1,r); }
br_status mk_gt(expr * a1, expr * a2, expr_ref & r) { return mk_lt(a2,a1,r); }
void mk_adds(expr* arg1, expr* arg2);
void mk_muls();
void mk_expand_muls(ptr_vector<expr>& muls);
void collect_powers();
bool extract_factors();
bool even(unsigned n) const { return 0 == (n & 0x1); }
void mk_is_negative(expr_ref& result, expr_ref_vector& eqs);
};
struct factor_rewriter_cfg : public default_rewriter_cfg {
factor_rewriter m_r;
bool rewrite_patterns() const { return false; }
bool flat_assoc(func_decl * f) const { return false; }
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = 0;
return m_r.mk_app_core(f, num, args, result);
}
factor_rewriter_cfg(ast_manager & m):m_r(m) {}
};
class factor_rewriter_star : public rewriter_tpl<factor_rewriter_cfg> {
factor_rewriter_cfg m_cfg;
public:
factor_rewriter_star(ast_manager & m):
rewriter_tpl<factor_rewriter_cfg>(m, false, m_cfg),
m_cfg(m) {}
};
#endif

View file

@ -0,0 +1,441 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
float_rewriter.cpp
Abstract:
Basic rewriting rules for floating point numbers.
Author:
Leonardo (leonardo) 2012-02-02
Notes:
--*/
#include"float_rewriter.h"
float_rewriter::float_rewriter(ast_manager & m, params_ref const & p):
m_util(m) {
updt_params(p);
}
float_rewriter::~float_rewriter() {
}
void float_rewriter::updt_params(params_ref const & p) {
}
void float_rewriter::get_param_descrs(param_descrs & r) {
}
br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
br_status st = BR_FAILED;
SASSERT(f->get_family_id() == get_fid());
switch (f->get_decl_kind()) {
case OP_TO_FLOAT: st = mk_to_float(f, num_args, args, result); break;
case OP_FLOAT_ADD: SASSERT(num_args == 3); st = mk_add(args[0], args[1], args[2], result); break;
case OP_FLOAT_SUB: SASSERT(num_args == 3); st = mk_sub(args[0], args[1], args[2], result); break;
case OP_FLOAT_UMINUS: SASSERT(num_args == 1); st = mk_uminus(args[0], result); break;
case OP_FLOAT_MUL: SASSERT(num_args == 3); st = mk_mul(args[0], args[1], args[2], result); break;
case OP_FLOAT_DIV: SASSERT(num_args == 3); st = mk_div(args[0], args[1], args[2], result); break;
case OP_FLOAT_REM: SASSERT(num_args == 2); st = mk_rem(args[0], args[1], result); break;
case OP_FLOAT_ABS: SASSERT(num_args == 1); st = mk_abs(args[0], result); break;
case OP_FLOAT_MIN: SASSERT(num_args == 2); st = mk_min(args[0], args[1], result); break;
case OP_FLOAT_MAX: SASSERT(num_args == 2); st = mk_max(args[0], args[1], result); break;
case OP_FLOAT_FUSED_MA: SASSERT(num_args == 4); st = mk_fused_ma(args[0], args[1], args[2], args[3], result); break;
case OP_FLOAT_SQRT: SASSERT(num_args == 2); st = mk_sqrt(args[0], args[1], result); break;
case OP_FLOAT_ROUND_TO_INTEGRAL: SASSERT(num_args == 2); st = mk_round(args[0], args[1], result); break;
case OP_FLOAT_EQ: SASSERT(num_args == 2); st = mk_float_eq(args[0], args[1], result); break;
case OP_FLOAT_LT: SASSERT(num_args == 2); st = mk_lt(args[0], args[1], result); break;
case OP_FLOAT_GT: SASSERT(num_args == 2); st = mk_gt(args[0], args[1], result); break;
case OP_FLOAT_LE: SASSERT(num_args == 2); st = mk_le(args[0], args[1], result); break;
case OP_FLOAT_GE: SASSERT(num_args == 2); st = mk_ge(args[0], args[1], result); break;
case OP_FLOAT_IS_ZERO: SASSERT(num_args == 1); st = mk_is_zero(args[0], result); break;
case OP_FLOAT_IS_NZERO: SASSERT(num_args == 1); st = mk_is_nzero(args[0], result); break;
case OP_FLOAT_IS_PZERO: SASSERT(num_args == 1); st = mk_is_pzero(args[0], result); break;
case OP_FLOAT_IS_SIGN_MINUS: SASSERT(num_args == 1); st = mk_is_sign_minus(args[0], result); break;
}
return st;
}
br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(f->get_num_parameters() == 2);
SASSERT(f->get_parameter(0).is_int());
SASSERT(f->get_parameter(1).is_int());
unsigned ebits = f->get_parameter(0).get_int();
unsigned sbits = f->get_parameter(1).get_int();
if (num_args == 2) {
mpf_rounding_mode rm;
if (!m_util.is_rm(args[0], rm))
return BR_FAILED;
rational q;
if (!m_util.au().is_numeral(args[1], q))
return BR_FAILED;
mpf v;
m_util.fm().set(v, ebits, sbits, rm, q.to_mpq());
result = m_util.mk_value(v);
m_util.fm().del(v);
return BR_DONE;
}
else if (num_args == 3 &&
m_util.is_rm(m().get_sort(args[0])) &&
m_util.au().is_real(args[1]) &&
m_util.au().is_int(args[2])) {
mpf_rounding_mode rm;
if (!m_util.is_rm(args[0], rm))
return BR_FAILED;
rational q;
if (!m_util.au().is_numeral(args[1], q))
return BR_FAILED;
rational e;
if (!m_util.au().is_numeral(args[2], e))
return BR_FAILED;
TRACE("fp_rewriter", tout << "q: " << q << ", e: " << e << "\n";);
mpf v;
m_util.fm().set(v, ebits, sbits, rm, q.to_mpq(), e.to_mpq().numerator());
result = m_util.mk_value(v);
m_util.fm().del(v);
return BR_DONE;
}
else {
return BR_FAILED;
}
}
br_status float_rewriter::mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
mpf_rounding_mode rm;
if (m_util.is_rm(arg1, rm)) {
scoped_mpf v2(m_util.fm()), v3(m_util.fm());
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) {
scoped_mpf t(m_util.fm());
m_util.fm().add(rm, v2, v3, t);
result = m_util.mk_value(t);
return BR_DONE;
}
}
return BR_FAILED;
}
br_status float_rewriter::mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
// a - b = a + (-b)
result = m_util.mk_add(arg1, arg2, m_util.mk_uminus(arg3));
return BR_REWRITE2;
}
br_status float_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
mpf_rounding_mode rm;
if (m_util.is_rm(arg1, rm)) {
scoped_mpf v2(m_util.fm()), v3(m_util.fm());
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) {
scoped_mpf t(m_util.fm());
m_util.fm().mul(rm, v2, v3, t);
result = m_util.mk_value(t);
return BR_DONE;
}
}
return BR_FAILED;
}
br_status float_rewriter::mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
mpf_rounding_mode rm;
if (m_util.is_rm(arg1, rm)) {
scoped_mpf v2(m_util.fm()), v3(m_util.fm());
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) {
scoped_mpf t(m_util.fm());
m_util.fm().div(rm, v2, v3, t);
result = m_util.mk_value(t);
return BR_DONE;
}
}
return BR_FAILED;
}
br_status float_rewriter::mk_uminus(expr * arg1, expr_ref & result) {
if (m_util.is_nan(arg1)) {
// -nan --> nan
result = arg1;
return BR_DONE;
}
if (m_util.is_plus_inf(arg1)) {
// - +oo --> -oo
result = m_util.mk_minus_inf(m().get_sort(arg1));
return BR_DONE;
}
if (m_util.is_minus_inf(arg1)) {
// - -oo -> +oo
result = m_util.mk_plus_inf(m().get_sort(arg1));
return BR_DONE;
}
if (m_util.is_uminus(arg1)) {
// - - a --> a
result = to_app(arg1)->get_arg(0);
return BR_DONE;
}
scoped_mpf v1(m_util.fm());
if (m_util.is_value(arg1, v1)) {
m_util.fm().neg(v1);
result = m_util.mk_value(v1);
return BR_DONE;
}
// TODO: more simplifications
return BR_FAILED;
}
br_status float_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
scoped_mpf t(m_util.fm());
m_util.fm().rem(v1, v2, t);
result = m_util.mk_value(t);
return BR_DONE;
}
return BR_FAILED;
}
br_status float_rewriter::mk_abs(expr * arg1, expr_ref & result) {
if (m_util.is_nan(arg1)) {
result = arg1;
return BR_DONE;
}
sort * s = m().get_sort(arg1);
result = m().mk_ite(m_util.mk_lt(arg1, m_util.mk_pzero(s)),
m_util.mk_uminus(arg1),
arg1);
return BR_REWRITE2;
}
br_status float_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) {
if (m_util.is_nan(arg1)) {
result = arg2;
return BR_DONE;
}
if (m_util.is_nan(arg2)) {
result = arg1;
return BR_DONE;
}
// expand as using ite's
result = m().mk_ite(mk_eq_nan(arg1),
arg2,
m().mk_ite(mk_eq_nan(arg2),
arg1,
m().mk_ite(m_util.mk_lt(arg1, arg2),
arg1,
arg2)));
return BR_REWRITE_FULL;
}
br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) {
if (m_util.is_nan(arg1)) {
result = arg2;
return BR_DONE;
}
if (m_util.is_nan(arg2)) {
result = arg1;
return BR_DONE;
}
// expand as using ite's
result = m().mk_ite(mk_eq_nan(arg1),
arg2,
m().mk_ite(mk_eq_nan(arg2),
arg1,
m().mk_ite(m_util.mk_gt(arg1, arg2),
arg1,
arg2)));
return BR_REWRITE_FULL;
}
br_status float_rewriter::mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) {
mpf_rounding_mode rm;
if (m_util.is_rm(arg1, rm)) {
scoped_mpf v2(m_util.fm()), v3(m_util.fm()), v4(m_util.fm());
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3) && m_util.is_value(arg4, v4)) {
scoped_mpf t(m_util.fm());
m_util.fm().fused_mul_add(rm, v2, v3, v4, t);
result = m_util.mk_value(t);
return BR_DONE;
}
}
return BR_FAILED;
}
br_status float_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) {
mpf_rounding_mode rm;
if (m_util.is_rm(arg1, rm)) {
scoped_mpf v2(m_util.fm());
if (m_util.is_value(arg2, v2)) {
scoped_mpf t(m_util.fm());
m_util.fm().sqrt(rm, v2, t);
result = m_util.mk_value(t);
return BR_DONE;
}
}
return BR_FAILED;
}
br_status float_rewriter::mk_round(expr * arg1, expr * arg2, expr_ref & result) {
mpf_rounding_mode rm;
if (m_util.is_rm(arg1, rm)) {
scoped_mpf v2(m_util.fm());
if (m_util.is_value(arg2, v2)) {
scoped_mpf t(m_util.fm());
m_util.fm().round_to_integral(rm, v2, t);
result = m_util.mk_value(t);
return BR_DONE;
}
}
return BR_FAILED;
}
// This the floating point theory ==
br_status float_rewriter::mk_float_eq(expr * arg1, expr * arg2, expr_ref & result) {
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
result = (m_util.fm().eq(v1, v2)) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
// Return (= arg NaN)
app * float_rewriter::mk_eq_nan(expr * arg) {
return m().mk_eq(arg, m_util.mk_nan(m().get_sort(arg)));
}
// Return (not (= arg NaN))
app * float_rewriter::mk_neq_nan(expr * arg) {
return m().mk_not(mk_eq_nan(arg));
}
br_status float_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) {
result = m().mk_false();
return BR_DONE;
}
if (m_util.is_minus_inf(arg1)) {
// -oo < arg2 --> not(arg2 = -oo) and not(arg2 = NaN)
result = m().mk_and(m().mk_not(m().mk_eq(arg2, arg1)), mk_neq_nan(arg2));
return BR_REWRITE2;
}
if (m_util.is_minus_inf(arg2)) {
// arg1 < -oo --> false
result = m().mk_false();
return BR_DONE;
}
if (m_util.is_plus_inf(arg1)) {
// +oo < arg2 --> false
result = m().mk_false();
return BR_DONE;
}
if (m_util.is_plus_inf(arg2)) {
// arg1 < +oo --> not(arg1 = +oo) and not(arg1 = NaN)
result = m().mk_and(m().mk_not(m().mk_eq(arg1, arg2)), mk_neq_nan(arg1));
return BR_REWRITE2;
}
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
result = (m_util.fm().lt(v1, v2)) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
// TODO: more simplifications
return BR_FAILED;
}
br_status float_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
result = m_util.mk_lt(arg2, arg1);
return BR_REWRITE1;
}
br_status float_rewriter::mk_le(expr * arg1, expr * arg2, expr_ref & result) {
if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) {
result = m().mk_false();
return BR_DONE;
}
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
result = (m_util.fm().le(v1, v2)) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
br_status float_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
result = m_util.mk_le(arg2, arg1);
return BR_REWRITE1;
}
br_status float_rewriter::mk_is_zero(expr * arg1, expr_ref & result) {
scoped_mpf v(m_util.fm());
if (m_util.is_value(arg1, v)) {
result = (m_util.fm().is_zero(v)) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
br_status float_rewriter::mk_is_nzero(expr * arg1, expr_ref & result) {
scoped_mpf v(m_util.fm());
if (m_util.is_value(arg1, v)) {
result = (m_util.fm().is_nzero(v)) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
br_status float_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) {
scoped_mpf v(m_util.fm());
if (m_util.is_value(arg1, v)) {
result = (m_util.fm().is_pzero(v)) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) {
scoped_mpf v(m_util.fm());
if (m_util.is_value(arg1, v)) {
result = (m_util.fm().is_neg(v)) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
// This the SMT =
br_status float_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
result = (v1 == v2) ? m().mk_true() : m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}

View file

@ -0,0 +1,72 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
float_rewriter.h
Abstract:
Basic rewriting rules for floating point numbers.
Author:
Leonardo (leonardo) 2012-02-02
Notes:
--*/
#ifndef _FLOAT_REWRITER_H_
#define _FLOAT_REWRITER_H_
#include"ast.h"
#include"rewriter.h"
#include"params.h"
#include"float_decl_plugin.h"
#include"mpf.h"
class float_rewriter {
float_util m_util;
mpf_manager m_fm;
app * mk_eq_nan(expr * arg);
app * mk_neq_nan(expr * arg);
public:
float_rewriter(ast_manager & m, params_ref const & p = params_ref());
~float_rewriter();
ast_manager & m() const { return m_util.m(); }
family_id get_fid() const { return m_util.get_fid(); }
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_to_float(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
br_status mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
br_status mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
br_status mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
br_status mk_uminus(expr * arg1, expr_ref & result);
br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_abs(expr * arg1, expr_ref & result);
br_status mk_min(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_max(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result);
br_status mk_sqrt(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_round(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_float_eq(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_le(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_is_zero(expr * arg1, expr_ref & result);
br_status mk_is_nzero(expr * arg1, expr_ref & result);
br_status mk_is_pzero(expr * arg1, expr_ref & result);
br_status mk_is_sign_minus(expr * arg1, expr_ref & result);
};
#endif

View file

@ -0,0 +1,105 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
mk_simplified_app.cpp
Abstract:
Functor for creating new simplified applications
Author:
Leonardo (leonardo) 2011-06-06
Notes:
--*/
#include"mk_simplified_app.h"
#include"bool_rewriter.h"
#include"arith_rewriter.h"
#include"bv_rewriter.h"
#include"datatype_rewriter.h"
#include"array_rewriter.h"
#include"float_rewriter.h"
struct mk_simplified_app::imp {
ast_manager & m;
bool_rewriter m_b_rw;
arith_rewriter m_a_rw;
bv_rewriter m_bv_rw;
array_rewriter m_ar_rw;
datatype_rewriter m_dt_rw;
float_rewriter m_f_rw;
imp(ast_manager & _m, params_ref const & p):
m(_m),
m_b_rw(m, p),
m_a_rw(m, p),
m_bv_rw(m, p),
m_ar_rw(m, p),
m_dt_rw(m),
m_f_rw(m, p) {
}
br_status mk_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
family_id fid = f->get_family_id();
if (fid == null_family_id)
return BR_FAILED;
br_status st = BR_FAILED;
if (fid == m_b_rw.get_fid()) {
decl_kind k = f->get_decl_kind();
if (k == OP_EQ) {
// theory dispatch for =
SASSERT(num == 2);
family_id s_fid = m.get_sort(args[0])->get_family_id();
if (s_fid == m_a_rw.get_fid())
st = m_a_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_bv_rw.get_fid())
st = m_bv_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_dt_rw.get_fid())
st = m_dt_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_f_rw.get_fid())
st = m_f_rw.mk_eq_core(args[0], args[1], result);
if (st != BR_FAILED)
return st;
}
return m_b_rw.mk_app_core(f, num, args, result);
}
if (fid == m_a_rw.get_fid())
return m_a_rw.mk_app_core(f, num, args, result);
if (fid == m_bv_rw.get_fid())
return m_bv_rw.mk_app_core(f, num, args, result);
if (fid == m_ar_rw.get_fid())
return m_ar_rw.mk_app_core(f, num, args, result);
if (fid == m_dt_rw.get_fid())
return m_dt_rw.mk_app_core(f, num, args, result);
if (fid == m_f_rw.get_fid())
return m_f_rw.mk_app_core(f, num, args, result);
return BR_FAILED;
}
};
mk_simplified_app::mk_simplified_app(ast_manager & m, params_ref const & p):
m_imp(alloc(imp, m, p)) {
}
mk_simplified_app::~mk_simplified_app() {
dealloc(m_imp);
}
br_status mk_simplified_app::mk_core(func_decl * decl, unsigned num, expr * const * args, expr_ref & result) {
return m_imp->mk_core(decl, num, args, result);
}
void mk_simplified_app::operator()(func_decl * decl, unsigned num, expr * const * args, expr_ref & result) {
result = 0;
mk_core(decl, num, args, result);
if (!result)
result = m_imp->m.mk_app(decl, num, args);
// TODO: if the result of mk_core is different from BR_FAILED, then the
// result is not really simplified.
}

View file

@ -0,0 +1,37 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
mk_simplified_app.h
Abstract:
Functor for creating new simplified applications
Author:
Leonardo (leonardo) 2011-06-06
Notes:
--*/
#ifndef _MK_SIMPLIFIED_APP_H_
#define _MK_SIMPLIFIED_APP_H_
#include"ast.h"
#include"params.h"
#include"rewriter_types.h"
class mk_simplified_app {
struct imp;
imp * m_imp;
public:
mk_simplified_app(ast_manager & m, params_ref const & p = params_ref());
~mk_simplified_app();
br_status mk_core(func_decl * decl, unsigned num, expr * const * args, expr_ref & result);
void operator()(func_decl * decl, unsigned num, expr * const * args, expr_ref & result);
};
#endif

View file

@ -0,0 +1,167 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
poly_rewriter.h
Abstract:
Basic rewriting rules for Polynomials.
Author:
Leonardo (leonardo) 2011-04-08
Notes:
--*/
#ifndef _POLY_REWRITER_H_
#define _POLY_REWRITER_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"rewriter_types.h"
#include"params.h"
template<typename Config>
class poly_rewriter : public Config {
public:
static char const * g_ste_blowup_msg;
protected:
typedef typename Config::numeral numeral;
sort * m_curr_sort;
obj_map<expr, unsigned> m_expr2pos;
bool m_flat;
bool m_som;
unsigned m_som_blowup;
bool m_sort_sums;
bool m_hoist_mul;
bool m_hoist_cmul;
bool is_numeral(expr * n) const { return Config::is_numeral(n); }
bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); }
bool is_zero(expr * n) const { return Config::is_zero(n); }
bool is_minus_one(expr * n) const { return Config::is_minus_one(n); }
void normalize(numeral & c) { Config::normalize(c, m_curr_sort); }
app * mk_numeral(numeral const & r) { return Config::mk_numeral(r, m_curr_sort); }
decl_kind add_decl_kind() const { return Config::add_decl_kind(); }
decl_kind mul_decl_kind() const { return Config::mul_decl_kind(); }
bool use_power() const { return Config::use_power(); }
decl_kind power_decl_kind() const { return Config::power_decl_kind(); }
bool is_power(expr * t) const { return is_app_of(t, get_fid(), power_decl_kind()); }
expr * get_power_body(expr * t, rational & k);
struct mon_pw_lt; // functor used to sort monomial elements when use_power() == true
expr * mk_mul_app(unsigned num_args, expr * const * args);
expr * mk_mul_app(numeral const & c, expr * arg);
expr * mk_add_app(unsigned num_args, expr * const * args);
br_status mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result);
expr * get_power_product(expr * t);
expr * get_power_product(expr * t, numeral & a);
br_status mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result);
void set_curr_sort(sort * s) { m_curr_sort = s; }
expr * const * get_monomials(expr * & t, unsigned & sz) {
if (is_add(t)) {
sz = to_app(t)->get_num_args();
return to_app(t)->get_args();
}
else {
sz = 1;
return &t;
}
}
br_status cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result);
bool hoist_multiplication(expr_ref& som);
expr* merge_muls(expr* x, expr* y);
struct hoist_cmul_lt;
bool is_mul(expr * t, numeral & c, expr * & pp);
void hoist_cmul(expr_ref_buffer & args);
public:
poly_rewriter(ast_manager & m, params_ref const & p = params_ref()):
Config(m),
m_curr_sort(0),
m_sort_sums(false) {
updt_params(p);
SASSERT(!m_som || m_flat); // som of monomials form requires flattening to be enabled.
SASSERT(!m_som || !m_hoist_mul); // som is mutually exclusive with hoisting multiplication.
updt_params(p);
}
ast_manager & m() const { return Config::m(); }
family_id get_fid() const { return Config::get_fid(); }
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
void set_flat(bool f) { m_flat = f; }
void set_sort_sums(bool f) { m_sort_sums = f; }
bool is_add(expr * n) const { return is_app_of(n, get_fid(), add_decl_kind()); }
bool is_mul(expr * n) const { return is_app_of(n, get_fid(), mul_decl_kind()); }
bool is_add(func_decl * f) const { return is_decl_of(f, get_fid(), add_decl_kind()); }
bool is_mul(func_decl * f) const { return is_decl_of(f, get_fid(), mul_decl_kind()); }
br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
if (num_args == 1) {
result = args[0];
return BR_DONE;
}
set_curr_sort(m().get_sort(args[0]));
return m_flat ?
mk_flat_mul_core(num_args, args, result) :
mk_nflat_mul_core(num_args, args, result);
}
br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
if (num_args == 1) {
result = args[0];
return BR_DONE;
}
set_curr_sort(m().get_sort(args[0]));
return m_flat ?
mk_flat_add_core(num_args, args, result) :
mk_nflat_add_core(num_args, args, result);
}
void mk_add(unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_add_core(num_args, args, result) == BR_FAILED)
result = mk_add_app(num_args, args);
}
void mk_add(expr* a1, expr* a2, expr_ref& result) {
expr* args[2] = { a1, a2 };
mk_add(2, args, result);
}
void mk_mul(unsigned num_args, expr * const * args, expr_ref & result) {
if (mk_mul_core(num_args, args, result) == BR_FAILED)
result = mk_mul_app(num_args, args);
}
void mk_mul(expr* a1, expr* a2, expr_ref& result) {
expr* args[2] = { a1, a2 };
mk_mul(2, args, result);
}
// The result of the following functions is never BR_FAILED
br_status mk_uminus(expr * arg, expr_ref & result);
br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
void mk_sub(expr* a1, expr* a2, expr_ref& result) {
expr* args[2] = { a1, a2 };
mk_sub(2, args, result);
}
};
#endif

View file

@ -0,0 +1,933 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
poly_rewriter_def.h
Abstract:
Basic rewriting rules for Polynomials.
Author:
Leonardo (leonardo) 2011-04-08
Notes:
--*/
#include"poly_rewriter.h"
#include"ast_lt.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
template<typename Config>
char const * poly_rewriter<Config>::g_ste_blowup_msg = "sum of monomials blowup";
template<typename Config>
void poly_rewriter<Config>::updt_params(params_ref const & p) {
m_flat = p.get_bool(":flat", true);
m_som = p.get_bool(":som", false);
m_hoist_mul = p.get_bool(":hoist-mul", false);
m_hoist_cmul = p.get_bool(":hoist-cmul", false);
m_som_blowup = p.get_uint(":som-blowup", UINT_MAX);
}
template<typename Config>
void poly_rewriter<Config>::get_param_descrs(param_descrs & r) {
r.insert(":som", CPK_BOOL, "(default: false) put polynomials in som-of-monomials form.");
r.insert(":som-blowup", CPK_UINT, "(default: infty) maximum number of monomials generated when putting a polynomial in sum-of-monomials normal form");
r.insert(":hoist-mul", CPK_BOOL, "(default: false) hoist multiplication over summation to minimize number of multiplications");
r.insert(":hoist-cmul", CPK_BOOL, "(default: false) hoist constant multiplication over summation to minimize number of multiplications");
}
template<typename Config>
expr * poly_rewriter<Config>::mk_add_app(unsigned num_args, expr * const * args) {
switch (num_args) {
case 0: return mk_numeral(numeral(0));
case 1: return args[0];
default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args);
}
}
// t = (^ x y) --> return x, and set k = y if k is an integer >= 1
// Otherwise return t and set k = 1
template<typename Config>
expr * poly_rewriter<Config>::get_power_body(expr * t, rational & k) {
if (!is_power(t)) {
k = rational(1);
return t;
}
if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) {
return to_app(t)->get_arg(0);
}
k = rational(1);
return t;
}
template<typename Config>
expr * poly_rewriter<Config>::mk_mul_app(unsigned num_args, expr * const * args) {
switch (num_args) {
case 0:
return mk_numeral(numeral(1));
case 1:
return args[0];
default:
if (use_power()) {
rational k_prev;
expr * prev = get_power_body(args[0], k_prev);
rational k;
ptr_buffer<expr> new_args;
#define PUSH_POWER() { \
if (k_prev.is_one()) { \
new_args.push_back(prev); \
} \
else { \
expr * pargs[2] = { prev, mk_numeral(k_prev) }; \
new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \
} \
}
for (unsigned i = 1; i < num_args; i++) {
expr * arg = get_power_body(args[i], k);
if (arg == prev) {
k_prev += k;
}
else {
PUSH_POWER();
prev = arg;
k_prev = k;
}
}
PUSH_POWER();
SASSERT(new_args.size() > 0);
if (new_args.size() == 1) {
return new_args[0];
}
else {
return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr());
}
}
else {
return m().mk_app(get_fid(), mul_decl_kind(), num_args, args);
}
}
}
template<typename Config>
expr * poly_rewriter<Config>::mk_mul_app(numeral const & c, expr * arg) {
if (c.is_one()) {
return arg;
}
else {
expr * new_args[2] = { mk_numeral(c), arg };
return mk_mul_app(2, new_args);
}
}
template<typename Config>
br_status poly_rewriter<Config>::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 2);
// only try to apply flattening if it is not already in one of the flat monomial forms
// - (* c x)
// - (* c (* x_1 ... x_n))
if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) {
unsigned i;
for (i = 0; i < num_args; i++) {
if (is_mul(args[i]))
break;
}
if (i < num_args) {
// input has nested monomials.
ptr_buffer<expr> flat_args;
// we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n)))
ptr_buffer<expr> todo;
flat_args.append(i, args);
for (unsigned j = i; j < num_args; j++) {
if (is_mul(args[j])) {
todo.push_back(args[j]);
while (!todo.empty()) {
expr * curr = todo.back();
todo.pop_back();
if (is_mul(curr)) {
unsigned k = to_app(curr)->get_num_args();
while (k > 0) {
--k;
todo.push_back(to_app(curr)->get_arg(k));
}
}
else {
flat_args.push_back(curr);
}
}
}
else {
flat_args.push_back(args[j]);
}
}
TRACE("poly_rewriter",
tout << "flat mul:\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n";
tout << "---->\n";
for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";);
br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result);
if (st == BR_FAILED) {
result = mk_mul_app(flat_args.size(), flat_args.c_ptr());
return BR_DONE;
}
return st;
}
}
return mk_nflat_mul_core(num_args, args, result);
}
template<typename Config>
struct poly_rewriter<Config>::mon_pw_lt {
poly_rewriter<Config> & m_owner;
mon_pw_lt(poly_rewriter<Config> & o):m_owner(o) {}
bool operator()(expr * n1, expr * n2) const {
rational k;
return lt(m_owner.get_power_body(n1, k),
m_owner.get_power_body(n2, k));
}
};
template<typename Config>
br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 2);
// cheap case
numeral a;
if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() &&
(is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid()))
return BR_FAILED;
numeral c(1);
unsigned num_coeffs = 0;
unsigned num_add = 0;
expr * var = 0;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (is_numeral(arg, a)) {
num_coeffs++;
c *= a;
}
else {
var = arg;
if (is_add(arg))
num_add++;
}
}
normalize(c);
// (* c_1 ... c_n) --> c_1*...*c_n
if (num_coeffs == num_args) {
result = mk_numeral(c);
return BR_DONE;
}
// (* s ... 0 ... r) --> 0
if (c.is_zero()) {
result = mk_numeral(c);
return BR_DONE;
}
if (num_coeffs == num_args - 1) {
SASSERT(var != 0);
// (* c_1 ... c_n x) --> x if c_1*...*c_n == 1
if (c.is_one()) {
result = var;
return BR_DONE;
}
numeral c_prime;
if (is_mul(var)) {
// apply basic simplification even when flattening is not enabled.
// (* c1 (* c2 x)) --> (* c1*c2 x)
if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) {
c *= c_prime;
normalize(c);
result = mk_mul_app(c, to_app(var)->get_arg(1));
return BR_REWRITE1;
}
else {
// var is a power-product
return BR_FAILED;
}
}
if (num_add == 0 || m_hoist_cmul) {
SASSERT(!is_add(var) || m_hoist_cmul);
if (num_args == 2 && args[1] == var) {
DEBUG_CODE({
numeral c_prime;
SASSERT(is_numeral(args[0], c_prime) && c == c_prime);
});
// it is already simplified
return BR_FAILED;
}
// (* c_1 ... c_n x) --> (* c_1*...*c_n x)
result = mk_mul_app(c, var);
return BR_DONE;
}
else {
SASSERT(is_add(var));
// (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m))
ptr_buffer<expr> new_add_args;
unsigned num = to_app(var)->get_num_args();
for (unsigned i = 0; i < num; i++) {
new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i)));
}
result = mk_add_app(new_add_args.size(), new_add_args.c_ptr());
TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";);
return BR_REWRITE2;
}
}
SASSERT(num_coeffs <= num_args - 2);
if (!m_som || num_add == 0) {
ptr_buffer<expr> new_args;
expr * prev = 0;
bool ordered = true;
for (unsigned i = 0; i < num_args; i++) {
expr * curr = args[i];
if (is_numeral(curr))
continue;
if (prev != 0 && lt(curr, prev))
ordered = false;
new_args.push_back(curr);
prev = curr;
}
TRACE("poly_rewriter",
for (unsigned i = 0; i < new_args.size(); i++) {
if (i > 0)
tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< ");
tout << mk_ismt2_pp(new_args[i], m());
}
tout << "\nordered: " << ordered << "\n";);
if (ordered && num_coeffs == 0 && !use_power())
return BR_FAILED;
if (!ordered) {
if (use_power())
std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this));
else
std::sort(new_args.begin(), new_args.end(), ast_to_lt());
TRACE("poly_rewriter",
tout << "after sorting:\n";
for (unsigned i = 0; i < new_args.size(); i++) {
if (i > 0)
tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< ");
tout << mk_ismt2_pp(new_args[i], m());
}
tout << "\n";);
}
SASSERT(new_args.size() >= 2);
result = mk_mul_app(new_args.size(), new_args.c_ptr());
result = mk_mul_app(c, result);
TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";);
return BR_DONE;
}
SASSERT(m_som && num_add > 0);
sbuffer<unsigned> szs;
sbuffer<unsigned> it;
sbuffer<expr **> sums;
for (unsigned i = 0; i < num_args; i ++) {
it.push_back(0);
expr * arg = args[i];
if (is_add(arg)) {
sums.push_back(const_cast<expr**>(to_app(arg)->get_args()));
szs.push_back(to_app(arg)->get_num_args());
}
else {
sums.push_back(const_cast<expr**>(args + i));
szs.push_back(1);
SASSERT(sums.back()[0] == arg);
}
}
expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception
ptr_buffer<expr> m_args;
TRACE("som", tout << "starting som...\n";);
do {
TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " ";
tout << "\n";);
if (sum.size() > m_som_blowup)
throw rewriter_exception(g_ste_blowup_msg);
m_args.reset();
for (unsigned i = 0; i < num_args; i++) {
expr * const * v = sums[i];
expr * arg = v[it[i]];
m_args.push_back(arg);
}
sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr()));
}
while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr()));
result = mk_add_app(sum.size(), sum.c_ptr());
return BR_REWRITE2;
}
template<typename Config>
br_status poly_rewriter<Config>::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
unsigned i;
for (i = 0; i < num_args; i++) {
if (is_add(args[i]))
break;
}
if (i < num_args) {
// has nested ADDs
ptr_buffer<expr> flat_args;
flat_args.append(i, args);
for (; i < num_args; i++) {
expr * arg = args[i];
// Remark: all rewrites are depth 1.
if (is_add(arg)) {
unsigned num = to_app(arg)->get_num_args();
for (unsigned j = 0; j < num; j++)
flat_args.push_back(to_app(arg)->get_arg(j));
}
else {
flat_args.push_back(arg);
}
}
br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result);
if (st == BR_FAILED) {
result = mk_add_app(flat_args.size(), flat_args.c_ptr());
return BR_DONE;
}
return st;
}
return mk_nflat_add_core(num_args, args, result);
}
template<typename Config>
inline expr * poly_rewriter<Config>::get_power_product(expr * t) {
if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0)))
return to_app(t)->get_arg(1);
return t;
}
template<typename Config>
inline expr * poly_rewriter<Config>::get_power_product(expr * t, numeral & a) {
if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a))
return to_app(t)->get_arg(1);
a = numeral(1);
return t;
}
template<typename Config>
bool poly_rewriter<Config>::is_mul(expr * t, numeral & c, expr * & pp) {
if (!is_mul(t) || to_app(t)->get_num_args() != 2)
return false;
if (!is_numeral(to_app(t)->get_arg(0), c))
return false;
pp = to_app(t)->get_arg(1);
return true;
}
template<typename Config>
struct poly_rewriter<Config>::hoist_cmul_lt {
poly_rewriter<Config> & m_r;
hoist_cmul_lt(poly_rewriter<Config> & r):m_r(r) {}
bool operator()(expr * t1, expr * t2) const {
expr * pp1, * pp2;
numeral c1, c2;
bool is_mul1 = m_r.is_mul(t1, c1, pp1);
bool is_mul2 = m_r.is_mul(t2, c2, pp2);
if (!is_mul1 && is_mul2)
return true;
if (is_mul1 && !is_mul2)
return false;
if (!is_mul1 && !is_mul2)
return t1->get_id() < t2->get_id();
if (c1 < c2)
return true;
if (c1 > c2)
return false;
return pp1->get_id() < pp2->get_id();
}
};
template<typename Config>
void poly_rewriter<Config>::hoist_cmul(expr_ref_buffer & args) {
unsigned sz = args.size();
std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this));
numeral c, c_prime;
ptr_buffer<expr> pps;
expr * pp, * pp_prime;
unsigned j = 0;
unsigned i = 0;
while (i < sz) {
expr * mon = args[i];
if (is_mul(mon, c, pp) && i < sz - 1) {
expr * mon_prime = args[i+1];
if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) {
// found target
pps.reset();
pps.push_back(pp);
pps.push_back(pp_prime);
i += 2;
while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) {
pps.push_back(pp_prime);
i++;
}
SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime);
expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) };
args.set(j, mk_mul_app(2, mul_args));
j++;
continue;
}
}
args.set(j, mon);
j++;
i++;
}
args.resize(j);
}
template<typename Config>
br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 2);
numeral c;
unsigned num_coeffs = 0;
numeral a;
expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args
expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once
bool has_multiple = false;
expr * prev = 0;
bool ordered = true;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (is_numeral(arg, a)) {
num_coeffs++;
c += a;
}
else {
// arg is not a numeral
if (m_sort_sums && ordered) {
if (prev != 0 && lt(arg, prev))
ordered = false;
prev = arg;
}
}
arg = get_power_product(arg);
if (visited.is_marked(arg)) {
multiple.mark(arg);
has_multiple = true;
}
else {
visited.mark(arg);
}
}
normalize(c);
SASSERT(m_sort_sums || ordered);
TRACE("sort_sums",
tout << "ordered: " << ordered << "\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";);
if (has_multiple) {
// expensive case
buffer<numeral> coeffs;
m_expr2pos.reset();
// compute the coefficient of power products that occur multiple times.
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (is_numeral(arg))
continue;
expr * pp = get_power_product(arg, a);
if (!multiple.is_marked(pp))
continue;
unsigned pos;
if (m_expr2pos.find(pp, pos)) {
coeffs[pos] += a;
}
else {
m_expr2pos.insert(pp, coeffs.size());
coeffs.push_back(a);
}
}
expr_ref_buffer new_args(m());
if (!c.is_zero()) {
new_args.push_back(mk_numeral(c));
}
// copy power products with non zero coefficients to new_args
visited.reset();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (is_numeral(arg))
continue;
expr * pp = get_power_product(arg);
if (!multiple.is_marked(pp)) {
new_args.push_back(arg);
}
else if (!visited.is_marked(pp)) {
visited.mark(pp);
unsigned pos = UINT_MAX;
m_expr2pos.find(pp, pos);
SASSERT(pos != UINT_MAX);
a = coeffs[pos];
normalize(a);
if (!a.is_zero())
new_args.push_back(mk_mul_app(a, pp));
}
}
if (m_hoist_cmul) {
hoist_cmul(new_args);
}
else if (m_sort_sums) {
TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";);
if (c.is_zero())
std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt());
else
std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt());
}
result = mk_add_app(new_args.size(), new_args.c_ptr());
if (hoist_multiplication(result)) {
return BR_REWRITE_FULL;
}
return BR_DONE;
}
else {
SASSERT(!has_multiple);
if (ordered && !m_hoist_mul && !m_hoist_cmul) {
if (num_coeffs == 0)
return BR_FAILED;
if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero())
return BR_FAILED;
}
expr_ref_buffer new_args(m());
if (!c.is_zero())
new_args.push_back(mk_numeral(c));
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (is_numeral(arg))
continue;
new_args.push_back(arg);
}
if (m_hoist_cmul) {
hoist_cmul(new_args);
}
else if (!ordered) {
if (c.is_zero())
std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt());
else
std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt());
}
result = mk_add_app(new_args.size(), new_args.c_ptr());
if (hoist_multiplication(result)) {
return BR_REWRITE_FULL;
}
return BR_DONE;
}
}
template<typename Config>
br_status poly_rewriter<Config>::mk_uminus(expr * arg, expr_ref & result) {
numeral a;
set_curr_sort(m().get_sort(arg));
if (is_numeral(arg, a)) {
a.neg();
normalize(a);
result = mk_numeral(a);
return BR_DONE;
}
else {
result = mk_mul_app(numeral(-1), arg);
return BR_REWRITE1;
}
}
template<typename Config>
br_status poly_rewriter<Config>::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
if (num_args == 1) {
result = args[0];
return BR_DONE;
}
set_curr_sort(m().get_sort(args[0]));
expr * minus_one = mk_numeral(numeral(-1));
ptr_buffer<expr> new_args;
new_args.push_back(args[0]);
for (unsigned i = 1; i < num_args; i++) {
expr * aux_args[2] = { minus_one, args[i] };
new_args.push_back(mk_mul_app(2, aux_args));
}
result = mk_add_app(new_args.size(), new_args.c_ptr());
return BR_REWRITE2;
}
/**
\brief Cancel/Combine monomials that occur is the left and right hand sides.
\remark If move = true, then all non-constant monomials are moved to the left-hand-side.
*/
template<typename Config>
br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) {
set_curr_sort(m().get_sort(lhs));
unsigned lhs_sz;
expr * const * lhs_monomials = get_monomials(lhs, lhs_sz);
unsigned rhs_sz;
expr * const * rhs_monomials = get_monomials(rhs, rhs_sz);
expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs
expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once
bool has_multiple = false;
numeral c(0);
numeral a;
unsigned num_coeffs = 0;
for (unsigned i = 0; i < lhs_sz; i++) {
expr * arg = lhs_monomials[i];
if (is_numeral(arg, a)) {
c += a;
num_coeffs++;
}
else {
visited.mark(get_power_product(arg));
}
}
if (move && num_coeffs == 0 && is_numeral(rhs))
return BR_FAILED;
for (unsigned i = 0; i < rhs_sz; i++) {
expr * arg = rhs_monomials[i];
if (is_numeral(arg, a)) {
c -= a;
num_coeffs++;
}
else {
expr * pp = get_power_product(arg);
if (visited.is_marked(pp)) {
multiple.mark(pp);
has_multiple = true;
}
}
}
normalize(c);
if (!has_multiple && num_coeffs <= 1) {
if (move) {
if (is_numeral(rhs))
return BR_FAILED;
}
else {
if (num_coeffs == 0 || is_numeral(rhs))
return BR_FAILED;
}
}
buffer<numeral> coeffs;
m_expr2pos.reset();
for (unsigned i = 0; i < lhs_sz; i++) {
expr * arg = lhs_monomials[i];
if (is_numeral(arg))
continue;
expr * pp = get_power_product(arg, a);
if (!multiple.is_marked(pp))
continue;
unsigned pos;
if (m_expr2pos.find(pp, pos)) {
coeffs[pos] += a;
}
else {
m_expr2pos.insert(pp, coeffs.size());
coeffs.push_back(a);
}
}
for (unsigned i = 0; i < rhs_sz; i++) {
expr * arg = rhs_monomials[i];
if (is_numeral(arg))
continue;
expr * pp = get_power_product(arg, a);
if (!multiple.is_marked(pp))
continue;
unsigned pos = UINT_MAX;
m_expr2pos.find(pp, pos);
SASSERT(pos != UINT_MAX);
coeffs[pos] -= a;
}
ptr_buffer<expr> new_lhs_monomials;
new_lhs_monomials.push_back(0); // save space for coefficient if needed
// copy power products with non zero coefficients to new_lhs_monomials
visited.reset();
for (unsigned i = 0; i < lhs_sz; i++) {
expr * arg = lhs_monomials[i];
if (is_numeral(arg))
continue;
expr * pp = get_power_product(arg);
if (!multiple.is_marked(pp)) {
new_lhs_monomials.push_back(arg);
}
else if (!visited.is_marked(pp)) {
visited.mark(pp);
unsigned pos = UINT_MAX;
m_expr2pos.find(pp, pos);
SASSERT(pos != UINT_MAX);
a = coeffs[pos];
if (!a.is_zero())
new_lhs_monomials.push_back(mk_mul_app(a, pp));
}
}
ptr_buffer<expr> new_rhs_monomials;
new_rhs_monomials.push_back(0); // save space for coefficient if needed
for (unsigned i = 0; i < rhs_sz; i++) {
expr * arg = rhs_monomials[i];
if (is_numeral(arg))
continue;
expr * pp = get_power_product(arg, a);
if (!multiple.is_marked(pp)) {
if (move) {
if (!a.is_zero()) {
if (a.is_minus_one()) {
new_lhs_monomials.push_back(pp);
}
else {
a.neg();
SASSERT(!a.is_one());
expr * args[2] = { mk_numeral(a), pp };
new_lhs_monomials.push_back(mk_mul_app(2, args));
}
}
}
else {
new_rhs_monomials.push_back(arg);
}
}
}
bool c_at_rhs = false;
if (move) {
if (m_sort_sums) {
// + 1 to skip coefficient
std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt());
}
c_at_rhs = true;
}
else if (new_rhs_monomials.size() == 1) { // rhs is empty
c_at_rhs = true;
}
else if (new_lhs_monomials.size() > 1) {
c_at_rhs = true;
}
if (c_at_rhs) {
c.neg();
normalize(c);
new_rhs_monomials[0] = mk_numeral(c);
lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1);
rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr());
}
else {
new_lhs_monomials[0] = mk_numeral(c);
lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr());
rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1);
}
return BR_DONE;
}
#define TO_BUFFER(_tester_, _buffer_, _e_) \
_buffer_.push_back(_e_); \
for (unsigned _i = 0; _i < _buffer_.size(); ) { \
expr* _e = _buffer_[_i]; \
if (_tester_(_e)) { \
app* a = to_app(_e); \
_buffer_[_i] = a->get_arg(0); \
for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \
_buffer_.push_back(a->get_arg(_j)); \
} \
} \
else { \
++_i; \
} \
} \
template<typename Config>
bool poly_rewriter<Config>::hoist_multiplication(expr_ref& som) {
if (!m_hoist_mul) {
return false;
}
ptr_buffer<expr> adds, muls;
TO_BUFFER(is_add, adds, som);
buffer<bool> valid(adds.size(), true);
obj_map<expr, unsigned> mul_map;
unsigned j;
bool change = false;
for (unsigned k = 0; k < adds.size(); ++k) {
expr* e = adds[k];
muls.reset();
TO_BUFFER(is_mul, muls, e);
for (unsigned i = 0; i < muls.size(); ++i) {
e = muls[i];
if (is_numeral(e)) {
continue;
}
if (mul_map.find(e, j) && valid[j] && j != k) {
m_curr_sort = m().get_sort(adds[k]);
adds[j] = merge_muls(adds[j], adds[k]);
adds[k] = mk_numeral(rational(0));
valid[j] = false;
valid[k] = false;
change = true;
break;
}
else {
mul_map.insert(e, k);
}
}
}
if (!change) {
return false;
}
som = mk_add_app(adds.size(), adds.c_ptr());
return true;
}
template<typename Config>
expr* poly_rewriter<Config>::merge_muls(expr* x, expr* y) {
ptr_buffer<expr> m1, m2;
TO_BUFFER(is_mul, m1, x);
TO_BUFFER(is_mul, m2, y);
unsigned k = 0;
for (unsigned i = 0; i < m1.size(); ++i) {
x = m1[i];
bool found = false;
unsigned j;
for (j = k; j < m2.size(); ++j) {
found = m2[j] == x;
if (found) break;
}
if (found) {
std::swap(m1[i],m1[k]);
std::swap(m2[j],m2[k]);
++k;
}
}
m_curr_sort = m().get_sort(x);
SASSERT(k > 0);
SASSERT(m1.size() >= k);
SASSERT(m2.size() >= k);
expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k),
mk_mul_app(m2.size()-k, m2.c_ptr()+k) };
if (k == m1.size()) {
m1.push_back(0);
}
m1[k] = mk_add_app(2, args);
return mk_mul_app(k+1, m1.c_ptr());
}

View file

@ -0,0 +1,239 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
quant_hoist.cpp
Abstract:
Quantifier hoisting utility.
Author:
Nikolaj Bjorner (nbjorner) 2010-02-19
Revision History:
Hoisted from quant_elim.
--*/
#include "quant_hoist.h"
#include "expr_functors.h"
#include "ast_smt_pp.h"
#include "bool_rewriter.h"
#include "var_subst.h"
#include "ast_pp.h"
//
// Bring quantifiers of common type into prenex form.
//
class quantifier_hoister::impl {
ast_manager& m;
bool_rewriter m_rewriter;
public:
impl(ast_manager& m) :
m(m),
m_rewriter(m)
{}
void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result) {
quantifier_type qt = Q_none_pos;
pull_quantifiers(fml, qt, vars, result);
TRACE("qe_verbose",
tout << mk_pp(fml, m) << "\n";
tout << mk_pp(result, m) << "\n";);
SASSERT(is_positive(qt));
is_fa = (Q_forall_pos == qt);
}
void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result) {
quantifier_type qt = Q_exists_pos;
pull_quantifiers(fml, qt, vars, result);
TRACE("qe_verbose",
tout << mk_pp(fml, m) << "\n";
tout << mk_pp(result, m) << "\n";);
}
void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars) {
quantifier_type qt = is_forall?Q_forall_pos:Q_exists_pos;
expr_ref result(m);
pull_quantifiers(fml, qt, vars, result);
TRACE("qe_verbose",
tout << mk_pp(fml, m) << "\n";
tout << mk_pp(result, m) << "\n";);
fml = result;
}
void extract_quantifier(quantifier* q, app_ref_vector& vars, expr_ref& result) {
unsigned nd = q->get_num_decls();
for (unsigned i = 0; i < nd; ++i) {
sort* s = q->get_decl_sort(i);
app* a = m.mk_fresh_const("x", s);
vars.push_back(a);
}
expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd);
instantiate(m, q, exprs, result);
}
private:
enum quantifier_type {
Q_forall_pos = 0x10,
Q_exists_pos = 0x20,
Q_none_pos = 0x40,
Q_forall_neg = 0x11,
Q_exists_neg = 0x21,
Q_none_neg = 0x41
};
void display(quantifier_type qt, std::ostream& out) {
switch(qt) {
case Q_forall_pos: out << "Forall+"; break;
case Q_exists_pos: out << "Exists+"; break;
case Q_none_pos: out << "None+"; break;
case Q_forall_neg: out << "Forall-"; break;
case Q_exists_neg: out << "Exists-"; break;
case Q_none_neg: out << "None-"; break;
}
}
quantifier_type& negate(quantifier_type& qt) {
TRACE("qe", display(qt, tout); tout << "\n";);
qt = static_cast<quantifier_type>(qt ^0x1);
TRACE("qe", display(qt, tout); tout << "\n";);
return qt;
}
static bool is_negative(quantifier_type qt) {
return 0 != (qt & 0x1);
}
static bool is_positive(quantifier_type qt) {
return 0 == (qt & 0x1);
}
static void set_quantifier_type(quantifier_type& qt, bool is_forall) {
switch(qt) {
case Q_forall_pos: SASSERT(is_forall); break;
case Q_forall_neg: SASSERT(!is_forall); break;
case Q_exists_pos: SASSERT(!is_forall); break;
case Q_exists_neg: SASSERT(is_forall); break;
case Q_none_pos: qt = is_forall?Q_forall_pos:Q_exists_pos; break;
case Q_none_neg: qt = is_forall?Q_exists_neg:Q_forall_neg; break;
}
}
bool is_compatible(quantifier_type qt, bool is_forall) {
switch(qt) {
case Q_forall_pos: return is_forall;
case Q_forall_neg: return !is_forall;
case Q_exists_pos: return !is_forall;
case Q_exists_neg: return is_forall;
case Q_none_pos: return true;
case Q_none_neg: return true;
default:
UNREACHABLE();
}
return false;
}
void pull_quantifiers(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result) {
if (!has_quantifiers(fml)) {
result = fml;
return;
}
switch(fml->get_kind()) {
case AST_APP: {
expr_ref_vector args(m);
expr_ref tmp(m);
unsigned num_args = 0;
app* a = to_app(fml);
if (m.is_and(fml)) {
num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; ++i) {
pull_quantifiers(a->get_arg(i), qt, vars, tmp);
args.push_back(tmp);
}
m_rewriter.mk_and(args.size(), args.c_ptr(), result);
}
else if (m.is_or(fml)) {
num_args = to_app(fml)->get_num_args();
for (unsigned i = 0; i < num_args; ++i) {
pull_quantifiers(to_app(fml)->get_arg(i), qt, vars, tmp);
args.push_back(tmp);
}
m_rewriter.mk_or(args.size(), args.c_ptr(), result);
}
else if (m.is_not(fml)) {
pull_quantifiers(to_app(fml)->get_arg(0), negate(qt), vars, tmp);
negate(qt);
result = m.mk_not(tmp);
}
else if (m.is_implies(fml)) {
pull_quantifiers(to_app(fml)->get_arg(0), negate(qt), vars, tmp);
negate(qt);
pull_quantifiers(to_app(fml)->get_arg(1), qt, vars, result);
result = m.mk_implies(tmp, result);
}
else if (m.is_ite(fml)) {
pull_quantifiers(to_app(fml)->get_arg(1), qt, vars, tmp);
pull_quantifiers(to_app(fml)->get_arg(2), qt, vars, result);
result = m.mk_ite(to_app(fml)->get_arg(0), tmp, result);
}
else {
// the formula contains a quantifier, but it is "inaccessible"
result = fml;
}
break;
}
case AST_QUANTIFIER: {
quantifier* q = to_quantifier(fml);
expr_ref tmp(m);
if (!is_compatible(qt, q->is_forall())) {
result = fml;
break;
}
set_quantifier_type(qt, q->is_forall());
extract_quantifier(q, vars, tmp);
pull_quantifiers(tmp, qt, vars, result);
break;
}
case AST_VAR:
result = fml;
break;
default:
UNREACHABLE();
result = fml;
break;
}
}
};
quantifier_hoister::quantifier_hoister(ast_manager& m) {
m_impl = alloc(impl, m);
}
quantifier_hoister::~quantifier_hoister() {
dealloc(m_impl);
}
void quantifier_hoister::operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result) {
(*m_impl)(fml, vars, is_fa, result);
}
void quantifier_hoister::pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result) {
m_impl->pull_exists(fml, vars, result);
}
void quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars) {
m_impl->pull_quantifier(is_forall, fml, vars);
}

View file

@ -0,0 +1,64 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
quant_hoist.h
Abstract:
Quantifier hoisting utility.
Author:
Nikolaj Bjorner (nbjorner) 2010-02-19
Revision History:
Hoisted from quant_elim.
--*/
#ifndef __QUANTIFIER_HOISTER_H_
#define __QUANTIFIER_HOISTER_H_
#include "ast.h"
class quantifier_hoister {
class impl;
impl* m_impl;
public:
quantifier_hoister(ast_manager& m);
~quantifier_hoister();
/**
\brief Pull top-most quantifier up.
Create fresh constants for the bound variables.
Return the constants, the quantifier type (forall or exists), and
the sub-formula under the quantifier.
The list of variables is empty if the formula is quantifier free or
if the existing quantifiers occur under a connective other than
or, and, implies, ite (then and else branch only).
*/
void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result);
/**
\brief Pull top-most existential quantifier up.
The list of variables is empty if there are no top-level existential quantifier.
*/
void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result);
/**
\brief Pull top-most universal (is_forall=true) or existential (is_forall=false) quantifier up.
The list of variables is empty if there are no top-level universal/existential quantifier.
*/
void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars);
};
#endif

View file

@ -0,0 +1,402 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
rewriter.cpp
Abstract:
Lean and mean rewriter
Author:
Leonardo (leonardo) 2011-03-31
Notes:
--*/
#include"rewriter_def.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
void rewriter_core::init_cache_stack() {
SASSERT(m_cache_stack.empty());
m_cache = alloc(cache, m());
m_cache_stack.push_back(m_cache);
if (m_proof_gen) {
SASSERT(m_cache_pr_stack.empty());
m_cache_pr = alloc(cache, m());
m_cache_pr_stack.push_back(m_cache_pr);
}
}
void rewriter_core::del_cache_stack() {
std::for_each(m_cache_stack.begin(), m_cache_stack.end(), delete_proc<cache>());
m_cache_stack.finalize();
m_cache = 0;
if (m_proof_gen) {
std::for_each(m_cache_pr_stack.begin(), m_cache_pr_stack.end(), delete_proc<cache>());
m_cache_pr_stack.finalize();
m_cache_pr = 0;
}
}
void rewriter_core::cache_result(expr * k, expr * v) {
#if 0
// trace for tracking cache usage
verbose_stream() << "1 " << k->get_id() << std::endl;
#endif
SASSERT(!m_proof_gen);
TRACE("rewriter_cache_result", tout << mk_ismt2_pp(k, m()) << "\n--->\n" << mk_ismt2_pp(v, m()) << "\n";);
m_cache->insert(k, v);
#if 0
static unsigned num_cached = 0;
num_cached ++;
if (num_cached % 100000 == 0)
verbose_stream() << "[rewriter] :num-cached " << num_cached << " :capacity " << m_cache->capacity() << " :size " << m_cache->size()
<< " :frame-stack-size " << m_frame_stack.size() << std::endl;
#endif
}
void rewriter_core::cache_result(expr * k, expr * v, proof * pr) {
m_cache->insert(k, v);
SASSERT(m_proof_gen);
m_cache_pr->insert(k, pr);
}
unsigned rewriter_core::get_cache_size() const {
return m_cache->size();
}
void rewriter_core::reset_cache() {
m_cache = m_cache_stack[0];
m_cache->reset();
if (m_proof_gen) {
m_cache_pr = m_cache_pr_stack[0];
m_cache_pr->reset();
}
}
// free memory allocated by the rewriter
void rewriter_core::free_memory() {
del_cache_stack();
m_frame_stack.finalize();
m_result_stack.finalize();
m_scopes.finalize();
}
void rewriter_core::begin_scope() {
m_scopes.push_back(scope(m_root, m_num_qvars));
unsigned lvl = m_scopes.size();
SASSERT(lvl <= m_cache_stack.size());
SASSERT(!m_proof_gen || m_cache_pr_stack.size() == m_cache_stack.size());
if (lvl == m_cache_stack.size()) {
m_cache_stack.push_back(alloc(cache, m()));
if (m_proof_gen)
m_cache_pr_stack.push_back(alloc(cache, m()));
}
m_cache = m_cache_stack[lvl];
m_cache->reset();
SASSERT(m_cache->empty());
if (m_proof_gen) {
m_cache_pr = m_cache_pr_stack[lvl];
m_cache_pr->reset();
SASSERT(m_cache_pr->empty());
}
}
void rewriter_core::end_scope() {
m_cache->reset();
if (m_proof_gen)
m_cache_pr->reset();
scope & s = m_scopes.back();
m_root = s.m_old_root;
m_num_qvars = s.m_old_num_qvars;
m_scopes.pop_back();
unsigned new_lvl = m_scopes.size();
m_cache = m_cache_stack[new_lvl];
if (m_proof_gen)
m_cache_pr = m_cache_pr_stack[new_lvl];
}
bool rewriter_core::is_child_of_top_frame(expr * t) const {
if (m_frame_stack.empty())
return true;
frame const & fr = m_frame_stack.back();
expr * parent = fr.m_curr;
unsigned num;
switch (parent->get_kind()) {
case AST_APP:
num = to_app(parent)->get_num_args();
for (unsigned i = 0; i < num; i++) {
if (to_app(parent)->get_arg(i) == t)
return true;
}
return false;
case AST_QUANTIFIER:
num = to_quantifier(parent)->get_num_children();
for (unsigned i = 0; i < num; i++) {
if (to_quantifier(parent)->get_child(i) == t)
return true;
}
return false;
default:
return false;
}
}
/**
\brief Eliminate (implicit) reflexivity proofs from m_result_pr_stack starting at position spos.
The implicit reflexivity proof is 0.
*/
void rewriter_core::elim_reflex_prs(unsigned spos) {
SASSERT(m_proof_gen);
unsigned sz = m_result_pr_stack.size();
SASSERT(spos <= sz);
unsigned j = spos;
for (unsigned i = spos; i < sz; i++) {
proof * pr = m_result_pr_stack.get(i);
if (pr != 0) {
if (i != j)
m_result_pr_stack.set(j, pr);
j++;
}
}
m_result_pr_stack.shrink(j);
}
rewriter_core::rewriter_core(ast_manager & m, bool proof_gen):
m_manager(m),
m_proof_gen(proof_gen),
m_result_stack(m),
m_result_pr_stack(m),
m_num_qvars(0) {
init_cache_stack();
}
rewriter_core::~rewriter_core() {
del_cache_stack();
}
// reset rewriter (macro definitions are not erased)
void rewriter_core::reset() {
SASSERT(!m_cache_stack.empty());
reset_cache();
m_frame_stack.reset();
m_result_stack.reset();
if (m_proof_gen)
m_result_pr_stack.reset();
m_root = 0;
m_num_qvars = 0;
m_scopes.reset();
}
// free memory & reset (macro definitions are not erased)
void rewriter_core::cleanup() {
free_memory();
init_cache_stack();
m_root = 0;
m_num_qvars = 0;
}
#ifdef _TRACE
void rewriter_core::display_stack(std::ostream & out, unsigned pp_depth) {
svector<frame>::iterator it = m_frame_stack.begin();
svector<frame>::iterator end = m_frame_stack.end();
for (; it != end; ++it) {
out << mk_bounded_pp(it->m_curr, m(), pp_depth) << "\n";
out << "state: " << it->m_state << "\n";
out << "cache: " << it->m_cache_result << ", new_child: " << it->m_new_child << ", max-depth: " << it->m_max_depth << ", i: " << it->m_i << "\n";
out << "------------------\n";
}
}
#endif
bool var_shifter_core::visit(expr * t) {
if (is_ground(t)) {
m_result_stack.push_back(t);
return true;
}
bool c = must_cache(t);
if (c) {
expr * r = get_cached(t);
if (r) {
m_result_stack.push_back(r);
set_new_child_flag(t, r);
return true;
}
}
switch (t->get_kind()) {
case AST_APP:
SASSERT(to_app(t)->get_num_args() > 0);
push_frame(t, c);
return false;
case AST_VAR:
process_var(to_var(t));
return true;
case AST_QUANTIFIER:
push_frame(t, c);
return false;
default:
UNREACHABLE();
return true;
}
}
void var_shifter_core::process_app(app * t, frame & fr) {
unsigned num_args = t->get_num_args();
while (fr.m_i < num_args) {
expr * arg = t->get_arg(fr.m_i);
fr.m_i++;
if (!visit(arg))
return;
}
SASSERT(fr.m_spos + num_args == m_result_stack.size());
expr * new_t;
if (fr.m_new_child) {
expr * const * new_args = m_result_stack.c_ptr() + fr.m_spos;
new_t = m().mk_app(t->get_decl(), num_args, new_args);
}
else {
new_t = t;
}
m_result_stack.shrink(fr.m_spos);
m_result_stack.push_back(new_t);
m_frame_stack.pop_back();
set_new_child_flag(t, new_t);
if (fr.m_cache_result)
cache_result(t, new_t);
}
void var_shifter_core::process_quantifier(quantifier * q, frame & fr) {
if (fr.m_i == 0) {
begin_scope();
m_num_qvars += q->get_num_decls();
m_root = q->get_expr();
}
unsigned num_children = q->get_num_children();
while (fr.m_i < num_children) {
expr * child = q->get_child(fr.m_i);
fr.m_i++;
if (!visit(child))
return;
}
SASSERT(fr.m_spos + num_children == m_result_stack.size());
expr * new_q;
if (fr.m_new_child) {
expr * const * it = m_result_stack.c_ptr() + fr.m_spos;
expr * new_expr = *it;
++it;
expr * const * new_pats = it;
expr * const * new_no_pats = new_pats + q->get_num_patterns();
new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_expr);
}
else {
new_q = q;
}
m_result_stack.shrink(fr.m_spos);
m_result_stack.push_back(new_q);
m_frame_stack.pop_back();
set_new_child_flag(q, new_q);
end_scope();
if (fr.m_cache_result)
cache_result(q, new_q);
}
void var_shifter_core::main_loop(expr * t, expr_ref & r) {
SASSERT(m_cache == m_cache_stack[0]);
SASSERT(m_frame_stack.empty());
SASSERT(m_result_stack.empty());
m_root = t;
if (visit(t)) {
r = m_result_stack.back();
m_result_stack.pop_back();
SASSERT(m_result_stack.empty());
return;
}
SASSERT(!m_frame_stack.empty());
while (!m_frame_stack.empty()) {
frame & fr = m_frame_stack.back();
expr * t = fr.m_curr;
if (fr.m_i == 0 && fr.m_cache_result) {
expr * r = get_cached(t);
if (r) {
m_result_stack.push_back(r);
m_frame_stack.pop_back();
set_new_child_flag(t, r);
continue;
}
}
switch (t->get_kind()) {
case AST_APP:
process_app(to_app(t), fr);
break;
case AST_QUANTIFIER:
process_quantifier(to_quantifier(t), fr);
break;
default:
UNREACHABLE();
}
}
r = m_result_stack.back();
m_result_stack.pop_back();
SASSERT(m_result_stack.empty());
}
void var_shifter::operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r) {
if (is_ground(t)) {
r = t;
return;
}
reset_cache();
m_bound = bound;
m_shift1 = shift1;
m_shift2 = shift2;
main_loop(t, r);
}
void var_shifter::process_var(var * v) {
unsigned vidx = v->get_idx();
if (vidx < m_num_qvars) {
m_result_stack.push_back(v);
}
else {
unsigned nvidx = vidx - m_num_qvars;
if (nvidx >= m_bound)
vidx += m_shift1;
else
vidx += m_shift2;
m_result_stack.push_back(m().mk_var(vidx, v->get_sort()));
set_new_child_flag(v);
}
}
void inv_var_shifter::operator()(expr * t, unsigned shift, expr_ref & r) {
if (is_ground(t)) {
r = t;
return;
}
reset_cache();
m_shift = shift;
main_loop(t, r);
}
void inv_var_shifter::process_var(var * v) {
unsigned vidx = v->get_idx();
if (vidx < m_num_qvars) {
m_result_stack.push_back(v);
}
else {
SASSERT(vidx >= m_num_qvars + m_shift);
vidx -= m_shift;
m_result_stack.push_back(m().mk_var(vidx, v->get_sort()));
set_new_child_flag(v);
}
}
template class rewriter_tpl<beta_reducer_cfg>;

399
src/ast/rewriter/rewriter.h Normal file
View file

@ -0,0 +1,399 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
rewriter.h
Abstract:
Lean and mean rewriter
Author:
Leonardo (leonardo) 2011-03-31
Notes:
--*/
#ifndef _REWRITER_H_
#define _REWRITER_H_
#include"ast.h"
#include"rewriter_types.h"
#include"act_cache.h"
/**
\brief Common infrastructure for AST rewriters.
*/
class rewriter_core {
protected:
struct frame {
expr * m_curr;
unsigned m_cache_result:1; // true if the result of rewriting m_expr must be cached.
unsigned m_new_child:1;
unsigned m_state:2;
unsigned m_max_depth:2; // bounded rewrite... if m_max_depth == 0, then children are not rewritten.
unsigned m_i:26;
unsigned m_spos; // top of the result stack, when the frame was created.
frame(expr * n, bool cache_res, unsigned st, unsigned max_depth, unsigned spos):
m_curr(n),
m_cache_result(cache_res),
m_new_child(false),
m_state(st),
m_max_depth(max_depth),
m_i(0),
m_spos(spos) {
}
};
ast_manager & m_manager;
bool m_proof_gen;
typedef act_cache cache;
ptr_vector<cache> m_cache_stack;
cache * m_cache; // current cache.
svector<frame> m_frame_stack;
expr_ref_vector m_result_stack;
// proof generation goodness ----
ptr_vector<cache> m_cache_pr_stack;
cache * m_cache_pr;
proof_ref_vector m_result_pr_stack;
// --------------------------
expr * m_root;
unsigned m_num_qvars;
struct scope {
expr * m_old_root;
unsigned m_old_num_qvars;
scope(expr * r, unsigned n):m_old_root(r), m_old_num_qvars(n) {}
};
svector<scope> m_scopes;
// Return true if the rewriting result of the given expression must be cached.
bool must_cache(expr * t) const {
return
t->get_ref_count() > 1 && // t must be a shared expression
t != m_root && // t must not be the root expression
((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t)); // t is a (non-constant) application or a quantifier.
}
void push_frame_core(expr * t, bool cache_res, unsigned st = 0, unsigned max_depth = RW_UNBOUNDED_DEPTH) {
SASSERT(!m_proof_gen || m_result_stack.size() == m_result_pr_stack.size());
m_frame_stack.push_back(frame(t, cache_res, st, max_depth, m_result_stack.size()));
}
void push_frame(expr * t, unsigned st = 0) {
push_frame_core(t, must_cache(t), st);
}
void init_cache_stack();
void del_cache_stack();
void reset_cache();
void cache_result(expr * k, expr * v);
expr * get_cached(expr * k) const { return m_cache->find(k); }
void cache_result(expr * k, expr * v, proof * pr);
proof * get_cached_pr(expr * k) const { return static_cast<proof*>(m_cache_pr->find(k)); }
void free_memory();
void begin_scope();
void end_scope();
bool is_child_of_top_frame(expr * t) const;
void set_new_child_flag(expr * old_t) {
CTRACE("rewriter_bug", !is_child_of_top_frame(old_t), display_stack(tout, 3););
SASSERT(is_child_of_top_frame(old_t));
if (!m_frame_stack.empty())
m_frame_stack.back().m_new_child = true;
}
void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); }
void elim_reflex_prs(unsigned spos);
public:
rewriter_core(ast_manager & m, bool proof_gen);
~rewriter_core();
ast_manager & m() const { return m_manager; }
void reset();
void cleanup();
#ifdef _TRACE
void display_stack(std::ostream & out, unsigned pp_depth);
#endif
unsigned get_cache_size() const;
};
class var_shifter_core : public rewriter_core {
protected:
bool visit(expr * t);
void process_app(app * t, frame & fr);
virtual void process_var(var * v) = 0;
void process_quantifier(quantifier * q, frame & fr);
void main_loop(expr * t, expr_ref & r);
public:
var_shifter_core(ast_manager & m):rewriter_core(m, false) {}
};
/**
\brief Functor used to shift the free variables of an AST by a given amount.
This functor is used by the var_subst functor.
This functor implements the following functions:
1) shift(n, s) will return a new AST where all free variables (VAR i) in n,
are replaced by (VAR (+ i s)).
2) shift(n, b, s1, s2) is a variant of the previous function such that
for a each free variable (VAR i) is shifted to:
- (VAR i + s1) if i >= b
- (VAR i + s2) if i < b
*/
class var_shifter : public var_shifter_core {
unsigned m_bound;
unsigned m_shift1;
unsigned m_shift2;
virtual void process_var(var * v);
public:
var_shifter(ast_manager & m):var_shifter_core(m) {}
void operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r);
void operator()(expr * t, unsigned s, expr_ref & r) {
operator()(t, 0, s, 0, r);
}
};
/**
\brief Functor used to shift the free variables of an AST by a negative amount.
Abstract implementation:
inv_shift(f(c_1, ..., c_n), d, s) =
f(inv_shift(c_1, d, s), ..., inv_shift(c_n, d, s))
inv_shift(var(i), d, s) =
if (i < d)
var(i)
else
assert(i - s >= d)
var(i-s)
inv_shift((forall (x) P), d, s) =
(forall (x) inv_shift(P, d+1, s))
This functor assumes that if we are shifting an expression F by N, then F
does not contain free variables #0, ... #N-1
See assertion above.
*/
class inv_var_shifter : public var_shifter_core {
protected:
unsigned m_shift;
virtual void process_var(var * v);
public:
inv_var_shifter(ast_manager & m):var_shifter_core(m) {}
void operator()(expr * t, unsigned shift, expr_ref & r);
};
template<typename Config>
class rewriter_tpl : public rewriter_core {
protected:
// Rewriter maintains a stack of frames.
// Each frame represents an expression that is being rewritten.
// The resultant expressions are store on the Result stack.
// Each frame is in a particular state.
// Let f(t_0,...,t_n) be the expression is the frame Fr. Then Fr can be is one of the
// following states:
// PROCESS_CHILDREN(i) the children t_0, ..., t_{i-1} have already been rewritten, and the result is on the Result stack.
// REWRITE_BUILTIN All t_0, ..., t_n have been rewritten to t_0',...,t_n', and the builtin rewriter (or plugin) produced a new term t
// that can be further rewritten.
// EXPAND_DEF All t_0, ..., t_n have been rewritten to t_0',...,t_n'.
// The function symbol f is a macro, and the body of the macro needs to be rewritten using the t_0',...,t_n' as bindings.
// REWRITE_RULE All t_0, ..., t_n have been rewritten to t_0',...,t_n'.
// There is rewrite rule lhs -> rhs s.t. f(t_0', ..., t_n') matches lhs with substitution delta.
// rhs is then rewritten using delta.
enum state {
PROCESS_CHILDREN,
REWRITE_BUILTIN,
EXPAND_DEF,
REWRITE_RULE
};
Config & m_cfg;
unsigned m_num_steps;
volatile bool m_cancel;
ptr_vector<expr> m_bindings;
var_shifter m_shifter;
expr_ref m_r;
proof_ref m_pr;
proof_ref m_pr2;
svector<frame> & frame_stack() { return this->m_frame_stack; }
svector<frame> const & frame_stack() const { return this->m_frame_stack; }
expr_ref_vector & result_stack() { return this->m_result_stack; }
expr_ref_vector const & result_stack() const { return this->m_result_stack; }
proof_ref_vector & result_pr_stack() { return this->m_result_pr_stack; }
proof_ref_vector const & result_pr_stack() const { return this->m_result_pr_stack; }
void set_new_child_flag(expr * old_t) {
SASSERT(frame_stack().empty() || frame_stack().back().m_state != PROCESS_CHILDREN || this->is_child_of_top_frame(old_t));
if (!frame_stack().empty())
frame_stack().back().m_new_child = true;
}
void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); }
// cache the result of shared non atomic expressions.
bool cache_results() const { return m_cfg.cache_results(); }
// cache all results share and non shared expressions non atomic expressions.
bool cache_all_results() const { return m_cfg.cache_all_results(); }
// flat non shared AC terms
bool flat_assoc(func_decl * f) const { return m_cfg.flat_assoc(f); }
// rewrite patterns
bool rewrite_patterns() const { return m_cfg.rewrite_patterns(); }
// check maximum number of scopes
void check_max_scopes() const {
if (m_cfg.max_scopes_exceeded(this->m_scopes.size()))
throw rewriter_exception(TACTIC_MAX_SCOPES_MSG);
}
// check maximum size of the frame stack
void check_max_frames() const {
if (m_cfg.max_frames_exceeded(frame_stack().size()))
throw rewriter_exception(TACTIC_MAX_FRAMES_MSG);
}
// check maximum number of rewriting steps
void check_max_steps() const {
if (m_cfg.max_steps_exceeded(m_num_steps))
throw rewriter_exception(TACTIC_MAX_STEPS_MSG);
}
// If pre_visit returns false, then t children are not visited/rewritten.
// This should be used with care, since it may produce incorrect results
// when the rewriter is used to apply variable substitution.
bool pre_visit(expr * t) {
return m_cfg.pre_visit(t);
}
// Return true if the rewriting result of the given expression must be cached.
bool must_cache(expr * t) const {
if (cache_all_results())
return t != this->m_root && ((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t));
if (cache_results())
return rewriter_core::must_cache(t);
return false;
}
bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) {
return m_cfg.get_macro(f, def, q, def_pr);
}
void push_frame(expr * t, bool mcache, unsigned max_depth) {
check_max_frames();
push_frame_core(t, mcache, PROCESS_CHILDREN, max_depth);
}
void begin_scope() {
check_max_scopes();
rewriter_core::begin_scope();
}
template<bool ProofGen>
void process_var(var * v);
template<bool ProofGen>
void process_const(app * t);
template<bool ProofGen>
bool visit(expr * t, unsigned max_depth);
template<bool ProofGen>
void cache_result(expr * t, expr * new_t, proof * pr, bool c) {
if (c) {
if (!ProofGen)
rewriter_core::cache_result(t, new_t);
else
rewriter_core::cache_result(t, new_t, pr);
}
}
template<bool ProofGen>
void process_app(app * t, frame & fr);
template<bool ProofGen>
void process_quantifier(quantifier * q, frame & fr);
bool first_visit(frame & fr) const {
return fr.m_state == PROCESS_CHILDREN && fr.m_i == 0;
}
bool not_rewriting() const;
template<bool ProofGen>
void main_loop(expr * t, expr_ref & result, proof_ref & result_pr);
template<bool ProofGen>
void resume_core(expr_ref & result, proof_ref & result_pr);
public:
rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg);
ast_manager & m() const { return this->m_manager; }
Config & cfg() { return m_cfg; }
Config const & cfg() const { return m_cfg; }
void set_cancel(bool f) { m_cancel = f; }
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
~rewriter_tpl();
void reset();
void cleanup();
void set_bindings(unsigned num_bindings, expr * const * bindings);
void set_inv_bindings(unsigned num_bindings, expr * const * bindings);
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); }
void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) {
SASSERT(!m_proof_gen);
reset();
set_inv_bindings(num_bindings, bindings);
operator()(n, result);
}
void resume(expr_ref & result, proof_ref & result_pr);
void resume(expr_ref & result) { resume(result, m_pr); }
// Return the number of steps performed by the rewriter in the last call to operator().
unsigned get_num_steps() const { return m_num_steps; }
};
struct default_rewriter_cfg {
bool cache_all_results() const { return false; }
bool cache_results() const { return true; }
bool flat_assoc(func_decl * f) const { return false; }
bool rewrite_patterns() const { return true; }
bool max_scopes_exceeded(unsigned num_scopes) const { return false; }
bool max_frames_exceeded(unsigned num_frames) const { return false; }
bool max_steps_exceeded(unsigned num_steps) const { return false; }
bool pre_visit(expr * t) { return true; }
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return BR_FAILED; }
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
return false;
}
bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { return false; }
bool get_macro(func_decl * d, expr * & def, quantifier * & q, proof * & def_pr) { return false; }
bool get_subst(expr * s, expr * & t, proof * & t_pr) { return false; }
void reset() {}
void cleanup() {}
};
struct beta_reducer_cfg : public default_rewriter_cfg {
bool pre_visit(expr * t) { return !is_ground(t); }
};
class beta_reducer : public rewriter_tpl<beta_reducer_cfg> {
beta_reducer_cfg m_cfg;
public:
beta_reducer(ast_manager & m):
rewriter_tpl<beta_reducer_cfg>(m, false, m_cfg) {}
};
#endif

View file

@ -0,0 +1,145 @@
The following classes implement theory specific rewriting rules:
- bool_rewriter
- arith_rewriter
- bv_rewriter
- array_rewriter
- datatype_rewriter
Each of the provide the method
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result)
where
- f is expected to be a func_decl of the given theory
- If the return value if different from BR_FAILED, the result is stored in result.
The template rewriter_tpl<Cfg> can be instantiated to implement
rewriters that traverse ASTs applying some kind of transformation/simplification.
The parameter Cfg is expected to be a class with the following methods:
bool cache_all_results() const;
bool cache_results() const;
bool flat_assoc(func_decl * f) const;
bool rewrite_patterns() const;
bool max_scopes_exceeded(unsigned num_scopes) const;
bool max_frames_exceeded(unsigned num_frames) const;
bool max_steps_exceeded(unsigned num_steps) const;
bool pre_visit(expr * t);
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr);
bool get_macro(func_decl * d, expr * & def, quantifier * & q, proof * & def_pr);
bool get_subst(expr * s, expr * & t, proof * & t_pr);
void reset();
void cleanup();
The class default_rewriter_cfg provides a default implementation for these methods.
The method reduce_app is the most important one. When implementing new rewriter,
we usually redefine this method.
We should return BR_FAILED, if we do not want to apply a "simplification" to
(f args[0] ... args[num-1])
This is very important for performance reasons, since the rewriter tries to
reuse AST when BR_FAILED is returned.
If one wants to simply (f args[0] ... args[num-1]) to t, then use:
result = t;
return BR_DONE;
Sometimes, by applying a simplification rule we may create new opportunites for
rewriting. One can instruct the rewriter to revisit the result by returning:
BR_REWRITE1, // rewrite the result (bounded by depth 1)
BR_REWRITE2, // rewrite the result (bounded by depth 2)
BR_REWRITE3, // rewrite the result (bounded by depth 3)
BR_REWRITE_FULL, // rewrite the result unbounded
Example:
Suppose one wants to implement the rewriting rule
(= (ite c t e) v) --> (ite c (= t v) (= e v))
This rule may create new opportunites for simplification since
it creates the equalities (= t v) and (= e v).
So, to force the rewriter to process these new equalities,
BR_REWRITE2 should be returned.
It is also correct (but unnecessary) to return BR_REWRITE3 and BR_REWRITE_FULL.
To instantiate a new rewriter, one should
1) Define a new configuration class.
class mycfg : public default_rewriter_cfg {
...
}
2) Force template instantiation using the new cfg.
templace class rewriter_tpl<mycfg>;
3) Define a subclass of rewriter_tpl<mycfg> that
owns the new cfg.
class myrewriter : public rewriter_tpl<mycfg> {
mycfg m_cfg;
public:
myrewriter(ast_manager & m):
rewriter_tpl<mycfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(...) {
}
};
The rewriter_tpl template supports proof production.
It can be disabled even when proof productions is
enabled in the ast_manager.
Examples of rewriter_tpl instances:
- th_rewriter.cpp
- assertion_set_bit_blaster.cpp
- model_evaluator.cpp
- elim_distinct.cpp
Notes for additional Cfg methods:
- bool rewrite_patterns() const;
If false is returned, then the rewriter will ignore patterns.
- bool pre_visit(expr * t);
If false is returned, then the rewriter will not process t.
This is very useful, for example, for implementing rewriters that will only
"process" the Boolean skeleton.
- bool max_steps_exceeded(unsigned num_steps) const;
If false, it interrupts the execution of the rewriter.
The interruption is performed by throwing an exception.
One can also used this callback to check whether the
amount of memory used is still reasonable.
- bool cache_all_results() const;
By default, the rewriter only caches the results of
non-atomic shared ASTs (get_ref_count() > 1). If true,
is returned, the rewriter will cache all intermediate results.
- bool flat_assoc(func_decl * f) const;
If true is returned, then the rewriter will do flattening of
non-shared nodes. For example,
(+ a (+ b (+ c d)))
will be treated as (+ a b c d)
Shared nodes are not considered. Thus, exponential blowup
in the rewriter stack is avoided.
So, when implementing
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
one should not assume that for an associative f, there is not
f-application in args when true is returned by flat_assoc.
when implementing
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
If result_pr is set to 0, and proofs are enabled, then the rewriter will use a generic
"rewrite" proof step for justifying that (f args[0] ... args[num-1]) is equal to result.
The th_rewriter takes care of flattening. So, in principle, there is
not need to return true in flat_assoc.

View file

@ -0,0 +1,642 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
rewriter_def.h
Abstract:
Lean and mean rewriter
Author:
Leonardo (leonardo) 2011-03-31
Notes:
--*/
#include"rewriter.h"
#include"ast_smt2_pp.h"
template<typename Config>
template<bool ProofGen>
void rewriter_tpl<Config>::process_var(var * v) {
if (m_cfg.reduce_var(v, m_r, m_pr)) {
result_stack().push_back(m_r);
if (ProofGen) {
result_pr_stack().push_back(m_pr);
m_pr = 0;
}
set_new_child_flag(v);
m_r = 0;
return;
}
if (!ProofGen) {
// bindings are only used when Proof Generation is not enabled.
unsigned idx = v->get_idx();
if (idx < m_bindings.size()) {
expr * r = m_bindings[m_bindings.size() - idx - 1];
TRACE("process_var", if (r) tout << "idx: " << idx << " --> " << mk_ismt2_pp(r, m()) << "\n";
tout << "bindings:\n";
for (unsigned i = 0; i < m_bindings.size(); i++) if (m_bindings[i]) tout << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n";);
if (r != 0) {
if (m_num_qvars == 0 || is_ground(r)) {
result_stack().push_back(r);
}
else {
expr_ref new_term(m());
m_shifter(r, m_num_qvars, new_term);
result_stack().push_back(new_term);
}
set_new_child_flag(v);
return;
}
}
}
result_stack().push_back(v);
if (ProofGen)
result_pr_stack().push_back(0); // implicit reflexivity
}
template<typename Config>
template<bool ProofGen>
void rewriter_tpl<Config>::process_const(app * t) {
SASSERT(t->get_num_args() == 0);
br_status st = m_cfg.reduce_app(t->get_decl(), 0, 0, m_r, m_pr);
SASSERT(st == BR_FAILED || st == BR_DONE);
if (st == BR_DONE) {
result_stack().push_back(m_r.get());
if (ProofGen) {
if (m_pr)
result_pr_stack().push_back(m_pr);
else
result_pr_stack().push_back(m().mk_rewrite(t, m_r));
m_pr = 0;
}
m_r = 0;
set_new_child_flag(t);
}
else {
result_stack().push_back(t);
if (ProofGen)
result_pr_stack().push_back(0); // implicit reflexivity
}
}
/**
\brief visit expression t. Return true if t was rewritten and the result is on the top m_result_stack.
We may skip t if:
- pre_visit(t) returns false
- max_depth == 0
- t is already in the cache
Otherwise, return false and add a new frame stack for t with the updated max_depth.
*/
template<typename Config>
template<bool ProofGen>
bool rewriter_tpl<Config>::visit(expr * t, unsigned max_depth) {
TRACE("rewriter_visit", tout << "visiting\n" << mk_ismt2_pp(t, m()) << "\n";);
expr * new_t;
proof * new_t_pr;
if (m_cfg.get_subst(t, new_t, new_t_pr)) {
TRACE("rewriter_subst", tout << "subst\n" << mk_ismt2_pp(t, m()) << "\n---->\n" << mk_ismt2_pp(new_t, m()) << "\n";);
result_stack().push_back(new_t);
set_new_child_flag(t, new_t);
if (ProofGen)
result_pr_stack().push_back(new_t_pr);
return true;
}
if (max_depth == 0) {
result_stack().push_back(t);
if (ProofGen)
result_pr_stack().push_back(0); // implicit reflexivity
return true; // t is not going to be processed
}
SASSERT(max_depth > 0);
SASSERT(max_depth <= RW_UNBOUNDED_DEPTH);
bool c = must_cache(t);
if (c) {
#if 0
static unsigned checked_cache = 0;
checked_cache ++;
if (checked_cache % 100000 == 0)
std::cerr << "[rewriter] num-cache-checks: " << checked_cache << std::endl;
#endif
expr * r = get_cached(t);
if (r) {
result_stack().push_back(r);
set_new_child_flag(t, r);
if (ProofGen) {
proof * pr = get_cached_pr(t);
result_pr_stack().push_back(pr);
}
return true;
}
}
if (!pre_visit(t)) {
result_stack().push_back(t);
if (ProofGen)
result_pr_stack().push_back(0); // implicit reflexivity
return true; // t is not going to be processed
}
switch (t->get_kind()) {
case AST_APP:
if (to_app(t)->get_num_args() == 0) {
process_const<ProofGen>(to_app(t));
return true;
}
else {
if (max_depth != RW_UNBOUNDED_DEPTH)
max_depth--;
push_frame(t, c, max_depth);
return false;
}
case AST_VAR:
process_var<ProofGen>(to_var(t));
return true;
case AST_QUANTIFIER:
if (max_depth != RW_UNBOUNDED_DEPTH)
max_depth--;
push_frame(t, c, max_depth);
return false;
default:
UNREACHABLE();
return true;
}
}
template<typename Config>
template<bool ProofGen>
void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
SASSERT(t->get_num_args() > 0);
SASSERT(!frame_stack().empty());
switch (fr.m_state) {
case PROCESS_CHILDREN: {
unsigned num_args = t->get_num_args();
while (fr.m_i < num_args) {
expr * arg = t->get_arg(fr.m_i);
fr.m_i++;
if (!visit<ProofGen>(arg, fr.m_max_depth))
return;
}
func_decl * f = t->get_decl();
// If AC flattening is enabled, f is associative, t is not shared, and there is a previous frame on the stack.
if (!ProofGen) {
// this optimization is only used when Proof generation is disabled.
if (f->is_associative() && t->get_ref_count() <= 1 && frame_stack().size() > 1) {
frame & prev_fr = frame_stack()[frame_stack().size() - 2];
if (is_app(prev_fr.m_curr) &&
to_app(prev_fr.m_curr)->get_decl() == f &&
prev_fr.m_state == PROCESS_CHILDREN &&
flat_assoc(f)) {
frame_stack().pop_back();
set_new_child_flag(t);
return;
}
}
}
unsigned new_num_args = result_stack().size() - fr.m_spos;
expr * const * new_args = result_stack().c_ptr() + fr.m_spos;
app * new_t;
if (ProofGen) {
elim_reflex_prs(fr.m_spos);
unsigned num_prs = result_pr_stack().size() - fr.m_spos;
if (num_prs == 0) {
new_t = t;
m_pr = 0;
}
else {
new_t = m().mk_app(f, new_num_args, new_args);
m_pr = m().mk_congruence(t, new_t, num_prs, result_pr_stack().c_ptr() + fr.m_spos);
}
}
br_status st = m_cfg.reduce_app(f, new_num_args, new_args, m_r, m_pr2);
TRACE("reduce_app",
tout << mk_ismt2_pp(t, m()) << "\n";
tout << "st: " << st;
if (m_r) tout << " --->\n" << mk_ismt2_pp(m_r, m());
tout << "\n";);
if (st != BR_FAILED) {
result_stack().shrink(fr.m_spos);
result_stack().push_back(m_r);
if (ProofGen) {
result_pr_stack().shrink(fr.m_spos);
if (!m_pr2)
m_pr2 = m().mk_rewrite(new_t, m_r);
m_pr = m().mk_transitivity(m_pr, m_pr2);
m_pr2 = 0;
result_pr_stack().push_back(m_pr);
}
if (st == BR_DONE) {
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
frame_stack().pop_back();
set_new_child_flag(t);
m_r = 0;
if (ProofGen)
m_pr = 0;
return;
}
else {
fr.m_state = REWRITE_BUILTIN;
SASSERT(st == BR_REWRITE1 || st == BR_REWRITE2 || st == BR_REWRITE3 || st == BR_REWRITE_FULL);
unsigned max_depth = static_cast<unsigned>(st);
SASSERT(0 <= max_depth && max_depth <= RW_UNBOUNDED_DEPTH);
SASSERT((st == BR_REWRITE1) == (max_depth == 0));
SASSERT((st == BR_REWRITE2) == (max_depth == 1));
SASSERT((st == BR_REWRITE3) == (max_depth == 2));
SASSERT((st == BR_REWRITE_FULL) == (max_depth == RW_UNBOUNDED_DEPTH));
if (max_depth != RW_UNBOUNDED_DEPTH)
max_depth++;
if (visit<ProofGen>(m_r, max_depth)) {
if (ProofGen) {
proof_ref pr2(m()), pr1(m());
pr2 = result_pr_stack().back();
result_pr_stack().pop_back();
pr1 = result_pr_stack().back();
result_pr_stack().pop_back();
m_pr = m().mk_transitivity(pr1, pr2);
result_pr_stack().push_back(m_pr);
}
m_r = result_stack().back();
result_stack().pop_back();
result_stack().pop_back();
result_stack().push_back(m_r);
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
frame_stack().pop_back();
set_new_child_flag(t);
m_r = 0;
if (ProofGen)
m_pr = 0;
return;
}
else {
// frame was created for processing m_r
m_r = 0;
if (ProofGen)
m_pr = 0;
return;
}
}
UNREACHABLE();
}
// TODO: add rewrite rules support
expr * def;
proof * def_pr;
quantifier * def_q;
// When get_macro succeeds, then
// we know that:
// forall X. f(X) = def[X]
// and def_pr is a proof for this quantifier.
//
// Remark: def_q is only used for proof generation.
// It is the quantifier forall X. f(X) = def[X]
if (get_macro(f, def, def_q, def_pr)) {
SASSERT(!f->is_associative() || !flat_assoc(f));
SASSERT(new_num_args == t->get_num_args());
if (is_ground(def)) {
m_r = def;
if (ProofGen) {
SASSERT(def_pr);
m_pr = m().mk_transitivity(m_pr, def_pr);
}
}
else {
if (ProofGen) {
NOT_IMPLEMENTED_YET();
// We do not support the use of bindings in proof generation mode.
// Thus we have to apply the subsitution here, and
// beta_reducer subst(m());
// subst.set_bindings(new_num_args, new_args);
// expr_ref r2(m());
// subst(def, r2);
}
else {
fr.m_state = EXPAND_DEF;
TRACE("get_macro", tout << "f: " << f->get_name() << ", def: \n" << mk_ismt2_pp(def, m()) << "\n";
tout << "Args num: " << num_args << "\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(new_args[i], m()) << "\n";);
unsigned i = num_args;
while (i > 0) {
--i;
m_bindings.push_back(new_args[i]);
}
result_stack().push_back(def);
TRACE("get_macro", tout << "bindings:\n";
for (unsigned i = 0; i < m_bindings.size(); i++) tout << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n";);
begin_scope();
m_num_qvars = 0;
m_root = def;
push_frame(def, false, RW_UNBOUNDED_DEPTH);
return;
}
}
}
else {
if (ProofGen) {
m_r = new_t;
}
else {
if (fr.m_new_child) {
m_r = m().mk_app(f, new_num_args, new_args);
}
else {
TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(t, m()) << "\n";);
m_r = t;
}
}
}
result_stack().shrink(fr.m_spos);
result_stack().push_back(m_r);
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
if (ProofGen) {
result_pr_stack().shrink(fr.m_spos);
result_pr_stack().push_back(m_pr);
m_pr = 0;
}
frame_stack().pop_back();
set_new_child_flag(t, m_r);
m_r = 0;
return;
}
case REWRITE_BUILTIN:
SASSERT(fr.m_spos + 2 == result_stack().size());
if (ProofGen) {
proof_ref pr2(m()), pr1(m());
pr2 = result_pr_stack().back();
result_pr_stack().pop_back();
pr1 = result_pr_stack().back();
result_pr_stack().pop_back();
m_pr = m().mk_transitivity(pr1, pr2);
result_pr_stack().push_back(m_pr);
}
m_r = result_stack().back();
result_stack().pop_back();
result_stack().pop_back();
result_stack().push_back(m_r);
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
frame_stack().pop_back();
set_new_child_flag(t);
return;
case EXPAND_DEF:
SASSERT(fr.m_spos + t->get_num_args() + 2 == result_stack().size());
SASSERT(t->get_num_args() <= m_bindings.size());
if (!ProofGen) {
m_bindings.shrink(m_bindings.size() - t->get_num_args());
end_scope();
m_r = result_stack().back();
result_stack().shrink(fr.m_spos);
result_stack().push_back(m_r);
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
frame_stack().pop_back();
set_new_child_flag(t);
}
else {
// TODO
NOT_IMPLEMENTED_YET();
}
return;
case REWRITE_RULE:
// support for rewriting rules was not implemented yet.
NOT_IMPLEMENTED_YET();
break;
default:
UNREACHABLE();
break;
}
}
template<typename Config>
template<bool ProofGen>
void rewriter_tpl<Config>::process_quantifier(quantifier * q, frame & fr) {
SASSERT(fr.m_state == PROCESS_CHILDREN);
if (fr.m_i == 0) {
if (!ProofGen) {
begin_scope();
m_root = q->get_expr();
}
m_num_qvars += q->get_num_decls();
if (!ProofGen) {
for (unsigned i = 0; i < q->get_num_decls(); i++)
m_bindings.push_back(0);
}
}
unsigned num_children = rewrite_patterns() ? q->get_num_children() : 1;
while (fr.m_i < num_children) {
expr * child = q->get_child(fr.m_i);
fr.m_i++;
if (!visit<ProofGen>(child, fr.m_max_depth))
return;
}
SASSERT(fr.m_spos + num_children == result_stack().size());
expr * const * it = result_stack().c_ptr() + fr.m_spos;
expr * new_body = *it;
expr * const * new_pats;
expr * const * new_no_pats;
if (rewrite_patterns()) {
new_pats = it + 1;
new_no_pats = new_pats + q->get_num_patterns();
}
else {
new_pats = q->get_patterns();
new_no_pats = q->get_no_patterns();
}
if (ProofGen) {
quantifier * new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body);
m_pr = q == new_q ? 0 : m().mk_quant_intro(q, new_q, result_pr_stack().get(fr.m_spos));
m_r = new_q;
proof_ref pr2(m());
if (m_cfg.reduce_quantifier(new_q, new_body, new_pats, new_no_pats, m_r, pr2)) {
m_pr = m().mk_transitivity(m_pr, pr2);
}
TRACE("reduce_quantifier_bug", tout << "m_pr is_null: " << (m_pr.get() == 0) << "\n";
if (m_pr) tout << mk_ismt2_pp(m_pr, m()) << "\n";);
result_pr_stack().shrink(fr.m_spos);
result_pr_stack().push_back(m_pr);
}
else {
if (!m_cfg.reduce_quantifier(q, new_body, new_pats, new_no_pats, m_r, m_pr)) {
if (fr.m_new_child) {
m_r = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body);
}
else {
TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(q, m()) << "\n";);
m_r = q;
}
}
}
result_stack().shrink(fr.m_spos);
result_stack().push_back(m_r.get());
if (!ProofGen) {
SASSERT(q->get_num_decls() <= m_bindings.size());
m_bindings.shrink(m_bindings.size() - q->get_num_decls());
end_scope();
cache_result<ProofGen>(q, m_r, m_pr, fr.m_cache_result);
}
else {
cache_result<ProofGen>(q, m_r, m_pr, fr.m_cache_result);
m_pr = 0;
}
m_r = 0;
frame_stack().pop_back();
set_new_child_flag(q, m_r);
}
template<typename Config>
bool rewriter_tpl<Config>::not_rewriting() const {
SASSERT(frame_stack().empty());
SASSERT(m_cache == m_cache_stack[0]);
return true;
}
template<typename Config>
rewriter_tpl<Config>::rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg):
rewriter_core(m, proof_gen),
m_cfg(cfg),
m_num_steps(0),
m_cancel(false),
m_shifter(m),
m_r(m),
m_pr(m),
m_pr2(m) {
}
template<typename Config>
rewriter_tpl<Config>::~rewriter_tpl() {
}
template<typename Config>
void rewriter_tpl<Config>::reset() {
m_cfg.reset();
rewriter_core::reset();
m_bindings.reset();
m_shifter.reset();
}
template<typename Config>
void rewriter_tpl<Config>::cleanup() {
m_cfg.cleanup();
rewriter_core::cleanup();
m_bindings.finalize();
m_shifter.cleanup();
}
template<typename Config>
void rewriter_tpl<Config>::set_bindings(unsigned num_bindings, expr * const * bindings) {
SASSERT(!m_proof_gen);
SASSERT(not_rewriting());
m_bindings.reset();
unsigned i = num_bindings;
while (i > 0) {
--i;
m_bindings.push_back(bindings[i]);
}
}
template<typename Config>
void rewriter_tpl<Config>::set_inv_bindings(unsigned num_bindings, expr * const * bindings) {
SASSERT(!m_proof_gen);
SASSERT(not_rewriting());
m_bindings.reset();
for (unsigned i = 0; i < num_bindings; i++) {
m_bindings.push_back(bindings[i]);
}
}
template<typename Config>
template<bool ProofGen>
void rewriter_tpl<Config>::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) {
SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size());
SASSERT(not_rewriting());
m_root = t;
m_num_qvars = 0;
m_num_steps = 0;
if (visit<ProofGen>(t, RW_UNBOUNDED_DEPTH)) {
result = result_stack().back();
result_stack().pop_back();
SASSERT(result_stack().empty());
if (ProofGen) {
result_pr = result_pr_stack().back();
result_pr_stack().pop_back();
if (result_pr.get() == 0)
result_pr = m().mk_reflexivity(t);
SASSERT(result_pr_stack().empty());
}
return;
}
resume_core<ProofGen>(result, result_pr);
}
/**
\brief Resume the execution when it was interrupted by cancel() or check_max_steps().
*/
template<typename Config>
template<bool ProofGen>
void rewriter_tpl<Config>::resume_core(expr_ref & result, proof_ref & result_pr) {
SASSERT(!frame_stack().empty());
while (!frame_stack().empty()) {
if (m_cancel)
throw rewriter_exception(TACTIC_CANCELED_MSG);
SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size());
frame & fr = frame_stack().back();
expr * t = fr.m_curr;
TRACE("rewriter_step", tout << "step\n" << mk_ismt2_pp(t, m()) << "\n";);
m_num_steps++;
check_max_steps();
if (first_visit(fr) && fr.m_cache_result) {
expr * r = get_cached(t);
if (r) {
result_stack().push_back(r);
if (ProofGen) {
proof * pr = get_cached_pr(t);
result_pr_stack().push_back(pr);
}
frame_stack().pop_back();
set_new_child_flag(t, r);
continue;
}
}
switch (t->get_kind()) {
case AST_APP:
process_app<ProofGen>(to_app(t), fr);
break;
case AST_QUANTIFIER:
process_quantifier<ProofGen>(to_quantifier(t), fr);
break;
case AST_VAR:
frame_stack().pop_back();
process_var<ProofGen>(to_var(t));
break;
default:
UNREACHABLE();
break;
}
}
result = result_stack().back();
result_stack().pop_back();
SASSERT(result_stack().empty());
if (ProofGen) {
result_pr = result_pr_stack().back();
result_pr_stack().pop_back();
if (result_pr.get() == 0)
result_pr = m().mk_reflexivity(m_root);
SASSERT(result_pr_stack().empty());
}
}
template<typename Config>
void rewriter_tpl<Config>::operator()(expr * t, expr_ref & result, proof_ref & result_pr) {
if (m_proof_gen)
main_loop<true>(t, result, result_pr);
else
main_loop<false>(t, result, result_pr);
}
template<typename Config>
void rewriter_tpl<Config>::resume(expr_ref & result, proof_ref & result_pr) {
if (m_proof_gen)
resume_core<true>(result, result_pr);
else
resume_core<false>(result, result_pr);
}

View file

@ -0,0 +1,51 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
rewriter_types.h
Abstract:
Lean and mean rewriter
Author:
Leonardo (leonardo) 2011-04-10
Notes:
--*/
#ifndef _REWRITER_TYPES_H_
#define _REWRITER_TYPES_H_
#include"tactic_exception.h"
/**
\brief Builtin rewrite result status
*/
enum br_status {
BR_REWRITE1, // rewrite the result (bounded by depth 1)
BR_REWRITE2, // rewrite the result (bounded by depth 2)
BR_REWRITE3, // rewrite the result (bounded by depth 3)
BR_REWRITE_FULL, // rewrite the result unbounded
BR_DONE, // done, the result is simplified
BR_FAILED // no builtin rewrite is available
};
#define RW_UNBOUNDED_DEPTH 3
inline br_status unsigned2br_status(unsigned u) {
br_status r = u >= RW_UNBOUNDED_DEPTH ? BR_REWRITE_FULL : static_cast<br_status>(u);
SASSERT((u == 0) == (r == BR_REWRITE1));
SASSERT((u == 1) == (r == BR_REWRITE2));
SASSERT((u == 2) == (r == BR_REWRITE3));
SASSERT((u >= 3) == (r == BR_REWRITE_FULL));
return r;
}
class rewriter_exception : public tactic_exception {
public:
rewriter_exception(char const * msg):tactic_exception(msg) {}
};
#endif

View file

@ -0,0 +1,770 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
th_rewriter.h
Abstract:
Rewriter for applying all builtin (cheap) theory rewrite rules.
Author:
Leonardo (leonardo) 2011-04-07
Notes:
--*/
#include"th_rewriter.h"
#include"bool_rewriter.h"
#include"arith_rewriter.h"
#include"bv_rewriter.h"
#include"datatype_rewriter.h"
#include"array_rewriter.h"
#include"float_rewriter.h"
#include"dl_rewriter.h"
#include"rewriter_def.h"
#include"expr_substitution.h"
#include"ast_smt2_pp.h"
#include"cooperate.h"
#include"var_subst.h"
#include"ast_util.h"
#include"well_sorted.h"
struct th_rewriter_cfg : public default_rewriter_cfg {
bool_rewriter m_b_rw;
arith_rewriter m_a_rw;
bv_rewriter m_bv_rw;
array_rewriter m_ar_rw;
datatype_rewriter m_dt_rw;
float_rewriter m_f_rw;
dl_rewriter m_dl_rw;
arith_util m_a_util;
bv_util m_bv_util;
unsigned long long m_max_memory; // in bytes
unsigned m_max_steps;
bool m_pull_cheap_ite;
bool m_flat;
bool m_cache_all;
bool m_push_ite_arith;
bool m_push_ite_bv;
// substitution support
expr_dependency_ref m_used_dependencies; // set of dependencies of used substitutions
expr_substitution * m_subst;
ast_manager & m() const { return m_b_rw.m(); }
void updt_local_params(params_ref const & p) {
m_flat = p.get_bool(":flat", true);
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
m_pull_cheap_ite = p.get_bool(":pull-cheap-ite", false);
m_cache_all = p.get_bool(":cache-all", false);
m_push_ite_arith = p.get_bool(":push-ite-arith", false);
m_push_ite_bv = p.get_bool(":push-ite-bv", false);
}
void updt_params(params_ref const & p) {
m_b_rw.updt_params(p);
m_a_rw.updt_params(p);
m_bv_rw.updt_params(p);
m_ar_rw.updt_params(p);
updt_local_params(p);
}
bool flat_assoc(func_decl * f) const {
if (!m_flat) return false;
family_id fid = f->get_family_id();
if (fid == null_family_id)
return false;
decl_kind k = f->get_decl_kind();
if (fid == m_b_rw.get_fid())
return k == OP_AND || k == OP_OR;
if (fid == m_a_rw.get_fid())
return k == OP_ADD;
if (fid == m_bv_rw.get_fid())
return k == OP_BADD || k == OP_BOR || k == OP_BAND || k == OP_BXOR;
return false;
}
bool rewrite_patterns() const { return false; }
bool cache_all_results() const { return m_cache_all; }
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("simplifier");
if (memory::get_allocation_size() > m_max_memory)
throw rewriter_exception(TACTIC_MAX_MEMORY_MSG);
return num_steps > m_max_steps;
}
// Return true if t is of the form
// (= t #b0)
// (= t #b1)
// (= #b0 t)
// (= #b1 t)
bool is_eq_bit(expr * t, expr * & x, unsigned & val) {
if (!m().is_eq(t))
return false;
expr * lhs = to_app(t)->get_arg(0);
if (!m_bv_rw.is_bv(lhs))
return false;
if (m_bv_rw.get_bv_size(lhs) != 1)
return false;
expr * rhs = to_app(t)->get_arg(1);
rational v;
unsigned sz;
if (m_bv_rw.is_numeral(lhs, v, sz)) {
x = rhs;
val = v.get_unsigned();
SASSERT(val == 0 || val == 1);
return true;
}
if (m_bv_rw.is_numeral(rhs, v, sz)) {
x = lhs;
val = v.get_unsigned();
SASSERT(val == 0 || val == 1);
return true;
}
return false;
}
// (iff (= x bit1) A)
// --->
// (= x (ite A bit1 bit0))
br_status apply_tamagotchi(expr * lhs, expr * rhs, expr_ref & result) {
expr * x;
unsigned val;
if (is_eq_bit(lhs, x, val)) {
result = m().mk_eq(x, m().mk_ite(rhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1)));
return BR_REWRITE2;
}
if (is_eq_bit(rhs, x, val)) {
result = m().mk_eq(x, m().mk_ite(lhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1)));
return BR_REWRITE2;
}
return BR_FAILED;
}
br_status reduce_app_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
family_id fid = f->get_family_id();
if (fid == null_family_id)
return BR_FAILED;
br_status st = BR_FAILED;
if (fid == m_b_rw.get_fid()) {
decl_kind k = f->get_decl_kind();
if (k == OP_EQ) {
// theory dispatch for =
SASSERT(num == 2);
family_id s_fid = m().get_sort(args[0])->get_family_id();
if (s_fid == m_a_rw.get_fid())
st = m_a_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_bv_rw.get_fid())
st = m_bv_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_dt_rw.get_fid())
st = m_dt_rw.mk_eq_core(args[0], args[1], result);
else if (s_fid == m_f_rw.get_fid())
st = m_f_rw.mk_eq_core(args[0], args[1], result);
if (st != BR_FAILED)
return st;
}
if (k == OP_EQ || k == OP_IFF) {
SASSERT(num == 2);
st = apply_tamagotchi(args[0], args[1], result);
if (st != BR_FAILED)
return st;
}
return m_b_rw.mk_app_core(f, num, args, result);
}
if (fid == m_a_rw.get_fid())
return m_a_rw.mk_app_core(f, num, args, result);
if (fid == m_bv_rw.get_fid())
return m_bv_rw.mk_app_core(f, num, args, result);
if (fid == m_ar_rw.get_fid())
return m_ar_rw.mk_app_core(f, num, args, result);
if (fid == m_dt_rw.get_fid())
return m_dt_rw.mk_app_core(f, num, args, result);
if (fid == m_f_rw.get_fid())
return m_f_rw.mk_app_core(f, num, args, result);
if (fid == m_dl_rw.get_fid())
return m_dl_rw.mk_app_core(f, num, args, result);
return BR_FAILED;
}
// auxiliary function for pull_ite_core
expr * mk_eq_value(expr * lhs, expr * value) {
SASSERT(m().is_value(value));
if (m().is_value(lhs)) {
return lhs == value ? m().mk_true() : m().mk_false();
}
return m().mk_eq(lhs, value);
}
template<bool SWAP>
br_status pull_ite_core(func_decl * p, app * ite, app * value, expr_ref & result) {
if (m().is_eq(p)) {
result = m().mk_ite(ite->get_arg(0),
mk_eq_value(ite->get_arg(1), value),
mk_eq_value(ite->get_arg(2), value));
return BR_REWRITE2;
}
else {
if (SWAP) {
result = m().mk_ite(ite->get_arg(0),
m().mk_app(p, value, ite->get_arg(1)),
m().mk_app(p, value, ite->get_arg(2)));
return BR_REWRITE2;
}
else {
result = m().mk_ite(ite->get_arg(0),
m().mk_app(p, ite->get_arg(1), value),
m().mk_app(p, ite->get_arg(2), value));
return BR_REWRITE2;
}
}
}
// Return true if t is an ite-value-tree form defined as:
// ite-value-tree := (ite c <subtree> <subtree>)
// subtree := value
// | (ite c <subtree> <subtree>)
//
bool is_ite_value_tree(expr * t) {
if (!m().is_ite(t))
return false;
ptr_buffer<app> todo;
todo.push_back(to_app(t));
while (!todo.empty()) {
app * ite = todo.back();
todo.pop_back();
expr * arg1 = ite->get_arg(1);
expr * arg2 = ite->get_arg(2);
if (m().is_ite(arg1) && arg1->get_ref_count() == 1) // do not apply on shared terms, since it may blowup
todo.push_back(to_app(arg1));
else if (!m().is_value(arg1))
return false;
if (m().is_ite(arg2) && arg2->get_ref_count() == 1) // do not apply on shared terms, since it may blowup
todo.push_back(to_app(arg2));
else if (!m().is_value(arg2))
return false;
}
return true;
}
br_status pull_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
if (num == 2 && m().is_bool(f->get_range()) && !m().is_bool(args[0])) {
if (m().is_ite(args[0])) {
if (m().is_value(args[1]))
return pull_ite_core<false>(f, to_app(args[0]), to_app(args[1]), result);
if (m().is_ite(args[1]) && to_app(args[0])->get_arg(0) == to_app(args[1])->get_arg(0)) {
// (p (ite C A1 B1) (ite C A2 B2)) --> (ite (p A1 A2) (p B1 B2))
result = m().mk_ite(to_app(args[0])->get_arg(0),
m().mk_app(f, to_app(args[0])->get_arg(1), to_app(args[1])->get_arg(1)),
m().mk_app(f, to_app(args[0])->get_arg(2), to_app(args[1])->get_arg(2)));
return BR_REWRITE2;
}
}
if (m().is_ite(args[1]) && m().is_value(args[0]))
return pull_ite_core<true>(f, to_app(args[1]), to_app(args[0]), result);
}
family_id fid = f->get_family_id();
if (num == 2 && (fid == m().get_basic_family_id() || fid == m_a_rw.get_fid() || fid == m_bv_rw.get_fid())) {
// (f v3 (ite c v1 v2)) --> (ite v (f v3 v1) (f v3 v2))
if (m().is_value(args[0]) && is_ite_value_tree(args[1]))
return pull_ite_core<true>(f, to_app(args[1]), to_app(args[0]), result);
// (f (ite c v1 v2) v3) --> (ite v (f v1 v3) (f v2 v3))
if (m().is_value(args[1]) && is_ite_value_tree(args[0]))
return pull_ite_core<false>(f, to_app(args[0]), to_app(args[1]), result);
}
return BR_FAILED;
}
br_status pull_ite(expr_ref & result) {
expr * t = result.get();
if (is_app(t)) {
br_status st = pull_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result);
if (st != BR_FAILED)
return st;
}
return BR_DONE;
}
bool is_arith_bv_app(expr * t) const {
if (!is_app(t))
return false;
family_id fid = to_app(t)->get_family_id();
return ((fid == m_a_rw.get_fid() && m_push_ite_arith) ||
(fid == m_bv_rw.get_fid() && m_push_ite_bv));
}
bool get_neutral_elem(app * t, expr_ref & n) {
family_id fid = t->get_family_id();
if (fid == m_a_rw.get_fid()) {
switch (t->get_decl_kind()) {
case OP_ADD: n = m_a_util.mk_numeral(rational(0), m().get_sort(t)); return true;
case OP_MUL: n = m_a_util.mk_numeral(rational(1), m().get_sort(t)); return true;
default:
return false;
}
}
if (fid == m_bv_rw.get_fid()) {
switch (t->get_decl_kind()) {
case OP_BADD: n = m_bv_util.mk_numeral(rational(0), m().get_sort(t)); return true;
case OP_BMUL: n = m_bv_util.mk_numeral(rational(1), m().get_sort(t)); return true;
default:
return false;
}
}
return false;
}
/**
\brief Try to "unify" t1 and t2
Examples
(+ 2 a) (+ 3 a) --> 2, 3, a
(+ 2 a) a --> 2, 0, a
...
*/
bool unify_core(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) {
if (t1->get_num_args() != 2)
return false;
expr * a1 = t1->get_arg(0);
expr * b1 = t1->get_arg(1);
if (t2 == b1) {
if (get_neutral_elem(t1, new_t2)) {
new_t1 = a1;
c = b1;
first = false;
return true;
}
}
else if (t2 == a1) {
if (get_neutral_elem(t1, new_t2)) {
new_t1 = b1;
c = a1;
first = true;
return true;
}
}
else if (is_app_of(t2, t1->get_decl()) && to_app(t2)->get_num_args() == 2) {
expr * a2 = to_app(t2)->get_arg(0);
expr * b2 = to_app(t2)->get_arg(1);
if (b1 == b2) {
new_t1 = a1;
new_t2 = a2;
c = b2;
first = false;
return true;
}
if (a1 == a2) {
new_t1 = b1;
new_t2 = b2;
c = a1;
first = true;
return true;
}
if (t1->get_decl()->is_commutative()) {
if (a1 == b2) {
new_t1 = b1;
new_t2 = a2;
c = a1;
first = true; // doesn't really matter for commutative ops.
return true;
}
if (b1 == a2) {
new_t1 = a1;
new_t2 = b2;
c = b1;
first = false; // doesn't really matter for commutative ops.
return true;
}
}
}
return false;
}
// Return true if t1 and t2 are of the form:
// t + a1*x1 + ... + an*xn
// t' + a1*x1 + ... + an*xn
// Store t in new_t1, t' in new_t2 and (a1*x1 + ... + an*xn) in c.
bool unify_add(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c) {
unsigned num1 = t1->get_num_args();
expr * const * ms1 = t1->get_args();
if (num1 < 2)
return false;
unsigned num2;
expr * const * ms2;
if (m_a_util.is_add(t2)) {
num2 = to_app(t2)->get_num_args();
ms2 = to_app(t2)->get_args();
}
else {
num2 = 1;
ms2 = &t2;
}
if (num1 != num2 && num1 != num2 + 1 && num1 != num2 - 1)
return false;
new_t1 = 0;
new_t2 = 0;
expr_fast_mark1 visited1;
expr_fast_mark2 visited2;
for (unsigned i = 0; i < num1; i++) {
expr * arg = ms1[i];
visited1.mark(arg);
}
for (unsigned i = 0; i < num2; i++) {
expr * arg = ms2[i];
visited2.mark(arg);
if (visited1.is_marked(arg))
continue;
if (new_t2)
return false; // more than one missing term
new_t2 = arg;
}
for (unsigned i = 0; i < num1; i++) {
expr * arg = ms1[i];
if (visited2.is_marked(arg))
continue;
if (new_t1)
return false; // more than one missing term
new_t1 = arg;
}
// terms matched...
bool is_int = m_a_util.is_int(t1);
if (!new_t1)
new_t1 = m_a_util.mk_numeral(rational(0), is_int);
if (!new_t2)
new_t2 = m_a_util.mk_numeral(rational(0), is_int);
// mk common part
ptr_buffer<expr> args;
for (unsigned i = 0; i < num1; i++) {
expr * arg = ms1[i];
if (arg == new_t1.get())
continue;
args.push_back(arg);
}
SASSERT(!args.empty());
if (args.size() == 1)
c = args[0];
else
c = m_a_util.mk_add(args.size(), args.c_ptr());
return true;
}
bool unify(expr * t1, expr * t2, func_decl * & f, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) {
#if 0
// Did not work for ring benchmarks
// Hack for handling more complex cases of + apps
// such as (+ 2 t1 t2 t3) and (+ 3 t3 t2 t1)
if (m_a_util.is_add(t1)) {
first = true; // doesn't matter for AC ops
f = to_app(t1)->get_decl();
if (unify_add(to_app(t1), t2, new_t1, new_t2, c))
return true;
}
if (m_a_util.is_add(t2)) {
first = true; // doesn't matter for AC ops
f = to_app(t2)->get_decl();
if (unify_add(to_app(t2), t1, new_t2, new_t1, c))
return true;
}
#endif
if (is_arith_bv_app(t1)) {
f = to_app(t1)->get_decl();
return unify_core(to_app(t1), t2, new_t1, new_t2, c, first);
}
else {
f = to_app(t2)->get_decl();
return unify_core(to_app(t2), t1, new_t2, new_t1, c, first);
}
}
// Apply transformations of the form
//
// (ite c (+ k1 a) (+ k2 a)) --> (+ (ite c k1 k2) a)
// (ite c (* k1 a) (* k2 a)) --> (* (ite c k1 k2) a)
//
// These transformations are useful for bit-vector problems, since
// they will minimize the number of adders/multipliers/etc
br_status push_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
if (!m().is_ite(f))
return BR_FAILED;
expr * c = args[0];
expr * t = args[1];
expr * e = args[2];
func_decl * f_prime = 0;
expr_ref new_t(m()), new_e(m()), common(m());
bool first;
TRACE("push_ite", tout << "unifying:\n" << mk_ismt2_pp(t, m()) << "\n" << mk_ismt2_pp(e, m()) << "\n";);
if (unify(t, e, f_prime, new_t, new_e, common, first)) {
if (first)
result = m().mk_app(f_prime, common, m().mk_ite(c, new_t, new_e));
else
result = m().mk_app(f_prime, m().mk_ite(c, new_t, new_e), common);
return BR_DONE;
}
TRACE("push_ite", tout << "failed\n";);
return BR_FAILED;
}
br_status push_ite(expr_ref & result) {
expr * t = result.get();
if (m().is_ite(t)) {
br_status st = push_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result);
if (st != BR_FAILED)
return st;
}
return BR_DONE;
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = 0;
br_status st = reduce_app_core(f, num, args, result);
if (st != BR_DONE && st != BR_FAILED) {
CTRACE("th_rewriter_step", st != BR_FAILED,
tout << f->get_name() << "\n";
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";
tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";);
return st;
}
if (m_push_ite_bv || m_push_ite_arith) {
if (st == BR_FAILED)
st = push_ite(f, num, args, result);
else
st = push_ite(result);
}
if (m_pull_cheap_ite) {
if (st == BR_FAILED)
st = pull_ite(f, num, args, result);
else
st = pull_ite(result);
}
CTRACE("th_rewriter_step", st != BR_FAILED,
tout << f->get_name() << "\n";
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";
tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";);
return st;
}
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
quantifier_ref q1(m());
proof * p1 = 0;
if (is_quantifier(new_body) &&
to_quantifier(new_body)->is_forall() == old_q->is_forall() &&
!old_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(old_q->get_num_decls(), old_q->get_decl_sorts());
names.append(old_q->get_num_decls(), old_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().mk_quantifier(old_q->is_forall(),
sorts.size(),
sorts.c_ptr(),
names.c_ptr(),
nested_q->get_expr(),
std::min(old_q->get_weight(), nested_q->get_weight()),
old_q->get_qid(),
old_q->get_skid(),
0, 0, 0, 0);
SASSERT(is_well_sorted(m(), q1));
if (m().proofs_enabled()) {
SASSERT(old_q->get_expr() == new_body);
p1 = m().mk_pull_quant(old_q, q1);
}
}
else {
ptr_buffer<expr> new_patterns_buf;
ptr_buffer<expr> new_no_patterns_buf;
new_patterns_buf.append(old_q->get_num_patterns(), new_patterns);
new_no_patterns_buf.append(old_q->get_num_no_patterns(), new_no_patterns);
remove_duplicates(new_patterns_buf);
remove_duplicates(new_no_patterns_buf);
q1 = m().update_quantifier(old_q,
new_patterns_buf.size(), new_patterns_buf.c_ptr(), new_no_patterns_buf.size(), new_no_patterns_buf.c_ptr(),
new_body);
TRACE("reduce_quantifier", tout << mk_ismt2_pp(old_q, m()) << "\n----->\n" << mk_ismt2_pp(q1, m()) << "\n";);
SASSERT(is_well_sorted(m(), q1));
}
elim_unused_vars(m(), q1, result);
TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m()) << "\n";);
result_pr = 0;
if (m().proofs_enabled()) {
proof * p2 = 0;
if (q1.get() != result.get())
p2 = m().mk_elim_unused_vars(q1, result);
result_pr = m().mk_transitivity(p1, p2);
}
return true;
}
th_rewriter_cfg(ast_manager & m, params_ref const & p):
m_b_rw(m, p),
m_a_rw(m, p),
m_bv_rw(m, p),
m_ar_rw(m, p),
m_dt_rw(m),
m_f_rw(m, p),
m_dl_rw(m),
m_a_util(m),
m_bv_util(m),
m_used_dependencies(m),
m_subst(0) {
updt_local_params(p);
}
void set_substitution(expr_substitution * s) {
reset();
m_subst = s;
}
void reset() {
m_subst = 0;
}
bool get_subst(expr * s, expr * & t, proof * & pr) {
if (m_subst == 0)
return false;
expr_dependency * d = 0;
if (m_subst->find(s, t, pr, d)) {
m_used_dependencies = m().mk_join(m_used_dependencies, d);
return true;
}
return false;
}
void set_cancel(bool f) {
m_a_rw.set_cancel(f);
}
};
template class rewriter_tpl<th_rewriter_cfg>;
struct th_rewriter::imp : public rewriter_tpl<th_rewriter_cfg> {
th_rewriter_cfg m_cfg;
imp(ast_manager & m, params_ref const & p):
rewriter_tpl<th_rewriter_cfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(m, p) {
}
};
th_rewriter::th_rewriter(ast_manager & m, params_ref const & p):
m_params(p) {
m_imp = alloc(imp, m, p);
}
ast_manager & th_rewriter::m() const {
return m_imp->m();
}
void th_rewriter::updt_params(params_ref const & p) {
m_params = p;
m_imp->cfg().updt_params(p);
}
void th_rewriter::get_param_descrs(param_descrs & r) {
bool_rewriter::get_param_descrs(r);
arith_rewriter::get_param_descrs(r);
bv_rewriter::get_param_descrs(r);
array_rewriter::get_param_descrs(r);
insert_max_memory(r);
insert_max_steps(r);
r.insert(":push-ite-arith", CPK_BOOL, "(default: false) push if-then-else over arithmetic terms.");
r.insert(":push-ite-bv", CPK_BOOL, "(default: false) push if-then-else over bit-vector terms.");
r.insert(":pull-cheap-ite", CPK_BOOL, "(default: false) pull if-then-else terms when cheap.");
r.insert(":cache-all", CPK_BOOL, "(default: false) cache all intermediate results.");
}
th_rewriter::~th_rewriter() {
dealloc(m_imp);
}
unsigned th_rewriter::get_cache_size() const {
return m_imp->get_cache_size();
}
unsigned th_rewriter::get_num_steps() const {
return m_imp->get_num_steps();
}
void th_rewriter::set_cancel(bool f) {
#pragma omp critical (th_rewriter)
{
m_imp->set_cancel(f);
m_imp->cfg().set_cancel(f);
}
}
void th_rewriter::cleanup() {
ast_manager & m = m_imp->m();
#pragma omp critical (th_rewriter)
{
dealloc(m_imp);
m_imp = alloc(imp, m, m_params);
}
}
void th_rewriter::reset() {
m_imp->reset();
m_imp->cfg().reset();
}
void th_rewriter::operator()(expr_ref & term) {
expr_ref result(term.get_manager());
m_imp->operator()(term, result);
term = result;
}
void th_rewriter::operator()(expr * t, expr_ref & result) {
m_imp->operator()(t, result);
}
void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) {
m_imp->operator()(t, result, result_pr);
}
void th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) {
m_imp->operator()(n, num_bindings, bindings, result);
}
void th_rewriter::set_substitution(expr_substitution * s) {
m_imp->reset(); // reset the cache
m_imp->cfg().set_substitution(s);
}
expr_dependency * th_rewriter::get_used_dependencies() {
return m_imp->cfg().m_used_dependencies;
}
void th_rewriter::reset_used_dependencies() {
if (get_used_dependencies() != 0) {
set_substitution(m_imp->cfg().m_subst); // reset cache preserving subst
m_imp->cfg().m_used_dependencies = 0;
}
}

View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
th_rewriter.h
Abstract:
Rewriter for applying all builtin (cheap) theory rewrite rules.
Author:
Leonardo (leonardo) 2011-04-07
Notes:
--*/
#ifndef _TH_REWRITER_H_
#define _TH_REWRITER_H_
#include"ast.h"
#include"rewriter_types.h"
#include"params.h"
class expr_substitution;
class th_rewriter {
struct imp;
imp * m_imp;
params_ref m_params;
public:
th_rewriter(ast_manager & m, params_ref const & p = params_ref());
~th_rewriter();
ast_manager & m () const;
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
unsigned get_cache_size() const;
unsigned get_num_steps() const;
void operator()(expr_ref& term);
void operator()(expr * t, expr_ref & result);
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result);
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
void set_cancel(bool f);
void cleanup();
void reset();
void set_substitution(expr_substitution * s);
// Dependency tracking is very coarse.
// The rewriter just keeps accumulating the dependencies of the used substitutions.
// The following methods are used to recover and reset them.
// Remark: reset_used_dependecies will reset the internal cache if get_used_dependencies() != 0
expr_dependency * get_used_dependencies();
void reset_used_dependencies();
};
#endif

View file

@ -0,0 +1,211 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
var_subst.cpp
Abstract:
Variable substitution.
Author:
Leonardo (leonardo) 2008-01-10
Notes:
--*/
#include"var_subst.h"
#include"used_vars.h"
#include"ast_ll_pp.h"
#include"ast_pp.h"
#include"ast_smt2_pp.h"
#include"well_sorted.h"
#include"for_each_expr.h"
void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(is_well_sorted(result.m(), n));
m_reducer.reset();
if (m_std_order)
m_reducer.set_inv_bindings(num_args, args);
else
m_reducer.set_bindings(num_args, args);
m_reducer(n, result);
SASSERT(is_well_sorted(m_reducer.m(), result));
TRACE("var_subst_bug",
tout << "m_std_order: " << m_std_order << "\n" << mk_ismt2_pp(n, m_reducer.m()) << "\nusing\n";
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m_reducer.m()) << "\n";
tout << "\n------>\n";
tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";);
}
void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) {
SASSERT(is_well_sorted(m, q));
if (is_ground(q->get_expr())) {
// ignore patterns if the body is a ground formula.
result = q->get_expr();
return;
}
if (!q->may_have_unused_vars()) {
result = q;
return;
}
used_vars used;
used.process(q->get_expr());
unsigned num_patterns = q->get_num_patterns();
for (unsigned i = 0; i < num_patterns; i++)
used.process(q->get_pattern(i));
unsigned num_no_patterns = q->get_num_no_patterns();
for (unsigned i = 0; i < num_no_patterns; i++)
used.process(q->get_no_pattern(i));
unsigned num_decls = q->get_num_decls();
if (used.uses_all_vars(num_decls)) {
q->set_no_unused_vars();
result = q;
return;
}
ptr_buffer<sort> used_decl_sorts;
buffer<symbol> used_decl_names;
for (unsigned i = 0; i < num_decls; ++i) {
if (used.contains(num_decls - i - 1)) {
used_decl_sorts.push_back(q->get_decl_sort(i));
used_decl_names.push_back(q->get_decl_name(i));
}
}
unsigned num_removed = 0;
expr_ref_buffer var_mapping(m);
int next_idx = 0;
unsigned sz = used.get_max_found_var_idx_plus_1();
for (unsigned i = 0; i < num_decls; ++i) {
sort * s = used.contains(i);
if (s) {
var_mapping.push_back(m.mk_var(next_idx, s));
next_idx++;
}
else {
num_removed++;
var_mapping.push_back(0);
}
}
// (VAR 0) is in the first position of var_mapping.
for (unsigned i = num_decls; i < sz; i++) {
sort * s = used.contains(i);
if (s)
var_mapping.push_back(m.mk_var(i - num_removed, s));
else
var_mapping.push_back(0);
}
// Remark:
// (VAR 0) should be in the last position of var_mapping.
// ...
// (VAR (var_mapping.size() - 1)) should be in the first position.
std::reverse(var_mapping.c_ptr(), var_mapping.c_ptr() + var_mapping.size());
expr_ref new_expr(m);
var_subst subst(m);
subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr);
if (num_removed == num_decls) {
result = new_expr;
return;
}
expr_ref tmp(m);
expr_ref_buffer new_patterns(m);
expr_ref_buffer new_no_patterns(m);
for (unsigned i = 0; i < num_patterns; i++) {
subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp);
new_patterns.push_back(tmp);
}
for (unsigned i = 0; i < num_no_patterns; i++) {
subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp);
new_no_patterns.push_back(tmp);
}
result = m.mk_quantifier(q->is_forall(),
used_decl_sorts.size(),
used_decl_sorts.c_ptr(),
used_decl_names.c_ptr(),
new_expr,
q->get_weight(),
q->get_qid(),
q->get_skid(),
num_patterns,
new_patterns.c_ptr(),
num_no_patterns,
new_no_patterns.c_ptr());
to_quantifier(result)->set_no_unused_vars();
SASSERT(is_well_sorted(m, result));
}
void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result) {
var_subst subst(m);
expr_ref new_expr(m);
subst(q->get_expr(), q->get_num_decls(), exprs, new_expr);
TRACE("var_subst", tout << mk_pp(q, m) << "\n" << mk_pp(new_expr, m) << "\n";);
inv_var_shifter shift(m);
shift(new_expr, q->get_num_decls(), result);
SASSERT(is_well_sorted(m, result));
TRACE("instantiate_bug", tout << mk_ismt2_pp(q, m) << "\nusing\n";
for (unsigned i = 0; i < q->get_num_decls(); i++) tout << mk_ismt2_pp(exprs[i], m) << "\n";
tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";);
}
static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector<sort>& sorts) {
ast_mark mark;
ptr_vector<expr> todo;
todo.push_back(e);
while (!todo.empty()) {
e = todo.back();
todo.pop_back();
if (mark.is_marked(e)) {
continue;
}
mark.mark(e, true);
switch(e->get_kind()) {
case AST_QUANTIFIER: {
quantifier* q = to_quantifier(e);
get_free_vars_offset(q->get_expr(), offset+q->get_num_decls(), sorts);
break;
}
case AST_VAR: {
var* v = to_var(e);
if (v->get_idx() >= offset) {
unsigned idx = v->get_idx()-offset;
if (sorts.size() <= idx) {
sorts.resize(idx+1);
}
if (!sorts[idx]) {
sorts[idx] = v->get_sort();
}
SASSERT(sorts[idx] == v->get_sort());
}
break;
}
case AST_APP: {
app* a = to_app(e);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
todo.push_back(a->get_arg(i));
}
break;
}
default:
UNREACHABLE();
}
}
}
void get_free_vars(expr* e, ptr_vector<sort>& sorts) {
get_free_vars_offset(e, 0, sorts);
}

View file

@ -0,0 +1,78 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
var_subst.h
Abstract:
Variable substitution.
Author:
Leonardo (leonardo) 2008-01-10
Notes:
--*/
#ifndef _VAR_SUBST_H_
#define _VAR_SUBST_H_
#include"rewriter.h"
/**
\brief Alias for var_shifter class.
*/
typedef var_shifter shift_vars;
/**
\brief Variable substitution functor. It substitutes variables by expressions.
The expressions may contain variables.
*/
class var_subst {
beta_reducer m_reducer;
bool m_std_order;
public:
var_subst(ast_manager & m, bool std_order = true):m_reducer(m), m_std_order(std_order) {}
bool std_order() const { return m_std_order; }
/**
When std_order() == true,
I'm using the same standard used in quantifier instantiation.
(VAR 0) is stored in the last position of the array.
...
(VAR (num_args - 1)) is stored in the first position of the array.
Otherwise, (VAR 0) is stored in the first position, (VAR 1) in the second, and so on.
*/
void operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result);
void reset() { m_reducer.reset(); }
};
/**
\brief Eliminate the unused variables from \c q. Store the result in \c r.
*/
void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & r);
/**
\brief Instantiate quantifier q using the given exprs.
The vector exprs should contain at least q->get_num_decls() expressions.
I'm using the same standard used in quantifier instantiation.
(VAR 0) is stored in the last position of the array.
...
(VAR (q->get_num_decls() - 1)) is stored in the first position of the array.
*/
void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result);
/**
\brief Enumerate set of free variables in expression.
Return the sorts of the free variables.
*/
void get_free_vars(expr* e, ptr_vector<sort>& sorts);
#endif