3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-08 18:31:49 +00:00
z3/lib/simplifier.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

950 lines
32 KiB
C++

/*++
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);
}