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

350 lines
12 KiB
C++

/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
degree_shift_tactic.cpp
Abstract:
Simple degree shift procedure.
Basic idea: if goal G contains a real variable x, x occurs with degrees
d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1.
Then, replace x^n with a new fresh variable y.
Author:
Leonardo de Moura (leonardo) 2011-12-30.
Revision History:
--*/
#include"tactical.h"
#include"filter_model_converter.h"
#include"extension_model_converter.h"
#include"cooperate.h"
#include"arith_decl_plugin.h"
#include"simplify_tactic.h"
#include"ast_smt2_pp.h"
#include"rewriter_def.h"
class degree_shift_tactic : public tactic {
struct imp {
ast_manager & m;
arith_util m_autil;
obj_map<app, rational> m_var2degree;
obj_map<app, app*> m_var2var;
obj_map<app, proof*> m_var2pr;
expr_ref_vector m_pinned;
ptr_vector<expr> m_todo;
rational m_one;
bool m_produce_models;
bool m_produce_proofs;
volatile bool m_cancel;
expr * mk_power(expr * t, rational const & k) {
if (k.is_one())
return t;
else
return m_autil.mk_power(t, m_autil.mk_numeral(k, false));
}
struct rw_cfg : public default_rewriter_cfg {
imp & o;
rw_cfg(imp & _o):o(_o) {}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
arith_util & u = o.m_autil;
if (!is_decl_of(f, u.get_family_id(), OP_POWER) || !is_app(args[0]))
return BR_FAILED;
ast_manager & m = o.m;
rational g;
app * t = to_app(args[0]);
if (!o.m_var2degree.find(t, g))
return BR_FAILED;
SASSERT(g > rational(1));
SASSERT(g.is_int());
rational k;
VERIFY(u.is_numeral(args[1], k));
SASSERT(gcd(k, g) == g);
rational new_k = div(k, g);
expr * new_arg = o.m_var2var.find(t);
result = o.mk_power(new_arg, new_k);
if (o.m_produce_proofs) {
proof * pr = o.m_var2pr.find(t);
app * fact = m.mk_eq(m.mk_app(f, num, args), result);
result_pr = m.mk_th_lemma(u.get_family_id(), fact, 1, &pr);
}
return BR_DONE;
}
};
class rw : public rewriter_tpl<rw_cfg> {
rw_cfg m_cfg;
public:
rw(imp & o):
rewriter_tpl<rw_cfg>(o.m, o.m_produce_proofs, m_cfg),
m_cfg(o) {
}
};
scoped_ptr<rw> m_rw;
imp(ast_manager & _m):
m(_m),
m_autil(_m),
m_pinned(_m),
m_one(1),
m_rw(0) {
m_cancel = false;
}
void set_cancel(bool f) {
m_cancel = f;
}
void checkpoint() {
if (m_cancel)
throw tactic_exception(TACTIC_CANCELED_MSG);
cooperate("degree_shift");
}
void visit(expr * t, expr_fast_mark1 & visited) {
if (!visited.is_marked(t)) {
visited.mark(t);
m_todo.push_back(t);
}
}
void save_degree(expr * t, rational const & k) {
SASSERT(k.is_int());
if (is_uninterp_const(t) && m_autil.is_real(t)) {
rational old_k;
if (m_var2degree.find(to_app(t), old_k)) {
old_k = gcd(k, old_k);
m_var2degree.insert(to_app(t), old_k);
}
else {
m_var2degree.insert(to_app(t), k);
}
}
}
void visit_args(expr * t, expr_fast_mark1 & visited) {
if (is_app(t)) {
unsigned num_args = to_app(t)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(t)->get_arg(i);
save_degree(arg, m_one);
visit(arg, visited);
}
}
}
void collect(expr * t, expr_fast_mark1 & visited) {
rational k;
visit(t, visited);
while (!m_todo.empty()) {
checkpoint();
expr * t = m_todo.back();
m_todo.pop_back();
if (is_var(t))
continue;
if (is_quantifier(t)) {
unsigned num_children = to_quantifier(t)->get_num_children();
for (unsigned i = 0; i < num_children; i ++)
visit(to_quantifier(t)->get_child(i), visited);
}
else {
SASSERT(is_app(t));
if (m_autil.is_power(t) && m_autil.is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k.is_pos()) {
expr * arg = to_app(t)->get_arg(0);
save_degree(arg, k);
visit_args(arg, visited);
}
else {
visit_args(t, visited);
}
}
}
}
void display_candidates(std::ostream & out) {
out << "candidates:\n";
obj_map<app, rational>::iterator it = m_var2degree.begin();
obj_map<app, rational>::iterator end = m_var2degree.end();
for (; it != end; ++it) {
if (!it->m_value.is_one()) {
out << "POWER: " << it->m_value << "\n" << mk_ismt2_pp(it->m_key, m) << "\n";
}
}
}
void collect(goal const & g) {
m_var2degree.reset();
expr_fast_mark1 visited;
unsigned sz = g.size();
for (unsigned i = 0; i < sz; i++) {
collect(g.form(i), visited);
}
TRACE("degree_shift", display_candidates(tout););
}
void discard_non_candidates() {
m_pinned.reset();
ptr_vector<app> to_delete;
obj_map<app, rational>::iterator it = m_var2degree.begin();
obj_map<app, rational>::iterator end = m_var2degree.end();
for (; it != end; ++it) {
if (it->m_value.is_one())
to_delete.push_back(it->m_key);
else
m_pinned.push_back(it->m_key); // make sure it is not deleted during simplifications
}
ptr_vector<app>::iterator it2 = to_delete.begin();
ptr_vector<app>::iterator end2 = to_delete.end();
for (; it2 != end2; ++it2)
m_var2degree.erase(*it2);
}
void prepare_substitution(model_converter_ref & mc) {
SASSERT(!m_var2degree.empty());
filter_model_converter * fmc = 0;
extension_model_converter * xmc = 0;
if (m_produce_models) {
fmc = alloc(filter_model_converter, m);
xmc = alloc(extension_model_converter, m);
mc = concat(fmc, xmc);
}
obj_map<app, rational>::iterator it = m_var2degree.begin();
obj_map<app, rational>::iterator end = m_var2degree.end();
for (; it != end; ++it) {
SASSERT(it->m_value.is_int());
SASSERT(it->m_value >= rational(2));
app * fresh = m.mk_fresh_const(0, it->m_key->get_decl()->get_range());
m_pinned.push_back(fresh);
m_var2var.insert(it->m_key, fresh);
if (m_produce_models) {
fmc->insert(fresh->get_decl());
xmc->insert(it->m_key->get_decl(), mk_power(fresh, rational(1)/it->m_value));
}
if (m_produce_proofs) {
expr * s = mk_power(it->m_key, it->m_value);
expr * eq = m.mk_eq(fresh, s);
proof * pr1 = m.mk_def_intro(eq);
proof * result_pr = m.mk_apply_def(fresh, s, pr1);
m_pinned.push_back(result_pr);
m_var2pr.insert(it->m_key, result_pr);
}
}
}
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
SASSERT(g->is_well_sorted());
mc = 0; pc = 0; core = 0;
m_produce_proofs = g->proofs_enabled();
m_produce_models = g->models_enabled();
tactic_report report("degree_shift", *g);
collect(*g);
discard_non_candidates();
if (!m_var2degree.empty()) {
prepare_substitution(mc);
m_rw = alloc(rw, *this);
// substitute
expr_ref new_curr(m);
proof_ref new_pr(m);
unsigned size = g->size();
for (unsigned idx = 0; idx < size; idx++) {
checkpoint();
expr * curr = g->form(idx);
(*m_rw)(curr, new_curr, new_pr);
if (m_produce_proofs) {
proof * pr = g->pr(idx);
new_pr = m.mk_modus_ponens(pr, new_pr);
}
g->update(idx, new_curr, new_pr, g->dep(idx));
}
// add >= 0 constraints for variables with even degree
obj_map<app, rational>::iterator it = m_var2degree.begin();
obj_map<app, rational>::iterator end = m_var2degree.end();
for (; it != end; ++it) {
SASSERT(it->m_value.is_int());
SASSERT(it->m_value >= rational(2));
if (it->m_value.is_even()) {
app * new_var = m_var2var.find(it->m_key);
app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false));
proof * new_pr = 0;
if (m_produce_proofs) {
proof * pr = m_var2pr.find(it->m_key);
new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr);
}
g->assert_expr(new_c, new_pr, 0);
}
}
}
g->inc_depth();
result.push_back(g.get());
TRACE("degree_shift", g->display(tout); if (mc) mc->display(tout););
SASSERT(g->is_well_sorted());
}
};
imp * m_imp;
public:
degree_shift_tactic(ast_manager & m) {
m_imp = alloc(imp, m);
}
virtual tactic * translate(ast_manager & m) {
return alloc(degree_shift_tactic, m);
}
virtual ~degree_shift_tactic() {
dealloc(m_imp);
}
virtual void operator()(goal_ref const & in,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
(*m_imp)(in, result, mc, pc, core);
}
virtual void cleanup() {
ast_manager & m = m_imp->m;
imp * d = m_imp;
#pragma omp critical (tactic_cancel)
{
m_imp = 0;
}
dealloc(d);
d = alloc(imp, m);
#pragma omp critical (tactic_cancel)
{
m_imp = d;
}
}
protected:
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p) {
params_ref mul2power_p;
mul2power_p.set_bool(":mul-to-power", true);
return and_then(using_params(mk_simplify_tactic(m), mul2power_p),
clean(alloc(degree_shift_tactic, m)));
}