3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-11-15 18:35:44 +00:00

reorganizing the code

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

View file

@ -0,0 +1,208 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
array_property_expander.cpp
Abstract:
Expand array operations for the array property fragment formulas.
Author:
Nikolaj Bjorner (nbjorner) 2010-16-12
Revision History:
--*/
#include"array_property_expander.h"
#include"obj_hashtable.h"
#include"var_subst.h"
#include"array_decl_plugin.h"
#include"for_each_expr.h"
array_property_expander::array_property_expander(ast_manager& m):
m_manager(m) {
}
namespace array_property_exp {
class proc {
ast_manager& m_manager;
unsigned& m_offset;
expr_ref_vector m_trail;
family_id m_fid;
array_util m_util;
obj_map<expr, expr*> m_mem;
void insert(expr* a, expr* b) {
m_trail.push_back(b);
m_mem.insert(a, b);
}
public:
proc(ast_manager& m, unsigned& offset) :
m_manager(m),
m_offset(offset),
m_trail(m),
m_fid(m.get_family_id("array")),
m_util(m)
{}
expr* find(expr* e) {
expr* result = 0;
VERIFY(m_mem.find(e, result));
return result;
}
void operator()(var* n) { insert(n, n); }
void operator()(quantifier* q) {
expr* e = find(q->get_expr());
quantifier* q2 = m_manager.update_quantifier(q, e);
insert(q, q2);
}
void operator()(app* n) {
ast_manager& m = m_manager;
unsigned num_args = n->get_num_args();
ptr_buffer<expr> args;
for (unsigned i = 0; i < num_args; ++i) {
args.push_back(find(n->get_arg(i)));
}
if (m_manager.is_eq(n) && m_util.is_array(args[0])) {
visit_eq(n);
return;
}
if (m_manager.is_distinct(n) && num_args > 0 && m_util.is_array(args[0])) {
ptr_buffer<expr> eqs;
for (unsigned i = 0; i < num_args; ++i) {
for (unsigned j = i + 1; j < num_args; ++j) {
eqs.push_back(m.mk_not(m.mk_eq(args[i], args[j])));
}
}
insert(n, m.mk_and(eqs.size(), eqs.c_ptr()));
return;
}
if (m_util.is_select(n)) {
SASSERT(num_args > 0);
// select(store(A,i,v),j) -> ite(i = j, v, select(A,j))
if (m_util.is_store(args[0])) {
app* a = to_app(args[0]);
expr* b = find(a->get_arg(0));
expr* v = find(a->get_arg(a->get_num_args()-1));
ptr_buffer<expr> eqs;
SASSERT(num_args + 1 == a->get_num_args());
for (unsigned i = 1; i < num_args; ++i) {
eqs.push_back(m.mk_eq(args[i], find(a->get_arg(i))));
}
expr* r = m.mk_ite(m.mk_and(eqs.size(), eqs.c_ptr()), v, mk_select(b, num_args-1, args.c_ptr()+1));
insert(n, r);
return;
}
// select(ite(a,b,c),i) -> ite(a, select(b,i), select(c, i))
if (m.is_ite(args[0])) {
app* k = to_app(args[0]);
expr* a = k->get_arg(0);
expr* b = mk_select(k->get_arg(1), args.size()-1, args.c_ptr()+1);
expr* c = mk_select(k->get_arg(2), args.size()-1, args.c_ptr()+1);
expr* r = m.mk_ite(a, b, c);
insert(n, r);
return;
}
// select(map_f(A,B),i) -> f(select(A,i), select(B,i))
if (m_util.is_map(args[0])) {
app* a = to_app(args[0]);
func_decl* f = a->get_decl();
SASSERT(f->get_num_parameters() == 1);
SASSERT(f->get_parameter(0).is_ast());
SASSERT(is_func_decl(f->get_parameter(0).get_ast()));
parameter p = f->get_parameter(0);
func_decl* d = to_func_decl(p.get_ast());
ptr_buffer<expr> args2;
for (unsigned i = 0; i < a->get_num_args(); ++i) {
args2.push_back(mk_select(find(a->get_arg(i)), args.size()-1, args.c_ptr()+1));
}
expr* r = m.mk_app(d, args2.size(), args2.c_ptr());
insert(n, r);
return;
}
// select(const v, i) -> v
if (m_util.is_const(args[0])) {
insert(n, to_app(args[0])->get_arg(0));
return;
}
}
expr* r = m_manager.mk_app(n->get_decl(), args.size(), args.c_ptr());
insert(n, r);
}
private:
void visit_eq(app* eq) {
ast_manager& m = m_manager;
SASSERT(m.is_eq(eq));
sort* s = m.get_sort(eq->get_arg(0));
SASSERT(is_sort_of(s, m_fid, ARRAY_SORT));
// sort* rng = get_array_range(s);
unsigned arity = get_array_arity(s);
shift_vars sh(m);
expr_ref e1(m), e2(m);
sh(find(eq->get_arg(0)), arity, e1);
sh(find(eq->get_arg(1)), arity, e2);
expr_ref_vector args(m);
buffer<symbol> names;
ptr_buffer<sort> sorts;
args.push_back(e1);
for (unsigned i = 0; i < arity; ++i) {
args.push_back(m.mk_var(i, get_array_domain(s, i)));
sorts.push_back(get_array_domain(s, arity - i - 1));
names.push_back(symbol(m_offset++));
}
e1 = mk_select(args.size(), args.c_ptr());
args[0] = e2;
e2 = mk_select(args.size(), args.c_ptr());
e1 = m.mk_eq(e1, e2);
e1 = m.mk_quantifier(true, arity, sorts.c_ptr(), names.c_ptr(), e1, 1);
insert(eq, e1);
}
app* mk_select(unsigned n, expr* const* args) {
return m_manager.mk_app(m_fid, OP_SELECT, 0, 0, n, args);
}
app* mk_select(expr* a, unsigned n, expr* const* args) {
ptr_buffer<expr> args2;
args2.push_back(a);
args2.append(n, args);
return mk_select(n+1, args2.c_ptr());
}
};
};
void array_property_expander::operator()(unsigned num_fmls, expr* const* fmls, expr_ref_vector& result) {
ast_manager& m = m_manager;
unsigned offset = 0;
for (unsigned i = 0; i < num_fmls; ++i) {
bool change = false;
expr_ref e(m);
result.push_back(fmls[i]);
do {
array_property_exp::proc p(m, offset);
e = result[i].get();
for_each_expr(p, e);
result[i] = p.find(e);
change = e != result[i].get();
}
while (change);
}
}

View file

@ -0,0 +1,33 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
array_property_expander.h
Abstract:
Expand array operations for the array property fragment formulas.
Author:
Nikolaj Bjorner (nbjorner) 2010-16-12
Revision History:
--*/
#ifndef _ARRAY_PROPERTY_EXPANDER_H_
#define _ARRAY_PROPERTY_EXPANDER_H_
#include"ast.h"
class array_property_expander {
ast_manager& m_manager;
public:
array_property_expander(ast_manager& m);
void operator()(unsigned num_fmls, expr* const* fmls, expr_ref_vector& result);
};
#endif /* _ARRAY_PROPERTY_EXPANDER_H_ */

View file

@ -0,0 +1,103 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
array_property_recognizer.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2010-16-12
Revision History:
--*/
#include"array_decl_plugin.h"
#include"array_property_recognizer.h"
#include"for_each_expr.h"
array_property_recognizer::array_property_recognizer(ast_manager& m):
m_manager(m) {}
namespace is_array_property_ns {
struct bad {};
class proc {
ast_manager& m_manager;
family_id m_fid;
bool m_has_quantifier;
void check_array_sort(expr* n) {
if (is_sort_of(m_manager.get_sort(n), m_fid, ARRAY_SORT)) {
throw bad();
}
}
public:
proc(ast_manager& m) :
m_manager(m),
m_fid(m.get_family_id("array")),
m_has_quantifier(false) {}
bool has_quantifier() const { return m_has_quantifier; }
void operator()(var* n) {
check_array_sort(n);
}
void operator()(quantifier * ) {
m_has_quantifier = true;
}
void operator()(app* n) {
unsigned num_args = n->get_num_args();
if (m_manager.is_eq(n) || m_manager.is_distinct(n)) {
return;
}
family_id fid = n->get_family_id();
if (fid == m_fid) {
switch(n->get_decl_kind()) {
case OP_STORE:
for (unsigned i = 1; i + 1 < num_args; ++i) {
check_array_sort(n->get_arg(i));
}
return;
case OP_SELECT:
for (unsigned i = 1; i < num_args; ++i) {
check_array_sort(n->get_arg(i));
}
return;
case OP_CONST_ARRAY:
case OP_ARRAY_MAP:
return;
default:
throw bad();
}
}
// arrays cannot be arguments of other functions.
for (unsigned i = 0; i < num_args; ++i) {
check_array_sort(n->get_arg(i));
}
}
};
};
bool array_property_recognizer::operator()(unsigned num_fmls, expr* const* fmls) {
is_array_property_ns::proc p(m_manager);
try {
for (unsigned i = 0; i < num_fmls; ++i) {
for_each_expr(p, fmls[i]);
}
}
catch (is_array_property_ns::bad) {
return false;
}
return p.has_quantifier();
}

View file

@ -0,0 +1,33 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
array_property_recognizer.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2010-16-12
Revision History:
--*/
#ifndef _ARRAY_PROPERTY_RECOGNIZER_H_
#define _ARRAY_PROPERTY_RECOGNIZER_H_
#include"ast.h"
class array_property_recognizer {
ast_manager& m_manager;
public:
array_property_recognizer(ast_manager& m);
bool operator()(unsigned num_fmls, expr* const* fmls);
};
#endif /* _ARRAY_PROPERTY_RECOGNIZER_H_ */

View file

@ -0,0 +1,223 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_finder.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
--*/
#include"macro_finder.h"
#include"occurs.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
bool macro_finder::is_macro(expr * n, app * & head, expr * & def) {
if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
return false;
TRACE("macro_finder", tout << "processing: " << mk_pp(n, m_manager) << "\n";);
expr * body = to_quantifier(n)->get_expr();
unsigned num_decls = to_quantifier(n)->get_num_decls();
return m_util.is_simple_macro(body, num_decls, head, def);
}
/**
\brief Detect macros of the form
1- (forall (X) (= (+ (f X) (R X)) c))
2- (forall (X) (<= (+ (f X) (R X)) c))
3- (forall (X) (>= (+ (f X) (R X)) c))
The second and third cases are first converted into
(forall (X) (= (f X) (+ c (* -1 (R x)) (k X))))
and
(forall (X) (<= (k X) 0)) when case 2
(forall (X) (>= (k X) 0)) when case 3
For case 2 & 3, the new quantifiers are stored in new_exprs and new_prs.
*/
bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
return false;
arith_simplifier_plugin * as = get_arith_simp();
arith_util & autil = as->get_arith_util();
expr * body = to_quantifier(n)->get_expr();
unsigned num_decls = to_quantifier(n)->get_num_decls();
if (!autil.is_le(body) && !autil.is_ge(body) && !m_manager.is_eq(body))
return false;
if (!as->is_add(to_app(body)->get_arg(0)))
return false;
app_ref head(m_manager);
expr_ref def(m_manager);
bool inv = false;
if (!m_util.is_arith_macro(body, num_decls, head, def, inv))
return false;
app_ref new_body(m_manager);
if (!inv || m_manager.is_eq(body))
new_body = m_manager.mk_app(to_app(body)->get_decl(), head, def);
else if (as->is_le(body))
new_body = autil.mk_ge(head, def);
else
new_body = autil.mk_le(head, def);
quantifier_ref new_q(m_manager);
new_q = m_manager.update_quantifier(to_quantifier(n), new_body);
proof * new_pr = 0;
if (m_manager.proofs_enabled()) {
proof * rw = m_manager.mk_rewrite(n, new_q);
new_pr = m_manager.mk_modus_ponens(pr, rw);
}
if (m_manager.is_eq(body)) {
return m_macro_manager.insert(head->get_decl(), new_q, new_pr);
}
// is ge or le
//
TRACE("macro_finder", tout << "is_arith_macro: is_ge or is_le\n";);
func_decl * f = head->get_decl();
func_decl * k = m_manager.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range());
app * k_app = m_manager.mk_app(k, head->get_num_args(), head->get_args());
expr_ref_buffer new_rhs_args(m_manager);
expr_ref new_rhs2(m_manager);
as->mk_add(def, k_app, new_rhs2);
expr * body1 = m_manager.mk_eq(head, new_rhs2);
expr * body2 = m_manager.mk_app(new_body->get_decl(), k_app, as->mk_numeral(rational(0)));
quantifier * q1 = m_manager.update_quantifier(new_q, body1);
expr * patterns[1] = { m_manager.mk_pattern(k_app) };
quantifier * q2 = m_manager.update_quantifier(new_q, 1, patterns, body2);
new_exprs.push_back(q1);
new_exprs.push_back(q2);
if (m_manager.proofs_enabled()) {
// new_pr : new_q
// rw : [rewrite] new_q ~ q1 & q2
// mp : [modus_pones new_pr rw] q1 & q2
// pr1 : [and-elim mp] q1
// pr2 : [and-elim mp] q2
app * q1q2 = m_manager.mk_and(q1,q2);
proof * rw = m_manager.mk_oeq_rewrite(new_q, q1q2);
proof * mp = m_manager.mk_modus_ponens(new_pr, rw);
proof * pr1 = m_manager.mk_and_elim(mp, 0);
proof * pr2 = m_manager.mk_and_elim(mp, 1);
new_prs.push_back(pr1);
new_prs.push_back(pr2);
}
return true;
}
/**
n is of the form: (forall (X) (iff (= (f X) t) def[X]))
Convert it into:
(forall (X) (= (f X) (ite def[X] t (k X))))
(forall (X) (not (= (k X) t)))
where k is a fresh symbol.
The new quantifiers and proofs are stored in new_exprs and new_prs
*/
static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, expr * def, quantifier * q, proof * pr,
expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
func_decl * f = head->get_decl();
func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range());
app * k_app = m.mk_app(k, head->get_num_args(), head->get_args());
app * ite = m.mk_ite(def, t, k_app);
app * body_1 = m.mk_eq(head, ite);
app * body_2 = m.mk_not(m.mk_eq(k_app, t));
quantifier * q1 = m.update_quantifier(q, body_1);
expr * pats[1] = { m.mk_pattern(k_app) };
quantifier * q2 = m.update_quantifier(q, 1, pats, body_2); // erase patterns
new_exprs.push_back(q1);
new_exprs.push_back(q2);
if (m.proofs_enabled()) {
// r : [rewrite] q ~ q1 & q2
// pr : q
// mp : [modus_pones pr pr1] q1 & q2
// pr1 : [and-elim mp] q1
// pr2 : [and-elim mp] q2
app * q1q2 = m.mk_and(q1,q2);
proof * r = m.mk_oeq_rewrite(q, q1q2);
proof * mp = m.mk_modus_ponens(pr, r);
proof * pr1 = m.mk_and_elim(mp, 0);
proof * pr2 = m.mk_and_elim(mp, 1);
new_prs.push_back(pr1);
new_prs.push_back(pr2);
}
}
macro_finder::macro_finder(ast_manager & m, macro_manager & mm):
m_manager(m),
m_macro_manager(mm),
m_util(mm.get_util()) {
}
macro_finder::~macro_finder() {
}
bool macro_finder::expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
TRACE("macro_finder", tout << "starting expand_macros:\n";
m_macro_manager.display(tout););
bool found_new_macro = false;
for (unsigned i = 0; i < num; i++) {
expr * n = exprs[i];
proof * pr = m_manager.proofs_enabled() ? prs[i] : 0;
expr_ref new_n(m_manager);
proof_ref new_pr(m_manager);
m_macro_manager.expand_macros(n, pr, new_n, new_pr);
app * head = 0;
expr * def = 0;
app * t = 0;
if (is_macro(new_n, head, def) && m_macro_manager.insert(head->get_decl(), to_quantifier(new_n.get()), new_pr)) {
TRACE("macro_finder_found", tout << "found new macro: " << head->get_decl()->get_name() << "\n" << mk_pp(new_n, m_manager) << "\n";);
found_new_macro = true;
}
else if (is_arith_macro(new_n, new_pr, new_exprs, new_prs)) {
TRACE("macro_finder_found", tout << "found new arith macro:\n" << mk_pp(new_n, m_manager) << "\n";);
found_new_macro = true;
}
else if (m_util.is_pseudo_predicate_macro(new_n, head, t, def)) {
TRACE("macro_finder_found", tout << "found new pseudo macro:\n" << mk_pp(head, m_manager) << "\n" << mk_pp(t, m_manager) << "\n" <<
mk_pp(def, m_manager) << "\n";);
pseudo_predicate_macro2macro(m_manager, head, t, def, to_quantifier(new_n), new_pr, new_exprs, new_prs);
found_new_macro = true;
}
else {
new_exprs.push_back(new_n);
if (m_manager.proofs_enabled())
new_prs.push_back(new_pr);
}
}
return found_new_macro;
}
void macro_finder::operator()(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
TRACE("macro_finder", tout << "processing macros...\n";);
expr_ref_vector _new_exprs(m_manager);
proof_ref_vector _new_prs(m_manager);
if (expand_macros(num, exprs, prs, _new_exprs, _new_prs)) {
while (true) {
expr_ref_vector old_exprs(m_manager);
proof_ref_vector old_prs(m_manager);
_new_exprs.swap(old_exprs);
_new_prs.swap(old_prs);
SASSERT(_new_exprs.empty());
SASSERT(_new_prs.empty());
if (!expand_macros(old_exprs.size(), old_exprs.c_ptr(), old_prs.c_ptr(), _new_exprs, _new_prs))
break;
}
}
new_exprs.append(_new_exprs);
new_prs.append(_new_prs);
}

View file

@ -0,0 +1,55 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_finder.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
--*/
#ifndef _MACRO_FINDER_H_
#define _MACRO_FINDER_H_
#include"macro_manager.h"
#include"arith_simplifier_plugin.h"
bool is_macro_head(expr * n, unsigned num_decls);
bool is_simple_macro(ast_manager & m, expr * n, unsigned num_decls, obj_hashtable<func_decl> const * forbidden_set, app * & head, expr * & def);
inline bool is_simple_macro(ast_manager & m, expr * n, unsigned num_decls, app * & head, expr * & def) {
return is_simple_macro(m, n, num_decls, 0, head, def);
}
/**
\brief Macro finder is responsible for finding universally quantified sub-formulas that can be used
as macros.
*/
class macro_finder {
ast_manager & m_manager;
macro_manager & m_macro_manager;
macro_util & m_util;
arith_simplifier_plugin * get_arith_simp() { return m_util.get_arith_simp(); }
bool expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
bool is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
bool is_macro(expr * n, app * & head, expr * & def);
bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t);
bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def);
public:
macro_finder(ast_manager & m, macro_manager & mm);
~macro_finder();
void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
};
#endif /* _MACRO_FINDER_H_ */

View file

@ -0,0 +1,319 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_manager.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
Christoph Wintersteiger (t-cwinte), 2010-04-13: Added cycle detection for macro definitions
Leonardo de Moura (leonardo) 2010-12-15: Moved dependency management to func_decl_dependencies.h
--*/
#include"macro_manager.h"
#include"for_each_expr.h"
#include"var_subst.h"
#include"ast_pp.h"
#include"recurse_expr_def.h"
macro_manager::macro_manager(ast_manager & m, simplifier & s):
m_manager(m),
m_simplifier(s),
m_util(m, s),
m_decls(m),
m_macros(m),
m_macro_prs(m),
m_forbidden(m),
m_deps(m) {
m_util.set_forbidden_set(&m_forbidden_set);
}
macro_manager::~macro_manager() {
}
void macro_manager::push_scope() {
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_decls_lim = m_decls.size();
s.m_forbidden_lim = m_forbidden.size();
}
void macro_manager::pop_scope(unsigned num_scopes) {
unsigned new_lvl = m_scopes.size() - num_scopes;
scope & s = m_scopes[new_lvl];
restore_decls(s.m_decls_lim);
restore_forbidden(s.m_forbidden_lim);
m_scopes.shrink(new_lvl);
}
void macro_manager::restore_decls(unsigned old_sz) {
unsigned sz = m_decls.size();
for (unsigned i = old_sz; i < sz; i++) {
m_decl2macro.erase(m_decls.get(i));
m_deps.erase(m_decls.get(i));
if (m_manager.proofs_enabled())
m_decl2macro_pr.erase(m_decls.get(i));
}
m_decls.shrink(old_sz);
m_macros.shrink(old_sz);
if (m_manager.proofs_enabled())
m_macro_prs.shrink(old_sz);
}
void macro_manager::restore_forbidden(unsigned old_sz) {
unsigned sz = m_forbidden.size();
for (unsigned i = old_sz; i < sz; i++)
m_forbidden_set.erase(m_forbidden.get(i));
m_forbidden.shrink(old_sz);
}
void macro_manager::reset() {
m_decl2macro.reset();
m_decl2macro_pr.reset();
m_decls.reset();
m_macros.reset();
m_macro_prs.reset();
m_scopes.reset();
m_forbidden_set.reset();
m_forbidden.reset();
m_deps.reset();
}
bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) {
TRACE("macro_insert", tout << "trying to create macro: " << f->get_name() << "\n" << mk_pp(m, m_manager) << "\n";);
// if we already have a macro for f then return false;
if (m_decls.contains(f)) {
TRACE("macro_insert", tout << "we already have a macro for: " << f->get_name() << "\n";);
return false;
}
app * head;
expr * definition;
get_head_def(m, f, head, definition);
func_decl_set * s = m_deps.mk_func_decl_set();
m_deps.collect_func_decls(definition, s);
if (!m_deps.insert(f, s)) {
return false;
}
// add macro
m_decl2macro.insert(f, m);
m_decls.push_back(f);
m_macros.push_back(m);
if (m_manager.proofs_enabled()) {
m_macro_prs.push_back(pr);
m_decl2macro_pr.insert(f, pr);
}
TRACE("macro_insert", tout << "A macro was successfully created for: " << f->get_name() << "\n";);
// Nothing's forbidden anymore; if something's bad, we detected it earlier.
// mark_forbidden(m->get_expr());
return true;
}
namespace macro_manager_ns {
struct proc {
obj_hashtable<func_decl> & m_forbidden_set;
func_decl_ref_vector & m_forbidden;
proc(obj_hashtable<func_decl> & s, func_decl_ref_vector & v):m_forbidden_set(s), m_forbidden(v) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app * n) {
func_decl * d = n->get_decl();
if (n->get_num_args() > 0 && n->get_family_id() == null_family_id && !m_forbidden_set.contains(d)) {
m_forbidden_set.insert(d);
m_forbidden.push_back(d);
}
}
};
};
/**
\brief Mark all func_decls used in exprs as forbidden.
*/
void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) {
expr_mark visited;
macro_manager_ns::proc p(m_forbidden_set, m_forbidden);
for (unsigned i = 0; i < n; i++)
for_each_expr(p, visited, exprs[i]);
}
void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const {
app * body = to_app(q->get_expr());
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
expr * lhs = to_app(body)->get_arg(0);
expr * rhs = to_app(body)->get_arg(1);
SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d));
SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d));
if (is_app_of(lhs, d)) {
head = to_app(lhs);
def = rhs;
}
else {
head = to_app(rhs);
def = lhs;
}
}
void macro_manager::display(std::ostream & out) {
unsigned sz = m_decls.size();
for (unsigned i = 0; i < sz; i++) {
func_decl * f = m_decls.get(i);
quantifier * q = 0;
m_decl2macro.find(f, q);
app * head;
expr * def;
get_head_def(q, f, head, def);
SASSERT(q);
out << mk_pp(head, m_manager) << " ->\n" << mk_pp(def, m_manager) << "\n";
}
}
func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & interp) const {
func_decl * f = m_decls.get(i);
quantifier * q = m_macros.get(i);
app * head;
expr * def;
get_head_def(q, f, head, def);
TRACE("macro_bug",
tout << f->get_name() << "\n" << mk_pp(head, m_manager) << "\n" << mk_pp(q, m_manager) << "\n";);
m_util.mk_macro_interpretation(head, def, interp);
return f;
}
macro_manager::macro_expander::macro_expander(ast_manager & m, macro_manager & mm, simplifier & s):
simplifier(m),
m_macro_manager(mm) {
// REMARK: theory simplifier should not be used by macro_expander...
// is_arith_macro rewrites a quantifer such as:
// forall (x Int) (= (+ x (+ (f x) 1)) 2)
// into
// forall (x Int) (= (f x) (+ 1 (* -1 x)))
// The goal is to make simple macro detection detect the arith macro.
// The arith simplifier will undo this transformation.
// borrow_plugins(s);
enable_ac_support(false);
}
macro_manager::macro_expander::~macro_expander() {
// release_plugins();
}
void macro_manager::macro_expander::reduce1_quantifier(quantifier * q) {
simplifier::reduce1_quantifier(q);
// If a macro was expanded in a pattern, we must erase it since it may not be a valid pattern anymore.
// The MAM assumes valid patterns, and it crashes if invalid patterns are provided.
// For example, it will crash if the pattern does not contain all variables.
//
// Alternative solution: use pattern_validation to check if the pattern is still valid.
// I'm not sure if this is a good solution, since the pattern may be meaningless after the macro expansion.
// So, I'm just erasing them.
expr * new_q_expr = 0;
proof * new_q_pr = 0;
get_cached(q, new_q_expr, new_q_pr);
if (!is_quantifier(new_q_expr))
return;
quantifier * new_q = to_quantifier(new_q_expr);
bool erase_patterns = false;
if (q->get_num_patterns() != new_q->get_num_patterns() ||
q->get_num_no_patterns() != new_q->get_num_no_patterns()) {
erase_patterns = true;
}
else {
for (unsigned i = 0; !erase_patterns && i < q->get_num_patterns(); i++) {
if (q->get_pattern(i) != new_q->get_pattern(i))
erase_patterns = true;
}
for (unsigned i = 0; !erase_patterns && i < q->get_num_no_patterns(); i++) {
if (q->get_no_pattern(i) != new_q->get_no_pattern(i))
erase_patterns = true;
}
}
if (erase_patterns) {
ast_manager & m = get_manager();
expr * new_new_q = m.update_quantifier(new_q, 0, 0, 0, 0, new_q->get_expr());
// we can use the same proof since new_new_q and new_q are identical modulo patterns/annotations
cache_result(q, new_new_q, new_q_pr);
}
}
bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref & p) {
if (!is_app(_n))
return false;
app * n = to_app(_n);
quantifier * q = 0;
func_decl * d = n->get_decl();
TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m_manager) << "\nd:\n" << d->get_name() << "\n";);
if (m_macro_manager.m_decl2macro.find(d, q)) {
TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m_manager) << "\n";);
app * head = 0;
expr * def = 0;
m_macro_manager.get_head_def(q, d, head, def);
unsigned num = n->get_num_args();
SASSERT(head && def);
ptr_buffer<expr> subst_args;
subst_args.resize(num, 0);
for (unsigned i = 0; i < num; i++) {
var * v = to_var(head->get_arg(i));
SASSERT(v->get_idx() < num);
unsigned nidx = num - v->get_idx() - 1;
SASSERT(subst_args[nidx] == 0);
subst_args[nidx] = n->get_arg(i);
}
var_subst s(m_manager);
s(def, num, subst_args.c_ptr(), r);
if (m_manager.proofs_enabled()) {
expr_ref instance(m_manager);
s(q->get_expr(), num, subst_args.c_ptr(), instance);
proof * qi_pr = m_manager.mk_quant_inst(m_manager.mk_or(m_manager.mk_not(q), instance), num, subst_args.c_ptr());
proof * q_pr = 0;
m_macro_manager.m_decl2macro_pr.find(d, q_pr);
SASSERT(q_pr != 0);
proof * prs[2] = { qi_pr, q_pr };
p = m_manager.mk_unit_resolution(2, prs);
}
else {
p = 0;
}
return true;
}
return false;
}
void macro_manager::expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr) {
if (has_macros()) {
// Expand macros with "real" proof production support (NO rewrite*)
expr_ref old_n(m_manager);
proof_ref old_pr(m_manager);
old_n = n;
old_pr = pr;
for (;;) {
macro_expander proc(m_manager, *this, m_simplifier);
proof_ref n_eq_r_pr(m_manager);
TRACE("macro_manager_bug", tout << "expand_macros:\n" << mk_pp(n, m_manager) << "\n";);
proc(old_n, r, n_eq_r_pr);
new_pr = m_manager.mk_modus_ponens(old_pr, n_eq_r_pr);
if (r.get() == old_n.get())
return;
old_n = r;
old_pr = new_pr;
}
}
else {
r = n;
new_pr = pr;
}
}

View file

@ -0,0 +1,99 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_manager.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
--*/
#ifndef _MACRO_MANAGER_H_
#define _MACRO_MANAGER_H_
#include"ast_util.h"
#include"obj_hashtable.h"
#include"simplifier.h"
#include"recurse_expr.h"
#include"func_decl_dependencies.h"
#include"macro_util.h"
/**
\brief Macros are universally quantified formulas of the form:
(forall X (= (f X) T[X]))
(forall X (iff (f X) T[X]))
where T[X] does not contain X.
This class is responsible for storing macros and expanding them.
It has support for backtracking and tagging declarations in an expression as forbidded for being macros.
*/
class macro_manager {
ast_manager & m_manager;
simplifier & m_simplifier;
macro_util m_util;
obj_map<func_decl, quantifier *> m_decl2macro; // func-decl -> quantifier
obj_map<func_decl, proof *> m_decl2macro_pr; // func-decl -> quantifier_proof
func_decl_ref_vector m_decls;
quantifier_ref_vector m_macros;
proof_ref_vector m_macro_prs;
obj_hashtable<func_decl> m_forbidden_set;
func_decl_ref_vector m_forbidden;
struct scope {
unsigned m_decls_lim;
unsigned m_forbidden_lim;
};
svector<scope> m_scopes;
func_decl_dependencies m_deps;
void restore_decls(unsigned old_sz);
void restore_forbidden(unsigned old_sz);
class macro_expander : public simplifier {
protected:
macro_manager & m_macro_manager;
virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
virtual void reduce1_quantifier(quantifier * q);
public:
macro_expander(ast_manager & m, macro_manager & mm, simplifier & s);
~macro_expander();
};
friend class macro_expander;
public:
macro_manager(ast_manager & m, simplifier & s);
~macro_manager();
ast_manager & get_manager() const { return m_manager; }
macro_util & get_util() { return m_util; }
bool insert(func_decl * f, quantifier * m, proof * pr);
bool has_macros() const { return !m_macros.empty(); }
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
void mark_forbidden(unsigned n, expr * const * exprs);
void mark_forbidden(expr * e) { mark_forbidden(1, &e); }
bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); }
obj_hashtable<func_decl> const & get_forbidden_set() const { return m_forbidden_set; }
void display(std::ostream & out);
unsigned get_num_macros() const { return m_decls.size(); }
unsigned get_first_macro_last_level() const { return m_scopes.empty() ? 0 : m_scopes.back().m_decls_lim; }
func_decl * get_macro_func_decl(unsigned i) const { return m_decls.get(i); }
func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const;
quantifier * get_macro_quantifier(func_decl * f) const { quantifier * q = 0; m_decl2macro.find(f, q); return q; }
void get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const;
void expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr);
};
#endif /* _MACRO_MANAGER_H_ */

View file

@ -0,0 +1,184 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
macro_substitution.cpp
Abstract:
Mapping from func_decl to quantifiers of the form
Forall X. f(X) = T[X]
Forall X. f(X) iff C[X]
Author:
Leonardo (leonardo) 2012-02-17
Notes:
--*/
#include"macro_substitution.h"
#include"ref_util.h"
typedef obj_map<func_decl, proof*> func_decl2proof;
typedef obj_map<func_decl, expr_dependency*> func_decl2expr_dependency;
void macro_substitution::init() {
if (proofs_enabled())
m_decl2macro_pr = alloc(func_decl2proof);
if (unsat_core_enabled())
m_decl2macro_dep = alloc(func_decl2expr_dependency);
}
macro_substitution::macro_substitution(ast_manager & m):
m_manager(m),
m_cores_enabled(false),
m_proofs_enabled(m.proofs_enabled()) {
init();
}
macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled):
m_manager(m),
m_cores_enabled(cores_enabled),
m_proofs_enabled(m.proofs_enabled()) {
init();
}
macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled):
m_manager(m),
m_cores_enabled(cores_enabled),
m_proofs_enabled(proofs_enabled) {
SASSERT(!proofs_enabled || m.proofs_enabled());
init();
}
macro_substitution::~macro_substitution() {
reset();
}
void macro_substitution::reset() {
dec_ref_map_key_values(m_manager, m_decl2macro);
if (proofs_enabled())
dec_ref_map_values(m_manager, *m_decl2macro_pr);
if (unsat_core_enabled())
dec_ref_map_values(m_manager, *m_decl2macro_dep);
}
void macro_substitution::cleanup() {
reset();
m_decl2macro.finalize();
if (proofs_enabled())
m_decl2macro_pr->finalize();
if (unsat_core_enabled())
m_decl2macro_dep->finalize();
}
void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency * dep) {
DEBUG_CODE({
app * body = to_app(q->get_expr());
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
expr * lhs = body->get_arg(0);
expr * rhs = body->get_arg(1);
SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f));
});
obj_map<func_decl, quantifier *>::obj_map_entry * entry = m_decl2macro.insert_if_not_there2(f, 0);
if (entry->get_data().m_value == 0) {
// new entry
m_manager.inc_ref(f);
m_manager.inc_ref(q);
entry->get_data().m_value = q;
if (proofs_enabled()) {
SASSERT(!m_decl2macro_pr->contains(f));
m_decl2macro_pr->insert(f, pr);
m_manager.inc_ref(pr);
}
if (unsat_core_enabled()) {
SASSERT(!m_decl2macro_dep->contains(f));
m_decl2macro_dep->insert(f, dep);
m_manager.inc_ref(dep);
}
}
else {
// replacing entry
m_manager.inc_ref(q);
m_manager.dec_ref(entry->get_data().m_value);
entry->get_data().m_value = q;
if (proofs_enabled()) {
obj_map<func_decl, proof *>::obj_map_entry * entry_pr = m_decl2macro_pr->find_core(f);
SASSERT(entry_pr != 0);
m_manager.inc_ref(pr);
m_manager.dec_ref(entry_pr->get_data().m_value);
entry_pr->get_data().m_value = pr;
}
if (unsat_core_enabled()) {
obj_map<func_decl, expr_dependency*>::obj_map_entry * entry_dep = m_decl2macro_dep->find_core(f);
SASSERT(entry_dep != 0);
m_manager.inc_ref(dep);
m_manager.dec_ref(entry_dep->get_data().m_value);
entry_dep->get_data().m_value = dep;
}
}
}
void macro_substitution::erase(func_decl * f) {
if (proofs_enabled()) {
proof * pr = 0;
if (m_decl2macro_pr->find(f, pr)) {
m_manager.dec_ref(pr);
m_decl2macro_pr->erase(f);
}
}
if (unsat_core_enabled()) {
expr_dependency * dep = 0;
if (m_decl2macro_dep->find(f, dep)) {
m_manager.dec_ref(dep);
m_decl2macro_dep->erase(f);
}
}
quantifier * q = 0;
if (m_decl2macro.find(f, q)) {
m_manager.dec_ref(f);
m_manager.dec_ref(q);
m_decl2macro.erase(f);
}
}
void macro_substitution::get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def) {
app * body = to_app(q->get_expr());
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
expr * lhs = to_app(body)->get_arg(0);
expr * rhs = to_app(body)->get_arg(1);
SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f));
SASSERT(!is_app_of(lhs, f) || !is_app_of(rhs, f));
if (is_app_of(lhs, f)) {
head = to_app(lhs);
def = rhs;
}
else {
head = to_app(rhs);
def = lhs;
}
}
bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr) {
if (m_decl2macro.find(f, q)) {
if (proofs_enabled())
m_decl2macro_pr->find(f, pr);
return true;
}
return false;
}
bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep) {
if (m_decl2macro.find(f, q)) {
if (proofs_enabled())
m_decl2macro_pr->find(f, pr);
if (unsat_core_enabled())
m_decl2macro_dep->find(f, dep);
return true;
}
return false;
}

View file

@ -0,0 +1,59 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
macro_substitution.h
Abstract:
Mapping from func_decl to quantifiers of the form
Forall X. f(X) = T[X]
Forall X. f(X) iff C[X]
Author:
Leonardo (leonardo) 2012-02-17
Notes:
--*/
#ifndef _MACRO_SUBSTITUTION_H_
#define _MACRO_SUBSTITUTION_H_
#include"ast.h"
class macro_substitution {
ast_manager & m_manager;
obj_map<func_decl, quantifier *> m_decl2macro;
scoped_ptr<obj_map<func_decl, proof *> > m_decl2macro_pr;
scoped_ptr<obj_map<func_decl, expr_dependency *> > m_decl2macro_dep;
unsigned m_cores_enabled:1;
unsigned m_proofs_enabled:1;
void init();
public:
macro_substitution(ast_manager & m);
macro_substitution(ast_manager & m, bool cores_enabled);
macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled);
~macro_substitution();
ast_manager & m() const { return m_manager; }
bool proofs_enabled() const { return m_proofs_enabled; }
bool unsat_core_enabled() const { return m_cores_enabled; }
bool empty() const { return m_decl2macro.empty(); }
void insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = 0);
void erase(func_decl * f);
bool contains(func_decl * f) { return m_decl2macro.contains(f); }
bool find(func_decl * f, quantifier * & q, proof * & pr);
bool find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep);
void get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def);
void reset();
void cleanup();
};
#endif

View file

@ -0,0 +1,928 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_util.cpp
Abstract:
Macro finding goodies.
They are used during preprocessing (MACRO_FINDER=true), and model building.
Author:
Leonardo de Moura (leonardo) 2010-12-15.
Revision History:
--*/
#include"macro_util.h"
#include"occurs.h"
#include"arith_simplifier_plugin.h"
#include"basic_simplifier_plugin.h"
#include"bv_simplifier_plugin.h"
#include"var_subst.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_util.h"
#include"for_each_expr.h"
#include"well_sorted.h"
macro_util::macro_util(ast_manager & m, simplifier & s):
m_manager(m),
m_simplifier(s),
m_arith_simp(0),
m_bv_simp(0),
m_basic_simp(0),
m_forbidden_set(0),
m_curr_clause(0) {
}
arith_simplifier_plugin * macro_util::get_arith_simp() const {
if (m_arith_simp == 0) {
const_cast<macro_util*>(this)->m_arith_simp = static_cast<arith_simplifier_plugin*>(m_simplifier.get_plugin(m_manager.get_family_id("arith")));
}
SASSERT(m_arith_simp != 0);
return m_arith_simp;
}
bv_simplifier_plugin * macro_util::get_bv_simp() const {
if (m_bv_simp == 0) {
const_cast<macro_util*>(this)->m_bv_simp = static_cast<bv_simplifier_plugin*>(m_simplifier.get_plugin(m_manager.get_family_id("bv")));
}
SASSERT(m_bv_simp != 0);
return m_bv_simp;
}
basic_simplifier_plugin * macro_util::get_basic_simp() const {
if (m_basic_simp == 0) {
const_cast<macro_util*>(this)->m_basic_simp = static_cast<basic_simplifier_plugin*>(m_simplifier.get_plugin(m_manager.get_basic_family_id()));
}
SASSERT(m_basic_simp != 0);
return m_basic_simp;
}
bool macro_util::is_bv(expr * n) const {
return get_bv_simp()->is_bv(n);
}
bool macro_util::is_bv_sort(sort * s) const {
return get_bv_simp()->is_bv_sort(s);
}
bool macro_util::is_add(expr * n) const {
return get_arith_simp()->is_add(n) || get_bv_simp()->is_add(n);
}
bool macro_util::is_times_minus_one(expr * n, expr * & arg) const {
return get_arith_simp()->is_times_minus_one(n, arg) || get_bv_simp()->is_times_minus_one(n, arg);
}
bool macro_util::is_le(expr * n) const {
return get_arith_simp()->is_le(n) || get_bv_simp()->is_le(n);
}
bool macro_util::is_le_ge(expr * n) const {
return get_arith_simp()->is_le_ge(n) || get_bv_simp()->is_le_ge(n);
}
poly_simplifier_plugin * macro_util::get_poly_simp_for(sort * s) const {
if (is_bv_sort(s))
return get_bv_simp();
else
return get_arith_simp();
}
app * macro_util::mk_zero(sort * s) const {
poly_simplifier_plugin * ps = get_poly_simp_for(s);
ps->set_curr_sort(s);
return ps->mk_zero();
}
void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const {
if (is_bv(t1)) {
get_bv_simp()->mk_sub(t1, t2, r);
}
else {
get_arith_simp()->mk_sub(t1, t2, r);
}
}
void macro_util::mk_add(expr * t1, expr * t2, expr_ref & r) const {
if (is_bv(t1)) {
get_bv_simp()->mk_add(t1, t2, r);
}
else {
get_arith_simp()->mk_add(t1, t2, r);
}
}
void macro_util::mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const {
if (num_args == 0) {
r = mk_zero(s);
return;
}
poly_simplifier_plugin * ps = get_poly_simp_for(s);
ps->set_curr_sort(s);
ps->mk_add(num_args, args, r);
}
/**
\brief Return true if \c n is an application of the form
(f x_{k_1}, ..., x_{k_n})
where f is uninterpreted
n == num_decls
x_{k_i}'s are variables
and {k_1, ..., k_n } is equals to the set {0, ..., num_decls-1}
*/
bool macro_util::is_macro_head(expr * n, unsigned num_decls) const {
if (is_app(n) &&
!to_app(n)->get_decl()->is_associative() &&
to_app(n)->get_family_id() == null_family_id &&
to_app(n)->get_num_args() == num_decls) {
sbuffer<int> var2pos;
var2pos.resize(num_decls, -1);
for (unsigned i = 0; i < num_decls; i++) {
expr * c = to_app(n)->get_arg(i);
if (!is_var(c))
return false;
unsigned idx = to_var(c)->get_idx();
if (idx >= num_decls || var2pos[idx] != -1)
return false;
var2pos[idx] = i;
}
return true;
}
return false;
}
/**
\brief Return true if n is of the form
(= (f x_{k_1}, ..., x_{k_n}) t) OR
(iff (f x_{k_1}, ..., x_{k_n}) t)
where
is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND
t does not contain f AND
f is not in forbidden_set
In case of success
head will contain (f x_{k_1}, ..., x_{k_n}) AND
def will contain t
*/
bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const {
if (m_manager.is_eq(n) || m_manager.is_iff(n)) {
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (is_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && !occurs(to_app(lhs)->get_decl(), rhs)) {
head = to_app(lhs);
def = rhs;
return true;
}
}
return false;
}
/**
\brief Return true if n is of the form
(= t (f x_{k_1}, ..., x_{k_n})) OR
(iff t (f x_{k_1}, ..., x_{k_n}))
where
is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND
t does not contain f AND
f is not in forbidden_set
In case of success
head will contain (f x_{k_1}, ..., x_{k_n}) AND
def will contain t
*/
bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const {
if (m_manager.is_eq(n) || m_manager.is_iff(n)) {
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (is_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && !occurs(to_app(rhs)->get_decl(), lhs)) {
head = to_app(rhs);
def = lhs;
return true;
}
}
return false;
}
/**
\brief Return true if n contains f. The method ignores the sub-expression \c exception.
\remark n is a "polynomial".
*/
bool macro_util::poly_contains_head(expr * n, func_decl * f, expr * exception) const {
expr * curr = n;
unsigned num_args;
expr * const * args;
if (is_add(n)) {
num_args = to_app(n)->get_num_args();
args = to_app(n)->get_args();
}
else {
num_args = 1;
args = &n;
}
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (arg != exception && occurs(f, arg))
return true;
}
return false;
}
bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const {
// TODO: obsolete... we should move to collect_arith_macro_candidates
arith_simplifier_plugin * as = get_arith_simp();
if (!m_manager.is_eq(n) && !as->is_le(n) && !as->is_ge(n))
return false;
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (!as->is_numeral(rhs))
return false;
inv = false;
ptr_buffer<expr> args;
expr * h = 0;
unsigned lhs_num_args;
expr * const * lhs_args;
if (is_add(lhs)) {
lhs_num_args = to_app(lhs)->get_num_args();
lhs_args = to_app(lhs)->get_args();
}
else {
lhs_num_args = 1;
lhs_args = &lhs;
}
for (unsigned i = 0; i < lhs_num_args; i++) {
expr * arg = lhs_args[i];
expr * neg_arg;
if (h == 0 &&
is_macro_head(arg, num_decls) &&
!is_forbidden(to_app(arg)->get_decl()) &&
!poly_contains_head(lhs, to_app(arg)->get_decl(), arg)) {
h = arg;
}
else if (h == 0 && as->is_times_minus_one(arg, neg_arg) &&
is_macro_head(neg_arg, num_decls) &&
!is_forbidden(to_app(neg_arg)->get_decl()) &&
!poly_contains_head(lhs, to_app(neg_arg)->get_decl(), arg)) {
h = neg_arg;
inv = true;
}
else {
args.push_back(arg);
}
}
if (h == 0)
return false;
head = to_app(h);
expr_ref tmp(m_manager);
as->mk_add(args.size(), args.c_ptr(), tmp);
if (inv)
as->mk_sub(tmp, rhs, def);
else
as->mk_sub(rhs, tmp, def);
return true;
}
/**
\brief Auxiliary function for is_pseudo_predicate_macro. It detects the pattern (= (f X) t)
*/
bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t) {
if (!m_manager.is_eq(n))
return false;
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (!is_ground(lhs) && !is_ground(rhs))
return false;
sort * s = m_manager.get_sort(lhs);
if (m_manager.is_uninterp(s))
return false;
sort_size sz = s->get_num_elements();
if (sz.is_finite() && sz.size() == 1)
return false;
if (is_macro_head(lhs, num_decls)) {
head = to_app(lhs);
t = to_app(rhs);
return true;
}
if (is_macro_head(rhs, num_decls)) {
head = to_app(rhs);
t = to_app(lhs);
return true;
}
return false;
}
/**
\brief Returns true if n if of the form (forall (X) (iff (= (f X) t) def[X]))
where t is a ground term, (f X) is the head.
*/
bool macro_util::is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def) {
if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
return false;
TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";);
expr * body = to_quantifier(n)->get_expr();
unsigned num_decls = to_quantifier(n)->get_num_decls();
if (!m_manager.is_iff(body))
return false;
expr * lhs = to_app(body)->get_arg(0);
expr * rhs = to_app(body)->get_arg(1);
if (is_pseudo_head(lhs, num_decls, head, t) &&
!is_forbidden(head->get_decl()) &&
!occurs(head->get_decl(), rhs)) {
def = rhs;
return true;
}
if (is_pseudo_head(rhs, num_decls, head, t) &&
!is_forbidden(head->get_decl()) &&
!occurs(head->get_decl(), lhs)) {
def = lhs;
return true;
}
return false;
}
/**
\brief A quasi-macro head is of the form f[X_1, ..., X_n],
where n == num_decls, f[X_1, ..., X_n] is a term starting with symbol f, f is uninterpreted,
contains all universally quantified variables as arguments.
Note that, some arguments of f[X_1, ..., X_n] may not be variables.
Examples of quasi-macros:
f(x_1, x_1 + x_2, x_2) for num_decls == 2
g(x_1, x_1) for num_decls == 1
Return true if \c n is a quasi-macro. Store the macro head in \c head, and the conditions to apply the macro in \c cond.
*/
bool macro_util::is_quasi_macro_head(expr * n, unsigned num_decls) const {
if (is_app(n) &&
to_app(n)->get_family_id() == null_family_id &&
to_app(n)->get_num_args() >= num_decls) {
unsigned num_args = to_app(n)->get_num_args();
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
unsigned num_found_vars = 0;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(n)->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (idx >= num_decls)
return false;
if (found_vars[idx] == false) {
found_vars[idx] = true;
num_found_vars++;
}
}
else {
if (occurs(to_app(n)->get_decl(), arg))
return false;
}
}
return num_found_vars == num_decls;
}
return false;
}
/**
\brief Convert a quasi-macro head into a macro head, and store the conditions under
which it is valid in cond.
*/
void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, app_ref & head, expr_ref & cond) const {
unsigned num_args = qhead->get_num_args();
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
ptr_buffer<expr> new_args;
ptr_buffer<expr> new_conds;
unsigned next_var_idx = num_decls;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = qhead->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
SASSERT(idx < num_decls);
if (found_vars[idx] == false) {
found_vars[idx] = true;
new_args.push_back(arg);
continue;
}
}
var * new_var = m_manager.mk_var(next_var_idx, m_manager.get_sort(arg));
next_var_idx++;
expr * new_cond = m_manager.mk_eq(new_var, arg);
new_args.push_back(new_var);
new_conds.push_back(new_cond);
}
get_basic_simp()->mk_and(new_conds.size(), new_conds.c_ptr(), cond);
head = m_manager.mk_app(qhead->get_decl(), new_args.size(), new_args.c_ptr());
}
/**
\brief Given a macro defined by head and def, stores an interpretation for head->get_decl() in interp.
This method assumes is_macro_head(head, head->get_num_args()) returns true,
and def does not contain head->get_decl().
See normalize_expr
*/
void macro_util::mk_macro_interpretation(app * head, expr * def, expr_ref & interp) const {
SASSERT(is_macro_head(head, head->get_num_args()));
SASSERT(!occurs(head->get_decl(), def));
normalize_expr(head, def, interp);
}
/**
\brief The variables in head may be in the wrong order.
Example: f(x_1, x_0) instead of f(x_0, x_1)
This method is essentially renaming the variables in t.
Suppose t is g(x_1, x_0 + x_1)
This method will store g(x_0, x_1 + x_0) in norm_t.
f(x_1, x_2) --> f(x_0, x_1)
f(x_3, x_2) --> f(x_0, x_1)
*/
void macro_util::normalize_expr(app * head, expr * t, expr_ref & norm_t) const {
expr_ref_buffer var_mapping(m_manager);
bool changed = false;
unsigned num_args = head->get_num_args();
unsigned max = num_args;
for (unsigned i = 0; i < num_args; i++) {
var * v = to_var(head->get_arg(i));
if (v->get_idx() >= max)
max = v->get_idx() + 1;
}
TRACE("normalize_expr_bug",
tout << "head: " << mk_pp(head, m_manager) << "\n";
tout << "applying substitution to:\n" << mk_bounded_pp(t, m_manager) << "\n";);
for (unsigned i = 0; i < num_args; i++) {
var * v = to_var(head->get_arg(i));
if (v->get_idx() != i) {
changed = true;
var * new_var = m_manager.mk_var(i, v->get_sort());
CTRACE("normalize_expr_bug", v->get_idx() >= num_args, tout << mk_pp(v, m_manager) << ", num_args: " << num_args << "\n";);
SASSERT(v->get_idx() < max);
var_mapping.setx(max - v->get_idx() - 1, new_var);
}
else {
var_mapping.setx(max - i - 1, v);
}
}
if (changed) {
// REMARK: t may have nested quantifiers... So, I must use the std order for variable substitution.
var_subst subst(m_manager);
TRACE("macro_util_bug",
tout << "head: " << mk_pp(head, m_manager) << "\n";
tout << "applying substitution to:\n" << mk_ll_pp(t, m_manager) << "\nsubstitituion:\n";
for (unsigned i = 0; i < var_mapping.size(); i++) {
tout << "#" << i << " -> " << mk_pp(var_mapping[i], m_manager) << "\n";
});
subst(t, var_mapping.size(), var_mapping.c_ptr(), norm_t);
SASSERT(is_well_sorted(m_manager, norm_t));
}
else {
norm_t = t;
}
}
// -----------------------------
//
// "Hint" support
// See comment at is_hint_atom
// for a definition of what a hint is.
//
// -----------------------------
bool is_hint_head(expr * n, ptr_buffer<var> & vars) {
if (!is_app(n))
return false;
if (to_app(n)->get_decl()->is_associative() || to_app(n)->get_family_id() != null_family_id)
return false;
unsigned num_args = to_app(n)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(n)->get_arg(i);
if (is_var(arg))
vars.push_back(to_var(arg));
}
return !vars.empty();
}
/**
\brief Returns true if the variables in n is a subset of \c vars.
*/
bool vars_of_is_subset(expr * n, ptr_buffer<var> const & vars) {
if (is_ground(n))
return true;
obj_hashtable<expr> visited;
ptr_buffer<expr> todo;
todo.push_back(n);
while (!todo.empty()) {
expr * curr = todo.back();
todo.pop_back();
if (is_var(curr)) {
if (std::find(vars.begin(), vars.end(), to_var(curr)) == vars.end())
return false;
}
else if (is_app(curr)) {
unsigned num_args = to_app(curr)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(curr)->get_arg(i);
if (is_ground(arg))
continue;
if (visited.contains(arg))
continue;
visited.insert(arg);
todo.push_back(arg);
}
}
else {
SASSERT(is_quantifier(curr));
return false; // do no support nested quantifier... being conservative.
}
}
return true;
}
/**
\brief (= lhs rhs) is a hint atom if
lhs is of the form (f t_1 ... t_n)
and all variables occurring in rhs are direct arguments of lhs.
*/
bool is_hint_atom(expr * lhs, expr * rhs) {
ptr_buffer<var> vars;
if (!is_hint_head(lhs, vars))
return false;
return !occurs(to_app(lhs)->get_decl(), rhs) && vars_of_is_subset(rhs, vars);
}
void hint_to_macro_head(ast_manager & m, app * head, unsigned num_decls, app_ref & new_head) {
unsigned num_args = head->get_num_args();
ptr_buffer<expr> new_args;
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
unsigned next_var_idx = num_decls;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = head->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
SASSERT(idx < num_decls);
if (found_vars[idx] == false) {
found_vars[idx] = true;
new_args.push_back(arg);
continue;
}
}
var * new_var = m.mk_var(next_var_idx, m.get_sort(arg));
next_var_idx++;
new_args.push_back(new_var);
}
new_head = m.mk_app(head->get_decl(), new_args.size(), new_args.c_ptr());
}
/**
\brief Return true if n can be viewed as a polynomial "hint" based on head.
That is, n (but the monomial exception) only uses the variables in head, and does not use
head->get_decl().
is_hint_head(head, vars) must also return true
*/
bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) {
TRACE("macro_util_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n"
<< mk_pp(exception, m_manager) << "\n";);
ptr_buffer<var> vars;
if (!is_hint_head(head, vars)) {
TRACE("macro_util_hint", tout << "failed because head is not hint head\n";);
return false;
}
func_decl * f = head->get_decl();
unsigned num_args;
expr * const * args;
if (is_add(n)) {
num_args = to_app(n)->get_num_args();
args = to_app(n)->get_args();
}
else {
num_args = 1;
args = &n;
}
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (arg != exception && (occurs(f, arg) || !vars_of_is_subset(arg, vars))) {
TRACE("macro_util_hint", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";);
return false;
}
}
TRACE("macro_util_hint", tout << "succeeded\n";);
return true;
}
// -----------------------------
//
// Macro candidates
//
// -----------------------------
macro_util::macro_candidates::macro_candidates(ast_manager & m):
m_defs(m),
m_conds(m) {
}
void macro_util::macro_candidates::reset() {
m_fs.reset();
m_defs.reset();
m_conds.reset();
m_ineq.reset();
m_satisfy.reset();
m_hint.reset();
}
void macro_util::macro_candidates::insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint) {
m_fs.push_back(f);
m_defs.push_back(def);
m_conds.push_back(cond);
m_ineq.push_back(ineq);
m_satisfy.push_back(satisfy_atom);
m_hint.push_back(hint);
}
// -----------------------------
//
// Macro util
//
// -----------------------------
void macro_util::insert_macro(app * head, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) {
expr_ref norm_def(m_manager);
expr_ref norm_cond(m_manager);
normalize_expr(head, def, norm_def);
if (cond != 0)
normalize_expr(head, cond, norm_cond);
else if (!hint)
norm_cond = m_manager.mk_true();
SASSERT(!hint || norm_cond.get() == 0);
r.insert(head->get_decl(), norm_def.get(), norm_cond.get(), ineq, satisfy_atom, hint);
}
void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom,
bool hint, macro_candidates & r) {
if (!is_macro_head(head, head->get_num_args())) {
app_ref new_head(m_manager);
expr_ref extra_cond(m_manager);
expr_ref new_cond(m_manager);
if (!hint) {
quasi_macro_head_to_macro_head(head, num_decls, new_head, extra_cond);
if (cond == 0)
new_cond = extra_cond;
else
get_basic_simp()->mk_and(cond, extra_cond, new_cond);
}
else {
hint_to_macro_head(m_manager, head, num_decls, new_head);
}
insert_macro(new_head, def, new_cond, ineq, satisfy_atom, hint, r);
}
else {
insert_macro(head, def, cond, ineq, satisfy_atom, hint, r);
}
}
bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) {
if (m_curr_clause == 0)
return false;
SASSERT(is_clause(m_manager, m_curr_clause));
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
for (unsigned i = 0; i < num_lits; i++) {
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
if (l != except_lit && occurs(f, l))
return true;
}
return false;
}
void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond) {
if (m_curr_clause == 0)
return;
SASSERT(is_clause(m_manager, m_curr_clause));
basic_simplifier_plugin * bs = get_basic_simp();
expr_ref_buffer neg_other_lits(m_manager);
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
for (unsigned i = 0; i < num_lits; i++) {
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
if (l != except_lit) {
expr_ref neg_l(m_manager);
bs->mk_not(l, neg_l);
neg_other_lits.push_back(neg_l);
}
}
if (neg_other_lits.empty())
return;
get_basic_simp()->mk_and(neg_other_lits.size(), neg_other_lits.c_ptr(), extra_cond);
}
void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer<expr> & args) {
args.reset();
bool stop = false;
unsigned num_args;
expr * const * _args;
if (is_add(n)) {
num_args = to_app(n)->get_num_args();
_args = to_app(n)->get_args();
}
else {
num_args = 1;
_args = &n;
}
for (unsigned i = 0; i < num_args; i++) {
expr * arg = _args[i];
if (arg != exception)
args.push_back(arg);
}
}
void macro_util::add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r) {
expr_ref cond(m_manager);
if (!hint)
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(head, num_decls, def, cond, ineq, true, hint, r);
}
void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool is_ineq, macro_candidates & r) {
if (!is_add(lhs) && m_manager.is_eq(atom)) // this case is a simple macro.
return;
bool stop = false;
ptr_buffer<expr> args;
unsigned lhs_num_args;
expr * const * lhs_args;
if (is_add(lhs)) {
lhs_num_args = to_app(lhs)->get_num_args();
lhs_args = to_app(lhs)->get_args();
}
else {
lhs_num_args = 1;
lhs_args = &lhs;
}
for (unsigned i = 0; i < lhs_num_args; i++) {
expr * arg = lhs_args[i];
expr * neg_arg;
if (!is_app(arg))
continue;
func_decl * f = to_app(arg)->get_decl();
bool _is_arith_macro =
is_quasi_macro_head(arg, num_decls) &&
!is_forbidden(f) &&
!poly_contains_head(lhs, f, arg) &&
!occurs(f, rhs) &&
!rest_contains_decl(f, atom);
bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(arg), arg);
if (_is_arith_macro || _is_poly_hint) {
collect_poly_args(lhs, arg, args);
expr_ref rest(m_manager);
mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest);
expr_ref def(m_manager);
mk_sub(rhs, rest, def);
add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r);
}
else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) {
f = to_app(neg_arg)->get_decl();
bool _is_arith_macro =
is_quasi_macro_head(neg_arg, num_decls) &&
!is_forbidden(f) &&
!poly_contains_head(lhs, f, arg) &&
!occurs(f, rhs) &&
!rest_contains_decl(f, atom);
bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(neg_arg), arg);
if (_is_arith_macro || _is_poly_hint) {
collect_poly_args(lhs, arg, args);
expr_ref rest(m_manager);
mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest);
expr_ref def(m_manager);
mk_sub(rest, rhs, def);
add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r);
}
}
}
}
void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) {
TRACE("macro_util_hint", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m_manager) << "\n";);
if (!m_manager.is_eq(atom) && !is_le_ge(atom))
return;
expr * lhs = to_app(atom)->get_arg(0);
expr * rhs = to_app(atom)->get_arg(1);
bool is_ineq = !m_manager.is_eq(atom);
collect_arith_macro_candidates(lhs, rhs, atom, num_decls, is_ineq, r);
collect_arith_macro_candidates(rhs, lhs, atom, num_decls, is_ineq, r);
}
/**
\brief Collect macro candidates for atom \c atom.
The candidates are stored in \c r.
The following post-condition holds:
for each i in [0, r.size() - 1]
we have a conditional macro of the form
r.get_cond(i) IMPLIES f(x_1, ..., x_n) = r.get_def(i)
where
f == r.get_fs(i) .., x_n), f is uninterpreted and x_1, ..., x_n are variables.
r.get_cond(i) and r.get_defs(i) do not contain f or variables not in {x_1, ..., x_n}
The idea is to use r.get_defs(i) as the interpretation for f in a model M whenever r.get_cond(i)
Given a model M and values { v_1, ..., v_n }
Let M' be M{x_1 -> v_1, ..., v_n -> v_n}
Note that M'(f(x_1, ..., x_n)) = M(f)(v_1, ..., v_n)
Then, IF we have that M(f)(v_1, ..., v_n) = M'(r.get_def(i)) AND
M'(r.get_cond(i)) = true
THEN M'(atom) = true
That is, if the conditional macro is used then the atom is satisfied when M'(r.get_cond(i)) = true
IF r.is_ineq(i) = false, then
M(f)(v_1, ..., v_n) ***MUST BE*** M'(r.get_def(i)) whenever M'(r.get_cond(i)) = true
IF r.satisfy_atom(i) = true, then we have the stronger property:
Then, IF we have that (M'(r.get_cond(i)) = true IMPLIES M(f)(v_1, ..., v_n) = M'(r.get_def(i)))
THEN M'(atom) = true
*/
void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r) {
if (m_manager.is_eq(atom) || m_manager.is_iff(atom)) {
expr * lhs = to_app(atom)->get_arg(0);
expr * rhs = to_app(atom)->get_arg(1);
if (is_quasi_macro_head(lhs, num_decls) &&
!is_forbidden(to_app(lhs)->get_decl()) &&
!occurs(to_app(lhs)->get_decl(), rhs) &&
!rest_contains_decl(to_app(lhs)->get_decl(), atom)) {
expr_ref cond(m_manager);
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(to_app(lhs), num_decls, rhs, cond, false, true, false, r);
}
else if (is_hint_atom(lhs, rhs)) {
insert_quasi_macro(to_app(lhs), num_decls, rhs, 0, false, true, true, r);
}
if (is_quasi_macro_head(rhs, num_decls) &&
!is_forbidden(to_app(rhs)->get_decl()) &&
!occurs(to_app(rhs)->get_decl(), lhs) &&
!rest_contains_decl(to_app(rhs)->get_decl(), atom)) {
expr_ref cond(m_manager);
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(to_app(rhs), num_decls, lhs, cond, false, true, false, r);
}
else if (is_hint_atom(rhs, lhs)) {
insert_quasi_macro(to_app(rhs), num_decls, lhs, 0, false, true, true, r);
}
}
collect_arith_macro_candidates(atom, num_decls, r);
}
void macro_util::collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) {
m_curr_clause = 0;
r.reset();
collect_macro_candidates_core(atom, num_decls, r);
}
void macro_util::collect_macro_candidates(quantifier * q, macro_candidates & r) {
r.reset();
expr * n = q->get_expr();
if (has_quantifiers(n))
return;
unsigned num_decls = q->get_num_decls();
SASSERT(m_curr_clause == 0);
if (is_clause(m_manager, n)) {
m_curr_clause = n;
unsigned num_lits = get_clause_num_literals(m_manager, n);
for (unsigned i = 0; i < num_lits; i++)
collect_macro_candidates_core(get_clause_literal(m_manager, n, i), num_decls, r);
m_curr_clause = 0;
}
else {
collect_macro_candidates_core(n, num_decls, r);
}
}

144
src/ast/macros/macro_util.h Normal file
View file

@ -0,0 +1,144 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_util.h
Abstract:
Macro finding goodies.
They are used during preprocessing (MACRO_FINDER=true), and model building.
Author:
Leonardo de Moura (leonardo) 2010-12-15.
Revision History:
--*/
#ifndef _MACRO_UTIL_H_
#define _MACRO_UTIL_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"simplifier.h"
class poly_simplifier_plugin;
class arith_simplifier_plugin;
class bv_simplifier_plugin;
class basic_simplifier_plugin;
class macro_util {
public:
/**
\brief See collect_macro_candidates.
*/
class macro_candidates {
ptr_vector<func_decl> m_fs;
expr_ref_vector m_defs;
expr_ref_vector m_conds;
svector<bool> m_ineq; // true if the macro is based on an inequality instead of equality.
svector<bool> m_satisfy;
svector<bool> m_hint; // macro did not contain all universal variables in the quantifier.
friend class macro_util;
ast_manager & get_manager() { return m_conds.get_manager(); }
public:
macro_candidates(ast_manager & m);
~macro_candidates() { reset(); }
void reset();
void insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint);
bool empty() const { return m_fs.empty(); }
unsigned size() const { return m_fs.size(); }
func_decl * get_f(unsigned i) const { return m_fs[i]; }
expr * get_def(unsigned i) const { return m_defs.get(i); }
expr * get_cond(unsigned i) const { return m_conds.get(i); }
bool ineq(unsigned i) const { return m_ineq[i]; }
bool satisfy_atom(unsigned i) const { return m_satisfy[i]; }
bool hint(unsigned i) const { return m_hint[i]; }
};
private:
ast_manager & m_manager;
simplifier & m_simplifier;
arith_simplifier_plugin * m_arith_simp;
bv_simplifier_plugin * m_bv_simp;
basic_simplifier_plugin * m_basic_simp;
obj_hashtable<func_decl> * m_forbidden_set;
bool is_forbidden(func_decl * f) const { return m_forbidden_set != 0 && m_forbidden_set->contains(f); }
bool poly_contains_head(expr * n, func_decl * f, expr * exception) const;
void collect_arith_macros(expr * n, unsigned num_decls, unsigned max_macros, bool allow_cond_macros,
macro_candidates & r);
void normalize_expr(app * head, expr * t, expr_ref & norm_t) const;
void insert_macro(app * head, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r);
void insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint,
macro_candidates & r);
expr * m_curr_clause; // auxiliary var used in collect_macro_candidates.
// Return true if m_curr_clause contains f in a literal different from except_lit
bool rest_contains_decl(func_decl * f, expr * except_lit);
// Store in extra_cond (and (not l_1) ... (not l_n)) where l_i's are the literals of m_curr_clause that are different from except_lit.
void get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond);
void collect_poly_args(expr * n, expr * exception, ptr_buffer<expr> & args);
void add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r);
void collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool ineq, macro_candidates & r);
void collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r);
void collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r);
bool is_poly_hint(expr * n, app * head, expr * exception);
public:
macro_util(ast_manager & m, simplifier & s);
void set_forbidden_set(obj_hashtable<func_decl> * s) { m_forbidden_set = s; }
arith_simplifier_plugin * get_arith_simp() const;
bv_simplifier_plugin * get_bv_simp() const;
basic_simplifier_plugin * get_basic_simp() const;
bool is_macro_head(expr * n, unsigned num_decls) const;
bool is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const;
bool is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const;
bool is_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const {
return is_left_simple_macro(n, num_decls, head, def) || is_right_simple_macro(n, num_decls, head, def);
}
bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const;
bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
bool inv;
return is_arith_macro(n, num_decls, head, def, inv);
}
bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t);
bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def);
bool is_quasi_macro_head(expr * n, unsigned num_decls) const;
void quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, app_ref & head, expr_ref & cond) const;
void mk_macro_interpretation(app * head, expr * def, expr_ref & interp) const;
void collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r);
void collect_macro_candidates(quantifier * q, macro_candidates & r);
//
// Auxiliary goodness that allows us to manipulate BV and Arith polynomials.
//
bool is_bv(expr * n) const;
bool is_bv_sort(sort * s) const;
app * mk_zero(sort * s) const;
bool is_add(expr * n) const;
bool is_times_minus_one(expr * n, expr * & arg) const;
bool is_le(expr * n) const;
bool is_le_ge(expr * n) const;
void mk_sub(expr * t1, expr * t2, expr_ref & r) const;
void mk_add(expr * t1, expr * t2, expr_ref & r) const;
void mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const;
poly_simplifier_plugin * get_poly_simp_for(sort * s) const;
};
#endif

View file

@ -0,0 +1,317 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
quasi_macros.cpp
Abstract:
<abstract>
Author:
Christoph Wintersteiger (t-cwinte) 2010-04-23
Revision History:
--*/
#include"quasi_macros.h"
#include"for_each_expr.h"
#include"ast_pp.h"
#include"uint_set.h"
#include"var_subst.h"
quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s) :
m_manager(m),
m_macro_manager(mm),
m_bsimp(p),
m_simplifier(s),
m_new_vars(m),
m_new_eqs(m),
m_new_qsorts(m) {
}
quasi_macros::~quasi_macros() {
}
void quasi_macros::find_occurrences(expr * e) {
unsigned j;
m_todo.reset();
m_todo.push_back(e);
// we remember whether we have seen an expr once, or more than once;
// when we see it the second time, we don't have to visit it another time,
// as we are only intersted in finding unique function applications.
m_visited_once.reset();
m_visited_more.reset();
while (!m_todo.empty()) {
expr * cur = m_todo.back();
m_todo.pop_back();
if (m_visited_more.is_marked(cur))
continue;
if (m_visited_once.is_marked(cur))
m_visited_more.mark(cur, true);
m_visited_once.mark(cur, true);
switch (cur->get_kind()) {
case AST_VAR: break;
case AST_QUANTIFIER: m_todo.push_back(to_quantifier(cur)->get_expr()); break;
case AST_APP:
if (is_uninterp(cur) && !is_ground(cur)) {
func_decl * f = to_app(cur)->get_decl();
m_occurrences.insert_if_not_there(f, 0);
occurrences_map::iterator it = m_occurrences.find_iterator(f);
it->m_value++;
}
j = to_app(cur)->get_num_args();
while (j)
m_todo.push_back(to_app(cur)->get_arg(--j));
break;
default: UNREACHABLE();
}
}
};
bool quasi_macros::is_unique(func_decl * f) const {
return m_occurrences.find(f) == 1;
}
struct var_dep_proc {
bit_vector m_bitset;
public:
var_dep_proc(quantifier * q) { m_bitset.resize(q->get_num_decls(), false); }
void operator()(var * n) { m_bitset.set(n->get_idx(), true); }
void operator()(quantifier * n) {}
void operator()(app * n) {}
bool all_used(void) {
for (unsigned i = 0; i < m_bitset.size() ; i++)
if (!m_bitset.get(i))
return false;
return true;
}
};
bool quasi_macros::fully_depends_on(app * a, quantifier * q) const {
// CMW: This checks whether all variables in q are used _somewhere_ deep down in the children of a
/* var_dep_proc proc(q);
for_each_expr(proc, a);
return proc.all_used(); */
// CMW: This code instead checks that all variables appear at least once as a
// direct argument of a, i.e., a->get_arg(i) == v for some i
bit_vector bitset;
bitset.resize(q->get_num_decls(), false);
for (unsigned i = 0 ; i < a->get_num_args() ; i++) {
if (is_var(a->get_arg(i)))
bitset.set(to_var(a->get_arg(i))->get_idx(), true);
}
for (unsigned i = 0; i < bitset.size() ; i++) {
if (!bitset.get(i))
return false;
}
return true;
}
bool quasi_macros::depends_on(expr * e, func_decl * f) const {
ptr_vector<expr> todo;
expr_mark visited;
todo.push_back(e);
while(!todo.empty()) {
expr * cur = todo.back();
todo.pop_back();
if (visited.is_marked(cur))
continue;
if (is_app(cur)) {
app * a = to_app(cur);
if (a->get_decl() == f)
return true;
unsigned j = a->get_num_args();
while (j>0)
todo.push_back(a->get_arg(--j));
}
visited.mark(cur, true);
}
return false;
}
bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
// Our definition of a quasi-macro:
// Forall X. f[X] = T[X], where f[X] is a term starting with symbol f, f is uninterpreted,
// f[X] contains all universally quantified variables, and f does not occur in T[X].
if (is_quantifier(e) && to_quantifier(e)->is_forall()) {
quantifier * q = to_quantifier(e);
expr * qe = q->get_expr();
if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) {
expr * lhs = to_app(qe)->get_arg(0);
expr * rhs = to_app(qe)->get_arg(1);
if (is_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) &&
!depends_on(rhs, to_app(lhs)->get_decl()) && fully_depends_on(to_app(lhs), q)) {
a = to_app(lhs);
t = rhs;
return true;
} else if (is_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) &&
!depends_on(lhs, to_app(rhs)->get_decl()) && fully_depends_on(to_app(rhs), q)) {
a = to_app(rhs);
t = lhs;
return true;
}
} else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0)) &&
is_unique(to_app(to_app(qe)->get_arg(0))->get_decl())) { // this is like f(...) = false
a = to_app(to_app(qe)->get_arg(0));
t = m_manager.mk_false();
return true;
} else if (is_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true
a = to_app(qe);
t = m_manager.mk_true();
return true;
}
}
return false;
}
void quasi_macros::quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro) {
m_new_var_names.reset();
m_new_vars.reset();
m_new_qsorts.reset();
m_new_eqs.reset();
func_decl * f = a->get_decl();
// CMW: we rely on the fact that all variables in q appear at least once as
// a direct argument of `a'.
bit_vector v_seen;
v_seen.resize(q->get_num_decls(), false);
for (unsigned i = 0 ; i < a->get_num_args() ; i++) {
if (!is_var(a->get_arg(i)) ||
v_seen.get(to_var(a->get_arg(i))->get_idx())) {
unsigned inx = m_new_var_names.size();
m_new_name.str("");
m_new_name << "X" << inx;
m_new_var_names.push_back(symbol(m_new_name.str().c_str()));
m_new_qsorts.push_back(f->get_domain()[i]);
m_new_vars.push_back(m_manager.mk_var(inx + q->get_num_decls(), f->get_domain()[i]));
m_new_eqs.push_back(m_manager.mk_eq(m_new_vars.back(), a->get_arg(i)));
} else {
var * v = to_var(a->get_arg(i));
m_new_vars.push_back(v);
v_seen.set(v->get_idx(), true);
}
}
// Reverse the new variable names and sorts. [CMW: There is a smarter way to do this.]
vector<symbol> new_var_names_rev;
sort_ref_vector new_qsorts_rev(m_manager);
unsigned i = m_new_var_names.size();
while (i > 0) {
i--;
new_var_names_rev.push_back(m_new_var_names.get(i));
new_qsorts_rev.push_back(m_new_qsorts.get(i));
}
// We want to keep all the old variables [already reversed]
for (unsigned i = 0 ; i < q->get_num_decls() ; i++) {
new_var_names_rev.push_back(q->get_decl_name(i));
new_qsorts_rev.push_back(q->get_decl_sort(i));
}
// Macro := Forall m_new_vars . appl = ITE( m_new_eqs, t, f_else)
app_ref appl(m_manager);
expr_ref eq(m_manager);
appl = m_manager.mk_app(f, m_new_vars.size(), m_new_vars.c_ptr());
func_decl * fd = m_manager.mk_fresh_func_decl(f->get_name(), symbol("else"),
f->get_arity(), f->get_domain(),
f->get_range());
expr * f_else = m_manager.mk_app(fd, m_new_vars.size(), m_new_vars.c_ptr());
expr_ref ite(m_manager);
ite = m_manager.mk_ite(m_manager.mk_and(m_new_eqs.size(), m_new_eqs.c_ptr()), t, f_else);
eq = m_manager.mk_eq(appl, ite);
macro = m_manager.mk_quantifier(true, new_var_names_rev.size(),
new_qsorts_rev.c_ptr(), new_var_names_rev.c_ptr(), eq);
}
bool quasi_macros::find_macros(unsigned n, expr * const * exprs) {
TRACE("quasi_macros", tout << "Finding quasi-macros in: " << std::endl;
for (unsigned i = 0 ; i < n ; i++)
tout << i << ": " << mk_pp(exprs[i], m_manager) << std::endl; );
bool res = false;
m_occurrences.reset();
// Find out how many non-ground appearences for each uninterpreted function there are
for ( unsigned i = 0 ; i < n ; i++ )
find_occurrences(exprs[i]);
TRACE("quasi_macros", tout << "Occurrences: " << std::endl;
for (occurrences_map::iterator it = m_occurrences.begin();
it != m_occurrences.end();
it++)
tout << it->m_key->get_name() << ": " << it->m_value << std::endl; );
// Find all macros
for ( unsigned i = 0 ; i < n ; i++ ) {
app_ref a(m_manager);
expr_ref t(m_manager);
if (is_quasi_macro(exprs[i], a, t)) {
quantifier_ref macro(m_manager);
quasi_macro_to_macro(to_quantifier(exprs[i]), a, t, macro);
TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i], m_manager) << std::endl;
tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; );
proof * pr = 0;
if (m_manager.proofs_enabled())
pr = m_manager.mk_def_axiom(macro);
if (m_macro_manager.insert(a->get_decl(), macro, pr))
res = true;
}
}
return res;
}
void quasi_macros::apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
for ( unsigned i = 0 ; i < n ; i++ ) {
expr_ref r(m_manager), rs(m_manager);
proof_ref pr(m_manager), ps(m_manager);
proof * p = m_manager.proofs_enabled() ? prs[i] : 0;
m_macro_manager.expand_macros(exprs[i], p, r, pr);
m_simplifier(r, rs, ps);
new_exprs.push_back(rs);
new_prs.push_back(ps);
}
}
bool quasi_macros::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
if (find_macros(n, exprs)) {
apply_macros(n, exprs, prs, new_exprs, new_prs);
return true;
} else {
// just copy them over
for ( unsigned i = 0 ; i < n ; i++ ) {
new_exprs.push_back(exprs[i]);
if (m_manager.proofs_enabled())
new_prs.push_back(prs[i]);
}
return false;
}
}

View file

@ -0,0 +1,69 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
quasi_macros.cpp
Abstract:
<abstract>
Author:
Christoph Wintersteiger (t-cwinte) 2010-04-23
Revision History:
--*/
#ifndef _QUASI_MACROS_H_
#define _QUASI_MACROS_H_
#include<sstream>
#include"macro_manager.h"
#include"basic_simplifier_plugin.h"
#include"simplifier.h"
/**
\brief Finds quasi macros and applies them.
*/
class quasi_macros {
typedef obj_map<func_decl, unsigned> occurrences_map;
ast_manager & m_manager;
macro_manager & m_macro_manager;
basic_simplifier_plugin & m_bsimp;
simplifier & m_simplifier;
occurrences_map m_occurrences;
ptr_vector<expr> m_todo;
vector<symbol> m_new_var_names;
expr_ref_vector m_new_vars;
expr_ref_vector m_new_eqs;
sort_ref_vector m_new_qsorts;
std::stringstream m_new_name;
expr_mark m_visited_once;
expr_mark m_visited_more;
bool is_unique(func_decl * f) const;
bool fully_depends_on(app * a, quantifier * q) const;
bool depends_on(expr * e, func_decl * f) const;
bool is_quasi_macro(expr * e, app_ref & a, expr_ref &v) const;
void quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro);
void find_occurrences(expr * e);
bool find_macros(unsigned n, expr * const * exprs);
void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
public:
quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s);
~quasi_macros();
/**
\brief Find pure function macros and apply them.
*/
bool operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
};
#endif

View file

@ -0,0 +1,741 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
pattern_inference.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2006-12-08.
Revision History:
--*/
#include"pattern_inference.h"
#include"ast_ll_pp.h"
#include"ast_pp.h"
#include"ast_util.h"
#include"warning.h"
#include"arith_decl_plugin.h"
#include"pull_quant.h"
#include"well_sorted.h"
#include"for_each_expr.h"
void smaller_pattern::save(expr * p1, expr * p2) {
expr_pair e(p1, p2);
if (!m_cache.contains(e)) {
TRACE("smaller_pattern_proc", tout << "saving: " << p1->get_id() << " " << p2->get_id() << "\n";);
m_cache.insert(e);
m_todo.push_back(e);
}
}
bool smaller_pattern::process(expr * p1, expr * p2) {
m_todo.reset();
m_cache.reset();
save(p1, p2);
while (!m_todo.empty()) {
expr_pair & curr = m_todo.back();
p1 = curr.first;
p2 = curr.second;
m_todo.pop_back();
ast_kind k1 = p1->get_kind();
if (k1 != AST_VAR && k1 != p2->get_kind())
return false;
switch (k1) {
case AST_APP: {
app * app1 = to_app(p1);
app * app2 = to_app(p2);
unsigned num1 = app1->get_num_args();
if (num1 != app2->get_num_args() || app1->get_decl() != app2->get_decl())
return false;
for (unsigned i = 0; i < num1; i++)
save(app1->get_arg(i), app2->get_arg(i));
break;
}
case AST_VAR: {
unsigned idx = to_var(p1)->get_idx();
if (idx < m_bindings.size()) {
if (m_bindings[idx] == 0)
m_bindings[idx] = p2;
else if (m_bindings[idx] != p2)
return false;
}
// it is a variable bound by an external quantifier
else if (p1 != p2)
return false;
break;
}
default:
if (p1 != p2)
return false;
break;
}
}
return true;
}
bool smaller_pattern::operator()(unsigned num_bindings, expr * p1, expr * p2) {
m_bindings.resize(num_bindings);
for (unsigned i = 0; i < num_bindings; i++)
m_bindings[i] = 0;
return process(p1, p2);
}
pattern_inference::pattern_inference(ast_manager & m, pattern_inference_params & params, pattern_database * db):
simplifier(m),
m_params(params),
m_bfid(m.get_basic_family_id()),
m_afid(m.get_family_id("arith")),
m_le(m),
m_nested_arith_only(true),
m_block_loop_patterns(params.m_pi_block_loop_patterns),
m_candidates(m),
m_pattern_weight_lt(m_candidates_info),
m_collect(m, *this),
m_contains_subpattern(*this),
m_database(db) {
if (params.m_pi_arith == AP_NO)
register_forbidden_family(m_afid);
enable_ac_support(false);
}
void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) {
SASSERT(m_info.empty());
SASSERT(m_todo.empty());
SASSERT(m_cache.empty());
m_num_bindings = num_bindings;
m_todo.push_back(entry(n, 0));
while (!m_todo.empty()) {
entry & e = m_todo.back();
n = e.m_node;
unsigned delta = e.m_delta;
TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";);
TRACE("collect_info", tout << mk_pp(n, m_manager) << "\n";);
if (visit_children(n, delta)) {
m_todo.pop_back();
save_candidate(n, delta);
}
}
reset();
}
inline void pattern_inference::collect::visit(expr * n, unsigned delta, bool & visited) {
entry e(n, delta);
if (!m_cache.contains(e)) {
m_todo.push_back(e);
visited = false;
}
}
bool pattern_inference::collect::visit_children(expr * n, unsigned delta) {
bool visited = true;
unsigned i;
switch (n->get_kind()) {
case AST_APP:
i = to_app(n)->get_num_args();
while (i > 0) {
--i;
visit(to_app(n)->get_arg(i), delta, visited);
}
break;
case AST_QUANTIFIER:
visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited);
break;
default:
break;
}
return visited;
}
inline void pattern_inference::collect::save(expr * n, unsigned delta, info * i) {
m_cache.insert(entry(n, delta), i);
if (i != 0)
m_info.push_back(i);
}
void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
switch (n->get_kind()) {
case AST_VAR: {
unsigned idx = to_var(n)->get_idx();
if (idx >= delta) {
idx = idx - delta;
uint_set free_vars;
if (idx < m_num_bindings)
free_vars.insert(idx);
info * i = 0;
if (delta == 0)
i = alloc(info, m_manager, n, free_vars, 1);
else
i = alloc(info, m_manager, m_manager.mk_var(idx, to_var(n)->get_sort()), free_vars, 1);
save(n, delta, i);
}
else {
save(n, delta, 0);
}
return;
}
case AST_APP: {
app * c = to_app(n);
func_decl * decl = c->get_decl();
if (m_owner.is_forbidden(c)) {
save(n, delta, 0);
return;
}
if (c->get_num_args() == 0) {
save(n, delta, alloc(info, m_manager, n, uint_set(), 1));
return;
}
ptr_buffer<expr> buffer;
bool changed = false; // false if none of the children is mapped to a node different from itself.
uint_set free_vars;
unsigned size = 1;
unsigned num = c->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * child = c->get_arg(i);
info * child_info = 0;
#ifdef Z3DEBUG
bool found =
#endif
m_cache.find(entry(child, delta), child_info);
SASSERT(found);
if (child_info == 0) {
save(n, delta, 0);
return;
}
buffer.push_back(child_info->m_node.get());
free_vars |= child_info->m_free_vars;
size += child_info->m_size;
if (child != child_info->m_node.get())
changed = true;
}
app * new_node = 0;
if (changed)
new_node = m_manager.mk_app(decl, buffer.size(), buffer.c_ptr());
else
new_node = to_app(n);
save(n, delta, alloc(info, m_manager, new_node, free_vars, size));
// Remark: arithmetic patterns are only used if they are nested inside other terms.
// That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern
// if arithmetic is not in the forbidden list.
//
// Remark: The rule above has an exception. The operators (div, idiv, mod) are allowed to be
// used as patterns even when they are not nested in other terms. The motivation is that
// Z3 currently doesn't implement them (i.e., they are uninterpreted). So, some users add axioms
// stating properties about these operators.
family_id fid = c->get_family_id();
decl_kind k = c->get_decl_kind();
if (!free_vars.empty() &&
(fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) {
TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m_manager) << "\n";);
m_owner.add_candidate(new_node, free_vars, size);
}
return;
}
default:
save(n, delta, 0);
return;
}
}
void pattern_inference::collect::reset() {
m_cache.reset();
std::for_each(m_info.begin(), m_info.end(), delete_proc<info>());
m_info.reset();
SASSERT(m_todo.empty());
}
void pattern_inference::add_candidate(app * n, uint_set const & free_vars, unsigned size) {
for (unsigned i = 0; i < m_num_no_patterns; i++) {
if (n == m_no_patterns[i])
return;
}
if (!m_candidates_info.contains(n)) {
m_candidates_info.insert(n, info(free_vars, size));
m_candidates.push_back(n);
}
}
/**
\brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true.
Otherwise, copy m_candidates to result.
*/
void pattern_inference::filter_looping_patterns(ptr_vector<app> & result) {
unsigned num = m_candidates.size();
for (unsigned i1 = 0; i1 < num; i1++) {
app * n1 = m_candidates.get(i1);
expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1);
SASSERT(e1);
uint_set const & s1 = e1->get_data().m_value.m_free_vars;
if (m_block_loop_patterns) {
bool smaller = false;
for (unsigned i2 = 0; i2 < num; i2++) {
if (i1 != i2) {
app * n2 = m_candidates.get(i2);
expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2);
if (e2) {
uint_set const & s2 = e2->get_data().m_value.m_free_vars;
// Remark: the comparison operator only makes sense if both AST nodes
// contain the same number of variables.
// Example:
// (f X Y) <: (f (g X Z W) Y)
if (s1 == s2 && m_le(m_num_bindings, n1, n2) && !m_le(m_num_bindings, n2, n1)) {
smaller = true;
break;
}
}
}
}
if (!smaller)
result.push_back(n1);
else
m_candidates_info.erase(n1);
}
else {
result.push_back(n1);
}
}
}
inline void pattern_inference::contains_subpattern::save(expr * n) {
unsigned id = n->get_id();
m_already_processed.assure_domain(id);
if (!m_already_processed.contains(id)) {
m_todo.push_back(n);
m_already_processed.insert(id);
}
}
bool pattern_inference::contains_subpattern::operator()(expr * n) {
m_already_processed.reset();
m_todo.reset();
expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n);
SASSERT(_e);
uint_set const & s1 = _e->get_data().m_value.m_free_vars;
save(n);
unsigned num;
while (!m_todo.empty()) {
expr * curr = m_todo.back();
m_todo.pop_back();
switch (curr->get_kind()) {
case AST_APP:
if (curr != n) {
expr2info::obj_map_entry * e = m_owner.m_candidates_info.find_core(curr);
if (e) {
uint_set const & s2 = e->get_data().m_value.m_free_vars;
SASSERT(s2.subset_of(s1));
if (s1 == s2) {
TRACE("pattern_inference", tout << mk_pp(n, m_owner.m_manager) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m_manager) << "\n";);
return true;
}
}
}
num = to_app(curr)->get_num_args();
for (unsigned i = 0; i < num; i++)
save(to_app(curr)->get_arg(i));
break;
case AST_VAR:
break;
default:
UNREACHABLE();
}
}
return false;
}
/**
Return true if n contains a direct/indirect child that is also a
pattern, and contains the same number of free variables.
*/
inline bool pattern_inference::contains_subpattern(expr * n) {
return m_contains_subpattern(n);
}
/**
\brief Copy a pattern p in patterns to result, if there is no
direct/indirect child of p in patterns which contains the same set
of variables.
Remark: Every pattern p in patterns is also a member of
m_pattern_map.
*/
void pattern_inference::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) {
ptr_vector<app>::const_iterator it = patterns.begin();
ptr_vector<app>::const_iterator end = patterns.end();
for (; it != end; ++it) {
app * curr = *it;
if (!contains_subpattern(curr))
result.push_back(curr);
}
}
bool pattern_inference::pattern_weight_lt::operator()(expr * n1, expr * n2) const {
expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1);
expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2);
SASSERT(e1 != 0);
SASSERT(e2 != 0);
info const & i1 = e1->get_data().m_value;
info const & i2 = e2->get_data().m_value;
unsigned num_free_vars1 = i1.m_free_vars.num_elems();
unsigned num_free_vars2 = i2.m_free_vars.num_elems();
return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size);
}
/**
\brief Create unary patterns (single expressions that contain all
bound variables). If a candidate does not contain all bound
variables, then it is copied to remaining_candidate_patterns. The
new patterns are stored in result.
*/
void pattern_inference::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
ptr_vector<app> & remaining_candidate_patterns,
app_ref_buffer & result) {
ptr_vector<app>::const_iterator it = candidate_patterns.begin();
ptr_vector<app>::const_iterator end = candidate_patterns.end();
for (; it != end; ++it) {
app * candidate = *it;
expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
info const & i = e->get_data().m_value;
if (i.m_free_vars.num_elems() == m_num_bindings) {
app * new_pattern = m_manager.mk_pattern(candidate);
result.push_back(new_pattern);
}
else {
remaining_candidate_patterns.push_back(candidate);
}
}
}
// TODO: this code is too inefficient when the number of candidate
// patterns is too big.
// HACK: limit the number of case-splits:
#define MAX_SPLITS 32
void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns,
ptr_vector<app> const & candidate_patterns,
app_ref_buffer & result) {
SASSERT(!candidate_patterns.empty());
m_pre_patterns.push_back(alloc(pre_pattern));
unsigned sz = candidate_patterns.size();
unsigned num_splits = 0;
for (unsigned j = 0; j < m_pre_patterns.size(); j++) {
pre_pattern * curr = m_pre_patterns[j];
if (curr->m_free_vars.num_elems() == m_num_bindings) {
app * new_pattern = m_manager.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr());
result.push_back(new_pattern);
if (result.size() >= max_num_patterns)
return;
}
else if (curr->m_idx < sz) {
app * n = candidate_patterns[curr->m_idx];
expr2info::obj_map_entry * e = m_candidates_info.find_core(n);
uint_set const & s = e->get_data().m_value.m_free_vars;
if (!s.subset_of(curr->m_free_vars)) {
pre_pattern * new_p = alloc(pre_pattern,*curr);
new_p->m_exprs.push_back(n);
new_p->m_free_vars |= s;
new_p->m_idx++;
m_pre_patterns.push_back(new_p);
if (num_splits < MAX_SPLITS) {
m_pre_patterns[j] = 0;
curr->m_idx++;
m_pre_patterns.push_back(curr);
num_splits++;
}
}
else {
m_pre_patterns[j] = 0;
curr->m_idx++;
m_pre_patterns.push_back(curr);
}
}
TRACE("pattern_inference", tout << "m_pre_patterns.size(): " << m_pre_patterns.size() <<
"\nnum_splits: " << num_splits << "\n";);
}
}
void pattern_inference::reset_pre_patterns() {
std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc<pre_pattern>());
m_pre_patterns.reset();
}
static void dump_app_vector(std::ostream & out, ptr_vector<app> const & v, ast_manager & m) {
ptr_vector<app>::const_iterator it = v.begin();
ptr_vector<app>::const_iterator end = v.end();
for (; it != end; ++it)
out << mk_pp(*it, m) << "\n";
}
bool pattern_inference::is_forbidden(app * n) const {
func_decl const * decl = n->get_decl();
if (is_ground(n))
return false;
// Remark: skolem constants should not be used in patterns, since they do not
// occur outside of the quantifier. That is, Z3 will never match this kind of
// pattern.
if (m_params.m_pi_avoid_skolems && decl->is_skolem()) {
CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m_manager) << "\n";);
return true;
}
if (is_forbidden(decl))
return true;
return false;
}
bool pattern_inference::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) {
if (m_preferred.empty())
return false;
bool found = false;
ptr_vector<app>::const_iterator it = candidate_patterns.begin();
ptr_vector<app>::const_iterator end = candidate_patterns.end();
for (; it != end; ++it) {
app * candidate = *it;
if (m_preferred.contains(to_app(candidate)->get_decl())) {
expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
info const & i = e->get_data().m_value;
if (i.m_free_vars.num_elems() == m_num_bindings) {
TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m_manager) << "\n";);
app * p = m_manager.mk_pattern(candidate);
result.push_back(p);
found = true;
}
}
}
return found;
}
void pattern_inference::mk_patterns(unsigned num_bindings,
expr * n,
unsigned num_no_patterns,
expr * const * no_patterns,
app_ref_buffer & result) {
m_num_bindings = num_bindings;
m_num_no_patterns = num_no_patterns;
m_no_patterns = no_patterns;
m_collect(n, num_bindings);
TRACE("pattern_inference",
tout << mk_pp(n, m_manager);
tout << "\ncandidates:\n";
unsigned num = m_candidates.size();
for (unsigned i = 0; i < num; i++) {
tout << mk_pp(m_candidates.get(i), m_manager) << "\n";
});
if (!m_candidates.empty()) {
m_tmp1.reset();
filter_looping_patterns(m_tmp1);
TRACE("pattern_inference",
tout << "candidates after removing looping-patterns:\n";
dump_app_vector(tout, m_tmp1, m_manager););
SASSERT(!m_tmp1.empty());
if (!has_preferred_patterns(m_tmp1, result)) {
// continue if there are no preferred patterns
m_tmp2.reset();
filter_bigger_patterns(m_tmp1, m_tmp2);
SASSERT(!m_tmp2.empty());
TRACE("pattern_inference",
tout << "candidates after removing bigger patterns:\n";
dump_app_vector(tout, m_tmp2, m_manager););
m_tmp1.reset();
candidates2unary_patterns(m_tmp2, m_tmp1, result);
unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns;
if (result.empty())
num_extra_multi_patterns++;
if (num_extra_multi_patterns > 0 && !m_tmp1.empty()) {
// m_pattern_weight_lt is not a total order
std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt);
TRACE("pattern_inference",
tout << "candidates after sorting:\n";
dump_app_vector(tout, m_tmp1, m_manager););
candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result);
}
}
}
reset_pre_patterns();
m_candidates_info.reset();
m_candidates.reset();
}
void pattern_inference::reduce1_quantifier(quantifier * q) {
TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m_manager) << "\n";);
if (!q->is_forall()) {
simplifier::reduce1_quantifier(q);
return;
}
int weight = q->get_weight();
if (m_database) {
app_ref_vector new_patterns(m_manager);
unsigned new_weight;
if (m_database->match_quantifier(q, new_patterns, new_weight)) {
#ifdef Z3DEBUG
for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m_manager, new_patterns.get(i))); }
#endif
quantifier_ref new_q(m_manager);
if (q->get_num_patterns() > 0) {
// just update the weight...
TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m_manager) << "\n";);
new_q = m_manager.update_quantifier_weight(q, new_weight);
}
else {
quantifier_ref tmp(m_manager);
tmp = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr());
new_q = m_manager.update_quantifier_weight(tmp, new_weight);
TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m_manager) << "\n";);
}
proof * pr = 0;
if (m_manager.fine_grain_proofs())
pr = m_manager.mk_rewrite(q, new_q);
cache_result(q, new_q, pr);
return;
}
}
if (q->get_num_patterns() > 0) {
simplifier::reduce1_quantifier(q);
return;
}
if (m_params.m_pi_nopat_weight >= 0)
weight = m_params.m_pi_nopat_weight;
SASSERT(q->get_num_patterns() == 0);
expr * new_body;
proof * new_body_pr;
get_cached(q->get_expr(), new_body, new_body_pr);
ptr_buffer<expr> new_no_patterns;
unsigned num_no_patterns = q->get_num_no_patterns();
for (unsigned i = 0; i < num_no_patterns; i++) {
expr * new_pattern;
proof * new_pattern_pr;
get_cached(q->get_no_pattern(i), new_pattern, new_pattern_pr);
new_no_patterns.push_back(new_pattern);
}
app_ref_buffer new_patterns(m_manager);
if (m_params.m_pi_arith == AP_CONSERVATIVE)
m_forbidden.push_back(m_afid);
mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns);
if (new_patterns.empty() && !new_no_patterns.empty()) {
if (new_patterns.empty()) {
mk_patterns(q->get_num_decls(), new_body, 0, 0, new_patterns);
if (m_params.m_pi_warnings && !new_patterns.empty()) {
warning_msg("ignoring nopats annotation because Z3 couldn't find any other pattern (quantifier id: %s)", q->get_qid().str().c_str());
}
}
}
if (m_params.m_pi_arith == AP_CONSERVATIVE) {
m_forbidden.pop_back();
if (new_patterns.empty()) {
flet<bool> l1(m_block_loop_patterns, false); // allow looping patterns
mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns);
if (!new_patterns.empty()) {
weight = std::max(weight, static_cast<int>(m_params.m_pi_arith_weight));
if (m_params.m_pi_warnings) {
warning_msg("using arith. in pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_ARITH_WEIGHT=<val>).",
q->get_qid().str().c_str(), weight);
}
}
}
}
if (m_params.m_pi_arith != AP_NO && new_patterns.empty()) {
if (new_patterns.empty()) {
flet<bool> l1(m_nested_arith_only, false); // try to find a non-nested arith pattern
flet<bool> l2(m_block_loop_patterns, false); // allow looping patterns
mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns);
if (!new_patterns.empty()) {
weight = std::max(weight, static_cast<int>(m_params.m_pi_non_nested_arith_weight));
if (m_params.m_pi_warnings) {
warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=<val>).",
q->get_qid().str().c_str(), weight);
}
// verbose_stream() << mk_pp(q, m_manager) << "\n";
}
}
}
quantifier_ref new_q(m_manager);
new_q = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body);
if (weight != q->get_weight())
new_q = m_manager.update_quantifier_weight(new_q, weight);
proof_ref pr(m_manager);
if (m_manager.fine_grain_proofs()) {
if (new_body_pr == 0)
new_body_pr = m_manager.mk_reflexivity(new_body);
pr = m_manager.mk_quant_intro(q, new_q, new_body_pr);
}
if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) {
pull_quant pull(m_manager);
expr_ref new_expr(m_manager);
proof_ref new_pr(m_manager);
pull(new_q, new_expr, new_pr);
quantifier * new_new_q = to_quantifier(new_expr);
if (new_new_q != new_q) {
mk_patterns(new_new_q->get_num_decls(), new_new_q->get_expr(), 0, 0, new_patterns);
if (!new_patterns.empty()) {
if (m_params.m_pi_warnings) {
warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str());
}
new_q = m_manager.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr());
if (m_manager.fine_grain_proofs()) {
pr = m_manager.mk_transitivity(pr, new_pr);
pr = m_manager.mk_transitivity(pr, m_manager.mk_quant_intro(new_new_q, new_q, m_manager.mk_reflexivity(new_q->get_expr())));
}
TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m_manager) << "\n";);
}
}
}
if (new_patterns.empty()) {
if (m_params.m_pi_warnings) {
warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str());
}
TRACE("pi_failed", tout << mk_pp(q, m_manager) << "\n";);
}
if (new_patterns.empty() && new_body == q->get_expr()) {
cache_result(q, q, 0);
return;
}
cache_result(q, new_q, pr);
}
#if 0
// unused
static void dump_expr_vector(std::ostream & out, ptr_vector<expr> const & v, ast_manager & m) {
ptr_vector<expr>::const_iterator it = v.begin();
ptr_vector<expr>::const_iterator end = v.end();
for (; it != end; ++it)
out << mk_pp(*it, m) << "\n";
}
#endif

View file

@ -0,0 +1,254 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
pattern_inference.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2006-12-08.
Revision History:
--*/
#ifndef _PATTERN_INFERENCE_H_
#define _PATTERN_INFERENCE_H_
#include"ast.h"
#include"simplifier.h"
#include"pattern_inference_params.h"
#include"vector.h"
#include"uint_set.h"
#include"nat_set.h"
#include"obj_hashtable.h"
#include"obj_pair_hashtable.h"
#include"map.h"
class pattern_database {
public:
virtual ~pattern_database() {}
virtual void initialize(char const * smt_patterns) = 0;
virtual bool match_quantifier(quantifier * qf, app_ref_vector & patterns, unsigned & weight) = 0;
};
/**
\brief A pattern p_1 is smaller than a pattern p_2 iff
every instance of p_2 is also an instance of p_1.
Example: f(X) is smaller than f(g(X)) because
every instance of f(g(X)) is also an instance of f(X).
*/
class smaller_pattern {
ast_manager & m_manager;
ptr_vector<expr> m_bindings;
typedef std::pair<expr *, expr *> expr_pair;
typedef obj_pair_hashtable<expr, expr> cache;
svector<expr_pair> m_todo;
cache m_cache;
void save(expr * p1, expr * p2);
bool process(expr * p1, expr * p2);
smaller_pattern & operator=(smaller_pattern const &);
public:
smaller_pattern(ast_manager & m):
m_manager(m) {
}
bool operator()(unsigned num_bindings, expr * p1, expr * p2);
};
class pattern_inference : public simplifier {
pattern_inference_params & m_params;
family_id m_bfid;
family_id m_afid;
svector<family_id> m_forbidden;
obj_hashtable<func_decl> m_preferred;
smaller_pattern m_le;
unsigned m_num_bindings;
unsigned m_num_no_patterns;
expr * const * m_no_patterns;
bool m_nested_arith_only;
bool m_block_loop_patterns;
struct info {
uint_set m_free_vars;
unsigned m_size;
info(uint_set const & vars, unsigned size):
m_free_vars(vars),
m_size(size) {
}
info():
m_free_vars(),
m_size(0) {
}
};
typedef obj_map<expr, info> expr2info;
expr2info m_candidates_info; // candidate -> set of free vars + size
app_ref_vector m_candidates;
ptr_vector<app> m_tmp1;
ptr_vector<app> m_tmp2;
ptr_vector<app> m_todo;
// Compare candidates patterns based on their usefulness
// p1 < p2 if
// - p1 has more free variables than p2
// - p1 and p2 has the same number of free variables,
// and p1 is smaller than p2.
struct pattern_weight_lt {
expr2info & m_candidates_info;
pattern_weight_lt(expr2info & i):
m_candidates_info(i) {
}
bool operator()(expr * n1, expr * n2) const;
};
pattern_weight_lt m_pattern_weight_lt;
//
// Functor for collecting candidates.
//
class collect {
struct entry {
expr * m_node;
unsigned m_delta;
entry():m_node(0), m_delta(0) {}
entry(expr * n, unsigned d):m_node(n), m_delta(d) {}
unsigned hash() const {
return hash_u_u(m_node->get_id(), m_delta);
}
bool operator==(entry const & e) const {
return m_node == e.m_node && m_delta == e.m_delta;
}
};
struct info {
expr_ref m_node;
uint_set m_free_vars;
unsigned m_size;
info(ast_manager & m, expr * n, uint_set const & vars, unsigned sz):
m_node(n, m), m_free_vars(vars), m_size(sz) {}
};
ast_manager & m_manager;
pattern_inference & m_owner;
family_id m_afid;
unsigned m_num_bindings;
typedef map<entry, info *, obj_hash<entry>, default_eq<entry> > cache;
cache m_cache;
ptr_vector<info> m_info;
svector<entry> m_todo;
void visit(expr * n, unsigned delta, bool & visited);
bool visit_children(expr * n, unsigned delta);
void save(expr * n, unsigned delta, info * i);
void save_candidate(expr * n, unsigned delta);
void reset();
public:
collect(ast_manager & m, pattern_inference & o):m_manager(m), m_owner(o), m_afid(m.get_family_id("arith")) {}
void operator()(expr * n, unsigned num_bindings);
};
collect m_collect;
void add_candidate(app * n, uint_set const & s, unsigned size);
void filter_looping_patterns(ptr_vector<app> & result);
bool has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result);
void filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result);
class contains_subpattern {
pattern_inference & m_owner;
nat_set m_already_processed;
ptr_vector<expr> m_todo;
void save(expr * n);
public:
contains_subpattern(pattern_inference & owner):
m_owner(owner) {}
bool operator()(expr * n);
};
contains_subpattern m_contains_subpattern;
bool contains_subpattern(expr * n);
struct pre_pattern {
ptr_vector<app> m_exprs; // elements of the pattern.
uint_set m_free_vars; // set of free variables in m_exprs
unsigned m_idx; // idx of the next candidate to process.
pre_pattern():
m_idx(0) {
}
};
ptr_vector<pre_pattern> m_pre_patterns;
pattern_database * m_database;
void candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
ptr_vector<app> & remaining_candidate_patterns,
app_ref_buffer & result);
void candidates2multi_patterns(unsigned max_num_patterns,
ptr_vector<app> const & candidate_patterns,
app_ref_buffer & result);
void reset_pre_patterns();
/**
\brief All minimal unary patterns (i.e., expressions that
contain all bound variables) are copied to result. If there
are unary patterns, then at most num_extra_multi_patterns multi
patterns are created. If there are no unary pattern, then at
most 1 + num_extra_multi_patterns multi_patterns are created.
*/
void mk_patterns(unsigned num_bindings, // IN number of bindings.
expr * n, // IN node where the patterns are going to be extracted.
unsigned num_no_patterns, // IN num. patterns that should not be used.
expr * const * no_patterns, // IN patterns that should not be used.
app_ref_buffer & result); // OUT result
virtual void reduce1_quantifier(quantifier * q);
public:
pattern_inference(ast_manager & m, pattern_inference_params & params, pattern_database * db);
void register_forbidden_family(family_id fid) {
SASSERT(fid != m_bfid);
m_forbidden.push_back(fid);
}
/**
\brief Register f as a preferred function symbol. The inference algorithm
gives preference to patterns rooted by this kind of function symbol.
*/
void register_preferred(func_decl * f) {
m_preferred.insert(f);
}
void register_preferred(unsigned num, func_decl * const * fs) { for (unsigned i = 0; i < num; i++) register_preferred(fs[i]); }
bool is_forbidden(func_decl const * decl) const {
family_id fid = decl->get_family_id();
if (fid == m_bfid && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE)
return true;
return std::find(m_forbidden.begin(), m_forbidden.end(), fid) != m_forbidden.end();
}
bool is_forbidden(app * n) const;
};
#endif /* _PATTERN_INFERENCE_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,116 @@
/*++
Copyright (c) 2008 Microsoft Corporation
Module Name:
proof_checker.h
Abstract:
Proof checker.
Author:
Nikolaj Bjorner (nbjorner) 2008-03-07.
Revision History:
--*/
#ifndef _PROOF_CHECKER_H_
#define _PROOF_CHECKER_H_
#include "ast.h"
#include "map.h"
class proof_checker {
ast_manager& m_manager;
proof_ref_vector m_todo;
expr_mark m_marked;
expr_ref_vector m_pinned;
obj_map<expr, expr*> m_hypotheses;
family_id m_hyp_fid;
family_id m_spc_fid;
app_ref m_nil;
bool m_dump_lemmas;
std::string m_logic;
unsigned m_proof_lemma_id;
enum hyp_decl_kind {
OP_CONS,
OP_ATOM,
OP_NIL
};
enum hyp_sort_kind {
CELL_SORT
};
class hyp_decl_plugin : public decl_plugin {
protected:
func_decl* m_cons;
func_decl* m_atom;
func_decl* m_nil;
sort* m_cell;
virtual void set_manager(ast_manager * m, family_id id);
func_decl * mk_func_decl(decl_kind k);
public:
hyp_decl_plugin();
virtual ~hyp_decl_plugin() {}
virtual void finalize();
virtual decl_plugin * mk_fresh() { return alloc(hyp_decl_plugin); }
virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters);
virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range);
virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned num_args, expr * const * args, sort * range);
virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
virtual void get_sort_names(svector<builtin_name> & sort_names, symbol const & logic);
};
public:
proof_checker(ast_manager& m);
void set_dump_lemmas(char const * logic = "AUFLIA") { m_dump_lemmas = true; m_logic = logic; }
bool check(proof* p, expr_ref_vector& side_conditions);
private:
bool check1(proof* p, expr_ref_vector& side_conditions);
bool check1_basic(proof* p, expr_ref_vector& side_conditions);
bool check1_spc(proof* p, expr_ref_vector& side_conditions);
bool check_arith_proof(proof* p);
bool check_arith_literal(bool is_pos, app* lit, rational const& coeff, expr_ref& sum, bool& is_strict);
bool match_fact(proof* p, expr_ref& fact);
void add_premise(proof* p);
bool match_proof(proof* p);
bool match_proof(proof* p, proof_ref& p0);
bool match_proof(proof* p, proof_ref& p0, proof_ref& p1);
bool match_proof(proof* p, proof_ref_vector& parents);
bool match_binary(expr* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2);
bool match_op(expr* e, decl_kind k, expr_ref& t1, expr_ref& t2);
bool match_op(expr* e, decl_kind k, expr_ref& t);
bool match_op(expr* e, decl_kind k, expr_ref_vector& terms);
bool match_iff(expr* e, expr_ref& t1, expr_ref& t2);
bool match_implies(expr* e, expr_ref& t1, expr_ref& t2);
bool match_eq(expr* e, expr_ref& t1, expr_ref& t2);
bool match_oeq(expr* e, expr_ref& t1, expr_ref& t2);
bool match_not(expr* e, expr_ref& t);
bool match_or(expr* e, expr_ref_vector& terms);
bool match_and(expr* e, expr_ref_vector& terms);
bool match_app(expr* e, func_decl_ref& d, expr_ref_vector& terms);
bool match_quantifier(expr*, bool& is_univ, sort_ref_vector&, expr_ref& body);
bool match_negated(expr* a, expr* b);
bool match_equiv(expr* a, expr_ref& t1, expr_ref& t2);
void get_ors(expr* e, expr_ref_vector& ors);
void get_hypotheses(proof* p, expr_ref_vector& ante);
bool match_nil(expr* e) const;
bool match_cons(expr* e, expr_ref& a, expr_ref& b) const;
bool match_atom(expr* e, expr_ref& a) const;
expr* mk_nil();
expr* mk_cons(expr* a, expr* b);
expr* mk_atom(expr* e);
bool is_hypothesis(proof* p) const;
expr* mk_hyp(unsigned num_hyps, expr * const * hyps);
void dump_proof(proof * pr);
void dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent);
};
#endif

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

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

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_offset.h
Abstract:
Expressions + Offsets.
In order to avoid creating variants of terms, we use a pair (expression, offset),
where offset is just a small integer.
Non ground terms with different offsets are always considered
disequal. For example, (f x):1 is different from (f x):2, and
(f x):1 is unifiable with x:2.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#ifndef _EXPR_OFFSET_H_
#define _EXPR_OFFSET_H_
#include"ast.h"
class expr_offset {
expr * m_expr;
unsigned m_offset;
public:
expr_offset():m_expr(0), m_offset(0) {}
expr_offset(expr * e, unsigned o):m_expr(e), m_offset(o) {}
expr * get_expr() const { return m_expr; }
unsigned get_offset() const { return m_offset; }
bool operator==(expr_offset const & other) const { return m_expr == other.m_expr && m_offset == other.m_offset; }
bool operator!=(expr_offset const & other) const { return !operator==(other); }
unsigned hash() const {
unsigned a = m_expr->get_id();
unsigned b = m_offset;
unsigned c = 17;
mix(a, b, c);
return c;
}
};
typedef std::pair<expr_offset, expr_offset> expr_offset_pair;
typedef pair_hash<obj_hash<expr_offset>, obj_hash<expr_offset> > expr_offset_pair_hash;
#endif /* _EXPR_OFFSET_H_ */

View file

@ -0,0 +1,94 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_offset_map.h
Abstract:
A generic mapping from (expression, offset) to a value T.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _EXPR_OFFSET_MAP_H_
#define _EXPR_OFFSET_MAP_H_
#include"expr_offset.h"
#include"vector.h"
/**
\brief A mapping from expr_offset to some value of type T.
*/
template<typename T>
class expr_offset_map {
struct data {
T m_data;
unsigned m_timestamp;
data():m_timestamp(0) {}
};
vector<svector<data> > m_map;
unsigned m_timestamp;
public:
expr_offset_map():
m_timestamp(1) {}
bool contains(expr_offset const & n) const {
unsigned off = n.get_offset();
if (off < m_map.size()) {
svector<data> const & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id < v.size())
return v[id].m_timestamp == m_timestamp;
}
return false;
}
bool find(expr_offset const & n, T & r) const {
unsigned off = n.get_offset();
if (off < m_map.size()) {
svector<data> const & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id < v.size() && v[id].m_timestamp == m_timestamp) {
r = v[id].m_data;
return true;
}
}
return false;
}
void insert(expr_offset const & n, T const & r) {
unsigned off = n.get_offset();
if (off >= m_map.size())
m_map.resize(off+1, svector<data>());
svector<data> & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id >= v.size())
v.resize(id+1);
v[id].m_data = r;
v[id].m_timestamp = m_timestamp;
}
void reset() {
m_timestamp++;
if (m_timestamp == UINT_MAX) {
typename vector<svector<data> >::iterator it = m_map.begin();
typename vector<svector<data> >::iterator end = m_map.end();
for (; it != end; ++it) {
svector<data> & v = *it;
typename svector<data>::iterator it2 = v.begin();
typename svector<data>::iterator end2 = v.end();
for (; it2 != end2; ++it2)
it2->m_timestamp = 0;
}
m_timestamp = 1;
}
}
};
#endif /* _EXPR_OFFSET_MAP_H_ */

View file

@ -0,0 +1,81 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
matcher.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#include"matcher.h"
matcher::matcher(ast_manager & m):
m_manager(m) {
}
bool matcher::operator()(expr * e1, expr * e2, substitution & s) {
reset();
m_subst = &s;
m_todo.push_back(expr_pair(e1, e2));
while (!m_todo.empty()) {
expr_pair const & p = m_todo.back();
// if (m_cache.contains(p)) {
// m_todo.pop_back();
// continue;
// }
if (is_var(p.first)) {
expr_offset r;
if (m_subst->find(to_var(p.first), 0, r)) {
if (r.get_expr() != p.second)
return false;
}
else {
m_subst->insert(to_var(p.first), 0, expr_offset(p.second, 1));
}
m_todo.pop_back();
continue;
}
if (is_var(p.second))
return false;
app * n1 = to_app(p.first);
app * n2 = to_app(p.second);
if (n1->get_decl() != n2->get_decl())
return false;
unsigned num_args1 = n1->get_num_args();
if (num_args1 != n2->get_num_args())
return false;
m_todo.pop_back();
if (num_args1 == 0)
continue;
// m_cache.insert(p);
unsigned j = num_args1;
while (j > 0) {
--j;
m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j)));
}
}
return true;
}
void matcher::reset() {
// m_cache.reset();
m_todo.reset();
}

View file

@ -0,0 +1,64 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
matcher.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _MATCHER_H_
#define _MATCHER_H_
#include"substitution.h"
#include"hashtable.h"
/**
\brief Functor for matching expressions.
*/
class matcher {
typedef std::pair<expr *, expr *> expr_pair;
typedef pair_hash<obj_ptr_hash<expr>, obj_ptr_hash<expr> > expr_pair_hash;
typedef hashtable<expr_pair, expr_pair_hash, default_eq<expr_pair> > cache;
ast_manager & m_manager;
substitution * m_subst;
cache m_cache;
svector<expr_pair> m_todo;
void reset();
public:
matcher(ast_manager & m);
/**
\brief Return true if e2 is an instance of e1.
In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s.
For example:
1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a))
The result is true, and s will contain x -> h(a)
2) e1 = f(a, x) e2 = f(x, a)
The result is false.
3) e1 = f(x, x) e2 = f(y, a)
The result is false
4) e1 = f(x, y) e2 = f(h(z), a)
The result is true, and s contains x->h(z) and y->a
*/
bool operator()(expr * e1, expr * e2, substitution & s);
};
#endif /* _MATCHER_H_ */

View file

@ -0,0 +1,286 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution.cpp
Abstract:
A substitution, that is, a mapping from (variable, offset) to (expr, offset).
We use offsets in order to avoid creating variants of terms.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#include"substitution.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
substitution::substitution(ast_manager & m):
m_manager(m),
m_new_exprs(m) {
}
void substitution::reset() {
reset_subst();
reset_cache();
}
void substitution::reset_subst() {
m_subst.reset();
m_vars.reset();
m_scopes.reset();
}
void substitution::reset_cache() {
TRACE("subst_bug", tout << "substitution::reset_cache\n";
for (unsigned i = 0; i < m_new_exprs.size(); i++) { tout << mk_pp(m_new_exprs.get(i), m_manager) << "\nref_count: " << m_new_exprs.get(i)->get_ref_count() << "\n"; });
m_apply_cache.reset();
m_new_exprs.reset();
}
void substitution::pop_scope(unsigned num_scopes) {
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
unsigned old_sz = m_scopes[new_lvl];
unsigned curr_sz = m_vars.size();
SASSERT(old_sz <= curr_sz);
for (unsigned i = old_sz; i < curr_sz; i++) {
var_offset & curr = m_vars[i];
m_subst.erase(curr.first, curr.second);
}
m_vars.shrink(old_sz);
m_scopes.shrink(new_lvl);
}
inline void substitution::apply_visit(expr_offset const & n, bool & visited) {
if (!m_apply_cache.contains(n)) {
m_todo.push_back(n);
visited = false;
}
}
void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n,
expr_offset const & s, expr_offset const & t, expr_ref & result) {
TRACE("subst_bug", tout << "BEGIN substitution::apply\n";);
// It is incorrect to cache results between different calls if we are applying a substitution
// modulo a substitution s -> t.
if (s != expr_offset(0,0))
reset_cache();
unsigned j;
expr * e;
unsigned off;
expr_offset n1;
bool visited;
unsigned num_args;
ptr_buffer<expr> new_args;
m_todo.push_back(n);
while (!m_todo.empty()) {
expr_offset n = m_todo.back();
TRACE("subst_bug", tout << "n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << "\n";);
if (m_apply_cache.contains(n)) {
m_todo.pop_back();
continue;
}
expr_offset n_prime = n == s ? t : n;
TRACE("subst_bug", tout << "n_prime: " << mk_pp(n_prime.get_expr(), m_manager) << " : " << n_prime.get_offset() << "\n";);
visited = true;
e = n_prime.get_expr();
off = n_prime.get_offset();
switch (e->get_kind()) {
case AST_VAR:
if (find(to_var(e)->get_idx(), off, n1)) {
apply_visit(n1, visited);
TRACE("subst_bug", tout << "visited: " << visited << ", n1: " << mk_pp(n1.get_expr(), m_manager) << " : " << n1.get_offset() << "\n";);
if (visited) {
m_todo.pop_back();
expr * new_expr;
m_apply_cache.find(n1, new_expr);
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "1. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
}
else {
m_todo.pop_back();
SASSERT(off < num_actual_offsets);
unsigned delta = deltas[off];
expr * new_expr = e;
if (delta > 0) {
new_expr = m_manager.mk_var(to_var(e)->get_idx() + delta, to_var(e)->get_sort());
m_new_exprs.push_back(new_expr);
}
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "2. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
break;
case AST_APP:
num_args = to_app(e)->get_num_args();
j = num_args;
while (j > 0) {
--j;
apply_visit(expr_offset(to_app(e)->get_arg(j), off), visited);
}
if (visited) {
m_todo.pop_back();
new_args.reset();
bool has_new_args = false;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(e)->get_arg(i);
expr * new_arg;
m_apply_cache.find(expr_offset(arg, off), new_arg);
new_args.push_back(new_arg);
if (arg != new_arg)
has_new_args = true;
}
if (!has_new_args) {
m_apply_cache.insert(n, e);
TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(e, m_manager) << "\n";);
}
else {
expr * new_expr = m_manager.mk_app(to_app(e)->get_decl(), new_args.size(), new_args.c_ptr());
m_new_exprs.push_back(new_expr);
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
}
break;
default:
UNREACHABLE();
}
}
SASSERT(m_apply_cache.contains(n));
m_apply_cache.find(n, e);
m_new_exprs.push_back(e);
result = e;
if (s != expr_offset(0,0))
reset_cache();
TRACE("subst_bug", tout << "END substitution::apply\nresult:\n" << mk_pp(e, m_manager) << "\nref_count: " << e->get_ref_count() << "\n";);
}
inline substitution::color substitution::get_color(expr_offset const & p) const {
color c;
if (m_color.find(p, c))
return c;
return White;
}
inline void substitution::set_color(expr_offset const & p, color c) {
m_color.insert(p, c);
}
inline void substitution::visit(expr_offset const & p, bool & visited) {
if (get_color(p) != Black) {
m_todo.push_back(p);
visited = false;
}
}
bool substitution::visit_children(expr_offset const & p) {
bool visited = true;
expr * n = p.get_expr();
unsigned off;
expr_offset p1;
unsigned j;
switch (n->get_kind()) {
case AST_VAR:
if (find(p, p1) && p != p1)
visit(p1, visited);
break;
case AST_APP:
off = p.get_offset();
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
visit(expr_offset(to_app(n)->get_arg(j), off), visited);
}
break;
default:
UNREACHABLE();
}
return visited;
}
bool substitution::acyclic(expr_offset p) {
if (get_color(p) == Black)
return true;
m_todo.reset();
m_todo.push_back(p);
while (!m_todo.empty()) {
expr_offset p = m_todo.back();
switch (get_color(p)) {
case Black:
m_todo.pop_back();
break;
case White:
set_color(p, Grey);
if (visit_children(p)) {
set_color(p, Black);
SASSERT(m_todo.back() == p);
m_todo.pop_back();
}
break;
case Grey:
if (!visit_children(p))
return false;
set_color(p, Black);
SASSERT(m_todo.back() == p);
m_todo.pop_back();
break;
}
}
return true;
}
bool substitution::acyclic() {
m_color.reset();
expr_offset r;
svector<var_offset>::iterator it = m_vars.begin();
svector<var_offset>::iterator end = m_vars.end();
for (; it != end; ++it) {
m_subst.find(it->first, it->second, r);
if (!acyclic(r))
return false;
}
return true;
}
void substitution::display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas) {
reset_cache();
for (unsigned i = 0; i < num_actual_offsets; i++)
for (unsigned j = 0; j < m_subst.vars_capacity(); j++) {
expr_offset r;
if (find(j, i, r)) {
expr_ref tmp(m_manager);
apply(num_actual_offsets, deltas, r, tmp);
out << "VAR " << j << ":" << i << " -->\n" << mk_pp(tmp, m_manager) << "\n";
}
}
}
void substitution::display(std::ostream & out) {
for (unsigned i = 0; i < m_subst.offsets_capacity(); i++)
for (unsigned j = 0; j < m_subst.vars_capacity(); j++) {
expr_offset r;
if (find(j, i, r))
out << "VAR " << j << ":" << i << " --> " << r.get_offset() << "\n" << mk_pp(r.get_expr(), m_manager) << "\n";
}
}

View file

@ -0,0 +1,202 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution.h
Abstract:
A substitution, that is, a mapping from (variable, offset) to (expr, offset).
We use offsets in order to avoid creating variants of terms.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _SUBSTITUTION_H_
#define _SUBSTITUTION_H_
#include"expr_offset_map.h"
#include"var_offset_map.h"
#include"ast_pp.h"
/**
\brief A mapping from (variable,offset) to expr_offset.
*/
class substitution {
ast_manager & m_manager;
var_offset_map<expr_offset> m_subst;
// field for backtracking
typedef std::pair<unsigned, unsigned> var_offset;
svector<var_offset> m_vars;
unsigned_vector m_scopes;
// fields for applying substitutions
svector<expr_offset> m_todo;
expr_offset_map<expr *> m_apply_cache;
expr_ref_vector m_new_exprs;
// fields for checking for cycles
enum color { White, Grey, Black };
expr_offset_map<color> m_color;
#ifdef Z3DEBUG
unsigned m_max_offset_since_reset;
#endif
void apply_visit(expr_offset const & n, bool & visited);
color get_color(expr_offset const & p) const;
void set_color(expr_offset const & p, color c);
void visit(expr_offset const & p, bool & visited);
bool visit_children(expr_offset const & p);
bool acyclic(expr_offset p);
public:
substitution(ast_manager & m);
ast_manager & get_manager() const { return m_manager; }
// -----------------------------------
//
// Reserve memory for the given number of
// offsets and variables.
//
// -----------------------------------
void reserve(unsigned num_offsets, unsigned num_vars) { m_subst.reserve(num_offsets, num_vars); }
void reserve_offsets(unsigned num_offsets) { m_subst.reserve_offsets(num_offsets); }
void reserve_vars(unsigned num_vars) { m_subst.reserve_vars(num_vars); }
// -----------------------------------
//
// Reset functions
//
// -----------------------------------
// reset everything
void reset();
// reset only the mapping from variables to expressions
void reset_subst();
// reset only the substitution application cache
void reset_cache();
// -----------------------------------
//
// Backtracking
//
// -----------------------------------
void push_scope() { m_scopes.push_back(m_vars.size()); }
void pop_scope(unsigned num_scopes = 1);
unsigned get_scope_lvl() { return m_scopes.size(); }
bool top_scope_has_bindings() const { return m_scopes.empty() ? !m_vars.empty() : m_scopes.back() < m_vars.size(); }
unsigned get_num_bindings() const { return m_vars.size(); }
// -----------------------------------
//
// Cycle detection
//
// -----------------------------------
bool acyclic();
// -----------------------------------
//
// Insertion & Lookup
//
// get_binding supplies a way to inspect the substitution.
//
// -----------------------------------
void insert(unsigned v_idx, unsigned offset, expr_offset const & t) {
TRACE("subst_insert", tout << "inserting: #" << v_idx << ":" << offset << " --> " << mk_pp(t.get_expr(), m_manager)
<< ":" << t.get_offset() << "\n";);
m_vars.push_back(var_offset(v_idx, offset));
m_subst.insert(v_idx, offset, t);
}
void insert(var * v, unsigned offset, expr_offset const & t) { insert(v->get_idx(), offset, t); }
void insert(expr_offset v, expr_offset const & t) {
SASSERT(is_var(v.get_expr()));
insert(to_var(v.get_expr()), v.get_offset(), t);
}
bool find(unsigned v_idx, unsigned offset, expr_offset & r) const { return m_subst.find(v_idx, offset, r); }
bool find(var * v, unsigned offset, expr_offset & r) const { return find(v->get_idx(), offset, r); }
bool find(expr_offset v, expr_offset & r) const {
SASSERT(is_var(v.get_expr()));
return find(to_var(v.get_expr()), v.get_offset(), r);
}
void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) {
var = m_vars[binding_num];
VERIFY(m_subst.find(var.first, var.second, r));
}
bool contains(var * v, unsigned offset) { expr_offset r; return find(v, offset, r); }
// -----------------------------------
//
// Application
//
// -----------------------------------
/**
\brief Apply the current substitution to the given
expression+offset. The result is an expression.
The argument num_actual_offsets is the maximum offset used in a
insert method since the last reset.
The argument deltas is an array of size num_actual_offsets. It contains
the variable delta for each offset. A free variable x:i in an expression offset t:j is mapped
to the variable x+delta[i].
*/
void apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_ref & result) {
apply(num_actual_offsets, deltas, n, expr_offset(0, 0), expr_offset(0, 0), result);
}
/**
\brief Similar to the previous method, but occurrences of s in n are substituted by t.
If s != expr_offset(0,0), then the cache is reset before and after the execution of this procedure.
*/
void apply(unsigned num_actual_offsets, unsigned const* deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result);
void apply(expr * n, expr_ref & result) {
unsigned deltas[1] = { 0 };
apply(1, deltas, expr_offset(n, 0), result);
}
// -----------------------------------
//
// Debugging
//
// -----------------------------------
/**
\brief Dump the current substitution (for debugging purposes).
*/
void display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas);
/**
\brief Dump the current substitution without normalizing expressions (for debugging purposes).
*/
void display(std::ostream & out);
// -----------------------------------
//
// Compare terms modulo a substitution
//
// -----------------------------------
bool compare(expr_offset t1, expr_offset t2);
};
#endif

View file

@ -0,0 +1,899 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution_tree.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-04.
Revision History:
--*/
#include"substitution_tree.h"
#include"ast_pp.h"
#include"ast_smt2_pp.h"
/**
\brief Return the next available register.
*/
unsigned substitution_tree::next_reg() {
while(true) {
unsigned curr = m_next_reg;
if (curr > m_max_reg)
m_max_reg = curr;
m_next_reg++;
if (curr >= m_used_regs.size() || !m_used_regs.get(curr))
return curr;
}
}
inline void substitution_tree::push(svector<subst> & sv, subst const & s) {
sv.push_back(s);
m_manager.inc_ref(s.first);
m_manager.inc_ref(s.second);
}
inline expr * substitution_tree::get_reg_value(unsigned ridx) {
return m_registers.get(ridx, 0);
}
inline void substitution_tree::set_reg_value(unsigned ridx, expr * e) {
m_registers.setx(ridx, e, 0);
}
inline void substitution_tree::erase_reg_from_todo(unsigned ridx) {
SASSERT(m_registers[ridx]);
m_registers[ridx] = 0;
SASSERT(m_todo.contains(ridx));
m_todo.erase(ridx);
}
/**
\brief Linearize the expressions in the registers stored in m_todo.
Store the result in \c result.
Example:
m_todo = { 3, 4 }
m_registers[3] = (f (g a))
m_registers[4] = b
next_regs are 5 6 7
result:
#3 -> (f #5); #4 -> b; #5 -> (g #6); #6 -> a
*/
void substitution_tree::linearize(svector<subst> & result) {
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < m_todo.size(); i++) {
unsigned ireg_idx = m_todo[i];
expr * n = get_reg_value(ireg_idx);
var * ireg = m_manager.mk_var(ireg_idx, m_manager.get_sort(n));
if (is_var(n))
push(result, subst(ireg, n));
else {
SASSERT(is_app(n));
app * new_app;
unsigned num = to_app(n)->get_num_args();
if (num == 0)
new_app = to_app(n);
else {
for (unsigned j = 0; j < num; j++) {
unsigned oreg = next_reg();
set_reg_value(oreg, to_app(n)->get_arg(j));
m_todo.push_back(oreg);
sort * s = m_manager.get_sort(get_reg_value(oreg));
new_args.push_back(m_manager.mk_var(oreg, s));
}
new_app = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr());
new_args.reset();
}
push(result, subst(ireg, new_app));
}
}
}
/**
\brief Process the pair in := (f t_1 ... t_n) and out := (f r_1 ... r_n),
where r_i's are variables (register ids), and t_i's are arbitrary expressions.
The r_i's are added to the m_todo list, and m_registers[r_i] is assigned to t_i.
If save_set_registers == true, then r_i's are stored in m_to_reset.
*/
void substitution_tree::process_args(app * in, app * out) {
CTRACE("subst_tree_bug", in->get_num_args() != out->get_num_args(), tout << mk_ismt2_pp(in, m_manager) << "\n"
<< mk_ismt2_pp(out, m_manager) << "\n";);
unsigned num = out->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * in_arg = in->get_arg(i);
expr * out_arg = out->get_arg(i);
SASSERT(is_var(out_arg));
unsigned oreg = to_var(out_arg)->get_idx();
set_reg_value(oreg, in_arg);
m_todo.push_back(oreg);
}
}
/**
\brief Reset registers in m_todo at [old_size, m_todo.size())
*/
void substitution_tree::reset_registers(unsigned old_size) {
SASSERT(m_todo.size() >= old_size);
unsigned_vector::iterator it2 = m_todo.begin() + old_size;
unsigned_vector::iterator end2 = m_todo.end();
for (; it2 != end2; ++it2)
m_registers[*it2] = 0;
m_todo.shrink(old_size);
}
/**
\brief Return a measure on how compatible sv and the expressions to be processed are.
*/
unsigned substitution_tree::get_compatibility_measure(svector<subst> const & sv) {
unsigned old_size = m_todo.size();
unsigned measure = 0;
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
if (is_var(out)) {
if (out == in)
measure += 1;
}
else {
SASSERT(is_app(out));
if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) {
measure += 2;
process_args(to_app(in), to_app(out));
}
}
}
reset_registers(old_size);
return measure;
}
/**
\brief Find the child of r that is most compatible with the expressions stored
in the registers in m_todo.
Return 0 if none of the children has any compatible substitution entry.
*/
substitution_tree::node * substitution_tree::find_best_child(node * r) {
SASSERT(!r->m_leaf);
#ifdef Z3DEBUG
unsigned todo_size = m_todo.size();
#endif
node * best_child = 0;
unsigned max_measure = 0;
node * curr_child = r->m_first_child;
while (curr_child) {
unsigned measure = get_compatibility_measure(curr_child->m_subst);
if (measure > max_measure) {
best_child = curr_child;
max_measure = measure;
}
curr_child = curr_child->m_next_sibling;
}
SASSERT(todo_size == m_todo.size());
return best_child;
}
/**
\brief Reset datastructures used to insert/erase elements from the substitution tree.
*/
void substitution_tree::reset_compiler() {
m_todo.reset();
m_used_regs.reset();
m_next_reg = 1; // register 0 is reserved for input.
DEBUG_CODE({
ptr_vector<expr>::iterator it = m_registers.begin();
ptr_vector<expr>::iterator end = m_registers.end();
for (; it != end; ++it) {
SASSERT(*it == 0);
}
});
}
/**
\brief Create a node with the linearization for all registers in todo.
Attach new_expr to it.
*/
substitution_tree::node * substitution_tree::mk_node_for(expr * new_expr) {
node * n = alloc(node, true);
linearize(n->m_subst);
n->m_expr = new_expr;
m_manager.inc_ref(new_expr);
return n;
}
/**
\brief Mark register ridx as used.
*/
void substitution_tree::mark_used_reg(unsigned ridx) {
if (ridx >= m_used_regs.size())
m_used_regs.resize(ridx+1);
m_used_regs.set(ridx);
}
/**
\brief Mark (m_used_regs) all registers used in \c sv.
*/
void substitution_tree::mark_used_regs(svector<subst> const & sv) {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
mark_used_reg(s.first->get_idx());
if (is_app(s.second)) {
unsigned num_args = to_app(s.second)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(s.second)->get_arg(i);
SASSERT(is_var(arg));
mark_used_reg(to_var(arg)->get_idx());
}
}
}
}
/**
\brief Insert a new expression in the substitution tree.
*/
void substitution_tree::insert(expr * new_expr) {
if (is_app(new_expr)) {
insert(to_app(new_expr));
}
else {
SASSERT(is_var(new_expr));
sort * s = to_var(new_expr)->get_sort();
unsigned id = s->get_decl_id();
if (id >= m_vars.size())
m_vars.resize(id+1, 0);
if (m_vars[id] == 0)
m_vars[id] = alloc(var_ref_vector, m_manager);
var_ref_vector * v = m_vars[id];
if (!v->contains(to_var(new_expr)))
v->push_back(to_var(new_expr));
}
}
/**
\brief Insert a new application in the substitution tree.
*/
void substitution_tree::insert(app * new_expr) {
reset_compiler();
set_reg_value(0, new_expr);
m_todo.push_back(0);
func_decl * d = new_expr->get_decl();
unsigned id = d->get_decl_id();
if (id >= m_roots.size())
m_roots.resize(id+1, 0);
if (!m_roots[id]) {
// there is no tree for the function symbol heading new_expr
m_roots[id] = mk_node_for(new_expr);
reset_registers(0);
m_size++;
return;
}
node * r = m_roots[id];
while (true) {
m_compatible.reset();
m_incompatible.reset();
svector<subst> & sv = r->m_subst;
// separate sv in the set of compatible & incompatible instructions
svector<subst>::iterator it = sv.begin();
svector<subst>::iterator end = sv.end();
for (; it != end; ++it) {
subst & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
SASSERT(is_var(out) || is_app(out));
if (is_var(out)) {
if (out == in) {
erase_reg_from_todo(ireg);
m_compatible.push_back(s);
}
else {
m_incompatible.push_back(s);
}
}
else {
if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) {
erase_reg_from_todo(ireg);
m_compatible.push_back(s);
process_args(to_app(in), to_app(out));
}
else {
m_incompatible.push_back(s);
}
}
}
// process m_compatible & m_incompatible
if (m_incompatible.empty()) {
if (m_todo.empty()) {
// nothing else to process
// new_expr is already in the substitution tree
SASSERT(r->m_leaf && r->m_expr == new_expr);
reset_registers(0);
return;
}
else {
mark_used_regs(r->m_subst);
node * best_child = find_best_child(r);
if (best_child == 0) {
// there is no compatible child
node * n = mk_node_for(new_expr);
n->m_next_sibling = r->m_first_child;
r->m_first_child = n;
reset_registers(0);
m_size++;
return;
}
else {
// continue with best_child
r = best_child;
}
}
}
else {
SASSERT(!m_compatible.empty());
SASSERT(!m_incompatible.empty());
mark_used_regs(m_compatible);
r->m_subst.swap(m_compatible);
node * n = mk_node_for(new_expr);
node * incomp = alloc(node, r->m_leaf);
incomp->m_subst.swap(m_incompatible);
if (r->m_leaf) {
incomp->m_expr = r->m_expr;
r->m_leaf = false;
}
else
incomp->m_first_child = r->m_first_child;
incomp->m_next_sibling = n;
SASSERT(!r->m_leaf);
r->m_first_child = incomp;
reset_registers(0);
m_size++;
return;
}
}
}
/**
\brief Return true if sv is fully compatible with the expressions in the registers in m_todo.
*/
bool substitution_tree::is_fully_compatible(svector<subst> const & sv) {
unsigned old_size = m_todo.size();
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
if (is_var(out)) {
if (out != in) {
reset_registers(old_size);
return false;
}
}
else {
if (!in || !is_app(in) || to_app(in)->get_decl() != to_app(out)->get_decl()) {
reset_registers(old_size);
return false;
}
process_args(to_app(in), to_app(out));
}
}
reset_registers(old_size);
return true;
}
/**
\brief Return a child of r that is fully compatible with the expressions in the registers in m_todo.
*/
bool substitution_tree::find_fully_compatible_child(node * r, node * & prev, node * & child) {
SASSERT(!r->m_leaf);
prev = 0;
child = r->m_first_child;
while (child) {
if (is_fully_compatible(child->m_subst))
return true;
prev = child;
child = child->m_next_sibling;
}
return false;
}
inline bool substitution_tree::at_least_3_children(node * r) {
return !r->m_leaf && r->m_first_child->m_next_sibling && r->m_first_child->m_next_sibling->m_next_sibling;
}
/**
\brief Remove expression from the substitution tree.
Do nothing, if n is not in the tree.
*/
void substitution_tree::erase(expr * e) {
if (is_app(e))
erase(to_app(e));
else {
SASSERT(is_var(e));
sort * s = to_var(e)->get_sort();
unsigned id = s->get_decl_id();
if (id >= m_vars.size() || m_vars[id] == 0)
return;
var_ref_vector * v = m_vars[id];
v->erase(to_var(e));
}
}
/**
\brief Remove application from the substitution tree.
Do nothing, if n is not in the tree.
*/
void substitution_tree::erase(app * e) {
func_decl * d = e->get_decl();
unsigned id = d->get_decl_id();
if (id >= m_roots.size() || !m_roots[id])
return;
reset_compiler();
set_reg_value(0, e);
m_todo.push_back(0);
node * r = m_roots[id];
node * parent = 0;
node * prev = 0;
while (true) {
svector<subst> & sv = r->m_subst;
svector<subst>::iterator it = sv.begin();
svector<subst>::iterator end = sv.end();
for (; it != end; ++it) {
subst & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
SASSERT(is_var(out) || is_app(out));
if (is_var(out)) {
if (out != in) {
reset_registers(0);
return; // node is not in the substitution tree
}
erase_reg_from_todo(ireg);
}
else {
if (!in || !is_app(in) || to_app(out)->get_decl() != to_app(in)->get_decl()) {
reset_registers(0);
return; // node is not in the substitution tree
}
erase_reg_from_todo(ireg);
process_args(to_app(in), to_app(out));
}
}
if (m_todo.empty()) {
reset_registers(0);
SASSERT(r->m_expr == e);
if (parent == 0) {
delete_node(r);
m_roots[id] = 0;
}
else if (at_least_3_children(parent)) {
if (prev == 0)
parent->m_first_child = r->m_next_sibling;
else
prev->m_next_sibling = r->m_next_sibling;
delete_node(r);
}
else {
SASSERT(parent->m_first_child && parent->m_first_child->m_next_sibling && !parent->m_first_child->m_next_sibling->m_next_sibling);
node * other_child = prev ? prev : r->m_next_sibling;
SASSERT(other_child);
parent->m_subst.append(other_child->m_subst);
parent->m_leaf = other_child->m_leaf;
if (other_child->m_leaf)
parent->m_expr = other_child->m_expr;
else
parent->m_first_child = other_child->m_first_child;
delete_node(r);
dealloc(other_child); // Remark: I didn't use delete_node since its resources were sent to parent.
}
m_size --;
return;
}
else {
parent = r;
if (!find_fully_compatible_child(r, prev, r)) {
// node is not in the substitution tree
reset_registers(0);
return;
}
// continue with fully compatible child
}
}
}
void substitution_tree::delete_node(node * n) {
ptr_buffer<node> todo;
SASSERT(todo.empty());
todo.push_back(n);
while (!todo.empty()) {
node * n = todo.back();
todo.pop_back();
svector<subst>::iterator it2 = n->m_subst.begin();
svector<subst>::iterator end2 = n->m_subst.end();
for (; it2 != end2; ++it2) {
m_manager.dec_ref(it2->first);
m_manager.dec_ref(it2->second);
}
if (n->m_leaf)
m_manager.dec_ref(n->m_expr);
else {
node * c = n->m_first_child;
while (c) {
todo.push_back(c);
c = c->m_next_sibling;
}
}
dealloc(n);
}
}
void substitution_tree::reset() {
ptr_vector<node>::iterator it = m_roots.begin();
ptr_vector<node>::iterator end = m_roots.end();
for (; it != end; ++it) {
if (*it)
delete_node(*it);
}
m_roots.reset();
std::for_each(m_vars.begin(), m_vars.end(), delete_proc<var_ref_vector>());
m_vars.reset();
m_size = 0;
}
void substitution_tree::display(std::ostream & out, subst const & s) const {
out << "r!" << s.first->get_idx() << " -> ";
if (is_app(s.second)) {
unsigned num = to_app(s.second)->get_num_args();
if (num == 0)
out << to_app(s.second)->get_decl()->get_name();
else {
out << "(" << to_app(s.second)->get_decl()->get_name();
for (unsigned i = 0; i < num; i++)
out << " r!" << to_var(to_app(s.second)->get_arg(i))->get_idx();
out << ")";
}
}
else {
out << mk_pp(s.second, m_manager);
}
}
void substitution_tree::display(std::ostream & out, svector<subst> const & sv) const {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (bool first = true; it != end; ++it, first = false) {
subst const & s = *it;
if (!first)
out << "; ";
display(out, s);
}
}
void substitution_tree::display(std::ostream & out, node * n, unsigned delta) const {
for (unsigned i = 0; i < delta; i++)
out << " ";
display(out, n->m_subst);
if (n->m_leaf) {
pp_params p;
p.m_pp_single_line = true;
out << " ==> ";
ast_pp(out, n->m_expr, m_manager, p);
out << "\n";
}
else {
out << "\n";
node * c = n->m_first_child;
while (c) {
display(out, c, delta+1);
c = c->m_next_sibling;
}
}
}
bool substitution_tree::backtrack() {
while (!m_bstack.empty()) {
TRACE("st", tout << "backtracking...\n";);
m_subst->pop_scope();
node * n = m_bstack.back();
if (n->m_next_sibling) {
m_bstack.back() = n->m_next_sibling;
return true;
}
m_bstack.pop_back();
}
return false;
}
inline expr_offset substitution_tree::find(expr_offset p) {
TRACE("substitution_tree_bug", tout << "find...\n";);
while (is_var(p.get_expr())) {
TRACE("substitution_tree_bug", tout << mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";);
if (!m_subst->find(to_var(p.get_expr()), p.get_offset(), p))
return p;
}
return p;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::bind_var(var * v, unsigned offset, expr_offset const & p) {
TRACE("st", tout << "bind_var: " << mk_pp(v, m_manager) << " " << offset << "\n" <<
mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";);
if (Mode == STV_INST && offset == m_st_offset) {
SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset);
if (is_var(p.get_expr()) && p.get_offset() == m_in_offset) {
m_subst->insert(p, expr_offset(v, offset));
return true;
}
return false;
}
if (Mode == STV_GEN && offset == m_in_offset) {
SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset);
if (is_var(p.get_expr()) && p.get_offset() == m_st_offset) {
m_subst->insert(p, expr_offset(v, offset));
return true;
}
return false;
}
m_subst->insert(v, offset, p);
TRACE("st_bug", tout << "substitution updated\n"; m_subst->display(tout););
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::unify_match(expr_offset p1, expr_offset p2) {
svector<entry> & todo = m_visit_todo;
todo.reset();
todo.push_back(entry(p1, p2));
while (!todo.empty()) {
entry const & e = todo.back();
p1 = find(e.first);
p2 = find(e.second);
todo.pop_back();
if (p1 != p2) {
expr * n1 = p1.get_expr();
expr * n2 = p2.get_expr();
SASSERT(!is_quantifier(n1));
SASSERT(!is_quantifier(n2));
bool v1 = is_var(n1);
bool v2 = is_var(n2);
TRACE("st",
tout << "n1: " << mk_pp(n1, m_manager) << " " << p1.get_offset() << "\n";
tout << "n2: " << mk_pp(n2, m_manager) << " " << p2.get_offset() << "\n";);
if (v1 && v2) {
if (p2.get_offset() == m_reg_offset)
std::swap(p1, p2);
if (!bind_var<Mode>(to_var(p1.get_expr()), p1.get_offset(), p2))
return false;
}
else if (v1) {
if (!bind_var<Mode>(to_var(n1), p1.get_offset(), p2))
return false;
}
else if (v2) {
if (!bind_var<Mode>(to_var(n2), p2.get_offset(), p1))
return false;
}
else {
app * a1 = to_app(n1);
app * a2 = to_app(n2);
unsigned off1 = p1.get_offset();
unsigned off2 = p2.get_offset();
if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args())
return false;
unsigned j = a1->get_num_args();
while (j > 0) {
--j;
entry new_e(expr_offset(a1->get_arg(j), off1),
expr_offset(a2->get_arg(j), off2));
todo.push_back(new_e);
}
}
}
}
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit_vars(expr * e, st_visitor & st) {
if (m_vars.empty())
return true; // continue
sort * s = m_manager.get_sort(e);
unsigned s_id = s->get_decl_id();
if (s_id < m_vars.size()) {
var_ref_vector * v = m_vars[s_id];
if (v && !v->empty()) {
unsigned sz = v->size();
for (unsigned i = 0; i < sz; i++) {
var * curr = v->get(i);
m_subst->push_scope();
if (unify_match<Mode>(expr_offset(curr, m_st_offset), expr_offset(e, m_in_offset))) {
if (Mode != STV_UNIF || m_subst->acyclic()) {
if (!st(curr)) {
m_subst->pop_scope();
return false; // stop
}
}
}
m_subst->pop_scope();
}
}
}
return true; // continue
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit(svector<subst> const & sv) {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
TRACE("st", tout << "processing subst:\n"; display(tout, s); tout << "\n";);
var * rin = s.first;
expr * out = s.second;
expr_offset p1(rin, m_reg_offset);
expr_offset p2(out, is_var(out) ? m_st_offset : m_reg_offset);
if (!unify_match<Mode>(p1, p2))
return false;
}
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit(expr * e, st_visitor & st, node * r) {
m_bstack.reset();
m_bstack.push_back(r);
m_subst->push_scope();
m_subst->insert(static_cast<unsigned>(0), m_reg_offset, expr_offset(e, m_in_offset));
while (true) {
node * n = m_bstack.back();
TRACE("st", tout << "push scope...\n";);
m_subst->push_scope();
TRACE("st", tout << "processing node:\n"; display(tout, n->m_subst); tout << "\n";);
if (visit<Mode>(n->m_subst)) {
if (n->m_leaf) {
// if searching for unifiers and the substitution is cyclic, then backtrack.
if (Mode == STV_UNIF && !m_subst->acyclic()) {
if (!backtrack())
break;
}
else {
TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";);
if (!st(n->m_expr))
return false;
if (!backtrack())
break;
}
}
else {
m_bstack.push_back(n->m_first_child);
}
}
else if (!backtrack())
break;
}
while (!m_bstack.empty()) {
m_subst->pop_scope();
m_bstack.pop_back();
}
m_subst->pop_scope();
return true;
}
template<substitution_tree::st_visit_mode Mode>
void substitution_tree::visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
m_in_offset = in_offset;
m_st_offset = st_offset;
m_reg_offset = reg_offset;
m_subst = &(st.get_substitution());
m_subst->reserve_vars(get_approx_num_regs());
if (visit_vars<Mode>(e, st)) {
if (is_app(e)) {
func_decl * d = to_app(e)->get_decl();
unsigned id = d->get_decl_id();
node * r = m_roots.get(id, 0);
if (r)
visit<Mode>(e, st, r);
}
else {
SASSERT(is_var(e));
ptr_vector<node>::iterator it = m_roots.begin();
ptr_vector<node>::iterator end = m_roots.end();
for (; it != end; ++it) {
node * r = *it;
if (r != 0) {
var * v = r->m_subst[0].first;
if (v->get_sort() == to_var(e)->get_sort())
if (!visit<Mode>(e, st, r))
break;
}
}
}
}
}
void substitution_tree::unify(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_UNIF>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::inst(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_INST>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::gen(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_GEN>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::display(std::ostream & out) const {
out << "substitution tree:\n";
ptr_vector<node>::const_iterator it = m_roots.begin();
ptr_vector<node>::const_iterator end = m_roots.end();
for (; it != end; ++it)
if (*it)
display(out, *it, 0);
bool found_var = false;
ptr_vector<var_ref_vector>::const_iterator it2 = m_vars.begin();
ptr_vector<var_ref_vector>::const_iterator end2 = m_vars.end();
for (; it2 != end2; ++it2) {
var_ref_vector * v = *it2;
if (v == 0)
continue; // m_vars may contain null pointers. See substitution_tree::insert.
unsigned num = v->size();
for (unsigned i = 0; i < num; i++) {
if (!found_var) {
found_var = true;
out << "vars: ";
}
out << mk_pp(v->get(i), m_manager) << " ";
}
}
if (found_var)
out << "\n";
}
substitution_tree::substitution_tree(ast_manager & m):
m_manager(m),
m_max_reg(0),
m_size(0) {
}
substitution_tree::~substitution_tree() {
reset();
}

View file

@ -0,0 +1,148 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution_tree.h
Abstract:
Substitution Trees
Author:
Leonardo de Moura (leonardo) 2008-02-03.
Revision History:
--*/
#ifndef _SUBSTITUTION_TREE_H_
#define _SUBSTITUTION_TREE_H_
#include"ast.h"
#include"substitution.h"
/**
\brief Substitution tree visitor.
*/
class st_visitor {
protected:
substitution & m_subst;
public:
st_visitor(substitution & s):m_subst(s) {}
virtual ~st_visitor() {}
substitution & get_substitution() { return m_subst; }
virtual bool operator()(expr * e) { return true; }
};
/**
\brief Substitution tree term index.
*/
class substitution_tree {
typedef std::pair<var *, expr *> subst;
struct node {
bool m_leaf;
svector<subst> m_subst;
node * m_next_sibling;
union {
node * m_first_child;
expr * m_expr;
};
node(bool leaf):m_leaf(leaf), m_next_sibling(0), m_first_child(0) {}
};
ast_manager & m_manager;
ptr_vector<node> m_roots;
unsigned m_max_reg;
ptr_vector<expr> m_registers;
unsigned m_size;
ptr_vector<var_ref_vector> m_vars; // mapping from decl_id to var_ref_vector
// Compilation time fields
unsigned m_next_reg;
bit_vector m_used_regs;
unsigned_vector m_todo;
svector<subst> m_compatible;
svector<subst> m_incompatible;
// Execution time fields
substitution * m_subst;
ptr_vector<node> m_bstack;
unsigned m_in_offset;
unsigned m_st_offset;
unsigned m_reg_offset;
typedef std::pair<expr_offset, expr_offset> entry;
svector<entry> m_visit_todo;
unsigned next_reg();
void push(svector<subst> & sv, subst const & s);
expr * get_reg_value(unsigned ridx);
void set_reg_value(unsigned ridx, expr * e);
void erase_reg_from_todo(unsigned ridx);
void linearize(svector<subst> & result);
void process_args(app * in, app * out);
void reset_registers(unsigned old_size);
unsigned get_compatibility_measure(svector<subst> const & sv);
node * find_best_child(node * r);
void reset_compiler();
node * mk_node_for(expr * new_expr);
void mark_used_reg(unsigned ridx);
void mark_used_regs(svector<subst> const & sv);
bool is_fully_compatible(svector<subst> const & sv);
bool find_fully_compatible_child(node * r, node * & prev, node * & child);
static bool at_least_3_children(node * r);
void delete_node(node * n);
void display(std::ostream & out, subst const & s) const;
void display(std::ostream & out, svector<subst> const & sv) const;
void display(std::ostream & out, node * n, unsigned delta) const;
enum st_visit_mode {
STV_UNIF,
STV_INST,
STV_GEN
};
expr_offset find(expr_offset p);
bool backtrack();
template<st_visit_mode Mode>
bool bind_var(var * v, unsigned offset, expr_offset const & p);
template<st_visit_mode Mode>
bool unify_match(expr_offset p1, expr_offset p2);
template<substitution_tree::st_visit_mode Mode>
bool visit_vars(expr * e, st_visitor & st);
template<st_visit_mode Mode>
bool visit(svector<subst> const & s);
template<st_visit_mode Mode>
bool visit(expr * e, st_visitor & st, node * r);
template<st_visit_mode Mode>
void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset);
public:
substitution_tree(ast_manager & m);
~substitution_tree();
void insert(app * n);
void insert(expr * n);
void erase(app * n);
void erase(expr * n);
void reset();
bool empty() const { return m_size == 0; }
void unify(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
void inst(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
void gen(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
unsigned get_approx_num_regs() const { return m_max_reg + 1; }
void display(std::ostream & out) const;
};
#endif /* _SUBSTITUTION_TREE_H_ */

View file

@ -0,0 +1,183 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
unifier.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#include"unifier.h"
#include"ast_pp.h"
void unifier::reset(unsigned num_offsets) {
m_todo.reset();
m_find.reset();
m_size.reset();
}
/**
\brief Find with path compression.
*/
expr_offset unifier::find(expr_offset p) {
buffer<expr_offset> path;
expr_offset next;
while (m_find.find(p, next)) {
path.push_back(p);
p = next;
}
buffer<expr_offset>::iterator it = path.begin();
buffer<expr_offset>::iterator end = path.end();
for (; it != end; ++it) {
expr_offset & prev = *it;
m_find.insert(prev, p);
}
return p;
}
void unifier::save_var(expr_offset const & p, expr_offset const & t) {
expr * n = p.get_expr();
if (is_var(n)) {
unsigned off = p.get_offset();
m_subst->insert(to_var(n)->get_idx(), off, t);
}
}
/**
\brief Merge the equivalence classes of n1 and n2. n2 will be the
root of the resultant equivalence class.
*/
void unifier::union1(expr_offset const & n1, expr_offset const & n2) {
DEBUG_CODE({
expr_offset f;
SASSERT(!m_find.find(n1, f));
SASSERT(!m_find.find(n2, f));
});
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(n1, sz1);
m_size.find(n2, sz2);
m_find.insert(n1, n2);
m_size.insert(n2, sz1 + sz2);
save_var(n1, n2);
}
/**
\brief Merge the equivalence classes of n1 and n2. The root of the
resultant equivalence class is the one with more elements.
*/
void unifier::union2(expr_offset n1, expr_offset n2) {
DEBUG_CODE({
expr_offset f;
SASSERT(!m_find.find(n1, f));
SASSERT(!m_find.find(n2, f));
});
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(n1, sz1);
m_size.find(n2, sz2);
if (sz1 > sz2)
std::swap(n1, n2);
m_find.insert(n1, n2);
m_size.insert(n2, sz1 + sz2);
save_var(n1, n2);
}
bool unifier::unify_core(expr_offset p1, expr_offset p2) {
entry e(p1, p2);
m_todo.push_back(e);
while (!m_todo.empty()) {
entry const & e = m_todo.back();
p1 = find(e.first);
p2 = find(e.second);
m_todo.pop_back();
if (p1 != p2) {
expr * n1 = p1.get_expr();
expr * n2 = p2.get_expr();
SASSERT(!is_quantifier(n1));
SASSERT(!is_quantifier(n2));
bool v1 = is_var(n1);
bool v2 = is_var(n2);
if (v1 && v2) {
union2(p1, p2);
}
else if (v1) {
union1(p1, p2);
}
else if (v2) {
union1(p2, p1);
}
else {
app * a1 = to_app(n1);
app * a2 = to_app(n2);
unsigned off1 = p1.get_offset();
unsigned off2 = p2.get_offset();
if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args())
return false;
union2(p1, p2);
unsigned j = a1->get_num_args();
while (j > 0) {
--j;
entry new_e(expr_offset(a1->get_arg(j), off1),
expr_offset(a2->get_arg(j), off2));
m_todo.push_back(new_e);
}
}
}
}
return true;
}
bool unifier::operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets) {
SASSERT(num_exprs > 0);
unsigned num_offsets = use_offsets ? num_exprs : 1;
reset(num_offsets);
m_subst = &s;
#if 1
TRACE("unifier", for (unsigned i = 0; i < num_exprs; ++i) tout << mk_pp(es[i], m_manager) << "\n";);
for (unsigned i = s.get_num_bindings(); i > 0; ) {
--i;
std::pair<unsigned,unsigned> bound;
expr_offset root, child;
s.get_binding(i, bound, root);
TRACE("unifier", tout << bound.first << " |-> " << mk_pp(root.get_expr(), m_manager) << "\n";);
if (is_var(root.get_expr())) {
var* v = m_manager.mk_var(bound.first,to_var(root.get_expr())->get_sort());
child = expr_offset(v, bound.second);
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(child, sz1);
m_size.find(root, sz2);
m_find.insert(child, root);
m_size.insert(root, sz1 + sz2);
}
}
#endif
for (unsigned i = 0; i < num_exprs - 1; i++) {
if (!unify_core(expr_offset(es[i], use_offsets ? i : 0),
expr_offset(es[i+1], use_offsets ? i + 1 : 0))) {
m_last_call_succeeded = false;
return m_last_call_succeeded;
}
}
m_last_call_succeeded = m_subst->acyclic();
return m_last_call_succeeded;
}
bool unifier::operator()(expr * e1, expr * e2, substitution & s, bool use_offsets) {
expr * es[2] = { e1, e2 };
return operator()(2, es, s, use_offsets);
}

Some files were not shown because too many files have changed in this diff Show more