mirror of
https://github.com/Z3Prover/z3
synced 2025-07-18 02:16:40 +00:00
using a consistent naming convention for naming tactic subfolders
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
67f5ed46c1
commit
0990a2e045
140 changed files with 14 additions and 12 deletions
198
src/tactic/arith/add_bounds_tactic.cpp
Normal file
198
src/tactic/arith/add_bounds_tactic.cpp
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
add_bounds_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for bounding unbounded variables.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-10-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"bound_manager.h"
|
||||
|
||||
struct is_unbounded_proc {
|
||||
struct found {};
|
||||
arith_util m_util;
|
||||
bound_manager & m_bm;
|
||||
|
||||
is_unbounded_proc(bound_manager & bm):m_util(bm.m()), m_bm(bm) {}
|
||||
|
||||
void operator()(app * t) {
|
||||
if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t)) && (!m_bm.has_lower(t) || !m_bm.has_upper(t)))
|
||||
throw found();
|
||||
}
|
||||
|
||||
void operator()(var *) {}
|
||||
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
bool is_unbounded(goal const & g) {
|
||||
ast_manager & m = g.m();
|
||||
bound_manager bm(m);
|
||||
bm(g);
|
||||
is_unbounded_proc proc(bm);
|
||||
return test(g, proc);
|
||||
}
|
||||
|
||||
class is_unbounded_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_unbounded(g);
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_is_unbounded_probe() {
|
||||
return alloc(is_unbounded_probe);
|
||||
}
|
||||
|
||||
class add_bounds_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
rational m_lower;
|
||||
rational m_upper;
|
||||
volatile bool m_cancel;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_lower = p.get_rat(":add-bound-lower", rational(-2));
|
||||
m_upper = p.get_rat(":add-bound-upper", rational(2));
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
struct add_bound_proc {
|
||||
arith_util m_util;
|
||||
bound_manager & m_bm;
|
||||
goal & m_goal;
|
||||
rational const & m_lower;
|
||||
rational const & m_upper;
|
||||
unsigned m_num_bounds;
|
||||
|
||||
add_bound_proc(bound_manager & bm, goal & g, rational const & l, rational const & u):
|
||||
m_util(bm.m()),
|
||||
m_bm(bm),
|
||||
m_goal(g),
|
||||
m_lower(l),
|
||||
m_upper(u) {
|
||||
m_num_bounds = 0;
|
||||
}
|
||||
|
||||
void operator()(app * t) {
|
||||
if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t))) {
|
||||
if (!m_bm.has_lower(t)) {
|
||||
m_goal.assert_expr(m_util.mk_le(t, m_util.mk_numeral(m_upper, m_util.is_int(t))));
|
||||
m_num_bounds++;
|
||||
}
|
||||
if (!m_bm.has_upper(t)) {
|
||||
m_goal.assert_expr(m_util.mk_ge(t, m_util.mk_numeral(m_lower, m_util.is_int(t))));
|
||||
m_num_bounds++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(var *) {}
|
||||
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
mc = 0; pc = 0; core = 0;
|
||||
tactic_report report("add-bounds", *g);
|
||||
bound_manager bm(m);
|
||||
expr_fast_mark1 visited;
|
||||
add_bound_proc proc(bm, *(g.get()), m_lower, m_upper);
|
||||
unsigned sz = g->size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
quick_for_each_expr(proc, visited, g->form(i));
|
||||
visited.reset();
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
if (proc.m_num_bounds > 0)
|
||||
g->updt_prec(goal::UNDER);
|
||||
report_tactic_progress(":added-bounds", proc.m_num_bounds);
|
||||
TRACE("add_bounds", g->display(tout););
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
|
||||
public:
|
||||
add_bounds_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(add_bounds_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~add_bounds_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":add-bound-lower", CPK_NUMERAL, "(default: -2) lower bound to be added to unbounded variables.");
|
||||
r.insert(":add-bound-upper", CPK_NUMERAL, "(default: 2) upper bound to be added to unbounded variables.");
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(g, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#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_add_bounds_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(add_bounds_tactic, m, p));
|
||||
}
|
34
src/tactic/arith/add_bounds_tactic.h
Normal file
34
src/tactic/arith/add_bounds_tactic.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
add_bounds.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for bounding unbounded variables.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _ADD_BOUNDS_H_
|
||||
#define _ADD_BOUNDS_H_
|
||||
|
||||
#include"params.h"
|
||||
|
||||
class ast_manager;
|
||||
class goal;
|
||||
class tactic;
|
||||
class probe;
|
||||
|
||||
bool is_unbounded(goal const & g);
|
||||
probe * mk_is_unbounded_probe();
|
||||
|
||||
tactic * mk_add_bounds_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
233
src/tactic/arith/bound_manager.cpp
Normal file
233
src/tactic/arith/bound_manager.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Collect bounds.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-05-16
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"bound_manager.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"goal.h"
|
||||
|
||||
bound_manager::bound_manager(ast_manager & m):
|
||||
m_util(m) {
|
||||
}
|
||||
|
||||
bound_manager::~bound_manager() {
|
||||
reset();
|
||||
}
|
||||
|
||||
static decl_kind swap_decl(decl_kind k) {
|
||||
switch (k) {
|
||||
case OP_LE: return OP_GE;
|
||||
case OP_LT: return OP_GT;
|
||||
case OP_GE: return OP_LE;
|
||||
case OP_GT: return OP_LT;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
decl_kind bound_manager::neg(decl_kind k) {
|
||||
switch (k) {
|
||||
case OP_LE: return OP_GT;
|
||||
case OP_LT: return OP_GE;
|
||||
case OP_GE: return OP_LT;
|
||||
case OP_GT: return OP_LE;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_manager::norm(numeral & n, decl_kind & k) {
|
||||
switch (k) {
|
||||
case OP_LE: return;
|
||||
case OP_GE: return;
|
||||
case OP_LT:
|
||||
// x < n --> x <= n-1
|
||||
n--;
|
||||
k = OP_LE;
|
||||
return;
|
||||
case OP_GT:
|
||||
// x > n --> x >= n+1
|
||||
n++;
|
||||
k = OP_GE;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_lower(decl_kind k) {
|
||||
return k == OP_GT || k == OP_GE;
|
||||
}
|
||||
|
||||
static bool is_strict(decl_kind k) {
|
||||
return k == OP_LT || k == OP_GT;
|
||||
}
|
||||
|
||||
void bound_manager::operator()(expr * f, expr_dependency * d) {
|
||||
TRACE("bound_manager", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";);
|
||||
expr * v;
|
||||
numeral n;
|
||||
if (is_disjunctive_bound(f, d))
|
||||
return;
|
||||
bool pos = true;
|
||||
while (m().is_not(f, f))
|
||||
pos = !pos;
|
||||
if (!is_app(f))
|
||||
return;
|
||||
app * t = to_app(f);
|
||||
if (t->get_family_id() != m_util.get_family_id())
|
||||
return;
|
||||
decl_kind k = t->get_decl_kind();
|
||||
if (k != OP_LE && k != OP_GE && k != OP_LT && k != OP_GT)
|
||||
return;
|
||||
expr * lhs = t->get_arg(0);
|
||||
expr * rhs = t->get_arg(1);
|
||||
bool is_int;
|
||||
if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, n, is_int)) {
|
||||
v = lhs;
|
||||
}
|
||||
else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, n, is_int)) {
|
||||
v = rhs;
|
||||
k = swap_decl(k);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (!pos)
|
||||
k = neg(k);
|
||||
if (is_int)
|
||||
norm(n, k);
|
||||
TRACE("bound_manager", tout << "found bound for:\n" << mk_ismt2_pp(v, m()) << "\n";);
|
||||
bool strict = is_strict(k);
|
||||
if (is_lower(k)) {
|
||||
insert_lower(v, strict, n, d);
|
||||
}
|
||||
else {
|
||||
insert_upper(v, strict, n, d);
|
||||
}
|
||||
}
|
||||
|
||||
void bound_manager::insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d) {
|
||||
limit old;
|
||||
if (m_uppers.find(v, old)) {
|
||||
if (n < old.first || (n == old.first && strict && !old.second)) {
|
||||
// improved bound
|
||||
m_uppers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_upper_deps.insert(v, d);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_uppers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_upper_deps.insert(v, d);
|
||||
if (!m_lowers.contains(v)) {
|
||||
m_bounded_vars.push_back(v);
|
||||
m().inc_ref(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d) {
|
||||
limit old;
|
||||
if (m_lowers.find(v, old)) {
|
||||
if (n > old.first || (n == old.first && strict && !old.second)) {
|
||||
// improved bound
|
||||
m_lowers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_lower_deps.insert(v, d);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_lowers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_lower_deps.insert(v, d);
|
||||
if (!m_uppers.contains(v)) {
|
||||
m_bounded_vars.push_back(v);
|
||||
m().inc_ref(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) {
|
||||
numeral lo, hi, n;
|
||||
if (!m().is_or(f)) return false;
|
||||
unsigned sz = to_app(f)->get_num_args();
|
||||
if (sz == 0) return false;
|
||||
expr * x, * y, * v = 0;
|
||||
bool is_int;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr * e = to_app(f)->get_arg(i);
|
||||
if (!m().is_eq(e, x, y)) return false;
|
||||
if (is_uninterp_const(x) &&
|
||||
m_util.is_numeral(y, n, is_int) && is_int &&
|
||||
(x == v || v == 0)) {
|
||||
if (v == 0) { v = x; lo = hi = n; }
|
||||
if (n < lo) lo = n;
|
||||
if (n > hi) hi = n;
|
||||
}
|
||||
else if (is_uninterp_const(y) &&
|
||||
m_util.is_numeral(x, n, is_int) && is_int &&
|
||||
(y == v || v == 0)) {
|
||||
if (v == 0) { v = y; lo = hi = n; }
|
||||
if (n < lo) lo = n;
|
||||
if (n > hi) hi = n;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TRACE("bound_manager", tout << "bounds: " << lo << " " << hi << "\n";);
|
||||
insert_lower(v, false, lo, d);
|
||||
insert_upper(v, false, hi, d);
|
||||
return true;
|
||||
}
|
||||
|
||||
void bound_manager::operator()(goal const & g) {
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
operator()(g.form(i), g.dep(i));
|
||||
}
|
||||
}
|
||||
|
||||
void bound_manager::reset() {
|
||||
m().dec_array_ref(m_bounded_vars.size(), m_bounded_vars.c_ptr());
|
||||
m_bounded_vars.finalize();
|
||||
m_lowers.finalize();
|
||||
m_uppers.finalize();
|
||||
m_lower_deps.finalize();
|
||||
m_upper_deps.finalize();
|
||||
}
|
||||
|
||||
void bound_manager::display(std::ostream & out) const {
|
||||
numeral n; bool strict;
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
expr * v = *it;
|
||||
if (has_lower(v, n, strict))
|
||||
out << n << " " << (strict ? "<" : "<=");
|
||||
else
|
||||
out << "-oo <";
|
||||
out << " " << mk_ismt2_pp(v, m()) << " ";
|
||||
if (has_upper(v, n, strict))
|
||||
out << (strict ? "<" : "<=") << " " << n;
|
||||
else
|
||||
out << "< oo";
|
||||
out << "\n";
|
||||
}
|
||||
}
|
109
src/tactic/arith/bound_manager.h
Normal file
109
src/tactic/arith/bound_manager.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Collect bounds.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-05-16
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _BOUND_MANAGER_H_
|
||||
#define _BOUND_MANAGER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
|
||||
class goal;
|
||||
|
||||
class bound_manager {
|
||||
public:
|
||||
typedef rational numeral;
|
||||
private:
|
||||
typedef std::pair<numeral, bool> limit;
|
||||
arith_util m_util;
|
||||
obj_map<expr, limit> m_lowers;
|
||||
obj_map<expr, limit> m_uppers;
|
||||
obj_map<expr, expr_dependency*> m_lower_deps;
|
||||
obj_map<expr, expr_dependency*> m_upper_deps;
|
||||
ptr_vector<expr> m_bounded_vars;
|
||||
bool is_disjunctive_bound(expr * f, expr_dependency * d);
|
||||
void insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d);
|
||||
void insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d);
|
||||
public:
|
||||
static decl_kind neg(decl_kind k);
|
||||
static void norm(numeral & n, decl_kind & k);
|
||||
|
||||
bound_manager(ast_manager & m);
|
||||
~bound_manager();
|
||||
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
|
||||
void operator()(goal const & g);
|
||||
void operator()(expr * n, expr_dependency * d = 0);
|
||||
|
||||
bool has_lower(expr * c, numeral & v, bool & strict) const {
|
||||
limit l;
|
||||
if (m_lowers.find(c, l)) {
|
||||
v = l.first;
|
||||
strict = l.second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_upper(expr * c, numeral & v, bool & strict) const {
|
||||
limit l;
|
||||
if (m_uppers.find(c, l)) {
|
||||
v = l.first;
|
||||
strict = l.second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expr_dependency * lower_dep(expr * c) const {
|
||||
expr_dependency * d;
|
||||
if (m_lower_deps.find(c, d))
|
||||
return d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
expr_dependency * upper_dep(expr * c) const {
|
||||
expr_dependency * d;
|
||||
if (m_upper_deps.find(c, d))
|
||||
return d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool has_lower(expr * c) const {
|
||||
return m_lowers.contains(c);
|
||||
}
|
||||
|
||||
bool has_upper(expr * c) const {
|
||||
return m_uppers.contains(c);
|
||||
}
|
||||
|
||||
typedef ptr_vector<expr>::const_iterator iterator;
|
||||
|
||||
/**
|
||||
\brief Iterator for all bounded constants.
|
||||
*/
|
||||
iterator begin() const { return m_bounded_vars.begin(); }
|
||||
iterator end() const { return m_bounded_vars.end(); }
|
||||
|
||||
void reset();
|
||||
|
||||
// for debugging purposes
|
||||
void display(std::ostream & out) const;
|
||||
};
|
||||
|
||||
#endif
|
956
src/tactic/arith/bound_propagator.cpp
Normal file
956
src/tactic/arith/bound_propagator.cpp
Normal file
|
@ -0,0 +1,956 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_propagator.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Bound propagators for arithmetic.
|
||||
Support class for implementing strategies and search procedures
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"bound_propagator.h"
|
||||
#include<cmath>
|
||||
|
||||
// -------------------------------
|
||||
// Bound Relaxation configuration
|
||||
//
|
||||
// The idea is to minimize errors in floating point computations
|
||||
//
|
||||
// If RELAX_BOUNDS is undefined, then bound relaxation is disabled.
|
||||
// Otherwise, lower bounds l are relaxed using the formula
|
||||
// PRECISION * floor(l * INV_PRECISION + TOLERANCE)
|
||||
// and upper bounds u as:
|
||||
// PRECISION * ceil(u * INV_PRECISION - TOLERANCE)
|
||||
// In the LP literature, the suggested values are
|
||||
// l := 10^-5 * floor(l*10^5 + 10^-6)
|
||||
// u := 10^-5 * ceil(u*10^5 - 10^-6)
|
||||
// I'm using the following values because of strict bounds
|
||||
// l := 10^-6 * floor(l*10^6 + 10^-7)
|
||||
// u := 10^-6 * ceil(u*10^6 - 10^-7)
|
||||
#define RELAX_BOUNDS
|
||||
#define TOLERANCE 0.0000001
|
||||
#define PRECISION 0.000001
|
||||
#define INV_PRECISION 1000000.0
|
||||
// -------------------------------
|
||||
|
||||
bound_propagator::bound::bound(numeral_manager & m,
|
||||
mpq const & k,
|
||||
double approx_k,
|
||||
bool lower,
|
||||
bool strict,
|
||||
unsigned lvl,
|
||||
unsigned ts,
|
||||
bkind bk,
|
||||
unsigned c_idx,
|
||||
assumption a,
|
||||
bound * prev):
|
||||
m_approx_k(approx_k),
|
||||
m_lower(lower),
|
||||
m_strict(strict),
|
||||
m_kind(bk),
|
||||
m_level(lvl),
|
||||
m_timestamp(ts),
|
||||
m_prev(prev) {
|
||||
m.set(m_k, k);
|
||||
if (bk == DERIVED)
|
||||
m_constraint_idx = c_idx;
|
||||
else
|
||||
m_assumption = a;
|
||||
}
|
||||
|
||||
bound_propagator::bound_propagator(numeral_manager & _m, allocator & a, params_ref const & p):
|
||||
m(_m),
|
||||
m_allocator(a),
|
||||
m_eq_manager(m, a) {
|
||||
m_timestamp = 0;
|
||||
m_qhead = 0;
|
||||
m_conflict = null_var;
|
||||
updt_params(p);
|
||||
reset_statistics();
|
||||
}
|
||||
|
||||
bound_propagator::~bound_propagator() {
|
||||
m.del(m_tmp);
|
||||
reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraints_core() {
|
||||
constraint_vector::iterator it = m_constraints.begin();
|
||||
constraint_vector::iterator end = m_constraints.end();
|
||||
for (; it != end; ++it) {
|
||||
del_constraint(*it);
|
||||
}
|
||||
m_constraints.reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraints() {
|
||||
SASSERT(scope_lvl() == 0);
|
||||
if (m_constraints.empty())
|
||||
return;
|
||||
del_constraints_core();
|
||||
m_constraints.finalize();
|
||||
vector<wlist>::iterator it = m_watches.begin();
|
||||
vector<wlist>::iterator end = m_watches.end();
|
||||
for (; it != end; ++it)
|
||||
it->finalize();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraint(constraint & c) {
|
||||
switch (c.m_kind) {
|
||||
case LINEAR:
|
||||
m_eq_manager.del(c.m_eq);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::updt_params(params_ref const & p) {
|
||||
m_max_refinements = p.get_uint(":bound-max-refinements", 16);
|
||||
m_threshold = p.get_double(":bound-threshold", 0.05);
|
||||
m_small_interval = p.get_double(":bound-small-interval", 128);
|
||||
m_strict2double = p.get_double(":strict2double", 0.00001);
|
||||
}
|
||||
|
||||
void bound_propagator::get_param_descrs(param_descrs & r) {
|
||||
r.insert(":bound-max-refinements", CPK_UINT, "(default: 16) maximum number of bound refinements (per round) for unbounded variables.");
|
||||
r.insert(":bound-threshold", CPK_DOUBLE, "(default: 0.05) bound propagation improvement threshold ratio.");
|
||||
}
|
||||
|
||||
void bound_propagator::collect_statistics(statistics & st) const {
|
||||
st.update("bound conflicts", m_conflicts);
|
||||
st.update("bound propagations", m_propagations);
|
||||
st.update("bound false alarms", m_false_alarms);
|
||||
}
|
||||
|
||||
void bound_propagator::reset_statistics() {
|
||||
m_conflicts = 0;
|
||||
m_propagations = 0;
|
||||
m_false_alarms = 0;
|
||||
}
|
||||
|
||||
void bound_propagator::mk_var(var x, bool is_int) {
|
||||
m_is_int.reserve(x+1, false);
|
||||
m_dead.reserve(x+1, true);
|
||||
m_lowers.reserve(x+1, 0);
|
||||
m_uppers.reserve(x+1, 0);
|
||||
m_lower_refinements.reserve(x+1, 0);
|
||||
m_upper_refinements.reserve(x+1, 0);
|
||||
m_watches.reserve(x+1);
|
||||
|
||||
SASSERT(m_dead[x]);
|
||||
|
||||
m_is_int[x] = is_int;
|
||||
m_dead[x] = false;
|
||||
m_lowers[x] = 0;
|
||||
m_uppers[x] = 0;
|
||||
m_lower_refinements[x] = 0;
|
||||
m_upper_refinements[x] = 0;
|
||||
m_watches[x].reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_var(var x) {
|
||||
SASSERT(!m_dead[x]);
|
||||
m_dead[x] = true;
|
||||
// mark constraints containing x as dead.
|
||||
wlist & wl = m_watches[x];
|
||||
wlist::iterator it = wl.begin();
|
||||
wlist::iterator end = wl.end();
|
||||
for (; it != end; ++it) {
|
||||
m_constraints[*it].m_dead = true;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::mk_eq(unsigned sz, mpq * as, var * xs) {
|
||||
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
||||
init_eq(eq);
|
||||
}
|
||||
|
||||
void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) {
|
||||
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
||||
init_eq(eq);
|
||||
}
|
||||
|
||||
void bound_propagator::init_eq(linear_equation * eq) {
|
||||
if (eq == 0)
|
||||
return;
|
||||
unsigned c_idx = m_constraints.size();
|
||||
m_constraints.push_back(constraint());
|
||||
constraint & new_c = m_constraints.back();
|
||||
new_c.m_kind = LINEAR;
|
||||
new_c.m_dead = false;
|
||||
new_c.m_timestamp = 0;
|
||||
new_c.m_act = 0;
|
||||
new_c.m_counter = 0;
|
||||
new_c.m_eq = eq;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m_watches[eq->x(i)].push_back(c_idx);
|
||||
}
|
||||
if (propagate(c_idx) && scope_lvl() > 0)
|
||||
m_reinit_stack.push_back(c_idx);
|
||||
}
|
||||
|
||||
void bound_propagator::push() {
|
||||
m_scopes.push_back(scope());
|
||||
scope & s = m_scopes.back();
|
||||
s.m_trail_limit = m_trail.size();
|
||||
s.m_qhead_old = m_qhead;
|
||||
s.m_reinit_stack_limit = m_reinit_stack.size();
|
||||
s.m_timestamp_old = m_timestamp;
|
||||
s.m_in_conflict = inconsistent();
|
||||
}
|
||||
|
||||
void bound_propagator::undo_trail(unsigned old_sz) {
|
||||
SASSERT(old_sz <= m_trail.size());
|
||||
unsigned i = m_trail.size();
|
||||
while (i > old_sz) {
|
||||
--i;
|
||||
trail_info & info = m_trail.back();
|
||||
var x = info.x();
|
||||
bool is_lower = info.is_lower();
|
||||
m_trail.pop_back();
|
||||
bound * b;
|
||||
if (is_lower) {
|
||||
b = m_lowers[x];
|
||||
m_lowers[x] = b->m_prev;
|
||||
}
|
||||
else {
|
||||
b = m_uppers[x];
|
||||
m_uppers[x] = b->m_prev;
|
||||
}
|
||||
m.del(b->m_k);
|
||||
b->~bound();
|
||||
m_allocator.deallocate(sizeof(bound), b);
|
||||
}
|
||||
SASSERT(m_trail.size() == old_sz);
|
||||
}
|
||||
|
||||
void bound_propagator::pop(unsigned num_scopes) {
|
||||
unsigned lvl = scope_lvl();
|
||||
SASSERT(num_scopes <= lvl);
|
||||
unsigned new_lvl = lvl - num_scopes;
|
||||
scope & s = m_scopes[new_lvl];
|
||||
undo_trail(s.m_trail_limit);
|
||||
m_timestamp = s.m_timestamp_old;
|
||||
m_qhead = s.m_qhead_old;
|
||||
if (!s.m_in_conflict)
|
||||
m_conflict = null_var;
|
||||
unsigned reinit_stack_sz = s.m_reinit_stack_limit;
|
||||
m_scopes.shrink(new_lvl);
|
||||
|
||||
// reinitialize
|
||||
unsigned i = reinit_stack_sz;
|
||||
unsigned j = reinit_stack_sz;
|
||||
unsigned sz = m_reinit_stack.size();
|
||||
for (; i < sz; i++) {
|
||||
unsigned c_idx = m_reinit_stack[i];
|
||||
bool p = propagate(c_idx);
|
||||
if (new_lvl > 0 && p) {
|
||||
m_reinit_stack[j] = c_idx;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
m_reinit_stack.shrink(j);
|
||||
}
|
||||
|
||||
bool bound_propagator::assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
||||
if (is_int(x)) {
|
||||
if (m.is_int(k)) {
|
||||
if (strict)
|
||||
m.inc(k);
|
||||
}
|
||||
else {
|
||||
m.ceil(k, k);
|
||||
}
|
||||
SASSERT(m.is_int(k));
|
||||
strict = false;
|
||||
}
|
||||
TRACE("bound_propagator_detail", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
|
||||
bound * old_lower = m_lowers[x];
|
||||
if (old_lower) {
|
||||
bool improves = m.gt(k, old_lower->m_k) || (!old_lower->m_strict && strict && m.eq(k, old_lower->m_k));
|
||||
if (!improves) {
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
||||
m_false_alarms++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_derived", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
m_propagations++;
|
||||
}
|
||||
|
||||
if (scope_lvl() == 0 && bk == DERIVED)
|
||||
bk = AXIOM; // don't need justification at level 0
|
||||
|
||||
double approx_k = m.get_double(k);
|
||||
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
||||
#ifdef RELAX_BOUNDS
|
||||
approx_k = PRECISION*floor(approx_k*INV_PRECISION + TOLERANCE);
|
||||
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
||||
#endif
|
||||
void * mem = m_allocator.allocate(sizeof(bound));
|
||||
bound * new_lower = new (mem) bound(m, k, approx_k, true, strict, scope_lvl(), m_timestamp, bk, c_idx, a, old_lower);
|
||||
m_timestamp++;
|
||||
m_lowers[x] = new_lower;
|
||||
m_trail.push_back(trail_info(x, true));
|
||||
m_lower_refinements[x]++;
|
||||
check_feasibility(x);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
||||
if (is_int(x)) {
|
||||
if (m.is_int(k)) {
|
||||
if (strict)
|
||||
m.dec(k);
|
||||
}
|
||||
else {
|
||||
m.floor(k, k);
|
||||
}
|
||||
SASSERT(m.is_int(k));
|
||||
strict = false;
|
||||
}
|
||||
|
||||
TRACE("bound_propagator_detail", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
|
||||
bound * old_upper = m_uppers[x];
|
||||
if (old_upper) {
|
||||
bool improves = m.lt(k, old_upper->m_k) || (!old_upper->m_strict && strict && m.eq(k, old_upper->m_k));
|
||||
if (!improves) {
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
||||
m_false_alarms++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bk == DERIVED) {
|
||||
m_propagations++;
|
||||
TRACE("bound_propagator_derived", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
}
|
||||
|
||||
if (scope_lvl() == 0 && bk == DERIVED)
|
||||
bk = AXIOM; // don't need justification at level 0
|
||||
|
||||
double approx_k = m.get_double(k);
|
||||
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
||||
#ifdef RELAX_BOUNDS
|
||||
approx_k = PRECISION*ceil(approx_k*INV_PRECISION - TOLERANCE);
|
||||
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
||||
#endif
|
||||
|
||||
void * mem = m_allocator.allocate(sizeof(bound));
|
||||
bound * new_upper = new (mem) bound(m, k, approx_k, false, strict, scope_lvl(), m_timestamp, bk, c_idx, a, m_uppers[x]);
|
||||
m_timestamp++;
|
||||
m_uppers[x] = new_upper;
|
||||
m_trail.push_back(trail_info(x, false));
|
||||
m_upper_refinements[x]++;
|
||||
check_feasibility(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::get_interval_size(var x, double & r) const {
|
||||
bound * l = m_lowers[x];
|
||||
bound * u = m_uppers[x];
|
||||
if (l && u) {
|
||||
r = u->m_approx_k - l->m_approx_k;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool LOWER>
|
||||
bool bound_propagator::relevant_bound(var x, double new_k) const {
|
||||
TRACE("bound_propagator_detail", tout << "relevant_bound x" << x << " " << new_k << " LOWER: " << LOWER << "\n";
|
||||
if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n";
|
||||
if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";);
|
||||
bound * b = LOWER ? m_lowers[x] : m_uppers[x];
|
||||
if (b == 0)
|
||||
return true; // variable did not have a bound
|
||||
|
||||
double interval_size;
|
||||
bool bounded = get_interval_size(x, interval_size);
|
||||
|
||||
if (!is_int(x)) {
|
||||
// check if the improvement is significant
|
||||
double improvement;
|
||||
double abs_k = b->m_approx_k;
|
||||
if (abs_k < 0.0)
|
||||
abs_k -= abs_k;
|
||||
if (bounded)
|
||||
improvement = m_threshold * std::max(std::min(interval_size, abs_k), 1.0);
|
||||
else
|
||||
improvement = m_threshold * std::max(abs_k, 1.0);
|
||||
|
||||
if (LOWER) {
|
||||
if (new_k <= b->m_approx_k + improvement) {
|
||||
TRACE("bound_propagator", tout << "LOWER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
||||
return false; // improvement is too small
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (new_k >= b->m_approx_k - improvement) {
|
||||
TRACE("bound_propagator", tout << "UPPER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
||||
return false; // improvement is too small
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (LOWER) {
|
||||
if (new_k < b->m_approx_k + 1.0)
|
||||
return false; // no improvement
|
||||
}
|
||||
else {
|
||||
if (new_k > b->m_approx_k - 1.0)
|
||||
return false; // no improvement
|
||||
}
|
||||
}
|
||||
|
||||
if (bounded && interval_size <= m_small_interval)
|
||||
return true;
|
||||
|
||||
if (LOWER)
|
||||
return m_lower_refinements[x] < m_max_refinements;
|
||||
else
|
||||
return m_upper_refinements[x] < m_max_refinements;
|
||||
}
|
||||
|
||||
bool bound_propagator::relevant_lower(var x, double approx_k) const {
|
||||
return relevant_bound<true>(x, approx_k);
|
||||
}
|
||||
|
||||
bool bound_propagator::relevant_upper(var x, double approx_k) const {
|
||||
return relevant_bound<false>(x, approx_k);
|
||||
}
|
||||
|
||||
void bound_propagator::check_feasibility(var x) {
|
||||
if (inconsistent())
|
||||
return;
|
||||
bound * l = m_lowers[x];
|
||||
bound * u = m_uppers[x];
|
||||
if (l && u) {
|
||||
if (m.lt(l->m_k, u->m_k))
|
||||
return;
|
||||
if (!l->m_strict && !u->m_strict && m.eq(l->m_k, u->m_k))
|
||||
return;
|
||||
m_conflict = x;
|
||||
m_conflicts++;
|
||||
SASSERT(inconsistent());
|
||||
TRACE("bound_propagator", tout << "inconsistency detected: x" << x << "\n"; display(tout););
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::propagate() {
|
||||
m_to_reset_ts.reset();
|
||||
|
||||
while (m_qhead < m_trail.size()) {
|
||||
if (inconsistent())
|
||||
break;
|
||||
trail_info & info = m_trail[m_qhead];
|
||||
var x = info.x();
|
||||
bool is_lower = info.is_lower();
|
||||
bound * b = is_lower ? m_lowers[x] : m_uppers[x];
|
||||
SASSERT(b);
|
||||
unsigned ts = b->m_timestamp;
|
||||
TRACE("bound_propagator_detail", tout << "propagating x" << x << "\n";);
|
||||
m_qhead++;
|
||||
wlist const & wl = m_watches[x];
|
||||
wlist::const_iterator it = wl.begin();
|
||||
wlist::const_iterator end = wl.end();
|
||||
for (; it != end; ++it) {
|
||||
unsigned c_idx = *it;
|
||||
constraint & c = m_constraints[c_idx];
|
||||
// We don't need to visit c if it was already propagated using b.
|
||||
// Whenever we visit c we store in c.m_timestamp the current timestamp
|
||||
// So, we know that c was already propagated any bound using bounds with timestamp lower than c.m_timestamp.
|
||||
if (ts >= c.m_timestamp) {
|
||||
if (c.m_timestamp == 0)
|
||||
m_to_reset_ts.push_back(c_idx);
|
||||
c.m_timestamp = m_timestamp;
|
||||
propagate(c_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned_vector::iterator it = m_to_reset_ts.begin();
|
||||
unsigned_vector::iterator end = m_to_reset_ts.end();
|
||||
for (; it != end; ++it)
|
||||
m_constraints[*it].m_timestamp = 0;
|
||||
}
|
||||
|
||||
bool bound_propagator::propagate(unsigned c_idx) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
if (c.m_dead)
|
||||
return false;
|
||||
if (c.m_kind == LINEAR)
|
||||
return propagate_eq(c_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bound_propagator::propagate_eq(unsigned c_idx) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
|
||||
#if 0
|
||||
{
|
||||
static unsigned counter = 0;
|
||||
static unsigned visited = 0;
|
||||
counter++;
|
||||
visited += eq->size();
|
||||
if (counter % 1000 == 0)
|
||||
verbose_stream() << "[bound-propagator] :propagate-eq " << counter << " :visited-vars " << visited << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACE("bound_propagator_detail", tout << "propagating using eq: "; m_eq_manager.display(tout, *eq); tout << "\n";);
|
||||
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
||||
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
||||
unsigned ll_i = UINT_MAX; // position of the variable that couldn't contribute to ll
|
||||
unsigned uu_i = UINT_MAX; // position of the variable that coundn't contribute to uu
|
||||
bool ll_failed = false;
|
||||
bool uu_failed = false;
|
||||
double ll = 0.0;
|
||||
double uu = 0.0;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
double a_i = eq->approx_a(i);
|
||||
bound * l_i = m_lowers[x_i];
|
||||
bound * u_i = m_uppers[x_i];
|
||||
if (a_i < 0.0) {
|
||||
if (!ll_failed) {
|
||||
if (l_i == 0) {
|
||||
if (ll_i == UINT_MAX)
|
||||
ll_i = i;
|
||||
else
|
||||
ll_failed = true;
|
||||
}
|
||||
else {
|
||||
ll -= a_i * l_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed) {
|
||||
if (u_i == 0) {
|
||||
if (uu_i == UINT_MAX)
|
||||
uu_i = i;
|
||||
else
|
||||
uu_failed = true;
|
||||
}
|
||||
else {
|
||||
uu -= a_i * u_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!ll_failed) {
|
||||
if (u_i == 0) {
|
||||
if (ll_i == UINT_MAX)
|
||||
ll_i = i;
|
||||
else
|
||||
ll_failed = true;
|
||||
}
|
||||
else {
|
||||
ll -= a_i * u_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed) {
|
||||
if (l_i == 0) {
|
||||
if (uu_i == UINT_MAX)
|
||||
uu_i = i;
|
||||
else
|
||||
uu_failed = true;
|
||||
}
|
||||
else {
|
||||
uu -= a_i * l_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ll_failed && uu_failed)
|
||||
return false; // nothing to propagate
|
||||
}
|
||||
|
||||
bool propagated = false;
|
||||
|
||||
SASSERT(!ll_failed || !uu_failed);
|
||||
if (ll_i == UINT_MAX || uu_i == UINT_MAX) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
double a_i = eq->approx_a(i);
|
||||
bound * l_i = m_lowers[x_i];
|
||||
bound * u_i = m_uppers[x_i];
|
||||
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
||||
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
||||
if (ll_i == UINT_MAX) {
|
||||
// can propagate a lower bound for a_i*x_i
|
||||
if (a_i > 0.0) {
|
||||
// can propagate a lower bound for x_i
|
||||
double new_k = (ll + a_i * u_i->m_approx_k)/a_i;
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
// a_i < 0.0
|
||||
// can propagate a upper bound for x_i
|
||||
double new_k = (ll + a_i * l_i->m_approx_k)/a_i;
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
if (uu_i == UINT_MAX) {
|
||||
// can propagate an upper bound for a_i*x_i
|
||||
if (a_i > 0.0) {
|
||||
// can propagate a upper bound for x_i
|
||||
double new_k = (uu + a_i * l_i->m_approx_k)/a_i;
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
// a_i < 0.0
|
||||
// can propagate a lower bound for x_i
|
||||
double new_k = (uu + a_i * u_i->m_approx_k)/a_i;
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ll_failed && ll_i != UINT_MAX) {
|
||||
// can propagate a lower bound for the monomial at position ll_i
|
||||
var x_i = eq->x(ll_i);
|
||||
double a_i = eq->approx_a(ll_i);
|
||||
double new_k = ll/a_i;
|
||||
if (a_i > 0.0) {
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, ll_i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, ll_i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed && uu_i != UINT_MAX) {
|
||||
// can propagate a upper bound for the monomial at position uu_i
|
||||
var x_i = eq->x(uu_i);
|
||||
double a_i = eq->approx_a(uu_i);
|
||||
double new_k = uu/a_i;
|
||||
if (a_i > 0.0) {
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, uu_i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, uu_i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return propagated;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Try to propagate a lower bound for the variable stored at position i, using mpq's (rationals).
|
||||
When this method is invoked, we know that all other variables have the "right" bounds, and
|
||||
using doubles we improve the current known bound.
|
||||
*/
|
||||
bool bound_propagator::propagate_lower(unsigned c_idx, unsigned i) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
var x_i = eq->x(i);
|
||||
mpz const & a_i = eq->a(i);
|
||||
unsigned sz = eq->size();
|
||||
mpq k;
|
||||
bool strict = false;
|
||||
bool neg_a_i = m.is_neg(a_i);
|
||||
for (unsigned j = 0; j < sz; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
var x_j = eq->x(j);
|
||||
mpz const & a_j = eq->a(j);
|
||||
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_uppers[x_j] : m_lowers[x_j];
|
||||
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << " b_j->m_k: " << m.to_string(b_j->m_k) <<
|
||||
" a_j: " << m.to_string(a_j) << "\n";);
|
||||
SASSERT(b_j);
|
||||
if (b_j->m_strict)
|
||||
strict = true;
|
||||
m.addmul(k, a_j, b_j->m_k, k);
|
||||
}
|
||||
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << "\n";);
|
||||
m.neg(k);
|
||||
m.div(k, a_i, k);
|
||||
TRACE("bound_propagator_step", tout << "propagating lower x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
||||
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
||||
bool r = assert_lower_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
||||
m.del(k);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Try to propagate a upper bound for the variable stored at position i, using mpq's (rationals).
|
||||
When this method is invoked, we know that all other variables have the "right" bounds, and
|
||||
using doubles we improve the current known bound.
|
||||
*/
|
||||
bool bound_propagator::propagate_upper(unsigned c_idx, unsigned i) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
var x_i = eq->x(i);
|
||||
mpz const & a_i = eq->a(i);
|
||||
unsigned sz = eq->size();
|
||||
mpq k;
|
||||
bool strict = false;
|
||||
bool neg_a_i = m.is_neg(a_i);
|
||||
for (unsigned j = 0; j < sz; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
var x_j = eq->x(j);
|
||||
mpz const & a_j = eq->a(j);
|
||||
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_lowers[x_j] : m_uppers[x_j];
|
||||
SASSERT(b_j);
|
||||
if (b_j->m_strict)
|
||||
strict = true;
|
||||
m.addmul(k, a_j, b_j->m_k, k);
|
||||
}
|
||||
m.neg(k);
|
||||
m.div(k, a_i, k);
|
||||
TRACE("bound_propagator_step", tout << "propagating upper x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
||||
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
||||
bool r = assert_upper_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
||||
m.del(k);
|
||||
return r;
|
||||
}
|
||||
|
||||
void bound_propagator::reset() {
|
||||
undo_trail(0);
|
||||
del_constraints_core();
|
||||
m_constraints.finalize();
|
||||
m_is_int.finalize();
|
||||
m_dead.finalize();
|
||||
m_lowers.finalize();
|
||||
m_uppers.finalize();
|
||||
m_watches.finalize();
|
||||
m_trail.finalize();
|
||||
m_qhead = 0;
|
||||
m_reinit_stack.finalize();
|
||||
m_lower_refinements.finalize();
|
||||
m_upper_refinements.finalize();
|
||||
m_timestamp = 0;
|
||||
m_conflict = null_var;
|
||||
m_scopes.finalize();
|
||||
}
|
||||
|
||||
bool bound_propagator::lower(var x, mpq & k, bool & strict, unsigned & ts) const {
|
||||
bound * b = m_lowers[x];
|
||||
if (!b)
|
||||
return false;
|
||||
m.set(k, b->m_k);
|
||||
strict = b->m_strict;
|
||||
ts = b->m_timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const {
|
||||
bound * b = m_uppers[x];
|
||||
if (!b)
|
||||
return false;
|
||||
m.set(k, b->m_k);
|
||||
strict = b->m_strict;
|
||||
ts = b->m_timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) {
|
||||
bound * r = this;
|
||||
while (r != 0 && r->m_timestamp >= timestamp)
|
||||
r = r->m_prev;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the coefficient of x in eq is positive
|
||||
*/
|
||||
bool bound_propagator::is_a_i_pos(linear_equation const & eq, var x) const {
|
||||
unsigned i = eq.pos(x);
|
||||
if (i == UINT_MAX)
|
||||
return false;
|
||||
return m.is_pos(eq.a(i));
|
||||
}
|
||||
|
||||
void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector & ex) const {
|
||||
if (!b)
|
||||
return;
|
||||
b = b->at(ts);
|
||||
if (!b)
|
||||
return;
|
||||
if (b->m_kind == AXIOM || b->m_kind == DECISION)
|
||||
return;
|
||||
if (b->m_kind == ASSUMPTION) {
|
||||
ex.push_back(b->m_assumption);
|
||||
return;
|
||||
}
|
||||
svector<var_bound> & todo = const_cast<bound_propagator*>(this)->m_todo;
|
||||
todo.reset();
|
||||
unsigned qhead = 0;
|
||||
todo.push_back(var_bound(x, b));
|
||||
b->m_mark = true;
|
||||
while (qhead < todo.size()) {
|
||||
var_bound & vb = todo[qhead];
|
||||
qhead ++;
|
||||
var x = vb.first;
|
||||
bound * b = vb.second;
|
||||
SASSERT(b->kind() == ASSUMPTION || b->kind() == DERIVED);
|
||||
if (b->kind() == ASSUMPTION) {
|
||||
ex.push_back(b->m_assumption);
|
||||
continue;
|
||||
}
|
||||
SASSERT(b->kind() == DERIVED);
|
||||
constraint const & c = m_constraints[b->m_constraint_idx];
|
||||
switch (c.m_kind) {
|
||||
case LINEAR: {
|
||||
linear_equation * eq = c.m_eq;
|
||||
bool is_lower = b->is_lower();
|
||||
if (!is_a_i_pos(*eq, x))
|
||||
is_lower = !is_lower;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
if (x_i == x)
|
||||
continue;
|
||||
bound * b = (m.is_neg(eq->a(i)) == is_lower) ? m_lowers[x_i] : m_uppers[x_i];
|
||||
SASSERT(b);
|
||||
if (b->kind() == DERIVED || b->kind() == ASSUMPTION) {
|
||||
if (!b->m_mark) {
|
||||
b->m_mark = true;
|
||||
todo.push_back(var_bound(x_i, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsigned sz = todo.size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
todo[i].second->m_mark = false;
|
||||
todo.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Compute lower (upper) bound for the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]
|
||||
|
||||
Return false if the lower (upper) bound is -oo (oo)
|
||||
*/
|
||||
template<bool LOWER, typename Numeral>
|
||||
bool bound_propagator::get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const {
|
||||
st = false;
|
||||
m.reset(r);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = xs[i];
|
||||
Numeral const & a_i = as[i];
|
||||
if (m.is_zero(a_i))
|
||||
continue;
|
||||
bound * b = (m.is_neg(a_i) == LOWER) ? m_uppers[x_i] : m_lowers[x_i];
|
||||
if (!b) {
|
||||
m.reset(r);
|
||||
return false;
|
||||
}
|
||||
if (b->m_strict)
|
||||
st = true;
|
||||
m.addmul(r, a_i, b->m_k, r);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
||||
return get_bound<true, mpq>(sz, as, xs, r, st);
|
||||
}
|
||||
|
||||
bool bound_propagator::upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
||||
return get_bound<false, mpq>(sz, as, xs, r, st);
|
||||
}
|
||||
|
||||
void bound_propagator::display_bounds_of(std::ostream & out, linear_equation const & eq) const {
|
||||
for (unsigned i = 0; i < eq.size(); i++) {
|
||||
display_var_bounds(out, eq.x(i));
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_var_bounds(std::ostream & out, var x, bool approx, bool precise) const {
|
||||
if (m_lowers[x]) {
|
||||
if (precise)
|
||||
out << m.to_string(m_lowers[x]->m_k);
|
||||
if (precise && approx)
|
||||
out << " | ";
|
||||
if (approx)
|
||||
out << m_lowers[x]->m_approx_k;
|
||||
out << " " << (m_lowers[x]->m_strict ? "<" : "<=");
|
||||
}
|
||||
else {
|
||||
out << "-oo <";
|
||||
}
|
||||
out << " x" << x << " ";
|
||||
if (m_uppers[x]) {
|
||||
out << (m_uppers[x]->m_strict ? "<" : "<=") << " ";
|
||||
if (precise)
|
||||
out << m.to_string(m_uppers[x]->m_k);
|
||||
if (precise && approx)
|
||||
out << " | ";
|
||||
if (approx)
|
||||
out << m_uppers[x]->m_approx_k;
|
||||
}
|
||||
else {
|
||||
out << "< oo";
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_bounds(std::ostream & out, bool approx, bool precise) const {
|
||||
unsigned num_vars = m_dead.size();
|
||||
for (unsigned x = 0; x < num_vars; x++) {
|
||||
if (!is_dead(x)) {
|
||||
display_var_bounds(out, x, approx, precise);
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_constraints(std::ostream & out) const {
|
||||
constraint_vector::const_iterator it = m_constraints.begin();
|
||||
constraint_vector::const_iterator end = m_constraints.end();
|
||||
for (; it != end; ++it) {
|
||||
constraint const & c = *it;
|
||||
if (c.m_kind == LINEAR) {
|
||||
m_eq_manager.display(out, *(c.m_eq));
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display(std::ostream & out) const {
|
||||
display_bounds(out);
|
||||
display_constraints(out);
|
||||
}
|
||||
|
||||
|
||||
|
265
src/tactic/arith/bound_propagator.h
Normal file
265
src/tactic/arith/bound_propagator.h
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_propagator.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Bound propagators for arithmetic.
|
||||
Support class for implementing strategies and search procedures
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _BOUND_PROPAGATOR_H_
|
||||
#define _BOUND_PROPAGATOR_H_
|
||||
|
||||
#include"mpq.h"
|
||||
#include"vector.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
#include"numeral_buffer.h"
|
||||
#include"linear_equation.h"
|
||||
|
||||
class bound_propagator {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
typedef unsigned assumption;
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef unsigned_vector assumption_vector;
|
||||
typedef unsigned constraint_id;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
typedef svector<double> double_vector;
|
||||
static const assumption null_assumption = UINT_MAX;
|
||||
static const var null_var = UINT_MAX;
|
||||
static const unsigned null_constraint_idx = UINT_MAX;
|
||||
class trail_info {
|
||||
unsigned m_x_lower;
|
||||
public:
|
||||
trail_info(var x, bool is_lower):m_x_lower((x << 1) + static_cast<unsigned>(is_lower)) {}
|
||||
trail_info():m_x_lower(UINT_MAX) {}
|
||||
var x() const { return m_x_lower >> 1; }
|
||||
bool is_lower() const { return (m_x_lower & 1) != 0; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
enum ckind { LINEAR // only linear equalities so far.
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Constraints don't need justification.
|
||||
*/
|
||||
class constraint {
|
||||
friend class bound_propagator;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_dead:1;
|
||||
unsigned m_timestamp; // Constraint tried to propagate new bounds using bounds with timestamp < m_timestamp.
|
||||
unsigned m_act; // activity
|
||||
unsigned m_counter; // number of times the constraint propagated
|
||||
union {
|
||||
linear_equation * m_eq;
|
||||
};
|
||||
};
|
||||
|
||||
enum bkind { AXIOM, // doesn't need justification
|
||||
ASSUMPTION, // aka external case-split, it is used to connect with external search engine.
|
||||
DERIVED, // implied
|
||||
DECISION // internal case-split
|
||||
};
|
||||
|
||||
struct bound {
|
||||
mpq m_k;
|
||||
double m_approx_k;
|
||||
unsigned m_lower:1;
|
||||
unsigned m_strict:1;
|
||||
unsigned m_mark:1;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_level:27;
|
||||
unsigned m_timestamp;
|
||||
union {
|
||||
assumption m_assumption;
|
||||
unsigned m_constraint_idx;
|
||||
};
|
||||
bound * m_prev;
|
||||
bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk,
|
||||
unsigned c_idx, assumption a, bound * prev);
|
||||
|
||||
bound * at(unsigned timestamp);
|
||||
bkind kind() const { return static_cast<bkind>(m_kind); }
|
||||
bool is_lower() const { return m_lower != 0; }
|
||||
};
|
||||
|
||||
typedef ptr_vector<bound> var2bound;
|
||||
typedef svector<var> var_vector;
|
||||
typedef svector<constraint> constraint_vector;
|
||||
typedef unsigned_vector c_idx_vector;
|
||||
typedef c_idx_vector wlist;
|
||||
typedef small_object_allocator allocator;
|
||||
typedef linear_equation_manager lin_eq_manager;
|
||||
|
||||
numeral_manager & m;
|
||||
allocator & m_allocator;
|
||||
lin_eq_manager m_eq_manager;
|
||||
constraint_vector m_constraints;
|
||||
char_vector m_is_int;
|
||||
char_vector m_dead;
|
||||
var2bound m_lowers;
|
||||
var2bound m_uppers;
|
||||
vector<wlist> m_watches;
|
||||
svector<trail_info> m_trail;
|
||||
unsigned m_qhead;
|
||||
c_idx_vector m_reinit_stack;
|
||||
unsigned_vector m_lower_refinements; // number of times a lower bound was propagated for each variable (loop prevention)
|
||||
unsigned_vector m_upper_refinements; // number of times a upper bound was propagated for each variable (loop prevention)
|
||||
unsigned m_timestamp;
|
||||
var m_conflict;
|
||||
mpq m_tmp;
|
||||
|
||||
struct scope {
|
||||
unsigned m_trail_limit;
|
||||
unsigned m_qhead_old;
|
||||
unsigned m_reinit_stack_limit;
|
||||
unsigned m_timestamp_old:31;
|
||||
unsigned m_in_conflict:1;
|
||||
};
|
||||
|
||||
svector<scope> m_scopes;
|
||||
|
||||
unsigned_vector m_to_reset_ts; // temp field: ids of the constraints we must reset the field m_timestamp
|
||||
|
||||
// config
|
||||
unsigned m_max_refinements; // maximum number of refinements per round
|
||||
double m_small_interval;
|
||||
double m_threshold; // improvement threshold
|
||||
double m_strict2double;
|
||||
|
||||
// statistics
|
||||
unsigned m_conflicts;
|
||||
unsigned m_propagations;
|
||||
unsigned m_false_alarms;
|
||||
|
||||
void del_constraint(constraint & cnstr);
|
||||
void del_constraints_core();
|
||||
|
||||
template<bool LOWER>
|
||||
bool relevant_bound(var x, double approx_k) const;
|
||||
bool relevant_lower(var x, double approx_k) const;
|
||||
bool relevant_upper(var x, double approx_k) const;
|
||||
bool get_interval_size(var x, double & r) const;
|
||||
void check_feasibility(var x);
|
||||
|
||||
bool assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
bool assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
|
||||
bool propagate(unsigned c_idx);
|
||||
bool propagate_eq(unsigned c_idx);
|
||||
bool propagate_lower(unsigned c_idx, unsigned i);
|
||||
bool propagate_upper(unsigned c_idx, unsigned i);
|
||||
void undo_trail(unsigned old_sz);
|
||||
|
||||
typedef std::pair<var, bound *> var_bound;
|
||||
svector<var_bound> m_todo;
|
||||
void explain(var x, bound * b, unsigned ts, assumption_vector & ex) const;
|
||||
bool is_a_i_pos(linear_equation const & eq, var x) const;
|
||||
|
||||
template<bool LOWER, typename Numeral>
|
||||
bool get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const;
|
||||
|
||||
void init_eq(linear_equation * eq);
|
||||
|
||||
public:
|
||||
bound_propagator(numeral_manager & m, allocator & a, params_ref const & p);
|
||||
~bound_propagator();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
|
||||
double strict2double() const { return m_strict2double; }
|
||||
|
||||
bool is_int(var x) const { return m_is_int[x] != 0; }
|
||||
|
||||
unsigned scope_lvl() const { return m_scopes.size(); }
|
||||
void mk_var(var x, bool is_int);
|
||||
void del_var(var x);
|
||||
bool is_dead(var x) const { return m_dead[x] != 0; }
|
||||
void mk_eq(unsigned sz, mpq * as, var * xs);
|
||||
void mk_eq(unsigned sz, mpz * as, var * xs);
|
||||
void del_constraints();
|
||||
void assert_lower(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_upper(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_decided_lower(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void assert_decided_upper(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void propagate();
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void reset();
|
||||
bool has_lower(var x) const { return m_lowers[x] != 0; }
|
||||
bool has_upper(var x) const { return m_uppers[x] != 0; }
|
||||
bool lower(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool upper(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool is_fixed(var x) const { return has_lower(x) && has_upper(x) && m.eq(m_lowers[x]->m_k, m_uppers[x]->m_k) && !inconsistent(); }
|
||||
mpq const & lower(var x, bool & strict) const { SASSERT(has_lower(x)); bound * b = m_lowers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & upper(var x, bool & strict) const { SASSERT(has_upper(x)); bound * b = m_uppers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_k; }
|
||||
mpq const & upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_k; }
|
||||
double approx_lower(var x) const {
|
||||
SASSERT(has_lower(x));
|
||||
return m_lowers[x]->m_strict ? m_lowers[x]->m_approx_k + m_strict2double : m_lowers[x]->m_approx_k;
|
||||
}
|
||||
double approx_upper(var x) const {
|
||||
SASSERT(has_upper(x));
|
||||
return m_uppers[x]->m_strict ? m_uppers[x]->m_approx_k - m_strict2double : m_uppers[x]->m_approx_k;
|
||||
}
|
||||
bool is_zero(var x) const { return has_lower(x) && has_upper(x) && m.is_zero(lower(x)) && m.is_zero(upper(x)); }
|
||||
void explain_lower(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_lowers[x], ts, ex); }
|
||||
void explain_upper(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_uppers[x], ts, ex); }
|
||||
void explain_lower(var x, assumption_vector & ex) const { explain_lower(x, m_timestamp, ex); }
|
||||
void explain_upper(var x, assumption_vector & ex) const { explain_upper(x, m_timestamp, ex); }
|
||||
var conflict_var() const { return m_conflict; }
|
||||
bool inconsistent() const { return m_conflict != null_var; }
|
||||
|
||||
unsigned trail_size() const { return m_trail.size(); }
|
||||
unsigned qhead() const { return m_qhead; }
|
||||
|
||||
typedef svector<trail_info>::const_iterator trail_iterator;
|
||||
|
||||
trail_iterator begin_trail() const { return m_trail.begin(); }
|
||||
trail_iterator end_trail() const { return m_trail.end(); }
|
||||
|
||||
bool lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
bool upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
void display(std::ostream & out) const;
|
||||
void display_var_bounds(std::ostream & out, var x, bool approx = true, bool precise = true) const;
|
||||
void display_bounds(std::ostream & out, bool approx = true, bool precise = true) const;
|
||||
void display_precise_bounds(std::ostream & out) const { display_bounds(out, false, true); }
|
||||
void display_approx_bounds(std::ostream & out) const { display_bounds(out, true, false); }
|
||||
void display_constraints(std::ostream & out) const;
|
||||
void display_bounds_of(std::ostream & out, linear_equation const & eq) const;
|
||||
|
||||
unsigned get_num_false_alarms() const { return m_false_alarms; }
|
||||
unsigned get_num_propagations() const { return m_propagations; }
|
||||
};
|
||||
|
||||
#endif
|
604
src/tactic/arith/bv2int_rewriter.cpp
Normal file
604
src/tactic/arith/bv2int_rewriter.cpp
Normal file
|
@ -0,0 +1,604 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2int_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2int propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include "bv2int_rewriter.h"
|
||||
#include "rewriter_def.h"
|
||||
#include "ast_pp.h"
|
||||
|
||||
void bv2int_rewriter_ctx::update_params(params_ref const& p) {
|
||||
m_max_size = p.get_uint(":max-bv-size", UINT_MAX);
|
||||
}
|
||||
|
||||
struct lt_rational {
|
||||
bool operator()(rational const& a, rational const& b) const { return a < b; }
|
||||
};
|
||||
|
||||
void bv2int_rewriter_ctx::collect_power2(goal const& s) {
|
||||
ast_manager& m = m_trail.get_manager();
|
||||
arith_util arith(m);
|
||||
bv_util bv(m);
|
||||
|
||||
for (unsigned j = 0; j < s.size(); ++j) {
|
||||
expr* f = s.form(j);
|
||||
if (!m.is_or(f)) continue;
|
||||
unsigned sz = to_app(f)->get_num_args();
|
||||
expr* x, *y, *v = 0;
|
||||
rational n;
|
||||
vector<rational> bounds;
|
||||
bool is_int, ok = true;
|
||||
|
||||
for (unsigned i = 0; ok && i < sz; ++i) {
|
||||
expr* e = to_app(f)->get_arg(i);
|
||||
if (!m.is_eq(e, x, y)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (arith.is_numeral(y, n, is_int) && is_int &&
|
||||
(x == v || v == 0)) {
|
||||
v = x;
|
||||
bounds.push_back(n);
|
||||
}
|
||||
else if (arith.is_numeral(x, n, is_int) && is_int &&
|
||||
(y == v || v == 0)) {
|
||||
v = y;
|
||||
bounds.push_back(n);
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok || !v) continue;
|
||||
SASSERT(!bounds.empty());
|
||||
lt_rational lt;
|
||||
// lt is a total order on rationals.
|
||||
std::sort(bounds.begin(), bounds.end(), lt);
|
||||
rational p(1);
|
||||
unsigned num_bits = 0;
|
||||
for (unsigned i = 0; ok && i < bounds.size(); ++i) {
|
||||
ok = (p == bounds[i]);
|
||||
p *= rational(2);
|
||||
++num_bits;
|
||||
}
|
||||
if (!ok) continue;
|
||||
unsigned log2 = 0;
|
||||
for (unsigned i = 1; i <= num_bits; i *= 2) ++log2;
|
||||
if(log2 == 0) continue;
|
||||
expr* logx = m.mk_fresh_const("log2_v", bv.mk_sort(log2));
|
||||
logx = bv.mk_zero_extend(num_bits - log2, logx);
|
||||
m_trail.push_back(logx);
|
||||
TRACE("bv2int_rewriter", tout << mk_pp(v, m) << " |-> " << mk_pp(logx, m) << "\n";);
|
||||
m_power2.insert(v, logx);
|
||||
}
|
||||
}
|
||||
|
||||
bool bv2int_rewriter_ctx::is_power2(expr* x, expr*& log_x) {
|
||||
return m_power2.find(x, log_x);
|
||||
}
|
||||
|
||||
bv2int_rewriter::bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx)
|
||||
:m_manager(m), m_ctx(ctx), m_bv(m), m_arith(m) {
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if(f->get_family_id() == m_arith.get_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_NUM: return BR_FAILED;
|
||||
case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result);
|
||||
case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result);
|
||||
case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result);
|
||||
case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result);
|
||||
case OP_ADD: return mk_add(num_args, args, result);
|
||||
case OP_MUL: return mk_mul(num_args, args, result);
|
||||
case OP_SUB: return mk_sub(num_args, args, result);
|
||||
case OP_DIV: return BR_FAILED;
|
||||
case OP_IDIV: SASSERT(num_args == 2); return mk_idiv(args[0], args[1], result);
|
||||
case OP_MOD: SASSERT(num_args == 2); return mk_mod(args[0], args[1], result);
|
||||
case OP_REM: SASSERT(num_args == 2); return mk_rem(args[0], args[1], result);
|
||||
case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result);
|
||||
case OP_TO_REAL: return BR_FAILED;
|
||||
case OP_TO_INT: return BR_FAILED;
|
||||
case OP_IS_INT: return BR_FAILED;
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
if (f->get_family_id() == m().get_basic_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result);
|
||||
case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result);
|
||||
default: return BR_FAILED;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_le(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_ule(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
// s1 - s2 <= t1 - t2
|
||||
// <=>
|
||||
// s1 + t2 <= t1 + s2
|
||||
//
|
||||
s1 = mk_bv_add(s1, t2, false);
|
||||
t1 = mk_bv_add(t1, s2, false);
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_ule(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = m_bv.mk_sle(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg2, arg1));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
return mk_le(arg2, arg1, result);
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), t1(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_bv2int(m().mk_ite(c, s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = mk_sbv2int(m().mk_ite(c, s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m().mk_eq(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
s1 = mk_bv_add(s1, t2, false);
|
||||
t1 = mk_bv_add(s2, t1, false);
|
||||
align_sizes(s1, t1, false);
|
||||
result = m().mk_eq(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = m().mk_eq(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
// TBD
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_bv2int(m_bv.mk_bv_urem(s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
//
|
||||
// (s1 - s2) mod t1 = (s1 + (t1 - (s2 mod t1))) mod t1
|
||||
//
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int(t, t1)) {
|
||||
expr_ref u1(m());
|
||||
align_sizes(s1, t1, false);
|
||||
u1 = m_bv.mk_bv_urem(s1, t1);
|
||||
u1 = m_bv.mk_bv_sub(t1, u1);
|
||||
u1 = mk_bv_add(s1, u1, false);
|
||||
align_sizes(u1, t1, false);
|
||||
result = m_bv.mk_bv2int(m_bv.mk_bv_urem(u1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TBD: check semantics
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = mk_sbv2int(m_bv.mk_bv_srem(s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
#endif
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
// TBD
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_uminus(expr * s, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m());
|
||||
if (is_bv2int_diff(s, s1, s2)) {
|
||||
result = m_arith.mk_sub(m_bv.mk_bv2int(s2), m_bv.mk_bv2int(s1));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1)) {
|
||||
result = mk_sbv2int(m_bv.mk_bv_neg(s1));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_add(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void bv2int_rewriter::align_sizes(expr_ref& s, expr_ref& t, bool is_signed) {
|
||||
unsigned sz1 = m_bv.get_bv_size(s);
|
||||
unsigned sz2 = m_bv.get_bv_size(t);
|
||||
if (sz1 > sz2 && is_signed) {
|
||||
t = mk_extend(sz1-sz2, t, true);
|
||||
}
|
||||
if (sz1 > sz2 && !is_signed) {
|
||||
t = mk_extend(sz1-sz2, t, false);
|
||||
}
|
||||
if (sz1 < sz2 && is_signed) {
|
||||
s = mk_extend(sz2-sz1, s, true);
|
||||
}
|
||||
if (sz1 < sz2 && !is_signed) {
|
||||
s = mk_extend(sz2-sz1, s, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool bv2int_rewriter::is_zero(expr* n) {
|
||||
rational r;
|
||||
unsigned sz;
|
||||
return m_bv.is_numeral(n, r, sz) && r.is_zero();
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_bv_add(expr* s, expr* t, bool is_signed) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
|
||||
if (is_zero(s)) {
|
||||
return t;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return s;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1, is_signed);
|
||||
s1 = mk_extend(1, s1, is_signed);
|
||||
t1 = mk_extend(1, t1, is_signed);
|
||||
return m_bv.mk_bv_add(s1, t1);
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_add(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
result = m_bv.mk_bv2int(mk_bv_add(s1, t1, false));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
// s1 - s2 + t1 - t2
|
||||
// =
|
||||
// s1 + t1 - (s2 + t2)
|
||||
//
|
||||
t1 = m_bv.mk_bv2int(mk_bv_add(s1, t1, false));
|
||||
t2 = m_bv.mk_bv2int(mk_bv_add(s2, t2, false));
|
||||
result = m_arith.mk_sub(t1, t2);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
result = mk_sbv2int(mk_bv_add(s1, t1, true));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_mul(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_bv_mul(expr* s, expr* t, bool is_signed) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
if (is_zero(s)) {
|
||||
return s;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return t;
|
||||
}
|
||||
rational r;
|
||||
unsigned sz;
|
||||
if (m_bv.is_numeral(s, r, sz) && r.is_one()) {
|
||||
return t;
|
||||
}
|
||||
if (m_bv.is_numeral(t, r, sz) && r.is_one()) {
|
||||
return s;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1, is_signed);
|
||||
unsigned n = m_bv.get_bv_size(t1);
|
||||
unsigned max_bits = m_ctx.get_max_num_bits();
|
||||
bool add_side_conds = 2*n > max_bits;
|
||||
if (n >= max_bits) {
|
||||
//
|
||||
}
|
||||
else if (2*n > max_bits) {
|
||||
s1 = mk_extend(max_bits-n, s1, is_signed);
|
||||
t1 = mk_extend(max_bits-n, t1, is_signed);
|
||||
}
|
||||
else {
|
||||
s1 = mk_extend(n, s1, is_signed);
|
||||
t1 = mk_extend(n, t1, is_signed);
|
||||
}
|
||||
if (add_side_conds) {
|
||||
if (is_signed) {
|
||||
m_ctx.add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1));
|
||||
m_ctx.add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1));
|
||||
}
|
||||
else {
|
||||
m_ctx.add_side_condition(m_bv.mk_bvumul_no_ovfl(s1, t1));
|
||||
}
|
||||
}
|
||||
return m_bv.mk_bv_mul(s1, t1);
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
if ((is_shl1(s, s1) && is_bv2int(t, t1)) ||
|
||||
(is_shl1(t, s1) && is_bv2int(s, t1))) {
|
||||
unsigned n = m_bv.get_bv_size(s1);
|
||||
unsigned m = m_bv.get_bv_size(t1);
|
||||
s1 = mk_extend(m, s1, false);
|
||||
t1 = mk_extend(n, t1, false);
|
||||
result = m_bv.mk_bv2int(m_bv.mk_bv_shl(t1, s1));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
result = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false));
|
||||
return BR_DONE;
|
||||
}
|
||||
if ((is_bv2int(s, s1) && is_bv2int_diff(t, t1, t2)) ||
|
||||
(is_bv2int(t, s1) && is_bv2int_diff(s, t1, t2))) {
|
||||
t1 = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false));
|
||||
t2 = m_bv.mk_bv2int(mk_bv_mul(s1, t2, false));
|
||||
result = m_arith.mk_sub(t1, t2);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
result = mk_sbv2int(mk_bv_mul(s1, t1, true));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_sub(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
// s1 - s2 - (t1 - t2)
|
||||
// =
|
||||
// s1 + t2 - (t1 + s2)
|
||||
//
|
||||
s1 = m_bv.mk_bv2int(mk_bv_add(s1, t2, false));
|
||||
s2 = m_bv.mk_bv2int(mk_bv_add(s2, t1, false));
|
||||
result = m_arith.mk_sub(s1, s2);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
s1 = m_bv.mk_sign_extend(1, s1);
|
||||
t1 = m_bv.mk_sign_extend(1, t1);
|
||||
result = mk_sbv2int(m_bv.mk_bv_sub(s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_bv2int(expr* n, expr_ref& s) {
|
||||
rational k;
|
||||
bool is_int;
|
||||
if (m_bv.is_bv2int(n)) {
|
||||
s = to_app(n)->get_arg(0);
|
||||
return true;
|
||||
}
|
||||
if (m_arith.is_numeral(n, k, is_int) && is_int && !k.is_neg()) {
|
||||
unsigned sz = k.get_num_bits();
|
||||
s = m_bv.mk_numeral(k, m_bv.mk_sort(sz));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_shl1(expr* n, expr_ref& s) {
|
||||
expr* s1, *s2;
|
||||
rational r;
|
||||
unsigned bv_size;
|
||||
if(m_bv.is_bv2int(n, s2) &&
|
||||
m_bv.is_bv_shl(s2, s1, s2) &&
|
||||
m_bv.is_numeral(s1, r, bv_size) &&
|
||||
r.is_one()) {
|
||||
s = s2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_bv2int_diff(expr* n, expr_ref& s, expr_ref& t) {
|
||||
if (is_bv2int(n, s)) {
|
||||
t = m_bv.mk_numeral(0, 1);
|
||||
return true;
|
||||
}
|
||||
rational k;
|
||||
bool is_int;
|
||||
if (m_arith.is_numeral(n, k, is_int) && is_int) {
|
||||
SASSERT(k.is_neg());
|
||||
k.neg();
|
||||
unsigned sz = k.get_num_bits();
|
||||
t = m_bv.mk_numeral(k, m_bv.mk_sort(sz));
|
||||
s = m_bv.mk_numeral(0, 1);
|
||||
return true;
|
||||
}
|
||||
//
|
||||
// bv2int(a) - bv2int(b)
|
||||
//
|
||||
expr *e1, *e2;
|
||||
if (m_arith.is_sub(n, e1, e2) &&
|
||||
is_bv2int(e1, s) &&
|
||||
is_bv2int(e2, t)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_sbv2int(expr* n, expr_ref& s) {
|
||||
if (is_bv2int(n, s)) {
|
||||
s = m_bv.mk_zero_extend(1, s);
|
||||
return true;
|
||||
}
|
||||
expr_ref u1(m()), u2(m());
|
||||
if (is_bv2int_diff(n, u1, u2)) {
|
||||
align_sizes(u1, u2, false);
|
||||
u1 = mk_extend(1, u1, false);
|
||||
u2 = mk_extend(1, u2, false);
|
||||
s = m_bv.mk_bv_sub(u1, u2);
|
||||
return true;
|
||||
}
|
||||
// ite(bv1 == b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2]))
|
||||
expr* c, *t, *e1, *c1, *c2, *c3, *t1, *t2, *e2, *e3;
|
||||
rational k;
|
||||
bool is_int;
|
||||
unsigned lo, hi, lo1, hi1, sz;
|
||||
|
||||
if (m().is_ite(n, c, t, e1) &&
|
||||
m().is_eq(c, c1, c2) &&
|
||||
m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 &&
|
||||
m_bv.is_extract(c2, lo, hi, c3) &&
|
||||
lo == hi && lo == m_bv.get_bv_size(c3) - 1 &&
|
||||
m_arith.is_sub(t, t1, t2) &&
|
||||
e1 == t1 &&
|
||||
m_bv.is_bv2int(e1, e2) &&
|
||||
m_bv.is_extract(e2, lo1, hi1, e3) &&
|
||||
lo1 == 0 && hi1 == hi-1 &&
|
||||
m_arith.is_numeral(t2, k, is_int) && is_int &&
|
||||
k == m_bv.power_of_two(hi)
|
||||
) {
|
||||
s = e3;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// bv2int(b[0:n-2]) - ite(bv1 == b[n-1:n-1], 2^{n-1}, 0)
|
||||
if (m().is_sub(n, e1, e2) &&
|
||||
m_bv.is_bv2int(e1, e3) &&
|
||||
m_bv.is_extract(e3, lo, hi, e4) &&
|
||||
lo == 0 && hi == m_bv.get_bv_size(e4) - 2 &&
|
||||
m().is_ite(e2, t1, t2, t3) &&
|
||||
m().is_eq(t1, c1, c2) &&
|
||||
m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 &&
|
||||
m_bv.is_extract(c2, lo1, hi1, c3) && lo1 == h1 + 1 && hi1 == lo1 &&
|
||||
c3 == e4 &&
|
||||
m_arith.is_numeral(t2, )) {
|
||||
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_sbv2int(expr* b) {
|
||||
//
|
||||
// ite(bit1 = b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2]))
|
||||
//
|
||||
expr* bv1 = m_bv.mk_numeral(1, 1);
|
||||
unsigned n = m_bv.get_bv_size(b);
|
||||
expr* c = m().mk_eq(bv1, m_bv.mk_extract(n-1, n-1, b));
|
||||
expr* e = m_bv.mk_bv2int(m_bv.mk_extract(n-2, 0, b));
|
||||
expr* t = m_arith.mk_sub(e, m_arith.mk_numeral(power(rational(2), n-1), true));
|
||||
return m().mk_ite(c, t, e);
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_extend(unsigned sz, expr* b, bool is_signed) {
|
||||
if (sz == 0) {
|
||||
return b;
|
||||
}
|
||||
rational r;
|
||||
unsigned bv_sz;
|
||||
if (is_signed) {
|
||||
return m_bv.mk_sign_extend(sz, b);
|
||||
}
|
||||
else if (m_bv.is_numeral(b, r, bv_sz)) {
|
||||
return m_bv.mk_numeral(r, bv_sz + sz);
|
||||
}
|
||||
else {
|
||||
return m_bv.mk_zero_extend(sz, b);
|
||||
}
|
||||
}
|
||||
|
||||
template class rewriter_tpl<bv2int_rewriter_cfg>;
|
||||
|
||||
|
||||
|
||||
|
120
src/tactic/arith/bv2int_rewriter.h
Normal file
120
src/tactic/arith/bv2int_rewriter.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2int_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2int propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _BV2INT_REWRITER_H_
|
||||
#define _BV2INT_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter.h"
|
||||
#include"bv_decl_plugin.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"params.h"
|
||||
#include"goal.h"
|
||||
|
||||
class bv2int_rewriter_ctx {
|
||||
unsigned m_max_size;
|
||||
expr_ref_vector m_side_conditions;
|
||||
obj_map<expr, expr*> m_power2;
|
||||
expr_ref_vector m_trail;
|
||||
|
||||
public:
|
||||
bv2int_rewriter_ctx(ast_manager& m, params_ref const& p) :
|
||||
m_side_conditions(m), m_trail(m) { update_params(p); }
|
||||
|
||||
void reset() { m_side_conditions.reset(); m_trail.reset(); m_power2.reset(); }
|
||||
void add_side_condition(expr* e) { m_side_conditions.push_back(e); }
|
||||
unsigned num_side_conditions() const { return m_side_conditions.size(); }
|
||||
expr* const* side_conditions() const { return m_side_conditions.c_ptr(); }
|
||||
unsigned get_max_num_bits() const { return m_max_size; }
|
||||
|
||||
void collect_power2(goal const & s);
|
||||
bool is_power2(expr* x, expr*& log_x);
|
||||
obj_map<expr, expr*> const& power2() const { return m_power2; }
|
||||
|
||||
private:
|
||||
void update_params(params_ref const& p);
|
||||
};
|
||||
|
||||
class bv2int_rewriter {
|
||||
ast_manager & m_manager;
|
||||
bv2int_rewriter_ctx& m_ctx;
|
||||
bv_util m_bv;
|
||||
arith_util m_arith;
|
||||
|
||||
public:
|
||||
bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx);
|
||||
ast_manager & m() const { return m_manager; }
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(f, num_args, args);
|
||||
}
|
||||
private:
|
||||
br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_le(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_idiv(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_mod(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_add(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_mul(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_sub(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_uminus(expr* e, expr_ref & result);
|
||||
|
||||
bool is_bv2int(expr* e, expr_ref& s);
|
||||
bool is_sbv2int(expr* e, expr_ref& s);
|
||||
bool is_bv2int_diff(expr* e, expr_ref& s, expr_ref& t);
|
||||
bool is_zero(expr* e);
|
||||
bool is_shl1(expr* e, expr_ref& s);
|
||||
|
||||
expr* mk_bv_add(expr* s, expr* t, bool is_signed);
|
||||
expr* mk_bv_mul(expr* s, expr* t, bool is_signed);
|
||||
expr* mk_sbv2int(expr* s);
|
||||
expr* mk_extend(unsigned sz, expr* b, bool is_signed);
|
||||
|
||||
void align_sizes(expr_ref& s, expr_ref& t, bool is_signed);
|
||||
|
||||
};
|
||||
|
||||
struct bv2int_rewriter_cfg : public default_rewriter_cfg {
|
||||
bv2int_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
bv2int_rewriter_cfg(ast_manager & m, bv2int_rewriter_ctx& ctx):m_r(m, ctx) {}
|
||||
};
|
||||
|
||||
class bv2int_rewriter_star : public rewriter_tpl<bv2int_rewriter_cfg> {
|
||||
bv2int_rewriter_cfg m_cfg;
|
||||
public:
|
||||
bv2int_rewriter_star(ast_manager & m, bv2int_rewriter_ctx& ctx):
|
||||
rewriter_tpl<bv2int_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(m, ctx) {}
|
||||
};
|
||||
|
||||
#endif
|
695
src/tactic/arith/bv2real_rewriter.cpp
Normal file
695
src/tactic/arith/bv2real_rewriter.cpp
Normal file
|
@ -0,0 +1,695 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2real_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2real propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-08-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"bv2real_rewriter.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"ast_pp.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
|
||||
bv2real_util::bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits) :
|
||||
m_manager(m),
|
||||
m_arith(m),
|
||||
m_bv(m),
|
||||
m_decls(m),
|
||||
m_pos_le(m),
|
||||
m_pos_lt(m),
|
||||
m_side_conditions(m),
|
||||
m_default_root(default_root),
|
||||
m_default_divisor(default_divisor),
|
||||
m_max_divisor(rational(2)*default_divisor),
|
||||
m_max_num_bits(max_num_bits) {
|
||||
sort* real = m_arith.mk_real();
|
||||
sort* domain[2] = { real, real };
|
||||
m_pos_lt = m.mk_fresh_func_decl("<","",2,domain,m.mk_bool_sort());
|
||||
m_pos_le = m.mk_fresh_func_decl("<=","",2,domain,m.mk_bool_sort());
|
||||
m_decls.push_back(m_pos_lt);
|
||||
m_decls.push_back(m_pos_le);
|
||||
}
|
||||
|
||||
bool bv2real_util::is_bv2real(func_decl* f) const {
|
||||
return m_decl2sig.contains(f);
|
||||
}
|
||||
|
||||
bool bv2real_util::is_bv2real(func_decl* f, unsigned num_args, expr* const* args,
|
||||
expr*& m, expr*& n, rational& d, rational& r) const {
|
||||
bvr_sig sig;
|
||||
if (!m_decl2sig.find(f, sig)) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(num_args == 2);
|
||||
m = args[0];
|
||||
n = args[1];
|
||||
d = sig.m_d;
|
||||
r = sig.m_r;
|
||||
SASSERT(sig.m_d.is_int() && sig.m_d.is_pos());
|
||||
SASSERT(sig.m_r.is_int() && sig.m_r.is_pos());
|
||||
SASSERT(m_bv.get_bv_size(m) == sig.m_msz);
|
||||
SASSERT(m_bv.get_bv_size(n) == sig.m_nsz);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bv2real_util::is_bv2real(expr* e, expr*& m, expr*& n, rational& d, rational& r) const {
|
||||
if (!is_app(e)) return false;
|
||||
func_decl* f = to_app(e)->get_decl();
|
||||
return is_bv2real(f, to_app(e)->get_num_args(), to_app(e)->get_args(), m, n, d, r);
|
||||
}
|
||||
|
||||
class bv2real_util::contains_bv2real_proc {
|
||||
bv2real_util const& m_util;
|
||||
public:
|
||||
class found {};
|
||||
contains_bv2real_proc(bv2real_util const& u): m_util(u) {}
|
||||
void operator()(app* a) {
|
||||
if (m_util.is_bv2real(a->get_decl())) {
|
||||
throw found();
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
bool bv2real_util::contains_bv2real(expr* e) const {
|
||||
contains_bv2real_proc p(*this);
|
||||
try {
|
||||
for_each_expr(p, e);
|
||||
}
|
||||
catch (contains_bv2real_proc::found) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2real_util::mk_bv2real(expr* _s, expr* _t, rational& d, rational& r, expr_ref& result) {
|
||||
expr_ref s(_s,m()), t(_t,m());
|
||||
if (align_divisor(s, t, d)) {
|
||||
result = mk_bv2real_c(s, t, d, r);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r) {
|
||||
bvr_sig sig;
|
||||
sig.m_msz = m_bv.get_bv_size(s);
|
||||
sig.m_nsz = m_bv.get_bv_size(t);
|
||||
sig.m_d = d;
|
||||
sig.m_r = r;
|
||||
func_decl* f;
|
||||
if (!m_sig2decl.find(sig, f)) {
|
||||
sort* domain[2] = { m_manager.get_sort(s), m_manager.get_sort(t) };
|
||||
sort* real = m_arith.mk_real();
|
||||
f = m_manager.mk_fresh_func_decl("bv2real", "", 2, domain, real);
|
||||
m_decls.push_back(f);
|
||||
m_sig2decl.insert(sig, f);
|
||||
m_decl2sig.insert(f, sig);
|
||||
}
|
||||
return m_manager.mk_app(f, s, t);
|
||||
}
|
||||
|
||||
void bv2real_util::mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result) {
|
||||
expr_ref s1(m()), t1(m()), r1(m());
|
||||
rational num;
|
||||
mk_sbv2real(s, s1);
|
||||
mk_sbv2real(t, t1);
|
||||
mk_div(s1, d, s1);
|
||||
mk_div(t1, d, t1);
|
||||
r1 = a().mk_power(a().mk_numeral(r, false), a().mk_numeral(rational(1,2),false));
|
||||
t1 = a().mk_mul(t1, r1);
|
||||
result = a().mk_add(s1, t1);
|
||||
}
|
||||
|
||||
void bv2real_util::mk_div(expr* e, rational const& d, expr_ref& result) {
|
||||
result = a().mk_div(e, a().mk_numeral(rational(d), false));
|
||||
}
|
||||
|
||||
void bv2real_util::mk_sbv2real(expr* e, expr_ref& result) {
|
||||
rational r;
|
||||
unsigned bv_size = m_bv.get_bv_size(e);
|
||||
rational bsize = power(rational(2), bv_size);
|
||||
expr_ref bvr(a().mk_to_real(m_bv.mk_bv2int(e)), m());
|
||||
expr_ref c(m_bv.mk_sle(m_bv.mk_numeral(rational(0), bv_size), e), m());
|
||||
result = m().mk_ite(c, bvr, a().mk_sub(bvr, a().mk_numeral(bsize, false)));
|
||||
}
|
||||
|
||||
|
||||
expr* bv2real_util::mk_bv_mul(rational const& n, expr* t) {
|
||||
if (n.is_one()) return t;
|
||||
expr* s = mk_sbv(n);
|
||||
return mk_bv_mul(s, t);
|
||||
}
|
||||
|
||||
void bv2real_util::align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2) {
|
||||
if (d1 == d2) {
|
||||
return;
|
||||
}
|
||||
// s/d1 ~ t/d2 <=> lcm*s/d1 ~ lcm*t/d2 <=> (lcm/d1)*s ~ (lcm/d2)*t
|
||||
// s/d1 ~ t/d2 <=> s/gcd*d1' ~ t/gcd*d2' <=> d2'*s/lcm ~ d1'*t/lcm
|
||||
|
||||
rational g = gcd(d1,d2);
|
||||
rational l = lcm(d1,d2);
|
||||
rational d1g = d1/g;
|
||||
rational d2g = d2/g;
|
||||
s1 = mk_bv_mul(d2g, s1);
|
||||
s2 = mk_bv_mul(d2g, s2);
|
||||
t1 = mk_bv_mul(d1g, t1);
|
||||
t2 = mk_bv_mul(d1g, t2);
|
||||
d1 = l;
|
||||
d2 = l;
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv_mul(expr* s, expr* t) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
if (is_zero(s)) {
|
||||
return s;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return t;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1);
|
||||
unsigned n = m_bv.get_bv_size(t1);
|
||||
unsigned max_bits = get_max_num_bits();
|
||||
bool add_side_conds = 2*n > max_bits;
|
||||
if (n >= max_bits) {
|
||||
// nothing
|
||||
}
|
||||
else if (2*n > max_bits) {
|
||||
s1 = mk_extend(max_bits-n, s1);
|
||||
t1 = mk_extend(max_bits-n, t1);
|
||||
}
|
||||
else {
|
||||
s1 = mk_extend(n, s1);
|
||||
t1 = mk_extend(n, t1);
|
||||
}
|
||||
if (add_side_conds) {
|
||||
add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1));
|
||||
add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1));
|
||||
}
|
||||
return m_bv.mk_bv_mul(s1, t1);
|
||||
}
|
||||
|
||||
bool bv2real_util::is_zero(expr* n) {
|
||||
rational r;
|
||||
unsigned sz;
|
||||
return m_bv.is_numeral(n, r, sz) && r.is_zero();
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv_add(expr* s, expr* t) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
|
||||
if (is_zero(s)) {
|
||||
return t;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return s;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1);
|
||||
s1 = mk_extend(1, s1);
|
||||
t1 = mk_extend(1, t1);
|
||||
return m_bv.mk_bv_add(s1, t1);
|
||||
}
|
||||
|
||||
void bv2real_util::align_sizes(expr_ref& s, expr_ref& t) {
|
||||
unsigned sz1 = m_bv.get_bv_size(s);
|
||||
unsigned sz2 = m_bv.get_bv_size(t);
|
||||
if (sz1 > sz2) {
|
||||
t = mk_extend(sz1-sz2, t);
|
||||
}
|
||||
else if (sz1 < sz2) {
|
||||
s = mk_extend(sz2-sz1, s);
|
||||
}
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_sbv(rational const& n) {
|
||||
SASSERT(n.is_int());
|
||||
if (n.is_neg()) {
|
||||
rational m = abs(n);
|
||||
unsigned nb = m.get_num_bits();
|
||||
return m_bv.mk_bv_neg(m_bv.mk_numeral(m, nb+1));
|
||||
}
|
||||
else {
|
||||
unsigned nb = n.get_num_bits();
|
||||
return m_bv.mk_numeral(n, nb+1);
|
||||
}
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv_sub(expr* s, expr* t) {
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1);
|
||||
s1 = mk_extend(1, s1);
|
||||
t1 = mk_extend(1, t1);
|
||||
return m_bv.mk_bv_sub(s1, t1);
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_extend(unsigned sz, expr* b) {
|
||||
if (sz == 0) {
|
||||
return b;
|
||||
}
|
||||
rational r;
|
||||
unsigned bv_sz;
|
||||
if (m_bv.is_numeral(b, r, bv_sz) &&
|
||||
power(rational(2),bv_sz-1) > r) {
|
||||
return m_bv.mk_numeral(r, bv_sz + sz);
|
||||
}
|
||||
return m_bv.mk_sign_extend(sz, b);
|
||||
}
|
||||
|
||||
|
||||
bool bv2real_util::is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r) {
|
||||
expr* _s, *_t;
|
||||
if (is_bv2real(n, _s, _t, d, r)) {
|
||||
s = _s;
|
||||
t = _t;
|
||||
return true;
|
||||
}
|
||||
rational k;
|
||||
bool is_int;
|
||||
if (m_arith.is_numeral(n, k, is_int) && !is_int) {
|
||||
d = denominator(k);
|
||||
r = default_root();
|
||||
s = mk_sbv(numerator(k));
|
||||
t = mk_sbv(rational(0));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2real_util::align_divisor(expr_ref& s, expr_ref& t, rational& d) {
|
||||
if (d > max_divisor()) {
|
||||
//
|
||||
// if divisor is over threshold, then divide s and t
|
||||
// add side condition that s, t are divisible.
|
||||
//
|
||||
rational overflow = d / max_divisor();
|
||||
if (!overflow.is_int()) return false;
|
||||
if (!mk_is_divisible_by(s, overflow)) return false;
|
||||
if (!mk_is_divisible_by(t, overflow)) return false;
|
||||
d = max_divisor();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bv2real_util::mk_is_divisible_by(expr_ref& s, rational const& _overflow) {
|
||||
rational overflow(_overflow);
|
||||
SASSERT(overflow.is_int());
|
||||
SASSERT(overflow.is_pos());
|
||||
SASSERT(!overflow.is_one());
|
||||
TRACE("bv2real_rewriter",
|
||||
tout << mk_pp(s, m()) << " " << overflow << "\n";);
|
||||
unsigned power2 = 0;
|
||||
while ((overflow % rational(2)) == rational(0)) {
|
||||
power2++;
|
||||
overflow = div(overflow, rational(2));
|
||||
}
|
||||
|
||||
if (power2 > 0) {
|
||||
unsigned sz = m_bv.get_bv_size(s);
|
||||
if (sz <= power2) {
|
||||
add_side_condition(m().mk_eq(s, m_bv.mk_numeral(rational(0), sz)));
|
||||
s = m_bv.mk_numeral(rational(0), 1);
|
||||
}
|
||||
else {
|
||||
expr* s1 = m_bv.mk_extract(power2-1, 0, s);
|
||||
add_side_condition(m().mk_eq(s1, m_bv.mk_numeral(rational(0), power2)));
|
||||
s = m_bv.mk_extract(sz-1, power2, s);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("bv2real_rewriter",
|
||||
tout << mk_pp(s, m()) << " " << overflow << "\n";);
|
||||
|
||||
return overflow.is_one();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// bv2real_rewriter
|
||||
|
||||
bv2real_rewriter::bv2real_rewriter(ast_manager& m, bv2real_util& util):
|
||||
m_manager(m),
|
||||
m_util(util),
|
||||
m_bv(m),
|
||||
m_arith(m)
|
||||
{}
|
||||
|
||||
|
||||
br_status bv2real_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
TRACE("bv2real_rewriter",
|
||||
tout << mk_pp(f, m()) << " ";
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
tout << mk_pp(args[i], m()) << " ";
|
||||
}
|
||||
tout << "\n";);
|
||||
if(f->get_family_id() == m_arith.get_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_NUM: return BR_FAILED;
|
||||
case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result);
|
||||
case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result);
|
||||
case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result);
|
||||
case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result);
|
||||
case OP_ADD: return mk_add(num_args, args, result);
|
||||
case OP_MUL: return mk_mul(num_args, args, result);
|
||||
case OP_SUB: return mk_sub(num_args, args, result);
|
||||
case OP_DIV: SASSERT(num_args == 2); return mk_div(args[0], args[1], result);
|
||||
case OP_IDIV: return BR_FAILED;
|
||||
case OP_MOD: return BR_FAILED;
|
||||
case OP_REM: return BR_FAILED;
|
||||
case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result);
|
||||
case OP_TO_REAL: return BR_FAILED; // TBD
|
||||
case OP_TO_INT: return BR_FAILED; // TBD
|
||||
case OP_IS_INT: return BR_FAILED; // TBD
|
||||
default: return BR_FAILED;
|
||||
}
|
||||
}
|
||||
if (f->get_family_id() == m().get_basic_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result);
|
||||
case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result);
|
||||
default: return BR_FAILED;
|
||||
}
|
||||
}
|
||||
if (u().is_pos_ltf(f)) {
|
||||
SASSERT(num_args == 2);
|
||||
return mk_lt_pos(args[0], args[1], result);
|
||||
}
|
||||
if (u().is_pos_lef(f)) {
|
||||
SASSERT(num_args == 2);
|
||||
return mk_le_pos(args[0], args[1], result);
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool bv2real_rewriter::mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
SASSERT(is_pos || is_neg);
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) &&
|
||||
r1 == r2 && r1 == rational(2)) {
|
||||
//
|
||||
// (s1 + s2*sqrt(2))/d1 <= (t1 + t2*sqrt(2))/d2
|
||||
// <=>
|
||||
//
|
||||
// let s1 = s1*d2-t1*d1, t2 = s2*d2-t2*d1
|
||||
//
|
||||
//
|
||||
// s1 + s2*sqrt(2) <= 0
|
||||
// <=
|
||||
// s1 + s2*approx(sign(s2),sqrt(2)) <= 0
|
||||
// or (s1 = 0 & s2 = 0)
|
||||
//
|
||||
// If s2 is negative use an under-approximation for sqrt(r).
|
||||
// If s2 is positive use an over-approximation for sqrt(r).
|
||||
// e.g., r = 2, then 5/4 and 3/2 are under/over approximations.
|
||||
// Then s1 + s2*approx(sign(s2), r) <= 0 => s1 + s2*sqrt(r) <= 0
|
||||
|
||||
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
s1 = u().mk_bv_sub(s1, t1);
|
||||
s2 = u().mk_bv_sub(s2, t2);
|
||||
unsigned s2_size = m_bv.get_bv_size(s2);
|
||||
expr_ref le_proxy(m().mk_fresh_const("le_proxy",m().mk_bool_sort()), m());
|
||||
u().add_aux_decl(to_app(le_proxy)->get_decl());
|
||||
expr_ref gt_proxy(m().mk_not(le_proxy), m());
|
||||
expr_ref s2_is_nonpos(m_bv.mk_sle(s2, m_bv.mk_numeral(rational(0), s2_size)), m());
|
||||
|
||||
expr_ref under(u().mk_bv_add(u().mk_bv_mul(rational(4), s1), u().mk_bv_mul(rational(5), s2)), m());
|
||||
expr_ref z1(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(under)), m());
|
||||
expr_ref le_under(m_bv.mk_sle(under, z1), m());
|
||||
expr_ref over(u().mk_bv_add(u().mk_bv_mul(rational(2), s1), u().mk_bv_mul(rational(3), s2)), m());
|
||||
expr_ref z2(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(over)), m());
|
||||
expr_ref le_over(m_bv.mk_sle(over, z2), m());
|
||||
|
||||
// predicate may occur in positive polarity.
|
||||
if (is_pos) {
|
||||
// s1 + s2*sqrt(2) <= 0 <== s2 <= 0 & s1 + s2*(5/4) <= 0; 4*s1 + 5*s2 <= 0
|
||||
expr* e1 = m().mk_implies(m().mk_and(le_proxy, s2_is_nonpos), le_under);
|
||||
// s1 + s2*sqrt(2) <= 0 <== s2 > 0 & s1 + s2*(3/2); 0 <=> 2*s1 + 3*s2 <= 0
|
||||
expr* e2 = m().mk_implies(m().mk_and(le_proxy, m().mk_not(s2_is_nonpos)), le_over);
|
||||
u().add_side_condition(e1);
|
||||
u().add_side_condition(e2);
|
||||
}
|
||||
// predicate may occur in negative polarity.
|
||||
if (is_neg) {
|
||||
// s1 + s2*sqrt(2) > 0 <== s2 > 0 & s1 + s2*(5/4) > 0; 4*s1 + 5*s2 > 0
|
||||
expr* e3 = m().mk_implies(m().mk_and(gt_proxy, m().mk_not(s2_is_nonpos)), m().mk_not(le_under));
|
||||
// s1 + s2*sqrt(2) > 0 <== s2 <= 0 & s1 + s2*(3/2) > 0 <=> 2*s1 + 3*s2 > 0
|
||||
expr* e4 = m().mk_implies(m().mk_and(gt_proxy, s2_is_nonpos), m().mk_not(le_over));
|
||||
u().add_side_condition(e3);
|
||||
u().add_side_condition(e4);
|
||||
}
|
||||
|
||||
TRACE("bv2real_rewriter", tout << "mk_le\n";);
|
||||
|
||||
if (is_pos) {
|
||||
result = le_proxy;
|
||||
}
|
||||
else {
|
||||
result = gt_proxy;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_le_pos(expr * s, expr * t, expr_ref & result) {
|
||||
if (mk_le(s, t, true, false, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_lt_pos(expr * s, expr * t, expr_ref & result) {
|
||||
if (mk_le(t, s, false, true, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2real_rewriter::mk_le(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
|
||||
if (mk_le(s, t, true, true, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
//
|
||||
// somewhat expensive approach without having
|
||||
// polarity information for sound approximation.
|
||||
//
|
||||
|
||||
// Convert to:
|
||||
// t1 + t2*sqrt(r) >= 0
|
||||
// then to:
|
||||
//
|
||||
// (t1 >= 0 && t2 <= 0 => t1^2 >= t2^2*r)
|
||||
// (t1 <= 0 && t2 >= 0 => t1^2 <= t2^2*r)
|
||||
// (t1 >= 0 || t2 >= 0)
|
||||
//
|
||||
// A cheaper approach is to approximate > under the assumption
|
||||
// that > occurs in positive polarity.
|
||||
// then if t2 is negative use an over-approximation for sqrt(r)
|
||||
// if t2 is positive use an under-approximation for sqrt(r).
|
||||
// e.g., r = 2, then 5/4 and 3/2 are under/over approximations.
|
||||
// Then t1 + t2*approx(sign(t2), r) > 0 => t1 + t2*sqrt(r) > 0
|
||||
//
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
t1 = u().mk_bv_sub(t1, s1);
|
||||
t2 = u().mk_bv_sub(t2, s2);
|
||||
expr_ref z1(m()), z2(m());
|
||||
z1 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t1));
|
||||
z2 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t2));
|
||||
|
||||
expr* gz1 = m_bv.mk_sle(z1, t1);
|
||||
expr* lz1 = m_bv.mk_sle(t1, z1);
|
||||
expr* gz2 = m_bv.mk_sle(z2, t2);
|
||||
expr* lz2 = m_bv.mk_sle(t2, z2);
|
||||
expr_ref t12(u().mk_bv_mul(t1, t1), m());
|
||||
expr_ref t22(u().mk_bv_mul(r1, u().mk_bv_mul(t2, t2)), m());
|
||||
u().align_sizes(t12, t22);
|
||||
expr* ge = m_bv.mk_sle(t22, t12);
|
||||
expr* le = m_bv.mk_sle(t12, t22);
|
||||
expr* e1 = m().mk_or(gz1, gz2);
|
||||
expr* e2 = m().mk_or(m().mk_not(gz1), m().mk_not(lz2), ge);
|
||||
expr* e3 = m().mk_or(m().mk_not(gz2), m().mk_not(lz1), le);
|
||||
result = m().mk_and(e1, e2, e3);
|
||||
TRACE("bv2real_rewriter", tout << "\n";);
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg2, arg1));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
return mk_le(arg2, arg1, result);
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
u().align_sizes(s1, t1);
|
||||
u().align_sizes(s2, t2);
|
||||
if (u().mk_bv2real(m().mk_ite(c, s1, t1), m().mk_ite(c, s2, t2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
u().align_sizes(s1, t1);
|
||||
u().align_sizes(s2, t2);
|
||||
result = m().mk_and(m().mk_eq(s1, t1), m().mk_eq(s2, t2));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_uminus(expr * s, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m());
|
||||
rational d1, r1;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1)) {
|
||||
s1 = u().mk_extend(1, s1);
|
||||
s2 = u().mk_extend(1, s2);
|
||||
if (u().mk_bv2real(m_bv.mk_bv_neg(s1), m_bv.mk_bv_neg(s2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_add(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_add(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
if (u().mk_bv2real(u().mk_bv_add(s1, t1), u().mk_bv_add(t2, s2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_mul(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_div(expr* s, expr* t, expr_ref& result) {
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) {
|
||||
// TBD: optimize
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
// s1*t1 + r1*(s2*t2) + (s1*t2 + s2*t2)*r1
|
||||
expr_ref u1(m()), u2(m());
|
||||
u1 = u().mk_bv_add(u().mk_bv_mul(s1, t1), u().mk_bv_mul(r1, u().mk_bv_mul(t2, s2)));
|
||||
u2 = u().mk_bv_add(u().mk_bv_mul(s1, t2), u().mk_bv_mul(s2, t1));
|
||||
rational tmp = d1*d2;
|
||||
if (u().mk_bv2real(u1, u2, tmp, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_sub(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2real_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
if (u().mk_bv2real(u().mk_bv_sub(s1, t1), u().mk_bv_sub(s2, t2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template class rewriter_tpl<bv2real_rewriter_cfg>;
|
||||
|
||||
|
||||
br_status bv2real_elim_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
expr* m, *n;
|
||||
rational d, r;
|
||||
if (m_util.is_bv2real(f, num_args, args, m, n, d, r)) {
|
||||
m_util.mk_bv2real_reduced(m, n, d, r, result);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
template class rewriter_tpl<bv2real_elim_rewriter_cfg>;
|
||||
|
233
src/tactic/arith/bv2real_rewriter.h
Normal file
233
src/tactic/arith/bv2real_rewriter.h
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2real_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2real propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-08-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _BV2REAL_REWRITER_H_
|
||||
#define _BV2REAL_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter.h"
|
||||
#include"bv_decl_plugin.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
|
||||
//
|
||||
// bv2real[d,r](n,m) has interpretation:
|
||||
// sbv2int(n)/d + sbv2int(m)/d*sqrt(r)
|
||||
// where
|
||||
// sbv2int is signed bit-vector 2 integer.
|
||||
//
|
||||
class bv2real_util {
|
||||
struct bvr_sig {
|
||||
unsigned m_msz, m_nsz;
|
||||
rational m_d, m_r;
|
||||
};
|
||||
|
||||
struct bvr_eq {
|
||||
bool operator()(bvr_sig const& x, bvr_sig const& y) const {
|
||||
return
|
||||
x.m_msz == y.m_msz &&
|
||||
x.m_nsz == y.m_nsz &&
|
||||
x.m_d == y.m_d &&
|
||||
x.m_r == y.m_r;
|
||||
}
|
||||
};
|
||||
|
||||
struct bvr_hash {
|
||||
unsigned operator()(bvr_sig const& x) const {
|
||||
unsigned a[3] = { x.m_msz, x.m_nsz, x.m_d.hash() };
|
||||
return string_hash((char const*)a, 12, x.m_r.hash());
|
||||
}
|
||||
};
|
||||
|
||||
ast_manager& m_manager;
|
||||
arith_util m_arith;
|
||||
bv_util m_bv;
|
||||
func_decl_ref_vector m_decls;
|
||||
func_decl_ref m_pos_le;
|
||||
func_decl_ref m_pos_lt;
|
||||
expr_ref_vector m_side_conditions;
|
||||
map<bvr_sig, func_decl*, bvr_hash, bvr_eq> m_sig2decl;
|
||||
obj_map<func_decl, bvr_sig> m_decl2sig;
|
||||
rational m_default_root;
|
||||
rational m_default_divisor;
|
||||
rational m_max_divisor;
|
||||
unsigned m_max_num_bits;
|
||||
|
||||
class contains_bv2real_proc;
|
||||
|
||||
public:
|
||||
bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits);
|
||||
|
||||
void reset() { m_side_conditions.reset(); }
|
||||
|
||||
bool is_bv2real(func_decl* f) const;
|
||||
bool is_bv2real(func_decl* f, unsigned num_args, expr* const* args,
|
||||
expr*& m, expr*& n, rational& d, rational& r) const;
|
||||
bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d, rational& r) const;
|
||||
bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d);
|
||||
|
||||
bool contains_bv2real(expr* e) const;
|
||||
|
||||
bool mk_bv2real(expr* s, expr* t, rational& d, rational& r, expr_ref& result);
|
||||
expr* mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r);
|
||||
expr* mk_bv2real(expr* n, expr* m) { return mk_bv2real_c(n, m, default_divisor(), default_root()); }
|
||||
|
||||
void mk_bv2real_reduced(expr* s, expr* t, expr_ref & result) { mk_bv2real_reduced(s, t, default_divisor(), default_root(), result); }
|
||||
void mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result);
|
||||
|
||||
|
||||
//
|
||||
// Positive polarity comparison operators.
|
||||
// Translation of positive polarity comparison requires fewer clauses.
|
||||
//
|
||||
bool is_pos_ltf(func_decl* f) const { return f == m_pos_lt; }
|
||||
bool is_pos_lef(func_decl* f) const { return f == m_pos_le; }
|
||||
bool is_pos_lt(expr const* e) const { return is_app(e) && is_pos_ltf(to_app(e)->get_decl()); }
|
||||
bool is_pos_le(expr const* e) const { return is_app(e) && is_pos_lef(to_app(e)->get_decl()); }
|
||||
MATCH_BINARY(is_pos_lt);
|
||||
MATCH_BINARY(is_pos_le);
|
||||
expr* mk_pos_lt(expr* s, expr* t) { return m().mk_app(m_pos_lt, s, t); }
|
||||
expr* mk_pos_le(expr* s, expr* t) { return m().mk_app(m_pos_le, s, t); }
|
||||
|
||||
rational const& default_root() const { return m_default_root; }
|
||||
rational const& default_divisor() const { return m_default_divisor; }
|
||||
rational const& max_divisor() const { return m_max_divisor; }
|
||||
|
||||
unsigned get_max_num_bits() const { return m_max_num_bits; }
|
||||
|
||||
void add_side_condition(expr* e) { m_side_conditions.push_back(e); }
|
||||
unsigned num_side_conditions() const { return m_side_conditions.size(); }
|
||||
expr* const* side_conditions() const { return m_side_conditions.c_ptr(); }
|
||||
|
||||
bool is_zero(expr* e);
|
||||
|
||||
expr* mk_bv_add(expr* s, expr* t);
|
||||
expr* mk_bv_sub(expr* s, expr* t);
|
||||
expr* mk_bv_mul(expr* s, expr* t);
|
||||
expr* mk_bv_mul(rational const& n, expr* t);
|
||||
expr* mk_extend(unsigned sz, expr* b);
|
||||
expr* mk_sbv(rational const& n);
|
||||
|
||||
void align_sizes(expr_ref& s, expr_ref& t);
|
||||
void align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2);
|
||||
|
||||
bool is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r);
|
||||
bool align_divisor(expr_ref& s, expr_ref& t, rational& d);
|
||||
|
||||
bool mk_is_divisible_by(expr_ref& s, rational const& _overflow);
|
||||
|
||||
void add_aux_decl(func_decl* f) { m_decls.push_back(f); }
|
||||
unsigned num_aux_decls() const { return m_decls.size(); }
|
||||
func_decl* get_aux_decl(unsigned i) const { return m_decls[i]; }
|
||||
|
||||
private:
|
||||
ast_manager & m() const { return m_manager; }
|
||||
arith_util & a() { return m_arith; }
|
||||
void mk_div(expr* e, rational const& d, expr_ref& result);
|
||||
void mk_sbv2real(expr* e, expr_ref& result);
|
||||
};
|
||||
|
||||
|
||||
class bv2real_rewriter {
|
||||
ast_manager & m_manager;
|
||||
bv2real_util& m_util;
|
||||
bv_util m_bv;
|
||||
arith_util m_arith;
|
||||
|
||||
public:
|
||||
bv2real_rewriter(ast_manager & m, bv2real_util& util);
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
private:
|
||||
ast_manager & m() const { return m_manager; }
|
||||
arith_util & a() { return m_arith; }
|
||||
bv2real_util& u() { return m_util; }
|
||||
br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result);
|
||||
bool mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result);
|
||||
br_status mk_le(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_le_pos(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt_pos(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_div(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_add(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_mul(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_sub(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_uminus(expr* e, expr_ref & result);
|
||||
};
|
||||
|
||||
struct bv2real_rewriter_cfg : public default_rewriter_cfg {
|
||||
bv2real_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
bv2real_rewriter_cfg(ast_manager & m, bv2real_util& u):m_r(m, u) {}
|
||||
};
|
||||
|
||||
class bv2real_rewriter_star : public rewriter_tpl<bv2real_rewriter_cfg> {
|
||||
bv2real_rewriter_cfg m_cfg;
|
||||
public:
|
||||
bv2real_rewriter_star(ast_manager & m, bv2real_util& u):
|
||||
rewriter_tpl<bv2real_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(m, u) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
\brief replace le(bv2real(a),bv2real(b)) by under-approximation.
|
||||
*/
|
||||
|
||||
class bv2real_elim_rewriter {
|
||||
bv2real_util& m_util;
|
||||
public:
|
||||
bv2real_elim_rewriter(bv2real_util& util) : m_util(util) {}
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
};
|
||||
|
||||
|
||||
struct bv2real_elim_rewriter_cfg : public default_rewriter_cfg {
|
||||
bv2real_elim_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
bv2real_elim_rewriter_cfg(bv2real_util& u):m_r(u) {}
|
||||
};
|
||||
|
||||
class bv2real_elim_rewriter_star : public rewriter_tpl<bv2real_elim_rewriter_cfg> {
|
||||
bv2real_elim_rewriter_cfg m_cfg;
|
||||
public:
|
||||
bv2real_elim_rewriter_star(ast_manager & m, bv2real_util& u):
|
||||
rewriter_tpl<bv2real_elim_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(u) {}
|
||||
};
|
||||
|
||||
#endif
|
1792
src/tactic/arith/dead/lu.cpp
Normal file
1792
src/tactic/arith/dead/lu.cpp
Normal file
File diff suppressed because it is too large
Load diff
404
src/tactic/arith/dead/lu.h
Normal file
404
src/tactic/arith/dead/lu.h
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
lu.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Simple LU factorization module based on the paper:
|
||||
|
||||
"Maintaining LU factors of a General Sparse Matrix"
|
||||
P. E. Gill, W. Murray, M. Saunders, M. Wright
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-09
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _LU_H_
|
||||
#define _LU_H_
|
||||
|
||||
#include"vector.h"
|
||||
#include"mpq.h"
|
||||
#include"double_manager.h"
|
||||
#include"permutation.h"
|
||||
#include"params.h"
|
||||
#include"strategy_exception.h"
|
||||
|
||||
MK_ST_EXCEPTION(lu_exception);
|
||||
|
||||
template<typename NumManager>
|
||||
class lu {
|
||||
public:
|
||||
typedef NumManager manager;
|
||||
typedef typename NumManager::numeral numeral;
|
||||
typedef svector<numeral> numeral_vector;
|
||||
|
||||
private:
|
||||
manager & m_manager;
|
||||
|
||||
// Configuration
|
||||
numeral m_mu; // maximum multiplier when selecting a pivot
|
||||
unsigned m_selection_cutoff;
|
||||
|
||||
// Matrix size
|
||||
unsigned m_sz; // supporting only square matrices
|
||||
|
||||
// Permutations
|
||||
permutation P;
|
||||
permutation Q;
|
||||
|
||||
// L
|
||||
//
|
||||
// It is 3 parallel vectors representing the sequence (product) of matrices
|
||||
// L[0] L[1] ... L[m-1]
|
||||
// where each L[i] is a tuple (A[i], indc[i], indr[i]).
|
||||
// Each tuple represents a triangular factor. That is, an identity matrix
|
||||
// where the position at row indc[i], and column indr[i] contains the value A[i].
|
||||
// Remark: The product L[0] L[1] ... L[n-1] is not really a triangular matrix.
|
||||
struct L_file {
|
||||
numeral_vector A;
|
||||
unsigned_vector indc;
|
||||
unsigned_vector indr;
|
||||
};
|
||||
L_file L;
|
||||
|
||||
|
||||
// U
|
||||
//
|
||||
// It is not really upper triangular, but the product PUQ is.
|
||||
// The rows of U are stored in the parallel vectors (A, indr)
|
||||
// Only the non-zero values are stored at U.
|
||||
// The non-zeros of row i start at position begr[i] and end at
|
||||
// position endr[i] of the parallel vectors (A, indr).
|
||||
// The length of the row is endr[i] - begr[i].
|
||||
// The coefficients are stored in A, and the column ids at indr.
|
||||
//
|
||||
// The factorization of a matrix A is represented as:
|
||||
// L[0] L[1] ... L[m-1] P U Q
|
||||
struct U_file {
|
||||
numeral_vector A;
|
||||
unsigned_vector indr;
|
||||
unsigned_vector begr;
|
||||
unsigned_vector endr;
|
||||
|
||||
unsigned num_entries;
|
||||
U_file():num_entries(0) {}
|
||||
};
|
||||
U_file U;
|
||||
|
||||
// The actual factorization
|
||||
|
||||
|
||||
// T_file: temporary file used for factorization
|
||||
struct T_file {
|
||||
// row list
|
||||
unsigned_vector indr;
|
||||
unsigned_vector begr;
|
||||
unsigned_vector endr;
|
||||
|
||||
// column list
|
||||
numeral_vector A;
|
||||
unsigned_vector indc;
|
||||
unsigned_vector begc;
|
||||
unsigned_vector endc;
|
||||
|
||||
unsigned num_entries;
|
||||
T_file():num_entries(0) {}
|
||||
};
|
||||
T_file T;
|
||||
|
||||
// Auxiliary fields
|
||||
unsigned_vector locw;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Main
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
lu(manager & m, params_ref const & p);
|
||||
~lu();
|
||||
|
||||
manager & m() const { return m_manager; }
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
|
||||
void reset();
|
||||
|
||||
unsigned size() const { return m_sz; }
|
||||
|
||||
protected:
|
||||
void del_nums(numeral_vector & nums);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
// Contract for setting up the initial matrix:
|
||||
// lu.init(size)
|
||||
// - for each row r in the matrix
|
||||
// - for each non-zero (a,x) in the row
|
||||
// lu.add_entry(a, x)
|
||||
// lu.end_row()
|
||||
void init(unsigned size);
|
||||
void add_entry(numeral const & a, unsigned x);
|
||||
void end_row();
|
||||
|
||||
protected:
|
||||
// auxiliary fields used during initialization
|
||||
bool ini; // try if the matrix T is being setup using the protocol above
|
||||
unsigned ini_irow;
|
||||
unsigned fillin_for(unsigned sz);
|
||||
void move_col_to_end(unsigned x);
|
||||
void move_row_to_end(unsigned x);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Factorization
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void fact();
|
||||
|
||||
protected:
|
||||
class todo {
|
||||
unsigned_vector m_elem2len;
|
||||
unsigned_vector m_elem2pos;
|
||||
vector<unsigned_vector> m_elems_per_len;
|
||||
unsigned m_size;
|
||||
public:
|
||||
todo():m_size(0) {}
|
||||
bool contains(unsigned elem) const { return m_elem2pos[elem] != UINT_MAX; }
|
||||
void init(unsigned capacity);
|
||||
void updt_len(unsigned elem, unsigned len);
|
||||
unsigned len(unsigned elem) const { return m_elem2len[elem]; }
|
||||
void erase(unsigned elem);
|
||||
unsigned size() const { return m_size; }
|
||||
void display(std::ostream & out) const;
|
||||
class iterator {
|
||||
todo const & m_todo;
|
||||
unsigned m_i;
|
||||
unsigned m_j;
|
||||
unsigned m_c;
|
||||
void find_next();
|
||||
public:
|
||||
iterator(todo const & t):m_todo(t), m_i(0), m_j(0), m_c(0) { if (!at_end()) find_next(); }
|
||||
bool at_end() const { return m_c == m_todo.m_size; }
|
||||
unsigned curr() const {
|
||||
unsigned_vector const & v_i = m_todo.m_elems_per_len[m_i];
|
||||
return v_i[m_j];
|
||||
}
|
||||
void next() { SASSERT(!at_end()); m_j++; m_c++; find_next(); }
|
||||
};
|
||||
};
|
||||
|
||||
todo m_todo_rows;
|
||||
todo m_todo_cols;
|
||||
svector<bool> m_enabled_rows;
|
||||
svector<bool> m_enabled_cols;
|
||||
|
||||
bool enabled_row(unsigned r) const { return m_enabled_rows[r]; }
|
||||
bool enabled_col(unsigned c) const { return m_enabled_cols[c]; }
|
||||
|
||||
unsigned_vector m_toadd_rows;
|
||||
svector<bool> m_marked_rows;
|
||||
|
||||
// Temporary numerals
|
||||
// I do not use local numerals to avoid memory leaks
|
||||
numeral tol;
|
||||
numeral C_max;
|
||||
numeral A_ij;
|
||||
numeral A_best;
|
||||
numeral A_aux;
|
||||
numeral tmp;
|
||||
numeral mu_best;
|
||||
numeral mu_1;
|
||||
|
||||
void init_fact();
|
||||
bool stability_test(unsigned rin, unsigned cin, bool improvingM);
|
||||
void select_pivot(unsigned & r_out, unsigned & c_out);
|
||||
void process_pivot_core(unsigned r, unsigned c);
|
||||
void process_pivot(unsigned i, unsigned r, unsigned c);
|
||||
bool check_locw() const;
|
||||
void dec_lenr(unsigned r);
|
||||
void inc_lenr(unsigned r);
|
||||
void dec_lenc(unsigned c);
|
||||
void inc_lenc(unsigned c);
|
||||
void del_row_entry(unsigned r, unsigned c);
|
||||
void del_disabled_cols(unsigned r);
|
||||
void add_row_entry(unsigned r, unsigned c);
|
||||
void add_col_entry(unsigned r, unsigned c, numeral const & a);
|
||||
void compress_rows();
|
||||
void compress_columns();
|
||||
void compress_if_needed();
|
||||
void copy_T_to_U();
|
||||
|
||||
bool check_lenr() const;
|
||||
bool check_lenc() const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Solving
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
|
||||
// Temporary vector used to interact with different solvers.
|
||||
// The vector has support for tracking non-zeros.
|
||||
class dense_vector {
|
||||
public:
|
||||
typedef typename lu<NumManager>::manager manager;
|
||||
typedef typename lu<NumManager>::numeral numeral;
|
||||
private:
|
||||
friend class lu;
|
||||
manager & m_manager;
|
||||
unsigned_vector m_non_zeros; // positions that may contain non-zeros. if a position is not here, then it must contain a zero
|
||||
char_vector m_in_non_zeros; // m_in_non_zeros[i] == true if m_non_zeros contains i.
|
||||
numeral_vector m_values;
|
||||
public:
|
||||
dense_vector(manager & m, unsigned sz);
|
||||
~dense_vector();
|
||||
|
||||
manager & m() const { return m_manager; }
|
||||
|
||||
void reset();
|
||||
void reset(unsigned new_sz);
|
||||
|
||||
unsigned size() const { return m_values.size(); }
|
||||
numeral const & operator[](unsigned idx) const { return m_values[idx]; }
|
||||
|
||||
void swap(dense_vector & other) {
|
||||
m_non_zeros.swap(other.m_non_zeros);
|
||||
m_in_non_zeros.swap(other.m_in_non_zeros);
|
||||
m_values.swap(other.m_values);
|
||||
}
|
||||
|
||||
// Get a given position for performing an update.
|
||||
// idx is inserted into m_non_zeros.
|
||||
numeral & get(unsigned idx) {
|
||||
if (!m_in_non_zeros[idx]) {
|
||||
m_in_non_zeros[idx] = true;
|
||||
m_non_zeros.push_back(idx);
|
||||
}
|
||||
return m_values[idx];
|
||||
}
|
||||
|
||||
typedef unsigned_vector::const_iterator iterator;
|
||||
|
||||
// iterator for positions that may contain non-zeros
|
||||
iterator begin_non_zeros() const { return m_non_zeros.begin(); }
|
||||
iterator end_non_zeros() const { return m_non_zeros.end(); }
|
||||
|
||||
void display(std::ostream & out) const;
|
||||
void display_non_zeros(std::ostream & out) const;
|
||||
void display_pol(std::ostream & out) const;
|
||||
|
||||
void elim_zeros();
|
||||
};
|
||||
|
||||
// Solve: Lx = y
|
||||
// The result is stored in y.
|
||||
void solve_Lx_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: PUQx = y
|
||||
// The result is stored in y.
|
||||
void solve_Ux_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: LPUQx = y
|
||||
// The result is stored in y.
|
||||
void solve_Ax_eq_y(dense_vector & y) {
|
||||
solve_Lx_eq_y(y);
|
||||
solve_Ux_eq_y(y);
|
||||
}
|
||||
|
||||
// Solve: xL = y
|
||||
// The result is stored in y.
|
||||
void solve_xL_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: xPUQ = y
|
||||
// The result is stored in y.
|
||||
void solve_xU_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: xA = y
|
||||
// The result is stored in y.
|
||||
void solve_xA_eq_y(dense_vector & y) {
|
||||
solve_xU_eq_y(y);
|
||||
solve_xL_eq_y(y);
|
||||
}
|
||||
|
||||
private:
|
||||
dense_vector m_tmp_xU_vector;
|
||||
dense_vector m_tmp_replace_column_vector;
|
||||
dense_vector m_tmp_vector;
|
||||
dense_vector m_tmp_row;
|
||||
|
||||
public:
|
||||
dense_vector & get_tmp_vector() { return m_tmp_vector; }
|
||||
dense_vector & get_tmp_row(unsigned size) { m_tmp_row.reset(size); return m_tmp_row; }
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Column replacement
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void replace_column(unsigned j, dense_vector & new_col);
|
||||
void replace_U_column(unsigned j, dense_vector & new_col);
|
||||
unsigned get_num_replacements() const { return m_num_replacements; }
|
||||
dense_vector & get_tmp_col() { return m_tmp_col; }
|
||||
|
||||
private:
|
||||
unsigned m_num_replacements;
|
||||
dense_vector m_tmp_col;
|
||||
|
||||
void del_U_row_entry(unsigned r, unsigned c);
|
||||
void compress_U_rows();
|
||||
void compress_U_if_needed();
|
||||
void move_U_row_to_end(unsigned r);
|
||||
void add_U_row_entry(unsigned r, unsigned c, numeral const & a);
|
||||
void add_replace_U_row_entry(unsigned r, unsigned c, numeral const & a);
|
||||
unsigned replace_U_column_core(unsigned j, dense_vector & new_col);
|
||||
bool check_U_except_col(unsigned c) const;
|
||||
bool check_U_except_row(unsigned r) const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Invariants
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
bool check_P() const;
|
||||
bool check_Q() const;
|
||||
bool check_L() const;
|
||||
bool check_U() const;
|
||||
bool T_col_contains(unsigned c, unsigned r) const;
|
||||
bool T_row_contains(unsigned r, unsigned c) const;
|
||||
bool check_T() const;
|
||||
bool check_invariant() const;
|
||||
|
||||
void display_T(std::ostream & out) const;
|
||||
void display_U(std::ostream & out, unsigned_vector const * var_ids = 0) const;
|
||||
void display_L(std::ostream & out) const;
|
||||
void display(std::ostream & out, unsigned_vector const * var_ids = 0) const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Info
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
unsigned L_size() const { return L.indc.size(); }
|
||||
unsigned U_size() const { return U.num_entries; }
|
||||
};
|
||||
|
||||
typedef lu<unsynch_mpq_manager> rational_lu;
|
||||
typedef lu<double_manager> double_lu;
|
||||
|
||||
#endif
|
135
src/tactic/arith/dead/mip_tactic.cpp
Normal file
135
src/tactic/arith/dead/mip_tactic.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
mip_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for solvig MIP (mixed integer) problem.
|
||||
This is a temporary tactic. It should be deleted
|
||||
after theory_arith is upgraded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-26
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"smt_solver_exp.h"
|
||||
|
||||
class mip_tactic : public tactic {
|
||||
struct imp;
|
||||
ast_manager & m;
|
||||
params_ref m_params;
|
||||
statistics m_stats;
|
||||
scoped_ptr<smt::solver_exp> m_solver;
|
||||
|
||||
void init_solver() {
|
||||
smt::solver_exp * new_solver = alloc(smt::solver_exp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_solver = new_solver;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
mip_tactic(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_params(p) {
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(mip_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~mip_tactic() {}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
}
|
||||
|
||||
virtual 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());
|
||||
bool produce_models = g->models_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("mip", *g);
|
||||
fail_if_proof_generation("mip", g);
|
||||
fail_if_unsat_core_generation("mip", g);
|
||||
|
||||
g->elim_redundancies();
|
||||
if (g->inconsistent()) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
init_solver();
|
||||
m_solver->assert_goal(*g);
|
||||
|
||||
lbool r;
|
||||
try {
|
||||
r = m_solver->check();
|
||||
}
|
||||
catch (strategy_exception & ex) {
|
||||
// solver_exp uses assertion_sets and strategy_exception's
|
||||
throw tactic_exception(ex.msg());
|
||||
}
|
||||
|
||||
m_solver->collect_statistics(m_stats);
|
||||
|
||||
if (r == l_false) {
|
||||
g->reset();
|
||||
g->assert_expr(m.mk_false());
|
||||
}
|
||||
else if (r == l_true) {
|
||||
g->reset();
|
||||
if (produce_models) {
|
||||
model_ref md;
|
||||
m_solver->get_model(md);
|
||||
mc = model2model_converter(md.get());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// failed
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("mip", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
if (m_solver)
|
||||
m_solver->collect_statistics(m_stats);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_solver = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void collect_statistics(statistics & st) const {
|
||||
st.copy(m_stats);
|
||||
}
|
||||
|
||||
virtual void reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_solver)
|
||||
m_solver->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_mip_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(mip_tactic, m, p));
|
||||
}
|
30
src/tactic/arith/dead/mip_tactic.h
Normal file
30
src/tactic/arith/dead/mip_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
mip_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for solvig MIP (mixed integer) problem.
|
||||
This is a temporary tactic. It should be deleted
|
||||
after theory_arith is upgraded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-26
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _MIP_TACTIC_H_
|
||||
#define _MIP_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_mip_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
3605
src/tactic/arith/dead/smt_arith.cpp
Normal file
3605
src/tactic/arith/dead/smt_arith.cpp
Normal file
File diff suppressed because it is too large
Load diff
56
src/tactic/arith/dead/smt_arith.h
Normal file
56
src/tactic/arith/dead/smt_arith.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_arith.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Arithmetic solver for smt::solver
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _SMT_ARITH_H_
|
||||
#define _SMT_ARITH_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"smt_solver_types.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
class model;
|
||||
|
||||
namespace smt {
|
||||
|
||||
class arith {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
arith(ast_manager & m, params_ref const & p);
|
||||
~arith();
|
||||
void updt_params(params_ref const & p);
|
||||
void assert_axiom(expr * t, bool neg);
|
||||
void mk_atom(expr * t, atom_id id);
|
||||
void asserted(atom_id id, bool is_true);
|
||||
bool inconsistent() const;
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void set_cancel(bool f);
|
||||
void simplify();
|
||||
void display(std::ostream & out) const;
|
||||
void reset();
|
||||
void preprocess();
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
lbool check();
|
||||
void mk_model(model * md);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
218
src/tactic/arith/dead/smt_formula_compiler.cpp
Normal file
218
src/tactic/arith/dead/smt_formula_compiler.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_formula_compiler.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Auxiliary class for smt::solver
|
||||
Convert Exprs into Internal data-structures.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
|
||||
--*/
|
||||
#include"smt_formula_compiler.h"
|
||||
#include"smt_solver_exp.h"
|
||||
#include"assertion_set_util.h"
|
||||
#include"assertion_set2sat.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
formula_compiler::formula_compiler(solver_exp & _s, params_ref const & p):
|
||||
s(_s),
|
||||
m_a_util(s.m),
|
||||
m_normalizer(s.m),
|
||||
m_elim_ite(s.m) {
|
||||
updt_params(p);
|
||||
|
||||
params_ref s_p;
|
||||
s_p.set_bool(":elim-and", true);
|
||||
s_p.set_bool(":blast-distinct", true);
|
||||
s_p.set_bool(":eq2ineq", true);
|
||||
s_p.set_bool(":arith-lhs", true);
|
||||
s_p.set_bool(":gcd-rounding", true);
|
||||
s_p.set_bool(":sort-sums", true);
|
||||
s_p.set_bool(":som", true);
|
||||
m_normalizer.updt_params(s_p);
|
||||
}
|
||||
|
||||
formula_compiler::~formula_compiler() {
|
||||
|
||||
}
|
||||
|
||||
// mark theory axioms: literals that do not occur in the boolean structure
|
||||
void formula_compiler::mark_axioms(assertion_set const & s, expr_fast_mark2 & axioms) {
|
||||
ast_manager & m = s.m();
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = s.form(i);
|
||||
while (m.is_not(f, f));
|
||||
if (!is_app(f) || to_app(f)->get_family_id() != m.get_basic_family_id()) {
|
||||
axioms.mark(f);
|
||||
continue;
|
||||
}
|
||||
SASSERT(is_app(f));
|
||||
SASSERT(to_app(f)->get_family_id() == m.get_basic_family_id());
|
||||
switch (to_app(f)->get_decl_kind()) {
|
||||
case OP_OR:
|
||||
case OP_IFF:
|
||||
break;
|
||||
case OP_ITE:
|
||||
SASSERT(m.is_bool(to_app(f)->get_arg(1)));
|
||||
break;
|
||||
case OP_EQ:
|
||||
if (!m.is_bool(to_app(f)->get_arg(1)))
|
||||
axioms.mark(f);
|
||||
break;
|
||||
case OP_AND:
|
||||
case OP_XOR:
|
||||
case OP_IMPLIES:
|
||||
case OP_DISTINCT:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct unmark_axioms_proc {
|
||||
expr_fast_mark2 & m_axioms;
|
||||
unmark_axioms_proc(expr_fast_mark2 & axioms):m_axioms(axioms) {}
|
||||
void operator()(quantifier *) {}
|
||||
void operator()(var *) {}
|
||||
void operator()(app * t) {
|
||||
m_axioms.reset_mark(t);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Unmark atoms that occur in the boolean structure.
|
||||
*/
|
||||
void formula_compiler::unmark_nested_atoms(assertion_set const & s, expr_fast_mark2 & axioms) {
|
||||
ast_manager & m = s.m();
|
||||
expr_fast_mark1 visited;
|
||||
unmark_axioms_proc proc(axioms);
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = s.form(i);
|
||||
while (m.is_not(f, f));
|
||||
if (axioms.is_marked(f))
|
||||
continue;
|
||||
quick_for_each_expr(proc, visited, f);
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::assert_axiom(expr * f, bool neg) {
|
||||
if (is_app(f)) {
|
||||
if (to_app(f)->get_family_id() == m_a_util.get_family_id())
|
||||
s.m_arith.assert_axiom(f, neg);
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::register_atom(expr * f, sat::bool_var p) {
|
||||
if (is_app(f)) {
|
||||
if (to_app(f)->get_family_id() == m_a_util.get_family_id())
|
||||
s.m_arith.mk_atom(f, p);
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::compile_formulas(assertion_set const & assertions) {
|
||||
ast_manager & m = assertions.m();
|
||||
expr_fast_mark2 axioms;
|
||||
mark_axioms(assertions, axioms);
|
||||
unmark_nested_atoms(assertions, axioms);
|
||||
ptr_vector<expr> formulas;
|
||||
|
||||
// send axioms to theories, and save formulas to compile
|
||||
unsigned sz = assertions.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = assertions.form(i);
|
||||
bool neg = false;
|
||||
while (m.is_not(f, f))
|
||||
neg = !neg;
|
||||
if (axioms.is_marked(f)) {
|
||||
assert_axiom(f, neg);
|
||||
}
|
||||
else {
|
||||
formulas.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
// compile formulas into sat::solver
|
||||
m_to_sat(m, formulas.size(), formulas.c_ptr(), s.m_params, *(s.m_sat), s.m_atom2bvar);
|
||||
|
||||
// register atoms nested in the boolean structure in the theories
|
||||
atom2bool_var::recent_iterator it = s.m_atom2bvar.begin_recent();
|
||||
atom2bool_var::recent_iterator end = s.m_atom2bvar.end_recent();
|
||||
for (; it != end; ++it) {
|
||||
expr * atom = *it;
|
||||
register_atom(atom, s.m_atom2bvar.to_bool_var(atom));
|
||||
}
|
||||
s.m_atom2bvar.reset_recent();
|
||||
}
|
||||
|
||||
void formula_compiler::normalize() {
|
||||
// make sure that the assertions are in the right format.
|
||||
m_normalizer(s.m_assertions);
|
||||
m_normalizer.cleanup();
|
||||
}
|
||||
|
||||
void formula_compiler::elim_term_ite() {
|
||||
if (has_term_ite(s.m_assertions)) {
|
||||
model_converter_ref mc;
|
||||
m_elim_ite(s.m_assertions, mc);
|
||||
s.m_mc = concat(s.m_mc.get(), mc.get());
|
||||
m_elim_ite.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::operator()() {
|
||||
if (s.m_assertions.inconsistent())
|
||||
return;
|
||||
// normalization
|
||||
elim_term_ite();
|
||||
normalize();
|
||||
|
||||
TRACE("before_formula_compiler", s.m_assertions.display(tout););
|
||||
|
||||
s.init();
|
||||
|
||||
compile_formulas(s.m_assertions);
|
||||
|
||||
s.m_arith.preprocess();
|
||||
TRACE("after_formula_compiler", s.display_state(tout););
|
||||
}
|
||||
|
||||
void formula_compiler::updt_params(params_ref const & p) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::collect_param_descrs(param_descrs & d) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::collect_statistics(statistics & st) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::reset_statistics() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::set_cancel(bool f) {
|
||||
m_normalizer.set_cancel(f);
|
||||
m_elim_ite.set_cancel(f);
|
||||
m_to_sat.set_cancel(f);
|
||||
}
|
||||
|
||||
};
|
63
src/tactic/arith/dead/smt_formula_compiler.h
Normal file
63
src/tactic/arith/dead/smt_formula_compiler.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_formula_compiler.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Auxiliary class for smt::solver
|
||||
Convert Exprs into Internal data-structures.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _SMT_FORMULA_COMPILER_H_
|
||||
#define _SMT_FORMULA_COMPILER_H_
|
||||
|
||||
#include"smt_solver_types.h"
|
||||
#include"assertion_set_rewriter.h"
|
||||
#include"elim_term_ite_strategy.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"assertion_set2sat.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
class formula_compiler {
|
||||
solver_exp & s;
|
||||
arith_util m_a_util;
|
||||
assertion_set_rewriter m_normalizer;
|
||||
elim_term_ite_strategy m_elim_ite;
|
||||
assertion_set2sat m_to_sat;
|
||||
|
||||
void normalize();
|
||||
void elim_term_ite();
|
||||
void mark_axioms(assertion_set const & s, expr_fast_mark2 & axioms);
|
||||
void unmark_nested_atoms(assertion_set const & s, expr_fast_mark2 & axioms);
|
||||
void assert_axiom(expr * f, bool neg);
|
||||
void register_atom(expr * f, sat::bool_var p);
|
||||
void compile_formulas(assertion_set const & assertions);
|
||||
|
||||
public:
|
||||
formula_compiler(solver_exp & s, params_ref const & p);
|
||||
~formula_compiler();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void collect_param_descrs(param_descrs & d);
|
||||
|
||||
void operator()();
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
void collect_statistics(statistics & st);
|
||||
void reset_statistics();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
232
src/tactic/arith/dead/smt_solver_exp.cpp
Normal file
232
src/tactic/arith/dead/smt_solver_exp.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_solver_exp.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT solver using strategies and search on top of sat::solver
|
||||
This solver uses assertion_set strategies during restarts.
|
||||
|
||||
It also uses the sat::solver to handle the Boolean structure of the problem.
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
|
||||
--*/
|
||||
#include"smt_solver_exp.h"
|
||||
#include"sat_solver.h"
|
||||
#include"ast_translation.h"
|
||||
#include"model.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
void solver_exp::bridge::get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r) {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::asserted(sat::literal l) {
|
||||
}
|
||||
|
||||
sat::check_result solver_exp::bridge::check() {
|
||||
return sat::CR_DONE;
|
||||
}
|
||||
|
||||
void solver_exp::bridge::push() {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::pop(unsigned n) {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::simplify() {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::clauses_modifed() {
|
||||
}
|
||||
|
||||
lbool solver_exp::bridge::get_phase(sat::bool_var v) {
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
solver_exp::solver_exp(ast_manager & ext_mng, params_ref const & p):
|
||||
m_ext_mng(ext_mng),
|
||||
m(ext_mng, true /* disable proof gen */),
|
||||
m_compiler(*this, p),
|
||||
m_assertions(m),
|
||||
m_atom2bvar(m),
|
||||
m_arith(m, p),
|
||||
m_bridge(*this) {
|
||||
updt_params_core(p);
|
||||
m_cancel = false;
|
||||
}
|
||||
|
||||
solver_exp::~solver_exp() {
|
||||
}
|
||||
|
||||
void solver_exp::updt_params_core(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
|
||||
void solver_exp::updt_params(params_ref const & p) {
|
||||
updt_params_core(p);
|
||||
m_arith.updt_params(p);
|
||||
if (m_sat)
|
||||
m_sat->updt_params(p);
|
||||
}
|
||||
|
||||
void solver_exp::collect_param_descrs(param_descrs & d) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void solver_exp::set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
#pragma omp critical (smt_solver_exp)
|
||||
{
|
||||
if (m_sat) {
|
||||
m_sat->set_cancel(f);
|
||||
}
|
||||
}
|
||||
m_arith.set_cancel(f);
|
||||
m_compiler.set_cancel(f);
|
||||
}
|
||||
|
||||
void solver_exp::init() {
|
||||
m_atom2bvar.reset();
|
||||
if (m_sat)
|
||||
m_sat->collect_statistics(m_stats);
|
||||
#pragma omp critical (smt_solver_exp)
|
||||
{
|
||||
m_sat = alloc(sat::solver, m_params, &m_bridge);
|
||||
}
|
||||
m_arith.collect_statistics(m_stats);
|
||||
m_arith.reset();
|
||||
set_cancel(m_cancel);
|
||||
}
|
||||
|
||||
void solver_exp::assert_expr_core(expr * t, ast_translation & translator) {
|
||||
expr * new_t = translator(t);
|
||||
m_assertions.assert_expr(new_t);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Assert an expression t (owned by the external manager)
|
||||
*/
|
||||
void solver_exp::assert_expr(expr * t) {
|
||||
ast_translation translator(m_ext_mng, m, false);
|
||||
assert_expr_core(t, translator);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Assert an assertion set s (owned by the external manager)
|
||||
*/
|
||||
void solver_exp::assert_set(assertion_set const & s) {
|
||||
SASSERT(&(s.m()) == &m_ext_mng);
|
||||
ast_translation translator(m_ext_mng, m, false);
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
assert_expr_core(s.form(i), translator);
|
||||
}
|
||||
}
|
||||
|
||||
void solver_exp::assert_goal(goal const & g) {
|
||||
SASSERT(&(g.m()) == &m_ext_mng);
|
||||
ast_translation translator(m_ext_mng, m, false);
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
assert_expr_core(g.form(i), translator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Store in r the current set of assertions.
|
||||
r is (owned) by the external assertion set
|
||||
*/
|
||||
void solver_exp::get_assertions(assertion_set & r) {
|
||||
SASSERT(&(r.m()) == &m_ext_mng);
|
||||
ast_translation translator(m, m_ext_mng, false);
|
||||
unsigned sz = m_assertions.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = m_assertions.form(i);
|
||||
r.assert_expr(translator(f));
|
||||
}
|
||||
}
|
||||
|
||||
void solver_exp::get_model_converter(model_converter_ref & mc) {
|
||||
ast_translation translator(m, m_ext_mng, false);
|
||||
if (m_mc)
|
||||
mc = m_mc->translate(translator);
|
||||
else
|
||||
mc = 0;
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Search
|
||||
//
|
||||
// -----------------------
|
||||
lbool solver_exp::check() {
|
||||
compile();
|
||||
lbool r = m_arith.check();
|
||||
if (r == l_false)
|
||||
return r;
|
||||
if (m_sat->num_vars() == 0 && r == l_true) {
|
||||
model_ref md = alloc(model, m);
|
||||
m_arith.mk_model(md.get());
|
||||
if (m_mc)
|
||||
(*m_mc)(md);
|
||||
ast_translation translator(m, m_ext_mng, false);
|
||||
m_model = md->translate(translator);
|
||||
return l_true;
|
||||
}
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
void solver_exp::compile() {
|
||||
m_compiler();
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Pretty Printing
|
||||
//
|
||||
// -----------------------
|
||||
void solver_exp::display(std::ostream & out) const {
|
||||
m_assertions.display(out);
|
||||
}
|
||||
|
||||
void solver_exp::display_state(std::ostream & out) const {
|
||||
if (m_sat) m_sat->display(out);
|
||||
m_arith.display(out);
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Statistics
|
||||
//
|
||||
// -----------------------
|
||||
void solver_exp::collect_statistics(statistics & st) const {
|
||||
solver_exp * _this = const_cast<solver_exp*>(this);
|
||||
if (m_sat) {
|
||||
m_sat->collect_statistics(_this->m_stats);
|
||||
m_sat->reset_statistics();
|
||||
}
|
||||
m_arith.collect_statistics(_this->m_stats);
|
||||
_this->m_arith.reset_statistics();
|
||||
st.copy(m_stats);
|
||||
}
|
||||
|
||||
void solver_exp::reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
130
src/tactic/arith/dead/smt_solver_exp.h
Normal file
130
src/tactic/arith/dead/smt_solver_exp.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_solver_exp.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT solver using strategies and search on top of sat::solver
|
||||
This solver uses assertion_set strategies during restarts.
|
||||
|
||||
It also uses the sat::solver to handle the Boolean structure of the problem.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
--*/
|
||||
#ifndef _SMT_SOLVER_EXP_H_
|
||||
#define _SMT_SOLVER_EXP_H_
|
||||
|
||||
#include"smt_solver_types.h"
|
||||
#include"model.h"
|
||||
#include"model_converter.h"
|
||||
#include"smt_formula_compiler.h"
|
||||
#include"smt_arith.h"
|
||||
#include"sat_extension.h"
|
||||
#include"goal.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
class solver_exp {
|
||||
friend class formula_compiler;
|
||||
|
||||
struct bridge : public sat::extension {
|
||||
solver_exp & s;
|
||||
bridge(solver_exp & _s):s(_s) {}
|
||||
virtual void propagate(sat::literal l, sat::ext_constraint_idx idx, bool & keep) {}
|
||||
virtual void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r);
|
||||
virtual void asserted(sat::literal l);
|
||||
virtual sat::check_result check();
|
||||
virtual void push();
|
||||
virtual void pop(unsigned n);
|
||||
virtual void simplify();
|
||||
virtual void clauses_modifed();
|
||||
virtual lbool get_phase(sat::bool_var v);
|
||||
};
|
||||
|
||||
// External ASTs are coming from m_ext_mng
|
||||
ast_manager & m_ext_mng;
|
||||
// The ASTs are translated into the internal manager for the following reasons:
|
||||
// 1. We can run multiple smt::solver_exps in parallel with minimal synchronization
|
||||
// 2. Minimize gaps in the AST ids.
|
||||
ast_manager m; // internal manager
|
||||
params_ref m_params;
|
||||
formula_compiler m_compiler;
|
||||
|
||||
// Set of asserted expressions.
|
||||
// This assertion set belongs to ast_manager m.
|
||||
assertion_set m_assertions;
|
||||
|
||||
model_ref m_model;
|
||||
model_converter_ref m_mc; // chain of model converters
|
||||
|
||||
atom2bool_var m_atom2bvar;
|
||||
scoped_ptr<sat::solver> m_sat;
|
||||
arith m_arith;
|
||||
bridge m_bridge;
|
||||
|
||||
statistics m_stats;
|
||||
|
||||
volatile bool m_cancel;
|
||||
|
||||
void updt_params_core(params_ref const & p);
|
||||
void assert_expr_core(expr * t, ast_translation & translator);
|
||||
|
||||
void init();
|
||||
void compile();
|
||||
|
||||
public:
|
||||
solver_exp(ast_manager & ext_mng, params_ref const & p);
|
||||
~solver_exp();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
void collect_param_descrs(param_descrs & d);
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
void assert_expr(expr * t);
|
||||
void assert_set(assertion_set const & s);
|
||||
void assert_goal(goal const & g);
|
||||
|
||||
void get_assertions(assertion_set & r);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Search
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
lbool check();
|
||||
void get_model(model_ref & m) const { m = m_model.get(); }
|
||||
void get_model_converter(model_converter_ref & mc);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Pretty Printing
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void display(std::ostream & out) const;
|
||||
void display_state(std::ostream & out) const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Statistics
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
47
src/tactic/arith/dead/smt_solver_types.h
Normal file
47
src/tactic/arith/dead/smt_solver_types.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_solver_types.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Auxiliary definitions for smt::solver class.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
|
||||
--*/
|
||||
#ifndef _SMT_SOLVER_TYPES_H_
|
||||
#define _SMT_SOLVER_TYPES_H_
|
||||
|
||||
#include"assertion_set.h"
|
||||
#include"strategy_exception.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
#include"lbool.h"
|
||||
#include"sat_types.h"
|
||||
|
||||
class ast_translation;
|
||||
|
||||
namespace sat {
|
||||
class solver;
|
||||
};
|
||||
|
||||
namespace smt {
|
||||
class solver_exp;
|
||||
class formula_compiler;
|
||||
typedef unsigned atom_id;
|
||||
typedef unsigned_vector atom_id_vector;
|
||||
const atom_id null_atom_id = sat::null_bool_var;
|
||||
};
|
||||
|
||||
MK_ST_EXCEPTION(smt_solver_exception);
|
||||
|
||||
#endif
|
349
src/tactic/arith/degree_shift_tactic.cpp
Normal file
349
src/tactic/arith/degree_shift_tactic.cpp
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*++
|
||||
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)));
|
||||
}
|
||||
|
31
src/tactic/arith/degree_shift_tactic.h
Normal file
31
src/tactic/arith/degree_shift_tactic.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
degree_shift_tactic.h
|
||||
|
||||
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:
|
||||
|
||||
--*/
|
||||
#ifndef _DEGREE_SHIFT_TACTIC_H_
|
||||
#define _DEGREE_SHIFT_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
429
src/tactic/arith/diff_neq_tactic.cpp
Normal file
429
src/tactic/arith/diff_neq_tactic.cpp
Normal file
|
@ -0,0 +1,429 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
diff_neq_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Solver for integer problems that contains literals of the form
|
||||
k <= x
|
||||
x <= k
|
||||
x - y != k
|
||||
And all variables are bounded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"model.h"
|
||||
|
||||
class diff_neq_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
typedef unsigned var;
|
||||
|
||||
expr_ref_vector m_var2expr;
|
||||
obj_map<expr, var> m_expr2var;
|
||||
|
||||
svector<int> m_lower;
|
||||
svector<int> m_upper;
|
||||
struct diseq {
|
||||
var m_y;
|
||||
int m_k;
|
||||
diseq(var y, int k):m_y(y), m_k(k) {}
|
||||
};
|
||||
typedef svector<diseq> diseqs;
|
||||
vector<diseqs> m_var_diseqs;
|
||||
typedef svector<int> decision_stack;
|
||||
decision_stack m_stack;
|
||||
volatile bool m_cancel;
|
||||
|
||||
bool m_produce_models;
|
||||
rational m_max_k;
|
||||
rational m_max_neg_k;
|
||||
|
||||
unsigned m_num_conflicts;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
u(m),
|
||||
m_var2expr(m) {
|
||||
updt_params(p);
|
||||
m_cancel = false;
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_max_k = rational(p.get_uint(":diff-neq-max-k", 1024));
|
||||
m_max_neg_k = -m_max_k;
|
||||
if (m_max_k >= rational(INT_MAX/2))
|
||||
m_max_k = rational(INT_MAX/2);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
void throw_not_supported() {
|
||||
throw tactic_exception("goal is not diff neq");
|
||||
}
|
||||
|
||||
unsigned num_vars() const {
|
||||
return m_upper.size();
|
||||
}
|
||||
|
||||
var mk_var(expr * t) {
|
||||
SASSERT(is_uninterp_const(t));
|
||||
var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = m_upper.size();
|
||||
m_expr2var.insert(t, x);
|
||||
m_var2expr.push_back(t);
|
||||
m_lower.push_back(INT_MIN); // unknown
|
||||
m_upper.push_back(INT_MAX); // unknown
|
||||
m_var_diseqs.push_back(diseqs());
|
||||
return x;
|
||||
}
|
||||
|
||||
void process_le(expr * lhs, expr * rhs) {
|
||||
if (!u.is_int(lhs))
|
||||
throw_not_supported();
|
||||
rational k;
|
||||
if (is_uninterp_const(lhs) && u.is_numeral(rhs, k) && m_max_neg_k <= k && k <= m_max_k) {
|
||||
var x = mk_var(lhs);
|
||||
int _k = static_cast<int>(k.get_int64());
|
||||
m_upper[x] = _k;
|
||||
|
||||
}
|
||||
else if (is_uninterp_const(rhs) && u.is_numeral(lhs, k) && m_max_neg_k <= k && k <= m_max_k) {
|
||||
var x = mk_var(rhs);
|
||||
int _k = static_cast<int>(k.get_int64());
|
||||
m_lower[x] = _k;
|
||||
}
|
||||
else {
|
||||
throw_not_supported();
|
||||
}
|
||||
}
|
||||
|
||||
// process t1 - t2 != k
|
||||
void process_neq_core(expr * t1, expr * t2, int k) {
|
||||
var x1 = mk_var(t1);
|
||||
var x2 = mk_var(t2);
|
||||
if (x1 == x2)
|
||||
throw_not_supported(); // must simplify first
|
||||
if (x1 < x2) {
|
||||
std::swap(x1, x2);
|
||||
k = -k;
|
||||
}
|
||||
m_var_diseqs[x1].push_back(diseq(x2, k));
|
||||
}
|
||||
|
||||
void process_neq(expr * lhs, expr * rhs) {
|
||||
if (!u.is_int(lhs))
|
||||
throw_not_supported();
|
||||
if (is_uninterp_const(lhs) && is_uninterp_const(rhs)) {
|
||||
process_neq_core(lhs, rhs, 0);
|
||||
return;
|
||||
}
|
||||
if (u.is_numeral(lhs))
|
||||
std::swap(lhs, rhs);
|
||||
rational k;
|
||||
if (!u.is_numeral(rhs, k))
|
||||
throw_not_supported();
|
||||
if (!(m_max_neg_k <= k && k <= m_max_k))
|
||||
throw_not_supported();
|
||||
int _k = static_cast<int>(k.get_int64());
|
||||
expr * t1, * t2, * mt1, * mt2;
|
||||
if (u.is_add(lhs, t1, t2)) {
|
||||
if (is_uninterp_const(t1) && u.is_times_minus_one(t2, mt2) && is_uninterp_const(mt2))
|
||||
process_neq_core(t1, mt2, _k);
|
||||
else if (is_uninterp_const(t2) && u.is_times_minus_one(t1, mt1) && is_uninterp_const(mt1))
|
||||
process_neq_core(t2, mt1, _k);
|
||||
else
|
||||
throw_not_supported();
|
||||
}
|
||||
else {
|
||||
throw_not_supported();
|
||||
}
|
||||
}
|
||||
|
||||
// throws exception if contains unbounded variable
|
||||
void check_unbounded() {
|
||||
unsigned num = num_vars();
|
||||
for (var x = 0; x < num; x++) {
|
||||
if (m_lower[x] == INT_MIN || m_upper[x] == INT_MAX)
|
||||
throw_not_supported();
|
||||
// possible extension: support bound normalization here
|
||||
if (m_lower[x] != 0)
|
||||
throw_not_supported(); // use bound normalizer
|
||||
}
|
||||
}
|
||||
|
||||
void compile(goal const & g) {
|
||||
expr * lhs;
|
||||
expr * rhs;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g.form(i);
|
||||
TRACE("diff_neq_tactic", tout << "processing: " << mk_ismt2_pp(f, m) << "\n";);
|
||||
if (u.is_le(f, lhs, rhs))
|
||||
process_le(lhs, rhs);
|
||||
else if (u.is_ge(f, lhs, rhs))
|
||||
process_le(rhs, lhs);
|
||||
else if (m.is_not(f, f) && m.is_eq(f, lhs, rhs))
|
||||
process_neq(lhs, rhs);
|
||||
else
|
||||
throw_not_supported();
|
||||
}
|
||||
check_unbounded();
|
||||
}
|
||||
|
||||
void display(std::ostream & out) {
|
||||
unsigned num = num_vars();
|
||||
for (var x = 0; x < num; x++) {
|
||||
out << m_lower[x] << " <= " << mk_ismt2_pp(m_var2expr.get(x), m) << " <= " << m_upper[x] << "\n";
|
||||
}
|
||||
for (var x = 0; x < num; x++) {
|
||||
diseqs::iterator it = m_var_diseqs[x].begin();
|
||||
diseqs::iterator end = m_var_diseqs[x].end();
|
||||
for (; it != end; ++it) {
|
||||
out << mk_ismt2_pp(m_var2expr.get(x), m) << " != " << mk_ismt2_pp(m_var2expr.get(it->m_y), m) << " + " << it->m_k << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void display_model(std::ostream & out) {
|
||||
unsigned num = m_stack.size();
|
||||
for (var x = 0; x < num; x++) {
|
||||
out << mk_ismt2_pp(m_var2expr.get(x), m) << " := " << m_stack[x] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
svector<bool> m_forbidden;
|
||||
|
||||
// make sure m_forbidden.size() > max upper bound
|
||||
void init_forbidden() {
|
||||
int max = 0;
|
||||
unsigned num = num_vars();
|
||||
for (var x = 0; x < num; x++) {
|
||||
if (m_upper[x] > max)
|
||||
max = m_upper[x];
|
||||
}
|
||||
m_forbidden.reset();
|
||||
m_forbidden.resize(max+1, false);
|
||||
}
|
||||
|
||||
// Return a value v s.t. v >= starting_at and v <= m_upper[x] and all diseqs in m_var_diseqs[x] are satisfied.
|
||||
// Return -1 if such value does not exist.
|
||||
int choose_value(var x, int starting_at) {
|
||||
int max = starting_at-1;
|
||||
int v = starting_at;
|
||||
int upper = m_upper[x];
|
||||
if (starting_at > upper)
|
||||
return -1;
|
||||
diseqs const & ds = m_var_diseqs[x];
|
||||
diseqs::const_iterator it = ds.begin();
|
||||
diseqs::const_iterator end = ds.end();
|
||||
for (; it != end; ++it) {
|
||||
int bad_v = m_stack[it->m_y] + it->m_k;
|
||||
if (bad_v < v)
|
||||
continue;
|
||||
if (bad_v > upper)
|
||||
continue;
|
||||
if (bad_v == v) {
|
||||
while (true) {
|
||||
v++;
|
||||
if (v > upper)
|
||||
return -1;
|
||||
if (!m_forbidden[v])
|
||||
break;
|
||||
m_forbidden[v] = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
SASSERT(bad_v > v && bad_v <= upper);
|
||||
m_forbidden[bad_v] = true;
|
||||
if (bad_v > max)
|
||||
max = bad_v;
|
||||
}
|
||||
// reset forbidden
|
||||
for (int i = starting_at + 1; i <= max; i++)
|
||||
m_forbidden[i] = false;
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 0; i < m_forbidden.size(); i++) {
|
||||
SASSERT(!m_forbidden[i]);
|
||||
}
|
||||
});
|
||||
return v;
|
||||
}
|
||||
|
||||
bool extend_model(var x) {
|
||||
int v = choose_value(x, 0);
|
||||
if (v == -1)
|
||||
return false;
|
||||
m_stack.push_back(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resolve_conflict() {
|
||||
m_num_conflicts++;
|
||||
while (!m_stack.empty()) {
|
||||
int v = m_stack.back();
|
||||
m_stack.pop_back();
|
||||
var x = m_stack.size();
|
||||
v = choose_value(x, v+1);
|
||||
if (v != -1) {
|
||||
m_stack.push_back(v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool search() {
|
||||
m_num_conflicts = 0;
|
||||
init_forbidden();
|
||||
unsigned nvars = num_vars();
|
||||
while (m_stack.size() < nvars) {
|
||||
if (m_cancel)
|
||||
throw tactic_exception(TACTIC_CANCELED_MSG);
|
||||
TRACE("diff_neq_tactic", display_model(tout););
|
||||
var x = m_stack.size();
|
||||
if (extend_model(x))
|
||||
continue;
|
||||
if (!resolve_conflict())
|
||||
return false;
|
||||
}
|
||||
TRACE("diff_neq_tactic", display_model(tout););
|
||||
return true;
|
||||
}
|
||||
|
||||
model * mk_model() {
|
||||
model * md = alloc(model, m);
|
||||
unsigned num = num_vars();
|
||||
SASSERT(m_stack.size() == num);
|
||||
for (var x = 0; x < num; x++) {
|
||||
func_decl * d = to_app(m_var2expr.get(x))->get_decl();
|
||||
md->register_decl(d, u.mk_numeral(rational(m_stack[x]), true));
|
||||
}
|
||||
return md;
|
||||
}
|
||||
|
||||
virtual 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());
|
||||
m_produce_models = g->models_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("diff-neq", *g);
|
||||
fail_if_proof_generation("diff-neq", g);
|
||||
fail_if_unsat_core_generation("diff-neq", g);
|
||||
if (g->inconsistent()) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
compile(*g);
|
||||
TRACE("diff_neq_tactic", g->display(tout); display(tout););
|
||||
bool r = search();
|
||||
report_tactic_progress(":conflicts", m_num_conflicts);
|
||||
if (r) {
|
||||
if (m_produce_models)
|
||||
mc = model2model_converter(mk_model());
|
||||
g->reset();
|
||||
}
|
||||
else {
|
||||
g->assert_expr(m.mk_false());
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("diff_neq", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
diff_neq_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(diff_neq_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~diff_neq_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":diff-neq-max-k", CPK_UINT, "(default: 1024) maximum variable upper bound for diff neq solver.");
|
||||
}
|
||||
|
||||
virtual void collect_statistics(statistics & st) const {
|
||||
st.update("conflicts", m_imp->m_num_conflicts);
|
||||
}
|
||||
|
||||
virtual void reset_statistics() {
|
||||
m_imp->m_num_conflicts = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Fix a DL variable in s to 0.
|
||||
If s is not really in the difference logic fragment, then this is a NOOP.
|
||||
*/
|
||||
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() {
|
||||
unsigned num_conflicts = m_imp->m_num_conflicts;
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
m_imp->m_num_conflicts = num_conflicts;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(diff_neq_tactic, m, p));
|
||||
}
|
||||
|
||||
|
||||
|
32
src/tactic/arith/diff_neq_tactic.h
Normal file
32
src/tactic/arith/diff_neq_tactic.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
diff_neq_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Solver for integer problems that contains literals of the form
|
||||
k <= x
|
||||
x <= k
|
||||
x - y != k
|
||||
And all variables are bounded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DIFF_NEQ_TACTIC_H_
|
||||
#define _DIFF_NEQ_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
361
src/tactic/arith/factor_tactic.cpp
Normal file
361
src/tactic/arith/factor_tactic.cpp
Normal file
|
@ -0,0 +1,361 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
factor_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Polynomial factorization tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-03
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"expr2polynomial.h"
|
||||
#include"rewriter_def.h"
|
||||
|
||||
class factor_tactic : public tactic {
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
ast_manager & m;
|
||||
arith_util m_util;
|
||||
unsynch_mpq_manager m_qm;
|
||||
polynomial::manager m_pm;
|
||||
default_expr2polynomial m_expr2poly;
|
||||
polynomial::factor_params m_fparams;
|
||||
bool m_split_factors;
|
||||
|
||||
rw_cfg(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_util(_m),
|
||||
m_pm(m_qm),
|
||||
m_expr2poly(m, m_pm) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_split_factors = p.get_bool(":split-factors", true);
|
||||
m_fparams.updt_params(p);
|
||||
}
|
||||
|
||||
expr * mk_mul(unsigned sz, expr * const * args) {
|
||||
SASSERT(sz > 0);
|
||||
if (sz == 1)
|
||||
return args[0];
|
||||
return m_util.mk_mul(sz, args);
|
||||
}
|
||||
|
||||
expr * mk_zero_for(expr * arg) {
|
||||
return m_util.mk_numeral(rational(0), m_util.is_int(arg));
|
||||
}
|
||||
|
||||
// p1^k1 * p2^k2 = 0 --> p1*p2 = 0
|
||||
void mk_eq(polynomial::factors const & fs, expr_ref & result) {
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
args.push_back(arg);
|
||||
}
|
||||
result = m.mk_eq(mk_mul(args.size(), args.c_ptr()), mk_zero_for(arg));
|
||||
}
|
||||
|
||||
// p1^k1 * p2^k2 = 0 --> p1 = 0 or p2 = 0
|
||||
void mk_split_eq(polynomial::factors const & fs, expr_ref & result) {
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
args.push_back(m.mk_eq(arg, mk_zero_for(arg)));
|
||||
}
|
||||
if (args.size() == 1)
|
||||
result = args[0];
|
||||
else
|
||||
result = m.mk_or(args.size(), args.c_ptr());
|
||||
}
|
||||
|
||||
decl_kind flip(decl_kind k) {
|
||||
SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE);
|
||||
switch (k) {
|
||||
case OP_LT: return OP_GT;
|
||||
case OP_LE: return OP_GE;
|
||||
case OP_GT: return OP_LT;
|
||||
case OP_GE: return OP_LE;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
// p1^{2*k1} * p2^{2*k2 + 1} >=< 0
|
||||
// -->
|
||||
// (p1^2)*p2 >=<0
|
||||
void mk_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) {
|
||||
SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE);
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
if (fs.get_degree(i) % 2 == 0)
|
||||
arg = m_util.mk_power(arg, m_util.mk_numeral(rational(2), m_util.is_int(arg)));
|
||||
args.push_back(arg);
|
||||
}
|
||||
expr * lhs = mk_mul(args.size(), args.c_ptr());
|
||||
result = m.mk_app(m_util.get_family_id(), k, lhs, mk_zero_for(lhs));
|
||||
}
|
||||
|
||||
// See mk_split_strict_comp and mk_split_nonstrict_comp
|
||||
void split_even_odd(bool strict, polynomial::factors const & fs, expr_ref_buffer & even_eqs, expr_ref_buffer & odd_factors) {
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
if (fs.get_degree(i) % 2 == 0) {
|
||||
expr * eq = m.mk_eq(arg, mk_zero_for(arg));
|
||||
if (strict)
|
||||
even_eqs.push_back(m.mk_not(eq));
|
||||
else
|
||||
even_eqs.push_back(eq);
|
||||
}
|
||||
else {
|
||||
odd_factors.push_back(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strict case
|
||||
// p1^{2*k1} * p2^{2*k2 + 1} >< 0
|
||||
// -->
|
||||
// p1 != 0 and p2 >< 0
|
||||
//
|
||||
// Nonstrict
|
||||
// p1^{2*k1} * p2^{2*k2 + 1} >=< 0
|
||||
// -->
|
||||
// p1 = 0 or p2 >=< 0
|
||||
//
|
||||
void mk_split_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) {
|
||||
SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE);
|
||||
bool strict = (k == OP_LT) || (k == OP_GT);
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref_buffer odd_factors(m);
|
||||
split_even_odd(strict, fs, args, odd_factors);
|
||||
if (odd_factors.empty()) {
|
||||
if (k == OP_LT) {
|
||||
result = m.mk_false();
|
||||
return;
|
||||
}
|
||||
if (k == OP_GE) {
|
||||
result = m.mk_true();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
args.push_back(m.mk_app(m_util.get_family_id(), k, mk_mul(odd_factors.size(), odd_factors.c_ptr()), mk_zero_for(odd_factors[0])));
|
||||
}
|
||||
SASSERT(!args.empty());
|
||||
if (args.size() == 1)
|
||||
result = args[0];
|
||||
else if (strict)
|
||||
result = m.mk_and(args.size(), args.c_ptr());
|
||||
else
|
||||
result = m.mk_or(args.size(), args.c_ptr());
|
||||
}
|
||||
|
||||
br_status factor(func_decl * f, expr * lhs, expr * rhs, expr_ref & result) {
|
||||
polynomial_ref p1(m_pm);
|
||||
polynomial_ref p2(m_pm);
|
||||
scoped_mpz d1(m_qm);
|
||||
scoped_mpz d2(m_qm);
|
||||
m_expr2poly.to_polynomial(lhs, p1, d1);
|
||||
m_expr2poly.to_polynomial(rhs, p2, d2);
|
||||
TRACE("factor_tactic_bug",
|
||||
tout << "lhs: " << mk_ismt2_pp(lhs, m) << "\n";
|
||||
tout << "p1: " << p1 << "\n";
|
||||
tout << "d1: " << d1 << "\n";
|
||||
tout << "rhs: " << mk_ismt2_pp(rhs, m) << "\n";
|
||||
tout << "p2: " << p2 << "\n";
|
||||
tout << "d2: " << d2 << "\n";);
|
||||
scoped_mpz lcm(m_qm);
|
||||
m_qm.lcm(d1, d2, lcm);
|
||||
m_qm.div(lcm, d1, d1);
|
||||
m_qm.div(lcm, d2, d2);
|
||||
m_qm.neg(d2);
|
||||
polynomial_ref p(m_pm);
|
||||
p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2);
|
||||
if (is_const(p))
|
||||
return BR_FAILED;
|
||||
polynomial::factors fs(m_pm);
|
||||
TRACE("factor_tactic_bug", tout << "p: " << p << "\n";);
|
||||
m_pm.factor(p, fs, m_fparams);
|
||||
SASSERT(fs.distinct_factors() > 0);
|
||||
TRACE("factor_tactic_bug", tout << "factors:\n"; fs.display(tout); tout << "\n";);
|
||||
if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1)
|
||||
return BR_FAILED;
|
||||
if (m.is_eq(f)) {
|
||||
if (m_split_factors)
|
||||
mk_split_eq(fs, result);
|
||||
else
|
||||
mk_eq(fs, result);
|
||||
}
|
||||
else {
|
||||
decl_kind k = f->get_decl_kind();
|
||||
if (m_qm.is_neg(fs.get_constant()))
|
||||
k = flip(k);
|
||||
|
||||
if (m_split_factors)
|
||||
mk_split_comp(k, fs, result);
|
||||
else
|
||||
mk_comp(k, fs, result);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
if (num != 2)
|
||||
return BR_FAILED;
|
||||
if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1])))
|
||||
return factor(f, args[0], args[1], result);
|
||||
if (f->get_family_id() != m_util.get_family_id())
|
||||
return BR_FAILED;
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_LT:
|
||||
case OP_GT:
|
||||
case OP_LE:
|
||||
case OP_GE:
|
||||
return factor(f, args[0], args[1], result);
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
};
|
||||
|
||||
struct rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
|
||||
rw(ast_manager & m, params_ref const & p):
|
||||
rewriter_tpl<rw_cfg>(m, m.proofs_enabled(), m_cfg),
|
||||
m_cfg(m, p) {
|
||||
}
|
||||
};
|
||||
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
rw m_rw;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_rw(m, p) {
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
m_rw.cfg().m_pm.set_cancel(f);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.cfg().updt_params(p);
|
||||
}
|
||||
|
||||
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;
|
||||
tactic_report report("factor", *g);
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = g->form(idx);
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (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));
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("factor", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
factor_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(factor_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~factor_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->m_rw.cfg().updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":split-factors", CPK_BOOL,
|
||||
"(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0)).");
|
||||
polynomial::factor_params::get_param_descrs(r);
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
try {
|
||||
(*m_imp)(in, result, mc, pc, core);
|
||||
}
|
||||
catch (z3_error & ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (z3_exception & ex) {
|
||||
throw tactic_exception(ex.msg());
|
||||
}
|
||||
}
|
||||
|
||||
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, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_factor_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(factor_tactic, m, p));
|
||||
}
|
||||
|
||||
|
||||
|
28
src/tactic/arith/factor_tactic.h
Normal file
28
src/tactic/arith/factor_tactic.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
factor_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Polynomial factorization tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-03
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _FACTOR_TACTIC_H_
|
||||
#define _FACTOR_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_factor_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
358
src/tactic/arith/fix_dl_var_tactic.cpp
Normal file
358
src/tactic/arith/fix_dl_var_tactic.cpp
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
fix_dl_var_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Fix a difference logic variable to 0.
|
||||
If the problem is in the difference logic fragment, that is, all arithmetic terms
|
||||
are of the form (x + k), and the arithmetic atoms are of the
|
||||
form x - y <= k or x - y = k. Then, we can set one variable to 0.
|
||||
|
||||
This is useful because, many bounds can be exposed after this operation is performed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-19
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class fix_dl_var_tactic : public tactic {
|
||||
|
||||
struct is_target {
|
||||
struct failed {};
|
||||
ast_manager & m;
|
||||
arith_util & m_util;
|
||||
expr_fast_mark1 * m_visited;
|
||||
ptr_vector<expr> m_todo;
|
||||
obj_map<app, unsigned> m_occs;
|
||||
obj_map<app, unsigned> m_non_nested_occs;
|
||||
|
||||
is_target(arith_util & u):
|
||||
m(u.get_manager()),
|
||||
m_util(u) {
|
||||
}
|
||||
|
||||
void throw_failed(expr * ctx1, expr * ctx2 = 0) {
|
||||
TRACE("fix_dl_var", tout << mk_ismt2_pp(ctx1, m) << "\n"; if (ctx2) tout << mk_ismt2_pp(ctx2, m) << "\n";);
|
||||
throw failed();
|
||||
}
|
||||
|
||||
bool is_arith(expr * n) {
|
||||
sort * s = m.get_sort(n);
|
||||
return s->get_family_id() == m_util.get_family_id();
|
||||
}
|
||||
// Return true if n is uninterpreted with respect to arithmetic.
|
||||
bool is_uninterp(expr * n) {
|
||||
return is_app(n) && to_app(n)->get_family_id() != m_util.get_family_id();
|
||||
}
|
||||
|
||||
// Remark: we say an expression is nested, if it occurs inside the boolean structure of the formula.
|
||||
// That is, the expression is not part of an unit clause comprising of a single inequality/equality.
|
||||
|
||||
void inc_occ(expr * n, bool nested) {
|
||||
if (is_uninterp_const(n) && is_arith(n)) {
|
||||
obj_map<app, unsigned>::obj_map_entry * entry = m_occs.insert_if_not_there2(to_app(n), 0);
|
||||
entry->get_data().m_value++;
|
||||
|
||||
if (!nested) {
|
||||
entry = m_non_nested_occs.insert_if_not_there2(to_app(n), 0);
|
||||
entry->get_data().m_value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(expr * n, bool nested) {
|
||||
inc_occ(n, nested);
|
||||
if (!m_visited->is_marked(n)) {
|
||||
m_visited->mark(n);
|
||||
m_todo.push_back(n);
|
||||
}
|
||||
}
|
||||
|
||||
void process_app(app * t) {
|
||||
unsigned num = t->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
visit(t->get_arg(i), false);
|
||||
}
|
||||
|
||||
void process_arith_atom(expr * lhs, expr * rhs, bool nested) {
|
||||
if (is_uninterp(lhs) && is_uninterp(rhs)) {
|
||||
visit(lhs, nested);
|
||||
visit(rhs, nested);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_util.is_numeral(lhs))
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
if (!m_util.is_numeral(rhs))
|
||||
throw_failed(lhs, rhs);
|
||||
|
||||
expr * t, * ms, * s;
|
||||
// check if lhs is of the form: (+ t (* (- 1) s))
|
||||
if (m_util.is_add(lhs, t, ms) && m_util.is_times_minus_one(ms, s) && is_uninterp(t) && is_uninterp(s)) {
|
||||
visit(t, nested);
|
||||
visit(s, nested);
|
||||
}
|
||||
else {
|
||||
CTRACE("fix_dl_var", m_util.is_add(lhs, t, ms),
|
||||
s = 0;
|
||||
tout << "is_times_minus_one: " << m_util.is_times_minus_one(ms, s) << "\n";
|
||||
tout << "is_uninterp(t): " << is_uninterp(t) << "\n";
|
||||
tout << "t.family_id(): " << (is_app(t) ? to_app(t)->get_family_id() : -1) << "\n";
|
||||
tout << "util.family_id: " << m_util.get_family_id() << "\n";
|
||||
if (s) {
|
||||
tout << "is_uninterp(s): " << is_uninterp(s) << "\n";
|
||||
tout << "s.family_id(): " << (is_app(s) ? to_app(s)->get_family_id() : -1) << "\n";
|
||||
});
|
||||
throw_failed(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void process_eq(app * t, bool nested) {
|
||||
if (!is_arith(t->get_arg(0))) {
|
||||
process_app(t);
|
||||
return;
|
||||
}
|
||||
process_arith_atom(t->get_arg(0), t->get_arg(1), nested);
|
||||
}
|
||||
|
||||
void process_arith(app * t, bool nested) {
|
||||
if (m.is_bool(t)) {
|
||||
process_arith_atom(t->get_arg(0), t->get_arg(1), nested);
|
||||
return;
|
||||
}
|
||||
// check if t is of the form c + k
|
||||
expr * c, * k;
|
||||
if (m_util.is_add(t, k, c) && is_uninterp(c) && m_util.is_numeral(k)) {
|
||||
visit(c, nested);
|
||||
}
|
||||
else {
|
||||
throw_failed(t);
|
||||
}
|
||||
}
|
||||
|
||||
void process(expr * n) {
|
||||
if (m_visited->is_marked(n))
|
||||
return;
|
||||
|
||||
while (m.is_not(n, n))
|
||||
;
|
||||
|
||||
if (is_app(n) && to_app(n)->get_family_id() == m_util.get_family_id()) {
|
||||
process_arith(to_app(n), false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_todo.push_back(n);
|
||||
m_visited->mark(n);
|
||||
|
||||
while (!m_todo.empty()) {
|
||||
expr * n = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
|
||||
if (!is_app(n))
|
||||
throw_failed(n);
|
||||
|
||||
app * t = to_app(n);
|
||||
|
||||
if (m.is_eq(t))
|
||||
process_eq(t, true);
|
||||
else if (t->get_family_id() == m_util.get_family_id())
|
||||
process_arith(t, true);
|
||||
else
|
||||
process_app(t);
|
||||
}
|
||||
}
|
||||
|
||||
app * most_occs(obj_map<app, unsigned> & occs, unsigned & best) {
|
||||
app * r = 0;
|
||||
best = 0;
|
||||
obj_map<app, unsigned>::iterator it = occs.begin();
|
||||
obj_map<app, unsigned>::iterator end = occs.end();
|
||||
for (; it != end; ++it) {
|
||||
if (it->m_value > best) {
|
||||
best = it->m_value;
|
||||
r = it->m_key;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// TODO make it a parameter
|
||||
#define NESTED_PENALTY 10
|
||||
|
||||
app * most_occs() {
|
||||
// We try to choose a variable that when set to 0 will generate many bounded variables.
|
||||
// That is why we give preference to variables occuring in non-nested inequalities.
|
||||
unsigned best1, best2;
|
||||
app * r1, * r2;
|
||||
r1 = most_occs(m_non_nested_occs, best1);
|
||||
r2 = most_occs(m_occs, best2);
|
||||
TRACE("fix_dl_var_choice",
|
||||
if (r1) {
|
||||
tout << "r1 occs: " << best1 << "\n";
|
||||
tout << mk_ismt2_pp(r1, m) << "\n";
|
||||
}
|
||||
if (r2) {
|
||||
tout << "r2 occs: " << best2 << "\n";
|
||||
tout << mk_ismt2_pp(r2, m) << "\n";
|
||||
});
|
||||
if (best2 > NESTED_PENALTY * best1)
|
||||
return r2;
|
||||
else
|
||||
return r1;
|
||||
}
|
||||
|
||||
app * operator()(goal const & g) {
|
||||
try {
|
||||
expr_fast_mark1 visited;
|
||||
m_visited = &visited;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
process(g.form(i));
|
||||
}
|
||||
return most_occs();
|
||||
}
|
||||
catch (failed) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
th_rewriter m_rw;
|
||||
bool m_produce_models;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
u(m),
|
||||
m_rw(m, p) {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
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;
|
||||
tactic_report report("fix-dl-var", *g);
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
m_produce_models = g->models_enabled();
|
||||
|
||||
app * var = is_target(u)(*g);
|
||||
if (var != 0) {
|
||||
IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(fixing-at-zero " << var->get_decl()->get_name() << ")\n";);
|
||||
tactic_report report("fix-dl-var", *g);
|
||||
|
||||
expr_substitution subst(m);
|
||||
app * zero = u.mk_numeral(rational(0), u.is_int(var));
|
||||
subst.insert(var, zero);
|
||||
m_rw.set_substitution(&subst);
|
||||
|
||||
if (m_produce_models) {
|
||||
extension_model_converter * _mc = alloc(extension_model_converter, m);
|
||||
_mc->insert(var->get_decl(), zero);
|
||||
mc = _mc;
|
||||
}
|
||||
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = g->form(idx);
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (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));
|
||||
}
|
||||
g->inc_depth();
|
||||
}
|
||||
result.push_back(g.get());
|
||||
TRACE("fix_dl_var", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
fix_dl_var_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(fix_dl_var_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~fix_dl_var_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
th_rewriter::get_param_descrs(r);
|
||||
}
|
||||
|
||||
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, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(fix_dl_var_tactic, m, p));
|
||||
}
|
33
src/tactic/arith/fix_dl_var_tactic.h
Normal file
33
src/tactic/arith/fix_dl_var_tactic.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
fix_dl_var_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Fix a difference logic variable to 0.
|
||||
If the problem is in the difference logic fragment, that is, all arithmetic terms
|
||||
are of the form (x + k), and the arithmetic atoms are of the
|
||||
form x - y <= k or x - y = k. Then, we can set one variable to 0.
|
||||
|
||||
This is useful because, many bounds can be exposed after this operation is performed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-12-29
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _FIX_DL_VAR_TACTIC_H_
|
||||
#define _FIX_DL_VAR_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
1715
src/tactic/arith/fm_tactic.cpp
Normal file
1715
src/tactic/arith/fm_tactic.cpp
Normal file
File diff suppressed because it is too large
Load diff
33
src/tactic/arith/fm_tactic.h
Normal file
33
src/tactic/arith/fm_tactic.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
fm_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Use Fourier-Motzkin to eliminate variables.
|
||||
This strategy can handle conditional bounds
|
||||
(i.e., clauses with at most one constraint).
|
||||
|
||||
The strategy mk_occf can be used to put the
|
||||
formula in OCC form.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-04.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _FM_TACTIC_H_
|
||||
#define _FM_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_fm_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
366
src/tactic/arith/lia2pb_tactic.cpp
Normal file
366
src/tactic/arith/lia2pb_tactic.cpp
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
lia2pb_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Reduce bounded LIA benchmark into 0-1 LIA benchmark.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"bound_manager.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"for_each_expr.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class lia2pb_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
bound_manager m_bm;
|
||||
arith_util m_util;
|
||||
expr_dependency_ref_vector m_new_deps;
|
||||
th_rewriter m_rw;
|
||||
bool m_produce_models;
|
||||
bool m_produce_unsat_cores;
|
||||
bool m_partial_lia2pb;
|
||||
unsigned m_max_bits;
|
||||
unsigned m_total_bits;
|
||||
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_bm(m),
|
||||
m_util(m),
|
||||
m_new_deps(m),
|
||||
m_rw(m, p) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
m_partial_lia2pb = p.get_bool(":lia2pb-partial", false);
|
||||
m_max_bits = p.get_uint(":lia2pb-max-bits", 32);
|
||||
m_total_bits = p.get_uint(":lia2pb-total-bits", 2048);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
bool is_target_core(expr * n, rational & u) {
|
||||
if (!is_uninterp_const(n))
|
||||
return false;
|
||||
rational l; bool s;
|
||||
if (m_bm.has_lower(n, l, s) &&
|
||||
m_bm.has_upper(n, u, s) &&
|
||||
l.is_zero() &&
|
||||
u.get_num_bits() <= m_max_bits) {
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_bounded(expr * n) {
|
||||
rational u;
|
||||
return is_target_core(n, u);
|
||||
}
|
||||
|
||||
bool is_target(expr * n) {
|
||||
rational u;
|
||||
return is_target_core(n, u) && u > rational(1);
|
||||
}
|
||||
|
||||
struct failed {};
|
||||
|
||||
struct visitor {
|
||||
imp & m_owner;
|
||||
|
||||
visitor(imp & o):m_owner(o) {}
|
||||
|
||||
void throw_failed(expr * n) {
|
||||
TRACE("lia2pb", tout << "Failed at:\n" << mk_ismt2_pp(n, m_owner.m) << "\n";);
|
||||
throw failed();
|
||||
}
|
||||
|
||||
void operator()(var * n) {
|
||||
throw_failed(n);
|
||||
}
|
||||
|
||||
void operator()(app * n) {
|
||||
family_id fid = n->get_family_id();
|
||||
if (fid == m_owner.m.get_basic_family_id()) {
|
||||
// all basic family ops are OK
|
||||
}
|
||||
else if (fid == m_owner.m_util.get_family_id()) {
|
||||
// check if linear
|
||||
switch (n->get_decl_kind()) {
|
||||
case OP_LE: case OP_GE: case OP_LT: case OP_GT:
|
||||
case OP_ADD: case OP_NUM:
|
||||
return;
|
||||
case OP_MUL:
|
||||
if (n->get_num_args() != 2)
|
||||
throw_failed(n);
|
||||
if (!m_owner.m_util.is_numeral(n->get_arg(0)))
|
||||
throw_failed(n);
|
||||
return;
|
||||
default:
|
||||
throw_failed(n);
|
||||
}
|
||||
}
|
||||
else if (is_uninterp_const(n)) {
|
||||
if (m_owner.m_util.is_real(n)) {
|
||||
if (!m_owner.m_partial_lia2pb)
|
||||
throw_failed(n);
|
||||
}
|
||||
else if (m_owner.m_util.is_int(n)) {
|
||||
if (!m_owner.m_partial_lia2pb && !m_owner.is_bounded(n))
|
||||
throw_failed(n);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sort * s = m_owner.m.get_sort(n);
|
||||
if (s->get_family_id() == m_owner.m_util.get_family_id())
|
||||
throw_failed(n);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(quantifier * n) {
|
||||
throw_failed(n);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool check(goal const & g) {
|
||||
try {
|
||||
expr_fast_mark1 visited;
|
||||
visitor proc(*this);
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g.form(i);
|
||||
for_each_expr_core<visitor, expr_fast_mark1, true, true>(proc, visited, f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (failed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_target() {
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
if (is_target(*it))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_num_bits() {
|
||||
unsigned num_bits = 0;
|
||||
rational u;
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
expr * x = *it;
|
||||
if (is_target_core(x, u) && u > rational(1)) {
|
||||
num_bits += u.get_num_bits();
|
||||
if (num_bits > m_total_bits)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual 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());
|
||||
fail_if_proof_generation("lia2pb", g);
|
||||
m_produce_models = g->models_enabled();
|
||||
m_produce_unsat_cores = g->unsat_core_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("lia2pb", *g);
|
||||
m_bm.reset(); m_rw.reset(); m_new_deps.reset();
|
||||
|
||||
if (g->inconsistent()) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
m_bm(*g);
|
||||
|
||||
TRACE("lia2pb", m_bm.display(tout););
|
||||
|
||||
// check if there is some variable to be converted
|
||||
if (!has_target()) {
|
||||
// nothing to be done
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!check(*g))
|
||||
throw tactic_exception("goal is in a fragment unsupported by lia2pb");
|
||||
|
||||
if (!check_num_bits())
|
||||
throw tactic_exception("lia2pb failed, number of necessary bits exceeds specified threshold (use option :lia2pb-total-bits to increase threshold)");
|
||||
|
||||
extension_model_converter * mc1 = 0;
|
||||
filter_model_converter * mc2 = 0;
|
||||
if (m_produce_models) {
|
||||
mc1 = alloc(extension_model_converter, m);
|
||||
mc2 = alloc(filter_model_converter, m);
|
||||
mc = concat(mc2, mc1);
|
||||
}
|
||||
|
||||
expr_ref zero(m);
|
||||
expr_ref one(m);
|
||||
zero = m_util.mk_numeral(rational(0), true);
|
||||
one = m_util.mk_numeral(rational(1), true);
|
||||
|
||||
unsigned num_converted = 0;
|
||||
expr_substitution subst(m, m_produce_unsat_cores, false);
|
||||
rational u;
|
||||
ptr_buffer<expr> def_args;
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
expr * x = *it;
|
||||
if (is_target_core(x, u) && u > rational(1)) {
|
||||
num_converted++;
|
||||
def_args.reset();
|
||||
rational a(1);
|
||||
unsigned num_bits = u.get_num_bits();
|
||||
for (unsigned i = 0; i < num_bits; i++) {
|
||||
app * x_prime = m.mk_fresh_const(0, m_util.mk_int());
|
||||
g->assert_expr(m_util.mk_le(zero, x_prime));
|
||||
g->assert_expr(m_util.mk_le(x_prime, one));
|
||||
if (a.is_one())
|
||||
def_args.push_back(x_prime);
|
||||
else
|
||||
def_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), x_prime));
|
||||
if (m_produce_models)
|
||||
mc2->insert(x_prime->get_decl());
|
||||
a *= rational(2);
|
||||
}
|
||||
SASSERT(def_args.size() > 1);
|
||||
expr * def = m_util.mk_add(def_args.size(), def_args.c_ptr());
|
||||
expr_dependency * dep = 0;
|
||||
if (m_produce_unsat_cores) {
|
||||
dep = m.mk_join(m_bm.lower_dep(x), m_bm.upper_dep(x));
|
||||
if (dep != 0)
|
||||
m_new_deps.push_back(dep);
|
||||
}
|
||||
TRACE("lia2pb", tout << mk_ismt2_pp(x, m) << " -> " << dep << "\n";);
|
||||
subst.insert(x, def, 0, dep);
|
||||
if (m_produce_models)
|
||||
mc1->insert(to_app(x)->get_decl(), def);
|
||||
}
|
||||
}
|
||||
|
||||
report_tactic_progress(":converted-lia2pb", num_converted);
|
||||
|
||||
m_rw.set_substitution(&subst);
|
||||
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = g->form(idx);
|
||||
expr_dependency * dep = 0;
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (m_produce_unsat_cores) {
|
||||
dep = m.mk_join(m_rw.get_used_dependencies(), g->dep(idx));
|
||||
m_rw.reset_used_dependencies();
|
||||
}
|
||||
g->update(idx, new_curr, 0, dep);
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("lia2pb", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
lia2pb_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(lia2pb_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~lia2pb_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":lia2pb-partial", CPK_BOOL, "(default: false) partial lia2pb conversion.");
|
||||
r.insert(":lia2pb-max-bits", CPK_UINT, "(default: 32) maximum number of bits to be used (per variable) in lia2pb.");
|
||||
r.insert(":lia2pb-total-bits", CPK_UINT, "(default: 2048) total number of bits to be used (per problem) in lia2pb.");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#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_lia2pb_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(lia2pb_tactic, m, p));
|
||||
}
|
||||
|
28
src/tactic/arith/lia2pb_tactic.h
Normal file
28
src/tactic/arith/lia2pb_tactic.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
lia2pb_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Reduce bounded LIA benchmark into 0-1 LIA benchmark.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _LIA2PB_TACTIC_H_
|
||||
#define _LIA2PB_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_lia2pb_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
279
src/tactic/arith/linear_equation.cpp
Normal file
279
src/tactic/arith/linear_equation.cpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"linear_equation.h"
|
||||
|
||||
/**
|
||||
\brief Return the position of variable x_i in the linear equation.
|
||||
Return UINT_MAX, if the variable is not in the linear_equation.
|
||||
*/
|
||||
unsigned linear_equation::pos(unsigned x_i) const {
|
||||
int low = 0;
|
||||
int high = m_size - 1;
|
||||
while (true) {
|
||||
int mid = low + ((high - low) / 2);
|
||||
var x_mid = m_xs[mid];
|
||||
if (x_i > x_mid) {
|
||||
low = mid + 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else if (x_i < x_mid) {
|
||||
high = mid - 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void linear_equation_manager::display(std::ostream & out, linear_equation const & eq) const {
|
||||
unsigned sz = eq.m_size;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (i > 0)
|
||||
out << " + ";
|
||||
out << m.to_string(eq.m_as[i]) << "*x" << eq.m_xs[i];
|
||||
}
|
||||
out << " = 0";
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpq * as, var * xs, bool normalized) {
|
||||
SASSERT(sz > 1);
|
||||
|
||||
// compute lcm of the denominators
|
||||
mpz l;
|
||||
mpz r;
|
||||
m.set(l, as[0].denominator());
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
m.set(r, as[i].denominator());
|
||||
m.lcm(r, l, l);
|
||||
}
|
||||
|
||||
TRACE("linear_equation_mk", tout << "lcm: " << m.to_string(l) << "\n";);
|
||||
|
||||
// copy l * as to m_int_buffer.
|
||||
m_int_buffer.reset();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
TRACE("linear_equation_mk", tout << "before as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
m.mul(l, as[i], as[i]);
|
||||
TRACE("linear_equation_mk", tout << "after as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
SASSERT(m.is_int(as[i]));
|
||||
m_int_buffer.push_back(as[i].numerator());
|
||||
}
|
||||
|
||||
linear_equation * new_eq = mk(sz, m_int_buffer.c_ptr(), xs, normalized);
|
||||
|
||||
m.del(r);
|
||||
m.del(l);
|
||||
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk_core(unsigned sz, mpz * as, var * xs) {
|
||||
SASSERT(sz > 0);
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
SASSERT(xs[i-1] < xs[i]);
|
||||
}
|
||||
});
|
||||
|
||||
TRACE("linear_equation_bug", for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
mpz g;
|
||||
m.set(g, as[0]);
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
if (m.is_one(g))
|
||||
break;
|
||||
if (m.is_neg(as[i])) {
|
||||
m.neg(as[i]);
|
||||
m.gcd(g, as[i], g);
|
||||
m.neg(as[i]);
|
||||
}
|
||||
else {
|
||||
m.gcd(g, as[i], g);
|
||||
}
|
||||
}
|
||||
if (!m.is_one(g)) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m.div(as[i], g, as[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("linear_equation_bug",
|
||||
tout << "g: " << m.to_string(g) << "\n";
|
||||
for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
m.del(g);
|
||||
|
||||
unsigned obj_sz = linear_equation::get_obj_size(sz);
|
||||
void * mem = m_allocator.allocate(obj_sz);
|
||||
linear_equation * new_eq = new (mem) linear_equation();
|
||||
mpz * new_as = reinterpret_cast<mpz*>(reinterpret_cast<char*>(new_eq) + sizeof(linear_equation));
|
||||
double * new_app_as = reinterpret_cast<double*>(reinterpret_cast<char*>(new_as) + sz * sizeof(mpz));
|
||||
var * new_xs = reinterpret_cast<var *>(reinterpret_cast<char*>(new_app_as) + sz * sizeof(double));
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
new (new_as + i) mpz();
|
||||
m.set(new_as[i], as[i]);
|
||||
new_app_as[i] = m.get_double(as[i]);
|
||||
var x_i = xs[i];
|
||||
new_xs[i] = x_i;
|
||||
}
|
||||
new_eq->m_size = sz;
|
||||
new_eq->m_as = new_as;
|
||||
new_eq->m_approx_as = new_app_as;
|
||||
new_eq->m_xs = new_xs;
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpz * as, var * xs, bool normalized) {
|
||||
if (!normalized) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
m_val_buffer.reserve(x+1);
|
||||
|
||||
if (m_mark[x]) {
|
||||
m.add(m_val_buffer[x], as[i], m_val_buffer[x]);
|
||||
}
|
||||
else {
|
||||
m.set(m_val_buffer[x], as[i]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
if (m_mark[x]) {
|
||||
if (!m.is_zero(m_val_buffer[x])) {
|
||||
xs[j] = xs[i];
|
||||
m.set(as[j], m_val_buffer[x]);
|
||||
j++;
|
||||
}
|
||||
m_mark[x] = false;
|
||||
}
|
||||
}
|
||||
sz = j;
|
||||
if (sz <= 1)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
SASSERT(!m_mark[x]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark[x] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_val_buffer.reserve(x+1);
|
||||
m.swap(m_val_buffer[x], as[i]);
|
||||
}
|
||||
std::sort(xs, xs+sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m.swap(as[i], m_val_buffer[x]);
|
||||
}
|
||||
|
||||
return mk_core(sz, as, xs);
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2) {
|
||||
SASSERT(!m.is_zero(b1));
|
||||
SASSERT(!m.is_zero(b2));
|
||||
mpz tmp, new_a;
|
||||
m_int_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
unsigned sz1 = eq1.size();
|
||||
unsigned sz2 = eq2.size();
|
||||
unsigned i1 = 0;
|
||||
unsigned i2 = 0;
|
||||
while (true) {
|
||||
if (i1 == sz1) {
|
||||
// copy remaining entries from eq2
|
||||
while (i2 < sz2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i2 == sz2) {
|
||||
// copy remaining entries from eq1
|
||||
while (i1 < sz1) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
var x1 = eq1.x(i1);
|
||||
var x2 = eq2.x(i2);
|
||||
if (x1 < x2) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
else if (x1 > x2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
else {
|
||||
m.mul(eq1.a(i1), b1, tmp);
|
||||
m.addmul(tmp, b2, eq2.a(i2), new_a);
|
||||
if (!m.is_zero(new_a)) {
|
||||
m_int_buffer.push_back(new_a);
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
}
|
||||
i1++;
|
||||
i2++;
|
||||
}
|
||||
}
|
||||
m.del(tmp);
|
||||
m.del(new_a);
|
||||
SASSERT(m_int_buffer.size() == m_var_buffer.size());
|
||||
if (m_int_buffer.empty())
|
||||
return 0;
|
||||
return mk_core(m_int_buffer.size(), m_int_buffer.c_ptr(), m_var_buffer.c_ptr());
|
||||
}
|
||||
|
||||
void linear_equation_manager::del(linear_equation * eq) {
|
||||
for (unsigned i = 0; i < eq->m_size; i++) {
|
||||
m.del(eq->m_as[i]);
|
||||
}
|
||||
unsigned obj_sz = linear_equation::get_obj_size(eq->m_size);
|
||||
m_allocator.deallocate(obj_sz, eq);
|
||||
}
|
||||
|
85
src/tactic/arith/linear_equation.h
Normal file
85
src/tactic/arith/linear_equation.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _LINEAR_EQUATION_H_
|
||||
#define _LINEAR_EQUATION_H_
|
||||
|
||||
#include"mpq.h"
|
||||
#include"small_object_allocator.h"
|
||||
#include"numeral_buffer.h"
|
||||
#include"double_manager.h"
|
||||
|
||||
class linear_equation {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
private:
|
||||
static unsigned get_obj_size(unsigned sz) { return sizeof(linear_equation) + sz * (sizeof(mpz) + sizeof(double) + sizeof(var)); }
|
||||
friend class linear_equation_manager;
|
||||
unsigned m_size;
|
||||
mpz * m_as; // precise coefficients
|
||||
double * m_approx_as; // approximated coefficients
|
||||
var * m_xs; // var ids
|
||||
linear_equation() {}
|
||||
public:
|
||||
unsigned size() const { return m_size; }
|
||||
mpz const & a(unsigned idx) const { SASSERT(idx < m_size); return m_as[idx]; }
|
||||
double approx_a(unsigned idx) const { SASSERT(idx < m_size); return m_approx_as[idx]; }
|
||||
var x(unsigned idx) const { SASSERT(idx < m_size); return m_xs[idx]; }
|
||||
unsigned pos(unsigned x_i) const;
|
||||
void get_a(double_manager & m, unsigned idx, double & r) const { r = m_approx_as[idx]; }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpq & r) const { m.set(r, m_as[idx]); }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpz & r) const { m.set(r, m_as[idx]); }
|
||||
};
|
||||
|
||||
class linear_equation_manager {
|
||||
public:
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef linear_equation::var var;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
private:
|
||||
typedef svector<var> var_buffer;
|
||||
|
||||
small_object_allocator & m_allocator;
|
||||
numeral_manager & m;
|
||||
mpz_buffer m_int_buffer;
|
||||
mpz_buffer m_val_buffer;
|
||||
char_vector m_mark;
|
||||
var_buffer m_var_buffer;
|
||||
|
||||
linear_equation * mk_core(unsigned sz, mpz * as, var * xs);
|
||||
|
||||
public:
|
||||
linear_equation_manager(numeral_manager & _m, small_object_allocator & a):m_allocator(a), m(_m), m_int_buffer(m), m_val_buffer(m) {}
|
||||
~linear_equation_manager() {}
|
||||
|
||||
linear_equation * mk(unsigned sz, mpq * as, var * xs, bool normalized = false);
|
||||
linear_equation * mk(unsigned sz, mpz * as, var * xs, bool normalized = false);
|
||||
void del(linear_equation * eq);
|
||||
|
||||
// Return b1 * eq1 + b2 * eq2
|
||||
// return 0 if the b1 * eq1 + b2 * eq2 == 0
|
||||
linear_equation * mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2);
|
||||
|
||||
void display(std::ostream & out, linear_equation const & eq) const;
|
||||
};
|
||||
|
||||
#endif
|
480
src/tactic/arith/nla2bv_tactic.cpp
Normal file
480
src/tactic/arith/nla2bv_tactic.cpp
Normal file
|
@ -0,0 +1,480 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
nla2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert quantified NIA problems to bounded bit-vector arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-3
|
||||
|
||||
Notes:
|
||||
Ported to tactic framework on 2012-02-28
|
||||
The original file was called qfnla2bv.cpp
|
||||
|
||||
--*/
|
||||
#include "tactical.h"
|
||||
#include "arith_decl_plugin.h"
|
||||
#include "bv_decl_plugin.h"
|
||||
#include "for_each_expr.h"
|
||||
#include "expr_replacer.h"
|
||||
#include "optional.h"
|
||||
#include "bv2int_rewriter.h"
|
||||
#include "bv2real_rewriter.h"
|
||||
#include "extension_model_converter.h"
|
||||
#include "filter_model_converter.h"
|
||||
#include "bound_manager.h"
|
||||
#include "obj_pair_hashtable.h"
|
||||
#include "ast_smt2_pp.h"
|
||||
|
||||
//
|
||||
//
|
||||
// 1. for each variable, determine bounds (s.t., non-negative variables
|
||||
// have unsigned bit-vectors).
|
||||
//
|
||||
// 2. replace uninterpreted variables of sort int by
|
||||
// expressions of the form +- bv2int(b) +- k
|
||||
// where k is a slack.
|
||||
//
|
||||
// 3. simplify resulting assertion set to reduce occurrences of bv2int.
|
||||
//
|
||||
|
||||
class nla2bv_tactic : public tactic {
|
||||
class imp {
|
||||
typedef rational numeral;
|
||||
ast_manager & m_manager;
|
||||
bool m_is_sat_preserving;
|
||||
arith_util m_arith;
|
||||
bv_util m_bv;
|
||||
bv2real_util m_bv2real;
|
||||
bv2int_rewriter_ctx m_bv2int_ctx;
|
||||
bound_manager m_bounds;
|
||||
expr_substitution m_subst;
|
||||
func_decl_ref_vector m_vars;
|
||||
expr_ref_vector m_defs;
|
||||
expr_ref_vector m_trail;
|
||||
unsigned m_num_bits;
|
||||
unsigned m_default_bv_size;
|
||||
ref<filter_model_converter> m_fmc;
|
||||
|
||||
public:
|
||||
imp(ast_manager & m, params_ref const& p):
|
||||
m_manager(m),
|
||||
m_is_sat_preserving(true),
|
||||
m_arith(m),
|
||||
m_bv(m),
|
||||
m_bv2real(m, rational(p.get_uint(":nla2bv-root",2)), rational(p.get_uint(":nla2bv-divisor",2)), p.get_uint(":nla2bv-max-bv-size", UINT_MAX)),
|
||||
m_bv2int_ctx(m, p),
|
||||
m_bounds(m),
|
||||
m_subst(m),
|
||||
m_vars(m),
|
||||
m_defs(m),
|
||||
m_trail(m),
|
||||
m_fmc(0) {
|
||||
m_default_bv_size = m_num_bits = p.get_uint(":nla2bv-bv-size", 4);
|
||||
}
|
||||
|
||||
~imp() {}
|
||||
|
||||
|
||||
void operator()(goal & g, model_converter_ref & mc) {
|
||||
TRACE("nla2bv", g.display(tout);
|
||||
tout << "Muls: " << count_mul(g) << "\n";
|
||||
);
|
||||
m_fmc = alloc(filter_model_converter, m_manager);
|
||||
m_bounds(g);
|
||||
collect_power2(g);
|
||||
if(!collect_vars(g)) {
|
||||
throw tactic_exception("goal is not in the fragment supported by nla2bv");
|
||||
}
|
||||
tactic_report report("nla->bv", g);
|
||||
substitute_vars(g);
|
||||
TRACE("nla2bv", g.display(tout << "substitute vars\n"););
|
||||
reduce_bv2int(g);
|
||||
reduce_bv2real(g);
|
||||
TRACE("nla2bv", g.display(tout << "after reduce\n"););
|
||||
extension_model_converter * evc = alloc(extension_model_converter, m_manager);
|
||||
mc = concat(m_fmc.get(), evc);
|
||||
for (unsigned i = 0; i < m_vars.size(); ++i) {
|
||||
evc->insert(m_vars[i].get(), m_defs[i].get());
|
||||
}
|
||||
for (unsigned i = 0; i < m_bv2real.num_aux_decls(); ++i) {
|
||||
m_fmc->insert(m_bv2real.get_aux_decl(i));
|
||||
}
|
||||
IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(nla->bv :sat-preserving " << m_is_sat_preserving << ")\n";);
|
||||
TRACE("nla2bv_verbose", g.display(tout););
|
||||
TRACE("nla2bv", tout << "Muls: " << count_mul(g) << "\n";);
|
||||
g.inc_depth();
|
||||
if (!is_sat_preserving())
|
||||
g.updt_prec(goal::UNDER);
|
||||
}
|
||||
|
||||
bool const& is_sat_preserving() const { return m_is_sat_preserving; }
|
||||
|
||||
private:
|
||||
void set_satisfiability_preserving(bool f) {
|
||||
m_is_sat_preserving = f;
|
||||
}
|
||||
|
||||
void collect_power2(goal & g) {
|
||||
m_bv2int_ctx.collect_power2(g);
|
||||
obj_map<expr, expr*> const& p2 = m_bv2int_ctx.power2();
|
||||
if (p2.empty()) return;
|
||||
obj_map<expr, expr*>::iterator it = p2.begin(), end = p2.end();
|
||||
for (; it != end; ++it) {
|
||||
expr* v = it->m_value;
|
||||
unsigned num_bits = m_bv.get_bv_size(v);
|
||||
expr* w = m_bv.mk_bv2int(m_bv.mk_bv_shl(m_bv.mk_numeral(1, num_bits), v));
|
||||
m_trail.push_back(w);
|
||||
m_subst.insert(it->m_key, w);
|
||||
TRACE("nla2bv", tout << mk_ismt2_pp(it->m_key, m_manager) << " " << mk_ismt2_pp(w, m_manager) << "\n";);
|
||||
}
|
||||
// eliminate the variables that are power of two.
|
||||
substitute_vars(g);
|
||||
m_subst.reset();
|
||||
}
|
||||
|
||||
|
||||
// eliminate bv2int from formula
|
||||
void reduce_bv2int(goal & g) {
|
||||
bv2int_rewriter_star reduce(m_manager, m_bv2int_ctx);
|
||||
expr_ref r(m_manager);
|
||||
for (unsigned i = 0; i < g.size(); ++i) {
|
||||
reduce(g.form(i), r);
|
||||
g.update(i, r);
|
||||
}
|
||||
assert_side_conditions(g, m_bv2int_ctx.num_side_conditions(),
|
||||
m_bv2int_ctx.side_conditions());
|
||||
}
|
||||
|
||||
// eliminate bv2real from formula
|
||||
void reduce_bv2real(goal & g) {
|
||||
bv2real_rewriter_star reduce(m_manager, m_bv2real);
|
||||
expr_ref r(m_manager);
|
||||
for (unsigned i = 0; i < g.size(); ++i) {
|
||||
reduce(g.form(i), r);
|
||||
if (m_bv2real.contains_bv2real(r)) {
|
||||
throw tactic_exception("nla2bv could not eliminate reals");
|
||||
}
|
||||
g.update(i, r);
|
||||
}
|
||||
assert_side_conditions(g, m_bv2real.num_side_conditions(),
|
||||
m_bv2real.side_conditions());
|
||||
}
|
||||
|
||||
void assert_side_conditions(goal & g, unsigned sz, expr * const * conditions) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
g.assert_expr(conditions[i]);
|
||||
set_satisfiability_preserving(false);
|
||||
}
|
||||
TRACE("nla2bv",
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
tout << mk_ismt2_pp(conditions[i], m_manager) << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
// substitute variables by bit-vectors
|
||||
void substitute_vars(goal & g) {
|
||||
scoped_ptr<expr_replacer> er = mk_default_expr_replacer(m_manager);
|
||||
er->set_substitution(&m_subst);
|
||||
expr_ref r(m_manager);
|
||||
for (unsigned i = 0; i < g.size(); ++i) {
|
||||
(*er)(g.form(i), r);
|
||||
g.update(i, r);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// collect uninterpreted variables in problem.
|
||||
// create a substitution from the variables to
|
||||
// bit-vector terms.
|
||||
//
|
||||
void add_var(app* n) {
|
||||
if (m_arith.is_int(n)) {
|
||||
add_int_var(n);
|
||||
}
|
||||
else {
|
||||
SASSERT(m_arith.is_real(n));
|
||||
add_real_var(n);
|
||||
}
|
||||
}
|
||||
|
||||
void add_int_var(app* n) {
|
||||
expr_ref s_bv(m_manager);
|
||||
sort_ref bv_sort(m_manager);
|
||||
optional<numeral> low, up;
|
||||
numeral tmp;
|
||||
bool is_strict;
|
||||
if (m_bounds.has_lower(n, tmp, is_strict)) {
|
||||
SASSERT(!is_strict);
|
||||
low = tmp;
|
||||
}
|
||||
if (m_bounds.has_upper(n, tmp, is_strict)) {
|
||||
SASSERT(!is_strict);
|
||||
up = tmp;
|
||||
}
|
||||
//
|
||||
// [low .. up]
|
||||
// num_bits = log2(1 + |up - low|) or m_num_bits
|
||||
//
|
||||
unsigned num_bits = m_num_bits;
|
||||
if (up && low) {
|
||||
num_bits = log2(abs(*up - *low)+numeral(1));
|
||||
}
|
||||
else {
|
||||
TRACE("nla2bv", tout << "no bounds for " << mk_ismt2_pp(n, m_manager) << "\n";);
|
||||
set_satisfiability_preserving(false);
|
||||
}
|
||||
bv_sort = m_bv.mk_sort(num_bits);
|
||||
std::string name = n->get_decl()->get_name().str();
|
||||
s_bv = m_manager.mk_fresh_const(name.c_str(), bv_sort);
|
||||
m_fmc->insert(to_app(s_bv)->get_decl());
|
||||
s_bv = m_bv.mk_bv2int(s_bv);
|
||||
if (low) {
|
||||
if (!(*low).is_zero()) {
|
||||
// low <= s_bv
|
||||
// ~>
|
||||
// replace s_bv by s_bv + low
|
||||
// add 'low' to model for n.
|
||||
//
|
||||
s_bv = m_arith.mk_add(s_bv, m_arith.mk_numeral(*low, true));
|
||||
}
|
||||
}
|
||||
else if (up) {
|
||||
// s_bv <= up
|
||||
// ~>
|
||||
// replace s_bv by up - s_bv
|
||||
//
|
||||
s_bv = m_arith.mk_sub(m_arith.mk_numeral(*up, true), s_bv);
|
||||
}
|
||||
else {
|
||||
s_bv = m_arith.mk_sub(s_bv, m_arith.mk_numeral(m_bv.power_of_two(num_bits-1), true));
|
||||
}
|
||||
|
||||
m_trail.push_back(s_bv);
|
||||
m_subst.insert(n, s_bv);
|
||||
m_vars.push_back(n->get_decl());
|
||||
m_defs.push_back(s_bv);
|
||||
}
|
||||
|
||||
void add_real_var(app* n) {
|
||||
expr_ref s_bv(m_manager), s_bvr(m_manager), s(m_manager), t(m_manager);
|
||||
sort_ref bv_sort(m_manager);
|
||||
bv_sort = m_bv.mk_sort(m_num_bits);
|
||||
set_satisfiability_preserving(false);
|
||||
std::string name = n->get_decl()->get_name().str();
|
||||
s = m_manager.mk_fresh_const(name.c_str(), bv_sort);
|
||||
name += "_r";
|
||||
t = m_manager.mk_fresh_const(name.c_str(), bv_sort);
|
||||
m_fmc->insert(to_app(s)->get_decl());
|
||||
m_fmc->insert(to_app(t)->get_decl());
|
||||
s_bv = m_bv2real.mk_bv2real(s, t);
|
||||
m_trail.push_back(s_bv);
|
||||
m_subst.insert(n, s_bv);
|
||||
m_vars.push_back(n->get_decl());
|
||||
|
||||
// use version without bv2real function.
|
||||
m_bv2real.mk_bv2real_reduced(s, t, s_bvr);
|
||||
m_defs.push_back(s_bvr);
|
||||
}
|
||||
|
||||
|
||||
// update number of bits based on the largest constant used.
|
||||
void update_num_bits(app* n) {
|
||||
bool is_int;
|
||||
numeral nm;
|
||||
if (m_arith.is_numeral(n, nm, is_int) && is_int) {
|
||||
nm = abs(nm);
|
||||
unsigned l = log2(nm);
|
||||
if (m_num_bits <= l) {
|
||||
m_num_bits = l+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned log2(rational const& n) {
|
||||
rational pow(1), two(2);
|
||||
unsigned sz = 0;
|
||||
while (pow < n) {
|
||||
++sz;
|
||||
pow *= two;
|
||||
}
|
||||
if (sz == 0) sz = 1;
|
||||
return sz;
|
||||
}
|
||||
|
||||
class get_uninterp_proc {
|
||||
imp& m_imp;
|
||||
ptr_vector<app> m_vars;
|
||||
bool m_in_supported_fragment;
|
||||
public:
|
||||
get_uninterp_proc(imp& s): m_imp(s), m_in_supported_fragment(true) {}
|
||||
ptr_vector<app> const& vars() { return m_vars; }
|
||||
void operator()(var * n) {
|
||||
m_in_supported_fragment = false;
|
||||
}
|
||||
void operator()(app* n) {
|
||||
arith_util& a = m_imp.m_arith;
|
||||
ast_manager& m = a.get_manager();
|
||||
if (a.is_int(n) &&
|
||||
is_uninterp_const(n)) {
|
||||
m_vars.push_back(n);
|
||||
}
|
||||
else if (a.is_real(n) &&
|
||||
is_uninterp_const(n)) {
|
||||
m_vars.push_back(n);
|
||||
}
|
||||
else if (m.is_bool(n) && is_uninterp_const(n)) {
|
||||
|
||||
}
|
||||
else if (!(a.is_mul(n) ||
|
||||
a.is_add(n) ||
|
||||
a.is_sub(n) ||
|
||||
a.is_le(n) ||
|
||||
a.is_lt(n) ||
|
||||
a.is_ge(n) ||
|
||||
a.is_gt(n) ||
|
||||
a.is_numeral(n) ||
|
||||
a.is_uminus(n) ||
|
||||
m_imp.m_bv2real.is_pos_le(n) ||
|
||||
m_imp.m_bv2real.is_pos_lt(n) ||
|
||||
n->get_family_id() == a.get_manager().get_basic_family_id())) {
|
||||
TRACE("nla2bv", tout << "Not supported: " << mk_ismt2_pp(n, a.get_manager()) << "\n";);
|
||||
m_in_supported_fragment = false;
|
||||
}
|
||||
m_imp.update_num_bits(n);
|
||||
}
|
||||
void operator()(quantifier* q) {
|
||||
m_in_supported_fragment = false;
|
||||
}
|
||||
bool is_supported() const { return m_in_supported_fragment; }
|
||||
};
|
||||
|
||||
bool collect_vars(goal const & g) {
|
||||
get_uninterp_proc fe_var(*this);
|
||||
for_each_expr_at(fe_var, g);
|
||||
for (unsigned i = 0; i < fe_var.vars().size(); ++i) {
|
||||
add_var(fe_var.vars()[i]);
|
||||
}
|
||||
return fe_var.is_supported() && !fe_var.vars().empty();
|
||||
}
|
||||
|
||||
class count_mul_proc {
|
||||
imp& m_imp;
|
||||
unsigned m_count;
|
||||
public:
|
||||
count_mul_proc(imp& s): m_imp(s), m_count(0) {}
|
||||
unsigned count() const { return m_count; }
|
||||
void operator()(var * n) {}
|
||||
void operator()(app* n) {
|
||||
if (m_imp.m_arith.is_mul(n)) {
|
||||
m_count += n->get_num_args()-1;
|
||||
}
|
||||
if (m_imp.m_bv.is_bv_mul(n)) {
|
||||
unsigned num_vars = 0;
|
||||
for (unsigned j = 0; j < n->get_num_args(); ++j) {
|
||||
if (!m_imp.m_bv.is_numeral(n->get_arg(j))) {
|
||||
++num_vars;
|
||||
}
|
||||
}
|
||||
if (num_vars > 1) {
|
||||
m_count += num_vars - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
void operator()(quantifier* q) {}
|
||||
};
|
||||
|
||||
unsigned count_mul(goal const & g) {
|
||||
count_mul_proc c(*this);
|
||||
for_each_expr_at(c, g);
|
||||
return c.count();
|
||||
}
|
||||
};
|
||||
|
||||
params_ref m_params;
|
||||
imp * m_imp;
|
||||
|
||||
struct scoped_set_imp {
|
||||
nla2bv_tactic & m_owner;
|
||||
scoped_set_imp(nla2bv_tactic & o, imp & i):
|
||||
m_owner(o) {
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_owner.m_imp = &i;
|
||||
}
|
||||
}
|
||||
|
||||
~scoped_set_imp() {
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_owner.m_imp = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
nla2bv_tactic(params_ref const & p):
|
||||
m_params(p),
|
||||
m_imp(0) {
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(nla2bv_tactic, m_params);
|
||||
}
|
||||
|
||||
virtual ~nla2bv_tactic() {
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":nla2bv-max-bv-size", CPK_UINT, "(default: inf) maximum bit-vector size used by nla2bv tactic");
|
||||
r.insert(":nla2bv-bv-size", CPK_UINT, "(default: 4) default bit-vector size used by nla2bv tactic.");
|
||||
r.insert(":nla2bv-root", CPK_UINT, "(default: 2) nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding.");
|
||||
r.insert(":nla2bv-divisor", CPK_UINT, "(default: 2) nla2bv tactic parameter.");
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Modify a goal to use bounded bit-vector
|
||||
arithmetic in place of non-linear integer arithmetic.
|
||||
\return false if transformation is not possible.
|
||||
*/
|
||||
virtual 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());
|
||||
fail_if_proof_generation("nla2bv", g);
|
||||
fail_if_unsat_core_generation("nla2bv", g);
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
|
||||
imp proc(g->m(), m_params);
|
||||
scoped_set_imp setter(*this, proc);
|
||||
proc(*(g.get()), mc);
|
||||
|
||||
result.push_back(g.get());
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
|
||||
virtual void cleanup(void) {
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p) {
|
||||
return alloc(nla2bv_tactic, p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
30
src/tactic/arith/nla2bv_tactic.h
Normal file
30
src/tactic/arith/nla2bv_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
nla2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert quantified NIA problems to bounded bit-vector arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-3
|
||||
|
||||
Notes:
|
||||
Ported to tactic framework on 2012-02-28
|
||||
|
||||
--*/
|
||||
#ifndef _NLA2BV_TACTIC_H_
|
||||
#define _NLA2BV_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
||||
|
211
src/tactic/arith/normalize_bounds_tactic.cpp
Normal file
211
src/tactic/arith/normalize_bounds_tactic.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
normalize_bounds_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Replace x with x' + l, when l <= x
|
||||
where x' is a fresh variable.
|
||||
Note that, after the transformation 0 <= x'.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-10-21.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"bound_manager.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class normalize_bounds_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
bound_manager m_bm;
|
||||
arith_util m_util;
|
||||
th_rewriter m_rw;
|
||||
bool m_normalize_int_only;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_bm(m),
|
||||
m_util(m),
|
||||
m_rw(m, p) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
m_normalize_int_only = p.get_bool(":norm-int-only", true);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
bool is_target(expr * var, rational & val) {
|
||||
bool strict;
|
||||
return
|
||||
is_uninterp_const(var) &&
|
||||
(!m_normalize_int_only || m_util.is_int(var)) &&
|
||||
m_bm.has_lower(var, val, strict) &&
|
||||
!val.is_zero();
|
||||
}
|
||||
|
||||
bool is_target(expr * var) {
|
||||
rational val;
|
||||
return is_target(var, val);
|
||||
}
|
||||
|
||||
bool has_lowers() {
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
TRACE("normalize_bounds_tactic",
|
||||
rational val; bool strict;
|
||||
tout << mk_ismt2_pp(*it, m) << " has_lower: " << m_bm.has_lower(*it, val, strict) << " val: " << val << "\n";);
|
||||
if (is_target(*it))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
mc = 0; pc = 0; core = 0;
|
||||
bool produce_models = in->models_enabled();
|
||||
bool produce_proofs = in->proofs_enabled();
|
||||
tactic_report report("normalize-bounds", *in);
|
||||
|
||||
m_bm(*in);
|
||||
|
||||
if (!has_lowers()) {
|
||||
result.push_back(in.get());
|
||||
// did not increase depth since it didn't do anything.
|
||||
return;
|
||||
}
|
||||
|
||||
extension_model_converter * mc1 = 0;
|
||||
filter_model_converter * mc2 = 0;
|
||||
if (produce_models) {
|
||||
mc1 = alloc(extension_model_converter, m);
|
||||
mc2 = alloc(filter_model_converter, m);
|
||||
mc = concat(mc2, mc1);
|
||||
}
|
||||
|
||||
unsigned num_norm_bounds = 0;
|
||||
expr_substitution subst(m);
|
||||
rational val;
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
expr * x = *it;
|
||||
if (is_target(x, val)) {
|
||||
num_norm_bounds++;
|
||||
sort * s = m.get_sort(x);
|
||||
app * x_prime = m.mk_fresh_const(0, s);
|
||||
expr * def = m_util.mk_add(x_prime, m_util.mk_numeral(val, s));
|
||||
subst.insert(x, def);
|
||||
if (produce_models) {
|
||||
mc1->insert(to_app(x)->get_decl(), def);
|
||||
mc2->insert(x_prime->get_decl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report_tactic_progress(":normalized-bounds", num_norm_bounds);
|
||||
|
||||
m_rw.set_substitution(&subst);
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = in->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = in->form(idx);
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (produce_proofs) {
|
||||
proof * pr = in->pr(idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
in->update(idx, new_curr, new_pr, in->dep(idx));
|
||||
}
|
||||
TRACE("normalize_bounds_tactic", in->display(tout););
|
||||
in->inc_depth();
|
||||
result.push_back(in.get());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
|
||||
public:
|
||||
normalize_bounds_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(normalize_bounds_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~normalize_bounds_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
insert_produce_models(r);
|
||||
r.insert(":norm-int-only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants.");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#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_normalize_bounds_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(normalize_bounds_tactic, m, p));
|
||||
}
|
30
src/tactic/arith/normalize_bounds_tactic.h
Normal file
30
src/tactic/arith/normalize_bounds_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
normalize_bounds_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Replace x with x' + l, when l <= x
|
||||
where x' is a fresh variable.
|
||||
Note that, after the transformation 0 <= x'.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-10-21.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _NORMALIZE_BOUNDS_TACTIC_H_
|
||||
#define _NORMALIZE_BOUNDS_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_normalize_bounds_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
102
src/tactic/arith/pb2bv_model_converter.cpp
Normal file
102
src/tactic/arith/pb2bv_model_converter.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_model_converter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Model converter for the pb2bv tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Christoph (cwinter) 2012-02-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"trace.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"model_v2_pp.h"
|
||||
#include"pb2bv_model_converter.h"
|
||||
|
||||
pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m, obj_map<func_decl, expr*> const & c2bit, bound_manager const & bm):
|
||||
m(_m) {
|
||||
obj_map<func_decl, expr*>::iterator it = c2bit.begin();
|
||||
obj_map<func_decl, expr*>::iterator end = c2bit.end();
|
||||
for ( ; it != end; it++) {
|
||||
m_c2bit.push_back(func_decl_pair(it->m_key, to_app(it->m_value)->get_decl()));
|
||||
m.inc_ref(it->m_key);
|
||||
m.inc_ref(to_app(it->m_value)->get_decl());
|
||||
}
|
||||
bound_manager::iterator it2 = bm.begin();
|
||||
bound_manager::iterator end2 = bm.end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
expr * c = *it2;
|
||||
SASSERT(is_uninterp_const(c));
|
||||
func_decl * d = to_app(c)->get_decl();
|
||||
if (!c2bit.contains(d)) {
|
||||
SASSERT(d->get_arity() == 0);
|
||||
m.inc_ref(d);
|
||||
m_c2bit.push_back(func_decl_pair(d, static_cast<func_decl*>(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pb2bv_model_converter::~pb2bv_model_converter() {
|
||||
svector<func_decl_pair>::const_iterator it = m_c2bit.begin();
|
||||
svector<func_decl_pair>::const_iterator end = m_c2bit.end();
|
||||
for (; it != end; ++it) {
|
||||
m.dec_ref(it->first);
|
||||
m.dec_ref(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void pb2bv_model_converter::operator()(model_ref & md) {
|
||||
(*this)(md, 0);
|
||||
}
|
||||
|
||||
void pb2bv_model_converter::operator()(model_ref & md, unsigned goal_idx) {
|
||||
SASSERT(goal_idx == 0);
|
||||
TRACE("pb2bv", tout << "converting model:\n"; model_v2_pp(tout, *md); display(tout););
|
||||
arith_util a_util(m);
|
||||
|
||||
svector<func_decl_pair>::const_iterator it = m_c2bit.begin();
|
||||
svector<func_decl_pair>::const_iterator end = m_c2bit.end();
|
||||
for (; it != end; ++it) {
|
||||
if (it->second) {
|
||||
expr * val = md->get_const_interp(it->second);
|
||||
if (val == 0 || m.is_false(val)) {
|
||||
/* false's and don't cares get the integer 0 solution*/
|
||||
md->register_decl(it->first, a_util.mk_numeral(rational(0), true));
|
||||
}
|
||||
else {
|
||||
md->register_decl(it->first, a_util.mk_numeral(rational(1), true));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// it->first is a don't care.
|
||||
md->register_decl(it->first, a_util.mk_numeral(rational(0), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pb2bv_model_converter::display(std::ostream & out) {
|
||||
out << "(pb2bv-model-converter";
|
||||
svector<func_decl_pair>::const_iterator it = m_c2bit.begin();
|
||||
svector<func_decl_pair>::const_iterator end = m_c2bit.end();
|
||||
for (; it != end; ++it) {
|
||||
out << "\n (" << it->first->get_name() << " ";
|
||||
if (it->second == 0)
|
||||
out << "0";
|
||||
else
|
||||
out << it->second->get_name();
|
||||
out << ")";
|
||||
}
|
||||
out << ")\n";
|
||||
}
|
||||
|
||||
model_converter * pb2bv_model_converter::translate(ast_translation & translator) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
}
|
39
src/tactic/arith/pb2bv_model_converter.h
Normal file
39
src/tactic/arith/pb2bv_model_converter.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_model_converter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Model converter for the pb2bv tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Christoph (cwinter) 2012-02-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PB2BV_MODEL_CONVERTER_
|
||||
#define _PB2BV_MODEL_CONVERTER_
|
||||
|
||||
#include"model_converter.h"
|
||||
#include"bound_manager.h"
|
||||
|
||||
class pb2bv_model_converter : public model_converter {
|
||||
typedef std::pair<func_decl *, func_decl *> func_decl_pair;
|
||||
|
||||
ast_manager & m;
|
||||
svector<func_decl_pair> m_c2bit;
|
||||
public:
|
||||
pb2bv_model_converter(ast_manager & _m, obj_map<func_decl, expr*> const & c2bit, bound_manager const & bm);
|
||||
virtual ~pb2bv_model_converter();
|
||||
virtual void operator()(model_ref & md);
|
||||
virtual void operator()(model_ref & md, unsigned goal_idx);
|
||||
virtual void display(std::ostream & out);
|
||||
virtual model_converter * translate(ast_translation & translator);
|
||||
};
|
||||
|
||||
#endif
|
1051
src/tactic/arith/pb2bv_tactic.cpp
Normal file
1051
src/tactic/arith/pb2bv_tactic.cpp
Normal file
File diff suppressed because it is too large
Load diff
30
src/tactic/arith/pb2bv_tactic.h
Normal file
30
src/tactic/arith/pb2bv_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for converting Pseudo-Boolean constraints to BV
|
||||
|
||||
Author:
|
||||
|
||||
Christoph (cwinter) 2012-02-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PB2BV_TACTIC_
|
||||
#define _PB2BV_TACTIC_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_pb2bv_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
probe * mk_is_pb_probe();
|
||||
|
||||
#endif
|
421
src/tactic/arith/probe_arith.cpp
Normal file
421
src/tactic/arith/probe_arith.cpp
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
probe_arith.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Some probes for arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-03-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"probe.h"
|
||||
#include"expr2polynomial.h"
|
||||
#include"for_each_expr.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"goal_util.h"
|
||||
|
||||
class arith_degree_probe : public probe {
|
||||
struct proc {
|
||||
ast_manager & m;
|
||||
unsynch_mpq_manager m_qm;
|
||||
polynomial::manager m_pm;
|
||||
default_expr2polynomial m_expr2poly;
|
||||
arith_util m_util;
|
||||
unsigned m_max_degree;
|
||||
unsigned long long m_acc_degree;
|
||||
unsigned m_counter;
|
||||
|
||||
proc(ast_manager & _m):m(_m), m_pm(m_qm), m_expr2poly(m, m_pm), m_util(m) {
|
||||
m_max_degree = 0;
|
||||
m_acc_degree = 0;
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
void updt_degree(polynomial_ref const & p) {
|
||||
unsigned deg = m_pm.total_degree(p);
|
||||
if (deg > m_max_degree)
|
||||
m_max_degree = deg;
|
||||
m_acc_degree += deg;
|
||||
m_counter++;
|
||||
}
|
||||
|
||||
void process(app * n) {
|
||||
expr * lhs = n->get_arg(0);
|
||||
expr * rhs = n->get_arg(1);
|
||||
polynomial_ref p1(m_pm);
|
||||
polynomial_ref p2(m_pm);
|
||||
scoped_mpz d1(m_qm);
|
||||
scoped_mpz d2(m_qm);
|
||||
m_expr2poly.to_polynomial(lhs, p1, d1);
|
||||
m_expr2poly.to_polynomial(rhs, p2, d2);
|
||||
updt_degree(p1);
|
||||
updt_degree(p2);
|
||||
}
|
||||
|
||||
void operator()(var * n) {}
|
||||
void operator()(quantifier * n) {}
|
||||
void operator()(app * n) {
|
||||
if (m_util.is_le(n) || m_util.is_lt(n) || m_util.is_gt(n) || m_util.is_ge(n))
|
||||
process(n);
|
||||
if (m.is_eq(n) && m_util.is_int_real(n->get_arg(0)))
|
||||
process(n);
|
||||
}
|
||||
};
|
||||
|
||||
bool m_avg;
|
||||
public:
|
||||
arith_degree_probe(bool avg):m_avg(avg) {}
|
||||
|
||||
virtual result operator()(goal const & g) {
|
||||
proc p(g.m());
|
||||
for_each_expr_at(p, g);
|
||||
if (m_avg)
|
||||
return p.m_counter == 0 ? 0.0 : static_cast<double>(p.m_acc_degree)/static_cast<double>(p.m_counter);
|
||||
else
|
||||
return p.m_max_degree;
|
||||
}
|
||||
};
|
||||
|
||||
class arith_bw_probe : public probe {
|
||||
struct proc {
|
||||
ast_manager & m;
|
||||
arith_util m_util;
|
||||
unsigned m_max_bw;
|
||||
unsigned long long m_acc_bw;
|
||||
unsigned m_counter;
|
||||
|
||||
proc(ast_manager & _m):m(_m), m_util(m) {
|
||||
m_max_bw = 0;
|
||||
m_acc_bw = 0;
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
void operator()(var * n) {}
|
||||
void operator()(quantifier * n) {}
|
||||
void operator()(app * n) {
|
||||
rational val;
|
||||
if (m_util.is_numeral(n, val)) {
|
||||
unsigned bw = val.bitsize();
|
||||
if (bw > m_max_bw)
|
||||
m_max_bw = bw;
|
||||
m_acc_bw += bw;
|
||||
m_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool m_avg;
|
||||
public:
|
||||
arith_bw_probe(bool avg):m_avg(avg) {}
|
||||
|
||||
virtual result operator()(goal const & g) {
|
||||
proc p(g.m());
|
||||
for_each_expr_at(p, g);
|
||||
if (m_avg)
|
||||
return p.m_counter == 0 ? 0.0 : static_cast<double>(p.m_acc_bw)/static_cast<double>(p.m_counter);
|
||||
else
|
||||
return p.m_max_bw;
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_arith_avg_degree_probe() {
|
||||
return alloc(arith_degree_probe, true);
|
||||
}
|
||||
|
||||
probe * mk_arith_max_degree_probe() {
|
||||
return alloc(arith_degree_probe, false);
|
||||
}
|
||||
|
||||
probe * mk_arith_avg_bw_probe() {
|
||||
return alloc(arith_bw_probe, true);
|
||||
}
|
||||
|
||||
probe * mk_arith_max_bw_probe() {
|
||||
return alloc(arith_bw_probe, false);
|
||||
}
|
||||
|
||||
struct is_non_qflira_functor {
|
||||
struct found {};
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
bool m_int;
|
||||
bool m_real;
|
||||
|
||||
is_non_qflira_functor(ast_manager & _m, bool _int, bool _real):m(_m), u(m), m_int(_int), m_real(_real) {}
|
||||
|
||||
void operator()(var *) { throw found(); }
|
||||
|
||||
void operator()(quantifier *) { throw found(); }
|
||||
|
||||
bool compatible_sort(app * n) const {
|
||||
if (m.is_bool(n))
|
||||
return true;
|
||||
if (m_int && u.is_int(n))
|
||||
return true;
|
||||
if (m_real && u.is_real(n))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void operator()(app * n) {
|
||||
if (!compatible_sort(n))
|
||||
throw found();
|
||||
family_id fid = n->get_family_id();
|
||||
if (fid == m.get_basic_family_id())
|
||||
return;
|
||||
if (fid == u.get_family_id()) {
|
||||
switch (n->get_decl_kind()) {
|
||||
case OP_LE: case OP_GE: case OP_LT: case OP_GT:
|
||||
case OP_ADD: case OP_NUM:
|
||||
return;
|
||||
case OP_MUL:
|
||||
if (n->get_num_args() != 2)
|
||||
throw found();
|
||||
if (!u.is_numeral(n->get_arg(0)))
|
||||
throw found();
|
||||
return;
|
||||
case OP_TO_REAL:
|
||||
if (!m_real)
|
||||
throw found();
|
||||
break;
|
||||
default:
|
||||
throw found();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (is_uninterp_const(n))
|
||||
return;
|
||||
throw found();
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qflia(goal const & g) {
|
||||
is_non_qflira_functor p(g.m(), true, false);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qflia_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qflia(g);
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qflra(goal const & g) {
|
||||
is_non_qflira_functor p(g.m(), false, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qflra_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qflra(g);
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qflira(goal const & g) {
|
||||
is_non_qflira_functor p(g.m(), true, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qflira_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qflira(g);
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_lp(goal const & g) {
|
||||
ast_manager & m = g.m();
|
||||
arith_util u(m);
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g.form(i);
|
||||
bool sign = false;
|
||||
while (m.is_not(f, f))
|
||||
sign = !sign;
|
||||
if (m.is_eq(f) && !sign) {
|
||||
if (m.get_sort(to_app(f)->get_arg(0))->get_family_id() != u.get_family_id())
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (u.is_le(f) || u.is_ge(f) || u.is_lt(f) || u.is_gt(f))
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_ilp(goal const & g) {
|
||||
if (!is_qflia(g))
|
||||
return false;
|
||||
if (has_term_ite(g))
|
||||
return false;
|
||||
return is_lp(g);
|
||||
}
|
||||
|
||||
static bool is_mip(goal const & g) {
|
||||
if (!is_qflira(g))
|
||||
return false;
|
||||
if (has_term_ite(g))
|
||||
return false;
|
||||
return is_lp(g);
|
||||
}
|
||||
|
||||
class is_ilp_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_ilp(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_mip_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_mip(g);
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_is_qflia_probe() {
|
||||
return alloc(is_qflia_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_qflra_probe() {
|
||||
return alloc(is_qflra_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_qflira_probe() {
|
||||
return alloc(is_qflira_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_ilp_probe() {
|
||||
return alloc(is_ilp_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_mip_probe() {
|
||||
return alloc(is_mip_probe);
|
||||
}
|
||||
|
||||
|
||||
struct is_non_nira_functor {
|
||||
struct found {};
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
bool m_int;
|
||||
bool m_real;
|
||||
bool m_quant;
|
||||
|
||||
is_non_nira_functor(ast_manager & _m, bool _int, bool _real, bool _quant):m(_m), u(m), m_int(_int), m_real(_real), m_quant(_quant) {}
|
||||
|
||||
void operator()(var * x) {
|
||||
if (!m_quant)
|
||||
throw found();
|
||||
sort * s = x->get_sort();
|
||||
if (m_int && u.is_int(s))
|
||||
return;
|
||||
if (m_real && u.is_real(s))
|
||||
return;
|
||||
throw found();
|
||||
}
|
||||
|
||||
void operator()(quantifier *) {
|
||||
if (!m_quant)
|
||||
throw found();
|
||||
}
|
||||
|
||||
bool compatible_sort(app * n) const {
|
||||
if (m.is_bool(n))
|
||||
return true;
|
||||
if (m_int && u.is_int(n))
|
||||
return true;
|
||||
if (m_real && u.is_real(n))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void operator()(app * n) {
|
||||
if (!compatible_sort(n))
|
||||
throw found();
|
||||
family_id fid = n->get_family_id();
|
||||
if (fid == m.get_basic_family_id())
|
||||
return;
|
||||
if (fid == u.get_family_id())
|
||||
return;
|
||||
if (is_uninterp_const(n))
|
||||
return;
|
||||
throw found();
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qfnia(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), true, false, false);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
static bool is_qfnra(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), false, true, false);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
static bool is_nia(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), true, false, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
static bool is_nra(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), false, true, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qfnia_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qfnia(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_qfnra_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qfnra(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_nia_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_nia(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_nra_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_nra(g);
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_is_qfnia_probe() {
|
||||
return alloc(is_qfnia_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_qfnra_probe() {
|
||||
return alloc(is_qfnra_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_nia_probe() {
|
||||
return alloc(is_nia_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_nra_probe() {
|
||||
return alloc(is_nra_probe);
|
||||
}
|
39
src/tactic/arith/probe_arith.h
Normal file
39
src/tactic/arith/probe_arith.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
probe_arith.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Some probes for arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-03-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _PROBE_ARITH_H_
|
||||
#define _PROBE_ARITH_H_
|
||||
|
||||
class probe;
|
||||
probe * mk_arith_avg_bw_probe();
|
||||
probe * mk_arith_max_bw_probe();
|
||||
probe * mk_arith_avg_degree_probe();
|
||||
probe * mk_arith_max_degree_probe();
|
||||
|
||||
probe * mk_is_qflia_probe();
|
||||
probe * mk_is_qflra_probe();
|
||||
probe * mk_is_qflira_probe();
|
||||
probe * mk_is_ilp_probe();
|
||||
probe * mk_is_mip_probe();
|
||||
|
||||
probe * mk_is_qfnia_probe();
|
||||
probe * mk_is_qfnra_probe();
|
||||
probe * mk_is_nia_probe();
|
||||
probe * mk_is_nra_probe();
|
||||
|
||||
#endif
|
563
src/tactic/arith/propagate_ineqs_tactic.cpp
Normal file
563
src/tactic/arith/propagate_ineqs_tactic.cpp
Normal file
|
@ -0,0 +1,563 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
propagate_ineqs_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
This tactic performs the following tasks:
|
||||
|
||||
- Propagate bounds using the bound_propagator.
|
||||
- Eliminate subsumed inequalities.
|
||||
For example:
|
||||
x - y >= 3
|
||||
can be replaced with true if we know that
|
||||
x >= 3 and y <= 0
|
||||
|
||||
- Convert inequalities of the form p <= k and p >= k into p = k,
|
||||
where p is a polynomial and k is a constant.
|
||||
|
||||
This strategy assumes the input is in arith LHS mode.
|
||||
This can be achieved by using option :arith-lhs true in the
|
||||
simplifier.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-19
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"bound_propagator.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"simplify_tactic.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class propagate_ineqs_tactic : public tactic {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
propagate_ineqs_tactic(ast_manager & m, params_ref const & p);
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(propagate_ineqs_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~propagate_ineqs_tactic();
|
||||
|
||||
virtual void updt_params(params_ref const & p);
|
||||
virtual void collect_param_descrs(param_descrs & r) {}
|
||||
|
||||
virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core);
|
||||
|
||||
virtual void cleanup();
|
||||
protected:
|
||||
virtual void set_cancel(bool f);
|
||||
};
|
||||
|
||||
tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(propagate_ineqs_tactic, m, p));
|
||||
}
|
||||
|
||||
struct propagate_ineqs_tactic::imp {
|
||||
ast_manager & m;
|
||||
unsynch_mpq_manager nm;
|
||||
small_object_allocator m_allocator;
|
||||
bound_propagator bp;
|
||||
arith_util m_util;
|
||||
typedef bound_propagator::var a_var;
|
||||
obj_map<expr, a_var> m_expr2var;
|
||||
expr_ref_vector m_var2expr;
|
||||
|
||||
typedef numeral_buffer<mpq, unsynch_mpq_manager> mpq_buffer;
|
||||
typedef svector<a_var> var_buffer;
|
||||
|
||||
mpq_buffer m_num_buffer;
|
||||
var_buffer m_var_buffer;
|
||||
goal_ref m_new_goal;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_allocator("ineq-simplifier"),
|
||||
bp(nm, m_allocator, p),
|
||||
m_util(m),
|
||||
m_var2expr(m),
|
||||
m_num_buffer(nm) {
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
updt_params_core(p);
|
||||
bp.updt_params(p);
|
||||
}
|
||||
|
||||
void display_bounds(std::ostream & out) {
|
||||
unsigned sz = m_var2expr.size();
|
||||
mpq k;
|
||||
bool strict;
|
||||
unsigned ts;
|
||||
for (unsigned x = 0; x < sz; x++) {
|
||||
if (bp.lower(x, k, strict, ts))
|
||||
out << nm.to_string(k) << " " << (strict ? "<" : "<=");
|
||||
else
|
||||
out << "-oo <";
|
||||
out << " " << mk_ismt2_pp(m_var2expr.get(x), m) << " ";
|
||||
if (bp.upper(x, k, strict, ts))
|
||||
out << (strict ? "<" : "<=") << " " << nm.to_string(k);
|
||||
else
|
||||
out << "< oo";
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
a_var mk_var(expr * t) {
|
||||
if (m_util.is_to_real(t))
|
||||
t = to_app(t)->get_arg(0);
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = m_var2expr.size();
|
||||
bp.mk_var(x, m_util.is_int(t));
|
||||
m_var2expr.push_back(t);
|
||||
m_expr2var.insert(t, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
void expr2linear_pol(expr * t, mpq_buffer & as, var_buffer & xs) {
|
||||
mpq c_mpq_val;
|
||||
if (m_util.is_add(t)) {
|
||||
rational c_val;
|
||||
unsigned num = to_app(t)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * mon = to_app(t)->get_arg(i);
|
||||
expr * c, * x;
|
||||
if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) {
|
||||
nm.set(c_mpq_val, c_val.to_mpq());
|
||||
as.push_back(c_mpq_val);
|
||||
xs.push_back(mk_var(x));
|
||||
}
|
||||
else {
|
||||
as.push_back(mpq(1));
|
||||
xs.push_back(mk_var(mon));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
as.push_back(mpq(1));
|
||||
xs.push_back(mk_var(t));
|
||||
}
|
||||
nm.del(c_mpq_val);
|
||||
}
|
||||
|
||||
a_var mk_linear_pol(expr * t) {
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = mk_var(t);
|
||||
if (m_util.is_add(t)) {
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(t, m_num_buffer, m_var_buffer);
|
||||
m_num_buffer.push_back(mpq(-1));
|
||||
m_var_buffer.push_back(x);
|
||||
bp.mk_eq(m_num_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr());
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
enum kind { EQ, LE, GE };
|
||||
|
||||
bool process(expr * t) {
|
||||
bool sign = false;
|
||||
while (m.is_not(t, t))
|
||||
sign = !sign;
|
||||
bool strict = false;
|
||||
kind k;
|
||||
if (m.is_eq(t)) {
|
||||
if (sign)
|
||||
return false;
|
||||
k = EQ;
|
||||
}
|
||||
else if (m_util.is_le(t)) {
|
||||
if (sign) {
|
||||
k = GE;
|
||||
strict = true;
|
||||
}
|
||||
else {
|
||||
k = LE;
|
||||
}
|
||||
}
|
||||
else if (m_util.is_ge(t)) {
|
||||
if (sign) {
|
||||
k = LE;
|
||||
strict = true;
|
||||
}
|
||||
else {
|
||||
k = GE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
expr * lhs = to_app(t)->get_arg(0);
|
||||
expr * rhs = to_app(t)->get_arg(1);
|
||||
if (m_util.is_numeral(lhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
if (k == LE)
|
||||
k = GE;
|
||||
else if (k == GE)
|
||||
k = LE;
|
||||
}
|
||||
|
||||
rational c;
|
||||
if (!m_util.is_numeral(rhs, c))
|
||||
return false;
|
||||
a_var x = mk_linear_pol(lhs);
|
||||
mpq c_prime;
|
||||
nm.set(c_prime, c.to_mpq());
|
||||
if (k == EQ) {
|
||||
SASSERT(!strict);
|
||||
bp.assert_lower(x, c_prime, false);
|
||||
bp.assert_upper(x, c_prime, false);
|
||||
}
|
||||
else if (k == LE) {
|
||||
bp.assert_upper(x, c_prime, strict);
|
||||
}
|
||||
else {
|
||||
SASSERT(k == GE);
|
||||
bp.assert_lower(x, c_prime, strict);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collect_bounds(goal const & g) {
|
||||
bool found = false;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * t = g.form(i);
|
||||
if (process(t))
|
||||
found = true;
|
||||
else
|
||||
m_new_goal->assert_expr(t); // save non-bounds here
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool lower_subsumed(expr * p, mpq const & k, bool strict) {
|
||||
if (!m_util.is_add(p))
|
||||
return false;
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(p, m_num_buffer, m_var_buffer);
|
||||
mpq implied_k;
|
||||
bool implied_strict;
|
||||
bool result =
|
||||
bp.lower(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) &&
|
||||
(nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
|
||||
nm.del(implied_k);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool upper_subsumed(expr * p, mpq const & k, bool strict) {
|
||||
if (!m_util.is_add(p))
|
||||
return false;
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(p, m_num_buffer, m_var_buffer);
|
||||
mpq implied_k;
|
||||
bool implied_strict;
|
||||
bool result =
|
||||
bp.upper(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) &&
|
||||
(nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
|
||||
nm.del(implied_k);
|
||||
return result;
|
||||
}
|
||||
|
||||
void restore_bounds() {
|
||||
mpq l, u;
|
||||
bool strict_l, strict_u, has_l, has_u;
|
||||
unsigned ts;
|
||||
unsigned sz = m_var2expr.size();
|
||||
for (unsigned x = 0; x < sz; x++) {
|
||||
expr * p = m_var2expr.get(x);
|
||||
has_l = bp.lower(x, l, strict_l, ts);
|
||||
has_u = bp.upper(x, u, strict_u, ts);
|
||||
if (!has_l && !has_u)
|
||||
continue;
|
||||
if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) {
|
||||
// l <= p <= l --> p = l
|
||||
m_new_goal->assert_expr(m.mk_eq(p, m_util.mk_numeral(rational(l), m_util.is_int(p))));
|
||||
continue;
|
||||
}
|
||||
if (has_l && !lower_subsumed(p, l, strict_l)) {
|
||||
if (strict_l)
|
||||
m_new_goal->assert_expr(m.mk_not(m_util.mk_le(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))));
|
||||
else
|
||||
m_new_goal->assert_expr(m_util.mk_ge(p, m_util.mk_numeral(rational(l), m_util.is_int(p))));
|
||||
}
|
||||
if (has_u && !upper_subsumed(p, u, strict_u)) {
|
||||
if (strict_u)
|
||||
m_new_goal->assert_expr(m.mk_not(m_util.mk_ge(p, m_util.mk_numeral(rational(u), m_util.is_int(p)))));
|
||||
else
|
||||
m_new_goal->assert_expr(m_util.mk_le(p, m_util.mk_numeral(rational(u), m_util.is_int(p))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_x_minus_y_eq_0(expr * t, expr * & x, expr * & y) {
|
||||
expr * lhs, * rhs, * m1, * m2;
|
||||
if (m.is_eq(t, lhs, rhs) && m_util.is_zero(rhs) && m_util.is_add(lhs, m1, m2)) {
|
||||
if (m_util.is_times_minus_one(m2, y) && is_uninterp_const(m1)) {
|
||||
x = m1;
|
||||
return true;
|
||||
}
|
||||
if (m_util.is_times_minus_one(m1, y) && is_uninterp_const(m2)) {
|
||||
x = m2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_unbounded(expr * t) {
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return !bp.has_lower(x) && !bp.has_upper(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lower(expr * t, mpq & k, bool & strict) {
|
||||
unsigned ts;
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return bp.lower(x, k, strict, ts);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool upper(expr * t, mpq & k, bool & strict) {
|
||||
unsigned ts;
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return bp.upper(x, k, strict, ts);
|
||||
return false;
|
||||
}
|
||||
|
||||
void find_ite_bounds(expr * root) {
|
||||
TRACE("find_ite_bounds_bug", display_bounds(tout););
|
||||
expr * n = root;
|
||||
expr * target = 0;
|
||||
expr * c, * t, * e;
|
||||
expr * x, * y;
|
||||
bool has_l, has_u;
|
||||
mpq l_min, u_max;
|
||||
bool l_strict, u_strict;
|
||||
mpq curr;
|
||||
bool curr_strict;
|
||||
while (true) {
|
||||
TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";);
|
||||
|
||||
if (m.is_ite(n, c, t, e)) {
|
||||
if (is_x_minus_y_eq_0(t, x, y))
|
||||
n = e;
|
||||
else if (is_x_minus_y_eq_0(e, x, y))
|
||||
n = t;
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (is_x_minus_y_eq_0(n, x, y)) {
|
||||
n = 0;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n";
|
||||
if (target) {
|
||||
tout << "target: " << mk_ismt2_pp(target, m) << "\n";
|
||||
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n";
|
||||
});
|
||||
|
||||
if (is_unbounded(y))
|
||||
std::swap(x, y);
|
||||
|
||||
if (!is_unbounded(x)) {
|
||||
TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";);
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == 0) {
|
||||
target = x;
|
||||
if (lower(y, curr, curr_strict)) {
|
||||
has_l = true;
|
||||
nm.set(l_min, curr);
|
||||
l_strict = curr_strict;
|
||||
}
|
||||
else {
|
||||
has_l = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
|
||||
}
|
||||
if (upper(y, curr, curr_strict)) {
|
||||
has_u = true;
|
||||
nm.set(u_max, curr);
|
||||
u_strict = curr_strict;
|
||||
}
|
||||
else {
|
||||
has_u = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
|
||||
}
|
||||
}
|
||||
else if (target == x) {
|
||||
if (has_l) {
|
||||
if (lower(y, curr, curr_strict)) {
|
||||
if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) {
|
||||
nm.set(l_min, curr);
|
||||
l_strict = curr_strict;
|
||||
}
|
||||
}
|
||||
else {
|
||||
has_l = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
|
||||
}
|
||||
}
|
||||
if (has_u) {
|
||||
if (upper(y, curr, curr_strict)) {
|
||||
if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) {
|
||||
nm.set(u_max, curr);
|
||||
u_strict = curr_strict;
|
||||
}
|
||||
}
|
||||
else {
|
||||
has_u = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!has_l && !has_u)
|
||||
break;
|
||||
|
||||
if (n == 0) {
|
||||
TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n";
|
||||
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n";
|
||||
tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n";
|
||||
tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";);
|
||||
a_var x = mk_var(target);
|
||||
if (has_l)
|
||||
bp.assert_lower(x, l_min, l_strict);
|
||||
if (has_u)
|
||||
bp.assert_upper(x, u_max, u_strict);
|
||||
break;
|
||||
}
|
||||
}
|
||||
nm.del(l_min);
|
||||
nm.del(u_max);
|
||||
nm.del(curr);
|
||||
}
|
||||
|
||||
void find_ite_bounds() {
|
||||
unsigned sz = m_new_goal->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = m_new_goal->form(i);
|
||||
if (m.is_ite(f))
|
||||
find_ite_bounds(to_app(f));
|
||||
}
|
||||
bp.propagate();
|
||||
TRACE("find_ite_bounds", display_bounds(tout););
|
||||
}
|
||||
|
||||
void operator()(goal * g, goal_ref & r) {
|
||||
tactic_report report("propagate-ineqs", *g);
|
||||
|
||||
m_new_goal = alloc(goal, *g, true);
|
||||
m_new_goal->inc_depth();
|
||||
r = m_new_goal.get();
|
||||
if (!collect_bounds(*g)) {
|
||||
m_new_goal = 0;
|
||||
r = g;
|
||||
return; // nothing to be done
|
||||
}
|
||||
|
||||
TRACE("propagate_ineqs_tactic", g->display(tout); display_bounds(tout); tout << "bound propagator:\n"; bp.display(tout););
|
||||
|
||||
bp.propagate();
|
||||
|
||||
report_tactic_progress(":bound-propagations", bp.get_num_propagations());
|
||||
report_tactic_progress(":bound-false-alarms", bp.get_num_false_alarms());
|
||||
|
||||
if (bp.inconsistent()) {
|
||||
r->reset();
|
||||
r->assert_expr(m.mk_false());
|
||||
return;
|
||||
}
|
||||
|
||||
// find_ite_bounds(); // did not help
|
||||
|
||||
restore_bounds();
|
||||
|
||||
TRACE("propagate_ineqs_tactic", tout << "after propagation:\n"; display_bounds(tout); bp.display(tout););
|
||||
TRACE("propagate_ineqs_tactic", r->display(tout););
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
// TODO
|
||||
}
|
||||
};
|
||||
|
||||
propagate_ineqs_tactic::propagate_ineqs_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
propagate_ineqs_tactic::~propagate_ineqs_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::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());
|
||||
fail_if_proof_generation("propagate-ineqs", g);
|
||||
fail_if_unsat_core_generation("propagate-ineqs", g);
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
goal_ref r;
|
||||
(*m_imp)(g.get(), r);
|
||||
result.push_back(r.get());
|
||||
SASSERT(r->is_well_sorted());
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
42
src/tactic/arith/propagate_ineqs_tactic.h
Normal file
42
src/tactic/arith/propagate_ineqs_tactic.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
propagate_ineqs_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
This tactic performs the following tasks:
|
||||
|
||||
- Propagate bounds using the bound_propagator.
|
||||
- Eliminate subsumed inequalities.
|
||||
For example:
|
||||
x - y >= 3
|
||||
can be replaced with true if we know that
|
||||
x >= 3 and y <= 0
|
||||
|
||||
- Convert inequalities of the form p <= k and p >= k into p = k,
|
||||
where p is a polynomial and k is a constant.
|
||||
|
||||
This strategy assumes the input is in arith LHS mode.
|
||||
This can be achieved by using option :arith-lhs true in the
|
||||
simplifier.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-19
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PROPAGATE_INEQS_TACTIC_H_
|
||||
#define _PROPAGATE_INEQS_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
909
src/tactic/arith/purify_arith_tactic.cpp
Normal file
909
src/tactic/arith/purify_arith_tactic.cpp
Normal file
|
@ -0,0 +1,909 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
purify_arith_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for eliminating arithmetic operators: DIV, IDIV, MOD,
|
||||
TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM).
|
||||
|
||||
This tactic uses the simplifier for also eliminating:
|
||||
OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"algebraic_numbers.h"
|
||||
#include"nnf_tactic.h"
|
||||
#include"simplify_tactic.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
/*
|
||||
----
|
||||
Some of the rules needed in the conversion are implemented in
|
||||
arith_rewriter.cpp. Here is a summary of these rules:
|
||||
|
||||
(^ t (/ p q)) --> (^ (^ t (/ 1 q)) p)
|
||||
|
||||
(^ t n) --> t*...*t
|
||||
when integer power expansion is requested
|
||||
|
||||
(is-int t) --> t = (to-real (to-int t))
|
||||
|
||||
(rem t1 t2) --> ite(t2 >= 0, (mod t1 t2), -(mod t1 t2))
|
||||
|
||||
----
|
||||
The tactic implements a set of transformation rules. These rules
|
||||
create fresh constants or (existential) variables, and add new
|
||||
constraints to the context.
|
||||
The context is the set of asserted formulas or a quantifier.
|
||||
|
||||
A rule is represented as:
|
||||
|
||||
From --> To | C
|
||||
|
||||
It means, any expression that matches From is replaced by To,
|
||||
and the constraints C are added to the context.
|
||||
For clarity reasons, I write the constraints using ad-hoc notation.
|
||||
|
||||
|
||||
Rules
|
||||
(^ t 0) --> k | t != 0 implies k = 1, t = 0 implies k = 0^0
|
||||
where k is fresh
|
||||
0^0 is a constant used to capture the meaning of (^ 0 0).
|
||||
|
||||
(^ t (/ 1 n)) --> k | t = k^n
|
||||
when n is odd
|
||||
where k is fresh
|
||||
|
||||
(^ t (/ 1 n)) --> k | t >= 0 implies t = k^n, t < 0 implies t = neg-root(t, n)
|
||||
when n is even
|
||||
where k is fresh
|
||||
neg-root is a function symbol used to capture the meaning of a negative root
|
||||
|
||||
(root-obj p(x) i) --> k | p(k) = 0, l < k < u
|
||||
when root object elimination is requested
|
||||
where k is fresh
|
||||
(l, u) is an isolating interval for the i-th root of p.
|
||||
|
||||
(to-int t) --> k | 0 <= to-real(k) - t < 1
|
||||
where k is a fresh integer constant/variable
|
||||
|
||||
(/ t1 t2) --> k | t2 != 0 implies k*t2 = t1, t2 = 0 implies k = div-0(t1)
|
||||
where k is fresh
|
||||
div-0 is a function symbol used to capture the meaning of division by 0.
|
||||
|
||||
Remark: If it can be shown that t2 != 0, then the div-0(t1) function application
|
||||
vanishes from the formula.
|
||||
|
||||
(div t1 t2) --> k1 | t2 = 0 \/ t1 = k1 * t2 + k2,
|
||||
t2 = 0 \/ 0 <= k2,
|
||||
t2 = 0 \/ k2 < |t2|,
|
||||
t2 != 0 \/ k1 = idiv-0(t1),
|
||||
t2 != 0 \/ k2 = mod-0(t1)
|
||||
k1 is a fresh name for (div t1 t2)
|
||||
k2 is a fresh name for (mod t1 t2)
|
||||
|
||||
(mod t1 t2) --> k2 | same constraints as above
|
||||
*/
|
||||
|
||||
struct purify_arith_decls {
|
||||
ast_manager & m;
|
||||
func_decl * m_int_0_pw_0_decl; // decl for: int 0^0
|
||||
func_decl * m_real_0_pw_0_decl; // decl for: rel 0^0
|
||||
func_decl * m_neg_root_decl; // decl for: even root of negative (real) number
|
||||
func_decl * m_div_0_decl; // decl for: x/0
|
||||
func_decl * m_idiv_0_decl; // decl for: div(x, 0)
|
||||
func_decl * m_mod_0_decl; // decl for: mod(x, 0)
|
||||
func_decl * m_asin_u_decl; // decl for: asin(x) when x < -1 or x > 1
|
||||
func_decl * m_acos_u_decl; // decl for: acos(x) when x < -1 or x > 1
|
||||
|
||||
void inc_refs() {
|
||||
m.inc_ref(m_int_0_pw_0_decl);
|
||||
m.inc_ref(m_real_0_pw_0_decl);
|
||||
m.inc_ref(m_neg_root_decl);
|
||||
m.inc_ref(m_div_0_decl);
|
||||
m.inc_ref(m_idiv_0_decl);
|
||||
m.inc_ref(m_mod_0_decl);
|
||||
m.inc_ref(m_asin_u_decl);
|
||||
m.inc_ref(m_acos_u_decl);
|
||||
}
|
||||
|
||||
void dec_refs() {
|
||||
m.dec_ref(m_int_0_pw_0_decl);
|
||||
m.dec_ref(m_real_0_pw_0_decl);
|
||||
m.dec_ref(m_neg_root_decl);
|
||||
m.dec_ref(m_div_0_decl);
|
||||
m.dec_ref(m_idiv_0_decl);
|
||||
m.dec_ref(m_mod_0_decl);
|
||||
m.dec_ref(m_asin_u_decl);
|
||||
m.dec_ref(m_acos_u_decl);
|
||||
}
|
||||
|
||||
purify_arith_decls(arith_util & u):
|
||||
m(u.get_manager()) {
|
||||
sort * i = u.mk_int();
|
||||
sort * r = u.mk_real();
|
||||
m_int_0_pw_0_decl = m.mk_const_decl(symbol("0^0-int"), i);
|
||||
m_real_0_pw_0_decl = m.mk_const_decl(symbol("0^0-real"), r);
|
||||
sort * rr[2] = { r, r };
|
||||
m_neg_root_decl = m.mk_func_decl(symbol("neg-root"), 2, rr, r);
|
||||
m_div_0_decl = m.mk_func_decl(symbol("/0"), 1, &r, r);
|
||||
m_idiv_0_decl = m.mk_func_decl(symbol("div0"), 1, &i, i);
|
||||
m_mod_0_decl = m.mk_func_decl(symbol("mod0"), 1, &i, i);
|
||||
m_asin_u_decl = m.mk_func_decl(symbol("asin-u"), 1, &r, r);
|
||||
m_acos_u_decl = m.mk_func_decl(symbol("acos-u"), 1, &r, r);
|
||||
inc_refs();
|
||||
}
|
||||
|
||||
purify_arith_decls(ast_manager & _m,
|
||||
func_decl * int_0_pw_0,
|
||||
func_decl * real_0_pw_0,
|
||||
func_decl * neg_root,
|
||||
func_decl * div_0,
|
||||
func_decl * idiv_0,
|
||||
func_decl * mod_0,
|
||||
func_decl * asin_u,
|
||||
func_decl * acos_u
|
||||
):
|
||||
m(_m),
|
||||
m_int_0_pw_0_decl(int_0_pw_0),
|
||||
m_real_0_pw_0_decl(real_0_pw_0),
|
||||
m_neg_root_decl(neg_root),
|
||||
m_div_0_decl(div_0),
|
||||
m_idiv_0_decl(idiv_0),
|
||||
m_mod_0_decl(mod_0),
|
||||
m_asin_u_decl(asin_u),
|
||||
m_acos_u_decl(acos_u) {
|
||||
inc_refs();
|
||||
}
|
||||
|
||||
~purify_arith_decls() {
|
||||
dec_refs();
|
||||
}
|
||||
};
|
||||
|
||||
struct purify_arith_proc {
|
||||
arith_util & m_util;
|
||||
purify_arith_decls & m_aux_decls;
|
||||
bool m_produce_proofs;
|
||||
bool m_elim_root_objs;
|
||||
bool m_elim_inverses;
|
||||
bool m_complete;
|
||||
|
||||
purify_arith_proc(arith_util & u, purify_arith_decls & d, bool produce_proofs, bool elim_root_objs, bool elim_inverses, bool complete):
|
||||
m_util(u),
|
||||
m_aux_decls(d),
|
||||
m_produce_proofs(produce_proofs),
|
||||
m_elim_root_objs(elim_root_objs),
|
||||
m_elim_inverses(elim_inverses),
|
||||
m_complete(complete) {
|
||||
}
|
||||
|
||||
arith_util & u() {
|
||||
return m_util;
|
||||
}
|
||||
|
||||
ast_manager & m() {
|
||||
return u().get_manager();
|
||||
}
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
purify_arith_proc & m_owner;
|
||||
obj_map<app, expr*> m_app2fresh;
|
||||
obj_map<app, proof*> m_app2pr;
|
||||
expr_ref_vector m_pinned;
|
||||
expr_ref_vector m_new_cnstrs;
|
||||
proof_ref_vector m_new_cnstr_prs;
|
||||
expr_ref m_subst;
|
||||
proof_ref m_subst_pr;
|
||||
bool m_in_q;
|
||||
unsigned m_var_idx;
|
||||
|
||||
rw_cfg(purify_arith_proc & o, bool in_q):
|
||||
m_owner(o),
|
||||
m_pinned(o.m()),
|
||||
m_new_cnstrs(o.m()),
|
||||
m_new_cnstr_prs(o.m()),
|
||||
m_subst(o.m()),
|
||||
m_subst_pr(o.m()),
|
||||
m_in_q(in_q),
|
||||
m_var_idx(0) {
|
||||
}
|
||||
|
||||
ast_manager & m() { return m_owner.m(); }
|
||||
|
||||
arith_util & u() { return m_owner.u(); }
|
||||
|
||||
bool produce_proofs() const { return m_owner.m_produce_proofs; }
|
||||
bool complete() const { return m_owner.m_complete; }
|
||||
bool elim_root_objs() const { return m_owner.m_elim_root_objs; }
|
||||
bool elim_inverses() const { return m_owner.m_elim_inverses; }
|
||||
|
||||
expr * mk_fresh_var(bool is_int) {
|
||||
if (m_in_q) {
|
||||
unsigned idx = m_var_idx;
|
||||
m_var_idx++;
|
||||
return m().mk_var(idx, is_int ? u().mk_int() : u().mk_real());
|
||||
}
|
||||
else {
|
||||
return m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real());
|
||||
}
|
||||
}
|
||||
|
||||
expr * mk_fresh_real_var() { return mk_fresh_var(false); }
|
||||
|
||||
expr * mk_fresh_int_var() { return mk_fresh_var(true); }
|
||||
|
||||
func_decl * div0_decl() { return m_owner.m_aux_decls.m_div_0_decl; }
|
||||
func_decl * idiv0_decl() { return m_owner.m_aux_decls.m_idiv_0_decl; }
|
||||
func_decl * mod0_decl() { return m_owner.m_aux_decls.m_mod_0_decl; }
|
||||
func_decl * int_0_pw_0_decl() { return m_owner.m_aux_decls.m_int_0_pw_0_decl; }
|
||||
func_decl * real_0_pw_0_decl() { return m_owner.m_aux_decls.m_real_0_pw_0_decl; }
|
||||
func_decl * neg_root_decl() { return m_owner.m_aux_decls.m_neg_root_decl; }
|
||||
func_decl * asin_u_decl() { return m_owner.m_aux_decls.m_asin_u_decl; }
|
||||
func_decl * acos_u_decl() { return m_owner.m_aux_decls.m_acos_u_decl; }
|
||||
|
||||
expr * mk_int_zero() { return u().mk_numeral(rational(0), true); }
|
||||
|
||||
expr * mk_real_zero() { return u().mk_numeral(rational(0), false); }
|
||||
|
||||
bool already_processed(app * t, expr_ref & result, proof_ref & result_pr) {
|
||||
expr * r;
|
||||
if (m_app2fresh.find(t, r)) {
|
||||
result = r;
|
||||
if (produce_proofs())
|
||||
result_pr = m_app2pr.find(t);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mk_def_proof(expr * k, expr * def, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
if (produce_proofs()) {
|
||||
expr * eq = m().mk_eq(k, def);
|
||||
proof * pr1 = m().mk_def_intro(eq);
|
||||
result_pr = m().mk_apply_def(k, def, pr1);
|
||||
}
|
||||
}
|
||||
|
||||
void push_cnstr_pr(proof * def_pr) {
|
||||
if (produce_proofs())
|
||||
m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 1, &def_pr));
|
||||
}
|
||||
|
||||
void push_cnstr_pr(proof * def_pr1, proof * def_pr2) {
|
||||
if (produce_proofs()) {
|
||||
proof * prs[2] = { def_pr1, def_pr2 };
|
||||
m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 2, prs));
|
||||
}
|
||||
}
|
||||
|
||||
void push_cnstr(expr * cnstr) {
|
||||
m_new_cnstrs.push_back(cnstr);
|
||||
}
|
||||
|
||||
void cache_result(app * t, expr * r, proof * pr) {
|
||||
m_app2fresh.insert(t, r);
|
||||
m_pinned.push_back(t);
|
||||
m_pinned.push_back(r);
|
||||
if (produce_proofs()) {
|
||||
m_app2pr.insert(t, pr);
|
||||
m_pinned.push_back(pr);
|
||||
}
|
||||
}
|
||||
|
||||
expr * OR(expr * arg1, expr * arg2) { return m().mk_or(arg1, arg2); }
|
||||
expr * AND(expr * arg1, expr * arg2) { return m().mk_and(arg1, arg2); }
|
||||
expr * EQ(expr * lhs, expr * rhs) { return m().mk_eq(lhs, rhs); }
|
||||
expr * NOT(expr * arg) { return m().mk_not(arg); }
|
||||
|
||||
void process_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k = mk_fresh_real_var();
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
expr * y = args[1];
|
||||
// y = 0 \/ y*k = x
|
||||
push_cnstr(OR(EQ(y, mk_real_zero()),
|
||||
EQ(u().mk_mul(y, k), x)));
|
||||
push_cnstr_pr(result_pr);
|
||||
|
||||
if (complete()) {
|
||||
// y != 0 \/ k = div-0(x)
|
||||
push_cnstr(OR(NOT(EQ(y, mk_real_zero())),
|
||||
EQ(k, m().mk_app(div0_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
}
|
||||
|
||||
void process_idiv(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref div_app(m());
|
||||
div_app = m().mk_app(f, num, args);
|
||||
if (already_processed(div_app, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k1 = mk_fresh_int_var();
|
||||
result = k1;
|
||||
mk_def_proof(k1, div_app, result_pr);
|
||||
cache_result(div_app, result, result_pr);
|
||||
|
||||
expr * k2 = mk_fresh_int_var();
|
||||
app_ref mod_app(m());
|
||||
proof_ref mod_pr(m());
|
||||
mod_app = u().mk_mod(args[0], args[1]);
|
||||
mk_def_proof(k2, mod_app, mod_pr);
|
||||
cache_result(mod_app, k2, mod_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
expr * y = args[1];
|
||||
// (div x y) --> k1 | y = 0 \/ x = k1 * y + k2,
|
||||
// y = 0 \/ 0 <= k2,
|
||||
// y = 0 \/ k2 < |y|,
|
||||
// y != 0 \/ k1 = idiv-0(x),
|
||||
// y != 0 \/ k2 = mod-0(x)
|
||||
// We can write y = 0 \/ k2 < |y| as:
|
||||
// y > 0 implies k2 < y ---> y <= 0 \/ k2 < y
|
||||
// y < 0 implies k2 < -y ---> y >= 0 \/ k2 < -y
|
||||
//
|
||||
expr * zero = mk_int_zero();
|
||||
push_cnstr(OR(EQ(y, zero), EQ(x, u().mk_add(u().mk_mul(k1, y), k2))));
|
||||
push_cnstr_pr(result_pr, mod_pr);
|
||||
|
||||
push_cnstr(OR(EQ(y, zero), u().mk_le(zero, k2)));
|
||||
push_cnstr_pr(mod_pr);
|
||||
|
||||
push_cnstr(OR(u().mk_le(y, zero), u().mk_lt(k2, y)));
|
||||
push_cnstr_pr(mod_pr);
|
||||
|
||||
push_cnstr(OR(u().mk_ge(y, zero), u().mk_lt(k2, u().mk_mul(u().mk_numeral(rational(-1), true), y))));
|
||||
push_cnstr_pr(mod_pr);
|
||||
|
||||
if (complete()) {
|
||||
push_cnstr(OR(NOT(EQ(y, zero)), EQ(k1, m().mk_app(idiv0_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
|
||||
push_cnstr(OR(NOT(EQ(y, zero)), EQ(k2, m().mk_app(mod0_decl(), x))));
|
||||
push_cnstr_pr(mod_pr);
|
||||
}
|
||||
}
|
||||
|
||||
void process_mod(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return;
|
||||
process_idiv(f, num, args, result, result_pr); // it will create mod
|
||||
VERIFY(already_processed(t, result, result_pr));
|
||||
}
|
||||
|
||||
void process_to_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k = mk_fresh_int_var();
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
// to-real(k) - x >= 0
|
||||
expr * diff = u().mk_add(u().mk_to_real(k), u().mk_mul(u().mk_numeral(rational(-1), false), x));
|
||||
push_cnstr(u().mk_ge(diff, mk_real_zero()));
|
||||
push_cnstr_pr(result_pr);
|
||||
|
||||
// not(to-real(k) - x >= 1)
|
||||
push_cnstr(NOT(u().mk_ge(diff, u().mk_numeral(rational(1), false))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
|
||||
br_status process_power(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
rational y;
|
||||
if (!u().is_numeral(args[1], y))
|
||||
return BR_FAILED;
|
||||
if (y.is_int() && !y.is_zero())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
bool is_int = u().is_int(args[0]);
|
||||
|
||||
expr * k = mk_fresh_var(is_int);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
expr * zero = u().mk_numeral(rational(0), is_int);
|
||||
expr * one = u().mk_numeral(rational(1), is_int);
|
||||
if (y.is_zero()) {
|
||||
// (^ x 0) --> k | x != 0 implies k = 1, x = 0 implies k = 0^0
|
||||
push_cnstr(OR(EQ(x, zero), EQ(k, one)));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
func_decl * z_pw_z = is_int ? int_0_pw_0_decl() : real_0_pw_0_decl();
|
||||
push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, m().mk_const(z_pw_z))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
}
|
||||
else if (!is_int) {
|
||||
SASSERT(!y.is_int());
|
||||
SASSERT(numerator(y).is_one());
|
||||
rational n = denominator(y);
|
||||
if (!n.is_even()) {
|
||||
// (^ x (/ 1 n)) --> k | x = k^n
|
||||
// when n is odd
|
||||
push_cnstr(EQ(x, u().mk_power(k, u().mk_numeral(n, false))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
else {
|
||||
SASSERT(n.is_even());
|
||||
// (^ x (/ 1 n)) --> k | x >= 0 implies (x = k^n and k >= 0), x < 0 implies k = neg-root(x, n)
|
||||
// when n is even
|
||||
push_cnstr(OR(NOT(u().mk_ge(x, zero)),
|
||||
AND(EQ(x, u().mk_power(k, u().mk_numeral(n, false))),
|
||||
u().mk_ge(k, zero))));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
push_cnstr(OR(u().mk_ge(x, zero),
|
||||
EQ(k, m().mk_app(neg_root_decl(), x, u().mk_numeral(n, false)))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// root not supported for integers.
|
||||
SASSERT(is_int);
|
||||
SASSERT(!y.is_int());
|
||||
return BR_FAILED;
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
void process_irrat(app * s, expr_ref & result, proof_ref & result_pr) {
|
||||
if (already_processed(s, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k = mk_fresh_real_var();
|
||||
result = k;
|
||||
mk_def_proof(k, s, result_pr);
|
||||
cache_result(s, result, result_pr);
|
||||
|
||||
anum_manager & am = u().am();
|
||||
anum const & a = u().to_irrational_algebraic_numeral(s);
|
||||
scoped_mpz_vector p(am.qm());
|
||||
am.get_polynomial(a, p);
|
||||
rational lower, upper;
|
||||
am.get_lower(a, lower);
|
||||
am.get_upper(a, upper);
|
||||
unsigned sz = p.size();
|
||||
SASSERT(sz > 2);
|
||||
ptr_buffer<expr> args;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (am.qm().is_zero(p[i]))
|
||||
continue;
|
||||
rational coeff = rational(p[i]);
|
||||
if (i == 0) {
|
||||
args.push_back(u().mk_numeral(coeff, false));
|
||||
}
|
||||
else {
|
||||
expr * m;
|
||||
if (i == 1)
|
||||
m = k;
|
||||
else
|
||||
m = u().mk_power(k, u().mk_numeral(rational(i), false));
|
||||
args.push_back(u().mk_mul(u().mk_numeral(coeff, false), m));
|
||||
}
|
||||
}
|
||||
SASSERT(args.size() >= 2);
|
||||
push_cnstr(EQ(u().mk_add(args.size(), args.c_ptr()), mk_real_zero()));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(u().mk_lt(u().mk_numeral(lower, false), k));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(u().mk_lt(k, u().mk_numeral(upper, false)));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
|
||||
br_status process_asin(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) {
|
||||
if (!elim_inverses())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, x);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
expr * k = mk_fresh_var(false);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
// Constraints:
|
||||
// -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2
|
||||
// If complete()
|
||||
// x < -1 implies k = asin_u(x)
|
||||
// x > 1 implies k = asin_u(x)
|
||||
expr * one = u().mk_numeral(rational(1), false);
|
||||
expr * mone = u().mk_numeral(rational(-1), false);
|
||||
expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi());
|
||||
expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi());
|
||||
// -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2
|
||||
push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)),
|
||||
NOT(u().mk_le(x, one))),
|
||||
AND(EQ(x, u().mk_sin(k)),
|
||||
AND(u().mk_ge(k, mpi2),
|
||||
u().mk_le(k, pi2)))));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
// x < -1 implies k = asin_u(x)
|
||||
// x > 1 implies k = asin_u(x)
|
||||
push_cnstr(OR(u().mk_ge(x, mone),
|
||||
EQ(k, m().mk_app(asin_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(OR(u().mk_le(x, one),
|
||||
EQ(k, m().mk_app(asin_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status process_acos(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) {
|
||||
if (!elim_inverses())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, x);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
expr * k = mk_fresh_var(false);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
// Constraints:
|
||||
// -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi
|
||||
// If complete()
|
||||
// x < -1 implies k = acos_u(x)
|
||||
// x > 1 implies k = acos_u(x)
|
||||
expr * one = u().mk_numeral(rational(1), false);
|
||||
expr * mone = u().mk_numeral(rational(-1), false);
|
||||
expr * pi = u().mk_pi();
|
||||
expr * zero = u().mk_numeral(rational(0), false);
|
||||
// -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi
|
||||
push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)),
|
||||
NOT(u().mk_le(x, one))),
|
||||
AND(EQ(x, u().mk_cos(k)),
|
||||
AND(u().mk_ge(k, zero),
|
||||
u().mk_le(k, pi)))));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
// x < -1 implies k = acos_u(x)
|
||||
// x > 1 implies k = acos_u(x)
|
||||
push_cnstr(OR(u().mk_ge(x, mone),
|
||||
EQ(k, m().mk_app(acos_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(OR(u().mk_le(x, one),
|
||||
EQ(k, m().mk_app(acos_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status process_atan(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) {
|
||||
if (!elim_inverses())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, x);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
expr * k = mk_fresh_var(false);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
// Constraints:
|
||||
// tan(k) = x, -pi/2 < k < pi/2
|
||||
expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi());
|
||||
expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi());
|
||||
push_cnstr(AND(EQ(x, u().mk_tan(k)),
|
||||
AND(u().mk_gt(k, mpi2),
|
||||
u().mk_lt(k, pi2))));
|
||||
push_cnstr_pr(result_pr);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
if (f->get_family_id() != u().get_family_id())
|
||||
return BR_FAILED;
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_DIV:
|
||||
process_div(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_IDIV:
|
||||
process_idiv(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_MOD:
|
||||
process_mod(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_TO_INT:
|
||||
process_to_int(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_POWER:
|
||||
return process_power(f, num, args, result, result_pr);
|
||||
case OP_ASIN:
|
||||
return process_asin(f, args[0], result, result_pr);
|
||||
case OP_ACOS:
|
||||
return process_acos(f, args[0], result, result_pr);
|
||||
case OP_ATAN:
|
||||
return process_atan(f, args[0], result, result_pr);
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
|
||||
if (is_quantifier(s)) {
|
||||
m_owner.process_quantifier(to_quantifier(s), m_subst, m_subst_pr);
|
||||
t = m_subst.get();
|
||||
t_pr = m_subst_pr.get();
|
||||
return true;
|
||||
}
|
||||
else if (u().is_irrational_algebraic_numeral(s) && elim_root_objs()) {
|
||||
process_irrat(to_app(s), m_subst, m_subst_pr);
|
||||
t = m_subst.get();
|
||||
t_pr = m_subst_pr.get();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
rw(purify_arith_proc & o, bool in_q):
|
||||
rewriter_tpl<rw_cfg>(o.m(), o.m_produce_proofs, m_cfg),
|
||||
m_cfg(o, in_q) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Return the number of (auxiliary) variables needed for converting an expression.
|
||||
*/
|
||||
struct num_vars_proc {
|
||||
arith_util & m_util;
|
||||
expr_fast_mark1 m_visited;
|
||||
ptr_vector<expr> m_todo;
|
||||
unsigned m_num_vars;
|
||||
bool m_elim_root_objs;
|
||||
|
||||
num_vars_proc(arith_util & u, bool elim_root_objs):
|
||||
m_util(u),
|
||||
m_elim_root_objs(elim_root_objs) {
|
||||
}
|
||||
|
||||
void visit(expr * t) {
|
||||
if (m_visited.is_marked(t))
|
||||
return;
|
||||
m_visited.mark(t);
|
||||
m_todo.push_back(t);
|
||||
}
|
||||
|
||||
void process(app * t) {
|
||||
if (t->get_family_id() == m_util.get_family_id()) {
|
||||
if (m_util.is_power(t)) {
|
||||
rational k;
|
||||
if (m_util.is_numeral(t->get_arg(1), k) && (k.is_zero() || !k.is_int())) {
|
||||
m_num_vars++;
|
||||
}
|
||||
}
|
||||
else if (m_util.is_div(t) ||
|
||||
m_util.is_idiv(t) ||
|
||||
m_util.is_mod(t) ||
|
||||
m_util.is_to_int(t) ||
|
||||
(m_util.is_irrational_algebraic_numeral(t) && m_elim_root_objs)) {
|
||||
m_num_vars++;
|
||||
}
|
||||
}
|
||||
unsigned num_args = t->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
visit(t->get_arg(i));
|
||||
}
|
||||
|
||||
unsigned operator()(expr * t) {
|
||||
m_num_vars = 0;
|
||||
visit(t);
|
||||
while (!m_todo.empty()) {
|
||||
expr * t = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
if (is_app(t))
|
||||
process(to_app(t));
|
||||
}
|
||||
m_visited.reset();
|
||||
return m_num_vars;
|
||||
}
|
||||
};
|
||||
|
||||
void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
num_vars_proc p(u(), m_elim_root_objs);
|
||||
expr_ref body(m());
|
||||
unsigned num_vars = p(q->get_expr());
|
||||
if (num_vars > 0) {
|
||||
// open space for aux vars
|
||||
var_shifter shifter(m());
|
||||
shifter(q->get_expr(), num_vars, body);
|
||||
}
|
||||
else {
|
||||
body = q->get_expr();
|
||||
}
|
||||
|
||||
rw r(*this, true);
|
||||
expr_ref new_body(m());
|
||||
proof_ref new_body_pr(m());
|
||||
r(body, new_body, new_body_pr);
|
||||
TRACE("purify_arith",
|
||||
tout << "num_vars: " << num_vars << "\n";
|
||||
tout << "body: " << mk_ismt2_pp(body, m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";);
|
||||
if (num_vars == 0) {
|
||||
result = m().update_quantifier(q, new_body);
|
||||
if (m_produce_proofs)
|
||||
result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), result_pr);
|
||||
}
|
||||
else {
|
||||
expr_ref_vector & cnstrs = r.cfg().m_new_cnstrs;
|
||||
cnstrs.push_back(new_body);
|
||||
new_body = m().mk_and(cnstrs.size(), cnstrs.c_ptr());
|
||||
ptr_buffer<sort> sorts;
|
||||
buffer<symbol> names;
|
||||
for (unsigned i = 0; i < num_vars; i++) {
|
||||
sorts.push_back(u().mk_real());
|
||||
names.push_back(m().mk_fresh_var_name("x"));
|
||||
}
|
||||
new_body = m().mk_exists(num_vars, sorts.c_ptr(), names.c_ptr(), new_body);
|
||||
result = m().update_quantifier(q, new_body);
|
||||
if (m_produce_proofs) {
|
||||
proof_ref_vector & cnstr_prs = r.cfg().m_new_cnstr_prs;
|
||||
cnstr_prs.push_back(result_pr);
|
||||
// TODO: improve proof
|
||||
result_pr = m().mk_quant_intro(q, to_quantifier(result.get()),
|
||||
m().mk_rewrite_star(q->get_expr(), new_body, cnstr_prs.size(), cnstr_prs.c_ptr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(goal & g, model_converter_ref & mc, bool produce_models) {
|
||||
rw r(*this, false);
|
||||
// purify
|
||||
expr_ref new_curr(m());
|
||||
proof_ref new_pr(m());
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * curr = g.form(i);
|
||||
r(curr, new_curr, new_pr);
|
||||
if (m_produce_proofs) {
|
||||
proof * pr = g.pr(i);
|
||||
new_pr = m().mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
g.update(i, new_curr, new_pr, g.dep(i));
|
||||
}
|
||||
|
||||
// add cnstraints
|
||||
sz = r.cfg().m_new_cnstrs.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
g.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : 0, 0);
|
||||
}
|
||||
|
||||
// add filter_model_converter to eliminate auxiliary variables from model
|
||||
if (produce_models) {
|
||||
filter_model_converter * fmc = alloc(filter_model_converter, m());
|
||||
mc = fmc;
|
||||
obj_map<app, expr*> & f2v = r.cfg().m_app2fresh;
|
||||
obj_map<app, expr*>::iterator it = f2v.begin();
|
||||
obj_map<app, expr*>::iterator end = f2v.end();
|
||||
for (; it != end; ++it) {
|
||||
app * v = to_app(it->m_value);
|
||||
SASSERT(is_uninterp_const(v));
|
||||
fmc->insert(v->get_decl());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class purify_arith_tactic : public tactic {
|
||||
arith_util m_util;
|
||||
purify_arith_decls m_aux_decls;
|
||||
params_ref m_params;
|
||||
public:
|
||||
purify_arith_tactic(ast_manager & m, params_ref const & p):
|
||||
m_util(m),
|
||||
m_aux_decls(m_util),
|
||||
m_params(p) {
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(purify_arith_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~purify_arith_tactic() {
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":complete", CPK_BOOL,
|
||||
"(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a functio. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root");
|
||||
r.insert(":elim-root-objects", CPK_BOOL,
|
||||
"(default: true) eliminate root objects.");
|
||||
r.insert(":elim-inverses", CPK_BOOL,
|
||||
"(default: true) eliminate inverse trigonometric functions (asin, acos, atan).");
|
||||
th_rewriter::get_param_descrs(r);
|
||||
}
|
||||
|
||||
virtual 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;
|
||||
tactic_report report("purify-arith", *g);
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
bool produce_models = g->models_enabled();
|
||||
bool elim_root_objs = m_params.get_bool(":elim-root-objects", true);
|
||||
bool elim_inverses = m_params.get_bool(":elim-inverses", true);
|
||||
bool complete = m_params.get_bool(":complete", true);
|
||||
purify_arith_proc proc(m_util, m_aux_decls, produce_proofs, elim_root_objs, elim_inverses, complete);
|
||||
|
||||
proc(*(g.get()), mc, produce_models);
|
||||
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("purify_arith", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p) {
|
||||
params_ref elim_rem_p = p;
|
||||
elim_rem_p.set_bool(":elim-rem", true);
|
||||
|
||||
params_ref skolemize_p;
|
||||
skolemize_p.set_bool(":skolemize", false);
|
||||
|
||||
return and_then(using_params(mk_snf_tactic(m, skolemize_p), skolemize_p),
|
||||
using_params(mk_simplify_tactic(m, elim_rem_p), elim_rem_p),
|
||||
alloc(purify_arith_tactic, m, p),
|
||||
mk_simplify_tactic(m, p));
|
||||
}
|
58
src/tactic/arith/purify_arith_tactic.h
Normal file
58
src/tactic/arith/purify_arith_tactic.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
purify_arith_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for eliminating arithmetic operators: DIV, IDIV, MOD,
|
||||
TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM).
|
||||
|
||||
This tactic uses the simplifier for also eliminating:
|
||||
OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT.
|
||||
|
||||
Remarks:
|
||||
- The semantics of division by zero is not specified. Thus,
|
||||
uninterpreted functions are used. An ExRCF procedure may
|
||||
treat the unintepreted function applications as fresh
|
||||
constants. Then, in any model produced by this procedure,
|
||||
the interpretation for division by zero must be checked.
|
||||
|
||||
- POWER operator can only be handled if the second argument is a
|
||||
rational value. The tactic has an option for preserving POWER
|
||||
operator where the second argument is an integer.
|
||||
|
||||
- The semantics of (^ t (/ 1 k)) is not specified when t < 0 and
|
||||
k is even. Similarly to the division by zero case,
|
||||
uninterpreted function symbols are created.
|
||||
|
||||
- The semantics of (^ t 0) is not specified if t == 0. Thus,
|
||||
uninterpreted function symbols are created.
|
||||
|
||||
- TO_REAL is not really outside of the RCF language
|
||||
since it is only used for "casting".
|
||||
|
||||
- All quantifiers must occur with positive polarity.
|
||||
The tactic snf (with skolemization disabled) is applied
|
||||
to enforce that.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _PURIFY_ARITH_TACTIC_H_
|
||||
#define _PURIFY_ARITH_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
||||
|
446
src/tactic/arith/recover_01_tactic.cpp
Normal file
446
src/tactic/arith/recover_01_tactic.cpp
Normal file
|
@ -0,0 +1,446 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
recover_01_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Recover 01 variables
|
||||
|
||||
Search for clauses of the form
|
||||
p or q or x = 0
|
||||
~p or q or x = k1
|
||||
p or ~q or x = k2
|
||||
~p or ~q or x = k1+k2
|
||||
|
||||
Then, replaces
|
||||
x with k1*y1 + k2*y2
|
||||
p with y1=1
|
||||
q with y2=1
|
||||
where y1 and y2 are fresh 01 variables
|
||||
|
||||
The clauses are also removed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"dec_ref_util.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class recover_01_tactic : public tactic {
|
||||
struct imp {
|
||||
typedef obj_map<func_decl, ptr_vector<app> > var2clauses;
|
||||
|
||||
ast_manager & m;
|
||||
var2clauses m_var2clauses;
|
||||
arith_util m_util;
|
||||
th_rewriter m_rw;
|
||||
bool m_produce_models;
|
||||
unsigned m_cls_max_size;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_util(m),
|
||||
m_rw(m, p) {
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
m_cls_max_size = p.get_uint(":recover-01-max-bits", 10);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
bool save_clause(expr * c) {
|
||||
if (!m.is_or(c))
|
||||
return false;
|
||||
func_decl * x = 0;
|
||||
app * cls = to_app(c);
|
||||
if (cls->get_num_args() <= 1 || cls->get_num_args() >= m_cls_max_size)
|
||||
return false;
|
||||
unsigned sz = cls->get_num_args();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * lit = cls->get_arg(i);
|
||||
expr * lhs, * rhs, * arg;
|
||||
if (is_uninterp_const(lit)) {
|
||||
// positive literal
|
||||
}
|
||||
else if (m.is_not(lit, arg) && is_uninterp_const(arg)) {
|
||||
// negative literal
|
||||
}
|
||||
else if (x == 0 && m.is_eq(lit, lhs, rhs)) {
|
||||
// x = k literal
|
||||
if (is_uninterp_const(lhs) && m_util.is_numeral(rhs)) {
|
||||
x = to_app(lhs)->get_decl();
|
||||
}
|
||||
else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs)) {
|
||||
x = to_app(rhs)->get_decl();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x != 0) {
|
||||
var2clauses::obj_map_entry * entry = m_var2clauses.insert_if_not_there2(x, ptr_vector<app>());
|
||||
if (entry->get_data().m_value.empty() || entry->get_data().m_value.back()->get_num_args() == cls->get_num_args()) {
|
||||
entry->get_data().m_value.push_back(cls);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// temporary fields used by operator() and process
|
||||
extension_model_converter * mc1;
|
||||
filter_model_converter * mc2;
|
||||
expr_substitution * subst;
|
||||
goal_ref new_goal;
|
||||
obj_map<expr, expr *> bool2int;
|
||||
|
||||
app * find_zero_cls(func_decl * x, ptr_vector<app> & clauses) {
|
||||
ptr_vector<app>::iterator it = clauses.begin();
|
||||
ptr_vector<app>::iterator end = clauses.end();
|
||||
for (; it != end; ++it) {
|
||||
app * cls = *it;
|
||||
unsigned num = cls->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * lhs, * rhs;
|
||||
if (m.is_eq(cls->get_arg(i), lhs, rhs)) {
|
||||
if (is_uninterp_const(lhs) && m_util.is_zero(rhs))
|
||||
return cls;
|
||||
if (is_uninterp_const(rhs) && m_util.is_zero(lhs))
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find coeff (the k of literal (x = k)) of clause cls.
|
||||
// Store in idx the bit-vector representing the literals.
|
||||
// Example: idx = 101 if cls has three boolean literals p1, p2, p3
|
||||
// where p1 = ~q1, p2 = q2, p3 = ~q3
|
||||
// and q1 q2 q3 are the corresponding literals in the
|
||||
// zero clause.
|
||||
// Return false, if the boolean literals of cls cannot be matched with the literals
|
||||
// of zero_cls
|
||||
bool find_coeff(app * cls, app * zero_cls, unsigned & idx, rational & k) {
|
||||
unsigned num = zero_cls->get_num_args();
|
||||
if (cls->get_num_args() != num)
|
||||
return false;
|
||||
idx = 0;
|
||||
unsigned val = 1;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * lit = zero_cls->get_arg(i);
|
||||
if (m.is_eq(lit))
|
||||
continue;
|
||||
// search for lit or ~lit in cls
|
||||
unsigned j;
|
||||
for (j = 0; j < num; j++) {
|
||||
expr * lit2 = cls->get_arg(j);
|
||||
if (m.is_eq(lit2))
|
||||
continue;
|
||||
if (lit2 == lit)
|
||||
break;
|
||||
if (m.is_complement(lit2, lit)) {
|
||||
idx += val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == num)
|
||||
return false; // cls does not contain literal lit
|
||||
val *= 2;
|
||||
}
|
||||
|
||||
// find k
|
||||
unsigned i;
|
||||
for (i = 0; i < num; i++) {
|
||||
expr * lhs, * rhs;
|
||||
if (m.is_eq(cls->get_arg(i), lhs, rhs) && (m_util.is_numeral(lhs, k) || m_util.is_numeral(rhs, k)))
|
||||
break;
|
||||
}
|
||||
if (i == num)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mk_ivar(expr * lit, expr_ref & def, bool real_ctx) {
|
||||
expr * atom;
|
||||
bool sign;
|
||||
if (m.is_not(lit, atom)) {
|
||||
sign = true;
|
||||
}
|
||||
else {
|
||||
atom = lit;
|
||||
sign = false;
|
||||
}
|
||||
SASSERT(is_uninterp_const(atom));
|
||||
expr * var;
|
||||
if (!bool2int.find(atom, var)) {
|
||||
var = m.mk_fresh_const(0, m_util.mk_int());
|
||||
new_goal->assert_expr(m_util.mk_le(m_util.mk_numeral(rational(0), true), var));
|
||||
new_goal->assert_expr(m_util.mk_le(var, m_util.mk_numeral(rational(1), true)));
|
||||
expr * bool_def = m.mk_eq(var, m_util.mk_numeral(rational(1), true));
|
||||
subst->insert(atom, bool_def);
|
||||
if (m_produce_models) {
|
||||
mc2->insert(to_app(var)->get_decl());
|
||||
mc1->insert(to_app(atom)->get_decl(), bool_def);
|
||||
}
|
||||
m.inc_ref(atom);
|
||||
m.inc_ref(var);
|
||||
bool2int.insert(atom, var);
|
||||
}
|
||||
expr * norm_var = real_ctx ? m_util.mk_to_real(var) : var;
|
||||
if (sign)
|
||||
def = m_util.mk_sub(m_util.mk_numeral(rational(1), !real_ctx), norm_var);
|
||||
else
|
||||
def = norm_var;
|
||||
}
|
||||
|
||||
bool process(func_decl * x, ptr_vector<app> & clauses) {
|
||||
unsigned cls_size = clauses.back()->get_num_args();
|
||||
unsigned expected_num_clauses = 1 << (cls_size - 1);
|
||||
if (clauses.size() < expected_num_clauses) // using < instead of != because we tolerate duplicates
|
||||
return false;
|
||||
app * zero_cls = find_zero_cls(x, clauses);
|
||||
if (zero_cls == 0)
|
||||
return false;
|
||||
|
||||
buffer<bool> found; // marks which idx were found
|
||||
buffer<rational> idx2coeff;
|
||||
found.resize(expected_num_clauses, false);
|
||||
idx2coeff.resize(expected_num_clauses);
|
||||
|
||||
ptr_vector<app>::iterator it = clauses.begin();
|
||||
ptr_vector<app>::iterator end = clauses.end();
|
||||
for (; it != end; ++it) {
|
||||
app * cls = *it;
|
||||
unsigned idx; rational k;
|
||||
if (!find_coeff(cls, zero_cls, idx, k))
|
||||
return false;
|
||||
SASSERT(idx < expected_num_clauses);
|
||||
if (found[idx] && k != idx2coeff[idx])
|
||||
return false;
|
||||
found[idx] = true;
|
||||
idx2coeff[idx] = k;
|
||||
}
|
||||
|
||||
unsigned num_bits = cls_size - 1;
|
||||
// check if idxs are consistent
|
||||
for (unsigned idx = 0; idx < expected_num_clauses; idx++) {
|
||||
if (!found[idx])
|
||||
return false; // case is missing
|
||||
rational expected_k;
|
||||
unsigned idx_aux = idx;
|
||||
unsigned idx_bit = 1;
|
||||
for (unsigned j = 0; j < num_bits; j++) {
|
||||
if (idx_aux % 2 == 1) {
|
||||
expected_k += idx2coeff[idx_bit];
|
||||
}
|
||||
idx_aux /= 2;
|
||||
idx_bit *= 2;
|
||||
}
|
||||
if (idx2coeff[idx] != expected_k)
|
||||
return false;
|
||||
}
|
||||
|
||||
expr_ref_buffer def_args(m);
|
||||
expr_ref def(m);
|
||||
bool real_ctx = m_util.is_real(x->get_range());
|
||||
unsigned idx_bit = 1;
|
||||
for (unsigned i = 0; i < cls_size; i++) {
|
||||
expr * lit = zero_cls->get_arg(i);
|
||||
if (m.is_eq(lit))
|
||||
continue;
|
||||
mk_ivar(lit, def, real_ctx);
|
||||
def_args.push_back(m_util.mk_mul(m_util.mk_numeral(idx2coeff[idx_bit], !real_ctx), def));
|
||||
idx_bit *= 2;
|
||||
}
|
||||
|
||||
expr * x_def;
|
||||
if (def_args.size() == 1)
|
||||
x_def = def_args[0];
|
||||
else
|
||||
x_def = m_util.mk_add(def_args.size(), def_args.c_ptr());
|
||||
|
||||
TRACE("recover_01", tout << x->get_name() << " --> " << mk_ismt2_pp(x_def, m) << "\n";);
|
||||
subst->insert(m.mk_const(x), x_def);
|
||||
if (m_produce_models) {
|
||||
mc1->insert(x, x_def);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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());
|
||||
fail_if_proof_generation("recover-01", g);
|
||||
fail_if_unsat_core_generation("recover-01", g);
|
||||
m_produce_models = g->models_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("recover-01", *g);
|
||||
|
||||
bool saved = false;
|
||||
new_goal = alloc(goal, *g, true);
|
||||
SASSERT(new_goal->depth() == g->depth());
|
||||
SASSERT(new_goal->prec() == g->prec());
|
||||
new_goal->inc_depth();
|
||||
|
||||
unsigned sz = g->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g->form(i);
|
||||
if (save_clause(f)) {
|
||||
saved = true;
|
||||
}
|
||||
else {
|
||||
new_goal->assert_expr(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!saved) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_produce_models) {
|
||||
mc1 = alloc(extension_model_converter, m);
|
||||
mc2 = alloc(filter_model_converter, m);
|
||||
mc = concat(mc2, mc1);
|
||||
}
|
||||
|
||||
dec_ref_key_values(m, bool2int);
|
||||
|
||||
unsigned counter = 0;
|
||||
bool recovered = false;
|
||||
expr_substitution _subst(m);
|
||||
subst = &_subst;
|
||||
var2clauses::iterator it = m_var2clauses.begin();
|
||||
var2clauses::iterator end = m_var2clauses.end();
|
||||
for (; it != end; ++it) {
|
||||
if (process(it->m_key, it->m_value)) {
|
||||
recovered = true;
|
||||
counter++;
|
||||
}
|
||||
else {
|
||||
ptr_vector<app>::iterator it2 = it->m_value.begin();
|
||||
ptr_vector<app>::iterator end2 = it->m_value.end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
new_goal->assert_expr(*it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!recovered) {
|
||||
result.push_back(g.get());
|
||||
mc = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
report_tactic_progress(":recovered-01-vars", counter);
|
||||
|
||||
m_rw.set_substitution(subst);
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = new_goal->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = new_goal->form(idx);
|
||||
m_rw(curr, new_curr);
|
||||
new_goal->update(idx, new_curr);
|
||||
}
|
||||
result.push_back(new_goal.get());
|
||||
TRACE("recover_01", new_goal->display(tout););
|
||||
SASSERT(new_goal->is_well_sorted());
|
||||
}
|
||||
|
||||
~imp() {
|
||||
dec_ref_key_values(m, bool2int);
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
recover_01_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(recover_01_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~recover_01_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
th_rewriter::get_param_descrs(r);
|
||||
r.insert(":recover-01-max-bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause.");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(g, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#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_recover_01_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(recover_01_tactic, m, p));
|
||||
}
|
42
src/tactic/arith/recover_01_tactic.h
Normal file
42
src/tactic/arith/recover_01_tactic.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
recover_01_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Recover 01 variables
|
||||
|
||||
Search for clauses of the form
|
||||
p or q or x = 0
|
||||
~p or q or x = k1
|
||||
p or ~q or x = k2
|
||||
~p or ~q or x = k1+k2
|
||||
|
||||
Then, replaces
|
||||
x with k1*y1 + k2*y2
|
||||
p with y1=1
|
||||
q with y2=1
|
||||
where y1 and y2 are fresh 01 variables
|
||||
|
||||
The clauses are also removed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _RECOVER_01_TACTIC_H_
|
||||
#define _RECOVER_01_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_recover_01_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue