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:
parent
9e299b88c4
commit
0a4446ae26
255 changed files with 17 additions and 17 deletions
208
src/ast/array_property/array_property_expander.cpp
Normal file
208
src/ast/array_property/array_property_expander.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
33
src/ast/array_property/array_property_expander.h
Normal file
33
src/ast/array_property/array_property_expander.h
Normal 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_ */
|
||||
|
||||
103
src/ast/array_property/array_property_recognizer.cpp
Normal file
103
src/ast/array_property/array_property_recognizer.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
33
src/ast/array_property/array_property_recognizer.h
Normal file
33
src/ast/array_property/array_property_recognizer.h
Normal 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_ */
|
||||
|
||||
223
src/ast/macros/macro_finder.cpp
Normal file
223
src/ast/macros/macro_finder.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
55
src/ast/macros/macro_finder.h
Normal file
55
src/ast/macros/macro_finder.h
Normal 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_ */
|
||||
|
||||
319
src/ast/macros/macro_manager.cpp
Normal file
319
src/ast/macros/macro_manager.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
99
src/ast/macros/macro_manager.h
Normal file
99
src/ast/macros/macro_manager.h
Normal 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_ */
|
||||
|
||||
184
src/ast/macros/macro_substitution.cpp
Normal file
184
src/ast/macros/macro_substitution.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
59
src/ast/macros/macro_substitution.h
Normal file
59
src/ast/macros/macro_substitution.h
Normal 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
|
||||
928
src/ast/macros/macro_util.cpp
Normal file
928
src/ast/macros/macro_util.cpp
Normal 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
144
src/ast/macros/macro_util.h
Normal 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
|
||||
317
src/ast/macros/quasi_macros.cpp
Normal file
317
src/ast/macros/quasi_macros.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
69
src/ast/macros/quasi_macros.h
Normal file
69
src/ast/macros/quasi_macros.h
Normal 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
|
||||
741
src/ast/pattern/pattern_inference.cpp
Normal file
741
src/ast/pattern/pattern_inference.cpp
Normal 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
|
||||
|
||||
254
src/ast/pattern/pattern_inference.h
Normal file
254
src/ast/pattern/pattern_inference.h
Normal 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_ */
|
||||
|
||||
1335
src/ast/proof_checker/proof_checker.cpp
Normal file
1335
src/ast/proof_checker/proof_checker.cpp
Normal file
File diff suppressed because it is too large
Load diff
116
src/ast/proof_checker/proof_checker.h
Normal file
116
src/ast/proof_checker/proof_checker.h
Normal 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
|
||||
1529
src/ast/rewriter/arith_rewriter.cpp
Normal file
1529
src/ast/rewriter/arith_rewriter.cpp
Normal file
File diff suppressed because it is too large
Load diff
183
src/ast/rewriter/arith_rewriter.h
Normal file
183
src/ast/rewriter/arith_rewriter.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
arith_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for arithmetic
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-10
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _ARITH_REWRITER_H_
|
||||
#define _ARITH_REWRITER_H_
|
||||
|
||||
#include"poly_rewriter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
|
||||
class arith_rewriter_core {
|
||||
protected:
|
||||
typedef rational numeral;
|
||||
arith_util m_util;
|
||||
bool m_expand_power;
|
||||
bool m_mul2power;
|
||||
bool m_expand_tan;
|
||||
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
|
||||
bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
|
||||
bool is_numeral(expr * n, numeral & r) const { return m_util.is_numeral(n, r); }
|
||||
bool is_zero(expr * n) const { return m_util.is_zero(n); }
|
||||
bool is_minus_one(expr * n) const { return m_util.is_minus_one(n); }
|
||||
void normalize(numeral & c, sort * s) {}
|
||||
app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); }
|
||||
decl_kind add_decl_kind() const { return OP_ADD; }
|
||||
decl_kind mul_decl_kind() const { return OP_MUL; }
|
||||
bool use_power() const { return m_mul2power && !m_expand_power; }
|
||||
decl_kind power_decl_kind() const { return OP_POWER; }
|
||||
public:
|
||||
arith_rewriter_core(ast_manager & m):m_util(m) {}
|
||||
};
|
||||
|
||||
class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
|
||||
bool m_arith_lhs;
|
||||
bool m_gcd_rounding;
|
||||
bool m_eq2ineq;
|
||||
bool m_elim_to_real;
|
||||
bool m_push_to_real;
|
||||
bool m_anum_simp;
|
||||
bool m_elim_rem;
|
||||
unsigned m_max_degree;
|
||||
|
||||
void get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts);
|
||||
enum const_treatment { CT_FLOOR, CT_CEIL, CT_FALSE };
|
||||
bool div_polynomial(expr * t, numeral const & g, const_treatment ct, expr_ref & result);
|
||||
enum op_kind { LE, GE, EQ };
|
||||
static op_kind inv(op_kind k) { return k == LE ? GE : (k == GE ? LE : EQ); }
|
||||
bool is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result);
|
||||
br_status mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result);
|
||||
|
||||
bool elim_to_real_var(expr * var, expr_ref & new_var);
|
||||
bool elim_to_real_mon(expr * monomial, expr_ref & new_monomial);
|
||||
bool elim_to_real_pol(expr * p, expr_ref & new_p);
|
||||
bool elim_to_real(expr * arg1, expr * arg2, expr_ref & new_arg1, expr_ref & new_arg2);
|
||||
|
||||
void updt_local_params(params_ref const & p);
|
||||
|
||||
bool is_anum_simp_target(unsigned num_args, expr * const * args);
|
||||
|
||||
br_status mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_div_irrat_irrat(expr * arg1, expr * arg2, expr_ref & result);
|
||||
|
||||
bool is_reduce_power_target(expr * arg, bool is_eq);
|
||||
expr * reduce_power(expr * arg, bool is_eq);
|
||||
br_status reduce_power(expr * arg1, expr * arg2, op_kind kind, expr_ref & result);
|
||||
|
||||
bool is_pi_multiple(expr * t, rational & k);
|
||||
bool is_pi_offset(expr * t, rational & k, expr * & m);
|
||||
bool is_2_pi_integer(expr * t);
|
||||
bool is_2_pi_integer_offset(expr * t, expr * & m);
|
||||
bool is_pi_integer(expr * t);
|
||||
bool is_pi_integer_offset(expr * t, expr * & m);
|
||||
expr * mk_sin_value(rational const & k);
|
||||
app * mk_sqrt(rational const & k);
|
||||
|
||||
public:
|
||||
arith_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
poly_rewriter<arith_rewriter_core>(m, p) {
|
||||
updt_local_params(p);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(f, num_args, args);
|
||||
}
|
||||
|
||||
br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_le_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ge_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_gt_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
|
||||
br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
void mk_eq(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_eq_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m_util.mk_le(arg1, arg2);
|
||||
}
|
||||
void mk_le(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_le_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m_util.mk_le(arg1, arg2);
|
||||
}
|
||||
void mk_lt(expr * arg1, expr * arg2, expr_ref & result) { mk_lt_core(arg1, arg2, result); }
|
||||
void mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_ge_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m_util.mk_ge(arg1, arg2);
|
||||
}
|
||||
void mk_gt(expr * arg1, expr * arg2, expr_ref & result) { mk_gt_core(arg1, arg2, result); }
|
||||
|
||||
br_status mk_div_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_idiv_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_mod_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_rem_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_power_core(expr* arg1, expr* arg2, expr_ref & result);
|
||||
void mk_div(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_div_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_DIV, arg1, arg2);
|
||||
}
|
||||
void mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_idiv_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_IDIV, arg1, arg2);
|
||||
}
|
||||
void mk_mod(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_mod_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_MOD, arg1, arg2);
|
||||
}
|
||||
void mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_rem_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_REM, arg1, arg2);
|
||||
}
|
||||
|
||||
br_status mk_to_int_core(expr * arg, expr_ref & result);
|
||||
br_status mk_to_real_core(expr * arg, expr_ref & result);
|
||||
void mk_to_int(expr * arg, expr_ref & result) {
|
||||
if (mk_to_int_core(arg, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_TO_INT, 1, &arg);
|
||||
}
|
||||
void mk_to_real(expr * arg, expr_ref & result) {
|
||||
if (mk_to_real_core(arg, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_TO_REAL, 1, &arg);
|
||||
}
|
||||
br_status mk_is_int(expr * arg, expr_ref & result);
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
br_status mk_sin_core(expr * arg, expr_ref & result);
|
||||
br_status mk_cos_core(expr * arg, expr_ref & result);
|
||||
br_status mk_tan_core(expr * arg, expr_ref & result);
|
||||
|
||||
br_status mk_asin_core(expr * arg, expr_ref & result);
|
||||
br_status mk_acos_core(expr * arg, expr_ref & result);
|
||||
br_status mk_atan_core(expr * arg, expr_ref & result);
|
||||
|
||||
br_status mk_sinh_core(expr * arg, expr_ref & result);
|
||||
br_status mk_cosh_core(expr * arg, expr_ref & result);
|
||||
br_status mk_tanh_core(expr * arg, expr_ref & result);
|
||||
};
|
||||
|
||||
#endif
|
||||
359
src/ast/rewriter/array_rewriter.cpp
Normal file
359
src/ast/rewriter/array_rewriter.cpp
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
array_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for Arrays.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-06
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"array_rewriter.h"
|
||||
#include"ast_lt.h"
|
||||
#include"ast_pp.h"
|
||||
|
||||
void array_rewriter::updt_params(params_ref const & p) {
|
||||
m_sort_store = p.get_bool(":sort-store", false);
|
||||
m_expand_select_store = p.get_bool(":expand-select-store", false);
|
||||
}
|
||||
|
||||
void array_rewriter::get_param_descrs(param_descrs & r) {
|
||||
r.insert(":expand-select-store", CPK_BOOL, "(default: false) replace a (select (store ...) ...) term by an if-then-else term.");
|
||||
r.insert(":sort-store", CPK_BOOL, "(default: false) sort nested stores when the indices are known to be different.");
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(f->get_family_id() == get_fid());
|
||||
TRACE("array_rewriter", tout << mk_pp(f, m()) << "\n";
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
tout << mk_pp(args[i], m()) << "\n";
|
||||
});
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_SELECT:
|
||||
return mk_select_core(num_args, args, result);
|
||||
case OP_STORE:
|
||||
return mk_store_core(num_args, args, result);
|
||||
case OP_ARRAY_MAP:
|
||||
SASSERT(f->get_num_parameters() == 1);
|
||||
SASSERT(f->get_parameter(0).is_ast());
|
||||
SASSERT(is_func_decl(f->get_parameter(0).get_ast()));
|
||||
return mk_map_core(to_func_decl(f->get_parameter(0).get_ast()), num_args, args, result);
|
||||
case OP_SET_UNION:
|
||||
return mk_set_union(num_args, args, result);
|
||||
case OP_SET_INTERSECT:
|
||||
return mk_set_intersect(num_args, args, result);
|
||||
case OP_SET_SUBSET:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_set_subset(args[0], args[1], result);
|
||||
case OP_SET_COMPLEMENT:
|
||||
SASSERT(num_args == 1);
|
||||
return mk_set_complement(args[0], result);
|
||||
case OP_SET_DIFFERENCE:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_set_difference(args[0], args[1], result);
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
// l_true -- all equal
|
||||
// l_false -- at least one disequal
|
||||
// l_undef -- don't know
|
||||
template<bool CHECK_DISEQ>
|
||||
lbool array_rewriter::compare_args(unsigned num_args, expr * const * args1, expr * const * args2) {
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
if (args1[i] == args2[i])
|
||||
continue;
|
||||
if (CHECK_DISEQ && m().are_distinct(args1[i], args2[i]))
|
||||
return l_false;
|
||||
return l_undef;
|
||||
}
|
||||
return l_true;
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_store_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args >= 3);
|
||||
|
||||
if (m_util.is_store(args[0])) {
|
||||
lbool r = m_sort_store ?
|
||||
compare_args<true>(num_args - 2, args + 1, to_app(args[0])->get_args() + 1) :
|
||||
compare_args<false>(num_args - 2, args + 1, to_app(args[0])->get_args() + 1);
|
||||
switch (r) {
|
||||
case l_true: {
|
||||
//
|
||||
// store(store(a,i,v),i,w) --> store(a,i,w)
|
||||
//
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(to_app(args[0])->get_arg(0));
|
||||
new_args.append(num_args-1, args+1);
|
||||
SASSERT(new_args.size() == num_args);
|
||||
result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
case l_false:
|
||||
SASSERT(m_sort_store);
|
||||
//
|
||||
// store(store(a,i,v),j,w) -> store(store(a,j,w),i,v)
|
||||
// if i, j are different, lt(i,j)
|
||||
//
|
||||
if (lex_lt(num_args-2, args+1, to_app(args[0])->get_args() + 1)) {
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(to_app(args[0])->get_arg(0));
|
||||
new_args.append(num_args-1, args+1);
|
||||
expr * nested_store = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr());
|
||||
new_args.reset();
|
||||
new_args.push_back(nested_store);
|
||||
new_args.append(num_args - 1, to_app(args[0])->get_args() + 1);
|
||||
result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
break;
|
||||
case l_undef:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// store(const(v),i,v) --> const(v)
|
||||
//
|
||||
if (m_util.is_const(args[0]) &&
|
||||
to_app(args[0])->get_arg(0) == args[num_args-1]) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
expr * v = args[num_args-1];
|
||||
|
||||
//
|
||||
// store(a, i, select(a, i)) --> a
|
||||
//
|
||||
if (m_util.is_select(v) &&
|
||||
compare_args<false>(num_args-1, args, to_app(v)->get_args())) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args >= 2);
|
||||
if (m_util.is_store(args[0])) {
|
||||
SASSERT(to_app(args[0])->get_num_args() == num_args+1);
|
||||
switch (compare_args<true>(num_args - 1, args+1, to_app(args[0])->get_args()+1)) {
|
||||
case l_true:
|
||||
// select(store(a, I, v), I) --> v
|
||||
result = to_app(args[0])->get_arg(num_args);
|
||||
return BR_DONE;
|
||||
case l_false: {
|
||||
// select(store(a, I, v), J) --> select(a, J) if I != J
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(to_app(args[0])->get_arg(0));
|
||||
new_args.append(num_args-1, args+1);
|
||||
result = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr());
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
default:
|
||||
if (m_expand_select_store) {
|
||||
// select(store(a, I, v), J) --> ite(I=J, v, select(a, J))
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(to_app(args[0])->get_arg(0));
|
||||
new_args.append(num_args-1, args+1);
|
||||
expr * sel_a_j = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr());
|
||||
expr * v = to_app(args[0])->get_arg(num_args);
|
||||
ptr_buffer<expr> eqs;
|
||||
unsigned num_indices = num_args-1;
|
||||
for (unsigned i = 0; i < num_indices; i++) {
|
||||
eqs.push_back(m().mk_eq(to_app(args[0])->get_arg(i+1), args[i+1]));
|
||||
}
|
||||
if (num_indices == 1) {
|
||||
result = m().mk_ite(eqs[0], v, sel_a_j);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
else {
|
||||
result = m().mk_ite(m().mk_and(eqs.size(), eqs.c_ptr()), v, sel_a_j);
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_util.is_const(args[0])) {
|
||||
// select(const(v), I) --> v
|
||||
result = to_app(args[0])->get_arg(0);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (m_util.is_as_array(args[0])) {
|
||||
// select(as-array[f], I) --> f(I)
|
||||
func_decl * f = m_util.get_as_array_func_decl(to_app(args[0]));
|
||||
result = m().mk_app(f, num_args - 1, args + 1);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args >= 0);
|
||||
bool is_store0 = m_util.is_store(args[0]);
|
||||
bool is_const0 = m_util.is_const(args[0]);
|
||||
if (num_args == 1) {
|
||||
//
|
||||
// map_f (store a j v) = (store (map_f a) j (f v))
|
||||
//
|
||||
if (is_store0) {
|
||||
app * store_expr = to_app(args[0]);
|
||||
unsigned num_args = store_expr->get_num_args();
|
||||
SASSERT(num_args >= 3);
|
||||
expr * a = store_expr->get_arg(0);
|
||||
expr * v = store_expr->get_arg(num_args-1);
|
||||
|
||||
ptr_buffer<expr> new_args;
|
||||
|
||||
new_args.push_back(m_util.mk_map(f, 1, &a)); // (map_f a)
|
||||
new_args.append(num_args - 2, store_expr->get_args() + 1); // j
|
||||
new_args.push_back(m().mk_app(f, v)); // (f v)
|
||||
|
||||
result = m().mk_app(get_fid(), OP_STORE, new_args.size(), new_args.c_ptr());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
//
|
||||
// map_f (const v) = (const (f v))
|
||||
//
|
||||
if (is_const0) {
|
||||
expr * fv = m().mk_app(f, to_app(args[0])->get_arg(0));
|
||||
result = m_util.mk_const_array(m().get_sort(args[0]), fv);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
SASSERT(num_args > 1);
|
||||
|
||||
if (is_store0) {
|
||||
unsigned num_indices = to_app(args[0])->get_num_args() - 2;
|
||||
unsigned i;
|
||||
for (i = 1; i < num_args; i++) {
|
||||
if (!m_util.is_store(args[i]))
|
||||
break;
|
||||
unsigned j;
|
||||
for (j = 1; j < num_indices+1; j++) {
|
||||
if (to_app(args[0])->get_arg(j) != to_app(args[i])->get_arg(j))
|
||||
break;
|
||||
}
|
||||
if (j < num_indices+1)
|
||||
break;
|
||||
}
|
||||
//
|
||||
// map_f (store a_1 j v_1) ... (store a_n j v_n) --> (store (map_f a_1 ... a_n) j (f v_1 ... v_n))
|
||||
//
|
||||
if (i == num_args) {
|
||||
ptr_buffer<expr> arrays;
|
||||
ptr_buffer<expr> values;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
arrays.push_back(to_app(args[i])->get_arg(0));
|
||||
values.push_back(to_app(args[i])->get_arg(num_indices+1));
|
||||
}
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(m_util.mk_map(f, arrays.size(), arrays.c_ptr()));
|
||||
new_args.append(num_indices, to_app(args[0])->get_args() + 1);
|
||||
new_args.push_back(m().mk_app(f, values.size(), values.c_ptr()));
|
||||
result = m().mk_app(get_fid(), OP_STORE, new_args.size(), new_args.c_ptr());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
if (is_const0) {
|
||||
unsigned i;
|
||||
for (i = 1; i < num_args; i++) {
|
||||
if (!m_util.is_const(args[i]))
|
||||
break;
|
||||
}
|
||||
if (i == num_args) {
|
||||
//
|
||||
// map_f (const v_1) ... (const v_n) = (const (f v_1 ... v_n))
|
||||
//
|
||||
ptr_buffer<expr> values;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
values.push_back(to_app(args[i])->get_arg(0));
|
||||
}
|
||||
|
||||
expr * fv = m().mk_app(f, values.size(), values.c_ptr());
|
||||
parameter p(m().get_sort(args[0]));
|
||||
result = m().mk_app(get_fid(), OP_CONST_ARRAY, 1, &p, 1, &fv);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
void array_rewriter::mk_store(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_store_core(num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_STORE, num_args, args);
|
||||
}
|
||||
|
||||
void array_rewriter::mk_select(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_select_core(num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_SELECT, num_args, args);
|
||||
}
|
||||
|
||||
void array_rewriter::mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_map_core(f, num_args, args, result) == BR_FAILED)
|
||||
result = m_util.mk_map(f, num_args, args);
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_set_union(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args > 0);
|
||||
if (num_args == 1) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
SASSERT(num_args >= 2);
|
||||
br_status r = unsigned2br_status(num_args - 2);
|
||||
result = m_util.mk_map(m().mk_or_decl(), num_args, args);
|
||||
return r;
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args > 0);
|
||||
if (num_args == 1) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
SASSERT(num_args >= 2);
|
||||
br_status r = unsigned2br_status(num_args - 2);
|
||||
result = m_util.mk_map(m().mk_and_decl(), num_args, args);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
br_status array_rewriter::mk_set_complement(expr * arg, expr_ref & result) {
|
||||
return mk_map_core(m().mk_not_decl(), 1, &arg, result);
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_set_difference(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
expr * args[2] = { arg1, m_util.mk_map(m().mk_not_decl(), 1, &arg2) };
|
||||
result = m_util.mk_map(m().mk_and_decl(), 2, args);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status array_rewriter::mk_set_subset(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
mk_set_difference(arg1, arg2, result);
|
||||
result = m().mk_eq(result.get(), m_util.mk_empty_set(m().get_sort(arg1)));
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
|
||||
65
src/ast/rewriter/array_rewriter.h
Normal file
65
src/ast/rewriter/array_rewriter.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
array_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for Arrays.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-06
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _ARRAY_REWRITER_H_
|
||||
#define _ARRAY_REWRITER_H_
|
||||
|
||||
#include"array_decl_plugin.h"
|
||||
#include"rewriter_types.h"
|
||||
#include"lbool.h"
|
||||
#include"params.h"
|
||||
|
||||
/**
|
||||
\brief Cheap rewrite rules for Arrays
|
||||
*/
|
||||
class array_rewriter {
|
||||
array_util m_util;
|
||||
bool m_sort_store;
|
||||
bool m_expand_select_store;
|
||||
template<bool CHECK_DISEQ>
|
||||
lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2);
|
||||
public:
|
||||
array_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
m_util(m) {
|
||||
updt_params(p);
|
||||
}
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
void mk_store(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_select(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
// The following methods never return BR_FAILED
|
||||
br_status mk_set_union(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_set_complement(expr * arg, expr_ref & result);
|
||||
br_status mk_set_difference(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_set_subset(expr * arg1, expr * arg2, expr_ref & result);
|
||||
};
|
||||
|
||||
#endif
|
||||
989
src/ast/rewriter/bool_rewriter.cpp
Normal file
989
src/ast/rewriter/bool_rewriter.cpp
Normal file
|
|
@ -0,0 +1,989 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bool_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewrites for Boolean operators.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-04
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"bool_rewriter.h"
|
||||
#include"rewriter_def.h"
|
||||
|
||||
void bool_rewriter::updt_params(params_ref const & p) {
|
||||
m_flat = p.get_bool(":flat", true);
|
||||
m_elim_and = p.get_bool(":elim-and", false);
|
||||
m_local_ctx = p.get_bool(":local-ctx", false);
|
||||
m_local_ctx_limit = p.get_uint(":local-ctx-limit", UINT_MAX);
|
||||
m_blast_distinct = p.get_bool(":blast-distinct", false);
|
||||
m_ite_extra_rules = p.get_bool(":ite-extra-rules", false);
|
||||
}
|
||||
|
||||
void bool_rewriter::get_param_descrs(param_descrs & r) {
|
||||
r.insert(":ite-extra-rules", CPK_BOOL, "(default: false) extra ite simplifications, these additional simplifications may reduce size locally but increase globally.");
|
||||
r.insert(":flat", CPK_BOOL, "(default: true) create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor.");
|
||||
r.insert(":elim-and", CPK_BOOL, "(default: false) conjunctions are rewritten using negation and disjunctions.");
|
||||
r.insert(":local-ctx", CPK_BOOL, "(default: false) perform local (i.e., cheap) context simplifications.");
|
||||
r.insert(":local-ctx-limit", CPK_UINT, "(default: inf) limit for applying local context simplifier.");
|
||||
r.insert(":blast-distinct", CPK_BOOL, "(default: false) expand a distinct predicate into a quadratic number of disequalities.");
|
||||
}
|
||||
|
||||
br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(f->get_family_id() == m().get_basic_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_EQ:
|
||||
case OP_IFF:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_eq_core(args[0], args[1], result);
|
||||
case OP_DISTINCT:
|
||||
return mk_distinct_core(num_args, args, result);
|
||||
case OP_AND:
|
||||
return mk_and_core(num_args, args, result);
|
||||
case OP_OR:
|
||||
return mk_or_core(num_args, args, result);
|
||||
case OP_NOT:
|
||||
SASSERT(num_args == 1);
|
||||
return mk_not_core(args[0], result);
|
||||
case OP_ITE:
|
||||
SASSERT(num_args == 3);
|
||||
return mk_ite_core(args[0], args[1], args[2], result);
|
||||
case OP_IMPLIES:
|
||||
SASSERT(num_args == 2);
|
||||
mk_implies(args[0], args[1], result);
|
||||
return BR_DONE;
|
||||
case OP_XOR:
|
||||
SASSERT(num_args == 2);
|
||||
mk_xor(args[0], args[1], result);
|
||||
return BR_DONE;
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
void bool_rewriter::mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
expr_ref_buffer new_args(m());
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr_ref tmp(m());
|
||||
mk_not(args[i], tmp);
|
||||
new_args.push_back(tmp);
|
||||
}
|
||||
expr_ref tmp(m());
|
||||
mk_or(new_args.size(), new_args.c_ptr(), tmp);
|
||||
mk_not(tmp, result);
|
||||
}
|
||||
|
||||
br_status bool_rewriter::mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
bool s = false;
|
||||
ptr_buffer<expr> buffer;
|
||||
expr_fast_mark1 neg_lits;
|
||||
expr_fast_mark2 pos_lits;
|
||||
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (m().is_true(arg)) {
|
||||
s = true;
|
||||
continue;
|
||||
}
|
||||
if (m().is_false(arg)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_not(arg)) {
|
||||
expr * atom = to_app(arg)->get_arg(0);
|
||||
if (neg_lits.is_marked(atom)) {
|
||||
s = true;
|
||||
continue;
|
||||
}
|
||||
if (pos_lits.is_marked(atom)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
neg_lits.mark(atom);
|
||||
}
|
||||
else {
|
||||
if (pos_lits.is_marked(arg)) {
|
||||
s = true;
|
||||
continue;
|
||||
}
|
||||
if (neg_lits.is_marked(arg)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
pos_lits.mark(arg);
|
||||
}
|
||||
buffer.push_back(arg);
|
||||
}
|
||||
|
||||
unsigned sz = buffer.size();
|
||||
|
||||
switch(sz) {
|
||||
case 0:
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
case 1:
|
||||
result = buffer.back();
|
||||
return BR_DONE;
|
||||
default:
|
||||
if (s) {
|
||||
result = m().mk_and(sz, buffer.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
br_status bool_rewriter::mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
unsigned i;
|
||||
for (i = 0; i < num_args; i++) {
|
||||
if (m().is_and(args[i]))
|
||||
break;
|
||||
}
|
||||
if (i < num_args) {
|
||||
// has nested ANDs
|
||||
ptr_buffer<expr> flat_args;
|
||||
flat_args.append(i, args);
|
||||
for (; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
// Remark: all rewrites are depth 1.
|
||||
if (m().is_and(arg)) {
|
||||
unsigned num = to_app(arg)->get_num_args();
|
||||
for (unsigned j = 0; j < num; j++)
|
||||
flat_args.push_back(to_app(arg)->get_arg(j));
|
||||
}
|
||||
else {
|
||||
flat_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
if (mk_nflat_and_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED)
|
||||
result = m().mk_and(flat_args.size(), flat_args.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
return mk_nflat_and_core(num_args, args, result);
|
||||
}
|
||||
|
||||
br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
bool s = false;
|
||||
ptr_buffer<expr> buffer;
|
||||
expr_fast_mark1 neg_lits;
|
||||
expr_fast_mark2 pos_lits;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (m().is_true(arg)) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_false(arg)) {
|
||||
s = true;
|
||||
continue;
|
||||
}
|
||||
if (m().is_not(arg)) {
|
||||
expr * atom = to_app(arg)->get_arg(0);
|
||||
if (neg_lits.is_marked(atom)) {
|
||||
s = true;
|
||||
continue;
|
||||
}
|
||||
if (pos_lits.is_marked(atom)) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
neg_lits.mark(atom);
|
||||
}
|
||||
else {
|
||||
if (pos_lits.is_marked(arg)) {
|
||||
s = true;
|
||||
continue;
|
||||
}
|
||||
if (neg_lits.is_marked(arg)) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
pos_lits.mark(arg);
|
||||
}
|
||||
buffer.push_back(arg);
|
||||
}
|
||||
|
||||
unsigned sz = buffer.size();
|
||||
|
||||
switch(sz) {
|
||||
case 0:
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
case 1:
|
||||
result = buffer.back();
|
||||
return BR_DONE;
|
||||
default:
|
||||
if (m_local_ctx && m_local_ctx_cost <= m_local_ctx_limit) {
|
||||
neg_lits.reset();
|
||||
pos_lits.reset();
|
||||
if (local_ctx_simp(sz, buffer.c_ptr(), result))
|
||||
return BR_DONE;
|
||||
}
|
||||
if (s) {
|
||||
result = m().mk_or(sz, buffer.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
unsigned i;
|
||||
for (i = 0; i < num_args; i++) {
|
||||
if (m().is_or(args[i]))
|
||||
break;
|
||||
}
|
||||
if (i < num_args) {
|
||||
// has nested ORs
|
||||
ptr_buffer<expr> flat_args;
|
||||
flat_args.append(i, args);
|
||||
for (; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
// Remark: all rewrites are depth 1.
|
||||
if (m().is_or(arg)) {
|
||||
unsigned num = to_app(arg)->get_num_args();
|
||||
for (unsigned j = 0; j < num; j++)
|
||||
flat_args.push_back(to_app(arg)->get_arg(j));
|
||||
}
|
||||
else {
|
||||
flat_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
if (mk_nflat_or_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED)
|
||||
result = m().mk_or(flat_args.size(), flat_args.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
return mk_nflat_or_core(num_args, args, result);
|
||||
}
|
||||
|
||||
expr * bool_rewriter::mk_or_app(unsigned num_args, expr * const * args) {
|
||||
switch(num_args) {
|
||||
case 0: return m().mk_false();
|
||||
case 1: return args[0];
|
||||
default: return m().mk_or(num_args, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Auxiliary method for local_ctx_simp.
|
||||
|
||||
Replace args[i] by true if marked in neg_lits.
|
||||
Replace args[i] by false if marked in pos_lits.
|
||||
*/
|
||||
bool bool_rewriter::simp_nested_not_or(unsigned num_args, expr * const * args,
|
||||
expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) {
|
||||
ptr_buffer<expr> new_args;
|
||||
bool simp = false;
|
||||
m_local_ctx_cost += num_args;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (neg_lits.is_marked(arg)) {
|
||||
result = m().mk_false();
|
||||
return true;
|
||||
}
|
||||
if (pos_lits.is_marked(arg)) {
|
||||
simp = true;
|
||||
continue;
|
||||
}
|
||||
if (m().is_not(arg)) {
|
||||
expr * atom = to_app(arg)->get_arg(0);
|
||||
if (neg_lits.is_marked(atom)) {
|
||||
simp = true;
|
||||
continue;
|
||||
}
|
||||
if (pos_lits.is_marked(atom)) {
|
||||
result = m().mk_false();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
new_args.push_back(arg);
|
||||
}
|
||||
if (simp) {
|
||||
switch(new_args.size()) {
|
||||
case 0:
|
||||
result = m().mk_true();
|
||||
return true;
|
||||
case 1:
|
||||
mk_not(new_args[0], result);
|
||||
return true;
|
||||
default:
|
||||
result = m().mk_not(m().mk_or(new_args.size(), new_args.c_ptr()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
expr * bool_rewriter::simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified) {
|
||||
if (m().is_not(arg)) {
|
||||
expr * atom = to_app(arg)->get_arg(0);
|
||||
if (neg_lits.is_marked(atom)) {
|
||||
modified = true;
|
||||
return m().mk_false();
|
||||
}
|
||||
if (pos_lits.is_marked(atom)) {
|
||||
modified = true;
|
||||
return m().mk_true();
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
else {
|
||||
if (neg_lits.is_marked(arg)) {
|
||||
modified = true;
|
||||
return m().mk_true();
|
||||
}
|
||||
if (pos_lits.is_marked(arg)) {
|
||||
modified = true;
|
||||
return m().mk_false();
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Simpler version of mk_ite, that will not invoke mk_or/mk_and.
|
||||
It is used byt local_ctx_simp to prevent a recursive call to local_ctx_simp.
|
||||
See comment at simp_nested_eq_ite.
|
||||
*/
|
||||
void bool_rewriter::mk_nested_ite(expr * c, expr * t, expr * e, expr_ref & result) {
|
||||
if (m().is_true(c)) {
|
||||
result = t;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m().is_false(c)) {
|
||||
result = e;
|
||||
return;
|
||||
}
|
||||
|
||||
if (t == e) {
|
||||
result = t;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m().is_bool(t)) {
|
||||
if (m().is_true(t)) {
|
||||
if (m().is_false(e)) {
|
||||
result = c;
|
||||
return;
|
||||
}
|
||||
result = m().mk_or(c, e);
|
||||
return;
|
||||
}
|
||||
if (m().is_false(t)) {
|
||||
if (m().is_true(e)) {
|
||||
mk_not(c, result);
|
||||
return;
|
||||
}
|
||||
expr_ref tmp(m());
|
||||
mk_not(e, tmp);
|
||||
result = m().mk_not(m().mk_or(c, tmp));
|
||||
return;
|
||||
}
|
||||
if (m().is_true(e)) {
|
||||
expr_ref tmp(m());
|
||||
mk_not(c, tmp);
|
||||
result = m().mk_or(tmp, t);
|
||||
return;
|
||||
}
|
||||
if (m().is_false(e) || c == e) {
|
||||
expr_ref tmp1(m());
|
||||
expr_ref tmp2(m());
|
||||
mk_not(c, tmp1);
|
||||
mk_not(t, tmp2);
|
||||
result = m().mk_not(m().mk_or(tmp1, tmp2));
|
||||
return;
|
||||
}
|
||||
if (c == t) {
|
||||
result = m().mk_or(c, e);
|
||||
return;
|
||||
}
|
||||
if (m().is_complement_core(t, e)) { // t = not(e)
|
||||
mk_eq(c, t, result);
|
||||
return;
|
||||
}
|
||||
if (m().is_complement_core(e, t)) { // e = not(t)
|
||||
mk_eq(c, t, result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
result = m().mk_ite(c, t, e);
|
||||
}
|
||||
|
||||
bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) {
|
||||
bool neg = false;
|
||||
m_local_ctx_cost += 3;
|
||||
if (m().is_not(t)) {
|
||||
neg = true;
|
||||
t = to_app(t)->get_arg(0);
|
||||
}
|
||||
if (m().is_iff(t) || m().is_eq(t)) {
|
||||
bool modified = false;
|
||||
expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified);
|
||||
expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified);
|
||||
if (!modified)
|
||||
return false;
|
||||
mk_eq(new_lhs, new_rhs, result);
|
||||
if (neg)
|
||||
mk_not(result, result);
|
||||
return true;
|
||||
}
|
||||
if (m().is_ite(t)) {
|
||||
bool modified = false;
|
||||
expr * new_c = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified);
|
||||
expr * new_t = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified);
|
||||
expr * new_e = simp_arg(to_app(t)->get_arg(2), neg_lits, pos_lits, modified);
|
||||
if (!modified)
|
||||
return false;
|
||||
// It is not safe to invoke mk_ite here, since it can recursively call
|
||||
// local_ctx_simp by
|
||||
// - transforming the ITE into an OR
|
||||
// - and invoked mk_or, that will invoke local_ctx_simp
|
||||
// mk_ite(new_c, new_t, new_e, result);
|
||||
mk_nested_ite(new_c, new_t, new_e, result);
|
||||
if (neg)
|
||||
mk_not(result, result);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Apply local context simplification at (OR args[0] ... args[num_args-1])
|
||||
Basic idea:
|
||||
- Replace args[i] by false in the other arguments
|
||||
- If args[i] is of the form (not t), then replace t by true in the other arguments.
|
||||
To make sure the simplification is efficient we bound the depth.
|
||||
*/
|
||||
bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
expr_ref_vector old_args(m());
|
||||
expr_ref_vector new_args(m());
|
||||
expr_ref new_arg(m());
|
||||
expr_fast_mark1 neg_lits;
|
||||
expr_fast_mark2 pos_lits;
|
||||
bool simp = false;
|
||||
bool modified = false;
|
||||
bool forward = true;
|
||||
unsigned rounds = 0;
|
||||
|
||||
while (true) {
|
||||
rounds++;
|
||||
#if 0
|
||||
if (rounds > 10)
|
||||
verbose_stream() << "rounds: " << rounds << "\n";
|
||||
#endif
|
||||
|
||||
#define PUSH_NEW_ARG(ARG) { \
|
||||
new_args.push_back(ARG); \
|
||||
if (m().is_not(ARG)) \
|
||||
neg_lits.mark(to_app(ARG)->get_arg(0)); \
|
||||
else \
|
||||
pos_lits.mark(ARG); \
|
||||
}
|
||||
|
||||
#define PROCESS_ARG() \
|
||||
{ \
|
||||
expr * arg = args[i]; \
|
||||
if (m().is_not(arg) && m().is_or(to_app(arg)->get_arg(0)) && \
|
||||
simp_nested_not_or(to_app(to_app(arg)->get_arg(0))->get_num_args(), \
|
||||
to_app(to_app(arg)->get_arg(0))->get_args(), \
|
||||
neg_lits, \
|
||||
pos_lits, \
|
||||
new_arg)) { \
|
||||
modified = true; simp = true; \
|
||||
arg = new_arg; \
|
||||
} \
|
||||
if (simp_nested_eq_ite(arg, neg_lits, pos_lits, new_arg)) { \
|
||||
modified = true; simp = true; \
|
||||
arg = new_arg; \
|
||||
} \
|
||||
if (m().is_false(arg)) \
|
||||
continue; \
|
||||
if (m().is_true(arg)) { \
|
||||
result = arg; \
|
||||
return true; \
|
||||
} \
|
||||
if (m_flat && m().is_or(arg)) { \
|
||||
unsigned sz = to_app(arg)->get_num_args(); \
|
||||
for (unsigned j = 0; j < sz; j++) { \
|
||||
expr * arg_arg = to_app(arg)->get_arg(j); \
|
||||
PUSH_NEW_ARG(arg_arg); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
PUSH_NEW_ARG(arg); \
|
||||
} \
|
||||
}
|
||||
|
||||
m_local_ctx_cost += 2*num_args;
|
||||
#if 0
|
||||
static unsigned counter = 0;
|
||||
counter++;
|
||||
if (counter % 10000 == 0)
|
||||
verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << "\n";
|
||||
#endif
|
||||
|
||||
if (forward) {
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
PROCESS_ARG();
|
||||
}
|
||||
forward = false;
|
||||
}
|
||||
else {
|
||||
unsigned i = num_args;
|
||||
while (i > 0) {
|
||||
--i;
|
||||
PROCESS_ARG();
|
||||
}
|
||||
if (!modified) {
|
||||
if (simp) {
|
||||
result = mk_or_app(num_args, args);
|
||||
return true;
|
||||
}
|
||||
return false; // didn't simplify
|
||||
}
|
||||
// preserve the original order...
|
||||
std::reverse(new_args.c_ptr(), new_args.c_ptr() + new_args.size());
|
||||
modified = false;
|
||||
forward = true;
|
||||
}
|
||||
pos_lits.reset();
|
||||
neg_lits.reset();
|
||||
old_args.reset();
|
||||
old_args.swap(new_args);
|
||||
SASSERT(new_args.empty());
|
||||
args = old_args.c_ptr();
|
||||
num_args = old_args.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Apply simplification if ite is an if-then-else tree where every leaf is a value.
|
||||
|
||||
This is an efficient way to
|
||||
|
||||
*/
|
||||
br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) {
|
||||
SASSERT(m().is_ite(ite));
|
||||
SASSERT(m().is_value(val));
|
||||
|
||||
expr * t = ite->get_arg(1);
|
||||
expr * e = ite->get_arg(2);
|
||||
if (!m().is_value(t) || !m().is_value(e))
|
||||
return BR_FAILED;
|
||||
|
||||
if (t != val && e != val) {
|
||||
TRACE("try_ite_value", tout << mk_ismt2_pp(t, m()) << " " << mk_ismt2_pp(e, m()) << " " << mk_ismt2_pp(val, m()) << "\n";
|
||||
tout << t << " " << e << " " << val << "\n";);
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (t == val && e == val) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (t == val) {
|
||||
result = ite->get_arg(0);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
SASSERT(e == val);
|
||||
|
||||
mk_not(ite->get_arg(0), result);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Return true if ite is an if-then-else tree where the leaves are values,
|
||||
// and they are all different from val
|
||||
static bool is_ite_value_tree_neq_value(ast_manager & m, app * ite, app * val) {
|
||||
SASSERT(m.is_ite(ite));
|
||||
SASSERT(m.is_value(val));
|
||||
|
||||
expr_fast_mark1 visited;
|
||||
ptr_buffer<app> todo;
|
||||
todo.push_back(ite);
|
||||
|
||||
#define VISIT(ARG) { \
|
||||
if (m.is_value(ARG)) { \
|
||||
if (ARG == val) \
|
||||
return false; \
|
||||
} \
|
||||
else if (m.is_ite(ARG)) { \
|
||||
if (!visited.is_marked(ARG)) { \
|
||||
visited.mark(ARG); \
|
||||
todo.push_back(to_app(ARG)); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
return false; \
|
||||
} \
|
||||
}
|
||||
|
||||
while (!todo.empty()) {
|
||||
app * ite = todo.back();
|
||||
todo.pop_back();
|
||||
SASSERT(m.is_ite(ite));
|
||||
expr * t = ite->get_arg(1);
|
||||
expr * e = ite->get_arg(2);
|
||||
VISIT(t);
|
||||
VISIT(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
if (lhs == rhs) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (m().are_distinct(lhs, rhs)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status r = BR_FAILED;
|
||||
if (m().is_ite(lhs) && m().is_value(rhs)) {
|
||||
// if (is_ite_value_tree_neq_value(m(), to_app(lhs), to_app(rhs))) {
|
||||
// result = m().mk_false();
|
||||
// return BR_DONE;
|
||||
// }
|
||||
r = try_ite_value(to_app(lhs), to_app(rhs), result);
|
||||
CTRACE("try_ite_value", r != BR_FAILED,
|
||||
tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";);
|
||||
}
|
||||
else if (m().is_ite(rhs) && m().is_value(lhs)) {
|
||||
// if (is_ite_value_tree_neq_value(m(), to_app(rhs), to_app(lhs))) {
|
||||
// result = m().mk_false();
|
||||
// return BR_DONE;
|
||||
// }
|
||||
r = try_ite_value(to_app(rhs), to_app(lhs), result);
|
||||
CTRACE("try_ite_value", r != BR_FAILED,
|
||||
tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";);
|
||||
}
|
||||
if (r != BR_FAILED)
|
||||
return r;
|
||||
|
||||
if (m().is_bool(lhs)) {
|
||||
bool unfolded = false;
|
||||
if (m().is_not(lhs) && m().is_not(rhs)) {
|
||||
lhs = to_app(lhs)->get_arg(0);
|
||||
rhs = to_app(rhs)->get_arg(0);
|
||||
unfolded = true;
|
||||
}
|
||||
if (m().is_true(lhs)) {
|
||||
result = rhs;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_false(lhs)) {
|
||||
mk_not(rhs, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_true(rhs)) {
|
||||
result = lhs;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_false(rhs)) {
|
||||
mk_not(lhs, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_complement(lhs, rhs)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (unfolded) {
|
||||
result = m().mk_eq(lhs, rhs);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (num_args <= 1) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (num_args == 2) {
|
||||
expr_ref tmp(m());
|
||||
result = m().mk_not(m().mk_eq(args[0], args[1]));
|
||||
return BR_REWRITE2; // mk_eq may be dispatched to other rewriters.
|
||||
}
|
||||
|
||||
expr_fast_mark1 visited;
|
||||
bool all_value = true;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (visited.is_marked(arg)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
visited.mark(arg);
|
||||
if (!m().is_value(arg))
|
||||
all_value = false;
|
||||
}
|
||||
if (all_value) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
SASSERT(num_args > 2);
|
||||
if (m().is_bool(args[0])) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (m_blast_distinct) {
|
||||
ptr_buffer<expr> new_diseqs;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned j = i + 1; j < num_args; j++)
|
||||
new_diseqs.push_back(m().mk_not(m().mk_eq(args[i], args[j])));
|
||||
}
|
||||
result = m().mk_and(new_diseqs.size(), new_diseqs.c_ptr());
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) {
|
||||
bool s = false;
|
||||
|
||||
// (ite (not c) a b) ==> (ite c b a)
|
||||
if (m().is_not(c)) {
|
||||
c = to_app(c)->get_arg(0);
|
||||
std::swap(t, e);
|
||||
s = true;
|
||||
}
|
||||
|
||||
// (ite c (ite c t1 t2) t3) ==> (ite c t1 t3)
|
||||
if (m().is_ite(t) && to_app(t)->get_arg(0) == c) {
|
||||
// Remark: (ite c (ite (not c) t1 t2) t3) ==> (ite c t2 t3) does not happen if applying rewrites bottom up
|
||||
t = to_app(t)->get_arg(1);
|
||||
s = true;
|
||||
}
|
||||
|
||||
// (ite c t1 (ite c t2 t3)) ==> (ite c t1 t3)
|
||||
if (m().is_ite(e) && to_app(e)->get_arg(0) == c) {
|
||||
// Remark: (ite c t1 (ite (not c) t2 t3)) ==> (ite c t1 t2) does not happen if applying rewrites bottom up
|
||||
e = to_app(e)->get_arg(2);
|
||||
s = true;
|
||||
}
|
||||
|
||||
if (m().is_true(c)) {
|
||||
result = t;
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (m().is_false(c)) {
|
||||
result = e;
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (t == e) {
|
||||
result = t;
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (m().is_bool(t)) {
|
||||
if (m().is_true(t)) {
|
||||
if (m().is_false(e)) {
|
||||
result = c;
|
||||
return BR_DONE;
|
||||
}
|
||||
mk_or(c, e, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_false(t)) {
|
||||
if (m().is_true(e)) {
|
||||
mk_not(c, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
expr_ref tmp(m());
|
||||
mk_not(c, tmp);
|
||||
mk_and(tmp, e, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_true(e)) {
|
||||
expr_ref tmp(m());
|
||||
mk_not(c, tmp);
|
||||
mk_or(tmp, t, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_false(e)) {
|
||||
mk_and(c, t, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (c == e) {
|
||||
mk_and(c, t, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (c == t) {
|
||||
mk_or(c, e, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_complement_core(t, e)) { // t = not(e)
|
||||
mk_eq(c, t, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_complement_core(e, t)) { // e = not(t)
|
||||
mk_eq(c, t, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (m().is_ite(t) && m_ite_extra_rules) {
|
||||
// (ite c1 (ite c2 t1 t2) t1) ==> (ite (and c1 (not c2)) t2 t1)
|
||||
if (e == to_app(t)->get_arg(1)) {
|
||||
expr_ref not_c2(m());
|
||||
mk_not(to_app(t)->get_arg(0), not_c2);
|
||||
expr_ref new_c(m());
|
||||
mk_and(c, not_c2, new_c);
|
||||
result = m().mk_ite(new_c, to_app(t)->get_arg(2), e);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
// (ite c1 (ite c2 t1 t2) t2) ==> (ite (and c1 c2) t1 t2)
|
||||
if (e == to_app(t)->get_arg(2)) {
|
||||
expr_ref new_c(m());
|
||||
mk_and(c, to_app(t)->get_arg(0), new_c);
|
||||
result = m().mk_ite(new_c, to_app(t)->get_arg(1), e);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
if (m().is_ite(e)) {
|
||||
// (ite c1 (ite c2 t1 t2) (ite c3 t1 t2)) ==> (ite (or (and c1 c2) (and (not c1) c3)) t1 t2)
|
||||
if (to_app(t)->get_arg(1) == to_app(e)->get_arg(1) &&
|
||||
to_app(t)->get_arg(2) == to_app(e)->get_arg(2)) {
|
||||
expr_ref and1(m());
|
||||
expr_ref and2(m());
|
||||
expr_ref notc(m());
|
||||
mk_and(c, to_app(t)->get_arg(0), and1);
|
||||
mk_not(c, notc);
|
||||
mk_and(notc, to_app(e)->get_arg(0), and2);
|
||||
expr_ref new_c(m());
|
||||
mk_or(and1, and2, new_c);
|
||||
result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2));
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
// (ite c1 (ite c2 t1 t2) (ite c3 t2 t1)) ==> (ite (or (and c1 c2) (and (not c1) (not c3))) t1 t2)
|
||||
if (to_app(t)->get_arg(1) == to_app(e)->get_arg(2) &&
|
||||
to_app(t)->get_arg(2) == to_app(e)->get_arg(1)) {
|
||||
expr_ref and1(m());
|
||||
expr_ref and2(m());
|
||||
expr_ref notc(m());
|
||||
mk_and(c, to_app(t)->get_arg(0), and1);
|
||||
mk_not(c, notc);
|
||||
expr_ref notc3(m());
|
||||
mk_not(to_app(e)->get_arg(0), notc3);
|
||||
mk_and(notc, notc3, and2);
|
||||
expr_ref new_c(m());
|
||||
mk_or(and1, and2, new_c);
|
||||
result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2));
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m().is_ite(e) && m_ite_extra_rules) {
|
||||
// (ite c1 t1 (ite c2 t1 t2)) ==> (ite (or c1 c2) t1 t2)
|
||||
if (t == to_app(e)->get_arg(1)) {
|
||||
expr_ref new_c(m());
|
||||
mk_or(c, to_app(e)->get_arg(0), new_c);
|
||||
result = m().mk_ite(new_c, t, to_app(e)->get_arg(2));
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
// (ite c1 t1 (ite c2 t2 t1)) ==> (ite (or c1 (not c2)) t1 t2)
|
||||
if (t == to_app(e)->get_arg(2)) {
|
||||
expr_ref not_c2(m());
|
||||
mk_not(to_app(e)->get_arg(0), not_c2);
|
||||
expr_ref new_c(m());
|
||||
mk_or(c, not_c2, new_c);
|
||||
result = m().mk_ite(new_c, t, to_app(e)->get_arg(1));
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
if (s) {
|
||||
result = m().mk_ite(c, t, e);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bool_rewriter::mk_not_core(expr * t, expr_ref & result) {
|
||||
if (m().is_not(t)) {
|
||||
result = to_app(t)->get_arg(0);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_true(t)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_false(t)) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_eq(t) && m().is_bool(to_app(t)->get_arg(0))) {
|
||||
expr_ref tmp(m());
|
||||
mk_not(to_app(t)->get_arg(0), tmp);
|
||||
mk_eq(tmp, to_app(t)->get_arg(1), result);
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
void bool_rewriter::mk_xor(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
expr_ref tmp(m());
|
||||
mk_not(lhs, tmp);
|
||||
mk_eq(tmp, rhs, result);
|
||||
}
|
||||
|
||||
void bool_rewriter::mk_implies(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
expr_ref tmp(m());
|
||||
mk_not(lhs, tmp);
|
||||
mk_or(tmp, rhs, result);
|
||||
}
|
||||
|
||||
void bool_rewriter::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
expr_ref tmp(m_manager);
|
||||
mk_and(num_args, args, tmp);
|
||||
mk_not(tmp, result);
|
||||
}
|
||||
|
||||
void bool_rewriter::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
expr_ref tmp(m_manager);
|
||||
mk_or(num_args, args, tmp);
|
||||
mk_not(tmp, result);
|
||||
}
|
||||
|
||||
void bool_rewriter::mk_nand(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
expr_ref tmp(m_manager);
|
||||
mk_and(arg1, arg2, tmp);
|
||||
mk_not(tmp, result);
|
||||
}
|
||||
|
||||
void bool_rewriter::mk_nor(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
expr_ref tmp(m_manager);
|
||||
mk_or(arg1, arg2, tmp);
|
||||
mk_not(tmp, result);
|
||||
}
|
||||
|
||||
template class rewriter_tpl<bool_rewriter_cfg>;
|
||||
199
src/ast/rewriter/bool_rewriter.h
Normal file
199
src/ast/rewriter/bool_rewriter.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bool_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for Boolean operators.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-04
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _BOOL_REWRITER_H_
|
||||
#define _BOOL_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter.h"
|
||||
#include"params.h"
|
||||
|
||||
/**
|
||||
\brief Apply basic Boolean rewriting operations.
|
||||
|
||||
Only depth 1 simplifications are performed.
|
||||
|
||||
Note: there are no recursive calls.
|
||||
|
||||
Note: arguments of AC operators are not sorted.
|
||||
Note: arguments of = and xor are also not sorted.
|
||||
|
||||
Note: By default, (AND A B) is not rewritten as (NOT (OR (NOT A) (NOT B)))
|
||||
|
||||
Note: AND OR operators are flattened only if mk_flat_app, mk_flat_or, mk_flat_and are used.
|
||||
|
||||
The following operators are expanded:
|
||||
- => (implies)
|
||||
- xor
|
||||
- nand
|
||||
- nor
|
||||
- iff
|
||||
|
||||
All methods run in time almost linear on the number of arguments.
|
||||
Actually, this is not true when flattening is enabled.
|
||||
A better approximation is O(Sum_{t \in args} size1(t)).
|
||||
Where size1(t) = max{t->get_num_args(), 1}.
|
||||
*/
|
||||
class bool_rewriter {
|
||||
ast_manager & m_manager;
|
||||
bool m_flat;
|
||||
bool m_local_ctx;
|
||||
bool m_elim_and;
|
||||
bool m_blast_distinct;
|
||||
bool m_ite_extra_rules;
|
||||
unsigned m_local_ctx_limit;
|
||||
unsigned m_local_ctx_cost;
|
||||
|
||||
br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
void mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
expr * mk_or_app(unsigned num_args, expr * const * args);
|
||||
bool simp_nested_not_or(unsigned num_args, expr * const * args, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result);
|
||||
expr * simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified);
|
||||
void mk_nested_ite(expr * new_c, expr * new_t, expr * new_e, expr_ref & result);
|
||||
bool simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result);
|
||||
bool local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status try_ite_value(app * ite, app * val, expr_ref & result);
|
||||
|
||||
public:
|
||||
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); }
|
||||
ast_manager & m() const { return m_manager; }
|
||||
family_id get_fid() const { return m().get_basic_family_id(); }
|
||||
bool is_eq(expr * t) const { return m().is_eq(t) || m().is_iff(t); }
|
||||
|
||||
bool flat() const { return m_flat; }
|
||||
void set_flat(bool f) { m_flat = f; }
|
||||
bool elim_and() const { return m_elim_and; }
|
||||
void set_elim_and(bool f) { m_elim_and = f; }
|
||||
void reset_local_ctx_cost() { m_local_ctx_cost = 0; }
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
// The core methods return true if a rewrite-step/simplification was applied
|
||||
// to the arguments, and the result is stored in 'result'. Otherwise, they return false
|
||||
// and result.get == 0.
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(f, num_args, args);
|
||||
}
|
||||
|
||||
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
|
||||
br_status mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_iff_core(expr * lhs, expr * rhs, expr_ref & result) { return mk_eq_core(lhs, rhs, result); }
|
||||
br_status mk_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (m_elim_and) {
|
||||
mk_and_as_or(num_args, args, result);
|
||||
return BR_DONE;
|
||||
}
|
||||
else if (m_flat) {
|
||||
return mk_flat_and_core(num_args, args, result);
|
||||
}
|
||||
else {
|
||||
return mk_nflat_and_core(num_args, args, result);
|
||||
}
|
||||
}
|
||||
br_status mk_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
return m_flat ?
|
||||
mk_flat_or_core(num_args, args, result) :
|
||||
mk_nflat_or_core(num_args, args, result);
|
||||
}
|
||||
br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result);
|
||||
br_status mk_not_core(expr * t, expr_ref & result);
|
||||
|
||||
void mk_eq(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
if (mk_eq_core(lhs, rhs, result) == BR_FAILED)
|
||||
result = m().mk_eq(lhs, rhs);
|
||||
}
|
||||
void mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); }
|
||||
void mk_xor(expr * lhs, expr * rhs, expr_ref & result);
|
||||
void mk_and(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_and_core(num_args, args, result) == BR_FAILED) {
|
||||
SASSERT(!m_elim_and);
|
||||
result = m().mk_and(num_args, args);
|
||||
}
|
||||
}
|
||||
void mk_or(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_or_core(num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_or(num_args, args);
|
||||
}
|
||||
void mk_and(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
expr * args[2] = {arg1, arg2};
|
||||
mk_and(2, args, result);
|
||||
}
|
||||
void mk_or(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
expr * args[2] = {arg1, arg2};
|
||||
mk_or(2, args, result);
|
||||
}
|
||||
void mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
|
||||
expr * args[3] = {arg1, arg2, arg3};
|
||||
mk_and(3, args, result);
|
||||
}
|
||||
void mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
|
||||
expr * args[3] = {arg1, arg2, arg3};
|
||||
mk_or(3, args, result);
|
||||
}
|
||||
void mk_implies(expr * lhs, expr * rhs, expr_ref & result);
|
||||
void mk_ite(expr * c, expr * t, expr * e, expr_ref & result) {
|
||||
if (mk_ite_core(c, t, e, result) == BR_FAILED)
|
||||
result = m().mk_ite(c, t, e);
|
||||
}
|
||||
void mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_distinct_core(num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_distinct(num_args, args);
|
||||
}
|
||||
void mk_not(expr * t, expr_ref & result) {
|
||||
if (mk_not_core(t, result) == BR_FAILED)
|
||||
result = m().mk_not(t);
|
||||
}
|
||||
|
||||
void mk_nand(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_nor(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_nand(expr * arg1, expr * arg2, expr_ref & result);
|
||||
void mk_nor(expr * arg1, expr * arg2, expr_ref & result);
|
||||
};
|
||||
|
||||
struct bool_rewriter_cfg : public default_rewriter_cfg {
|
||||
bool_rewriter m_r;
|
||||
bool flat_assoc(func_decl * f) const { return m_r.flat() && (m_r.m().is_and(f) || m_r.m().is_or(f)); }
|
||||
bool rewrite_patterns() const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
if (f->get_family_id() != m_r.get_fid())
|
||||
return BR_FAILED;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
bool_rewriter_cfg(ast_manager & m, params_ref const & p):m_r(m, p) {}
|
||||
};
|
||||
|
||||
class bool_rewriter_star : public rewriter_tpl<bool_rewriter_cfg> {
|
||||
bool_rewriter_cfg m_cfg;
|
||||
public:
|
||||
bool_rewriter_star(ast_manager & m, params_ref const & p):
|
||||
rewriter_tpl<bool_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(m, p) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
2076
src/ast/rewriter/bv_rewriter.cpp
Normal file
2076
src/ast/rewriter/bv_rewriter.cpp
Normal file
File diff suppressed because it is too large
Load diff
171
src/ast/rewriter/bv_rewriter.h
Normal file
171
src/ast/rewriter/bv_rewriter.h
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bit-vectors
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-14
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _BV_REWRITER_H_
|
||||
#define _BV_REWRITER_H_
|
||||
|
||||
#include"poly_rewriter.h"
|
||||
#include"bv_decl_plugin.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
|
||||
class mk_extract_proc {
|
||||
bv_util & m_util;
|
||||
unsigned m_high;
|
||||
unsigned m_low;
|
||||
sort * m_domain;
|
||||
func_decl * m_f_cached;
|
||||
public:
|
||||
mk_extract_proc(bv_util & u);
|
||||
~mk_extract_proc();
|
||||
app * operator()(unsigned high, unsigned low, expr * arg);
|
||||
};
|
||||
|
||||
class bv_rewriter_core {
|
||||
protected:
|
||||
typedef rational numeral;
|
||||
bv_util m_util;
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
|
||||
bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
|
||||
bool is_numeral(expr * n, numeral & r) const { unsigned sz; return m_util.is_numeral(n, r, sz); }
|
||||
bool is_zero(expr * n) const { return m_util.is_zero(n); }
|
||||
bool is_minus_one(expr * n) const { return m_util.is_allone(n); }
|
||||
void normalize(numeral & c, sort * s) { unsigned bv_size = m_util.get_bv_size(s); c = m_util.norm(c, bv_size); }
|
||||
app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); }
|
||||
decl_kind add_decl_kind() const { return OP_BADD; }
|
||||
decl_kind mul_decl_kind() const { return OP_BMUL; }
|
||||
bool use_power() const { return false; }
|
||||
decl_kind power_decl_kind() const { UNREACHABLE(); return static_cast<decl_kind>(UINT_MAX); }
|
||||
public:
|
||||
bv_rewriter_core(ast_manager & m):m_util(m) {}
|
||||
};
|
||||
|
||||
class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
|
||||
mk_extract_proc m_mk_extract;
|
||||
arith_util m_autil;
|
||||
bool m_hi_div0;
|
||||
bool m_elim_sign_ext;
|
||||
bool m_mul2concat;
|
||||
bool m_bit2bool;
|
||||
bool m_blast_eq_value;
|
||||
bool m_mkbv2num;
|
||||
bool m_split_concat_eq;
|
||||
bool m_udiv2mul;
|
||||
|
||||
bool is_zero_bit(expr * x, unsigned idx);
|
||||
|
||||
br_status mk_ule(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_uge(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_ult(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_sle(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_sge(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_slt(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result);
|
||||
|
||||
br_status mk_concat(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result);
|
||||
br_status mk_repeat(unsigned n, expr * arg, expr_ref & result);
|
||||
br_status mk_zero_extend(unsigned n, expr * arg, expr_ref & result);
|
||||
br_status mk_sign_extend(unsigned n, expr * arg, expr_ref & result);
|
||||
br_status mk_bv_not(expr * arg, expr_ref & result);
|
||||
br_status mk_bv_or(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_xor(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_and(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_nand(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_nor(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_xnor(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_rotate_left(unsigned n, expr * arg, expr_ref & result);
|
||||
br_status mk_bv_rotate_right(unsigned n, expr * arg, expr_ref & result);
|
||||
br_status mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_add(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
expr * args[2] = { arg1, arg2 };
|
||||
return mk_bv_add(2, args, result);
|
||||
}
|
||||
br_status mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result);
|
||||
bool is_minus_one_core(expr * arg) const;
|
||||
bool is_x_minus_one(expr * arg, expr * & x);
|
||||
br_status mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
|
||||
br_status mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
|
||||
br_status mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
|
||||
br_status mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
|
||||
br_status mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
|
||||
br_status mk_bv_sdiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, m_hi_div0, result); }
|
||||
br_status mk_bv_udiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, m_hi_div0, result); }
|
||||
br_status mk_bv_srem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, m_hi_div0, result); }
|
||||
br_status mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, m_hi_div0, result); }
|
||||
br_status mk_bv_smod(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, m_hi_div0, result); }
|
||||
br_status mk_bv_sdiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, true, result); }
|
||||
br_status mk_bv_udiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, true, result); }
|
||||
br_status mk_bv_srem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, true, result); }
|
||||
br_status mk_bv_urem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, true, result); }
|
||||
br_status mk_bv_smod_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, true, result); }
|
||||
br_status mk_int2bv(unsigned bv_size, expr * arg, expr_ref & result);
|
||||
br_status mk_bv2int(expr * arg, expr_ref & result);
|
||||
br_status mk_bv_redor(expr * arg, expr_ref & result);
|
||||
br_status mk_bv_redand(expr * arg, expr_ref & result);
|
||||
br_status mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result);
|
||||
br_status mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result);
|
||||
br_status mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result);
|
||||
br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result);
|
||||
bool is_minus_one_times_t(expr * arg);
|
||||
void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result);
|
||||
|
||||
bool is_concat_split_target(expr * t) const;
|
||||
|
||||
br_status mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result);
|
||||
|
||||
void updt_local_params(params_ref const & p);
|
||||
|
||||
public:
|
||||
bv_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
poly_rewriter<bv_rewriter_core>(m, p),
|
||||
m_mk_extract(m_util),
|
||||
m_autil(m) {
|
||||
updt_local_params(p);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
void set_mkbv2num(bool f) { m_mkbv2num = f; }
|
||||
|
||||
unsigned get_bv_size(expr * t) const {return m_util.get_bv_size(t); }
|
||||
bool is_numeral(expr * t) const { return m_util.is_numeral(t); }
|
||||
bool is_numeral(expr * t, numeral & r, unsigned & sz) const { return m_util.is_numeral(t, r, sz); }
|
||||
bool is_bv(expr * t) const { return m_util.is_bv(t); }
|
||||
expr * mk_numeral(numeral const & v, unsigned sz) { return m_util.mk_numeral(v, sz); }
|
||||
expr * mk_numeral(unsigned v, unsigned sz) { return m_util.mk_numeral(numeral(v), sz); }
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(f, num_args, args);
|
||||
}
|
||||
|
||||
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
|
||||
};
|
||||
|
||||
#endif
|
||||
111
src/ast/rewriter/datatype_rewriter.cpp
Normal file
111
src/ast/rewriter/datatype_rewriter.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
datatype_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for Datatypes.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-06
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"datatype_rewriter.h"
|
||||
|
||||
br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(f->get_family_id() == get_fid());
|
||||
switch(f->get_decl_kind()) {
|
||||
case OP_DT_CONSTRUCTOR: return BR_FAILED;
|
||||
case OP_DT_RECOGNISER:
|
||||
//
|
||||
// simplify is_cons(cons(x,y)) -> true
|
||||
// simplify is_cons(nil) -> false
|
||||
//
|
||||
SASSERT(num_args == 1);
|
||||
if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0])))
|
||||
return BR_FAILED;
|
||||
if (to_app(args[0])->get_decl() == m_util.get_recognizer_constructor(f))
|
||||
result = m().mk_true();
|
||||
else
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
case OP_DT_ACCESSOR: {
|
||||
//
|
||||
// simplify head(cons(x,y)) -> x
|
||||
//
|
||||
SASSERT(num_args == 1);
|
||||
if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0])))
|
||||
return BR_FAILED;
|
||||
|
||||
app * a = to_app(args[0]);
|
||||
func_decl * c_decl = a->get_decl();
|
||||
if (c_decl != m_util.get_accessor_constructor(f))
|
||||
return BR_FAILED;
|
||||
ptr_vector<func_decl> const * acc = m_util.get_constructor_accessors(c_decl);
|
||||
SASSERT(acc && acc->size() == a->get_num_args());
|
||||
unsigned num = acc->size();
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
if (f == (*acc)[i]) {
|
||||
// found it.
|
||||
result = a->get_arg(i);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status datatype_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
if (!is_app(lhs) || !is_app(rhs) || !m_util.is_constructor(to_app(lhs)) || !m_util.is_constructor(to_app(rhs)))
|
||||
return BR_FAILED;
|
||||
if (to_app(lhs)->get_decl() != to_app(rhs)->get_decl()) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
// Remark: In datatype_simplifier_plugin, we used
|
||||
// m_basic_simplifier to create '=' and 'and' applications in the
|
||||
// following code. This trick not guarantee that the final expression
|
||||
// will be fully simplified.
|
||||
//
|
||||
// Example:
|
||||
// The assertion
|
||||
// (assert (= (cons a1 (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil))))))
|
||||
// (cons b1 (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil))))))))
|
||||
//
|
||||
// After applying asserted_formulas::reduce(), the following formula was generated.
|
||||
//
|
||||
// (= a1 b1)
|
||||
// (= a2 b2)
|
||||
// (= a3 b3)
|
||||
// (= (+ a4 (* (- 1) b4)) (- 1))
|
||||
// (= (+ c5 a5) b5) <<< NOT SIMPLIFIED WITH RESPECT TO ARITHMETIC
|
||||
// (= (cons a6 nil) (cons b6 nil))) <<< NOT SIMPLIFIED WITH RESPECT TO DATATYPE theory
|
||||
//
|
||||
// Note that asserted_formulas::reduce() applied the simplier many times.
|
||||
// After the first simplification step we had:
|
||||
// (= a1 b1)
|
||||
// (= (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil))))))
|
||||
// (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil))))))
|
||||
|
||||
ptr_buffer<expr> eqs;
|
||||
unsigned num = to_app(lhs)->get_num_args();
|
||||
SASSERT(num == to_app(rhs)->get_num_args());
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
eqs.push_back(m().mk_eq(to_app(lhs)->get_arg(i), to_app(rhs)->get_arg(i)));
|
||||
}
|
||||
result = m().mk_and(eqs.size(), eqs.c_ptr());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
35
src/ast/rewriter/datatype_rewriter.h
Normal file
35
src/ast/rewriter/datatype_rewriter.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
datatype_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for Datatypes.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-06
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _DATATYPE_REWRITER_H_
|
||||
#define _DATATYPE_REWRITER_H_
|
||||
|
||||
#include"datatype_decl_plugin.h"
|
||||
#include"rewriter_types.h"
|
||||
|
||||
class datatype_rewriter {
|
||||
datatype_util m_util;
|
||||
public:
|
||||
datatype_rewriter(ast_manager & m):m_util(m) {}
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
|
||||
};
|
||||
|
||||
#endif
|
||||
467
src/ast/rewriter/der.cpp
Normal file
467
src/ast/rewriter/der.cpp
Normal 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
187
src/ast/rewriter/der.h
Normal 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_ */
|
||||
|
||||
57
src/ast/rewriter/dl_rewriter.cpp
Normal file
57
src/ast/rewriter/dl_rewriter.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-08-10
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"dl_rewriter.h"
|
||||
|
||||
br_status dl_rewriter::mk_app_core(
|
||||
func_decl * f, unsigned num_args, expr* const* args, expr_ref& result) {
|
||||
ast_manager& m = result.get_manager();
|
||||
uint64 v1, v2;
|
||||
switch(f->get_decl_kind()) {
|
||||
case datalog::OP_DL_LT:
|
||||
if (m_util.is_numeral_ext(args[0], v1) &&
|
||||
m_util.is_numeral_ext(args[1], v2)) {
|
||||
result = (v1 < v2)?m.mk_true():m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
// x < x <=> false
|
||||
if (args[0] == args[1]) {
|
||||
result = m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
// x < 0 <=> false
|
||||
if (m_util.is_numeral_ext(args[1], v2) && v2 == 0) {
|
||||
result = m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
// 0 < x <=> 0 != x
|
||||
if (m_util.is_numeral_ext(args[1], v1) && v1 == 0) {
|
||||
result = m.mk_not(m.mk_eq(args[0], args[1]));
|
||||
return BR_DONE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
33
src/ast/rewriter/dl_rewriter.h
Normal file
33
src/ast/rewriter/dl_rewriter.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for atalog finite domains.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-03-09
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_REWRITER_H_
|
||||
#define _DL_REWRITER_H_
|
||||
|
||||
#include"dl_decl_plugin.h"
|
||||
#include"rewriter_types.h"
|
||||
|
||||
class dl_rewriter {
|
||||
datalog::dl_decl_util m_util;
|
||||
public:
|
||||
dl_rewriter(ast_manager & m):m_util(m) {}
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
};
|
||||
|
||||
#endif
|
||||
156
src/ast/rewriter/expr_replacer.cpp
Normal file
156
src/ast/rewriter/expr_replacer.cpp
Normal 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);
|
||||
}
|
||||
62
src/ast/rewriter/expr_replacer.h
Normal file
62
src/ast/rewriter/expr_replacer.h
Normal 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
|
||||
352
src/ast/rewriter/factor_rewriter.cpp
Normal file
352
src/ast/rewriter/factor_rewriter.cpp
Normal 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>;
|
||||
78
src/ast/rewriter/factor_rewriter.h
Normal file
78
src/ast/rewriter/factor_rewriter.h
Normal 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
|
||||
441
src/ast/rewriter/float_rewriter.cpp
Normal file
441
src/ast/rewriter/float_rewriter.cpp
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
float_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for floating point numbers.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-02
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"float_rewriter.h"
|
||||
|
||||
float_rewriter::float_rewriter(ast_manager & m, params_ref const & p):
|
||||
m_util(m) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
float_rewriter::~float_rewriter() {
|
||||
}
|
||||
|
||||
void float_rewriter::updt_params(params_ref const & p) {
|
||||
}
|
||||
|
||||
void float_rewriter::get_param_descrs(param_descrs & r) {
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
br_status st = BR_FAILED;
|
||||
SASSERT(f->get_family_id() == get_fid());
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_TO_FLOAT: st = mk_to_float(f, num_args, args, result); break;
|
||||
case OP_FLOAT_ADD: SASSERT(num_args == 3); st = mk_add(args[0], args[1], args[2], result); break;
|
||||
case OP_FLOAT_SUB: SASSERT(num_args == 3); st = mk_sub(args[0], args[1], args[2], result); break;
|
||||
case OP_FLOAT_UMINUS: SASSERT(num_args == 1); st = mk_uminus(args[0], result); break;
|
||||
case OP_FLOAT_MUL: SASSERT(num_args == 3); st = mk_mul(args[0], args[1], args[2], result); break;
|
||||
case OP_FLOAT_DIV: SASSERT(num_args == 3); st = mk_div(args[0], args[1], args[2], result); break;
|
||||
case OP_FLOAT_REM: SASSERT(num_args == 2); st = mk_rem(args[0], args[1], result); break;
|
||||
case OP_FLOAT_ABS: SASSERT(num_args == 1); st = mk_abs(args[0], result); break;
|
||||
case OP_FLOAT_MIN: SASSERT(num_args == 2); st = mk_min(args[0], args[1], result); break;
|
||||
case OP_FLOAT_MAX: SASSERT(num_args == 2); st = mk_max(args[0], args[1], result); break;
|
||||
case OP_FLOAT_FUSED_MA: SASSERT(num_args == 4); st = mk_fused_ma(args[0], args[1], args[2], args[3], result); break;
|
||||
case OP_FLOAT_SQRT: SASSERT(num_args == 2); st = mk_sqrt(args[0], args[1], result); break;
|
||||
case OP_FLOAT_ROUND_TO_INTEGRAL: SASSERT(num_args == 2); st = mk_round(args[0], args[1], result); break;
|
||||
|
||||
case OP_FLOAT_EQ: SASSERT(num_args == 2); st = mk_float_eq(args[0], args[1], result); break;
|
||||
case OP_FLOAT_LT: SASSERT(num_args == 2); st = mk_lt(args[0], args[1], result); break;
|
||||
case OP_FLOAT_GT: SASSERT(num_args == 2); st = mk_gt(args[0], args[1], result); break;
|
||||
case OP_FLOAT_LE: SASSERT(num_args == 2); st = mk_le(args[0], args[1], result); break;
|
||||
case OP_FLOAT_GE: SASSERT(num_args == 2); st = mk_ge(args[0], args[1], result); break;
|
||||
case OP_FLOAT_IS_ZERO: SASSERT(num_args == 1); st = mk_is_zero(args[0], result); break;
|
||||
case OP_FLOAT_IS_NZERO: SASSERT(num_args == 1); st = mk_is_nzero(args[0], result); break;
|
||||
case OP_FLOAT_IS_PZERO: SASSERT(num_args == 1); st = mk_is_pzero(args[0], result); break;
|
||||
case OP_FLOAT_IS_SIGN_MINUS: SASSERT(num_args == 1); st = mk_is_sign_minus(args[0], result); break;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(f->get_num_parameters() == 2);
|
||||
SASSERT(f->get_parameter(0).is_int());
|
||||
SASSERT(f->get_parameter(1).is_int());
|
||||
unsigned ebits = f->get_parameter(0).get_int();
|
||||
unsigned sbits = f->get_parameter(1).get_int();
|
||||
|
||||
if (num_args == 2) {
|
||||
mpf_rounding_mode rm;
|
||||
if (!m_util.is_rm(args[0], rm))
|
||||
return BR_FAILED;
|
||||
|
||||
rational q;
|
||||
if (!m_util.au().is_numeral(args[1], q))
|
||||
return BR_FAILED;
|
||||
|
||||
mpf v;
|
||||
m_util.fm().set(v, ebits, sbits, rm, q.to_mpq());
|
||||
result = m_util.mk_value(v);
|
||||
m_util.fm().del(v);
|
||||
return BR_DONE;
|
||||
}
|
||||
else if (num_args == 3 &&
|
||||
m_util.is_rm(m().get_sort(args[0])) &&
|
||||
m_util.au().is_real(args[1]) &&
|
||||
m_util.au().is_int(args[2])) {
|
||||
|
||||
mpf_rounding_mode rm;
|
||||
if (!m_util.is_rm(args[0], rm))
|
||||
return BR_FAILED;
|
||||
|
||||
rational q;
|
||||
if (!m_util.au().is_numeral(args[1], q))
|
||||
return BR_FAILED;
|
||||
|
||||
rational e;
|
||||
if (!m_util.au().is_numeral(args[2], e))
|
||||
return BR_FAILED;
|
||||
|
||||
TRACE("fp_rewriter", tout << "q: " << q << ", e: " << e << "\n";);
|
||||
|
||||
mpf v;
|
||||
m_util.fm().set(v, ebits, sbits, rm, q.to_mpq(), e.to_mpq().numerator());
|
||||
result = m_util.mk_value(v);
|
||||
m_util.fm().del(v);
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
|
||||
mpf_rounding_mode rm;
|
||||
if (m_util.is_rm(arg1, rm)) {
|
||||
scoped_mpf v2(m_util.fm()), v3(m_util.fm());
|
||||
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) {
|
||||
scoped_mpf t(m_util.fm());
|
||||
m_util.fm().add(rm, v2, v3, t);
|
||||
result = m_util.mk_value(t);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
|
||||
// a - b = a + (-b)
|
||||
result = m_util.mk_add(arg1, arg2, m_util.mk_uminus(arg3));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
|
||||
mpf_rounding_mode rm;
|
||||
if (m_util.is_rm(arg1, rm)) {
|
||||
scoped_mpf v2(m_util.fm()), v3(m_util.fm());
|
||||
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) {
|
||||
scoped_mpf t(m_util.fm());
|
||||
m_util.fm().mul(rm, v2, v3, t);
|
||||
result = m_util.mk_value(t);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
|
||||
mpf_rounding_mode rm;
|
||||
if (m_util.is_rm(arg1, rm)) {
|
||||
scoped_mpf v2(m_util.fm()), v3(m_util.fm());
|
||||
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) {
|
||||
scoped_mpf t(m_util.fm());
|
||||
m_util.fm().div(rm, v2, v3, t);
|
||||
result = m_util.mk_value(t);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_uminus(expr * arg1, expr_ref & result) {
|
||||
if (m_util.is_nan(arg1)) {
|
||||
// -nan --> nan
|
||||
result = arg1;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_plus_inf(arg1)) {
|
||||
// - +oo --> -oo
|
||||
result = m_util.mk_minus_inf(m().get_sort(arg1));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_minus_inf(arg1)) {
|
||||
// - -oo -> +oo
|
||||
result = m_util.mk_plus_inf(m().get_sort(arg1));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_uminus(arg1)) {
|
||||
// - - a --> a
|
||||
result = to_app(arg1)->get_arg(0);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
scoped_mpf v1(m_util.fm());
|
||||
if (m_util.is_value(arg1, v1)) {
|
||||
m_util.fm().neg(v1);
|
||||
result = m_util.mk_value(v1);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
// TODO: more simplifications
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
|
||||
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
|
||||
scoped_mpf t(m_util.fm());
|
||||
m_util.fm().rem(v1, v2, t);
|
||||
result = m_util.mk_value(t);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_abs(expr * arg1, expr_ref & result) {
|
||||
if (m_util.is_nan(arg1)) {
|
||||
result = arg1;
|
||||
return BR_DONE;
|
||||
}
|
||||
sort * s = m().get_sort(arg1);
|
||||
result = m().mk_ite(m_util.mk_lt(arg1, m_util.mk_pzero(s)),
|
||||
m_util.mk_uminus(arg1),
|
||||
arg1);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (m_util.is_nan(arg1)) {
|
||||
result = arg2;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_nan(arg2)) {
|
||||
result = arg1;
|
||||
return BR_DONE;
|
||||
}
|
||||
// expand as using ite's
|
||||
result = m().mk_ite(mk_eq_nan(arg1),
|
||||
arg2,
|
||||
m().mk_ite(mk_eq_nan(arg2),
|
||||
arg1,
|
||||
m().mk_ite(m_util.mk_lt(arg1, arg2),
|
||||
arg1,
|
||||
arg2)));
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (m_util.is_nan(arg1)) {
|
||||
result = arg2;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_nan(arg2)) {
|
||||
result = arg1;
|
||||
return BR_DONE;
|
||||
}
|
||||
// expand as using ite's
|
||||
result = m().mk_ite(mk_eq_nan(arg1),
|
||||
arg2,
|
||||
m().mk_ite(mk_eq_nan(arg2),
|
||||
arg1,
|
||||
m().mk_ite(m_util.mk_gt(arg1, arg2),
|
||||
arg1,
|
||||
arg2)));
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) {
|
||||
mpf_rounding_mode rm;
|
||||
if (m_util.is_rm(arg1, rm)) {
|
||||
scoped_mpf v2(m_util.fm()), v3(m_util.fm()), v4(m_util.fm());
|
||||
if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3) && m_util.is_value(arg4, v4)) {
|
||||
scoped_mpf t(m_util.fm());
|
||||
m_util.fm().fused_mul_add(rm, v2, v3, v4, t);
|
||||
result = m_util.mk_value(t);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
mpf_rounding_mode rm;
|
||||
if (m_util.is_rm(arg1, rm)) {
|
||||
scoped_mpf v2(m_util.fm());
|
||||
if (m_util.is_value(arg2, v2)) {
|
||||
scoped_mpf t(m_util.fm());
|
||||
m_util.fm().sqrt(rm, v2, t);
|
||||
result = m_util.mk_value(t);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_round(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
mpf_rounding_mode rm;
|
||||
if (m_util.is_rm(arg1, rm)) {
|
||||
scoped_mpf v2(m_util.fm());
|
||||
if (m_util.is_value(arg2, v2)) {
|
||||
scoped_mpf t(m_util.fm());
|
||||
m_util.fm().round_to_integral(rm, v2, t);
|
||||
result = m_util.mk_value(t);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
// This the floating point theory ==
|
||||
br_status float_rewriter::mk_float_eq(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
|
||||
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
|
||||
result = (m_util.fm().eq(v1, v2)) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
// Return (= arg NaN)
|
||||
app * float_rewriter::mk_eq_nan(expr * arg) {
|
||||
return m().mk_eq(arg, m_util.mk_nan(m().get_sort(arg)));
|
||||
}
|
||||
|
||||
// Return (not (= arg NaN))
|
||||
app * float_rewriter::mk_neq_nan(expr * arg) {
|
||||
return m().mk_not(mk_eq_nan(arg));
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_minus_inf(arg1)) {
|
||||
// -oo < arg2 --> not(arg2 = -oo) and not(arg2 = NaN)
|
||||
result = m().mk_and(m().mk_not(m().mk_eq(arg2, arg1)), mk_neq_nan(arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
if (m_util.is_minus_inf(arg2)) {
|
||||
// arg1 < -oo --> false
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_plus_inf(arg1)) {
|
||||
// +oo < arg2 --> false
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_plus_inf(arg2)) {
|
||||
// arg1 < +oo --> not(arg1 = +oo) and not(arg1 = NaN)
|
||||
result = m().mk_and(m().mk_not(m().mk_eq(arg1, arg2)), mk_neq_nan(arg1));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
|
||||
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
|
||||
result = (m_util.fm().lt(v1, v2)) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
// TODO: more simplifications
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m_util.mk_lt(arg2, arg1);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_le(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) {
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
|
||||
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
|
||||
result = (m_util.fm().le(v1, v2)) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m_util.mk_le(arg2, arg1);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_is_zero(expr * arg1, expr_ref & result) {
|
||||
scoped_mpf v(m_util.fm());
|
||||
if (m_util.is_value(arg1, v)) {
|
||||
result = (m_util.fm().is_zero(v)) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_is_nzero(expr * arg1, expr_ref & result) {
|
||||
scoped_mpf v(m_util.fm());
|
||||
if (m_util.is_value(arg1, v)) {
|
||||
result = (m_util.fm().is_nzero(v)) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) {
|
||||
scoped_mpf v(m_util.fm());
|
||||
if (m_util.is_value(arg1, v)) {
|
||||
result = (m_util.fm().is_pzero(v)) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) {
|
||||
scoped_mpf v(m_util.fm());
|
||||
if (m_util.is_value(arg1, v)) {
|
||||
result = (m_util.fm().is_neg(v)) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
// This the SMT =
|
||||
br_status float_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
|
||||
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
|
||||
result = (v1 == v2) ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
72
src/ast/rewriter/float_rewriter.h
Normal file
72
src/ast/rewriter/float_rewriter.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
float_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for floating point numbers.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-02
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _FLOAT_REWRITER_H_
|
||||
#define _FLOAT_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter.h"
|
||||
#include"params.h"
|
||||
#include"float_decl_plugin.h"
|
||||
#include"mpf.h"
|
||||
|
||||
class float_rewriter {
|
||||
float_util m_util;
|
||||
mpf_manager m_fm;
|
||||
|
||||
app * mk_eq_nan(expr * arg);
|
||||
app * mk_neq_nan(expr * arg);
|
||||
|
||||
public:
|
||||
float_rewriter(ast_manager & m, params_ref const & p = params_ref());
|
||||
~float_rewriter();
|
||||
|
||||
ast_manager & m() const { return m_util.m(); }
|
||||
family_id get_fid() const { return m_util.get_fid(); }
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
|
||||
br_status mk_to_float(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
|
||||
br_status mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
|
||||
br_status mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
|
||||
br_status mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
|
||||
br_status mk_uminus(expr * arg1, expr_ref & result);
|
||||
br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_abs(expr * arg1, expr_ref & result);
|
||||
br_status mk_min(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_max(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result);
|
||||
br_status mk_sqrt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_round(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_float_eq(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_le(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_is_zero(expr * arg1, expr_ref & result);
|
||||
br_status mk_is_nzero(expr * arg1, expr_ref & result);
|
||||
br_status mk_is_pzero(expr * arg1, expr_ref & result);
|
||||
br_status mk_is_sign_minus(expr * arg1, expr_ref & result);
|
||||
};
|
||||
|
||||
#endif
|
||||
105
src/ast/rewriter/mk_simplified_app.cpp
Normal file
105
src/ast/rewriter/mk_simplified_app.cpp
Normal 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.
|
||||
}
|
||||
37
src/ast/rewriter/mk_simplified_app.h
Normal file
37
src/ast/rewriter/mk_simplified_app.h
Normal 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
|
||||
167
src/ast/rewriter/poly_rewriter.h
Normal file
167
src/ast/rewriter/poly_rewriter.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
poly_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for Polynomials.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-08
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _POLY_REWRITER_H_
|
||||
#define _POLY_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"obj_hashtable.h"
|
||||
#include"rewriter_types.h"
|
||||
#include"params.h"
|
||||
|
||||
template<typename Config>
|
||||
class poly_rewriter : public Config {
|
||||
public:
|
||||
static char const * g_ste_blowup_msg;
|
||||
protected:
|
||||
typedef typename Config::numeral numeral;
|
||||
sort * m_curr_sort;
|
||||
obj_map<expr, unsigned> m_expr2pos;
|
||||
bool m_flat;
|
||||
bool m_som;
|
||||
unsigned m_som_blowup;
|
||||
bool m_sort_sums;
|
||||
bool m_hoist_mul;
|
||||
bool m_hoist_cmul;
|
||||
|
||||
bool is_numeral(expr * n) const { return Config::is_numeral(n); }
|
||||
bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); }
|
||||
bool is_zero(expr * n) const { return Config::is_zero(n); }
|
||||
bool is_minus_one(expr * n) const { return Config::is_minus_one(n); }
|
||||
void normalize(numeral & c) { Config::normalize(c, m_curr_sort); }
|
||||
app * mk_numeral(numeral const & r) { return Config::mk_numeral(r, m_curr_sort); }
|
||||
decl_kind add_decl_kind() const { return Config::add_decl_kind(); }
|
||||
decl_kind mul_decl_kind() const { return Config::mul_decl_kind(); }
|
||||
bool use_power() const { return Config::use_power(); }
|
||||
decl_kind power_decl_kind() const { return Config::power_decl_kind(); }
|
||||
bool is_power(expr * t) const { return is_app_of(t, get_fid(), power_decl_kind()); }
|
||||
expr * get_power_body(expr * t, rational & k);
|
||||
struct mon_pw_lt; // functor used to sort monomial elements when use_power() == true
|
||||
|
||||
expr * mk_mul_app(unsigned num_args, expr * const * args);
|
||||
expr * mk_mul_app(numeral const & c, expr * arg);
|
||||
expr * mk_add_app(unsigned num_args, expr * const * args);
|
||||
|
||||
br_status mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
expr * get_power_product(expr * t);
|
||||
expr * get_power_product(expr * t, numeral & a);
|
||||
|
||||
br_status mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
void set_curr_sort(sort * s) { m_curr_sort = s; }
|
||||
|
||||
expr * const * get_monomials(expr * & t, unsigned & sz) {
|
||||
if (is_add(t)) {
|
||||
sz = to_app(t)->get_num_args();
|
||||
return to_app(t)->get_args();
|
||||
}
|
||||
else {
|
||||
sz = 1;
|
||||
return &t;
|
||||
}
|
||||
}
|
||||
|
||||
br_status cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result);
|
||||
|
||||
bool hoist_multiplication(expr_ref& som);
|
||||
expr* merge_muls(expr* x, expr* y);
|
||||
|
||||
struct hoist_cmul_lt;
|
||||
bool is_mul(expr * t, numeral & c, expr * & pp);
|
||||
void hoist_cmul(expr_ref_buffer & args);
|
||||
|
||||
public:
|
||||
poly_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
Config(m),
|
||||
m_curr_sort(0),
|
||||
m_sort_sums(false) {
|
||||
updt_params(p);
|
||||
SASSERT(!m_som || m_flat); // som of monomials form requires flattening to be enabled.
|
||||
SASSERT(!m_som || !m_hoist_mul); // som is mutually exclusive with hoisting multiplication.
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
ast_manager & m() const { return Config::m(); }
|
||||
family_id get_fid() const { return Config::get_fid(); }
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
void set_flat(bool f) { m_flat = f; }
|
||||
|
||||
void set_sort_sums(bool f) { m_sort_sums = f; }
|
||||
|
||||
bool is_add(expr * n) const { return is_app_of(n, get_fid(), add_decl_kind()); }
|
||||
bool is_mul(expr * n) const { return is_app_of(n, get_fid(), mul_decl_kind()); }
|
||||
bool is_add(func_decl * f) const { return is_decl_of(f, get_fid(), add_decl_kind()); }
|
||||
bool is_mul(func_decl * f) const { return is_decl_of(f, get_fid(), mul_decl_kind()); }
|
||||
|
||||
br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args > 0);
|
||||
if (num_args == 1) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
set_curr_sort(m().get_sort(args[0]));
|
||||
return m_flat ?
|
||||
mk_flat_mul_core(num_args, args, result) :
|
||||
mk_nflat_mul_core(num_args, args, result);
|
||||
}
|
||||
br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args > 0);
|
||||
if (num_args == 1) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
set_curr_sort(m().get_sort(args[0]));
|
||||
return m_flat ?
|
||||
mk_flat_add_core(num_args, args, result) :
|
||||
mk_nflat_add_core(num_args, args, result);
|
||||
}
|
||||
void mk_add(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_add_core(num_args, args, result) == BR_FAILED)
|
||||
result = mk_add_app(num_args, args);
|
||||
}
|
||||
void mk_add(expr* a1, expr* a2, expr_ref& result) {
|
||||
expr* args[2] = { a1, a2 };
|
||||
mk_add(2, args, result);
|
||||
}
|
||||
|
||||
void mk_mul(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_mul_core(num_args, args, result) == BR_FAILED)
|
||||
result = mk_mul_app(num_args, args);
|
||||
}
|
||||
void mk_mul(expr* a1, expr* a2, expr_ref& result) {
|
||||
expr* args[2] = { a1, a2 };
|
||||
mk_mul(2, args, result);
|
||||
}
|
||||
// The result of the following functions is never BR_FAILED
|
||||
br_status mk_uminus(expr * arg, expr_ref & result);
|
||||
br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
void mk_sub(expr* a1, expr* a2, expr_ref& result) {
|
||||
expr* args[2] = { a1, a2 };
|
||||
mk_sub(2, args, result);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
933
src/ast/rewriter/poly_rewriter_def.h
Normal file
933
src/ast/rewriter/poly_rewriter_def.h
Normal file
|
|
@ -0,0 +1,933 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
poly_rewriter_def.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for Polynomials.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-08
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"poly_rewriter.h"
|
||||
#include"ast_lt.h"
|
||||
#include"ast_ll_pp.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
template<typename Config>
|
||||
char const * poly_rewriter<Config>::g_ste_blowup_msg = "sum of monomials blowup";
|
||||
|
||||
|
||||
template<typename Config>
|
||||
void poly_rewriter<Config>::updt_params(params_ref const & p) {
|
||||
m_flat = p.get_bool(":flat", true);
|
||||
m_som = p.get_bool(":som", false);
|
||||
m_hoist_mul = p.get_bool(":hoist-mul", false);
|
||||
m_hoist_cmul = p.get_bool(":hoist-cmul", false);
|
||||
m_som_blowup = p.get_uint(":som-blowup", UINT_MAX);
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void poly_rewriter<Config>::get_param_descrs(param_descrs & r) {
|
||||
r.insert(":som", CPK_BOOL, "(default: false) put polynomials in som-of-monomials form.");
|
||||
r.insert(":som-blowup", CPK_UINT, "(default: infty) maximum number of monomials generated when putting a polynomial in sum-of-monomials normal form");
|
||||
r.insert(":hoist-mul", CPK_BOOL, "(default: false) hoist multiplication over summation to minimize number of multiplications");
|
||||
r.insert(":hoist-cmul", CPK_BOOL, "(default: false) hoist constant multiplication over summation to minimize number of multiplications");
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
expr * poly_rewriter<Config>::mk_add_app(unsigned num_args, expr * const * args) {
|
||||
switch (num_args) {
|
||||
case 0: return mk_numeral(numeral(0));
|
||||
case 1: return args[0];
|
||||
default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args);
|
||||
}
|
||||
}
|
||||
|
||||
// t = (^ x y) --> return x, and set k = y if k is an integer >= 1
|
||||
// Otherwise return t and set k = 1
|
||||
template<typename Config>
|
||||
expr * poly_rewriter<Config>::get_power_body(expr * t, rational & k) {
|
||||
if (!is_power(t)) {
|
||||
k = rational(1);
|
||||
return t;
|
||||
}
|
||||
if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) {
|
||||
return to_app(t)->get_arg(0);
|
||||
}
|
||||
k = rational(1);
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
expr * poly_rewriter<Config>::mk_mul_app(unsigned num_args, expr * const * args) {
|
||||
switch (num_args) {
|
||||
case 0:
|
||||
return mk_numeral(numeral(1));
|
||||
case 1:
|
||||
return args[0];
|
||||
default:
|
||||
if (use_power()) {
|
||||
rational k_prev;
|
||||
expr * prev = get_power_body(args[0], k_prev);
|
||||
rational k;
|
||||
ptr_buffer<expr> new_args;
|
||||
#define PUSH_POWER() { \
|
||||
if (k_prev.is_one()) { \
|
||||
new_args.push_back(prev); \
|
||||
} \
|
||||
else { \
|
||||
expr * pargs[2] = { prev, mk_numeral(k_prev) }; \
|
||||
new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \
|
||||
} \
|
||||
}
|
||||
|
||||
for (unsigned i = 1; i < num_args; i++) {
|
||||
expr * arg = get_power_body(args[i], k);
|
||||
if (arg == prev) {
|
||||
k_prev += k;
|
||||
}
|
||||
else {
|
||||
PUSH_POWER();
|
||||
prev = arg;
|
||||
k_prev = k;
|
||||
}
|
||||
}
|
||||
PUSH_POWER();
|
||||
SASSERT(new_args.size() > 0);
|
||||
if (new_args.size() == 1) {
|
||||
return new_args[0];
|
||||
}
|
||||
else {
|
||||
return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return m().mk_app(get_fid(), mul_decl_kind(), num_args, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
expr * poly_rewriter<Config>::mk_mul_app(numeral const & c, expr * arg) {
|
||||
if (c.is_one()) {
|
||||
return arg;
|
||||
}
|
||||
else {
|
||||
expr * new_args[2] = { mk_numeral(c), arg };
|
||||
return mk_mul_app(2, new_args);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
br_status poly_rewriter<Config>::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args >= 2);
|
||||
// only try to apply flattening if it is not already in one of the flat monomial forms
|
||||
// - (* c x)
|
||||
// - (* c (* x_1 ... x_n))
|
||||
if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) {
|
||||
unsigned i;
|
||||
for (i = 0; i < num_args; i++) {
|
||||
if (is_mul(args[i]))
|
||||
break;
|
||||
}
|
||||
if (i < num_args) {
|
||||
// input has nested monomials.
|
||||
ptr_buffer<expr> flat_args;
|
||||
// we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n)))
|
||||
ptr_buffer<expr> todo;
|
||||
flat_args.append(i, args);
|
||||
for (unsigned j = i; j < num_args; j++) {
|
||||
if (is_mul(args[j])) {
|
||||
todo.push_back(args[j]);
|
||||
while (!todo.empty()) {
|
||||
expr * curr = todo.back();
|
||||
todo.pop_back();
|
||||
if (is_mul(curr)) {
|
||||
unsigned k = to_app(curr)->get_num_args();
|
||||
while (k > 0) {
|
||||
--k;
|
||||
todo.push_back(to_app(curr)->get_arg(k));
|
||||
}
|
||||
}
|
||||
else {
|
||||
flat_args.push_back(curr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
flat_args.push_back(args[j]);
|
||||
}
|
||||
}
|
||||
TRACE("poly_rewriter",
|
||||
tout << "flat mul:\n";
|
||||
for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n";
|
||||
tout << "---->\n";
|
||||
for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";);
|
||||
br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result);
|
||||
if (st == BR_FAILED) {
|
||||
result = mk_mul_app(flat_args.size(), flat_args.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
}
|
||||
return mk_nflat_mul_core(num_args, args, result);
|
||||
}
|
||||
|
||||
|
||||
template<typename Config>
|
||||
struct poly_rewriter<Config>::mon_pw_lt {
|
||||
poly_rewriter<Config> & m_owner;
|
||||
mon_pw_lt(poly_rewriter<Config> & o):m_owner(o) {}
|
||||
|
||||
bool operator()(expr * n1, expr * n2) const {
|
||||
rational k;
|
||||
return lt(m_owner.get_power_body(n1, k),
|
||||
m_owner.get_power_body(n2, k));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Config>
|
||||
br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args >= 2);
|
||||
// cheap case
|
||||
numeral a;
|
||||
if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() &&
|
||||
(is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid()))
|
||||
return BR_FAILED;
|
||||
numeral c(1);
|
||||
unsigned num_coeffs = 0;
|
||||
unsigned num_add = 0;
|
||||
expr * var = 0;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (is_numeral(arg, a)) {
|
||||
num_coeffs++;
|
||||
c *= a;
|
||||
}
|
||||
else {
|
||||
var = arg;
|
||||
if (is_add(arg))
|
||||
num_add++;
|
||||
}
|
||||
}
|
||||
|
||||
normalize(c);
|
||||
// (* c_1 ... c_n) --> c_1*...*c_n
|
||||
if (num_coeffs == num_args) {
|
||||
result = mk_numeral(c);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
// (* s ... 0 ... r) --> 0
|
||||
if (c.is_zero()) {
|
||||
result = mk_numeral(c);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (num_coeffs == num_args - 1) {
|
||||
SASSERT(var != 0);
|
||||
// (* c_1 ... c_n x) --> x if c_1*...*c_n == 1
|
||||
if (c.is_one()) {
|
||||
result = var;
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
numeral c_prime;
|
||||
if (is_mul(var)) {
|
||||
// apply basic simplification even when flattening is not enabled.
|
||||
// (* c1 (* c2 x)) --> (* c1*c2 x)
|
||||
if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) {
|
||||
c *= c_prime;
|
||||
normalize(c);
|
||||
result = mk_mul_app(c, to_app(var)->get_arg(1));
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
else {
|
||||
// var is a power-product
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_add == 0 || m_hoist_cmul) {
|
||||
SASSERT(!is_add(var) || m_hoist_cmul);
|
||||
if (num_args == 2 && args[1] == var) {
|
||||
DEBUG_CODE({
|
||||
numeral c_prime;
|
||||
SASSERT(is_numeral(args[0], c_prime) && c == c_prime);
|
||||
});
|
||||
// it is already simplified
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
// (* c_1 ... c_n x) --> (* c_1*...*c_n x)
|
||||
result = mk_mul_app(c, var);
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
SASSERT(is_add(var));
|
||||
// (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m))
|
||||
ptr_buffer<expr> new_add_args;
|
||||
unsigned num = to_app(var)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i)));
|
||||
}
|
||||
result = mk_add_app(new_add_args.size(), new_add_args.c_ptr());
|
||||
TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(num_coeffs <= num_args - 2);
|
||||
|
||||
if (!m_som || num_add == 0) {
|
||||
ptr_buffer<expr> new_args;
|
||||
expr * prev = 0;
|
||||
bool ordered = true;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * curr = args[i];
|
||||
if (is_numeral(curr))
|
||||
continue;
|
||||
if (prev != 0 && lt(curr, prev))
|
||||
ordered = false;
|
||||
new_args.push_back(curr);
|
||||
prev = curr;
|
||||
}
|
||||
TRACE("poly_rewriter",
|
||||
for (unsigned i = 0; i < new_args.size(); i++) {
|
||||
if (i > 0)
|
||||
tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< ");
|
||||
tout << mk_ismt2_pp(new_args[i], m());
|
||||
}
|
||||
tout << "\nordered: " << ordered << "\n";);
|
||||
if (ordered && num_coeffs == 0 && !use_power())
|
||||
return BR_FAILED;
|
||||
if (!ordered) {
|
||||
if (use_power())
|
||||
std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this));
|
||||
else
|
||||
std::sort(new_args.begin(), new_args.end(), ast_to_lt());
|
||||
TRACE("poly_rewriter",
|
||||
tout << "after sorting:\n";
|
||||
for (unsigned i = 0; i < new_args.size(); i++) {
|
||||
if (i > 0)
|
||||
tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< ");
|
||||
tout << mk_ismt2_pp(new_args[i], m());
|
||||
}
|
||||
tout << "\n";);
|
||||
}
|
||||
SASSERT(new_args.size() >= 2);
|
||||
result = mk_mul_app(new_args.size(), new_args.c_ptr());
|
||||
result = mk_mul_app(c, result);
|
||||
TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
SASSERT(m_som && num_add > 0);
|
||||
|
||||
sbuffer<unsigned> szs;
|
||||
sbuffer<unsigned> it;
|
||||
sbuffer<expr **> sums;
|
||||
for (unsigned i = 0; i < num_args; i ++) {
|
||||
it.push_back(0);
|
||||
expr * arg = args[i];
|
||||
if (is_add(arg)) {
|
||||
sums.push_back(const_cast<expr**>(to_app(arg)->get_args()));
|
||||
szs.push_back(to_app(arg)->get_num_args());
|
||||
}
|
||||
else {
|
||||
sums.push_back(const_cast<expr**>(args + i));
|
||||
szs.push_back(1);
|
||||
SASSERT(sums.back()[0] == arg);
|
||||
}
|
||||
}
|
||||
expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception
|
||||
ptr_buffer<expr> m_args;
|
||||
TRACE("som", tout << "starting som...\n";);
|
||||
do {
|
||||
TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " ";
|
||||
tout << "\n";);
|
||||
if (sum.size() > m_som_blowup)
|
||||
throw rewriter_exception(g_ste_blowup_msg);
|
||||
m_args.reset();
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * const * v = sums[i];
|
||||
expr * arg = v[it[i]];
|
||||
m_args.push_back(arg);
|
||||
}
|
||||
sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr()));
|
||||
}
|
||||
while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr()));
|
||||
result = mk_add_app(sum.size(), sum.c_ptr());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
br_status poly_rewriter<Config>::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
unsigned i;
|
||||
for (i = 0; i < num_args; i++) {
|
||||
if (is_add(args[i]))
|
||||
break;
|
||||
}
|
||||
if (i < num_args) {
|
||||
// has nested ADDs
|
||||
ptr_buffer<expr> flat_args;
|
||||
flat_args.append(i, args);
|
||||
for (; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
// Remark: all rewrites are depth 1.
|
||||
if (is_add(arg)) {
|
||||
unsigned num = to_app(arg)->get_num_args();
|
||||
for (unsigned j = 0; j < num; j++)
|
||||
flat_args.push_back(to_app(arg)->get_arg(j));
|
||||
}
|
||||
else {
|
||||
flat_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result);
|
||||
if (st == BR_FAILED) {
|
||||
result = mk_add_app(flat_args.size(), flat_args.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
return mk_nflat_add_core(num_args, args, result);
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
inline expr * poly_rewriter<Config>::get_power_product(expr * t) {
|
||||
if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0)))
|
||||
return to_app(t)->get_arg(1);
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
inline expr * poly_rewriter<Config>::get_power_product(expr * t, numeral & a) {
|
||||
if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a))
|
||||
return to_app(t)->get_arg(1);
|
||||
a = numeral(1);
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
bool poly_rewriter<Config>::is_mul(expr * t, numeral & c, expr * & pp) {
|
||||
if (!is_mul(t) || to_app(t)->get_num_args() != 2)
|
||||
return false;
|
||||
if (!is_numeral(to_app(t)->get_arg(0), c))
|
||||
return false;
|
||||
pp = to_app(t)->get_arg(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
struct poly_rewriter<Config>::hoist_cmul_lt {
|
||||
poly_rewriter<Config> & m_r;
|
||||
hoist_cmul_lt(poly_rewriter<Config> & r):m_r(r) {}
|
||||
|
||||
bool operator()(expr * t1, expr * t2) const {
|
||||
expr * pp1, * pp2;
|
||||
numeral c1, c2;
|
||||
bool is_mul1 = m_r.is_mul(t1, c1, pp1);
|
||||
bool is_mul2 = m_r.is_mul(t2, c2, pp2);
|
||||
if (!is_mul1 && is_mul2)
|
||||
return true;
|
||||
if (is_mul1 && !is_mul2)
|
||||
return false;
|
||||
if (!is_mul1 && !is_mul2)
|
||||
return t1->get_id() < t2->get_id();
|
||||
if (c1 < c2)
|
||||
return true;
|
||||
if (c1 > c2)
|
||||
return false;
|
||||
return pp1->get_id() < pp2->get_id();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Config>
|
||||
void poly_rewriter<Config>::hoist_cmul(expr_ref_buffer & args) {
|
||||
unsigned sz = args.size();
|
||||
std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this));
|
||||
numeral c, c_prime;
|
||||
ptr_buffer<expr> pps;
|
||||
expr * pp, * pp_prime;
|
||||
unsigned j = 0;
|
||||
unsigned i = 0;
|
||||
while (i < sz) {
|
||||
expr * mon = args[i];
|
||||
if (is_mul(mon, c, pp) && i < sz - 1) {
|
||||
expr * mon_prime = args[i+1];
|
||||
if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) {
|
||||
// found target
|
||||
pps.reset();
|
||||
pps.push_back(pp);
|
||||
pps.push_back(pp_prime);
|
||||
i += 2;
|
||||
while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) {
|
||||
pps.push_back(pp_prime);
|
||||
i++;
|
||||
}
|
||||
SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime);
|
||||
expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) };
|
||||
args.set(j, mk_mul_app(2, mul_args));
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
args.set(j, mon);
|
||||
j++;
|
||||
i++;
|
||||
}
|
||||
args.resize(j);
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args >= 2);
|
||||
numeral c;
|
||||
unsigned num_coeffs = 0;
|
||||
numeral a;
|
||||
expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args
|
||||
expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once
|
||||
bool has_multiple = false;
|
||||
expr * prev = 0;
|
||||
bool ordered = true;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (is_numeral(arg, a)) {
|
||||
num_coeffs++;
|
||||
c += a;
|
||||
}
|
||||
else {
|
||||
// arg is not a numeral
|
||||
if (m_sort_sums && ordered) {
|
||||
if (prev != 0 && lt(arg, prev))
|
||||
ordered = false;
|
||||
prev = arg;
|
||||
}
|
||||
}
|
||||
|
||||
arg = get_power_product(arg);
|
||||
if (visited.is_marked(arg)) {
|
||||
multiple.mark(arg);
|
||||
has_multiple = true;
|
||||
}
|
||||
else {
|
||||
visited.mark(arg);
|
||||
}
|
||||
}
|
||||
normalize(c);
|
||||
SASSERT(m_sort_sums || ordered);
|
||||
TRACE("sort_sums",
|
||||
tout << "ordered: " << ordered << "\n";
|
||||
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";);
|
||||
|
||||
if (has_multiple) {
|
||||
// expensive case
|
||||
buffer<numeral> coeffs;
|
||||
m_expr2pos.reset();
|
||||
// compute the coefficient of power products that occur multiple times.
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (is_numeral(arg))
|
||||
continue;
|
||||
expr * pp = get_power_product(arg, a);
|
||||
if (!multiple.is_marked(pp))
|
||||
continue;
|
||||
unsigned pos;
|
||||
if (m_expr2pos.find(pp, pos)) {
|
||||
coeffs[pos] += a;
|
||||
}
|
||||
else {
|
||||
m_expr2pos.insert(pp, coeffs.size());
|
||||
coeffs.push_back(a);
|
||||
}
|
||||
}
|
||||
expr_ref_buffer new_args(m());
|
||||
if (!c.is_zero()) {
|
||||
new_args.push_back(mk_numeral(c));
|
||||
}
|
||||
// copy power products with non zero coefficients to new_args
|
||||
visited.reset();
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (is_numeral(arg))
|
||||
continue;
|
||||
expr * pp = get_power_product(arg);
|
||||
if (!multiple.is_marked(pp)) {
|
||||
new_args.push_back(arg);
|
||||
}
|
||||
else if (!visited.is_marked(pp)) {
|
||||
visited.mark(pp);
|
||||
unsigned pos = UINT_MAX;
|
||||
m_expr2pos.find(pp, pos);
|
||||
SASSERT(pos != UINT_MAX);
|
||||
a = coeffs[pos];
|
||||
normalize(a);
|
||||
if (!a.is_zero())
|
||||
new_args.push_back(mk_mul_app(a, pp));
|
||||
}
|
||||
}
|
||||
if (m_hoist_cmul) {
|
||||
hoist_cmul(new_args);
|
||||
}
|
||||
else if (m_sort_sums) {
|
||||
TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";);
|
||||
if (c.is_zero())
|
||||
std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt());
|
||||
else
|
||||
std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt());
|
||||
}
|
||||
result = mk_add_app(new_args.size(), new_args.c_ptr());
|
||||
if (hoist_multiplication(result)) {
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
SASSERT(!has_multiple);
|
||||
if (ordered && !m_hoist_mul && !m_hoist_cmul) {
|
||||
if (num_coeffs == 0)
|
||||
return BR_FAILED;
|
||||
if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero())
|
||||
return BR_FAILED;
|
||||
}
|
||||
expr_ref_buffer new_args(m());
|
||||
if (!c.is_zero())
|
||||
new_args.push_back(mk_numeral(c));
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (is_numeral(arg))
|
||||
continue;
|
||||
new_args.push_back(arg);
|
||||
}
|
||||
if (m_hoist_cmul) {
|
||||
hoist_cmul(new_args);
|
||||
}
|
||||
else if (!ordered) {
|
||||
if (c.is_zero())
|
||||
std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt());
|
||||
else
|
||||
std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt());
|
||||
}
|
||||
result = mk_add_app(new_args.size(), new_args.c_ptr());
|
||||
if (hoist_multiplication(result)) {
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename Config>
|
||||
br_status poly_rewriter<Config>::mk_uminus(expr * arg, expr_ref & result) {
|
||||
numeral a;
|
||||
set_curr_sort(m().get_sort(arg));
|
||||
if (is_numeral(arg, a)) {
|
||||
a.neg();
|
||||
normalize(a);
|
||||
result = mk_numeral(a);
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
result = mk_mul_app(numeral(-1), arg);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
br_status poly_rewriter<Config>::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num_args > 0);
|
||||
if (num_args == 1) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
set_curr_sort(m().get_sort(args[0]));
|
||||
expr * minus_one = mk_numeral(numeral(-1));
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(args[0]);
|
||||
for (unsigned i = 1; i < num_args; i++) {
|
||||
expr * aux_args[2] = { minus_one, args[i] };
|
||||
new_args.push_back(mk_mul_app(2, aux_args));
|
||||
}
|
||||
result = mk_add_app(new_args.size(), new_args.c_ptr());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Cancel/Combine monomials that occur is the left and right hand sides.
|
||||
|
||||
\remark If move = true, then all non-constant monomials are moved to the left-hand-side.
|
||||
*/
|
||||
template<typename Config>
|
||||
br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) {
|
||||
set_curr_sort(m().get_sort(lhs));
|
||||
unsigned lhs_sz;
|
||||
expr * const * lhs_monomials = get_monomials(lhs, lhs_sz);
|
||||
unsigned rhs_sz;
|
||||
expr * const * rhs_monomials = get_monomials(rhs, rhs_sz);
|
||||
|
||||
expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs
|
||||
expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once
|
||||
bool has_multiple = false;
|
||||
|
||||
numeral c(0);
|
||||
numeral a;
|
||||
unsigned num_coeffs = 0;
|
||||
|
||||
for (unsigned i = 0; i < lhs_sz; i++) {
|
||||
expr * arg = lhs_monomials[i];
|
||||
if (is_numeral(arg, a)) {
|
||||
c += a;
|
||||
num_coeffs++;
|
||||
}
|
||||
else {
|
||||
visited.mark(get_power_product(arg));
|
||||
}
|
||||
}
|
||||
|
||||
if (move && num_coeffs == 0 && is_numeral(rhs))
|
||||
return BR_FAILED;
|
||||
|
||||
for (unsigned i = 0; i < rhs_sz; i++) {
|
||||
expr * arg = rhs_monomials[i];
|
||||
if (is_numeral(arg, a)) {
|
||||
c -= a;
|
||||
num_coeffs++;
|
||||
}
|
||||
else {
|
||||
expr * pp = get_power_product(arg);
|
||||
if (visited.is_marked(pp)) {
|
||||
multiple.mark(pp);
|
||||
has_multiple = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
normalize(c);
|
||||
|
||||
if (!has_multiple && num_coeffs <= 1) {
|
||||
if (move) {
|
||||
if (is_numeral(rhs))
|
||||
return BR_FAILED;
|
||||
}
|
||||
else {
|
||||
if (num_coeffs == 0 || is_numeral(rhs))
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
buffer<numeral> coeffs;
|
||||
m_expr2pos.reset();
|
||||
for (unsigned i = 0; i < lhs_sz; i++) {
|
||||
expr * arg = lhs_monomials[i];
|
||||
if (is_numeral(arg))
|
||||
continue;
|
||||
expr * pp = get_power_product(arg, a);
|
||||
if (!multiple.is_marked(pp))
|
||||
continue;
|
||||
unsigned pos;
|
||||
if (m_expr2pos.find(pp, pos)) {
|
||||
coeffs[pos] += a;
|
||||
}
|
||||
else {
|
||||
m_expr2pos.insert(pp, coeffs.size());
|
||||
coeffs.push_back(a);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < rhs_sz; i++) {
|
||||
expr * arg = rhs_monomials[i];
|
||||
if (is_numeral(arg))
|
||||
continue;
|
||||
expr * pp = get_power_product(arg, a);
|
||||
if (!multiple.is_marked(pp))
|
||||
continue;
|
||||
unsigned pos = UINT_MAX;
|
||||
m_expr2pos.find(pp, pos);
|
||||
SASSERT(pos != UINT_MAX);
|
||||
coeffs[pos] -= a;
|
||||
}
|
||||
|
||||
|
||||
ptr_buffer<expr> new_lhs_monomials;
|
||||
new_lhs_monomials.push_back(0); // save space for coefficient if needed
|
||||
// copy power products with non zero coefficients to new_lhs_monomials
|
||||
visited.reset();
|
||||
for (unsigned i = 0; i < lhs_sz; i++) {
|
||||
expr * arg = lhs_monomials[i];
|
||||
if (is_numeral(arg))
|
||||
continue;
|
||||
expr * pp = get_power_product(arg);
|
||||
if (!multiple.is_marked(pp)) {
|
||||
new_lhs_monomials.push_back(arg);
|
||||
}
|
||||
else if (!visited.is_marked(pp)) {
|
||||
visited.mark(pp);
|
||||
unsigned pos = UINT_MAX;
|
||||
m_expr2pos.find(pp, pos);
|
||||
SASSERT(pos != UINT_MAX);
|
||||
a = coeffs[pos];
|
||||
if (!a.is_zero())
|
||||
new_lhs_monomials.push_back(mk_mul_app(a, pp));
|
||||
}
|
||||
}
|
||||
|
||||
ptr_buffer<expr> new_rhs_monomials;
|
||||
new_rhs_monomials.push_back(0); // save space for coefficient if needed
|
||||
for (unsigned i = 0; i < rhs_sz; i++) {
|
||||
expr * arg = rhs_monomials[i];
|
||||
if (is_numeral(arg))
|
||||
continue;
|
||||
expr * pp = get_power_product(arg, a);
|
||||
if (!multiple.is_marked(pp)) {
|
||||
if (move) {
|
||||
if (!a.is_zero()) {
|
||||
if (a.is_minus_one()) {
|
||||
new_lhs_monomials.push_back(pp);
|
||||
}
|
||||
else {
|
||||
a.neg();
|
||||
SASSERT(!a.is_one());
|
||||
expr * args[2] = { mk_numeral(a), pp };
|
||||
new_lhs_monomials.push_back(mk_mul_app(2, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
new_rhs_monomials.push_back(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool c_at_rhs = false;
|
||||
if (move) {
|
||||
if (m_sort_sums) {
|
||||
// + 1 to skip coefficient
|
||||
std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt());
|
||||
}
|
||||
c_at_rhs = true;
|
||||
}
|
||||
else if (new_rhs_monomials.size() == 1) { // rhs is empty
|
||||
c_at_rhs = true;
|
||||
}
|
||||
else if (new_lhs_monomials.size() > 1) {
|
||||
c_at_rhs = true;
|
||||
}
|
||||
|
||||
if (c_at_rhs) {
|
||||
c.neg();
|
||||
normalize(c);
|
||||
new_rhs_monomials[0] = mk_numeral(c);
|
||||
lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1);
|
||||
rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr());
|
||||
}
|
||||
else {
|
||||
new_lhs_monomials[0] = mk_numeral(c);
|
||||
lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr());
|
||||
rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
#define TO_BUFFER(_tester_, _buffer_, _e_) \
|
||||
_buffer_.push_back(_e_); \
|
||||
for (unsigned _i = 0; _i < _buffer_.size(); ) { \
|
||||
expr* _e = _buffer_[_i]; \
|
||||
if (_tester_(_e)) { \
|
||||
app* a = to_app(_e); \
|
||||
_buffer_[_i] = a->get_arg(0); \
|
||||
for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \
|
||||
_buffer_.push_back(a->get_arg(_j)); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
++_i; \
|
||||
} \
|
||||
} \
|
||||
|
||||
template<typename Config>
|
||||
bool poly_rewriter<Config>::hoist_multiplication(expr_ref& som) {
|
||||
if (!m_hoist_mul) {
|
||||
return false;
|
||||
}
|
||||
ptr_buffer<expr> adds, muls;
|
||||
TO_BUFFER(is_add, adds, som);
|
||||
buffer<bool> valid(adds.size(), true);
|
||||
obj_map<expr, unsigned> mul_map;
|
||||
unsigned j;
|
||||
bool change = false;
|
||||
for (unsigned k = 0; k < adds.size(); ++k) {
|
||||
expr* e = adds[k];
|
||||
muls.reset();
|
||||
TO_BUFFER(is_mul, muls, e);
|
||||
for (unsigned i = 0; i < muls.size(); ++i) {
|
||||
e = muls[i];
|
||||
if (is_numeral(e)) {
|
||||
continue;
|
||||
}
|
||||
if (mul_map.find(e, j) && valid[j] && j != k) {
|
||||
m_curr_sort = m().get_sort(adds[k]);
|
||||
adds[j] = merge_muls(adds[j], adds[k]);
|
||||
adds[k] = mk_numeral(rational(0));
|
||||
valid[j] = false;
|
||||
valid[k] = false;
|
||||
change = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
mul_map.insert(e, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!change) {
|
||||
return false;
|
||||
}
|
||||
|
||||
som = mk_add_app(adds.size(), adds.c_ptr());
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
expr* poly_rewriter<Config>::merge_muls(expr* x, expr* y) {
|
||||
ptr_buffer<expr> m1, m2;
|
||||
TO_BUFFER(is_mul, m1, x);
|
||||
TO_BUFFER(is_mul, m2, y);
|
||||
unsigned k = 0;
|
||||
for (unsigned i = 0; i < m1.size(); ++i) {
|
||||
x = m1[i];
|
||||
bool found = false;
|
||||
unsigned j;
|
||||
for (j = k; j < m2.size(); ++j) {
|
||||
found = m2[j] == x;
|
||||
if (found) break;
|
||||
}
|
||||
if (found) {
|
||||
std::swap(m1[i],m1[k]);
|
||||
std::swap(m2[j],m2[k]);
|
||||
++k;
|
||||
}
|
||||
}
|
||||
m_curr_sort = m().get_sort(x);
|
||||
SASSERT(k > 0);
|
||||
SASSERT(m1.size() >= k);
|
||||
SASSERT(m2.size() >= k);
|
||||
expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k),
|
||||
mk_mul_app(m2.size()-k, m2.c_ptr()+k) };
|
||||
if (k == m1.size()) {
|
||||
m1.push_back(0);
|
||||
}
|
||||
m1[k] = mk_add_app(2, args);
|
||||
return mk_mul_app(k+1, m1.c_ptr());
|
||||
}
|
||||
239
src/ast/rewriter/quant_hoist.cpp
Normal file
239
src/ast/rewriter/quant_hoist.cpp
Normal 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);
|
||||
}
|
||||
|
||||
64
src/ast/rewriter/quant_hoist.h
Normal file
64
src/ast/rewriter/quant_hoist.h
Normal 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
|
||||
402
src/ast/rewriter/rewriter.cpp
Normal file
402
src/ast/rewriter/rewriter.cpp
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Lean and mean rewriter
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-03-31
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"rewriter_def.h"
|
||||
#include"ast_ll_pp.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
void rewriter_core::init_cache_stack() {
|
||||
SASSERT(m_cache_stack.empty());
|
||||
m_cache = alloc(cache, m());
|
||||
m_cache_stack.push_back(m_cache);
|
||||
if (m_proof_gen) {
|
||||
SASSERT(m_cache_pr_stack.empty());
|
||||
m_cache_pr = alloc(cache, m());
|
||||
m_cache_pr_stack.push_back(m_cache_pr);
|
||||
}
|
||||
}
|
||||
|
||||
void rewriter_core::del_cache_stack() {
|
||||
std::for_each(m_cache_stack.begin(), m_cache_stack.end(), delete_proc<cache>());
|
||||
m_cache_stack.finalize();
|
||||
m_cache = 0;
|
||||
if (m_proof_gen) {
|
||||
std::for_each(m_cache_pr_stack.begin(), m_cache_pr_stack.end(), delete_proc<cache>());
|
||||
m_cache_pr_stack.finalize();
|
||||
m_cache_pr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void rewriter_core::cache_result(expr * k, expr * v) {
|
||||
#if 0
|
||||
// trace for tracking cache usage
|
||||
verbose_stream() << "1 " << k->get_id() << std::endl;
|
||||
#endif
|
||||
SASSERT(!m_proof_gen);
|
||||
|
||||
TRACE("rewriter_cache_result", tout << mk_ismt2_pp(k, m()) << "\n--->\n" << mk_ismt2_pp(v, m()) << "\n";);
|
||||
|
||||
m_cache->insert(k, v);
|
||||
#if 0
|
||||
static unsigned num_cached = 0;
|
||||
num_cached ++;
|
||||
if (num_cached % 100000 == 0)
|
||||
verbose_stream() << "[rewriter] :num-cached " << num_cached << " :capacity " << m_cache->capacity() << " :size " << m_cache->size()
|
||||
<< " :frame-stack-size " << m_frame_stack.size() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void rewriter_core::cache_result(expr * k, expr * v, proof * pr) {
|
||||
m_cache->insert(k, v);
|
||||
SASSERT(m_proof_gen);
|
||||
m_cache_pr->insert(k, pr);
|
||||
}
|
||||
|
||||
unsigned rewriter_core::get_cache_size() const {
|
||||
return m_cache->size();
|
||||
}
|
||||
|
||||
void rewriter_core::reset_cache() {
|
||||
m_cache = m_cache_stack[0];
|
||||
m_cache->reset();
|
||||
if (m_proof_gen) {
|
||||
m_cache_pr = m_cache_pr_stack[0];
|
||||
m_cache_pr->reset();
|
||||
}
|
||||
}
|
||||
|
||||
// free memory allocated by the rewriter
|
||||
void rewriter_core::free_memory() {
|
||||
del_cache_stack();
|
||||
m_frame_stack.finalize();
|
||||
m_result_stack.finalize();
|
||||
m_scopes.finalize();
|
||||
}
|
||||
|
||||
void rewriter_core::begin_scope() {
|
||||
m_scopes.push_back(scope(m_root, m_num_qvars));
|
||||
unsigned lvl = m_scopes.size();
|
||||
SASSERT(lvl <= m_cache_stack.size());
|
||||
SASSERT(!m_proof_gen || m_cache_pr_stack.size() == m_cache_stack.size());
|
||||
if (lvl == m_cache_stack.size()) {
|
||||
m_cache_stack.push_back(alloc(cache, m()));
|
||||
if (m_proof_gen)
|
||||
m_cache_pr_stack.push_back(alloc(cache, m()));
|
||||
}
|
||||
m_cache = m_cache_stack[lvl];
|
||||
m_cache->reset();
|
||||
SASSERT(m_cache->empty());
|
||||
if (m_proof_gen) {
|
||||
m_cache_pr = m_cache_pr_stack[lvl];
|
||||
m_cache_pr->reset();
|
||||
SASSERT(m_cache_pr->empty());
|
||||
}
|
||||
}
|
||||
|
||||
void rewriter_core::end_scope() {
|
||||
m_cache->reset();
|
||||
if (m_proof_gen)
|
||||
m_cache_pr->reset();
|
||||
scope & s = m_scopes.back();
|
||||
m_root = s.m_old_root;
|
||||
m_num_qvars = s.m_old_num_qvars;
|
||||
m_scopes.pop_back();
|
||||
unsigned new_lvl = m_scopes.size();
|
||||
m_cache = m_cache_stack[new_lvl];
|
||||
if (m_proof_gen)
|
||||
m_cache_pr = m_cache_pr_stack[new_lvl];
|
||||
}
|
||||
|
||||
bool rewriter_core::is_child_of_top_frame(expr * t) const {
|
||||
if (m_frame_stack.empty())
|
||||
return true;
|
||||
frame const & fr = m_frame_stack.back();
|
||||
expr * parent = fr.m_curr;
|
||||
unsigned num;
|
||||
switch (parent->get_kind()) {
|
||||
case AST_APP:
|
||||
num = to_app(parent)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
if (to_app(parent)->get_arg(i) == t)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case AST_QUANTIFIER:
|
||||
num = to_quantifier(parent)->get_num_children();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
if (to_quantifier(parent)->get_child(i) == t)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Eliminate (implicit) reflexivity proofs from m_result_pr_stack starting at position spos.
|
||||
The implicit reflexivity proof is 0.
|
||||
*/
|
||||
void rewriter_core::elim_reflex_prs(unsigned spos) {
|
||||
SASSERT(m_proof_gen);
|
||||
unsigned sz = m_result_pr_stack.size();
|
||||
SASSERT(spos <= sz);
|
||||
unsigned j = spos;
|
||||
for (unsigned i = spos; i < sz; i++) {
|
||||
proof * pr = m_result_pr_stack.get(i);
|
||||
if (pr != 0) {
|
||||
if (i != j)
|
||||
m_result_pr_stack.set(j, pr);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
m_result_pr_stack.shrink(j);
|
||||
}
|
||||
|
||||
rewriter_core::rewriter_core(ast_manager & m, bool proof_gen):
|
||||
m_manager(m),
|
||||
m_proof_gen(proof_gen),
|
||||
m_result_stack(m),
|
||||
m_result_pr_stack(m),
|
||||
m_num_qvars(0) {
|
||||
init_cache_stack();
|
||||
}
|
||||
|
||||
rewriter_core::~rewriter_core() {
|
||||
del_cache_stack();
|
||||
}
|
||||
|
||||
// reset rewriter (macro definitions are not erased)
|
||||
void rewriter_core::reset() {
|
||||
SASSERT(!m_cache_stack.empty());
|
||||
reset_cache();
|
||||
m_frame_stack.reset();
|
||||
m_result_stack.reset();
|
||||
if (m_proof_gen)
|
||||
m_result_pr_stack.reset();
|
||||
m_root = 0;
|
||||
m_num_qvars = 0;
|
||||
m_scopes.reset();
|
||||
}
|
||||
|
||||
// free memory & reset (macro definitions are not erased)
|
||||
void rewriter_core::cleanup() {
|
||||
free_memory();
|
||||
init_cache_stack();
|
||||
m_root = 0;
|
||||
m_num_qvars = 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _TRACE
|
||||
void rewriter_core::display_stack(std::ostream & out, unsigned pp_depth) {
|
||||
svector<frame>::iterator it = m_frame_stack.begin();
|
||||
svector<frame>::iterator end = m_frame_stack.end();
|
||||
for (; it != end; ++it) {
|
||||
out << mk_bounded_pp(it->m_curr, m(), pp_depth) << "\n";
|
||||
out << "state: " << it->m_state << "\n";
|
||||
out << "cache: " << it->m_cache_result << ", new_child: " << it->m_new_child << ", max-depth: " << it->m_max_depth << ", i: " << it->m_i << "\n";
|
||||
out << "------------------\n";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool var_shifter_core::visit(expr * t) {
|
||||
if (is_ground(t)) {
|
||||
m_result_stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
bool c = must_cache(t);
|
||||
if (c) {
|
||||
expr * r = get_cached(t);
|
||||
if (r) {
|
||||
m_result_stack.push_back(r);
|
||||
set_new_child_flag(t, r);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
switch (t->get_kind()) {
|
||||
case AST_APP:
|
||||
SASSERT(to_app(t)->get_num_args() > 0);
|
||||
push_frame(t, c);
|
||||
return false;
|
||||
case AST_VAR:
|
||||
process_var(to_var(t));
|
||||
return true;
|
||||
case AST_QUANTIFIER:
|
||||
push_frame(t, c);
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void var_shifter_core::process_app(app * t, frame & fr) {
|
||||
unsigned num_args = t->get_num_args();
|
||||
while (fr.m_i < num_args) {
|
||||
expr * arg = t->get_arg(fr.m_i);
|
||||
fr.m_i++;
|
||||
if (!visit(arg))
|
||||
return;
|
||||
}
|
||||
SASSERT(fr.m_spos + num_args == m_result_stack.size());
|
||||
expr * new_t;
|
||||
if (fr.m_new_child) {
|
||||
expr * const * new_args = m_result_stack.c_ptr() + fr.m_spos;
|
||||
new_t = m().mk_app(t->get_decl(), num_args, new_args);
|
||||
}
|
||||
else {
|
||||
new_t = t;
|
||||
}
|
||||
m_result_stack.shrink(fr.m_spos);
|
||||
m_result_stack.push_back(new_t);
|
||||
m_frame_stack.pop_back();
|
||||
set_new_child_flag(t, new_t);
|
||||
if (fr.m_cache_result)
|
||||
cache_result(t, new_t);
|
||||
}
|
||||
|
||||
void var_shifter_core::process_quantifier(quantifier * q, frame & fr) {
|
||||
if (fr.m_i == 0) {
|
||||
begin_scope();
|
||||
m_num_qvars += q->get_num_decls();
|
||||
m_root = q->get_expr();
|
||||
}
|
||||
unsigned num_children = q->get_num_children();
|
||||
while (fr.m_i < num_children) {
|
||||
expr * child = q->get_child(fr.m_i);
|
||||
fr.m_i++;
|
||||
if (!visit(child))
|
||||
return;
|
||||
}
|
||||
SASSERT(fr.m_spos + num_children == m_result_stack.size());
|
||||
expr * new_q;
|
||||
if (fr.m_new_child) {
|
||||
expr * const * it = m_result_stack.c_ptr() + fr.m_spos;
|
||||
expr * new_expr = *it;
|
||||
++it;
|
||||
expr * const * new_pats = it;
|
||||
expr * const * new_no_pats = new_pats + q->get_num_patterns();
|
||||
new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_expr);
|
||||
}
|
||||
else {
|
||||
new_q = q;
|
||||
}
|
||||
m_result_stack.shrink(fr.m_spos);
|
||||
m_result_stack.push_back(new_q);
|
||||
m_frame_stack.pop_back();
|
||||
set_new_child_flag(q, new_q);
|
||||
end_scope();
|
||||
if (fr.m_cache_result)
|
||||
cache_result(q, new_q);
|
||||
}
|
||||
|
||||
void var_shifter_core::main_loop(expr * t, expr_ref & r) {
|
||||
SASSERT(m_cache == m_cache_stack[0]);
|
||||
SASSERT(m_frame_stack.empty());
|
||||
SASSERT(m_result_stack.empty());
|
||||
m_root = t;
|
||||
|
||||
if (visit(t)) {
|
||||
r = m_result_stack.back();
|
||||
m_result_stack.pop_back();
|
||||
SASSERT(m_result_stack.empty());
|
||||
return;
|
||||
}
|
||||
SASSERT(!m_frame_stack.empty());
|
||||
while (!m_frame_stack.empty()) {
|
||||
frame & fr = m_frame_stack.back();
|
||||
expr * t = fr.m_curr;
|
||||
if (fr.m_i == 0 && fr.m_cache_result) {
|
||||
expr * r = get_cached(t);
|
||||
if (r) {
|
||||
m_result_stack.push_back(r);
|
||||
m_frame_stack.pop_back();
|
||||
set_new_child_flag(t, r);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (t->get_kind()) {
|
||||
case AST_APP:
|
||||
process_app(to_app(t), fr);
|
||||
break;
|
||||
case AST_QUANTIFIER:
|
||||
process_quantifier(to_quantifier(t), fr);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
r = m_result_stack.back();
|
||||
m_result_stack.pop_back();
|
||||
SASSERT(m_result_stack.empty());
|
||||
}
|
||||
|
||||
void var_shifter::operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r) {
|
||||
if (is_ground(t)) {
|
||||
r = t;
|
||||
return;
|
||||
}
|
||||
reset_cache();
|
||||
m_bound = bound;
|
||||
m_shift1 = shift1;
|
||||
m_shift2 = shift2;
|
||||
main_loop(t, r);
|
||||
}
|
||||
|
||||
void var_shifter::process_var(var * v) {
|
||||
unsigned vidx = v->get_idx();
|
||||
if (vidx < m_num_qvars) {
|
||||
m_result_stack.push_back(v);
|
||||
}
|
||||
else {
|
||||
unsigned nvidx = vidx - m_num_qvars;
|
||||
if (nvidx >= m_bound)
|
||||
vidx += m_shift1;
|
||||
else
|
||||
vidx += m_shift2;
|
||||
m_result_stack.push_back(m().mk_var(vidx, v->get_sort()));
|
||||
set_new_child_flag(v);
|
||||
}
|
||||
}
|
||||
|
||||
void inv_var_shifter::operator()(expr * t, unsigned shift, expr_ref & r) {
|
||||
if (is_ground(t)) {
|
||||
r = t;
|
||||
return;
|
||||
}
|
||||
reset_cache();
|
||||
m_shift = shift;
|
||||
main_loop(t, r);
|
||||
}
|
||||
|
||||
void inv_var_shifter::process_var(var * v) {
|
||||
unsigned vidx = v->get_idx();
|
||||
if (vidx < m_num_qvars) {
|
||||
m_result_stack.push_back(v);
|
||||
}
|
||||
else {
|
||||
SASSERT(vidx >= m_num_qvars + m_shift);
|
||||
vidx -= m_shift;
|
||||
m_result_stack.push_back(m().mk_var(vidx, v->get_sort()));
|
||||
set_new_child_flag(v);
|
||||
}
|
||||
}
|
||||
|
||||
template class rewriter_tpl<beta_reducer_cfg>;
|
||||
399
src/ast/rewriter/rewriter.h
Normal file
399
src/ast/rewriter/rewriter.h
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Lean and mean rewriter
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-03-31
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _REWRITER_H_
|
||||
#define _REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter_types.h"
|
||||
#include"act_cache.h"
|
||||
|
||||
/**
|
||||
\brief Common infrastructure for AST rewriters.
|
||||
*/
|
||||
class rewriter_core {
|
||||
protected:
|
||||
struct frame {
|
||||
expr * m_curr;
|
||||
unsigned m_cache_result:1; // true if the result of rewriting m_expr must be cached.
|
||||
unsigned m_new_child:1;
|
||||
unsigned m_state:2;
|
||||
unsigned m_max_depth:2; // bounded rewrite... if m_max_depth == 0, then children are not rewritten.
|
||||
unsigned m_i:26;
|
||||
unsigned m_spos; // top of the result stack, when the frame was created.
|
||||
frame(expr * n, bool cache_res, unsigned st, unsigned max_depth, unsigned spos):
|
||||
m_curr(n),
|
||||
m_cache_result(cache_res),
|
||||
m_new_child(false),
|
||||
m_state(st),
|
||||
m_max_depth(max_depth),
|
||||
m_i(0),
|
||||
m_spos(spos) {
|
||||
}
|
||||
};
|
||||
ast_manager & m_manager;
|
||||
bool m_proof_gen;
|
||||
typedef act_cache cache;
|
||||
ptr_vector<cache> m_cache_stack;
|
||||
cache * m_cache; // current cache.
|
||||
svector<frame> m_frame_stack;
|
||||
expr_ref_vector m_result_stack;
|
||||
|
||||
// proof generation goodness ----
|
||||
ptr_vector<cache> m_cache_pr_stack;
|
||||
cache * m_cache_pr;
|
||||
proof_ref_vector m_result_pr_stack;
|
||||
// --------------------------
|
||||
|
||||
expr * m_root;
|
||||
unsigned m_num_qvars;
|
||||
struct scope {
|
||||
expr * m_old_root;
|
||||
unsigned m_old_num_qvars;
|
||||
scope(expr * r, unsigned n):m_old_root(r), m_old_num_qvars(n) {}
|
||||
};
|
||||
svector<scope> m_scopes;
|
||||
|
||||
// Return true if the rewriting result of the given expression must be cached.
|
||||
bool must_cache(expr * t) const {
|
||||
return
|
||||
t->get_ref_count() > 1 && // t must be a shared expression
|
||||
t != m_root && // t must not be the root expression
|
||||
((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t)); // t is a (non-constant) application or a quantifier.
|
||||
}
|
||||
|
||||
void push_frame_core(expr * t, bool cache_res, unsigned st = 0, unsigned max_depth = RW_UNBOUNDED_DEPTH) {
|
||||
SASSERT(!m_proof_gen || m_result_stack.size() == m_result_pr_stack.size());
|
||||
m_frame_stack.push_back(frame(t, cache_res, st, max_depth, m_result_stack.size()));
|
||||
}
|
||||
|
||||
void push_frame(expr * t, unsigned st = 0) {
|
||||
push_frame_core(t, must_cache(t), st);
|
||||
}
|
||||
|
||||
void init_cache_stack();
|
||||
void del_cache_stack();
|
||||
void reset_cache();
|
||||
void cache_result(expr * k, expr * v);
|
||||
expr * get_cached(expr * k) const { return m_cache->find(k); }
|
||||
|
||||
void cache_result(expr * k, expr * v, proof * pr);
|
||||
proof * get_cached_pr(expr * k) const { return static_cast<proof*>(m_cache_pr->find(k)); }
|
||||
|
||||
void free_memory();
|
||||
void begin_scope();
|
||||
void end_scope();
|
||||
bool is_child_of_top_frame(expr * t) const;
|
||||
void set_new_child_flag(expr * old_t) {
|
||||
CTRACE("rewriter_bug", !is_child_of_top_frame(old_t), display_stack(tout, 3););
|
||||
SASSERT(is_child_of_top_frame(old_t));
|
||||
if (!m_frame_stack.empty())
|
||||
m_frame_stack.back().m_new_child = true;
|
||||
}
|
||||
void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); }
|
||||
|
||||
void elim_reflex_prs(unsigned spos);
|
||||
public:
|
||||
rewriter_core(ast_manager & m, bool proof_gen);
|
||||
~rewriter_core();
|
||||
ast_manager & m() const { return m_manager; }
|
||||
void reset();
|
||||
void cleanup();
|
||||
#ifdef _TRACE
|
||||
void display_stack(std::ostream & out, unsigned pp_depth);
|
||||
#endif
|
||||
unsigned get_cache_size() const;
|
||||
};
|
||||
|
||||
class var_shifter_core : public rewriter_core {
|
||||
protected:
|
||||
bool visit(expr * t);
|
||||
void process_app(app * t, frame & fr);
|
||||
virtual void process_var(var * v) = 0;
|
||||
void process_quantifier(quantifier * q, frame & fr);
|
||||
void main_loop(expr * t, expr_ref & r);
|
||||
public:
|
||||
var_shifter_core(ast_manager & m):rewriter_core(m, false) {}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Functor used to shift the free variables of an AST by a given amount.
|
||||
This functor is used by the var_subst functor.
|
||||
|
||||
This functor implements the following functions:
|
||||
1) shift(n, s) will return a new AST where all free variables (VAR i) in n,
|
||||
are replaced by (VAR (+ i s)).
|
||||
|
||||
2) shift(n, b, s1, s2) is a variant of the previous function such that
|
||||
for a each free variable (VAR i) is shifted to:
|
||||
- (VAR i + s1) if i >= b
|
||||
- (VAR i + s2) if i < b
|
||||
*/
|
||||
class var_shifter : public var_shifter_core {
|
||||
unsigned m_bound;
|
||||
unsigned m_shift1;
|
||||
unsigned m_shift2;
|
||||
virtual void process_var(var * v);
|
||||
public:
|
||||
var_shifter(ast_manager & m):var_shifter_core(m) {}
|
||||
void operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r);
|
||||
void operator()(expr * t, unsigned s, expr_ref & r) {
|
||||
operator()(t, 0, s, 0, r);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Functor used to shift the free variables of an AST by a negative amount.
|
||||
|
||||
Abstract implementation:
|
||||
|
||||
inv_shift(f(c_1, ..., c_n), d, s) =
|
||||
f(inv_shift(c_1, d, s), ..., inv_shift(c_n, d, s))
|
||||
inv_shift(var(i), d, s) =
|
||||
if (i < d)
|
||||
var(i)
|
||||
else
|
||||
assert(i - s >= d)
|
||||
var(i-s)
|
||||
inv_shift((forall (x) P), d, s) =
|
||||
(forall (x) inv_shift(P, d+1, s))
|
||||
|
||||
This functor assumes that if we are shifting an expression F by N, then F
|
||||
does not contain free variables #0, ... #N-1
|
||||
|
||||
See assertion above.
|
||||
*/
|
||||
class inv_var_shifter : public var_shifter_core {
|
||||
protected:
|
||||
unsigned m_shift;
|
||||
virtual void process_var(var * v);
|
||||
public:
|
||||
inv_var_shifter(ast_manager & m):var_shifter_core(m) {}
|
||||
void operator()(expr * t, unsigned shift, expr_ref & r);
|
||||
};
|
||||
|
||||
template<typename Config>
|
||||
class rewriter_tpl : public rewriter_core {
|
||||
protected:
|
||||
// Rewriter maintains a stack of frames.
|
||||
// Each frame represents an expression that is being rewritten.
|
||||
// The resultant expressions are store on the Result stack.
|
||||
// Each frame is in a particular state.
|
||||
// Let f(t_0,...,t_n) be the expression is the frame Fr. Then Fr can be is one of the
|
||||
// following states:
|
||||
// PROCESS_CHILDREN(i) the children t_0, ..., t_{i-1} have already been rewritten, and the result is on the Result stack.
|
||||
// REWRITE_BUILTIN All t_0, ..., t_n have been rewritten to t_0',...,t_n', and the builtin rewriter (or plugin) produced a new term t
|
||||
// that can be further rewritten.
|
||||
// EXPAND_DEF All t_0, ..., t_n have been rewritten to t_0',...,t_n'.
|
||||
// The function symbol f is a macro, and the body of the macro needs to be rewritten using the t_0',...,t_n' as bindings.
|
||||
// REWRITE_RULE All t_0, ..., t_n have been rewritten to t_0',...,t_n'.
|
||||
// There is rewrite rule lhs -> rhs s.t. f(t_0', ..., t_n') matches lhs with substitution delta.
|
||||
// rhs is then rewritten using delta.
|
||||
enum state {
|
||||
PROCESS_CHILDREN,
|
||||
REWRITE_BUILTIN,
|
||||
EXPAND_DEF,
|
||||
REWRITE_RULE
|
||||
};
|
||||
Config & m_cfg;
|
||||
unsigned m_num_steps;
|
||||
volatile bool m_cancel;
|
||||
ptr_vector<expr> m_bindings;
|
||||
var_shifter m_shifter;
|
||||
expr_ref m_r;
|
||||
proof_ref m_pr;
|
||||
proof_ref m_pr2;
|
||||
|
||||
svector<frame> & frame_stack() { return this->m_frame_stack; }
|
||||
svector<frame> const & frame_stack() const { return this->m_frame_stack; }
|
||||
expr_ref_vector & result_stack() { return this->m_result_stack; }
|
||||
expr_ref_vector const & result_stack() const { return this->m_result_stack; }
|
||||
proof_ref_vector & result_pr_stack() { return this->m_result_pr_stack; }
|
||||
proof_ref_vector const & result_pr_stack() const { return this->m_result_pr_stack; }
|
||||
|
||||
void set_new_child_flag(expr * old_t) {
|
||||
SASSERT(frame_stack().empty() || frame_stack().back().m_state != PROCESS_CHILDREN || this->is_child_of_top_frame(old_t));
|
||||
if (!frame_stack().empty())
|
||||
frame_stack().back().m_new_child = true;
|
||||
}
|
||||
void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); }
|
||||
|
||||
|
||||
// cache the result of shared non atomic expressions.
|
||||
bool cache_results() const { return m_cfg.cache_results(); }
|
||||
// cache all results share and non shared expressions non atomic expressions.
|
||||
bool cache_all_results() const { return m_cfg.cache_all_results(); }
|
||||
// flat non shared AC terms
|
||||
bool flat_assoc(func_decl * f) const { return m_cfg.flat_assoc(f); }
|
||||
// rewrite patterns
|
||||
bool rewrite_patterns() const { return m_cfg.rewrite_patterns(); }
|
||||
|
||||
// check maximum number of scopes
|
||||
void check_max_scopes() const {
|
||||
if (m_cfg.max_scopes_exceeded(this->m_scopes.size()))
|
||||
throw rewriter_exception(TACTIC_MAX_SCOPES_MSG);
|
||||
}
|
||||
// check maximum size of the frame stack
|
||||
void check_max_frames() const {
|
||||
if (m_cfg.max_frames_exceeded(frame_stack().size()))
|
||||
throw rewriter_exception(TACTIC_MAX_FRAMES_MSG);
|
||||
}
|
||||
// check maximum number of rewriting steps
|
||||
void check_max_steps() const {
|
||||
if (m_cfg.max_steps_exceeded(m_num_steps))
|
||||
throw rewriter_exception(TACTIC_MAX_STEPS_MSG);
|
||||
}
|
||||
|
||||
// If pre_visit returns false, then t children are not visited/rewritten.
|
||||
// This should be used with care, since it may produce incorrect results
|
||||
// when the rewriter is used to apply variable substitution.
|
||||
bool pre_visit(expr * t) {
|
||||
return m_cfg.pre_visit(t);
|
||||
}
|
||||
|
||||
// Return true if the rewriting result of the given expression must be cached.
|
||||
bool must_cache(expr * t) const {
|
||||
if (cache_all_results())
|
||||
return t != this->m_root && ((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t));
|
||||
if (cache_results())
|
||||
return rewriter_core::must_cache(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) {
|
||||
return m_cfg.get_macro(f, def, q, def_pr);
|
||||
}
|
||||
|
||||
void push_frame(expr * t, bool mcache, unsigned max_depth) {
|
||||
check_max_frames();
|
||||
push_frame_core(t, mcache, PROCESS_CHILDREN, max_depth);
|
||||
}
|
||||
|
||||
void begin_scope() {
|
||||
check_max_scopes();
|
||||
rewriter_core::begin_scope();
|
||||
}
|
||||
|
||||
template<bool ProofGen>
|
||||
void process_var(var * v);
|
||||
|
||||
template<bool ProofGen>
|
||||
void process_const(app * t);
|
||||
|
||||
template<bool ProofGen>
|
||||
bool visit(expr * t, unsigned max_depth);
|
||||
|
||||
template<bool ProofGen>
|
||||
void cache_result(expr * t, expr * new_t, proof * pr, bool c) {
|
||||
if (c) {
|
||||
if (!ProofGen)
|
||||
rewriter_core::cache_result(t, new_t);
|
||||
else
|
||||
rewriter_core::cache_result(t, new_t, pr);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool ProofGen>
|
||||
void process_app(app * t, frame & fr);
|
||||
|
||||
template<bool ProofGen>
|
||||
void process_quantifier(quantifier * q, frame & fr);
|
||||
|
||||
bool first_visit(frame & fr) const {
|
||||
return fr.m_state == PROCESS_CHILDREN && fr.m_i == 0;
|
||||
}
|
||||
|
||||
bool not_rewriting() const;
|
||||
|
||||
template<bool ProofGen>
|
||||
void main_loop(expr * t, expr_ref & result, proof_ref & result_pr);
|
||||
|
||||
template<bool ProofGen>
|
||||
void resume_core(expr_ref & result, proof_ref & result_pr);
|
||||
|
||||
public:
|
||||
rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg);
|
||||
|
||||
ast_manager & m() const { return this->m_manager; }
|
||||
Config & cfg() { return m_cfg; }
|
||||
Config const & cfg() const { return m_cfg; }
|
||||
|
||||
void set_cancel(bool f) { m_cancel = f; }
|
||||
void cancel() { set_cancel(true); }
|
||||
void reset_cancel() { set_cancel(false); }
|
||||
|
||||
~rewriter_tpl();
|
||||
|
||||
void reset();
|
||||
void cleanup();
|
||||
|
||||
void set_bindings(unsigned num_bindings, expr * const * bindings);
|
||||
void set_inv_bindings(unsigned num_bindings, expr * const * bindings);
|
||||
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
|
||||
void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); }
|
||||
void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) {
|
||||
SASSERT(!m_proof_gen);
|
||||
reset();
|
||||
set_inv_bindings(num_bindings, bindings);
|
||||
operator()(n, result);
|
||||
}
|
||||
|
||||
void resume(expr_ref & result, proof_ref & result_pr);
|
||||
void resume(expr_ref & result) { resume(result, m_pr); }
|
||||
|
||||
// Return the number of steps performed by the rewriter in the last call to operator().
|
||||
unsigned get_num_steps() const { return m_num_steps; }
|
||||
};
|
||||
|
||||
struct default_rewriter_cfg {
|
||||
bool cache_all_results() const { return false; }
|
||||
bool cache_results() const { return true; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
bool rewrite_patterns() const { return true; }
|
||||
bool max_scopes_exceeded(unsigned num_scopes) const { return false; }
|
||||
bool max_frames_exceeded(unsigned num_frames) const { return false; }
|
||||
bool max_steps_exceeded(unsigned num_steps) const { return false; }
|
||||
bool pre_visit(expr * t) { return true; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return BR_FAILED; }
|
||||
bool reduce_quantifier(quantifier * old_q,
|
||||
expr * new_body,
|
||||
expr * const * new_patterns,
|
||||
expr * const * new_no_patterns,
|
||||
expr_ref & result,
|
||||
proof_ref & result_pr) {
|
||||
return false;
|
||||
}
|
||||
bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { return false; }
|
||||
bool get_macro(func_decl * d, expr * & def, quantifier * & q, proof * & def_pr) { return false; }
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) { return false; }
|
||||
void reset() {}
|
||||
void cleanup() {}
|
||||
};
|
||||
|
||||
struct beta_reducer_cfg : public default_rewriter_cfg {
|
||||
bool pre_visit(expr * t) { return !is_ground(t); }
|
||||
};
|
||||
|
||||
class beta_reducer : public rewriter_tpl<beta_reducer_cfg> {
|
||||
beta_reducer_cfg m_cfg;
|
||||
public:
|
||||
beta_reducer(ast_manager & m):
|
||||
rewriter_tpl<beta_reducer_cfg>(m, false, m_cfg) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
145
src/ast/rewriter/rewriter.txt
Normal file
145
src/ast/rewriter/rewriter.txt
Normal 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
642
src/ast/rewriter/rewriter_def.h
Normal file
642
src/ast/rewriter/rewriter_def.h
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
rewriter_def.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Lean and mean rewriter
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-03-31
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"rewriter.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::process_var(var * v) {
|
||||
if (m_cfg.reduce_var(v, m_r, m_pr)) {
|
||||
result_stack().push_back(m_r);
|
||||
if (ProofGen) {
|
||||
result_pr_stack().push_back(m_pr);
|
||||
m_pr = 0;
|
||||
}
|
||||
set_new_child_flag(v);
|
||||
m_r = 0;
|
||||
return;
|
||||
}
|
||||
if (!ProofGen) {
|
||||
// bindings are only used when Proof Generation is not enabled.
|
||||
unsigned idx = v->get_idx();
|
||||
if (idx < m_bindings.size()) {
|
||||
expr * r = m_bindings[m_bindings.size() - idx - 1];
|
||||
TRACE("process_var", if (r) tout << "idx: " << idx << " --> " << mk_ismt2_pp(r, m()) << "\n";
|
||||
tout << "bindings:\n";
|
||||
for (unsigned i = 0; i < m_bindings.size(); i++) if (m_bindings[i]) tout << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n";);
|
||||
if (r != 0) {
|
||||
if (m_num_qvars == 0 || is_ground(r)) {
|
||||
result_stack().push_back(r);
|
||||
}
|
||||
else {
|
||||
expr_ref new_term(m());
|
||||
m_shifter(r, m_num_qvars, new_term);
|
||||
result_stack().push_back(new_term);
|
||||
}
|
||||
set_new_child_flag(v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
result_stack().push_back(v);
|
||||
if (ProofGen)
|
||||
result_pr_stack().push_back(0); // implicit reflexivity
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::process_const(app * t) {
|
||||
SASSERT(t->get_num_args() == 0);
|
||||
br_status st = m_cfg.reduce_app(t->get_decl(), 0, 0, m_r, m_pr);
|
||||
SASSERT(st == BR_FAILED || st == BR_DONE);
|
||||
if (st == BR_DONE) {
|
||||
result_stack().push_back(m_r.get());
|
||||
if (ProofGen) {
|
||||
if (m_pr)
|
||||
result_pr_stack().push_back(m_pr);
|
||||
else
|
||||
result_pr_stack().push_back(m().mk_rewrite(t, m_r));
|
||||
m_pr = 0;
|
||||
}
|
||||
m_r = 0;
|
||||
set_new_child_flag(t);
|
||||
}
|
||||
else {
|
||||
result_stack().push_back(t);
|
||||
if (ProofGen)
|
||||
result_pr_stack().push_back(0); // implicit reflexivity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief visit expression t. Return true if t was rewritten and the result is on the top m_result_stack.
|
||||
We may skip t if:
|
||||
- pre_visit(t) returns false
|
||||
- max_depth == 0
|
||||
- t is already in the cache
|
||||
Otherwise, return false and add a new frame stack for t with the updated max_depth.
|
||||
*/
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
bool rewriter_tpl<Config>::visit(expr * t, unsigned max_depth) {
|
||||
TRACE("rewriter_visit", tout << "visiting\n" << mk_ismt2_pp(t, m()) << "\n";);
|
||||
expr * new_t;
|
||||
proof * new_t_pr;
|
||||
if (m_cfg.get_subst(t, new_t, new_t_pr)) {
|
||||
TRACE("rewriter_subst", tout << "subst\n" << mk_ismt2_pp(t, m()) << "\n---->\n" << mk_ismt2_pp(new_t, m()) << "\n";);
|
||||
result_stack().push_back(new_t);
|
||||
set_new_child_flag(t, new_t);
|
||||
if (ProofGen)
|
||||
result_pr_stack().push_back(new_t_pr);
|
||||
return true;
|
||||
}
|
||||
if (max_depth == 0) {
|
||||
result_stack().push_back(t);
|
||||
if (ProofGen)
|
||||
result_pr_stack().push_back(0); // implicit reflexivity
|
||||
return true; // t is not going to be processed
|
||||
}
|
||||
SASSERT(max_depth > 0);
|
||||
SASSERT(max_depth <= RW_UNBOUNDED_DEPTH);
|
||||
bool c = must_cache(t);
|
||||
if (c) {
|
||||
#if 0
|
||||
static unsigned checked_cache = 0;
|
||||
checked_cache ++;
|
||||
if (checked_cache % 100000 == 0)
|
||||
std::cerr << "[rewriter] num-cache-checks: " << checked_cache << std::endl;
|
||||
#endif
|
||||
expr * r = get_cached(t);
|
||||
if (r) {
|
||||
result_stack().push_back(r);
|
||||
set_new_child_flag(t, r);
|
||||
if (ProofGen) {
|
||||
proof * pr = get_cached_pr(t);
|
||||
result_pr_stack().push_back(pr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!pre_visit(t)) {
|
||||
result_stack().push_back(t);
|
||||
if (ProofGen)
|
||||
result_pr_stack().push_back(0); // implicit reflexivity
|
||||
return true; // t is not going to be processed
|
||||
}
|
||||
switch (t->get_kind()) {
|
||||
case AST_APP:
|
||||
if (to_app(t)->get_num_args() == 0) {
|
||||
process_const<ProofGen>(to_app(t));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (max_depth != RW_UNBOUNDED_DEPTH)
|
||||
max_depth--;
|
||||
push_frame(t, c, max_depth);
|
||||
return false;
|
||||
}
|
||||
case AST_VAR:
|
||||
process_var<ProofGen>(to_var(t));
|
||||
return true;
|
||||
case AST_QUANTIFIER:
|
||||
if (max_depth != RW_UNBOUNDED_DEPTH)
|
||||
max_depth--;
|
||||
push_frame(t, c, max_depth);
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
|
||||
SASSERT(t->get_num_args() > 0);
|
||||
SASSERT(!frame_stack().empty());
|
||||
switch (fr.m_state) {
|
||||
case PROCESS_CHILDREN: {
|
||||
unsigned num_args = t->get_num_args();
|
||||
while (fr.m_i < num_args) {
|
||||
expr * arg = t->get_arg(fr.m_i);
|
||||
fr.m_i++;
|
||||
if (!visit<ProofGen>(arg, fr.m_max_depth))
|
||||
return;
|
||||
}
|
||||
func_decl * f = t->get_decl();
|
||||
|
||||
// If AC flattening is enabled, f is associative, t is not shared, and there is a previous frame on the stack.
|
||||
if (!ProofGen) {
|
||||
// this optimization is only used when Proof generation is disabled.
|
||||
if (f->is_associative() && t->get_ref_count() <= 1 && frame_stack().size() > 1) {
|
||||
frame & prev_fr = frame_stack()[frame_stack().size() - 2];
|
||||
if (is_app(prev_fr.m_curr) &&
|
||||
to_app(prev_fr.m_curr)->get_decl() == f &&
|
||||
prev_fr.m_state == PROCESS_CHILDREN &&
|
||||
flat_assoc(f)) {
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned new_num_args = result_stack().size() - fr.m_spos;
|
||||
expr * const * new_args = result_stack().c_ptr() + fr.m_spos;
|
||||
app * new_t;
|
||||
if (ProofGen) {
|
||||
elim_reflex_prs(fr.m_spos);
|
||||
unsigned num_prs = result_pr_stack().size() - fr.m_spos;
|
||||
if (num_prs == 0) {
|
||||
new_t = t;
|
||||
m_pr = 0;
|
||||
}
|
||||
else {
|
||||
new_t = m().mk_app(f, new_num_args, new_args);
|
||||
m_pr = m().mk_congruence(t, new_t, num_prs, result_pr_stack().c_ptr() + fr.m_spos);
|
||||
}
|
||||
}
|
||||
br_status st = m_cfg.reduce_app(f, new_num_args, new_args, m_r, m_pr2);
|
||||
TRACE("reduce_app",
|
||||
tout << mk_ismt2_pp(t, m()) << "\n";
|
||||
tout << "st: " << st;
|
||||
if (m_r) tout << " --->\n" << mk_ismt2_pp(m_r, m());
|
||||
tout << "\n";);
|
||||
if (st != BR_FAILED) {
|
||||
result_stack().shrink(fr.m_spos);
|
||||
result_stack().push_back(m_r);
|
||||
if (ProofGen) {
|
||||
result_pr_stack().shrink(fr.m_spos);
|
||||
if (!m_pr2)
|
||||
m_pr2 = m().mk_rewrite(new_t, m_r);
|
||||
m_pr = m().mk_transitivity(m_pr, m_pr2);
|
||||
m_pr2 = 0;
|
||||
result_pr_stack().push_back(m_pr);
|
||||
}
|
||||
if (st == BR_DONE) {
|
||||
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t);
|
||||
m_r = 0;
|
||||
if (ProofGen)
|
||||
m_pr = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
fr.m_state = REWRITE_BUILTIN;
|
||||
SASSERT(st == BR_REWRITE1 || st == BR_REWRITE2 || st == BR_REWRITE3 || st == BR_REWRITE_FULL);
|
||||
unsigned max_depth = static_cast<unsigned>(st);
|
||||
SASSERT(0 <= max_depth && max_depth <= RW_UNBOUNDED_DEPTH);
|
||||
SASSERT((st == BR_REWRITE1) == (max_depth == 0));
|
||||
SASSERT((st == BR_REWRITE2) == (max_depth == 1));
|
||||
SASSERT((st == BR_REWRITE3) == (max_depth == 2));
|
||||
SASSERT((st == BR_REWRITE_FULL) == (max_depth == RW_UNBOUNDED_DEPTH));
|
||||
if (max_depth != RW_UNBOUNDED_DEPTH)
|
||||
max_depth++;
|
||||
if (visit<ProofGen>(m_r, max_depth)) {
|
||||
if (ProofGen) {
|
||||
proof_ref pr2(m()), pr1(m());
|
||||
pr2 = result_pr_stack().back();
|
||||
result_pr_stack().pop_back();
|
||||
pr1 = result_pr_stack().back();
|
||||
result_pr_stack().pop_back();
|
||||
m_pr = m().mk_transitivity(pr1, pr2);
|
||||
result_pr_stack().push_back(m_pr);
|
||||
}
|
||||
m_r = result_stack().back();
|
||||
result_stack().pop_back();
|
||||
result_stack().pop_back();
|
||||
result_stack().push_back(m_r);
|
||||
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t);
|
||||
m_r = 0;
|
||||
if (ProofGen)
|
||||
m_pr = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// frame was created for processing m_r
|
||||
m_r = 0;
|
||||
if (ProofGen)
|
||||
m_pr = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
// TODO: add rewrite rules support
|
||||
expr * def;
|
||||
proof * def_pr;
|
||||
quantifier * def_q;
|
||||
// When get_macro succeeds, then
|
||||
// we know that:
|
||||
// forall X. f(X) = def[X]
|
||||
// and def_pr is a proof for this quantifier.
|
||||
//
|
||||
// Remark: def_q is only used for proof generation.
|
||||
// It is the quantifier forall X. f(X) = def[X]
|
||||
if (get_macro(f, def, def_q, def_pr)) {
|
||||
SASSERT(!f->is_associative() || !flat_assoc(f));
|
||||
SASSERT(new_num_args == t->get_num_args());
|
||||
if (is_ground(def)) {
|
||||
m_r = def;
|
||||
if (ProofGen) {
|
||||
SASSERT(def_pr);
|
||||
m_pr = m().mk_transitivity(m_pr, def_pr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ProofGen) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
// We do not support the use of bindings in proof generation mode.
|
||||
// Thus we have to apply the subsitution here, and
|
||||
// beta_reducer subst(m());
|
||||
// subst.set_bindings(new_num_args, new_args);
|
||||
// expr_ref r2(m());
|
||||
// subst(def, r2);
|
||||
}
|
||||
else {
|
||||
fr.m_state = EXPAND_DEF;
|
||||
TRACE("get_macro", tout << "f: " << f->get_name() << ", def: \n" << mk_ismt2_pp(def, m()) << "\n";
|
||||
tout << "Args num: " << num_args << "\n";
|
||||
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(new_args[i], m()) << "\n";);
|
||||
unsigned i = num_args;
|
||||
while (i > 0) {
|
||||
--i;
|
||||
m_bindings.push_back(new_args[i]);
|
||||
}
|
||||
result_stack().push_back(def);
|
||||
TRACE("get_macro", tout << "bindings:\n";
|
||||
for (unsigned i = 0; i < m_bindings.size(); i++) tout << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n";);
|
||||
begin_scope();
|
||||
m_num_qvars = 0;
|
||||
m_root = def;
|
||||
push_frame(def, false, RW_UNBOUNDED_DEPTH);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ProofGen) {
|
||||
m_r = new_t;
|
||||
}
|
||||
else {
|
||||
if (fr.m_new_child) {
|
||||
m_r = m().mk_app(f, new_num_args, new_args);
|
||||
}
|
||||
else {
|
||||
TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(t, m()) << "\n";);
|
||||
m_r = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
result_stack().shrink(fr.m_spos);
|
||||
result_stack().push_back(m_r);
|
||||
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
|
||||
if (ProofGen) {
|
||||
result_pr_stack().shrink(fr.m_spos);
|
||||
result_pr_stack().push_back(m_pr);
|
||||
m_pr = 0;
|
||||
}
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t, m_r);
|
||||
m_r = 0;
|
||||
return;
|
||||
}
|
||||
case REWRITE_BUILTIN:
|
||||
SASSERT(fr.m_spos + 2 == result_stack().size());
|
||||
if (ProofGen) {
|
||||
proof_ref pr2(m()), pr1(m());
|
||||
pr2 = result_pr_stack().back();
|
||||
result_pr_stack().pop_back();
|
||||
pr1 = result_pr_stack().back();
|
||||
result_pr_stack().pop_back();
|
||||
m_pr = m().mk_transitivity(pr1, pr2);
|
||||
result_pr_stack().push_back(m_pr);
|
||||
}
|
||||
m_r = result_stack().back();
|
||||
result_stack().pop_back();
|
||||
result_stack().pop_back();
|
||||
result_stack().push_back(m_r);
|
||||
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t);
|
||||
return;
|
||||
case EXPAND_DEF:
|
||||
SASSERT(fr.m_spos + t->get_num_args() + 2 == result_stack().size());
|
||||
SASSERT(t->get_num_args() <= m_bindings.size());
|
||||
if (!ProofGen) {
|
||||
m_bindings.shrink(m_bindings.size() - t->get_num_args());
|
||||
end_scope();
|
||||
m_r = result_stack().back();
|
||||
result_stack().shrink(fr.m_spos);
|
||||
result_stack().push_back(m_r);
|
||||
cache_result<ProofGen>(t, m_r, m_pr, fr.m_cache_result);
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t);
|
||||
}
|
||||
else {
|
||||
// TODO
|
||||
NOT_IMPLEMENTED_YET();
|
||||
}
|
||||
return;
|
||||
case REWRITE_RULE:
|
||||
// support for rewriting rules was not implemented yet.
|
||||
NOT_IMPLEMENTED_YET();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::process_quantifier(quantifier * q, frame & fr) {
|
||||
SASSERT(fr.m_state == PROCESS_CHILDREN);
|
||||
if (fr.m_i == 0) {
|
||||
if (!ProofGen) {
|
||||
begin_scope();
|
||||
m_root = q->get_expr();
|
||||
}
|
||||
m_num_qvars += q->get_num_decls();
|
||||
if (!ProofGen) {
|
||||
for (unsigned i = 0; i < q->get_num_decls(); i++)
|
||||
m_bindings.push_back(0);
|
||||
}
|
||||
}
|
||||
unsigned num_children = rewrite_patterns() ? q->get_num_children() : 1;
|
||||
while (fr.m_i < num_children) {
|
||||
expr * child = q->get_child(fr.m_i);
|
||||
fr.m_i++;
|
||||
if (!visit<ProofGen>(child, fr.m_max_depth))
|
||||
return;
|
||||
}
|
||||
SASSERT(fr.m_spos + num_children == result_stack().size());
|
||||
expr * const * it = result_stack().c_ptr() + fr.m_spos;
|
||||
expr * new_body = *it;
|
||||
expr * const * new_pats;
|
||||
expr * const * new_no_pats;
|
||||
if (rewrite_patterns()) {
|
||||
new_pats = it + 1;
|
||||
new_no_pats = new_pats + q->get_num_patterns();
|
||||
}
|
||||
else {
|
||||
new_pats = q->get_patterns();
|
||||
new_no_pats = q->get_no_patterns();
|
||||
}
|
||||
if (ProofGen) {
|
||||
quantifier * new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body);
|
||||
m_pr = q == new_q ? 0 : m().mk_quant_intro(q, new_q, result_pr_stack().get(fr.m_spos));
|
||||
m_r = new_q;
|
||||
proof_ref pr2(m());
|
||||
if (m_cfg.reduce_quantifier(new_q, new_body, new_pats, new_no_pats, m_r, pr2)) {
|
||||
m_pr = m().mk_transitivity(m_pr, pr2);
|
||||
}
|
||||
TRACE("reduce_quantifier_bug", tout << "m_pr is_null: " << (m_pr.get() == 0) << "\n";
|
||||
if (m_pr) tout << mk_ismt2_pp(m_pr, m()) << "\n";);
|
||||
result_pr_stack().shrink(fr.m_spos);
|
||||
result_pr_stack().push_back(m_pr);
|
||||
}
|
||||
else {
|
||||
if (!m_cfg.reduce_quantifier(q, new_body, new_pats, new_no_pats, m_r, m_pr)) {
|
||||
if (fr.m_new_child) {
|
||||
m_r = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body);
|
||||
}
|
||||
else {
|
||||
TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(q, m()) << "\n";);
|
||||
m_r = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
result_stack().shrink(fr.m_spos);
|
||||
result_stack().push_back(m_r.get());
|
||||
if (!ProofGen) {
|
||||
SASSERT(q->get_num_decls() <= m_bindings.size());
|
||||
m_bindings.shrink(m_bindings.size() - q->get_num_decls());
|
||||
end_scope();
|
||||
cache_result<ProofGen>(q, m_r, m_pr, fr.m_cache_result);
|
||||
}
|
||||
else {
|
||||
cache_result<ProofGen>(q, m_r, m_pr, fr.m_cache_result);
|
||||
m_pr = 0;
|
||||
}
|
||||
m_r = 0;
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(q, m_r);
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
bool rewriter_tpl<Config>::not_rewriting() const {
|
||||
SASSERT(frame_stack().empty());
|
||||
SASSERT(m_cache == m_cache_stack[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
rewriter_tpl<Config>::rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg):
|
||||
rewriter_core(m, proof_gen),
|
||||
m_cfg(cfg),
|
||||
m_num_steps(0),
|
||||
m_cancel(false),
|
||||
m_shifter(m),
|
||||
m_r(m),
|
||||
m_pr(m),
|
||||
m_pr2(m) {
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
rewriter_tpl<Config>::~rewriter_tpl() {
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::reset() {
|
||||
m_cfg.reset();
|
||||
rewriter_core::reset();
|
||||
m_bindings.reset();
|
||||
m_shifter.reset();
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::cleanup() {
|
||||
m_cfg.cleanup();
|
||||
rewriter_core::cleanup();
|
||||
m_bindings.finalize();
|
||||
m_shifter.cleanup();
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::set_bindings(unsigned num_bindings, expr * const * bindings) {
|
||||
SASSERT(!m_proof_gen);
|
||||
SASSERT(not_rewriting());
|
||||
m_bindings.reset();
|
||||
unsigned i = num_bindings;
|
||||
while (i > 0) {
|
||||
--i;
|
||||
m_bindings.push_back(bindings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::set_inv_bindings(unsigned num_bindings, expr * const * bindings) {
|
||||
SASSERT(!m_proof_gen);
|
||||
SASSERT(not_rewriting());
|
||||
m_bindings.reset();
|
||||
for (unsigned i = 0; i < num_bindings; i++) {
|
||||
m_bindings.push_back(bindings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) {
|
||||
SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size());
|
||||
SASSERT(not_rewriting());
|
||||
m_root = t;
|
||||
m_num_qvars = 0;
|
||||
m_num_steps = 0;
|
||||
if (visit<ProofGen>(t, RW_UNBOUNDED_DEPTH)) {
|
||||
result = result_stack().back();
|
||||
result_stack().pop_back();
|
||||
SASSERT(result_stack().empty());
|
||||
if (ProofGen) {
|
||||
result_pr = result_pr_stack().back();
|
||||
result_pr_stack().pop_back();
|
||||
if (result_pr.get() == 0)
|
||||
result_pr = m().mk_reflexivity(t);
|
||||
SASSERT(result_pr_stack().empty());
|
||||
}
|
||||
return;
|
||||
}
|
||||
resume_core<ProofGen>(result, result_pr);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Resume the execution when it was interrupted by cancel() or check_max_steps().
|
||||
*/
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::resume_core(expr_ref & result, proof_ref & result_pr) {
|
||||
SASSERT(!frame_stack().empty());
|
||||
while (!frame_stack().empty()) {
|
||||
if (m_cancel)
|
||||
throw rewriter_exception(TACTIC_CANCELED_MSG);
|
||||
SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size());
|
||||
frame & fr = frame_stack().back();
|
||||
expr * t = fr.m_curr;
|
||||
TRACE("rewriter_step", tout << "step\n" << mk_ismt2_pp(t, m()) << "\n";);
|
||||
m_num_steps++;
|
||||
check_max_steps();
|
||||
if (first_visit(fr) && fr.m_cache_result) {
|
||||
expr * r = get_cached(t);
|
||||
if (r) {
|
||||
result_stack().push_back(r);
|
||||
if (ProofGen) {
|
||||
proof * pr = get_cached_pr(t);
|
||||
result_pr_stack().push_back(pr);
|
||||
}
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t, r);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (t->get_kind()) {
|
||||
case AST_APP:
|
||||
process_app<ProofGen>(to_app(t), fr);
|
||||
break;
|
||||
case AST_QUANTIFIER:
|
||||
process_quantifier<ProofGen>(to_quantifier(t), fr);
|
||||
break;
|
||||
case AST_VAR:
|
||||
frame_stack().pop_back();
|
||||
process_var<ProofGen>(to_var(t));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = result_stack().back();
|
||||
result_stack().pop_back();
|
||||
SASSERT(result_stack().empty());
|
||||
if (ProofGen) {
|
||||
result_pr = result_pr_stack().back();
|
||||
result_pr_stack().pop_back();
|
||||
if (result_pr.get() == 0)
|
||||
result_pr = m().mk_reflexivity(m_root);
|
||||
SASSERT(result_pr_stack().empty());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::operator()(expr * t, expr_ref & result, proof_ref & result_pr) {
|
||||
if (m_proof_gen)
|
||||
main_loop<true>(t, result, result_pr);
|
||||
else
|
||||
main_loop<false>(t, result, result_pr);
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::resume(expr_ref & result, proof_ref & result_pr) {
|
||||
if (m_proof_gen)
|
||||
resume_core<true>(result, result_pr);
|
||||
else
|
||||
resume_core<false>(result, result_pr);
|
||||
}
|
||||
51
src/ast/rewriter/rewriter_types.h
Normal file
51
src/ast/rewriter/rewriter_types.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*++
|
||||
Copyright (c) 2007 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
rewriter_types.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Lean and mean rewriter
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-10
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _REWRITER_TYPES_H_
|
||||
#define _REWRITER_TYPES_H_
|
||||
|
||||
#include"tactic_exception.h"
|
||||
|
||||
/**
|
||||
\brief Builtin rewrite result status
|
||||
*/
|
||||
enum br_status {
|
||||
BR_REWRITE1, // rewrite the result (bounded by depth 1)
|
||||
BR_REWRITE2, // rewrite the result (bounded by depth 2)
|
||||
BR_REWRITE3, // rewrite the result (bounded by depth 3)
|
||||
BR_REWRITE_FULL, // rewrite the result unbounded
|
||||
BR_DONE, // done, the result is simplified
|
||||
BR_FAILED // no builtin rewrite is available
|
||||
};
|
||||
|
||||
#define RW_UNBOUNDED_DEPTH 3
|
||||
inline br_status unsigned2br_status(unsigned u) {
|
||||
br_status r = u >= RW_UNBOUNDED_DEPTH ? BR_REWRITE_FULL : static_cast<br_status>(u);
|
||||
SASSERT((u == 0) == (r == BR_REWRITE1));
|
||||
SASSERT((u == 1) == (r == BR_REWRITE2));
|
||||
SASSERT((u == 2) == (r == BR_REWRITE3));
|
||||
SASSERT((u >= 3) == (r == BR_REWRITE_FULL));
|
||||
return r;
|
||||
}
|
||||
|
||||
class rewriter_exception : public tactic_exception {
|
||||
public:
|
||||
rewriter_exception(char const * msg):tactic_exception(msg) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
770
src/ast/rewriter/th_rewriter.cpp
Normal file
770
src/ast/rewriter/th_rewriter.cpp
Normal file
|
|
@ -0,0 +1,770 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
th_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Rewriter for applying all builtin (cheap) theory rewrite rules.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-07
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"th_rewriter.h"
|
||||
#include"bool_rewriter.h"
|
||||
#include"arith_rewriter.h"
|
||||
#include"bv_rewriter.h"
|
||||
#include"datatype_rewriter.h"
|
||||
#include"array_rewriter.h"
|
||||
#include"float_rewriter.h"
|
||||
#include"dl_rewriter.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"cooperate.h"
|
||||
#include"var_subst.h"
|
||||
#include"ast_util.h"
|
||||
#include"well_sorted.h"
|
||||
|
||||
struct th_rewriter_cfg : public default_rewriter_cfg {
|
||||
bool_rewriter m_b_rw;
|
||||
arith_rewriter m_a_rw;
|
||||
bv_rewriter m_bv_rw;
|
||||
array_rewriter m_ar_rw;
|
||||
datatype_rewriter m_dt_rw;
|
||||
float_rewriter m_f_rw;
|
||||
dl_rewriter m_dl_rw;
|
||||
arith_util m_a_util;
|
||||
bv_util m_bv_util;
|
||||
unsigned long long m_max_memory; // in bytes
|
||||
unsigned m_max_steps;
|
||||
bool m_pull_cheap_ite;
|
||||
bool m_flat;
|
||||
bool m_cache_all;
|
||||
bool m_push_ite_arith;
|
||||
bool m_push_ite_bv;
|
||||
|
||||
// substitution support
|
||||
expr_dependency_ref m_used_dependencies; // set of dependencies of used substitutions
|
||||
expr_substitution * m_subst;
|
||||
|
||||
ast_manager & m() const { return m_b_rw.m(); }
|
||||
|
||||
void updt_local_params(params_ref const & p) {
|
||||
m_flat = p.get_bool(":flat", true);
|
||||
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
|
||||
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
|
||||
m_pull_cheap_ite = p.get_bool(":pull-cheap-ite", false);
|
||||
m_cache_all = p.get_bool(":cache-all", false);
|
||||
m_push_ite_arith = p.get_bool(":push-ite-arith", false);
|
||||
m_push_ite_bv = p.get_bool(":push-ite-bv", false);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_b_rw.updt_params(p);
|
||||
m_a_rw.updt_params(p);
|
||||
m_bv_rw.updt_params(p);
|
||||
m_ar_rw.updt_params(p);
|
||||
updt_local_params(p);
|
||||
}
|
||||
|
||||
bool flat_assoc(func_decl * f) const {
|
||||
if (!m_flat) return false;
|
||||
family_id fid = f->get_family_id();
|
||||
if (fid == null_family_id)
|
||||
return false;
|
||||
decl_kind k = f->get_decl_kind();
|
||||
if (fid == m_b_rw.get_fid())
|
||||
return k == OP_AND || k == OP_OR;
|
||||
if (fid == m_a_rw.get_fid())
|
||||
return k == OP_ADD;
|
||||
if (fid == m_bv_rw.get_fid())
|
||||
return k == OP_BADD || k == OP_BOR || k == OP_BAND || k == OP_BXOR;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rewrite_patterns() const { return false; }
|
||||
|
||||
bool cache_all_results() const { return m_cache_all; }
|
||||
|
||||
bool max_steps_exceeded(unsigned num_steps) const {
|
||||
cooperate("simplifier");
|
||||
if (memory::get_allocation_size() > m_max_memory)
|
||||
throw rewriter_exception(TACTIC_MAX_MEMORY_MSG);
|
||||
return num_steps > m_max_steps;
|
||||
}
|
||||
|
||||
// Return true if t is of the form
|
||||
// (= t #b0)
|
||||
// (= t #b1)
|
||||
// (= #b0 t)
|
||||
// (= #b1 t)
|
||||
bool is_eq_bit(expr * t, expr * & x, unsigned & val) {
|
||||
if (!m().is_eq(t))
|
||||
return false;
|
||||
expr * lhs = to_app(t)->get_arg(0);
|
||||
if (!m_bv_rw.is_bv(lhs))
|
||||
return false;
|
||||
if (m_bv_rw.get_bv_size(lhs) != 1)
|
||||
return false;
|
||||
expr * rhs = to_app(t)->get_arg(1);
|
||||
rational v;
|
||||
unsigned sz;
|
||||
if (m_bv_rw.is_numeral(lhs, v, sz)) {
|
||||
x = rhs;
|
||||
val = v.get_unsigned();
|
||||
SASSERT(val == 0 || val == 1);
|
||||
return true;
|
||||
}
|
||||
if (m_bv_rw.is_numeral(rhs, v, sz)) {
|
||||
x = lhs;
|
||||
val = v.get_unsigned();
|
||||
SASSERT(val == 0 || val == 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// (iff (= x bit1) A)
|
||||
// --->
|
||||
// (= x (ite A bit1 bit0))
|
||||
br_status apply_tamagotchi(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
expr * x;
|
||||
unsigned val;
|
||||
if (is_eq_bit(lhs, x, val)) {
|
||||
result = m().mk_eq(x, m().mk_ite(rhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1)));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
if (is_eq_bit(rhs, x, val)) {
|
||||
result = m().mk_eq(x, m().mk_ite(lhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1)));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status reduce_app_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
family_id fid = f->get_family_id();
|
||||
if (fid == null_family_id)
|
||||
return BR_FAILED;
|
||||
br_status st = BR_FAILED;
|
||||
if (fid == m_b_rw.get_fid()) {
|
||||
decl_kind k = f->get_decl_kind();
|
||||
if (k == OP_EQ) {
|
||||
// theory dispatch for =
|
||||
SASSERT(num == 2);
|
||||
family_id s_fid = m().get_sort(args[0])->get_family_id();
|
||||
if (s_fid == m_a_rw.get_fid())
|
||||
st = m_a_rw.mk_eq_core(args[0], args[1], result);
|
||||
else if (s_fid == m_bv_rw.get_fid())
|
||||
st = m_bv_rw.mk_eq_core(args[0], args[1], result);
|
||||
else if (s_fid == m_dt_rw.get_fid())
|
||||
st = m_dt_rw.mk_eq_core(args[0], args[1], result);
|
||||
else if (s_fid == m_f_rw.get_fid())
|
||||
st = m_f_rw.mk_eq_core(args[0], args[1], result);
|
||||
|
||||
if (st != BR_FAILED)
|
||||
return st;
|
||||
}
|
||||
if (k == OP_EQ || k == OP_IFF) {
|
||||
SASSERT(num == 2);
|
||||
st = apply_tamagotchi(args[0], args[1], result);
|
||||
if (st != BR_FAILED)
|
||||
return st;
|
||||
}
|
||||
return m_b_rw.mk_app_core(f, num, args, result);
|
||||
}
|
||||
if (fid == m_a_rw.get_fid())
|
||||
return m_a_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_bv_rw.get_fid())
|
||||
return m_bv_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_ar_rw.get_fid())
|
||||
return m_ar_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_dt_rw.get_fid())
|
||||
return m_dt_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_f_rw.get_fid())
|
||||
return m_f_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_dl_rw.get_fid())
|
||||
return m_dl_rw.mk_app_core(f, num, args, result);
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
// auxiliary function for pull_ite_core
|
||||
expr * mk_eq_value(expr * lhs, expr * value) {
|
||||
SASSERT(m().is_value(value));
|
||||
if (m().is_value(lhs)) {
|
||||
return lhs == value ? m().mk_true() : m().mk_false();
|
||||
}
|
||||
return m().mk_eq(lhs, value);
|
||||
}
|
||||
|
||||
template<bool SWAP>
|
||||
br_status pull_ite_core(func_decl * p, app * ite, app * value, expr_ref & result) {
|
||||
if (m().is_eq(p)) {
|
||||
result = m().mk_ite(ite->get_arg(0),
|
||||
mk_eq_value(ite->get_arg(1), value),
|
||||
mk_eq_value(ite->get_arg(2), value));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
else {
|
||||
if (SWAP) {
|
||||
result = m().mk_ite(ite->get_arg(0),
|
||||
m().mk_app(p, value, ite->get_arg(1)),
|
||||
m().mk_app(p, value, ite->get_arg(2)));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
else {
|
||||
result = m().mk_ite(ite->get_arg(0),
|
||||
m().mk_app(p, ite->get_arg(1), value),
|
||||
m().mk_app(p, ite->get_arg(2), value));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if t is an ite-value-tree form defined as:
|
||||
// ite-value-tree := (ite c <subtree> <subtree>)
|
||||
// subtree := value
|
||||
// | (ite c <subtree> <subtree>)
|
||||
//
|
||||
bool is_ite_value_tree(expr * t) {
|
||||
if (!m().is_ite(t))
|
||||
return false;
|
||||
ptr_buffer<app> todo;
|
||||
todo.push_back(to_app(t));
|
||||
while (!todo.empty()) {
|
||||
app * ite = todo.back();
|
||||
todo.pop_back();
|
||||
expr * arg1 = ite->get_arg(1);
|
||||
expr * arg2 = ite->get_arg(2);
|
||||
|
||||
if (m().is_ite(arg1) && arg1->get_ref_count() == 1) // do not apply on shared terms, since it may blowup
|
||||
todo.push_back(to_app(arg1));
|
||||
else if (!m().is_value(arg1))
|
||||
return false;
|
||||
|
||||
if (m().is_ite(arg2) && arg2->get_ref_count() == 1) // do not apply on shared terms, since it may blowup
|
||||
todo.push_back(to_app(arg2));
|
||||
else if (!m().is_value(arg2))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
br_status pull_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
if (num == 2 && m().is_bool(f->get_range()) && !m().is_bool(args[0])) {
|
||||
if (m().is_ite(args[0])) {
|
||||
if (m().is_value(args[1]))
|
||||
return pull_ite_core<false>(f, to_app(args[0]), to_app(args[1]), result);
|
||||
if (m().is_ite(args[1]) && to_app(args[0])->get_arg(0) == to_app(args[1])->get_arg(0)) {
|
||||
// (p (ite C A1 B1) (ite C A2 B2)) --> (ite (p A1 A2) (p B1 B2))
|
||||
result = m().mk_ite(to_app(args[0])->get_arg(0),
|
||||
m().mk_app(f, to_app(args[0])->get_arg(1), to_app(args[1])->get_arg(1)),
|
||||
m().mk_app(f, to_app(args[0])->get_arg(2), to_app(args[1])->get_arg(2)));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
if (m().is_ite(args[1]) && m().is_value(args[0]))
|
||||
return pull_ite_core<true>(f, to_app(args[1]), to_app(args[0]), result);
|
||||
}
|
||||
family_id fid = f->get_family_id();
|
||||
if (num == 2 && (fid == m().get_basic_family_id() || fid == m_a_rw.get_fid() || fid == m_bv_rw.get_fid())) {
|
||||
// (f v3 (ite c v1 v2)) --> (ite v (f v3 v1) (f v3 v2))
|
||||
if (m().is_value(args[0]) && is_ite_value_tree(args[1]))
|
||||
return pull_ite_core<true>(f, to_app(args[1]), to_app(args[0]), result);
|
||||
|
||||
// (f (ite c v1 v2) v3) --> (ite v (f v1 v3) (f v2 v3))
|
||||
if (m().is_value(args[1]) && is_ite_value_tree(args[0]))
|
||||
return pull_ite_core<false>(f, to_app(args[0]), to_app(args[1]), result);
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status pull_ite(expr_ref & result) {
|
||||
expr * t = result.get();
|
||||
if (is_app(t)) {
|
||||
br_status st = pull_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result);
|
||||
if (st != BR_FAILED)
|
||||
return st;
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
bool is_arith_bv_app(expr * t) const {
|
||||
if (!is_app(t))
|
||||
return false;
|
||||
family_id fid = to_app(t)->get_family_id();
|
||||
return ((fid == m_a_rw.get_fid() && m_push_ite_arith) ||
|
||||
(fid == m_bv_rw.get_fid() && m_push_ite_bv));
|
||||
}
|
||||
|
||||
bool get_neutral_elem(app * t, expr_ref & n) {
|
||||
family_id fid = t->get_family_id();
|
||||
if (fid == m_a_rw.get_fid()) {
|
||||
switch (t->get_decl_kind()) {
|
||||
case OP_ADD: n = m_a_util.mk_numeral(rational(0), m().get_sort(t)); return true;
|
||||
case OP_MUL: n = m_a_util.mk_numeral(rational(1), m().get_sort(t)); return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (fid == m_bv_rw.get_fid()) {
|
||||
switch (t->get_decl_kind()) {
|
||||
case OP_BADD: n = m_bv_util.mk_numeral(rational(0), m().get_sort(t)); return true;
|
||||
case OP_BMUL: n = m_bv_util.mk_numeral(rational(1), m().get_sort(t)); return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Try to "unify" t1 and t2
|
||||
Examples
|
||||
(+ 2 a) (+ 3 a) --> 2, 3, a
|
||||
(+ 2 a) a --> 2, 0, a
|
||||
...
|
||||
*/
|
||||
bool unify_core(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) {
|
||||
if (t1->get_num_args() != 2)
|
||||
return false;
|
||||
expr * a1 = t1->get_arg(0);
|
||||
expr * b1 = t1->get_arg(1);
|
||||
if (t2 == b1) {
|
||||
if (get_neutral_elem(t1, new_t2)) {
|
||||
new_t1 = a1;
|
||||
c = b1;
|
||||
first = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (t2 == a1) {
|
||||
if (get_neutral_elem(t1, new_t2)) {
|
||||
new_t1 = b1;
|
||||
c = a1;
|
||||
first = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (is_app_of(t2, t1->get_decl()) && to_app(t2)->get_num_args() == 2) {
|
||||
expr * a2 = to_app(t2)->get_arg(0);
|
||||
expr * b2 = to_app(t2)->get_arg(1);
|
||||
if (b1 == b2) {
|
||||
new_t1 = a1;
|
||||
new_t2 = a2;
|
||||
c = b2;
|
||||
first = false;
|
||||
return true;
|
||||
}
|
||||
if (a1 == a2) {
|
||||
new_t1 = b1;
|
||||
new_t2 = b2;
|
||||
c = a1;
|
||||
first = true;
|
||||
return true;
|
||||
}
|
||||
if (t1->get_decl()->is_commutative()) {
|
||||
if (a1 == b2) {
|
||||
new_t1 = b1;
|
||||
new_t2 = a2;
|
||||
c = a1;
|
||||
first = true; // doesn't really matter for commutative ops.
|
||||
return true;
|
||||
}
|
||||
if (b1 == a2) {
|
||||
new_t1 = a1;
|
||||
new_t2 = b2;
|
||||
c = b1;
|
||||
first = false; // doesn't really matter for commutative ops.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if t1 and t2 are of the form:
|
||||
// t + a1*x1 + ... + an*xn
|
||||
// t' + a1*x1 + ... + an*xn
|
||||
// Store t in new_t1, t' in new_t2 and (a1*x1 + ... + an*xn) in c.
|
||||
bool unify_add(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c) {
|
||||
unsigned num1 = t1->get_num_args();
|
||||
expr * const * ms1 = t1->get_args();
|
||||
if (num1 < 2)
|
||||
return false;
|
||||
unsigned num2;
|
||||
expr * const * ms2;
|
||||
if (m_a_util.is_add(t2)) {
|
||||
num2 = to_app(t2)->get_num_args();
|
||||
ms2 = to_app(t2)->get_args();
|
||||
}
|
||||
else {
|
||||
num2 = 1;
|
||||
ms2 = &t2;
|
||||
}
|
||||
if (num1 != num2 && num1 != num2 + 1 && num1 != num2 - 1)
|
||||
return false;
|
||||
new_t1 = 0;
|
||||
new_t2 = 0;
|
||||
expr_fast_mark1 visited1;
|
||||
expr_fast_mark2 visited2;
|
||||
for (unsigned i = 0; i < num1; i++) {
|
||||
expr * arg = ms1[i];
|
||||
visited1.mark(arg);
|
||||
}
|
||||
for (unsigned i = 0; i < num2; i++) {
|
||||
expr * arg = ms2[i];
|
||||
visited2.mark(arg);
|
||||
if (visited1.is_marked(arg))
|
||||
continue;
|
||||
if (new_t2)
|
||||
return false; // more than one missing term
|
||||
new_t2 = arg;
|
||||
}
|
||||
for (unsigned i = 0; i < num1; i++) {
|
||||
expr * arg = ms1[i];
|
||||
if (visited2.is_marked(arg))
|
||||
continue;
|
||||
if (new_t1)
|
||||
return false; // more than one missing term
|
||||
new_t1 = arg;
|
||||
}
|
||||
// terms matched...
|
||||
bool is_int = m_a_util.is_int(t1);
|
||||
if (!new_t1)
|
||||
new_t1 = m_a_util.mk_numeral(rational(0), is_int);
|
||||
if (!new_t2)
|
||||
new_t2 = m_a_util.mk_numeral(rational(0), is_int);
|
||||
// mk common part
|
||||
ptr_buffer<expr> args;
|
||||
for (unsigned i = 0; i < num1; i++) {
|
||||
expr * arg = ms1[i];
|
||||
if (arg == new_t1.get())
|
||||
continue;
|
||||
args.push_back(arg);
|
||||
}
|
||||
SASSERT(!args.empty());
|
||||
if (args.size() == 1)
|
||||
c = args[0];
|
||||
else
|
||||
c = m_a_util.mk_add(args.size(), args.c_ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unify(expr * t1, expr * t2, func_decl * & f, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) {
|
||||
#if 0
|
||||
// Did not work for ring benchmarks
|
||||
|
||||
// Hack for handling more complex cases of + apps
|
||||
// such as (+ 2 t1 t2 t3) and (+ 3 t3 t2 t1)
|
||||
if (m_a_util.is_add(t1)) {
|
||||
first = true; // doesn't matter for AC ops
|
||||
f = to_app(t1)->get_decl();
|
||||
if (unify_add(to_app(t1), t2, new_t1, new_t2, c))
|
||||
return true;
|
||||
}
|
||||
if (m_a_util.is_add(t2)) {
|
||||
first = true; // doesn't matter for AC ops
|
||||
f = to_app(t2)->get_decl();
|
||||
if (unify_add(to_app(t2), t1, new_t2, new_t1, c))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_arith_bv_app(t1)) {
|
||||
f = to_app(t1)->get_decl();
|
||||
return unify_core(to_app(t1), t2, new_t1, new_t2, c, first);
|
||||
}
|
||||
else {
|
||||
f = to_app(t2)->get_decl();
|
||||
return unify_core(to_app(t2), t1, new_t2, new_t1, c, first);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply transformations of the form
|
||||
//
|
||||
// (ite c (+ k1 a) (+ k2 a)) --> (+ (ite c k1 k2) a)
|
||||
// (ite c (* k1 a) (* k2 a)) --> (* (ite c k1 k2) a)
|
||||
//
|
||||
// These transformations are useful for bit-vector problems, since
|
||||
// they will minimize the number of adders/multipliers/etc
|
||||
br_status push_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
if (!m().is_ite(f))
|
||||
return BR_FAILED;
|
||||
expr * c = args[0];
|
||||
expr * t = args[1];
|
||||
expr * e = args[2];
|
||||
func_decl * f_prime = 0;
|
||||
expr_ref new_t(m()), new_e(m()), common(m());
|
||||
bool first;
|
||||
TRACE("push_ite", tout << "unifying:\n" << mk_ismt2_pp(t, m()) << "\n" << mk_ismt2_pp(e, m()) << "\n";);
|
||||
if (unify(t, e, f_prime, new_t, new_e, common, first)) {
|
||||
if (first)
|
||||
result = m().mk_app(f_prime, common, m().mk_ite(c, new_t, new_e));
|
||||
else
|
||||
result = m().mk_app(f_prime, m().mk_ite(c, new_t, new_e), common);
|
||||
return BR_DONE;
|
||||
}
|
||||
TRACE("push_ite", tout << "failed\n";);
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status push_ite(expr_ref & result) {
|
||||
expr * t = result.get();
|
||||
if (m().is_ite(t)) {
|
||||
br_status st = push_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result);
|
||||
if (st != BR_FAILED)
|
||||
return st;
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
br_status st = reduce_app_core(f, num, args, result);
|
||||
if (st != BR_DONE && st != BR_FAILED) {
|
||||
CTRACE("th_rewriter_step", st != BR_FAILED,
|
||||
tout << f->get_name() << "\n";
|
||||
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";
|
||||
tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";);
|
||||
return st;
|
||||
}
|
||||
if (m_push_ite_bv || m_push_ite_arith) {
|
||||
if (st == BR_FAILED)
|
||||
st = push_ite(f, num, args, result);
|
||||
else
|
||||
st = push_ite(result);
|
||||
}
|
||||
if (m_pull_cheap_ite) {
|
||||
if (st == BR_FAILED)
|
||||
st = pull_ite(f, num, args, result);
|
||||
else
|
||||
st = pull_ite(result);
|
||||
}
|
||||
CTRACE("th_rewriter_step", st != BR_FAILED,
|
||||
tout << f->get_name() << "\n";
|
||||
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";
|
||||
tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";);
|
||||
return st;
|
||||
}
|
||||
|
||||
bool reduce_quantifier(quantifier * old_q,
|
||||
expr * new_body,
|
||||
expr * const * new_patterns,
|
||||
expr * const * new_no_patterns,
|
||||
expr_ref & result,
|
||||
proof_ref & result_pr) {
|
||||
quantifier_ref q1(m());
|
||||
proof * p1 = 0;
|
||||
if (is_quantifier(new_body) &&
|
||||
to_quantifier(new_body)->is_forall() == old_q->is_forall() &&
|
||||
!old_q->has_patterns() &&
|
||||
!to_quantifier(new_body)->has_patterns()) {
|
||||
|
||||
quantifier * nested_q = to_quantifier(new_body);
|
||||
|
||||
ptr_buffer<sort> sorts;
|
||||
buffer<symbol> names;
|
||||
sorts.append(old_q->get_num_decls(), old_q->get_decl_sorts());
|
||||
names.append(old_q->get_num_decls(), old_q->get_decl_names());
|
||||
sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts());
|
||||
names.append(nested_q->get_num_decls(), nested_q->get_decl_names());
|
||||
|
||||
q1 = m().mk_quantifier(old_q->is_forall(),
|
||||
sorts.size(),
|
||||
sorts.c_ptr(),
|
||||
names.c_ptr(),
|
||||
nested_q->get_expr(),
|
||||
std::min(old_q->get_weight(), nested_q->get_weight()),
|
||||
old_q->get_qid(),
|
||||
old_q->get_skid(),
|
||||
0, 0, 0, 0);
|
||||
|
||||
SASSERT(is_well_sorted(m(), q1));
|
||||
|
||||
if (m().proofs_enabled()) {
|
||||
SASSERT(old_q->get_expr() == new_body);
|
||||
p1 = m().mk_pull_quant(old_q, q1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ptr_buffer<expr> new_patterns_buf;
|
||||
ptr_buffer<expr> new_no_patterns_buf;
|
||||
|
||||
new_patterns_buf.append(old_q->get_num_patterns(), new_patterns);
|
||||
new_no_patterns_buf.append(old_q->get_num_no_patterns(), new_no_patterns);
|
||||
|
||||
remove_duplicates(new_patterns_buf);
|
||||
remove_duplicates(new_no_patterns_buf);
|
||||
|
||||
q1 = m().update_quantifier(old_q,
|
||||
new_patterns_buf.size(), new_patterns_buf.c_ptr(), new_no_patterns_buf.size(), new_no_patterns_buf.c_ptr(),
|
||||
new_body);
|
||||
TRACE("reduce_quantifier", tout << mk_ismt2_pp(old_q, m()) << "\n----->\n" << mk_ismt2_pp(q1, m()) << "\n";);
|
||||
SASSERT(is_well_sorted(m(), q1));
|
||||
}
|
||||
|
||||
elim_unused_vars(m(), q1, result);
|
||||
|
||||
TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m()) << "\n";);
|
||||
|
||||
result_pr = 0;
|
||||
if (m().proofs_enabled()) {
|
||||
proof * p2 = 0;
|
||||
if (q1.get() != result.get())
|
||||
p2 = m().mk_elim_unused_vars(q1, result);
|
||||
result_pr = m().mk_transitivity(p1, p2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
th_rewriter_cfg(ast_manager & m, params_ref const & p):
|
||||
m_b_rw(m, p),
|
||||
m_a_rw(m, p),
|
||||
m_bv_rw(m, p),
|
||||
m_ar_rw(m, p),
|
||||
m_dt_rw(m),
|
||||
m_f_rw(m, p),
|
||||
m_dl_rw(m),
|
||||
m_a_util(m),
|
||||
m_bv_util(m),
|
||||
m_used_dependencies(m),
|
||||
m_subst(0) {
|
||||
updt_local_params(p);
|
||||
}
|
||||
|
||||
void set_substitution(expr_substitution * s) {
|
||||
reset();
|
||||
m_subst = s;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_subst = 0;
|
||||
}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & pr) {
|
||||
if (m_subst == 0)
|
||||
return false;
|
||||
expr_dependency * d = 0;
|
||||
if (m_subst->find(s, t, pr, d)) {
|
||||
m_used_dependencies = m().mk_join(m_used_dependencies, d);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_a_rw.set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
template class rewriter_tpl<th_rewriter_cfg>;
|
||||
|
||||
struct th_rewriter::imp : public rewriter_tpl<th_rewriter_cfg> {
|
||||
th_rewriter_cfg m_cfg;
|
||||
imp(ast_manager & m, params_ref const & p):
|
||||
rewriter_tpl<th_rewriter_cfg>(m, m.proofs_enabled(), m_cfg),
|
||||
m_cfg(m, p) {
|
||||
}
|
||||
};
|
||||
|
||||
th_rewriter::th_rewriter(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
ast_manager & th_rewriter::m() const {
|
||||
return m_imp->m();
|
||||
}
|
||||
|
||||
void th_rewriter::updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->cfg().updt_params(p);
|
||||
}
|
||||
|
||||
void th_rewriter::get_param_descrs(param_descrs & r) {
|
||||
bool_rewriter::get_param_descrs(r);
|
||||
arith_rewriter::get_param_descrs(r);
|
||||
bv_rewriter::get_param_descrs(r);
|
||||
array_rewriter::get_param_descrs(r);
|
||||
insert_max_memory(r);
|
||||
insert_max_steps(r);
|
||||
r.insert(":push-ite-arith", CPK_BOOL, "(default: false) push if-then-else over arithmetic terms.");
|
||||
r.insert(":push-ite-bv", CPK_BOOL, "(default: false) push if-then-else over bit-vector terms.");
|
||||
r.insert(":pull-cheap-ite", CPK_BOOL, "(default: false) pull if-then-else terms when cheap.");
|
||||
r.insert(":cache-all", CPK_BOOL, "(default: false) cache all intermediate results.");
|
||||
}
|
||||
|
||||
th_rewriter::~th_rewriter() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
unsigned th_rewriter::get_cache_size() const {
|
||||
return m_imp->get_cache_size();
|
||||
}
|
||||
|
||||
unsigned th_rewriter::get_num_steps() const {
|
||||
return m_imp->get_num_steps();
|
||||
}
|
||||
|
||||
void th_rewriter::set_cancel(bool f) {
|
||||
#pragma omp critical (th_rewriter)
|
||||
{
|
||||
m_imp->set_cancel(f);
|
||||
m_imp->cfg().set_cancel(f);
|
||||
}
|
||||
}
|
||||
|
||||
void th_rewriter::cleanup() {
|
||||
ast_manager & m = m_imp->m();
|
||||
#pragma omp critical (th_rewriter)
|
||||
{
|
||||
dealloc(m_imp);
|
||||
m_imp = alloc(imp, m, m_params);
|
||||
}
|
||||
}
|
||||
|
||||
void th_rewriter::reset() {
|
||||
m_imp->reset();
|
||||
m_imp->cfg().reset();
|
||||
}
|
||||
|
||||
void th_rewriter::operator()(expr_ref & term) {
|
||||
expr_ref result(term.get_manager());
|
||||
m_imp->operator()(term, result);
|
||||
term = result;
|
||||
}
|
||||
|
||||
void th_rewriter::operator()(expr * t, expr_ref & result) {
|
||||
m_imp->operator()(t, result);
|
||||
}
|
||||
|
||||
void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) {
|
||||
m_imp->operator()(t, result, result_pr);
|
||||
}
|
||||
|
||||
void th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) {
|
||||
m_imp->operator()(n, num_bindings, bindings, result);
|
||||
}
|
||||
|
||||
void th_rewriter::set_substitution(expr_substitution * s) {
|
||||
m_imp->reset(); // reset the cache
|
||||
m_imp->cfg().set_substitution(s);
|
||||
}
|
||||
|
||||
expr_dependency * th_rewriter::get_used_dependencies() {
|
||||
return m_imp->cfg().m_used_dependencies;
|
||||
}
|
||||
|
||||
void th_rewriter::reset_used_dependencies() {
|
||||
if (get_used_dependencies() != 0) {
|
||||
set_substitution(m_imp->cfg().m_subst); // reset cache preserving subst
|
||||
m_imp->cfg().m_used_dependencies = 0;
|
||||
}
|
||||
}
|
||||
65
src/ast/rewriter/th_rewriter.h
Normal file
65
src/ast/rewriter/th_rewriter.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
th_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Rewriter for applying all builtin (cheap) theory rewrite rules.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-07
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _TH_REWRITER_H_
|
||||
#define _TH_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter_types.h"
|
||||
#include"params.h"
|
||||
|
||||
class expr_substitution;
|
||||
|
||||
class th_rewriter {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
th_rewriter(ast_manager & m, params_ref const & p = params_ref());
|
||||
~th_rewriter();
|
||||
|
||||
ast_manager & m () const;
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
unsigned get_cache_size() const;
|
||||
unsigned get_num_steps() const;
|
||||
|
||||
void operator()(expr_ref& term);
|
||||
void operator()(expr * t, expr_ref & result);
|
||||
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
|
||||
void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result);
|
||||
|
||||
void cancel() { set_cancel(true); }
|
||||
void reset_cancel() { set_cancel(false); }
|
||||
void set_cancel(bool f);
|
||||
void cleanup();
|
||||
void reset();
|
||||
|
||||
void set_substitution(expr_substitution * s);
|
||||
|
||||
// Dependency tracking is very coarse.
|
||||
// The rewriter just keeps accumulating the dependencies of the used substitutions.
|
||||
// The following methods are used to recover and reset them.
|
||||
// Remark: reset_used_dependecies will reset the internal cache if get_used_dependencies() != 0
|
||||
expr_dependency * get_used_dependencies();
|
||||
void reset_used_dependencies();
|
||||
};
|
||||
|
||||
#endif
|
||||
211
src/ast/rewriter/var_subst.cpp
Normal file
211
src/ast/rewriter/var_subst.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/*++
|
||||
Copyright (c) 2007 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
var_subst.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Variable substitution.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2008-01-10
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"var_subst.h"
|
||||
#include"used_vars.h"
|
||||
#include"ast_ll_pp.h"
|
||||
#include"ast_pp.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"well_sorted.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
SASSERT(is_well_sorted(result.m(), n));
|
||||
m_reducer.reset();
|
||||
if (m_std_order)
|
||||
m_reducer.set_inv_bindings(num_args, args);
|
||||
else
|
||||
m_reducer.set_bindings(num_args, args);
|
||||
m_reducer(n, result);
|
||||
SASSERT(is_well_sorted(m_reducer.m(), result));
|
||||
TRACE("var_subst_bug",
|
||||
tout << "m_std_order: " << m_std_order << "\n" << mk_ismt2_pp(n, m_reducer.m()) << "\nusing\n";
|
||||
for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m_reducer.m()) << "\n";
|
||||
tout << "\n------>\n";
|
||||
tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";);
|
||||
}
|
||||
|
||||
void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) {
|
||||
SASSERT(is_well_sorted(m, q));
|
||||
if (is_ground(q->get_expr())) {
|
||||
// ignore patterns if the body is a ground formula.
|
||||
result = q->get_expr();
|
||||
return;
|
||||
}
|
||||
if (!q->may_have_unused_vars()) {
|
||||
result = q;
|
||||
return;
|
||||
}
|
||||
used_vars used;
|
||||
used.process(q->get_expr());
|
||||
unsigned num_patterns = q->get_num_patterns();
|
||||
for (unsigned i = 0; i < num_patterns; i++)
|
||||
used.process(q->get_pattern(i));
|
||||
unsigned num_no_patterns = q->get_num_no_patterns();
|
||||
for (unsigned i = 0; i < num_no_patterns; i++)
|
||||
used.process(q->get_no_pattern(i));
|
||||
|
||||
unsigned num_decls = q->get_num_decls();
|
||||
if (used.uses_all_vars(num_decls)) {
|
||||
q->set_no_unused_vars();
|
||||
result = q;
|
||||
return;
|
||||
}
|
||||
|
||||
ptr_buffer<sort> used_decl_sorts;
|
||||
buffer<symbol> used_decl_names;
|
||||
for (unsigned i = 0; i < num_decls; ++i) {
|
||||
if (used.contains(num_decls - i - 1)) {
|
||||
used_decl_sorts.push_back(q->get_decl_sort(i));
|
||||
used_decl_names.push_back(q->get_decl_name(i));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned num_removed = 0;
|
||||
expr_ref_buffer var_mapping(m);
|
||||
int next_idx = 0;
|
||||
unsigned sz = used.get_max_found_var_idx_plus_1();
|
||||
|
||||
for (unsigned i = 0; i < num_decls; ++i) {
|
||||
sort * s = used.contains(i);
|
||||
if (s) {
|
||||
var_mapping.push_back(m.mk_var(next_idx, s));
|
||||
next_idx++;
|
||||
}
|
||||
else {
|
||||
num_removed++;
|
||||
var_mapping.push_back(0);
|
||||
}
|
||||
}
|
||||
// (VAR 0) is in the first position of var_mapping.
|
||||
|
||||
for (unsigned i = num_decls; i < sz; i++) {
|
||||
sort * s = used.contains(i);
|
||||
if (s)
|
||||
var_mapping.push_back(m.mk_var(i - num_removed, s));
|
||||
else
|
||||
var_mapping.push_back(0);
|
||||
}
|
||||
|
||||
|
||||
// Remark:
|
||||
// (VAR 0) should be in the last position of var_mapping.
|
||||
// ...
|
||||
// (VAR (var_mapping.size() - 1)) should be in the first position.
|
||||
std::reverse(var_mapping.c_ptr(), var_mapping.c_ptr() + var_mapping.size());
|
||||
|
||||
expr_ref new_expr(m);
|
||||
var_subst subst(m);
|
||||
|
||||
subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr);
|
||||
|
||||
if (num_removed == num_decls) {
|
||||
result = new_expr;
|
||||
return;
|
||||
}
|
||||
|
||||
expr_ref tmp(m);
|
||||
expr_ref_buffer new_patterns(m);
|
||||
expr_ref_buffer new_no_patterns(m);
|
||||
|
||||
for (unsigned i = 0; i < num_patterns; i++) {
|
||||
subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp);
|
||||
new_patterns.push_back(tmp);
|
||||
}
|
||||
for (unsigned i = 0; i < num_no_patterns; i++) {
|
||||
subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp);
|
||||
new_no_patterns.push_back(tmp);
|
||||
}
|
||||
|
||||
result = m.mk_quantifier(q->is_forall(),
|
||||
used_decl_sorts.size(),
|
||||
used_decl_sorts.c_ptr(),
|
||||
used_decl_names.c_ptr(),
|
||||
new_expr,
|
||||
q->get_weight(),
|
||||
q->get_qid(),
|
||||
q->get_skid(),
|
||||
num_patterns,
|
||||
new_patterns.c_ptr(),
|
||||
num_no_patterns,
|
||||
new_no_patterns.c_ptr());
|
||||
to_quantifier(result)->set_no_unused_vars();
|
||||
SASSERT(is_well_sorted(m, result));
|
||||
}
|
||||
|
||||
void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result) {
|
||||
var_subst subst(m);
|
||||
expr_ref new_expr(m);
|
||||
subst(q->get_expr(), q->get_num_decls(), exprs, new_expr);
|
||||
TRACE("var_subst", tout << mk_pp(q, m) << "\n" << mk_pp(new_expr, m) << "\n";);
|
||||
inv_var_shifter shift(m);
|
||||
shift(new_expr, q->get_num_decls(), result);
|
||||
SASSERT(is_well_sorted(m, result));
|
||||
TRACE("instantiate_bug", tout << mk_ismt2_pp(q, m) << "\nusing\n";
|
||||
for (unsigned i = 0; i < q->get_num_decls(); i++) tout << mk_ismt2_pp(exprs[i], m) << "\n";
|
||||
tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";);
|
||||
}
|
||||
|
||||
static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector<sort>& sorts) {
|
||||
ast_mark mark;
|
||||
ptr_vector<expr> todo;
|
||||
todo.push_back(e);
|
||||
while (!todo.empty()) {
|
||||
e = todo.back();
|
||||
todo.pop_back();
|
||||
if (mark.is_marked(e)) {
|
||||
continue;
|
||||
}
|
||||
mark.mark(e, true);
|
||||
switch(e->get_kind()) {
|
||||
case AST_QUANTIFIER: {
|
||||
quantifier* q = to_quantifier(e);
|
||||
get_free_vars_offset(q->get_expr(), offset+q->get_num_decls(), sorts);
|
||||
break;
|
||||
}
|
||||
case AST_VAR: {
|
||||
var* v = to_var(e);
|
||||
if (v->get_idx() >= offset) {
|
||||
unsigned idx = v->get_idx()-offset;
|
||||
if (sorts.size() <= idx) {
|
||||
sorts.resize(idx+1);
|
||||
}
|
||||
if (!sorts[idx]) {
|
||||
sorts[idx] = v->get_sort();
|
||||
}
|
||||
SASSERT(sorts[idx] == v->get_sort());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_APP: {
|
||||
app* a = to_app(e);
|
||||
for (unsigned i = 0; i < a->get_num_args(); ++i) {
|
||||
todo.push_back(a->get_arg(i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void get_free_vars(expr* e, ptr_vector<sort>& sorts) {
|
||||
get_free_vars_offset(e, 0, sorts);
|
||||
}
|
||||
78
src/ast/rewriter/var_subst.h
Normal file
78
src/ast/rewriter/var_subst.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*++
|
||||
Copyright (c) 2007 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
var_subst.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Variable substitution.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2008-01-10
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _VAR_SUBST_H_
|
||||
#define _VAR_SUBST_H_
|
||||
|
||||
#include"rewriter.h"
|
||||
|
||||
/**
|
||||
\brief Alias for var_shifter class.
|
||||
*/
|
||||
typedef var_shifter shift_vars;
|
||||
|
||||
/**
|
||||
\brief Variable substitution functor. It substitutes variables by expressions.
|
||||
The expressions may contain variables.
|
||||
*/
|
||||
class var_subst {
|
||||
beta_reducer m_reducer;
|
||||
bool m_std_order;
|
||||
public:
|
||||
var_subst(ast_manager & m, bool std_order = true):m_reducer(m), m_std_order(std_order) {}
|
||||
bool std_order() const { return m_std_order; }
|
||||
|
||||
/**
|
||||
When std_order() == true,
|
||||
I'm using the same standard used in quantifier instantiation.
|
||||
(VAR 0) is stored in the last position of the array.
|
||||
...
|
||||
(VAR (num_args - 1)) is stored in the first position of the array.
|
||||
|
||||
Otherwise, (VAR 0) is stored in the first position, (VAR 1) in the second, and so on.
|
||||
*/
|
||||
void operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void reset() { m_reducer.reset(); }
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Eliminate the unused variables from \c q. Store the result in \c r.
|
||||
*/
|
||||
void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & r);
|
||||
|
||||
/**
|
||||
\brief Instantiate quantifier q using the given exprs.
|
||||
The vector exprs should contain at least q->get_num_decls() expressions.
|
||||
|
||||
I'm using the same standard used in quantifier instantiation.
|
||||
(VAR 0) is stored in the last position of the array.
|
||||
...
|
||||
(VAR (q->get_num_decls() - 1)) is stored in the first position of the array.
|
||||
*/
|
||||
void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result);
|
||||
|
||||
/**
|
||||
\brief Enumerate set of free variables in expression.
|
||||
|
||||
Return the sorts of the free variables.
|
||||
*/
|
||||
void get_free_vars(expr* e, ptr_vector<sort>& sorts);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
2
src/ast/simplifier/README
Normal file
2
src/ast/simplifier/README
Normal 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.
|
||||
26
src/ast/simplifier/arith_simplifier_params.cpp
Normal file
26
src/ast/simplifier/arith_simplifier_params.cpp
Normal 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);
|
||||
}
|
||||
|
||||
37
src/ast/simplifier/arith_simplifier_params.h
Normal file
37
src/ast/simplifier/arith_simplifier_params.h
Normal 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_ */
|
||||
|
||||
442
src/ast/simplifier/arith_simplifier_plugin.cpp
Normal file
442
src/ast/simplifier/arith_simplifier_plugin.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
95
src/ast/simplifier/arith_simplifier_plugin.h
Normal file
95
src/ast/simplifier/arith_simplifier_plugin.h
Normal 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_ */
|
||||
864
src/ast/simplifier/array_simplifier_plugin.cpp
Normal file
864
src/ast/simplifier/array_simplifier_plugin.cpp
Normal 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 1, &a );
|
||||
}
|
||||
|
||||
|
||||
154
src/ast/simplifier/array_simplifier_plugin.h
Normal file
154
src/ast/simplifier/array_simplifier_plugin.h
Normal 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_ */
|
||||
|
||||
54
src/ast/simplifier/base_simplifier.h
Normal file
54
src/ast/simplifier/base_simplifier.h
Normal 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_ */
|
||||
142
src/ast/simplifier/basic_simplifier_plugin.cpp
Normal file
142
src/ast/simplifier/basic_simplifier_plugin.cpp
Normal 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);
|
||||
}
|
||||
78
src/ast/simplifier/basic_simplifier_plugin.h
Normal file
78
src/ast/simplifier/basic_simplifier_plugin.h
Normal 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_ */
|
||||
421
src/ast/simplifier/bit2int.cpp
Normal file
421
src/ast/simplifier/bit2int.cpp
Normal 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);
|
||||
}
|
||||
96
src/ast/simplifier/bit2int.h
Normal file
96
src/ast/simplifier/bit2int.h
Normal 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_ */
|
||||
|
||||
113
src/ast/simplifier/bv_elim.cpp
Normal file
113
src/ast/simplifier/bv_elim.cpp
Normal 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);
|
||||
}
|
||||
45
src/ast/simplifier/bv_elim.h
Normal file
45
src/ast/simplifier/bv_elim.h
Normal 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_ */
|
||||
|
||||
39
src/ast/simplifier/bv_simplifier_params.h
Normal file
39
src/ast/simplifier/bv_simplifier_params.h
Normal 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_ */
|
||||
|
||||
2226
src/ast/simplifier/bv_simplifier_plugin.cpp
Normal file
2226
src/ast/simplifier/bv_simplifier_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
187
src/ast/simplifier/bv_simplifier_plugin.h
Normal file
187
src/ast/simplifier/bv_simplifier_plugin.h
Normal 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_ */
|
||||
113
src/ast/simplifier/datatype_simplifier_plugin.cpp
Normal file
113
src/ast/simplifier/datatype_simplifier_plugin.cpp
Normal 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;
|
||||
}
|
||||
|
||||
42
src/ast/simplifier/datatype_simplifier_plugin.h
Normal file
42
src/ast/simplifier/datatype_simplifier_plugin.h
Normal 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_ */
|
||||
170
src/ast/simplifier/distribute_forall.cpp
Normal file
170
src/ast/simplifier/distribute_forall.cpp
Normal 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);
|
||||
}
|
||||
82
src/ast/simplifier/distribute_forall.h
Normal file
82
src/ast/simplifier/distribute_forall.h
Normal 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_ */
|
||||
222
src/ast/simplifier/elim_bounds.cpp
Normal file
222
src/ast/simplifier/elim_bounds.cpp
Normal 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);
|
||||
}
|
||||
|
||||
69
src/ast/simplifier/elim_bounds.h
Normal file
69
src/ast/simplifier/elim_bounds.h
Normal 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_ */
|
||||
|
||||
142
src/ast/simplifier/inj_axiom.cpp
Normal file
142
src/ast/simplifier/inj_axiom.cpp
Normal 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;
|
||||
}
|
||||
|
||||
27
src/ast/simplifier/inj_axiom.h
Normal file
27
src/ast/simplifier/inj_axiom.h
Normal 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_ */
|
||||
|
||||
192
src/ast/simplifier/maximise_ac_sharing.cpp
Normal file
192
src/ast/simplifier/maximise_ac_sharing.cpp
Normal 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) {
|
||||
}
|
||||
124
src/ast/simplifier/maximise_ac_sharing.h
Normal file
124
src/ast/simplifier/maximise_ac_sharing.h
Normal 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_ */
|
||||
|
||||
812
src/ast/simplifier/poly_simplifier_plugin.cpp
Normal file
812
src/ast/simplifier/poly_simplifier_plugin.cpp
Normal 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;
|
||||
}
|
||||
151
src/ast/simplifier/poly_simplifier_plugin.h
Normal file
151
src/ast/simplifier/poly_simplifier_plugin.h
Normal 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_ */
|
||||
213
src/ast/simplifier/pull_ite_tree.cpp
Normal file
213
src/ast/simplifier/pull_ite_tree.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
101
src/ast/simplifier/pull_ite_tree.h
Normal file
101
src/ast/simplifier/pull_ite_tree.h
Normal 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_ */
|
||||
|
||||
220
src/ast/simplifier/push_app_ite.cpp
Normal file
220
src/ast/simplifier/push_app_ite.cpp
Normal 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) {
|
||||
}
|
||||
63
src/ast/simplifier/push_app_ite.h
Normal file
63
src/ast/simplifier/push_app_ite.h
Normal 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_ */
|
||||
|
||||
949
src/ast/simplifier/simplifier.cpp
Normal file
949
src/ast/simplifier/simplifier.cpp
Normal 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);
|
||||
}
|
||||
|
||||
232
src/ast/simplifier/simplifier.h
Normal file
232
src/ast/simplifier/simplifier.h
Normal 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
|
||||
46
src/ast/simplifier/simplifier_plugin.cpp
Normal file
46
src/ast/simplifier/simplifier_plugin.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
94
src/ast/simplifier/simplifier_plugin.h
Normal file
94
src/ast/simplifier/simplifier_plugin.h
Normal 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
|
||||
76
src/ast/simplifier/theory_array_params.h
Normal file
76
src/ast/simplifier/theory_array_params.h
Normal 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_ */
|
||||
|
||||
53
src/ast/substitution/expr_offset.h
Normal file
53
src/ast/substitution/expr_offset.h
Normal 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_ */
|
||||
94
src/ast/substitution/expr_offset_map.h
Normal file
94
src/ast/substitution/expr_offset_map.h
Normal 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_ */
|
||||
81
src/ast/substitution/matcher.cpp
Normal file
81
src/ast/substitution/matcher.cpp
Normal 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();
|
||||
}
|
||||
64
src/ast/substitution/matcher.h
Normal file
64
src/ast/substitution/matcher.h
Normal 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_ */
|
||||
|
||||
286
src/ast/substitution/substitution.cpp
Normal file
286
src/ast/substitution/substitution.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
|
||||
202
src/ast/substitution/substitution.h
Normal file
202
src/ast/substitution/substitution.h
Normal 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
|
||||
899
src/ast/substitution/substitution_tree.cpp
Normal file
899
src/ast/substitution/substitution_tree.cpp
Normal 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();
|
||||
}
|
||||
148
src/ast/substitution/substitution_tree.h
Normal file
148
src/ast/substitution/substitution_tree.h
Normal 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_ */
|
||||
|
||||
183
src/ast/substitution/unifier.cpp
Normal file
183
src/ast/substitution/unifier.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue