mirror of
https://github.com/Z3Prover/z3
synced 2025-08-11 13:40:52 +00:00
Reorganizing code base
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
9a84cba6c9
commit
ded42feeb6
62 changed files with 4 additions and 115 deletions
1529
src/rewriter/arith_rewriter.cpp
Normal file
1529
src/rewriter/arith_rewriter.cpp
Normal file
File diff suppressed because it is too large
Load diff
183
src/rewriter/arith_rewriter.h
Normal file
183
src/rewriter/arith_rewriter.h
Normal 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
|
359
src/rewriter/array_rewriter.cpp
Normal file
359
src/rewriter/array_rewriter.cpp
Normal 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;
|
||||
}
|
||||
|
65
src/rewriter/array_rewriter.h
Normal file
65
src/rewriter/array_rewriter.h
Normal 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
|
989
src/rewriter/bool_rewriter.cpp
Normal file
989
src/rewriter/bool_rewriter.cpp
Normal 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>;
|
199
src/rewriter/bool_rewriter.h
Normal file
199
src/rewriter/bool_rewriter.h
Normal 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
|
2076
src/rewriter/bv_rewriter.cpp
Normal file
2076
src/rewriter/bv_rewriter.cpp
Normal file
File diff suppressed because it is too large
Load diff
171
src/rewriter/bv_rewriter.h
Normal file
171
src/rewriter/bv_rewriter.h
Normal 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
|
111
src/rewriter/datatype_rewriter.cpp
Normal file
111
src/rewriter/datatype_rewriter.cpp
Normal 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;
|
||||
}
|
35
src/rewriter/datatype_rewriter.h
Normal file
35
src/rewriter/datatype_rewriter.h
Normal 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
|
57
src/rewriter/dl_rewriter.cpp
Normal file
57
src/rewriter/dl_rewriter.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
33
src/rewriter/dl_rewriter.h
Normal file
33
src/rewriter/dl_rewriter.h
Normal 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
|
441
src/rewriter/float_rewriter.cpp
Normal file
441
src/rewriter/float_rewriter.cpp
Normal 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;
|
||||
}
|
72
src/rewriter/float_rewriter.h
Normal file
72
src/rewriter/float_rewriter.h
Normal 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
|
167
src/rewriter/poly_rewriter.h
Normal file
167
src/rewriter/poly_rewriter.h
Normal 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
|
933
src/rewriter/poly_rewriter_def.h
Normal file
933
src/rewriter/poly_rewriter_def.h
Normal 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());
|
||||
}
|
402
src/rewriter/rewriter.cpp
Normal file
402
src/rewriter/rewriter.cpp
Normal 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/rewriter/rewriter.h
Normal file
399
src/rewriter/rewriter.h
Normal 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
|
642
src/rewriter/rewriter_def.h
Normal file
642
src/rewriter/rewriter_def.h
Normal 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);
|
||||
}
|
51
src/rewriter/rewriter_types.h
Normal file
51
src/rewriter/rewriter_types.h
Normal 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
|
770
src/rewriter/th_rewriter.cpp
Normal file
770
src/rewriter/th_rewriter.cpp
Normal 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;
|
||||
}
|
||||
}
|
65
src/rewriter/th_rewriter.h
Normal file
65
src/rewriter/th_rewriter.h
Normal 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
|
211
src/rewriter/var_subst.cpp
Normal file
211
src/rewriter/var_subst.cpp
Normal 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);
|
||||
}
|
78
src/rewriter/var_subst.h
Normal file
78
src/rewriter/var_subst.h
Normal 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
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue