mirror of
https://github.com/Z3Prover/z3
synced 2025-06-20 12:53:38 +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
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
|
Loading…
Add table
Add a link
Reference in a new issue