z3-z3-4.13.0.src.tactic.arith.degree_shift_tactic.cpp Maven / Gradle / Ivy
The newest version!
/*++
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 "tactic/tactical.h"
#include "ast/converters/generic_model_converter.h"
#include "ast/arith_decl_plugin.h"
#include "tactic/core/simplify_tactic.h"
#include "ast/ast_smt2_pp.h"
#include "ast/rewriter/rewriter_def.h"
class degree_shift_tactic : public tactic {
struct imp {
ast_manager & m;
arith_util m_autil;
obj_map m_var2degree;
obj_map m_var2var;
obj_map m_var2pr;
expr_ref_vector m_pinned;
ptr_vector m_todo;
rational m_one;
bool m_produce_models;
bool m_produce_proofs;
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 m_cfg;
public:
rw(imp & o):
rewriter_tpl(o.m, o.m_produce_proofs, m_cfg),
m_cfg(o) {
}
};
scoped_ptr m_rw;
imp(ast_manager & _m):
m(_m),
m_autil(_m),
m_pinned(_m),
m_one(1),
m_rw(nullptr) {
}
void checkpoint() {
if (!m.inc())
throw tactic_exception(m.limit().get_cancel_msg());
}
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)) {
for (expr * arg : *to_app(t)) {
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";
for (auto const& kv : m_var2degree) {
if (!kv.m_value.is_one()) {
out << "POWER: " << kv.m_value << "\n" << mk_ismt2_pp(kv.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 to_delete;
for (auto const& kv : m_var2degree) {
if (kv.m_value.is_one())
to_delete.push_back(kv.m_key);
else
m_pinned.push_back(kv.m_key); // make sure it is not deleted during simplifications
}
for (app* a : to_delete)
m_var2degree.erase(a);
}
void prepare_substitution(model_converter_ref & mc) {
SASSERT(!m_var2degree.empty());
generic_model_converter * xmc = nullptr;
if (m_produce_models) {
xmc = alloc(generic_model_converter, m, "degree_shift");
mc = xmc;
}
for (auto const& kv : m_var2degree) {
SASSERT(kv.m_value.is_int());
SASSERT(kv.m_value >= rational(2));
app * fresh = m.mk_fresh_const(nullptr, kv.m_key->get_decl()->get_range());
m_pinned.push_back(fresh);
m_var2var.insert(kv.m_key, fresh);
if (m_produce_models) {
xmc->hide(fresh->get_decl());
xmc->add(kv.m_key->get_decl(), mk_power(fresh, rational(1)/kv.m_value));
}
if (m_produce_proofs) {
expr * s = mk_power(kv.m_key, kv.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(kv.m_key, result_pr);
}
}
}
void operator()(goal_ref const & g,
goal_ref_buffer & result) {
m_produce_proofs = g->proofs_enabled();
m_produce_models = g->models_enabled();
tactic_report report("degree_shift", *g);
collect(*g);
model_converter_ref mc;
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
for (auto const& kv : m_var2degree) {
SASSERT(kv.m_value.is_int());
SASSERT(kv.m_value >= rational(2));
if (kv.m_value.is_even()) {
app * new_var = m_var2var.find(kv.m_key);
app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false));
proof * new_pr = nullptr;
if (m_produce_proofs) {
proof * pr = m_var2pr.find(kv.m_key);
new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr);
}
g->assert_expr(new_c, new_pr, nullptr);
}
}
}
g->inc_depth();
g->add(mc.get());
result.push_back(g.get());
TRACE("degree_shift", g->display(tout); if (mc) mc->display(tout););
}
};
imp * m_imp;
public:
degree_shift_tactic(ast_manager & m) {
m_imp = alloc(imp, m);
}
tactic * translate(ast_manager & m) override {
return alloc(degree_shift_tactic, m);
}
~degree_shift_tactic() override {
dealloc(m_imp);
}
char const* name() const override { return "degree_shift"; }
void operator()(goal_ref const & in,
goal_ref_buffer & result) override {
(*m_imp)(in, result);
}
void cleanup() override {
imp * d = alloc(imp, m_imp->m);
std::swap(d, m_imp);
dealloc(d);
}
};
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)));
}