3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 09:35:32 +00:00

Integrated structured branch into unstable branch (the official 'working in progress' branch)

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-24 13:19:19 -07:00
commit 3da69a4f1b
1502 changed files with 2524 additions and 5113 deletions

1812
src/tactic/aig/aig.cpp Normal file

File diff suppressed because it is too large Load diff

85
src/tactic/aig/aig.h Normal file
View file

@ -0,0 +1,85 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
aig.h
Abstract:
And-inverted graphs
Author:
Leonardo (leonardo) 2011-05-13
Notes:
--*/
#ifndef _AIG_H_
#define _AIG_H_
#include"ast.h"
#include"tactic_exception.h"
class assertion_set;
class goal;
class aig_lit;
class aig_manager;
class aig_exception : public tactic_exception {
public:
aig_exception(char const * msg):tactic_exception(msg) {}
};
class aig_ref {
friend class aig_lit;
friend class aig_manager;
aig_manager * m_manager;
void * m_ref;
aig_ref(aig_manager & m, aig_lit const & l);
public:
aig_ref();
~aig_ref();
aig_ref & operator=(aig_ref const & r);
bool operator==(aig_ref const & r) const { return m_ref == r.m_ref; }
bool operator!=(aig_ref const & r) const { return m_ref != r.m_ref; }
};
class aig_manager {
struct imp;
imp * m_imp;
friend class aig_ref;
public:
// If default_gate_encoding == true, then
// ite(a, b, c) is encoded as (NOT a OR b) AND (a OR c)
// iff(a, b) is encoded as (NOT a OR b) AND (a OR NOT b)
//
// If default_gate_encoding == false, then
// ite(a, b, c) is encoded as (a AND b) OR (NOT a AND c)
// iff(a, b) is encoded as (a AND b) OR (NOT a AND NOT b)
aig_manager(ast_manager & m, unsigned long long max_memory = UINT64_MAX, bool default_gate_encoding = true);
~aig_manager();
void set_max_memory(unsigned long long max);
aig_ref mk_aig(expr * n);
aig_ref mk_aig(assertion_set const & s); // TODO delete
aig_ref mk_aig(goal const & g);
aig_ref mk_not(aig_ref const & r);
aig_ref mk_and(aig_ref const & r1, aig_ref const & r2);
aig_ref mk_or(aig_ref const & r1, aig_ref const & r2);
aig_ref mk_iff(aig_ref const & r1, aig_ref const & r2);
aig_ref mk_ite(aig_ref const & r1, aig_ref const & r2, aig_ref const & r3);
void max_sharing(aig_ref & r);
void to_formula(aig_ref const & r, assertion_set & s); // TODO delete
void to_formula(aig_ref const & r, expr_ref & result);
void to_formula(aig_ref const & r, goal & result);
void to_cnf(aig_ref const & r, goal & result);
void display(std::ostream & out, aig_ref const & r) const;
void display_smt2(std::ostream & out, aig_ref const & r) const;
unsigned get_num_aigs() const;
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
void set_cancel(bool f);
};
#endif

View file

@ -0,0 +1,126 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
aig_tactic.cpp
Abstract:
Tactic for minimizing circuits using AIGs.
Author:
Leonardo (leonardo) 2011-10-24
Notes:
--*/
#include"tactical.h"
#include"aig.h"
class aig_manager;
class aig_tactic : public tactic {
unsigned long long m_max_memory;
bool m_aig_gate_encoding;
bool m_aig_per_assertion;
aig_manager * m_aig_manager;
struct mk_aig_manager {
aig_tactic & m_owner;
mk_aig_manager(aig_tactic & o, ast_manager & m):m_owner(o) {
aig_manager * mng = alloc(aig_manager, m, o.m_max_memory, o.m_aig_gate_encoding);
#pragma omp critical (aig_tactic)
{
m_owner.m_aig_manager = mng;
}
}
~mk_aig_manager() {
aig_manager * mng = m_owner.m_aig_manager;
#pragma omp critical (aig_tactic)
{
m_owner.m_aig_manager = 0;
}
dealloc(mng);
}
};
public:
aig_tactic(params_ref const & p = params_ref()):m_aig_manager(0) {
updt_params(p);
}
virtual tactic * translate(ast_manager & m) {
aig_tactic * t = alloc(aig_tactic);
t->m_max_memory = m_max_memory;
t->m_aig_gate_encoding = m_aig_gate_encoding;
t->m_aig_per_assertion = m_aig_per_assertion;
return t;
}
virtual void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_aig_gate_encoding = p.get_bool(":aig-default-gate-encoding", true);
m_aig_per_assertion = p.get_bool(":aig-per-assertion", true);
}
virtual void collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
r.insert(":aig-per-assertion", CPK_BOOL, "(default: true) process one assertion at a time.");
}
void operator()(goal_ref const & g) {
SASSERT(g->is_well_sorted());
tactic_report report("aig", *g);
mk_aig_manager mk(*this, g->m());
if (m_aig_per_assertion) {
unsigned size = g->size();
for (unsigned i = 0; i < size; i++) {
aig_ref r = m_aig_manager->mk_aig(g->form(i));
m_aig_manager->max_sharing(r);
expr_ref new_f(g->m());
m_aig_manager->to_formula(r, new_f);
g->update(i, new_f, 0, g->dep(i));
}
}
else {
fail_if_unsat_core_generation("aig", g);
aig_ref r = m_aig_manager->mk_aig(*(g.get()));
g->reset(); // save memory
m_aig_manager->max_sharing(r);
m_aig_manager->to_formula(r, *(g.get()));
}
SASSERT(g->is_well_sorted());
}
virtual void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
fail_if_proof_generation("aig", g);
mc = 0; pc = 0; core = 0;
operator()(g);
g->inc_depth();
result.push_back(g.get());
}
virtual void cleanup() {}
protected:
virtual void set_cancel(bool f) {
#pragma omp critical (aig_tactic)
{
if (m_aig_manager)
m_aig_manager->set_cancel(f);
}
}
};
tactic * mk_aig_tactic(params_ref const & p) {
return clean(alloc(aig_tactic, p));
}

View file

@ -0,0 +1,27 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
aig_tactic.h
Abstract:
Tactic for minimizing circuits using AIGs.
Author:
Leonardo (leonardo) 2011-10-24
Notes:
--*/
#ifndef _AIG_TACTIC_H_
#define _AIG_TACTIC_H_
#include"params.h"
class tactic;
tactic * mk_aig_tactic(params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,164 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
add_bounds.h
Abstract:
Strategy for bounding unbounded variables.
Author:
Leonardo de Moura (leonardo) 2011-06-30.
Revision History:
--*/
#include"add_bounds.h"
#include"arith_decl_plugin.h"
#include"ast_smt2_pp.h"
#include"bound_manager.h"
#include"for_each_expr.h"
#include"assertion_set_util.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(assertion_set const & s) {
ast_manager & m = s.m();
bound_manager bm(m);
bm(s);
is_unbounded_proc proc(bm);
return test(s, proc);
}
struct add_bounds::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;
assertion_set & m_set;
rational const & m_lower;
rational const & m_upper;
unsigned m_num_bounds;
add_bound_proc(bound_manager & bm, assertion_set & s, rational const & l, rational const & u):
m_util(bm.m()),
m_bm(bm),
m_set(s),
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_set.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_set.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*) {}
};
void operator()(assertion_set & s, model_converter_ref & mc) {
mc = 0;
if (s.inconsistent())
return;
as_st_report report("add-bounds", s);
bound_manager bm(m);
expr_fast_mark1 visited;
add_bound_proc proc(bm, s, m_lower, m_upper);
unsigned sz = s.size();
for (unsigned i = 0; i < sz; i++)
quick_for_each_expr(proc, visited, s.form(i));
visited.reset();
report_st_progress(":added-bounds", proc.m_num_bounds);
TRACE("add_bounds", s.display(tout););
}
};
add_bounds::add_bounds(ast_manager & m, params_ref const & p):
m_params(p) {
m_imp = alloc(imp, m, p);
}
add_bounds::~add_bounds() {
dealloc(m_imp);
}
void add_bounds::updt_params(params_ref const & p) {
m_params = p;
m_imp->updt_params(p);
}
void add_bounds::get_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.");
}
void add_bounds::operator()(assertion_set & s, model_converter_ref & mc) {
m_imp->operator()(s, mc);
}
void add_bounds::set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
void add_bounds::cleanup() {
ast_manager & m = m_imp->m;
imp * d = m_imp;
#pragma omp critical (as_st_cancel)
{
d = m_imp;
}
dealloc(d);
d = alloc(imp, m, m_params);
#pragma omp critical (as_st_cancel)
{
m_imp = d;
}
}

View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
add_bounds.h
Abstract:
Strategy for bounding unbounded variables.
Author:
Leonardo de Moura (leonardo) 2011-06-30.
Revision History:
--*/
#ifndef _ADD_BOUNDS_H_
#define _ADD_BOUNDS_H_
#include"assertion_set_strategy.h"
bool is_unbounded(assertion_set const & s);
class add_bounds : public assertion_set_strategy {
struct imp;
imp * m_imp;
params_ref m_params;
public:
add_bounds(ast_manager & m, params_ref const & p = params_ref());
virtual ~add_bounds();
virtual void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
virtual void operator()(assertion_set & s, model_converter_ref & mc);
virtual void cleanup();
protected:
virtual void set_cancel(bool f);
};
inline as_st * mk_add_bounds(ast_manager & m, params_ref const & p = params_ref()) {
return clean(alloc(add_bounds, m, p));
}
#endif

View 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));
}

View 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

View file

@ -0,0 +1,240 @@
/*++
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()(assertion_set const & s) {
unsigned sz = s.size();
for (unsigned i = 0; i < sz; i++) {
operator()(s.form(i), 0);
}
}
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";
}
}

View file

@ -0,0 +1,111 @@
/*++
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"assertion_set.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()(assertion_set const & s); // TODO: delete
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

View 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);
}

View 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

View 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>;

View 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

View 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>;

View 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

View 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)));
}

View 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

View 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));
}

View 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

View file

@ -0,0 +1,174 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
elim_term_ite_strategy.cpp
Abstract:
Eliminate term if-then-else by adding
new fresh auxiliary variables.
Author:
Leonardo (leonardo) 2011-06-15
Notes:
--*/
#include"elim_term_ite_strategy.h"
#include"defined_names.h"
#include"rewriter_def.h"
#include"filter_model_converter.h"
#include"cooperate.h"
struct elim_term_ite_strategy::imp {
struct rw_cfg : public default_rewriter_cfg {
ast_manager & m;
defined_names m_defined_names;
ref<filter_model_converter> m_mc;
assertion_set * m_set;
unsigned long long m_max_memory; // in bytes
bool m_produce_models;
unsigned m_num_fresh;
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("elim term ite");
if (memory::get_allocation_size() > m_max_memory)
throw elim_term_ite_exception(STE_MAX_MEMORY_MSG);
return false;
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
if (!m.is_term_ite(f))
return BR_FAILED;
expr_ref new_ite(m);
new_ite = m.mk_app(f, num, args);
expr_ref new_def(m);
proof_ref new_def_pr(m);
app_ref _result(m);
if (m_defined_names.mk_name(new_ite, new_def, new_def_pr, _result, result_pr)) {
m_set->assert_expr(new_def, new_def_pr);
m_num_fresh++;
if (m_produce_models) {
if (!m_mc)
m_mc = alloc(filter_model_converter, m);
m_mc->insert(_result->get_decl());
}
}
result = _result.get();
return BR_DONE;
}
rw_cfg(ast_manager & _m, params_ref const & p):
m(_m),
m_defined_names(m, 0 /* don't use prefix */) {
updt_params(p);
m_set = 0;
m_num_fresh = 0;
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_produce_models = p.get_bool(":produce-models", false);
}
};
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) {
}
};
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);
}
void updt_params(params_ref const & p) {
m_rw.cfg().updt_params(p);
}
void operator()(assertion_set & s, model_converter_ref & mc) {
mc = 0;
if (s.inconsistent())
return;
{
as_st_report report("elim-term-ite", s);
m_rw.m_cfg.m_num_fresh = 0;
m_rw.m_cfg.m_set = &s;
expr_ref new_curr(m);
proof_ref new_pr(m);
unsigned size = s.size();
for (unsigned idx = 0; idx < size; idx++) {
expr * curr = s.form(idx);
m_rw(curr, new_curr, new_pr);
if (m.proofs_enabled()) {
proof * pr = s.pr(idx);
new_pr = m.mk_modus_ponens(pr, new_pr);
}
s.update(idx, new_curr, new_pr);
}
mc = m_rw.m_cfg.m_mc.get();
}
report_st_progress(":elim-term-ite-consts", m_rw.m_cfg.m_num_fresh);
}
};
elim_term_ite_strategy::elim_term_ite_strategy(ast_manager & m, params_ref const & p):
m_params(p) {
m_imp = alloc(imp, m, p);
}
elim_term_ite_strategy::~elim_term_ite_strategy() {
dealloc(m_imp);
}
void elim_term_ite_strategy::updt_params(params_ref const & p) {
m_params = p;
m_imp->updt_params(p);
}
void elim_term_ite_strategy::get_param_descrs(param_descrs & r) {
insert_max_memory(r);
insert_produce_models(r);
}
void elim_term_ite_strategy::operator()(assertion_set & s, model_converter_ref & mc) {
m_imp->operator()(s, mc);
}
void elim_term_ite_strategy::set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
void elim_term_ite_strategy::cleanup() {
ast_manager & m = m_imp->m;
imp * d = m_imp;
#pragma omp critical (as_st_cancel)
{
d = m_imp;
}
dealloc(d);
d = alloc(imp, m, m_params);
#pragma omp critical (as_st_cancel)
{
m_imp = d;
}
}

View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
elim_term_ite_strategy.h
Abstract:
Eliminate term if-then-else by adding
new fresh auxiliary variables.
Author:
Leonardo (leonardo) 2011-06-15
Notes:
--*/
#ifndef _ELIM_TERM_ITE_STRATEGY_H_
#define _ELIM_TERM_ITE_STRATEGY_H_
#include"strategy_exception.h"
#include"model_converter.h"
#include"params.h"
#include"assertion_set_strategy.h"
class assertion_set;
MK_ST_EXCEPTION(elim_term_ite_exception);
class elim_term_ite_strategy : public assertion_set_strategy {
struct imp;
imp * m_imp;
params_ref m_params;
public:
elim_term_ite_strategy(ast_manager & m, params_ref const & p = params_ref());
virtual ~elim_term_ite_strategy();
virtual void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
virtual void operator()(assertion_set & s, model_converter_ref & mc);
virtual void cleanup();
virtual void set_cancel(bool f);
};
inline as_st * mk_elim_term_ite(ast_manager & m, params_ref const & p = params_ref()) {
return clean(alloc(elim_term_ite_strategy, m, p));
}
#endif

View 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));
}

View 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

View 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));
}

View 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

File diff suppressed because it is too large Load diff

View 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

View 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));
}

View 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

View 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);
}

View 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

File diff suppressed because it is too large Load diff

View 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

View 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));
}

View 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

View 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);
}

View 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

View 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));
}

View 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

View 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();
}

View 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

File diff suppressed because it is too large Load diff

View 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

View 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);
}

View 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

View 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;
}
}

View 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

View 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));
}

View 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

View 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));
}

View 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

File diff suppressed because it is too large Load diff

View 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

View 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);
}
};

View 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

View 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();
}
};

View 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

View file

@ -0,0 +1,96 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sat_solver_strategy.cpp
Abstract:
Strategy for using the SMT solver.
Author:
Leonardo (leonardo) 2011-06-25
Notes:
--*/
#include"smt_solver_strategy.h"
#include"smt_solver_exp.h"
smt_solver_strategy::smt_solver_strategy(ast_manager & _m, params_ref const & p):
m(_m),
m_params(p) {
}
smt_solver_strategy::~smt_solver_strategy() {
}
void smt_solver_strategy::init_solver() {
smt::solver_exp * new_solver = alloc(smt::solver_exp, m, m_params);
#pragma omp critical (as_st_cancel)
{
m_solver = new_solver;
}
}
void smt_solver_strategy::updt_params(params_ref const & p) {
m_params = p;
}
void smt_solver_strategy::get_param_descrs(param_descrs & r) {
// TODO
}
void smt_solver_strategy::operator()(assertion_set & s, model_converter_ref & mc) {
if (s.m().proofs_enabled())
throw smt_solver_exception("smt quick solver does not support proof generation");
mc = 0;
s.elim_redundancies();
if (s.inconsistent())
return;
init_solver();
m_solver->assert_set(s);
s.reset();
lbool r = m_solver->check();
m_solver->collect_statistics(m_stats);
if (r == l_false) {
s.assert_expr(m.mk_false());
}
else if (r == l_true) {
model_ref md;
m_solver->get_model(md);
mc = model2model_converter(md.get());
}
else {
// recover simplified assertion set
m_solver->get_assertions(s);
m_solver->get_model_converter(mc);
}
}
void smt_solver_strategy::cleanup() {
if (m_solver)
m_solver->collect_statistics(m_stats);
#pragma omp critical (as_st_cancel)
{
m_solver = 0;
}
}
void smt_solver_strategy::set_cancel(bool f) {
if (m_solver)
m_solver->set_cancel(f);
}
void smt_solver_strategy::reset_statistics() {
m_stats.reset();
}
void smt_solver_strategy::collect_statistics(statistics & st) const {
st.copy(m_stats);
}

View file

@ -0,0 +1,55 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
smt_solver_strategy.h
Abstract:
Strategy for using the SAT solver.
Author:
Leonardo (leonardo) 2011-06-25
Notes:
--*/
#ifndef _SMT_SOLVER_STRATEGY_H_
#define _SMT_SOLVER_STRATEGY_H_
#include"assertion_set_strategy.h"
namespace smt { class solver_exp; };
class smt_solver_strategy : public assertion_set_strategy {
struct imp;
ast_manager & m;
params_ref m_params;
statistics m_stats;
scoped_ptr<smt::solver_exp> m_solver;
void init_solver();
public:
smt_solver_strategy(ast_manager & m, params_ref const & p = params_ref());
virtual ~smt_solver_strategy();
virtual void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
virtual void operator()(assertion_set & s, model_converter_ref & mc);
virtual void cleanup();
virtual void collect_statistics(statistics & st) const;
virtual void reset_statistics();
protected:
virtual void set_cancel(bool f);
};
inline as_st * mk_smt2_solver(ast_manager & m, params_ref const & p = params_ref()) {
return clean(alloc(smt_solver_strategy, m, p));
}
#endif

View 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

View file

@ -0,0 +1,128 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bit_blaster.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-05.
Revision History:
--*/
#include"bit_blaster.h"
#include"bit_blaster_tpl_def.h"
#include"ast_pp.h"
#include"bv_decl_plugin.h"
bit_blaster_cfg::bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, basic_simplifier_plugin & _s):
m_util(u),
m_params(p),
s(_s) {
}
static void sort_args(expr * & l1, expr * & l2, expr * & l3) {
expr * args[3] = {l1, l2, l3};
// ast_lt_proc is based on the AST ids. So, it is a total order on AST nodes.
// No need for stable_sort
std::sort(args, args+3, ast_lt_proc());
l1 = args[0]; l2 = args[1]; l3 = args[2];
}
void bit_blaster_cfg::mk_xor3(expr * l1, expr * l2, expr * l3, expr_ref & r) {
TRACE("xor3", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
sort_args(l1, l2, l3);
TRACE("xor3_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
if (m_params.m_bb_ext_gates) {
if (l1 == l2)
r = l3;
else if (l1 == l3)
r = l2;
else if (l2 == l3)
r = l1;
else if (m().is_complement(l1, l2))
s.mk_not(l3, r);
else if (m().is_complement(l1, l3))
s.mk_not(l2, r);
else if (m().is_complement(l2, l3))
s.mk_not(l1, r);
else if (m().is_true(l1))
s.mk_iff(l2, l3, r);
else if (m().is_false(l1))
s.mk_xor(l2, l3, r);
else if (m().is_true(l2))
s.mk_iff(l1, l3, r);
else if (m().is_false(l2))
s.mk_xor(l1, l3, r);
else if (m().is_true(l3))
s.mk_iff(l1, l2, r);
else if (m().is_false(l3))
s.mk_xor(l1, l2, r);
else
r = m().mk_app(m_util.get_family_id(), OP_XOR3, l1, l2, l3);
}
else {
expr_ref t(m());
s.mk_xor(l1, l2, t);
s.mk_xor(t, l3, r);
}
}
void bit_blaster_cfg::mk_carry(expr * l1, expr * l2, expr * l3, expr_ref & r) {
TRACE("carry", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
sort_args(l1, l2, l3);
TRACE("carry_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
if (m_params.m_bb_ext_gates) {
if ((m().is_false(l1) && m().is_false(l2)) ||
(m().is_false(l1) && m().is_false(l3)) ||
(m().is_false(l2) && m().is_false(l3)))
r = m().mk_false();
else if ((m().is_true(l1) && m().is_true(l2)) ||
(m().is_true(l1) && m().is_true(l3)) ||
(m().is_true(l2) && m().is_true(l3)))
r = m().mk_true();
else if (l1 == l2 && l1 == l3)
r = l1;
else if (m().is_false(l1))
s.mk_and(l2, l3, r);
else if (m().is_false(l2))
s.mk_and(l1, l3, r);
else if (m().is_false(l3))
s.mk_and(l1, l2, r);
else if (m().is_true(l1))
s.mk_or(l2, l3, r);
else if (m().is_true(l2))
s.mk_or(l1, l3, r);
else if (m().is_true(l3))
s.mk_or(l1, l2, r);
else if (m().is_complement(l1, l2))
r = l3;
else if (m().is_complement(l1, l3))
r = l2;
else if (m().is_complement(l2, l3))
r = l1;
else
r = m().mk_app(m_util.get_family_id(), OP_CARRY, l1, l2, l3);
}
else {
expr_ref t1(m()), t2(m()), t3(m());
s.mk_and(l1, l2, t1);
s.mk_and(l1, l3, t2);
s.mk_and(l2, l3, t3);
s.mk_or(t1, t2, t3, r);
}
}
template class bit_blaster_tpl<bit_blaster_cfg>;
bit_blaster::bit_blaster(ast_manager & m, bit_blaster_params const & params):
bit_blaster_tpl<bit_blaster_cfg>(bit_blaster_cfg(m_util, params, m_simp)),
m_util(m),
m_simp(m) {
}

View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bit_blaster.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-05.
Revision History:
--*/
#ifndef _BIT_BLASTER_H_
#define _BIT_BLASTER_H_
#include"basic_simplifier_plugin.h"
#include"bit_blaster_params.h"
#include"bit_blaster_tpl.h"
#include"bv_decl_plugin.h"
#include"rational.h"
class bit_blaster_cfg {
public:
typedef rational numeral;
protected:
bv_util & m_util;
bit_blaster_params const & m_params;
basic_simplifier_plugin & s;
public:
bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, basic_simplifier_plugin & _s);
ast_manager & m() const { return m_util.get_manager(); }
numeral power(unsigned n) const { return m_util.power_of_two(n); }
void mk_xor(expr * a, expr * b, expr_ref & r) { s.mk_xor(a, b, r); }
void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r);
void mk_carry(expr * a, expr * b, expr * c, expr_ref & r);
void mk_iff(expr * a, expr * b, expr_ref & r) { s.mk_iff(a, b, r); }
void mk_and(expr * a, expr * b, expr_ref & r) { s.mk_and(a, b, r); }
void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { s.mk_and(a, b, c, r); }
void mk_and(unsigned sz, expr * const * args, expr_ref & r) { s.mk_and(sz, args, r); }
void mk_or(expr * a, expr * b, expr_ref & r) { s.mk_or(a, b, r); }
void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { s.mk_or(a, b, c, r); }
void mk_or(unsigned sz, expr * const * args, expr_ref & r) { s.mk_or(sz, args, r); }
void mk_not(expr * a, expr_ref & r) { s.mk_not(a, r); }
void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { s.mk_ite(c, t, e, r); }
void mk_nand(expr * a, expr * b, expr_ref & r) { s.mk_nand(a, b, r); }
void mk_nor(expr * a, expr * b, expr_ref & r) { s.mk_nor(a, b, r); }
};
class bit_blaster : public bit_blaster_tpl<bit_blaster_cfg> {
bv_util m_util;
basic_simplifier_plugin m_simp;
public:
bit_blaster(ast_manager & m, bit_blaster_params const & params);
bit_blaster_params const & get_params() const { return this->m_params; }
};
#endif /* _BIT_BLASTER_H_ */

View file

@ -0,0 +1,187 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_model_convert.cpp
Abstract:
Model converter for bit-blasting tactics.
Author:
Leonardo (leonardo) 2011-05-09
Notes:
--*/
#include"model.h"
#include"model_pp.h"
#include"model_converter.h"
#include"bv_decl_plugin.h"
#include"ast_smt2_pp.h"
/**
If TO_BOOL == true, then bit-vectors of size n were blasted into n-tuples of Booleans.
If TO_BOOL == false, then bit-vectors of size n were blasted into n-tuples of bit-vectors of size 1.
*/
template<bool TO_BOOL>
struct bit_blaster_model_converter : public model_converter {
func_decl_ref_vector m_vars;
expr_ref_vector m_bits;
ast_manager & m() const { return m_vars.get_manager(); }
bit_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits):m_vars(m), m_bits(m) {
obj_map<func_decl, expr*>::iterator it = const2bits.begin();
obj_map<func_decl, expr*>::iterator end = const2bits.end();
for (; it != end; ++it) {
func_decl * v = it->m_key;
expr * bits = it->m_value;
SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV));
SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT));
m_vars.push_back(v);
m_bits.push_back(bits);
}
}
virtual ~bit_blaster_model_converter() {
}
void collect_bits(obj_hashtable<func_decl> & bits) {
unsigned sz = m_bits.size();
for (unsigned i = 0; i < sz; i++) {
expr * bs = m_bits.get(i);
SASSERT(!TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_MKBV));
SASSERT(TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_CONCAT));
unsigned num_args = to_app(bs)->get_num_args();
for (unsigned j = 0; j < num_args; j++) {
expr * bit = to_app(bs)->get_arg(j);
SASSERT(!TO_BOOL || m().is_bool(bit));
SASSERT(TO_BOOL || is_sort_of(m().get_sort(bit), m().get_family_id("bv"), BV_SORT));
SASSERT(is_uninterp_const(bit));
bits.insert(to_app(bit)->get_decl());
}
}
TRACE("blaster_mc",
tout << "bits that should not be included in the model:\n";
obj_hashtable<func_decl>::iterator it = bits.begin();
obj_hashtable<func_decl>::iterator end = bits.end();
for (; it != end; ++it) {
tout << (*it)->get_name() << " ";
}
tout << "\n";);
}
void copy_non_bits(obj_hashtable<func_decl> & bits, model * old_model, model * new_model) {
unsigned num = old_model->get_num_constants();
for (unsigned i = 0; i < num; i++) {
func_decl * f = old_model->get_constant(i);
if (bits.contains(f))
continue;
TRACE("blaster_mc", tout << "non-bit: " << f->get_name() << "\n";);
expr * fi = old_model->get_const_interp(f);
new_model->register_decl(f, fi);
}
TRACE("blaster_mc", tout << "after copy non bits:\n"; model_pp(tout, *new_model););
new_model->copy_func_interps(*old_model);
new_model->copy_usort_interps(*old_model);
TRACE("blaster_mc", tout << "after copying functions and sorts:\n"; model_pp(tout, *new_model););
}
void mk_bvs(model * old_model, model * new_model) {
bv_util util(m());
rational val;
rational two(2);
SASSERT(m_vars.size() == m_bits.size());
unsigned sz = m_vars.size();
for (unsigned i = 0; i < sz; i++) {
expr * bs = m_bits.get(i);
val.reset();
unsigned bv_sz = to_app(bs)->get_num_args();
if (TO_BOOL) {
SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_MKBV));
unsigned j = bv_sz;
while (j > 0) {
--j;
val *= two;
expr * bit = to_app(bs)->get_arg(j);
SASSERT(m().is_bool(bit));
SASSERT(is_uninterp_const(bit));
func_decl * bit_decl = to_app(bit)->get_decl();
expr * bit_val = old_model->get_const_interp(bit_decl);
// remark: if old_model does not assign bit_val, then assume it is false.
if (bit_val != 0 && m().is_true(bit_val))
val++;
}
}
else {
SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_CONCAT));
for (unsigned j = 0; j < bv_sz; j++) {
val *= two;
expr * bit = to_app(bs)->get_arg(j);
SASSERT(util.is_bv(bit));
SASSERT(util.get_bv_size(bit) == 1);
SASSERT(is_uninterp_const(bit));
func_decl * bit_decl = to_app(bit)->get_decl();
expr * bit_val = old_model->get_const_interp(bit_decl);
// remark: if old_model does not assign bit_val, then assume it is false.
if (bit_val != 0 && !util.is_zero(bit_val))
val++;
}
}
expr * new_val = util.mk_numeral(val, bv_sz);
new_model->register_decl(m_vars.get(i), new_val);
}
}
virtual void operator()(model_ref & md, unsigned goal_idx) {
SASSERT(goal_idx == 0);
model * new_model = alloc(model, m());
obj_hashtable<func_decl> bits;
collect_bits(bits);
copy_non_bits(bits, md.get(), new_model);
mk_bvs(md.get(), new_model);
md = new_model;
}
virtual void operator()(model_ref & md) {
operator()(md, 0);
}
virtual void display(std::ostream & out) {
out << "(bit-blaster-model-converter";
unsigned sz = m_vars.size();
for (unsigned i = 0; i < sz; i++) {
out << "\n (" << m_vars.get(i)->get_name() << " ";
unsigned indent = m_vars.get(i)->get_name().size() + 4;
out << mk_ismt2_pp(m_bits.get(i), m(), indent) << ")";
}
out << ")" << std::endl;
}
protected:
bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m) { }
public:
virtual model_converter * translate(ast_translation & translator) {
bit_blaster_model_converter * res = alloc(bit_blaster_model_converter, translator.to());
for (unsigned i = 0; i < m_vars.size(); i++)
res->m_vars.push_back(translator(m_vars[i].get()));
for (unsigned i = 0; i < m_bits.size(); i++)
res->m_bits.push_back(translator(m_bits[i].get()));
return res;
}
};
model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits) {
return alloc(bit_blaster_model_converter<true>, m, const2bits);
}
model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits) {
return alloc(bit_blaster_model_converter<false>, m, const2bits);
}

View file

@ -0,0 +1,27 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_model_convert.h
Abstract:
Model converter for bit-blasting tactics.
Author:
Leonardo (leonardo) 2011-05-09
Notes:
--*/
#ifndef _BIT_BLASTER_MODEL_CONVERTER_H_
#define _BIT_BLASTER_MODEL_CONVERTER_H_
#include"model_converter.h"
model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits);
model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits);
#endif

View file

@ -0,0 +1,649 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_rewriter.cpp
Abstract:
Bit-blasting rewriter
Author:
Leonardo (leonardo) 2012-10-04
Notes:
--*/
#include"bit_blaster_rewriter.h"
#include"bv_decl_plugin.h"
#include"bit_blaster_tpl_def.h"
#include"rewriter_def.h"
#include"bool_rewriter.h"
#include"ref_util.h"
#include"ast_smt2_pp.h"
struct blaster_cfg {
typedef rational numeral;
bool_rewriter & m_rewriter;
bv_util & m_util;
blaster_cfg(bool_rewriter & r, bv_util & u):m_rewriter(r), m_util(u) {}
ast_manager & m() const { return m_util.get_manager(); }
numeral power(unsigned n) const { return m_util.power_of_two(n); }
void mk_xor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_xor(a, b, r); }
void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) {
expr_ref tmp(m());
mk_xor(b, c, tmp);
mk_xor(a, tmp, r);
}
void mk_iff(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_iff(a, b, r); }
void mk_and(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_and(a, b, r); }
void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_and(a, b, c, r); }
void mk_and(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_and(sz, args, r); }
void mk_or(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_or(a, b, r); }
void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_or(a, b, c, r); }
void mk_or(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_or(sz, args, r); }
void mk_not(expr * a, expr_ref & r) { m_rewriter.mk_not(a, r); }
void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) {
expr_ref t1(m()), t2(m()), t3(m());
#if 1
mk_and(a, b, t1);
mk_and(a, c, t2);
mk_and(b, c, t3);
mk_or(t1, t2, t3, r);
#else
mk_or(a, b, t1);
mk_or(a, c, t2);
mk_or(b, c, t3);
mk_and(t1, t2, t3, r);
#endif
}
void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { m_rewriter.mk_ite(c, t, e, r); }
void mk_nand(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nand(a, b, r); }
void mk_nor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nor(a, b, r); }
};
// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o
// template class bit_blaster_tpl<blaster_cfg>;
class blaster : public bit_blaster_tpl<blaster_cfg> {
bool_rewriter m_rewriter;
bv_util m_util;
public:
blaster(ast_manager & m):
bit_blaster_tpl<blaster_cfg>(blaster_cfg(m_rewriter, m_util)),
m_rewriter(m),
m_util(m) {
m_rewriter.set_flat(false);
m_rewriter.set_elim_and(true);
}
bv_util & butil() { return m_util; }
};
struct blaster_rewriter_cfg : public default_rewriter_cfg {
ast_manager & m_manager;
blaster & m_blaster;
expr_ref_vector m_in1;
expr_ref_vector m_in2;
expr_ref_vector m_out;
obj_map<func_decl, expr*> m_const2bits;
expr_ref_vector m_bindings;
bool m_blast_mul;
bool m_blast_add;
bool m_blast_quant;
bool m_blast_full;
unsigned long long m_max_memory;
unsigned m_max_steps;
ast_manager & m() const { return m_manager; }
bv_util & butil() { return m_blaster.butil(); }
void cleanup_buffers() {
m_in1.finalize();
m_in2.finalize();
m_out.finalize();
m_bindings.finalize();
}
blaster_rewriter_cfg(ast_manager & m, blaster & b, params_ref const & p):
m_manager(m),
m_blaster(b),
m_in1(m),
m_in2(m),
m_out(m),
m_bindings(m) {
updt_params(p);
}
~blaster_rewriter_cfg() {
dec_ref_map_key_values(m_manager, m_const2bits);
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
m_blast_add = p.get_bool(":blast-add", true);
m_blast_mul = p.get_bool(":blast-mul", true);
m_blast_full = p.get_bool(":blast-full", false);
m_blast_quant = p.get_bool(":blast-quant", false);
m_blaster.set_max_memory(m_max_memory);
}
bool rewrite_patterns() const { return true; }
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("bit blaster");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
return num_steps > m_max_steps;
}
void get_bits(expr * t, expr_ref_vector & out_bits) {
if (butil().is_mkbv(t)) {
out_bits.append(to_app(t)->get_num_args(), to_app(t)->get_args());
}
else {
unsigned bv_size = butil().get_bv_size(t);
for (unsigned i = 0; i < bv_size; i++) {
parameter p(i);
out_bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t));
}
SASSERT(bv_size == out_bits.size());
}
}
template<typename V>
app * mk_mkbv(V const & bits) {
return m().mk_app(butil().get_family_id(), OP_MKBV, bits.size(), bits.c_ptr());
}
void mk_const(func_decl * f, expr_ref & result) {
SASSERT(f->get_family_id() == null_family_id);
SASSERT(f->get_arity() == 0);
expr * r;
if (m_const2bits.find(f, r)) {
result = r;
return;
}
sort * s = f->get_range();
SASSERT(butil().is_bv_sort(s));
unsigned bv_size = butil().get_bv_size(s);
sort * b = m().mk_bool_sort();
m_out.reset();
for (unsigned i = 0; i < bv_size; i++) {
m_out.push_back(m().mk_fresh_const(0, b));
}
r = mk_mkbv(m_out);
m_const2bits.insert(f, r);
m_manager.inc_ref(f);
m_manager.inc_ref(r);
result = r;
}
#define MK_UNARY_REDUCE(OP, BB_OP) \
void OP(expr * arg, expr_ref & result) { \
m_in1.reset(); \
get_bits(arg, m_in1); \
m_out.reset(); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_out); \
result = mk_mkbv(m_out); \
}
MK_UNARY_REDUCE(reduce_not, mk_not);
MK_UNARY_REDUCE(reduce_redor, mk_redor);
MK_UNARY_REDUCE(reduce_redand, mk_redand);
#define MK_BIN_REDUCE(OP, BB_OP) \
void OP(expr * arg1, expr * arg2, expr_ref & result) { \
m_in1.reset(); m_in2.reset(); \
get_bits(arg1, m_in1); \
get_bits(arg2, m_in2); \
m_out.reset(); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out); \
result = mk_mkbv(m_out); \
}
MK_BIN_REDUCE(reduce_shl, mk_shl);
MK_BIN_REDUCE(reduce_ashr, mk_ashr);
MK_BIN_REDUCE(reduce_lshr, mk_lshr);
MK_BIN_REDUCE(reduce_udiv, mk_udiv);
MK_BIN_REDUCE(reduce_urem, mk_urem);
MK_BIN_REDUCE(reduce_sdiv, mk_sdiv);
MK_BIN_REDUCE(reduce_srem, mk_srem);
MK_BIN_REDUCE(reduce_smod, mk_smod);
MK_BIN_REDUCE(reduce_ext_rotate_left, mk_ext_rotate_left);
MK_BIN_REDUCE(reduce_ext_rotate_right, mk_ext_rotate_right);
#define MK_BIN_AC_REDUCE(OP, BIN_OP, BB_OP) \
MK_BIN_REDUCE(BIN_OP, BB_OP); \
void OP(unsigned num_args, expr * const * args, expr_ref & result) { \
SASSERT(num_args > 0); \
result = args[0]; \
expr_ref new_result(m_manager); \
for (unsigned i = 1; i < num_args; i++) { \
BIN_OP(result.get(), args[i], new_result); \
result = new_result; \
} \
}
MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder);
MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier);
MK_BIN_AC_REDUCE(reduce_or, reduce_bin_or, mk_or);
MK_BIN_AC_REDUCE(reduce_xor, reduce_bin_xor, mk_xor);
#define MK_BIN_PRED_REDUCE(OP, BB_OP) \
void OP(expr * arg1, expr * arg2, expr_ref & result) { \
m_in1.reset(); m_in2.reset(); \
get_bits(arg1, m_in1); \
get_bits(arg2, m_in2); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), result); \
}
MK_BIN_PRED_REDUCE(reduce_eq, mk_eq);
MK_BIN_PRED_REDUCE(reduce_sle, mk_sle);
MK_BIN_PRED_REDUCE(reduce_ule, mk_ule);
MK_BIN_PRED_REDUCE(reduce_umul_no_overflow, mk_umul_no_overflow);
MK_BIN_PRED_REDUCE(reduce_smul_no_overflow, mk_smul_no_overflow);
MK_BIN_PRED_REDUCE(reduce_smul_no_underflow, mk_smul_no_underflow);
#define MK_PARAMETRIC_UNARY_REDUCE(OP, BB_OP) \
void OP(expr * arg, unsigned n, expr_ref & result) { \
m_in1.reset(); \
get_bits(arg, m_in1); \
m_out.reset(); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), n, m_out); \
result = mk_mkbv(m_out); \
}
MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
void reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
m_in1.reset();
m_in2.reset();
get_bits(arg2, m_in1);
get_bits(arg3, m_in2);
m_out.reset();
m_blaster.mk_multiplexer(arg1, m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out);
result = mk_mkbv(m_out);
}
void reduce_concat(unsigned num_args, expr * const * args, expr_ref & result) {
m_out.reset();
unsigned i = num_args;
while (i > 0) {
i--;
m_in1.reset();
get_bits(args[i], m_in1);
m_out.append(m_in1.size(), m_in1.c_ptr());
}
result = mk_mkbv(m_out);
}
void reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result) {
m_in1.reset();
get_bits(arg, m_in1);
m_out.reset();
for (unsigned i = start; i <= end; ++i)
m_out.push_back(m_in1.get(i));
result = mk_mkbv(m_out);
}
void reduce_num(func_decl * f, expr_ref & result) {
SASSERT(f->get_num_parameters() == 2);
SASSERT(f->get_parameter(0).is_rational());
SASSERT(f->get_parameter(1).is_int());
rational v = f->get_parameter(0).get_rational();
unsigned bv_sz = f->get_parameter(1).get_int();
m_out.reset();
m_blaster.num2bits(v, bv_sz, m_out);
result = mk_mkbv(m_out);
}
void throw_unsupported() {
throw tactic_exception("operator is not supported, you must simplify the goal before applying bit-blasting");
}
void blast_bv_term(expr * t, expr_ref & result, proof_ref & result_pr) {
ptr_buffer<expr> bits;
unsigned bv_size = butil().get_bv_size(t);
for (unsigned i = 0; i < bv_size; i++) {
parameter p(i);
bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t));
}
result = mk_mkbv(bits);
result_pr = 0;
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = 0;
if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) {
mk_const(f, result);
return BR_DONE;
}
if (m().is_eq(f)) {
SASSERT(num == 2);
if (butil().is_bv(args[0])) {
reduce_eq(args[0], args[1], result);
return BR_DONE;
}
return BR_FAILED;
}
if (m().is_ite(f)) {
SASSERT(num == 3);
if (butil().is_bv(args[1])) {
reduce_ite(args[0], args[1], args[2], result);
return BR_DONE;
}
return BR_FAILED;
}
if (f->get_family_id() == butil().get_family_id()) {
switch (f->get_decl_kind()) {
case OP_BV_NUM:
SASSERT(num == 0);
reduce_num(f, result);
return BR_DONE;
case OP_BADD:
if (!m_blast_add)
return BR_FAILED;
reduce_add(num, args, result);
return BR_DONE;
case OP_BMUL:
if (!m_blast_mul)
return BR_FAILED;
reduce_mul(num, args, result);
return BR_DONE;
case OP_BSDIV:
case OP_BUDIV:
case OP_BSREM:
case OP_BUREM:
case OP_BSMOD:
if (m_blast_mul)
throw_unsupported(); // must simplify to DIV_I AND DIV0
return BR_FAILED; // keep them
case OP_BSDIV0:
case OP_BUDIV0:
case OP_BSREM0:
case OP_BUREM0:
case OP_BSMOD0:
return BR_FAILED;
case OP_BSDIV_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_sdiv(args[0], args[1], result);
return BR_DONE;
case OP_BUDIV_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_udiv(args[0], args[1], result);
return BR_DONE;
case OP_BSREM_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_srem(args[0], args[1], result);
return BR_DONE;
case OP_BUREM_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_urem(args[0], args[1], result);
return BR_DONE;
case OP_BSMOD_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_smod(args[0], args[1], result);
return BR_DONE;
case OP_ULEQ:
SASSERT(num == 2);
reduce_ule(args[0], args[1], result);
return BR_DONE;
case OP_SLEQ:
SASSERT(num == 2);
reduce_sle(args[0], args[1], result);
return BR_DONE;
case OP_BOR:
reduce_or(num, args, result);
return BR_DONE;
case OP_BNOT:
SASSERT(num == 1);
reduce_not(args[0], result);
return BR_DONE;
case OP_BXOR:
reduce_xor(num, args, result);
return BR_DONE;
case OP_CONCAT:
reduce_concat(num, args, result);
return BR_DONE;
case OP_SIGN_EXT:
SASSERT(num == 1);
reduce_sign_extend(args[0], f->get_parameter(0).get_int(), result);
return BR_DONE;
case OP_EXTRACT:
SASSERT(num == 1);
reduce_extract(f->get_parameter(1).get_int(), f->get_parameter(0).get_int(), args[0], result);
return BR_DONE;
case OP_BREDOR:
SASSERT(num == 1);
reduce_redor(args[0], result);
return BR_DONE;
case OP_BREDAND:
SASSERT(num == 1);
reduce_redand(args[0], result);
return BR_DONE;
case OP_BSHL:
SASSERT(num == 2);
reduce_shl(args[0], args[1], result);
return BR_DONE;
case OP_BLSHR:
SASSERT(num == 2);
reduce_lshr(args[0], args[1], result);
return BR_DONE;
case OP_BASHR:
SASSERT(num == 2);
reduce_ashr(args[0], args[1], result);
return BR_DONE;
case OP_EXT_ROTATE_LEFT:
SASSERT(num == 2);
reduce_ext_rotate_left(args[0], args[1], result);
return BR_DONE;
case OP_EXT_ROTATE_RIGHT:
SASSERT(num == 2);
reduce_ext_rotate_right(args[0], args[1], result);
return BR_DONE;
case OP_BUMUL_NO_OVFL:
SASSERT(num == 2);
reduce_umul_no_overflow(args[0], args[1], result);
return BR_DONE;
case OP_BSMUL_NO_OVFL:
SASSERT(num == 2);
reduce_smul_no_overflow(args[0], args[1], result);
return BR_DONE;
case OP_BSMUL_NO_UDFL:
SASSERT(num == 2);
reduce_smul_no_underflow(args[0], args[1], result);
return BR_DONE;
case OP_BIT2BOOL:
case OP_MKBV:
case OP_INT2BV:
case OP_BV2INT:
return BR_FAILED;
default:
TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n";
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;);
throw_unsupported();
}
}
if (m_blast_full && butil().is_bv_sort(f->get_range())) {
blast_bv_term(m().mk_app(f, num, args), result, result_pr);
return BR_DONE;
}
return BR_FAILED;
}
bool pre_visit(expr * t) {
if (m_blast_quant && is_quantifier(t)) {
quantifier * q = to_quantifier(t);
ptr_buffer<expr> new_bindings;
ptr_buffer<expr> new_args;
unsigned i = q->get_num_decls();
unsigned j = 0;
while (i > 0) {
--i;
sort * s = q->get_decl_sort(i);
if (butil().is_bv_sort(s)) {
unsigned bv_size = butil().get_bv_size(s);
new_args.reset();
for (unsigned k = 0; k < bv_size; k++) {
new_args.push_back(m().mk_var(j, m().mk_bool_sort()));
j++;
}
new_bindings.push_back(mk_mkbv(new_args));
}
else {
new_bindings.push_back(m().mk_var(j, s));
j++;
}
}
SASSERT(new_bindings.size() == q->get_num_decls());
i = q->get_num_decls();
while (i > 0) {
i--;
m_bindings.push_back(new_bindings[i]);
}
}
return true;
}
bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) {
if (m_blast_quant) {
if (t->get_idx() >= m_bindings.size())
return false;
result = m_bindings.get(m_bindings.size() - t->get_idx() - 1);
result_pr = 0;
return true;
}
if (m_blast_full && butil().is_bv(t)) {
blast_bv_term(t, result, result_pr);
return true;
}
return false;
}
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
if (!m_blast_quant)
return false;
unsigned curr_sz = m_bindings.size();
SASSERT(old_q->get_num_decls() <= curr_sz);
unsigned num_decls = old_q->get_num_decls();
unsigned old_sz = curr_sz - num_decls;
string_buffer<> name_buffer;
ptr_buffer<sort> new_decl_sorts;
sbuffer<symbol> new_decl_names;
for (unsigned i = 0; i < num_decls; i++) {
symbol const & n = old_q->get_decl_name(i);
sort * s = old_q->get_decl_sort(i);
if (butil().is_bv_sort(s)) {
unsigned bv_size = butil().get_bv_size(s);
for (unsigned j = 0; j < bv_size; j++) {
name_buffer.reset();
name_buffer << n << "." << j;
new_decl_names.push_back(symbol(name_buffer.c_str()));
new_decl_sorts.push_back(m().mk_bool_sort());
}
}
else {
new_decl_sorts.push_back(s);
new_decl_names.push_back(n);
}
}
result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(),
new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(),
old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns);
result_pr = 0;
m_bindings.shrink(old_sz);
return true;
}
};
// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o
// template class rewriter_tpl<blaster_rewriter_cfg>;
struct bit_blaster_rewriter::imp : public rewriter_tpl<blaster_rewriter_cfg> {
blaster m_blaster;
blaster_rewriter_cfg m_cfg;
imp(ast_manager & m, params_ref const & p):
rewriter_tpl<blaster_rewriter_cfg>(m,
m.proofs_enabled(),
m_cfg),
m_blaster(m),
m_cfg(m, m_blaster, p) {
SASSERT(m_blaster.butil().get_family_id() == m.get_family_id("bv"));
}
};
bit_blaster_rewriter::bit_blaster_rewriter(ast_manager & m, params_ref const & p):
m_imp(alloc(imp, m, p)) {
}
bit_blaster_rewriter::~bit_blaster_rewriter() {
dealloc(m_imp);
}
void bit_blaster_rewriter::updt_params(params_ref const& p) {
m_imp->m_cfg.updt_params(p);
}
void bit_blaster_rewriter::set_cancel(bool f) {
m_imp->set_cancel(f);
m_imp->m_blaster.set_cancel(f);
}
ast_manager & bit_blaster_rewriter::m() const {
return m_imp->m();
}
unsigned bit_blaster_rewriter::get_num_steps() const {
return m_imp->get_num_steps();
}
void bit_blaster_rewriter::cleanup() {
m_imp->cleanup();
}
obj_map<func_decl, expr*> const & bit_blaster_rewriter::const2bits() const {
return m_imp->m_cfg.m_const2bits;
}
void bit_blaster_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) {
m_imp->operator()(e, result, result_proof);
}

View file

@ -0,0 +1,42 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_rewriter.h
Abstract:
Bit-blasting rewriter
Author:
Leonardo (leonardo) 2012-10-04
Notes:
--*/
#ifndef _BIT_BLASTER_REWRITER_H_
#define _BIT_BLASTER_REWRITER_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"params.h"
class bit_blaster_rewriter {
struct imp;
imp * m_imp;
public:
bit_blaster_rewriter(ast_manager & m, params_ref const & p);
~bit_blaster_rewriter();
void updt_params(params_ref const & p);
void set_cancel(bool f);
ast_manager & m() const;
unsigned get_num_steps() const;
void cleanup();
obj_map<func_decl, expr*> const& const2bits() const;
void operator()(expr * e, expr_ref & result, proof_ref & result_proof);
};
#endif

View file

@ -0,0 +1,135 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bit_blaster_tpl.h
Abstract:
Template for bit-blaster operations
Author:
Leonardo de Moura (leonardo) 2011-05-02.
Revision History:
--*/
#ifndef _BIT_BLASTER_TPL_H_
#define _BIT_BLASTER_TPL_H_
#include"rational.h"
#include"strategy_exception.h"
template<typename Cfg>
class bit_blaster_tpl : public Cfg {
public:
typedef rational numeral;
protected:
template<bool Signed>
void mk_le(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
template<unsigned k>
void mk_sdiv_srem_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
template<bool Left>
void mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
unsigned long long m_max_memory;
volatile bool m_cancel;
bool m_use_wtm; /* Wallace Tree Multiplier */
bool m_use_bcm; /* Booth Multiplier for constants */
void checkpoint();
public:
bit_blaster_tpl(Cfg const & cfg = Cfg(), unsigned long long max_memory = UINT64_MAX, bool use_wtm = false, bool use_bcm=false):
Cfg(cfg),
m_max_memory(max_memory),
m_cancel(false),
m_use_wtm(use_wtm),
m_use_bcm(use_bcm) {
}
void set_max_memory(unsigned long long max_memory) {
m_max_memory = max_memory;
}
void set_cancel(bool f) { m_cancel = f; }
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
// Cfg required API
ast_manager & m() const { return Cfg::m(); }
numeral power(unsigned n) const { return Cfg::power(n); }
void mk_xor(expr * a, expr * b, expr_ref & r) { Cfg::mk_xor(a, b, r); }
void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_xor3(a, b, c, r); }
void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_carry(a, b, c, r); }
void mk_iff(expr * a, expr * b, expr_ref & r) { Cfg::mk_iff(a, b, r); }
void mk_and(expr * a, expr * b, expr_ref & r) { Cfg::mk_and(a, b, r); }
void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_and(a, b, c, r); }
void mk_and(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_and(sz, args, r); }
void mk_or(expr * a, expr * b, expr_ref & r) { Cfg::mk_or(a, b, r); }
void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_or(a, b, c, r); }
void mk_or(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_or(sz, args, r); }
void mk_not(expr * a, expr_ref & r) { Cfg::mk_not(a, r); }
void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { Cfg::mk_ite(c, t, e, r); }
void mk_nand(expr * a, expr * b, expr_ref & r) { Cfg::mk_nand(a, b, r); }
void mk_nor(expr * a, expr * b, expr_ref & r) { Cfg::mk_nor(a, b, r); }
//
bool is_numeral(unsigned sz, expr * const * bits) const;
bool is_numeral(unsigned sz, expr * const * bits, numeral & r) const;
bool is_minus_one(unsigned sz, expr * const * bits) const;
void num2bits(numeral const & v, unsigned sz, expr_ref_vector & out_bits) const;
void mk_half_adder(expr * a, expr * b, expr_ref & out, expr_ref & cout);
void mk_full_adder(expr * a, expr * b, expr * cin, expr_ref & out, expr_ref & cout);
void mk_neg(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_subtracter(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits, expr_ref & cout);
void mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_udiv_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits, expr_ref_vector & r_bits);
void mk_udiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits);
void mk_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & r_bits);
void mk_multiplexer(expr * c, unsigned sz, expr * const * t_bits, expr * const * e_bits, expr_ref_vector & out_bits);
void mk_sdiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_srem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_rotate_right(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_ext_rotate_left(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_ext_rotate_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_sign_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_zero_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_is_eq(unsigned sz, expr * const * a_bits, unsigned n, expr_ref & out);
void mk_eqs(unsigned sz, expr * const * a_bits, expr_ref_vector & eqs);
void mk_shl(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_lshr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_ashr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_sle(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_ule(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_not(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_and(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_or(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_xor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_xnor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_nand(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_nor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_redand(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_redor(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_smul_no_overflow_core(unsigned sz, expr * const * a_bits, expr * const * b_bits, bool is_overflow, expr_ref & result);
void mk_smul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_smul_no_underflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits);
void mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,508 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bv1_blaster_tactic.cpp
Abstract:
Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1.
This rewriter only supports concat and extract operators.
This transformation is useful for handling benchmarks that contain
many BV equalities.
Remark: other operators can be mapped into concat/extract by using
the simplifiers.
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#include"tactical.h"
#include"bit_blaster_model_converter.h"
#include"bv_decl_plugin.h"
#include"rewriter_def.h"
#include"for_each_expr.h"
#include"cooperate.h"
class bv1_blaster_tactic : public tactic {
struct rw_cfg : public default_rewriter_cfg {
ast_manager & m_manager;
bv_util m_util;
obj_map<func_decl, expr*> m_const2bits;
expr_ref_vector m_saved;
expr_ref m_bit1;
expr_ref m_bit0;
unsigned long long m_max_memory; // in bytes
unsigned m_max_steps;
bool m_produce_models;
ast_manager & m() const { return m_manager; }
bv_util & butil() { return m_util; }
bv_util const & butil() const { return m_util; }
void cleanup_buffers() {
m_saved.finalize();
}
rw_cfg(ast_manager & m, params_ref const & p):
m_manager(m),
m_util(m),
m_saved(m),
m_bit1(m),
m_bit0(m) {
m_bit1 = butil().mk_numeral(rational(1), 1);
m_bit0 = butil().mk_numeral(rational(0), 1);
updt_params(p);
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
m_produce_models = p.get_bool(":produce-models", false);
}
bool rewrite_patterns() const { UNREACHABLE(); return false; }
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("bv1 blaster");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
return num_steps > m_max_steps;
}
typedef ptr_buffer<expr, 128> bit_buffer;
void get_bits(expr * arg, bit_buffer & bits) {
SASSERT(butil().is_concat(arg) || butil().get_bv_size(arg) == 1);
if (butil().is_concat(arg))
bits.append(to_app(arg)->get_num_args(), to_app(arg)->get_args());
else
bits.push_back(arg);
}
void mk_const(func_decl * f, expr_ref & result) {
SASSERT(f->get_family_id() == null_family_id);
SASSERT(f->get_arity() == 0);
expr * r;
if (m_const2bits.find(f, r)) {
result = r;
return;
}
sort * s = f->get_range();
SASSERT(butil().is_bv_sort(s));
unsigned bv_size = butil().get_bv_size(s);
if (bv_size == 1) {
result = m().mk_const(f);
return;
}
sort * b = butil().mk_sort(1);
ptr_buffer<expr> bits;
for (unsigned i = 0; i < bv_size; i++) {
bits.push_back(m().mk_fresh_const(0, b));
}
r = butil().mk_concat(bits.size(), bits.c_ptr());
m_saved.push_back(r);
m_const2bits.insert(f, r);
result = r;
}
void blast_bv_term(expr * t, expr_ref & result) {
bit_buffer bits;
unsigned bv_size = butil().get_bv_size(t);
if (bv_size == 1) {
result = t;
return;
}
unsigned i = bv_size;
while (i > 0) {
--i;
bits.push_back(butil().mk_extract(i, i, t));
}
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_eq(expr * arg1, expr * arg2, expr_ref & result) {
bit_buffer bits1;
bit_buffer bits2;
get_bits(arg1, bits1);
get_bits(arg2, bits2);
SASSERT(bits1.size() == bits2.size());
bit_buffer new_eqs;
unsigned i = bits1.size();
while (i > 0) {
--i;
new_eqs.push_back(m().mk_eq(bits1[i], bits2[i]));
}
result = m().mk_and(new_eqs.size(), new_eqs.c_ptr());
}
void reduce_ite(expr * c, expr * t, expr * e, expr_ref & result) {
bit_buffer t_bits;
bit_buffer e_bits;
get_bits(t, t_bits);
get_bits(e, e_bits);
SASSERT(t_bits.size() == e_bits.size());
bit_buffer new_ites;
unsigned num = t_bits.size();
for (unsigned i = 0; i < num; i++)
new_ites.push_back(m().mk_ite(c, t_bits[i], e_bits[i]));
result = butil().mk_concat(new_ites.size(), new_ites.c_ptr());
}
void reduce_num(func_decl * f, expr_ref & result) {
SASSERT(f->get_num_parameters() == 2);
SASSERT(f->get_parameter(0).is_rational());
SASSERT(f->get_parameter(1).is_int());
bit_buffer bits;
rational v = f->get_parameter(0).get_rational();
rational two(2);
unsigned sz = f->get_parameter(1).get_int();
for (unsigned i = 0; i < sz; i++) {
if ((v % two).is_zero())
bits.push_back(m_bit0);
else
bits.push_back(m_bit1);
v = div(v, two);
}
std::reverse(bits.begin(), bits.end());
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_extract(func_decl * f, expr * arg, expr_ref & result) {
bit_buffer arg_bits;
get_bits(arg, arg_bits);
SASSERT(arg_bits.size() == butil().get_bv_size(arg));
unsigned high = butil().get_extract_high(f);
unsigned low = butil().get_extract_low(f);
unsigned sz = arg_bits.size();
unsigned start = sz - 1 - high;
unsigned end = sz - 1 - low;
bit_buffer bits;
for (unsigned i = start; i <= end; i++) {
bits.push_back(arg_bits[i]);
}
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_concat(unsigned num, expr * const * args, expr_ref & result) {
bit_buffer bits;
bit_buffer arg_bits;
for (unsigned i = 0; i < num; i++) {
expr * arg = args[i];
arg_bits.reset();
get_bits(arg, arg_bits);
bits.append(arg_bits.size(), arg_bits.c_ptr());
}
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_bin_xor(expr * arg1, expr * arg2, expr_ref & result) {
bit_buffer bits1;
bit_buffer bits2;
get_bits(arg1, bits1);
get_bits(arg2, bits2);
SASSERT(bits1.size() == bits2.size());
bit_buffer new_bits;
unsigned num = bits1.size();
for (unsigned i = 0; i < num; i++) {
new_bits.push_back(m().mk_ite(m().mk_eq(bits1[i], bits2[i]), m_bit0, m_bit1));
}
result = butil().mk_concat(new_bits.size(), new_bits.c_ptr());
}
void reduce_xor(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
#if 1
if (num_args == 1) {
result = args[0];
return;
}
reduce_bin_xor(args[0], args[1], result);
for (unsigned i = 2; i < num_args; i++) {
reduce_bin_xor(result, args[i], result);
}
#else
ptr_buffer<bit_buffer> args_bits;
for (unsigned i = 0; i < num_args; i++) {
bit_buffer * buff_i = alloc(bit_buffer);
get_bits(args[i], *buff_i);
args_bits.push_back(buff_i);
}
bit_buffer new_bits;
unsigned sz = butil().get_bv_size(args[0]);
for (unsigned i = 0; i < sz; i++) {
ptr_buffer<expr> eqs;
for (unsigned j = 0; j < num_args; j++) {
bit_buffer * buff_j = args_bits[j];
eqs.push_back(m().mk_eq(buff_j->get(i), m_bit1));
}
expr * cond = m().mk_xor(eqs.size(), eqs.c_ptr());
new_bits.push_back(m().mk_ite(cond, m_bit1, m_bit0));
}
result = butil().mk_concat(new_bits.size(), new_bits.c_ptr());
std::for_each(args_bits.begin(), args_bits.end(), delete_proc<bit_buffer>());
#endif
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = 0;
if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) {
mk_const(f, result);
return BR_DONE;
}
if (m().is_eq(f)) {
SASSERT(num == 2);
if (butil().is_bv(args[0])) {
reduce_eq(args[0], args[1], result);
return BR_DONE;
}
return BR_FAILED;
}
if (m().is_ite(f)) {
SASSERT(num == 3);
if (butil().is_bv(args[1])) {
reduce_ite(args[0], args[1], args[2], result);
return BR_DONE;
}
return BR_FAILED;
}
if (f->get_family_id() == butil().get_family_id()) {
switch (f->get_decl_kind()) {
case OP_BV_NUM:
reduce_num(f, result);
return BR_DONE;
case OP_EXTRACT:
SASSERT(num == 1);
reduce_extract(f, args[0], result);
return BR_DONE;
case OP_CONCAT:
reduce_concat(num, args, result);
return BR_DONE;
case OP_BXOR:
reduce_xor(num, args, result);
return BR_DONE;
default:
UNREACHABLE();
return BR_FAILED;
}
}
if (butil().is_bv_sort(f->get_range())) {
blast_bv_term(m().mk_app(f, num, args), result);
return BR_DONE;
}
return BR_FAILED;
}
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
UNREACHABLE();
return false;
}
};
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 {
rw m_rw;
unsigned m_num_steps;
imp(ast_manager & m, params_ref const & p):
m_rw(m, p) {
}
struct not_target {};
struct visitor {
family_id m_bv_fid;
visitor(family_id bv_fid):m_bv_fid(bv_fid) {}
void operator()(var const * n) { throw not_target(); }
void operator()(app const * n) {
if (n->get_family_id() == m_bv_fid) {
switch (n->get_decl_kind()) {
case OP_BV_NUM:
case OP_EXTRACT:
case OP_CONCAT:
return;
case OP_BXOR:
// it doesn't payoff to do the reduction in this case.
throw not_target();
default:
throw not_target();
}
}
}
void operator()(quantifier const * n) { throw not_target(); }
};
bool is_target(goal const & g) const {
expr_fast_mark1 visited;
unsigned sz = g.size();
visitor proc(m_rw.cfg().butil().get_family_id());
try {
for (unsigned i = 0; i < sz; i++) {
expr * f = g.form(i);
for_each_expr_core<visitor, expr_fast_mark1, false, true>(proc, visited, f);
}
}
catch (not_target) {
return false;
}
return true;
}
ast_manager & m() const { return m_rw.m(); }
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) {
mc = 0; pc = 0; core = 0;
if (!is_target(*g))
throw tactic_exception("bv1 blaster cannot be applied to goal");
tactic_report report("bv1-blaster", *g);
m_num_steps = 0;
bool proofs_enabled = g->proofs_enabled();
expr_ref new_curr(m());
proof_ref new_pr(m());
unsigned size = g->size();
for (unsigned idx = 0; idx < size; idx++) {
if (g->inconsistent())
break;
expr * curr = g->form(idx);
m_rw(curr, new_curr, new_pr);
m_num_steps += m_rw.get_num_steps();
if (proofs_enabled) {
proof * pr = g->pr(idx);
new_pr = m().mk_modus_ponens(pr, new_pr);
}
g->update(idx, new_curr, new_pr, g->dep(idx));
}
if (g->models_enabled())
mc = mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits);
g->inc_depth();
result.push_back(g.get());
m_rw.cfg().cleanup();
}
unsigned get_num_steps() const { return m_num_steps; }
};
imp * m_imp;
params_ref m_params;
public:
bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()):
m_params(p) {
m_imp = alloc(imp, m, p);
}
virtual tactic * translate(ast_manager & m) {
return alloc(bv1_blaster_tactic, m, m_params);
}
virtual ~bv1_blaster_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) {
insert_max_memory(r);
insert_max_steps(r);
}
bool is_target(goal const & g) const {
return m_imp->is_target(g);
}
/**
\brief "Blast" bit-vectors of size n in s into bit-vectors of size 1.
If s contains other bit-vectors operators different from concat/extract, then this is method is a NO-OP.
It also does not support quantifiers.
Return a model_converter that converts any model for the updated set into a model for the old set.
*/
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;
}
}
unsigned get_num_steps() const {
return m_imp->get_num_steps();
}
protected:
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(bv1_blaster_tactic, m, p));
}
class is_qfbv_eq_probe : public probe {
public:
virtual result operator()(goal const & g) {
bv1_blaster_tactic t(g.m());
return t.is_target(g);
}
};
probe * mk_is_qfbv_eq_probe() {
return alloc(is_qfbv_eq_probe);
}

View file

@ -0,0 +1,35 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bv1_blaster_tactic.h
Abstract:
Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1.
This rewriter only supports concat and extract operators.
This transformation is useful for handling benchmarks that contain
many BV equalities.
Remark: other operators can be mapped into concat/extract by using
the simplifiers.
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#ifndef _BV1_BLASTER_TACTIC_H_
#define _BV1_BLASTER_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref());
probe * mk_is_qfbv_eq_probe();
#endif

View file

@ -0,0 +1,436 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
eager_bit_blaster.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-02.
Revision History:
--*/
#include"ast_ll_pp.h"
#include"eager_bit_blaster.h"
eager_bit_blaster::basic_plugin::basic_plugin(ast_manager & m, eager_bit_blaster::bv_plugin & p, basic_simplifier_plugin & s):
simplifier_plugin(symbol("basic"), m),
m_main(p),
m_s(s) {
}
eager_bit_blaster::basic_plugin::~basic_plugin() {
}
bool eager_bit_blaster::basic_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (f->get_decl_kind() == OP_ITE) {
SASSERT(num_args == 3);
return m_main.reduce_ite(args[0], args[1], args[2], result);
}
else if (f->get_decl_kind() == OP_NOT) {
// the internalizer assumes there is not double negation (not (not x))
SASSERT(num_args == 1);
m_s.mk_not(args[0], result);
return true;
}
return false;
}
eager_bit_blaster::bv_plugin::bv_plugin(ast_manager & m, bit_blaster_params const & p):
simplifier_plugin(symbol("bv"), m),
m_util(m),
m_bb(m, p),
m_s(m) {
}
eager_bit_blaster::bv_plugin::~bv_plugin() {
}
void eager_bit_blaster::bv_plugin::get_bits(expr * n, expr_ref_vector & out_bits) {
rational val;
unsigned bv_size;
if (m_util.is_numeral(n, val, bv_size)) {
TRACE("eager_bb_bug", tout << "bv_size: " << bv_size << "\n";);
m_bb.num2bits(val, bv_size, out_bits);
SASSERT(out_bits.size() == bv_size);
}
else if (m_util.is_mkbv(n)) {
out_bits.append(to_app(n)->get_num_args(), to_app(n)->get_args());
}
else {
unsigned bv_size = m_util.get_bv_size(n);
for (unsigned i = 0; i < bv_size; i++) {
parameter p(i);
out_bits.push_back(m_manager.mk_app(get_family_id(), OP_BIT2BOOL, 1, &p, 1, &n));
}
SASSERT(bv_size == out_bits.size());
}
}
inline app * eager_bit_blaster::bv_plugin::mk_mkbv(expr_ref_vector const & bits) {
#ifdef Z3DEBUG
for (unsigned i = 0; i < bits.size(); i++) {
expr * b = bits.get(i);
SASSERT(!m_manager.is_not(b) || !m_manager.is_not(to_app(b)->get_arg(0)));
}
#endif
return m_manager.mk_app(get_family_id(), OP_MKBV, bits.size(), bits.c_ptr());
}
#define MK_UNARY_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg, expr_ref & result) { \
expr_ref_vector bits(m_manager); \
get_bits(arg, bits); \
expr_ref_vector out_bits(m_manager); \
m_bb.BB_OP(bits.size(), bits.c_ptr(), out_bits); \
result = mk_mkbv(out_bits); \
}
#define MK_BIN_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg1, expr * arg2, expr_ref & result) { \
expr_ref_vector bits1(m_manager); \
expr_ref_vector bits2(m_manager); \
get_bits(arg1, bits1); \
get_bits(arg2, bits2); \
expr_ref_vector out_bits(m_manager); \
m_bb.BB_OP(bits1.size(), bits1.c_ptr(), bits2.c_ptr(), out_bits); \
result = mk_mkbv(out_bits); \
}
#define MK_BIN_AC_FLAT_REDUCE(OP, BIN_OP, S_OP, BB_OP) \
MK_BIN_REDUCE(BIN_OP, BB_OP); \
void eager_bit_blaster::bv_plugin::OP(unsigned num_args, expr * const * args, expr_ref & result) { \
SASSERT(num_args > 0); \
if (num_args == 2) { \
BIN_OP(args[0], args[1], result); \
return; \
} \
\
ptr_buffer<expr_ref_vector> args_bits; \
for (unsigned i = 0; i < num_args; i++) { \
expr_ref_vector * bits = alloc(expr_ref_vector, m_manager); \
get_bits(args[i], *bits); \
args_bits.push_back(bits); \
} \
\
unsigned bv_size = m_util.get_bv_size(args[0]); \
expr_ref_vector new_bits(m_manager); \
for (unsigned i = 0; i < bv_size; i++) { \
expr_ref_vector arg_bits(m_manager); \
for (unsigned j = 0; j < num_args; j++) \
arg_bits.push_back(args_bits[j]->get(i)); \
expr_ref new_bit(m_manager); \
m_s.S_OP(arg_bits.size(), arg_bits.c_ptr(), new_bit); \
new_bits.push_back(new_bit); \
} \
result = mk_mkbv(new_bits); \
std::for_each(args_bits.begin(), args_bits.end(), delete_proc<expr_ref_vector>()); \
}
#define MK_BIN_AC_REDUCE(OP, BIN_OP, BB_OP) \
MK_BIN_REDUCE(BIN_OP, BB_OP); \
void eager_bit_blaster::bv_plugin::OP(unsigned num_args, expr * const * args, expr_ref & result) { \
SASSERT(num_args > 0); \
result = args[0]; \
for (unsigned i = 1; i < num_args; i++) { \
expr_ref new_result(m_manager); \
BIN_OP(result.get(), args[i], new_result); \
result = new_result; \
} \
}
#define MK_BIN_PRED_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg1, expr * arg2, expr_ref & result) { \
expr_ref_vector bits1(m_manager); \
expr_ref_vector bits2(m_manager); \
get_bits(arg1, bits1); \
get_bits(arg2, bits2); \
m_bb.BB_OP(bits1.size(), bits1.c_ptr(), bits2.c_ptr(), result); \
}
#define MK_PARAMETRIC_UNARY_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg, unsigned n, expr_ref & result) { \
expr_ref_vector bits(m_manager); \
get_bits(arg, bits); \
expr_ref_vector out_bits(m_manager); \
m_bb.BB_OP(bits.size(), bits.c_ptr(), n, out_bits); \
result = mk_mkbv(out_bits); \
}
MK_UNARY_REDUCE(reduce_not, mk_not);
MK_BIN_AC_FLAT_REDUCE(reduce_or, reduce_bin_or, mk_or, mk_or);
MK_BIN_AC_FLAT_REDUCE(reduce_and, reduce_bin_and, mk_and, mk_and);
MK_BIN_AC_FLAT_REDUCE(reduce_nor, reduce_bin_nor, mk_nor, mk_nor);
MK_BIN_AC_FLAT_REDUCE(reduce_nand, reduce_bin_nand, mk_nand, mk_nand);
MK_BIN_REDUCE(reduce_xor, mk_xor);
MK_BIN_REDUCE(reduce_xnor, mk_xnor);
MK_UNARY_REDUCE(reduce_neg, mk_neg);
MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder);
MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier);
MK_BIN_PRED_REDUCE(reduce_sle, mk_sle);
MK_BIN_PRED_REDUCE(reduce_ule, mk_ule);
MK_PARAMETRIC_UNARY_REDUCE(reduce_rotate_left, mk_rotate_left);
MK_PARAMETRIC_UNARY_REDUCE(reduce_rotate_right, mk_rotate_right);
MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
MK_PARAMETRIC_UNARY_REDUCE(reduce_zero_extend, mk_zero_extend);
MK_UNARY_REDUCE(reduce_redor, mk_redor);
MK_UNARY_REDUCE(reduce_redand, mk_redand);
MK_BIN_REDUCE(reduce_shl, mk_shl);
MK_BIN_REDUCE(reduce_ashr, mk_ashr);
MK_BIN_REDUCE(reduce_lshr, mk_lshr);
MK_BIN_REDUCE(reduce_comp, mk_comp);
MK_BIN_REDUCE(reduce_udiv, mk_udiv);
MK_BIN_REDUCE(reduce_urem, mk_urem);
MK_BIN_REDUCE(reduce_sdiv, mk_sdiv);
MK_BIN_REDUCE(reduce_srem, mk_srem);
MK_BIN_REDUCE(reduce_smod, mk_smod);
void eager_bit_blaster::bv_plugin::reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result) {
expr_ref_vector bits(m_manager);
get_bits(arg, bits);
expr_ref_vector out_bits(m_manager);
for (unsigned i = start; i <= end; ++i)
out_bits.push_back(bits.get(i));
result = mk_mkbv(out_bits);
}
void eager_bit_blaster::bv_plugin::reduce_concat(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref_vector out_bits(m_manager);
unsigned i = num_args;
while (i > 0) {
i--;
expr_ref_vector bits(m_manager);
get_bits(args[i], bits);
out_bits.append(bits.size(), bits.c_ptr());
}
result = mk_mkbv(out_bits);
}
bool eager_bit_blaster::bv_plugin::reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
sort * s = m_manager.get_sort(arg2);
if (!m_util.is_bv_sort(s))
return false;
expr_ref_vector bits1(m_manager);
expr_ref_vector bits2(m_manager);
get_bits(arg2, bits1);
get_bits(arg3, bits2);
SASSERT(bits1.size() == bits2.size());
expr_ref_vector out_bits(m_manager);
unsigned bv_size = bits1.size();
for (unsigned i = 0; i < bv_size; i++) {
expr_ref new_bit(m_manager);
m_s.mk_ite(arg1, bits1.get(i), bits2.get(i), new_bit);
out_bits.push_back(new_bit);
}
result = mk_mkbv(out_bits);
return true;
}
bool eager_bit_blaster::bv_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
bv_op_kind k = static_cast<bv_op_kind>(f->get_decl_kind());
switch (k) {
case OP_BNOT:
SASSERT(num_args == 1);
reduce_not(args[0], result);
return true;
case OP_BOR:
reduce_or(num_args, args, result);
return true;
case OP_BAND:
reduce_and(num_args, args, result);
return true;
case OP_BNOR:
reduce_nor(num_args, args, result);
return true;
case OP_BNAND:
reduce_nand(num_args, args, result);
return true;
case OP_BXOR:
SASSERT(num_args == 2);
reduce_xor(args[0], args[1], result);
return true;
case OP_BXNOR:
SASSERT(num_args == 2);
reduce_xnor(args[0], args[1], result);
return true;
case OP_BNEG:
SASSERT(num_args == 1);
reduce_neg(args[0], result);
return true;
case OP_BADD:
reduce_add(num_args, args, result);
return true;
case OP_BMUL:
reduce_mul(num_args, args, result);
return true;
case OP_BIT1:
case OP_BIT0:
case OP_BSUB:
// I'm assuming the expressions were simplified before invoking this method.
UNREACHABLE();
return false;
case OP_BSDIV:
case OP_BUDIV:
case OP_BSREM:
case OP_BUREM:
case OP_BSMOD:
// I'm assuming the expressions were simplified before invoking this method.
UNREACHABLE();
return false;
case OP_BSDIV0:
case OP_BUDIV0:
case OP_BSREM0:
case OP_BUREM0:
case OP_BSMOD0:
// do nothing... these are uninterpreted
return true;
case OP_BSDIV_I:
SASSERT(num_args == 2);
reduce_sdiv(args[0], args[1], result);
return true;
case OP_BUDIV_I:
SASSERT(num_args == 2);
reduce_udiv(args[0], args[1], result);
return true;
case OP_BSREM_I:
SASSERT(num_args == 2);
reduce_srem(args[0], args[1], result);
return true;
case OP_BUREM_I:
SASSERT(num_args == 2);
reduce_urem(args[0], args[1], result);
return true;
case OP_BSMOD_I:
SASSERT(num_args == 2);
reduce_smod(args[0], args[1], result);
return true;
case OP_ULEQ:
SASSERT(num_args == 2);
reduce_ule(args[0], args[1], result);
return true;
case OP_SLEQ:
SASSERT(num_args == 2);
reduce_sle(args[0], args[1], result);
return true;
case OP_UGEQ:
case OP_SGEQ:
case OP_ULT:
case OP_SLT:
case OP_UGT:
case OP_SGT:
// I'm assuming the expressions were simplified before invoking this method.
UNREACHABLE();
return false;
case OP_EXTRACT:
SASSERT(num_args == 1);
reduce_extract(f->get_parameter(1).get_int(), f->get_parameter(0).get_int(), args[0], result);
return true;
case OP_CONCAT:
reduce_concat(num_args, args, result);
return true;
case OP_SIGN_EXT:
SASSERT(num_args == 1);
reduce_sign_extend(args[0], f->get_parameter(0).get_int(), result);
return true;
case OP_ZERO_EXT:
SASSERT(num_args == 1);
reduce_zero_extend(args[0], f->get_parameter(0).get_int(), result);
return true;
case OP_REPEAT:
UNREACHABLE();
return false;
case OP_BREDOR:
SASSERT(num_args == 1);
reduce_redor(args[0], result);
return true;
case OP_BREDAND:
SASSERT(num_args == 1);
reduce_redand(args[0], result);
return true;
case OP_BCOMP:
SASSERT(num_args == 2);
reduce_comp(args[0], args[1], result);
return true;
case OP_BSHL:
SASSERT(num_args == 2);
reduce_shl(args[0], args[1], result);
return true;
case OP_BLSHR:
SASSERT(num_args == 2);
reduce_lshr(args[0], args[1], result);
return true;
case OP_BASHR:
SASSERT(num_args == 2);
reduce_ashr(args[0], args[1], result);
return true;
case OP_ROTATE_LEFT:
SASSERT(num_args == 1);
reduce_rotate_left(args[0], f->get_parameter(0).get_int(), result);
return true;
case OP_ROTATE_RIGHT:
SASSERT(num_args == 1);
reduce_rotate_right(args[0], f->get_parameter(0).get_int(), result);
return true;
default:
return false;
}
}
bool eager_bit_blaster::bv_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
TRACE("eager_bb_eq", tout << mk_ll_pp(lhs, m_manager) << "\n" << mk_ll_pp(rhs, m_manager) << "\n";);
SASSERT(m_util.get_bv_size(lhs) == m_util.get_bv_size(rhs));
expr_ref_vector bits1(m_manager);
expr_ref_vector bits2(m_manager);
get_bits(lhs, bits1);
get_bits(rhs, bits2);
SASSERT(bits1.size() == bits2.size());
m_bb.mk_eq(bits1.size(), bits1.c_ptr(), bits2.c_ptr(), result);
return true;
}
bool eager_bit_blaster::bv_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
if (num_args <= 1) {
result = m_manager.mk_true();
}
if (num_args == 2) {
expr_ref tmp(m_manager);
reduce_eq(args[0], args[1], tmp);
m_s.mk_not(tmp, result);
}
else {
expr_ref_vector new_args(m_manager);
for (unsigned i = 0; i < num_args - 1; i++) {
expr * a1 = args[i];
for (unsigned j = i + 1; j < num_args; j++) {
expr * a2 = args[j];
expr_ref tmp1(m_manager);
reduce_eq(a1, a2, tmp1);
expr_ref tmp2(m_manager);
m_s.mk_not(tmp1, tmp2);
new_args.push_back(tmp2);
}
}
m_s.mk_and(new_args.size(), new_args.c_ptr(), result);
}
return true;
}
eager_bit_blaster::eager_bit_blaster(ast_manager & m, bit_blaster_params const & p):
m_simplifier(m) {
m_simplifier.enable_ac_support(false);
bv_plugin * bv_p = alloc(bv_plugin, m, p);
m_simplifier.register_plugin(bv_p);
m_simplifier.register_plugin(alloc(basic_plugin, m, *bv_p, bv_p->get_basic_simplifier()));
}
void eager_bit_blaster::operator()(expr * s, expr_ref & r, proof_ref & p) {
m_simplifier.operator()(s, r, p);
}

View file

@ -0,0 +1,107 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
eager_bit_blaster.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-02.
Revision History:
--*/
#ifndef _EAGER_BIT_BLASTER_H_
#define _EAGER_BIT_BLASTER_H_
#include"bv_decl_plugin.h"
#include"bit_blaster.h"
#include"simplifier.h"
#include"basic_simplifier_plugin.h"
class eager_bit_blaster {
class bv_plugin : public simplifier_plugin {
bv_util m_util;
bit_blaster m_bb;
basic_simplifier_plugin m_s;
void get_bits(expr * n, expr_ref_vector & out_bits);
app * mk_mkbv(expr_ref_vector const & bits);
void reduce_not(expr * arg, expr_ref & result);
void reduce_bin_or(expr * arg1, expr * arg2, expr_ref & result);
void reduce_or(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_and(expr * arg1, expr * arg2, expr_ref & result);
void reduce_and(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_nor(expr * arg1, expr * arg2, expr_ref & result);
void reduce_nor(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_nand(expr * arg1, expr * arg2, expr_ref & result);
void reduce_nand(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_xor(expr * arg1, expr * arg2, expr_ref & result);
void reduce_xnor(expr * arg1, expr * arg2, expr_ref & result);
void reduce_neg(expr * arg, expr_ref & result);
void reduce_bin_add(expr * arg1, expr * arg2, expr_ref & result);
void reduce_add(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_mul(expr * arg1, expr * arg2, expr_ref & result);
void reduce_mul(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_sdiv(expr * arg1, expr * arg2, expr_ref & result);
void reduce_udiv(expr * arg1, expr * arg2, expr_ref & result);
void reduce_srem(expr * arg1, expr * arg2, expr_ref & result);
void reduce_urem(expr * arg1, expr * arg2, expr_ref & result);
void reduce_smod(expr * arg1, expr * arg2, expr_ref & result);
void reduce_sle(expr * arg1, expr * arg2, expr_ref & result);
void reduce_ule(expr * arg1, expr * arg2, expr_ref & result);
void reduce_concat(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result);
void reduce_redor(expr * arg, expr_ref & result);
void reduce_redand(expr * arg, expr_ref & result);
void reduce_comp(expr * arg1, expr * arg2, expr_ref & result);
void reduce_shl(expr * arg1, expr * arg2, expr_ref & result);
void reduce_ashr(expr * arg1, expr * arg2, expr_ref & result);
void reduce_lshr(expr * arg1, expr * arg2, expr_ref & result);
void reduce_rotate_left(expr * arg, unsigned n, expr_ref & result);
void reduce_rotate_right(expr * arg, unsigned n, expr_ref & result);
void reduce_sign_extend(expr * arg, unsigned n, expr_ref & result);
void reduce_zero_extend(expr * arg, unsigned n, expr_ref & result);
public:
bv_plugin(ast_manager & m, bit_blaster_params const & p);
virtual ~bv_plugin();
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);
basic_simplifier_plugin & get_basic_simplifier() { return m_s; }
bool reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
};
/**
\brief Plugin for handling the term-ite.
*/
class basic_plugin : public simplifier_plugin {
bv_plugin & m_main;
basic_simplifier_plugin & m_s;
public:
basic_plugin(ast_manager & m, bv_plugin & p, basic_simplifier_plugin & s);
virtual ~basic_plugin();
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
};
simplifier m_simplifier;
public:
eager_bit_blaster(ast_manager & m, bit_blaster_params const & p);
void operator()(expr * s, expr_ref & r, proof_ref & p);
};
#endif /* _EAGER_BIT_BLASTER_H_ */

View file

@ -0,0 +1,165 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_tactic.cpp
Abstract:
Apply bit-blasting to a given goal
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#include"tactical.h"
#include"bit_blaster_model_converter.h"
#include"bit_blaster_rewriter.h"
#include"ast_smt2_pp.h"
#include"model_pp.h"
class bit_blaster_tactic : public tactic {
struct imp {
bit_blaster_rewriter m_rewriter;
unsigned m_num_steps;
bool m_blast_quant;
imp(ast_manager & m, params_ref const & p):
m_rewriter(m, p) {
updt_params(p);
}
void updt_params_core(params_ref const & p) {
m_blast_quant = p.get_bool(":blast-quant", false);
}
void updt_params(params_ref const & p) {
m_rewriter.updt_params(p);
updt_params_core(p);
}
ast_manager & m() const { return m_rewriter.m(); }
void set_cancel(bool f) {
m_rewriter.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) {
mc = 0; pc = 0; core = 0;
bool proofs_enabled = g->proofs_enabled();
if (proofs_enabled && m_blast_quant)
throw tactic_exception("quantified variable blasting does not support proof generation");
tactic_report report("bit-blaster", *g);
TRACE("before_bit_blaster", g->display(tout););
m_num_steps = 0;
expr_ref new_curr(m());
proof_ref new_pr(m());
unsigned size = g->size();
for (unsigned idx = 0; idx < size; idx++) {
if (g->inconsistent())
break;
expr * curr = g->form(idx);
m_rewriter(curr, new_curr, new_pr);
m_num_steps += m_rewriter.get_num_steps();
if (proofs_enabled) {
proof * pr = g->pr(idx);
new_pr = m().mk_modus_ponens(pr, new_pr);
}
g->update(idx, new_curr, new_pr, g->dep(idx));
}
if (g->models_enabled())
mc = mk_bit_blaster_model_converter(m(), m_rewriter.const2bits());
else
mc = 0;
g->inc_depth();
result.push_back(g.get());
TRACE("after_bit_blaster", g->display(tout); if (mc) mc->display(tout); tout << "\n";);
m_rewriter.cleanup();
}
unsigned get_num_steps() const { return m_num_steps; }
};
imp * m_imp;
params_ref m_params;
public:
bit_blaster_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(bit_blaster_tactic, m, m_params);
}
virtual ~bit_blaster_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) {
insert_max_memory(r);
insert_max_steps(r);
r.insert(":blast-mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders).");
r.insert(":blast-add", CPK_BOOL, "(default: true) bit-blast adders.");
r.insert(":blast-quant", CPK_BOOL, "(default: false) bit-blast quantified variables.");
r.insert(":blast-full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms.");
}
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)
{
m_imp = 0;
}
dealloc(d);
d = alloc(imp, m, m_params);
#pragma omp critical (tactic_cancel)
{
m_imp = d;
}
}
unsigned get_num_steps() const {
return m_imp->get_num_steps();
}
protected:
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(bit_blaster_tactic, m, p));
}

View file

@ -0,0 +1,28 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_tactic.h
Abstract:
Apply bit-blasting to a given goal.
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#ifndef _BIT_BLASTER_TACTIC_H_
#define _BIT_BLASTER_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,366 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
bv_size_reduction_tactic.cpp
Abstract:
Reduce the number of bits used to encode constants, by using signed bounds.
Example: suppose x is a bit-vector of size 8, and we have
signed bounds for x such that:
-2 <= x <= 2
Then, x can be replaced by ((sign-extend 5) k)
where k is a fresh bit-vector constant of size 3.
Author:
Leonardo (leonardo) 2012-02-19
Notes:
--*/
#include"tactical.h"
#include"bv_decl_plugin.h"
#include"expr_replacer.h"
#include"extension_model_converter.h"
#include"ast_smt2_pp.h"
class bv_size_reduction_tactic : public tactic {
struct imp;
imp * m_imp;
public:
bv_size_reduction_tactic(ast_manager & m);
virtual tactic * translate(ast_manager & m) {
return alloc(bv_size_reduction_tactic, m);
}
virtual ~bv_size_reduction_tactic();
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();
virtual void set_cancel(bool f);
};
tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(bv_size_reduction_tactic, m));
}
struct bv_size_reduction_tactic::imp {
typedef rational numeral;
typedef extension_model_converter bv_size_reduction_mc;
ast_manager & m;
bv_util m_util;
obj_map<app, numeral> m_signed_lowers;
obj_map<app, numeral> m_signed_uppers;
obj_map<app, numeral> m_unsigned_lowers;
obj_map<app, numeral> m_unsigned_uppers;
ref<bv_size_reduction_mc> m_mc;
scoped_ptr<expr_replacer> m_replacer;
bool m_produce_models;
volatile bool m_cancel;
imp(ast_manager & _m):
m(_m),
m_util(m),
m_replacer(mk_default_expr_replacer(m)),
m_cancel(false) {
}
void update_signed_lower(app * v, numeral const & k) {
// k <= v
obj_map<app, numeral>::obj_map_entry * entry = m_signed_lowers.insert_if_not_there2(v, k);
if (entry->get_data().m_value < k) {
// improve bound
entry->get_data().m_value = k;
}
}
void update_signed_upper(app * v, numeral const & k) {
// v <= k
obj_map<app, numeral>::obj_map_entry * entry = m_signed_uppers.insert_if_not_there2(v, k);
if (k < entry->get_data().m_value) {
// improve bound
entry->get_data().m_value = k;
}
}
void update_unsigned_lower(app * v, numeral const & k) {
SASSERT(k > numeral(0));
// k <= v
obj_map<app, numeral>::obj_map_entry * entry = m_unsigned_lowers.insert_if_not_there2(v, k);
if (entry->get_data().m_value < k) {
// improve bound
entry->get_data().m_value = k;
}
}
void update_unsigned_upper(app * v, numeral const & k) {
SASSERT(k > numeral(0));
// v <= k
obj_map<app, numeral>::obj_map_entry * entry = m_unsigned_uppers.insert_if_not_there2(v, k);
if (k < entry->get_data().m_value) {
// improve bound
entry->get_data().m_value = k;
}
}
void collect_bounds(goal const & g) {
unsigned sz = g.size();
numeral val;
unsigned bv_sz;
expr * f, * lhs, * rhs;
for (unsigned i = 0; i < sz; i++) {
bool negated = false;
f = g.form(i);
if (m.is_not(f)) {
negated = true;
f = to_app(f)->get_arg(0);
}
if (m_util.is_bv_sle(f, lhs, rhs)) {
if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) {
TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; );
// v <= k
if (negated) update_signed_lower(to_app(lhs), val+numeral(1));
else update_signed_upper(to_app(lhs), val);
}
else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) {
TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; );
// k <= v
if (negated) update_signed_upper(to_app(rhs), val-numeral(1));
else update_signed_lower(to_app(rhs), val);
}
}
#if 0
else if (m_util.is_bv_ule(f, lhs, rhs)) {
if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) {
TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; );
// v <= k
if (negated) update_unsigned_lower(to_app(lhs), val+numeral(1));
else update_unsigned_upper(to_app(lhs), val);
}
else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) {
TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; );
// k <= v
if (negated) update_unsigned_upper(to_app(rhs), val-numeral(1));
else update_unsigned_lower(to_app(rhs), val);
}
}
#endif
}
}
void checkpoint() {
if (m_cancel)
throw tactic_exception(TACTIC_CANCELED_MSG);
}
void operator()(goal & g, model_converter_ref & mc) {
if (g.inconsistent())
return;
TRACE("before_bv_size_reduction", g.display(tout););
m_produce_models = g.models_enabled();
mc = 0;
m_mc = 0;
unsigned num_reduced = 0;
{
tactic_report report("bv-size-reduction", g);
collect_bounds(g);
// create substitution
expr_substitution subst(m);
if (!(m_signed_lowers.empty() || m_signed_uppers.empty())) {
TRACE("bv_size_reduction",
tout << "m_signed_lowers: " << std::endl;
for (obj_map<app, numeral>::iterator it = m_signed_lowers.begin(); it != m_signed_lowers.end(); it++)
tout << mk_ismt2_pp(it->m_key, m) << " >= " << it->m_value.to_string() << std::endl;
tout << "m_signed_uppers: " << std::endl;
for (obj_map<app, numeral>::iterator it = m_signed_uppers.begin(); it != m_signed_uppers.end(); it++)
tout << mk_ismt2_pp(it->m_key, m) << " <= " << it->m_value.to_string() << std::endl;
);
obj_map<app, numeral>::iterator it = m_signed_lowers.begin();
obj_map<app, numeral>::iterator end = m_signed_lowers.end();
for (; it != end; ++it) {
app * v = it->m_key;
unsigned bv_sz = m_util.get_bv_size(v);
numeral l = m_util.norm(it->m_value, bv_sz, true);
obj_map<app, numeral>::obj_map_entry * entry = m_signed_uppers.find_core(v);
if (entry != 0) {
numeral u = m_util.norm(entry->get_data().m_value, bv_sz, true);
TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";);
expr * new_def = 0;
if (l > u) {
g.assert_expr(m.mk_false());
return;
}
else if (l == u) {
new_def = m_util.mk_numeral(l, m.get_sort(v));
}
else {
// l < u
if (l.is_neg()) {
unsigned i_nb = (u - l).get_num_bits();
unsigned v_nb = m_util.get_bv_size(v);
if (i_nb < v_nb)
new_def = m_util.mk_sign_extend(v_nb - i_nb, m.mk_fresh_const(0, m_util.mk_sort(i_nb)));
}
else {
// 0 <= l <= v <= u
unsigned u_nb = u.get_num_bits();
unsigned v_nb = m_util.get_bv_size(v);
if (u_nb < v_nb)
new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), m.mk_fresh_const(0, m_util.mk_sort(u_nb)));
}
}
if (new_def) {
subst.insert(v, new_def);
if (m_produce_models) {
if (!m_mc)
m_mc = alloc(bv_size_reduction_mc, m);
m_mc->insert(v->get_decl(), new_def);
}
num_reduced++;
}
}
}
}
#if 0
if (!(m_unsigned_lowers.empty() && m_unsigned_uppers.empty())) {
TRACE("bv_size_reduction",
tout << "m_unsigned_lowers: " << std::endl;
for (obj_map<app, numeral>::iterator it = m_unsigned_lowers.begin(); it != m_unsigned_lowers.end(); it++)
tout << mk_ismt2_pp(it->m_key, m) << " >= " << it->m_value.to_string() << std::endl;
tout << "m_unsigned_uppers: " << std::endl;
for (obj_map<app, numeral>::iterator it = m_unsigned_uppers.begin(); it != m_unsigned_uppers.end(); it++)
tout << mk_ismt2_pp(it->m_key, m) << " <= " << it->m_value.to_string() << std::endl;
);
obj_map<app, numeral>::iterator it = m_unsigned_uppers.begin();
obj_map<app, numeral>::iterator end = m_unsigned_uppers.end();
for (; it != end; ++it) {
app * v = it->m_key;
unsigned bv_sz = m_util.get_bv_size(v);
numeral u = m_util.norm(it->m_value, bv_sz, false);
obj_map<app, numeral>::obj_map_entry * entry = m_signed_lowers.find_core(v);
numeral l = (entry != 0) ? m_util.norm(entry->get_data().m_value, bv_sz, false) : numeral(0);
obj_map<app, numeral>::obj_map_entry * lse = m_signed_lowers.find_core(v);
obj_map<app, numeral>::obj_map_entry * use = m_signed_uppers.find_core(v);
if ((lse != 0 && lse->get_data().m_value > l) &&
(use != 0 && use->get_data().m_value < u))
continue; // Skip, we had better signed bounds.
if (lse != 0 && lse->get_data().m_value > l) l = lse->get_data().m_value;
if (use != 0 && use->get_data().m_value < u) u = use->get_data().m_value;
TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";);
expr * new_def = 0;
if (l > u) {
g.assert_expr(m.mk_false());
return;
}
else if (l == u) {
new_def = m_util.mk_numeral(l, m.get_sort(v));
}
else {
// 0 <= l <= v <= u
unsigned u_nb = u.get_num_bits();
unsigned v_nb = m_util.get_bv_size(v);
if (u_nb < v_nb)
new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), m.mk_fresh_const(0, m_util.mk_sort(u_nb)));
}
if (new_def) {
subst.insert(v, new_def);
if (m_produce_models) {
if (!m_mc)
m_mc = alloc(bv_size_reduction_mc, m);
m_mc->insert(v->get_decl(), new_def);
}
num_reduced++;
TRACE("bv_size_reduction", tout << "New definition = " << mk_ismt2_pp(new_def, m) << "\n";);
}
}
}
#endif
if (subst.empty())
return;
m_replacer->set_substitution(&subst);
unsigned sz = g.size();
expr * f;
expr_ref new_f(m);
for (unsigned i = 0; i < sz; i++) {
if (g.inconsistent())
return;
f = g.form(i);
(*m_replacer)(f, new_f);
g.update(i, new_f);
}
mc = m_mc.get();
m_mc = 0;
}
report_tactic_progress(":bv-reduced", num_reduced);
TRACE("after_bv_size_reduction", g.display(tout); if (m_mc) m_mc->display(tout););
}
void set_cancel(bool f) {
m_replacer->set_cancel(f);
m_cancel = f;
}
};
bv_size_reduction_tactic::bv_size_reduction_tactic(ast_manager & m) {
m_imp = alloc(imp, m);
}
bv_size_reduction_tactic::~bv_size_reduction_tactic() {
dealloc(m_imp);
}
void bv_size_reduction_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("bv-size-reduction", g);
fail_if_unsat_core_generation("bv-size-reduction", g);
mc = 0; pc = 0; core = 0; result.reset();
m_imp->operator()(*(g.get()), mc);
g->inc_depth();
result.push_back(g.get());
SASSERT(g->is_well_sorted());
}
void bv_size_reduction_tactic::set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
void bv_size_reduction_tactic::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;
}
}

View file

@ -0,0 +1,33 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
bv_size_reduction.h
Abstract:
Reduce the number of bits used to encode constants, by using signed bounds.
Example: suppose x is a bit-vector of size 8, and we have
signed bounds for x such that:
-2 <= x <= 2
Then, x can be replaced by ((sign-extend 5) k)
where k is a fresh bit-vector constant of size 3.
Author:
Leonardo (leonardo) 2012-02-19
Notes:
--*/
#ifndef _BV_SIZE_REDUCTION_TACTIC_H_
#define _BV_SIZE_REDUCTION_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,337 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
max_bv_sharing_tactic.cpp
Abstract:
Rewriter for "maximing" the number of shared terms.
The idea is to rewrite AC terms to maximize sharing.
This rewriter is particularly useful for reducing
the number of Adders and Multipliers before "bit-blasting".
Author:
Leonardo de Moura (leonardo) 2011-12-29.
Revision History:
--*/
#include"tactical.h"
#include"bv_decl_plugin.h"
#include"rewriter_def.h"
#include"obj_pair_hashtable.h"
#include"ast_lt.h"
#include"cooperate.h"
class max_bv_sharing_tactic : public tactic {
struct rw_cfg : public default_rewriter_cfg {
typedef std::pair<expr *, expr *> expr_pair;
typedef obj_pair_hashtable<expr, expr> set;
bv_util m_util;
set m_add_apps;
set m_mul_apps;
set m_xor_apps;
set m_or_apps;
unsigned long long m_max_memory;
unsigned m_max_steps;
unsigned m_max_args;
ast_manager & m() const { return m_util.get_manager(); }
rw_cfg(ast_manager & m, params_ref const & p):
m_util(m) {
updt_params(p);
}
void cleanup() {
m_add_apps.finalize();
m_mul_apps.finalize();
m_or_apps.finalize();
m_xor_apps.finalize();
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
m_max_args = p.get_uint(":max-args", 128);
}
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("max bv sharing");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
return num_steps > m_max_steps;
}
set & f2set(func_decl * f) {
switch (f->get_decl_kind()) {
case OP_BADD: return m_add_apps;
case OP_BMUL: return m_mul_apps;
case OP_BXOR: return m_xor_apps;
case OP_BOR: return m_or_apps;
default:
UNREACHABLE();
return m_or_apps; // avoid compilation error
}
}
expr * reuse(set & s, func_decl * f, expr * arg1, expr * arg2) {
if (s.contains(expr_pair(arg1, arg2)))
return m().mk_app(f, arg1, arg2);
if (s.contains(expr_pair(arg2, arg1)))
return m().mk_app(f, arg2, arg1);
return 0;
}
struct ref_count_lt {
bool operator()(expr * t1, expr * t2) const {
if (t1->get_ref_count() < t2->get_ref_count())
return true;
return (t1->get_ref_count() == t2->get_ref_count()) && lt(t1, t2);
}
};
br_status reduce_ac_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
set & s = f2set(f);
if (num_args == 2) {
if (!m_util.is_numeral(args[0]) && !m_util.is_numeral(args[1]))
s.insert(expr_pair(args[0], args[1]));
return BR_FAILED;
}
ptr_buffer<expr, 128> _args;
bool first = false;
expr * num = 0;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (num == 0 && m_util.is_numeral(arg)) {
if (i == 0) first = true;
num = arg;
}
else {
_args.push_back(arg);
}
}
num_args = _args.size();
// std::sort(_args.begin(), _args.end(), ref_count_lt());
// std::sort(_args.begin(), _args.end(), ast_to_lt());
try_to_reuse:
if (num_args > 1 && num_args < m_max_args) {
for (unsigned i = 0; i < num_args - 1; i++) {
for (unsigned j = i + 1; j < num_args; j++) {
expr * r = reuse(s, f, _args[i], _args[j]);
if (r != 0) {
TRACE("bv_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";);
_args[i] = r;
SASSERT(num_args > 1);
for (unsigned w = j; w < num_args - 1; w++) {
_args[w] = _args[w+1];
}
num_args--;
goto try_to_reuse;
}
}
}
}
// TODO:
// some benchmarks are more efficiently solved using a tree-like structure (better sharing)
// other benchmarks are more efficiently solved using a chain-like structure (better propagation for arguments "closer to the output").
//
// One possible solution is to do a global analysis that finds a good order that increases sharing without affecting
// propagation.
//
// Another cheap trick is to create an option, and try both for a small amount of time.
#if 0
SASSERT(num_args > 0);
if (num_args == 1) {
result = _args[0];
}
else {
// ref_count_lt is not a total order on expr's
std::stable_sort(_args.c_ptr(), _args.c_ptr() + num_args, ref_count_lt());
result = m().mk_app(f, _args[0], _args[1]);
for (unsigned i = 2; i < num_args; i++) {
result = m().mk_app(f, result.get(), _args[i]);
}
}
if (num != 0) {
if (first)
result = m().mk_app(f, num, result);
else
result = m().mk_app(f, result, num);
}
return BR_DONE;
#else
// Create "tree-like circuit"
while (true) {
TRACE("bv_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";);
unsigned j = 0;
for (unsigned i = 0; i < num_args; i += 2, j++) {
if (i == num_args - 1) {
_args[j] = _args[i];
}
else {
s.insert(expr_pair(_args[i], _args[i+1]));
_args[j] = m().mk_app(f, _args[i], _args[i+1]);
}
}
num_args = j;
if (num_args == 1) {
if (num == 0) {
result = _args[0];
}
else {
if (first)
result = m().mk_app(f, num, _args[0]);
else
result = m().mk_app(f, _args[0], num);
}
return BR_DONE;
}
}
#endif
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
if (f->get_family_id() != m_util.get_family_id())
return BR_FAILED;
switch (f->get_decl_kind()) {
case OP_BADD:
case OP_BMUL:
case OP_BOR:
case OP_BXOR:
result_pr = 0;
return reduce_ac_app(f, num, args, result);
default:
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 {
rw m_rw;
unsigned m_num_steps;
imp(ast_manager & m, params_ref const & p):
m_rw(m, p) {
}
ast_manager & m() const { return m_rw.m(); }
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("max-bv-sharing", *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++) {
if (g->inconsistent())
break;
expr * curr = g->form(idx);
m_rw(curr, new_curr, new_pr);
m_num_steps += m_rw.get_num_steps();
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));
}
m_rw.cfg().cleanup();
g->inc_depth();
result.push_back(g.get());
TRACE("qe", g->display(tout););
SASSERT(g->is_well_sorted());
}
};
imp * m_imp;
params_ref m_params;
public:
max_bv_sharing_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(max_bv_sharing_tactic, m, m_params);
}
virtual ~max_bv_sharing_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) {
insert_max_memory(r);
insert_max_steps(r);
r.insert(":max-args", CPK_UINT,
"(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic.");
}
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_max_bv_sharing_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(max_bv_sharing_tactic, m, p));
}

View file

@ -0,0 +1,32 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
max_bv_sharing_tactic.h
Abstract:
Rewriter for "maximing" the number of shared terms.
The idea is to rewrite AC terms to maximize sharing.
This rewriter is particularly useful for reducing
the number of Adders and Multipliers before "bit-blasting".
Author:
Leonardo de Moura (leonardo) 2011-12-29.
Revision History:
--*/
#ifndef _MAX_BV_SHARING_TACTIC_H_
#define _MAX_BV_SHARING_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

142
src/tactic/converter.h Normal file
View file

@ -0,0 +1,142 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
converter.h
Abstract:
Abstract class and templates for proof and model converters.
Author:
Leonardo (leonardo) 2011-11-14
Notes:
--*/
#ifndef _CONVERTER_H_
#define _CONVERTER_H_
#include"vector.h"
#include"ref.h"
#include"ast_translation.h"
class converter {
unsigned m_ref_count;
public:
converter():m_ref_count(0) {}
virtual ~converter() {}
void inc_ref() { ++m_ref_count; }
void dec_ref() {
--m_ref_count;
if (m_ref_count == 0)
dealloc(this);
}
virtual void cancel() {}
// for debugging purposes
virtual void display(std::ostream & out) {}
};
template<typename T>
class concat_converter : public T {
protected:
ref<T> m_c1;
ref<T> m_c2;
template<typename T2>
T * translate_core(ast_translation & translator) {
T * t1 = m_c1->translate(translator);
T * t2 = m_c2->translate(translator);
return alloc(T2, t1, t2);
}
public:
concat_converter(T * c1, T * c2):m_c1(c1), m_c2(c2) {}
virtual ~concat_converter() {}
virtual void cancel() {
m_c2->cancel();
m_c1->cancel();
}
virtual char const * get_name() const = 0;
virtual void display(std::ostream & out) {
out << "(" << get_name() << "\n";
m_c1->display(out);
m_c2->display(out);
out << ")\n";
}
};
template<typename T>
class concat_star_converter : public T {
protected:
ref<T> m_c1;
ptr_vector<T> m_c2s;
unsigned_vector m_szs;
template<typename T2>
T * translate_core(ast_translation & translator) {
T * t1 = m_c1 ? m_c1->translate(translator) : 0;
ptr_buffer<T> t2s;
unsigned num = m_c2s.size();
for (unsigned i = 0; i < num; i++)
t2s.push_back(m_c2s[i] ? m_c2s[i]->translate(translator) : 0);
return alloc(T2, t1, num, t2s.c_ptr(), m_szs.c_ptr());
}
public:
concat_star_converter(T * c1, unsigned num, T * const * c2s, unsigned * szs):
m_c1(c1) {
for (unsigned i = 0; i < num; i++) {
T * c2 = c2s[i];
if (c2)
c2->inc_ref();
m_c2s.push_back(c2);
m_szs.push_back(szs[i]);
}
}
virtual ~concat_star_converter() {
unsigned sz = m_c2s.size();
for (unsigned i = 0; i < sz; i++) {
T * c2 = m_c2s[i];
if (c2)
c2->dec_ref();
}
}
virtual void cancel() {
if (m_c1)
m_c1->cancel();
unsigned num = m_c2s.size();
for (unsigned i = 0; i < num; i++) {
if (m_c2s[i])
m_c2s[i]->cancel();
}
}
virtual char const * get_name() const = 0;
virtual void display(std::ostream & out) {
out << "(" << get_name() << "\n";
if (m_c1)
m_c1->display(out);
out << "(\n";
unsigned num = m_c2s.size();
for (unsigned i = 0; i < num; i++)
if (m_c2s[i])
m_c2s[i]->display(out);
out << "))\n";
}
};
#endif

View file

@ -0,0 +1,712 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
cofactor_elim_term_ite.cpp
Abstract:
Eliminate term if-then-else's using cofactors.
Author:
Leonardo de Moura (leonardo) 2011-06-05.
Revision History:
--*/
#include"cofactor_elim_term_ite.h"
#include"mk_simplified_app.h"
#include"rewriter_def.h"
#include"cooperate.h"
#include"for_each_expr.h"
#include"ast_smt2_pp.h"
#include"ast_ll_pp.h"
#include"tactic.h"
struct cofactor_elim_term_ite::imp {
ast_manager & m;
params_ref m_params;
unsigned long long m_max_memory;
volatile bool m_cancel;
void checkpoint() {
cooperate("cofactor ite");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
if (m_cancel)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
}
// Collect atoms that contain term if-then-else
struct analyzer {
struct frame {
expr * m_t;
unsigned m_first:1;
unsigned m_form_ctx:1;
frame(expr * t, bool form_ctx):m_t(t), m_first(true), m_form_ctx(form_ctx) {}
};
ast_manager & m;
imp & m_owner;
obj_hashtable<expr> m_candidates;
expr_fast_mark1 m_processed;
expr_fast_mark2 m_has_term_ite;
svector<frame> m_frame_stack;
analyzer(ast_manager & _m, imp & owner):m(_m), m_owner(owner) {}
void push_frame(expr * t, bool form_ctx) {
m_frame_stack.push_back(frame(t, form_ctx && m.is_bool(t)));
}
void visit(expr * t, bool form_ctx, bool & visited) {
if (!m_processed.is_marked(t)) {
visited = false;
push_frame(t, form_ctx);
}
}
void save_candidate(expr * t, bool form_ctx) {
if (!form_ctx)
return;
if (!m.is_bool(t))
return;
if (!m_has_term_ite.is_marked(t))
return;
if (!is_app(t))
return;
if (to_app(t)->get_family_id() == m.get_basic_family_id()) {
switch (to_app(t)->get_decl_kind()) {
case OP_OR:
case OP_AND:
case OP_NOT:
case OP_XOR:
case OP_IMPLIES:
case OP_TRUE:
case OP_FALSE:
case OP_ITE:
case OP_IFF:
return;
case OP_EQ:
case OP_DISTINCT:
if (m.is_bool(to_app(t)->get_arg(0)))
return;
break;
default:
break;
}
}
// it is an atom in a formula context (i.e., it is not nested inside a term),
// and it contains a term if-then-else.
m_candidates.insert(t);
}
void operator()(expr * t) {
SASSERT(m.is_bool(t));
push_frame(t, true);
SASSERT(!m_frame_stack.empty());
while (!m_frame_stack.empty()) {
frame & fr = m_frame_stack.back();
expr * t = fr.m_t;
bool form_ctx = fr.m_form_ctx;
TRACE("cofactor_ite_analyzer", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";);
m_owner.checkpoint();
if (m_processed.is_marked(t)) {
save_candidate(t, form_ctx);
m_frame_stack.pop_back();
continue;
}
if (m.is_term_ite(t)) {
m_has_term_ite.mark(t);
m_processed.mark(t);
m_frame_stack.pop_back();
continue;
}
if (fr.m_first) {
fr.m_first = false;
bool visited = true;
if (is_app(t)) {
unsigned num_args = to_app(t)->get_num_args();
for (unsigned i = 0; i < num_args; i++)
visit(to_app(t)->get_arg(i), form_ctx, visited);
}
// ignoring quantifiers
if (!visited)
continue;
}
if (is_app(t)) {
unsigned num_args = to_app(t)->get_num_args();
unsigned i;
for (i = 0; i < num_args; i++) {
if (m_has_term_ite.is_marked(to_app(t)->get_arg(i)))
break;
}
if (i < num_args) {
m_has_term_ite.mark(t);
TRACE("cofactor_ite_analyzer", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";);
save_candidate(t, form_ctx);
}
}
else {
SASSERT(is_quantifier(t) || is_var(t));
// ignoring quantifiers... they are treated as black boxes.
}
m_processed.mark(t);
m_frame_stack.pop_back();
}
m_processed.reset();
m_has_term_ite.reset();
}
};
expr * get_first(expr * t) {
typedef std::pair<expr *, unsigned> frame;
expr_fast_mark1 visited;
sbuffer<frame> stack;
stack.push_back(frame(t, 0));
while (!stack.empty()) {
start:
checkpoint();
frame & fr = stack.back();
expr * curr = fr.first;
if (m.is_term_ite(curr))
return to_app(curr)->get_arg(0);
switch (curr->get_kind()) {
case AST_VAR:
case AST_QUANTIFIER:
// ignore quantifiers
stack.pop_back();
break;
case AST_APP: {
unsigned num_args = to_app(curr)->get_num_args();
while (fr.second < num_args) {
expr * arg = to_app(curr)->get_arg(fr.second);
fr.second++;
if (arg->get_ref_count() > 1) {
if (visited.is_marked(arg))
continue;
visited.mark(arg);
}
switch (arg->get_kind()) {
case AST_VAR:
case AST_QUANTIFIER:
// ingore quantifiers
break;
case AST_APP:
if (to_app(arg)->get_num_args() > 0) {
stack.push_back(frame(arg, 0));
goto start;
}
break;
default:
UNREACHABLE();
break;
}
}
stack.pop_back();
break;
}
default:
UNREACHABLE();
break;
}
}
return 0;
}
/**
\brief Fuctor for selecting the term if-then-else condition with the most number of occurrences.
*/
expr * get_best(expr * t) {
typedef std::pair<expr *, unsigned> frame;
obj_map<expr, unsigned> occs;
expr_fast_mark1 visited;
sbuffer<frame> stack;
stack.push_back(frame(t, 0));
while (!stack.empty()) {
start:
checkpoint();
frame & fr = stack.back();
expr * curr = fr.first;
switch (curr->get_kind()) {
case AST_VAR:
case AST_QUANTIFIER:
// ignore quantifiers
stack.pop_back();
break;
case AST_APP: {
unsigned num_args = to_app(curr)->get_num_args();
bool is_term_ite = m.is_term_ite(curr);
while (fr.second < num_args) {
expr * arg = to_app(curr)->get_arg(fr.second);
if (fr.second == 0 && is_term_ite) {
unsigned num = 0;
if (occs.find(arg, num))
occs.insert(arg, num+1);
else
occs.insert(arg, 1);
}
fr.second++;
if (arg->get_ref_count() > 1) {
if (visited.is_marked(arg))
continue;
visited.mark(arg);
}
switch (arg->get_kind()) {
case AST_VAR:
case AST_QUANTIFIER:
// ingore quantifiers
break;
case AST_APP:
if (to_app(arg)->get_num_args() > 0) {
stack.push_back(frame(arg, 0));
goto start;
}
break;
default:
UNREACHABLE();
break;
}
}
stack.pop_back();
break;
}
default:
UNREACHABLE();
break;
}
}
expr * best = 0;
unsigned best_occs = 0;
obj_map<expr, unsigned>::iterator it = occs.begin();
obj_map<expr, unsigned>::iterator end = occs.end();
for (; it != end; ++it) {
if ((!best) ||
(get_depth(it->m_key) < get_depth(best)) ||
(get_depth(it->m_key) == get_depth(best) && it->m_value > best_occs) ||
// break ties by giving preference to equalities
(get_depth(it->m_key) == get_depth(best) && it->m_value == best_occs && m.is_eq(it->m_key) && !m.is_eq(best))) {
best = it->m_key;
best_occs = it->m_value;
}
}
visited.reset();
CTRACE("cofactor_ite_get_best", best != 0, tout << "best num-occs: " << best_occs << "\n" << mk_ismt2_pp(best, m) << "\n";);
return best;
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
void set_cancel(bool f) {
m_cancel = f;
}
struct cofactor_rw_cfg : public default_rewriter_cfg {
ast_manager & m;
imp & m_owner;
obj_hashtable<expr> * m_has_term_ite;
mk_simplified_app m_mk_app;
expr * m_atom;
bool m_sign;
expr * m_term;
app * m_value;
bool m_strict_lower;
app * m_lower;
bool m_strict_upper;
app * m_upper;
cofactor_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable<expr> * has_term_ite = 0):
m(_m),
m_owner(owner),
m_has_term_ite(has_term_ite),
m_mk_app(m, owner.m_params) {
}
bool max_steps_exceeded(unsigned num_steps) const {
m_owner.checkpoint();
return false;
}
bool pre_visit(expr * t) {
return true;
}
void set_cofactor_atom(expr * t) {
if (m.is_not(t)) {
m_atom = to_app(t)->get_arg(0);
m_sign = true;
m_term = 0;
// TODO: bounds
}
else {
m_atom = t;
m_sign = false;
m_term = 0;
expr * lhs;
expr * rhs;
if (m.is_eq(t, lhs, rhs)) {
if (m.is_value(lhs)) {
m_term = rhs;
m_value = to_app(lhs);
TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";);
}
else if (m.is_value(rhs)) {
m_term = lhs;
m_value = to_app(rhs);
TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";);
}
}
// TODO: bounds
}
}
bool rewrite_patterns() 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_mk_app.mk_core(f, num, args, result);
}
bool get_subst(expr * s, expr * & t, proof * & pr) {
pr = 0;
if (s == m_atom) {
t = m_sign ? m.mk_false() : m.mk_true();
return true;
}
if (s == m_term && m_value != 0) {
t = m_value;
return true;
}
// TODO: handle simple bounds
// Example: s is of the form (<= s 10) and m_term == s, and m_upper is 9
// then rewrite to true.
return false;
}
};
struct cofactor_rw : rewriter_tpl<cofactor_rw_cfg> {
cofactor_rw_cfg m_cfg;
public:
cofactor_rw(ast_manager & m, imp & owner, obj_hashtable<expr> * has_term_ite = 0):
rewriter_tpl<cofactor_rw_cfg>(m, false, m_cfg),
m_cfg(m, owner, has_term_ite) {
}
void set_cofactor_atom(expr * t) {
m_cfg.set_cofactor_atom(t);
reset();
}
};
struct main_rw_cfg : public default_rewriter_cfg {
ast_manager & m;
imp & m_owner;
cofactor_rw m_cofactor;
obj_hashtable<expr> const & m_candidates;
obj_map<expr, expr*> m_cache;
expr_ref_vector m_cache_domain;
main_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable<expr> & candidates):
m(_m),
m_owner(owner),
m_cofactor(m, m_owner),
m_candidates(candidates),
m_cache_domain(_m) {
}
bool max_steps_exceeded(unsigned num_steps) const {
m_owner.checkpoint();
return false;
}
bool pre_visit(expr * t) {
return m.is_bool(t) && !is_quantifier(t);
}
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
if (m_candidates.contains(s)) {
t_pr = 0;
if (m_cache.find(s, t))
return true;
unsigned step = 0;
TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(s, m) << "\n";);
expr_ref curr(m);
curr = s;
while (true) {
// expr * c = m_owner.get_best(curr);
expr * c = m_owner.get_first(curr);
if (c == 0) {
m_cache.insert(s, curr);
m_cache_domain.push_back(curr);
t = curr.get();
return true;
}
step++;
expr_ref pos_cofactor(m);
expr_ref neg_cofactor(m);
m_cofactor.set_cofactor_atom(c);
m_cofactor(curr, pos_cofactor);
expr_ref neg_c(m);
neg_c = m.is_not(c) ? to_app(c)->get_arg(0) : m.mk_not(c);
m_cofactor.set_cofactor_atom(neg_c);
m_cofactor(curr, neg_cofactor);
curr = m.mk_ite(c, pos_cofactor, neg_cofactor);
TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";);
}
}
return false;
}
};
struct main_rw : rewriter_tpl<main_rw_cfg> {
main_rw_cfg m_cfg;
public:
main_rw(ast_manager & m, imp & owner, obj_hashtable<expr> & candidates):
rewriter_tpl<main_rw_cfg>(m, false, m_cfg),
m_cfg(m, owner, candidates) {
}
};
struct bottom_up_elim {
typedef std::pair<expr*, bool> frame;
ast_manager & m;
imp & m_owner;
obj_map<expr, expr*> m_cache;
expr_ref_vector m_cache_domain;
obj_hashtable<expr> m_has_term_ite;
svector<frame> m_frames;
cofactor_rw m_cofactor;
bottom_up_elim(ast_manager & _m, imp & owner):
m(_m),
m_owner(owner),
m_cache_domain(m),
m_cofactor(m, owner, &m_has_term_ite) {
}
bool is_atom(expr * t) const {
if (!m.is_bool(t))
return false;
if (!is_app(t))
return false;
if (to_app(t)->get_family_id() == m.get_basic_family_id()) {
switch (to_app(t)->get_decl_kind()) {
case OP_EQ:
case OP_DISTINCT:
if (m.is_bool(to_app(t)->get_arg(0)))
return false;
else
return true;
default:
return false;
}
}
return true;
}
void cofactor(expr * t, expr_ref & r) {
unsigned step = 0;
TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";);
expr_ref curr(m);
curr = t;
while (true) {
expr * c = m_owner.get_best(curr);
// expr * c = m_owner.get_first(curr);
if (c == 0) {
r = curr.get();
return;
}
step++;
expr_ref pos_cofactor(m);
expr_ref neg_cofactor(m);
m_cofactor.set_cofactor_atom(c);
m_cofactor(curr, pos_cofactor);
expr_ref neg_c(m);
neg_c = m.is_not(c) ? to_app(c)->get_arg(0) : m.mk_not(c);
m_cofactor.set_cofactor_atom(neg_c);
m_cofactor(curr, neg_cofactor);
if (pos_cofactor == neg_cofactor) {
curr = pos_cofactor;
TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";);
continue;
}
if (m.is_true(pos_cofactor) && m.is_false(neg_cofactor)) {
curr = c;
TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";);
continue;
}
if (m.is_false(pos_cofactor) && m.is_true(neg_cofactor)) {
curr = neg_c;
TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";);
continue;
}
curr = m.mk_ite(c, pos_cofactor, neg_cofactor);
TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";);
}
}
void visit(expr * t, bool & visited) {
if (!m_cache.contains(t)) {
m_frames.push_back(frame(t, true));
visited = false;
}
}
void operator()(expr * t, expr_ref & r) {
ptr_vector<expr> new_args;
m_frames.push_back(frame(t, true));
while (!m_frames.empty()) {
m_owner.checkpoint();
frame & fr = m_frames.back();
expr * t = fr.first;
TRACE("cofactor_bug", tout << "processing: " << t->get_id() << " :first " << fr.second << "\n";);
if (!is_app(t)) {
m_cache.insert(t, t);
m_frames.pop_back();
continue;
}
if (m_cache.contains(t)) {
m_frames.pop_back();
continue;
}
if (fr.second) {
fr.second = false;
bool visited = true;
unsigned i = to_app(t)->get_num_args();
while (i > 0) {
--i;
expr * arg = to_app(t)->get_arg(i);
visit(arg, visited);
}
if (!visited)
continue;
}
new_args.reset();
bool has_new_args = false;
bool has_term_ite = false;
unsigned num = to_app(t)->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * arg = to_app(t)->get_arg(i);
expr * new_arg = 0;
TRACE("cofactor_bug", tout << "collecting child: " << arg->get_id() << "\n";);
m_cache.find(arg, new_arg);
SASSERT(new_arg != 0);
if (new_arg != arg)
has_new_args = true;
if (m_has_term_ite.contains(new_arg))
has_term_ite = true;
new_args.push_back(new_arg);
}
if (m.is_term_ite(t))
has_term_ite = true;
expr_ref new_t(m);
if (has_new_args)
new_t = m.mk_app(to_app(t)->get_decl(), num, new_args.c_ptr());
else
new_t = t;
if (has_term_ite && is_atom(new_t)) {
// update new_t
expr_ref new_new_t(m);
m_has_term_ite.insert(new_t);
cofactor(new_t, new_new_t);
m_has_term_ite.erase(new_t);
new_t = new_new_t;
has_term_ite = false;
}
if (has_term_ite)
m_has_term_ite.insert(new_t);
SASSERT(new_t.get() != 0);
TRACE("cofactor_bug", tout << "caching: " << t->get_id() << "\n";);
#if 0
counter ++;
verbose_stream() << counter << "\n";
#endif
m_cache.insert(t, new_t);
m_cache_domain.push_back(new_t);
m_frames.pop_back();
}
expr * result = 0;
m_cache.find(t, result);
r = result;
}
};
imp(ast_manager & _m, params_ref const & p):
m(_m),
m_params(p) {
m_cancel = false;
updt_params(p);
}
void operator()(expr * t, expr_ref & r) {
#if 0
analyzer proc(m, *this);
proc(t);
main_rw rw(m, *this, proc.m_candidates);
rw(t, r);
#else
bottom_up_elim proc(m, *this);
proc(t, r);
#endif
}
};
cofactor_elim_term_ite::cofactor_elim_term_ite(ast_manager & m, params_ref const & p):
m_imp(alloc(imp, m, p)),
m_params(p) {
}
cofactor_elim_term_ite::~cofactor_elim_term_ite() {
imp * d = m_imp;
#pragma omp critical (cofactor_elim_term_ite)
{
m_imp = 0;
}
dealloc(d);
}
void cofactor_elim_term_ite::updt_params(params_ref const & p) {
m_imp->updt_params(p);
}
void cofactor_elim_term_ite::get_param_descrs(param_descrs & r) {
}
void cofactor_elim_term_ite::operator()(expr * t, expr_ref & r) {
m_imp->operator()(t, r);
}
void cofactor_elim_term_ite::set_cancel(bool f) {
#pragma omp critical (cofactor_elim_term_ite)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
void cofactor_elim_term_ite::cleanup() {
ast_manager & m = m_imp->m;
#pragma omp critical (cofactor_elim_term_ite)
{
dealloc(m_imp);
m_imp = alloc(imp, m, m_params);
}
}

View file

@ -0,0 +1,44 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
cofactor_elim_term_ite.h
Abstract:
Eliminate (ground) term if-then-else's using cofactors.
Author:
Leonardo de Moura (leonardo) 2011-06-05.
Revision History:
--*/
#ifndef _COFACTOR_ELIM_TERM_ITE_H_
#define _COFACTOR_ELIM_TERM_ITE_H_
#include"ast.h"
#include"params.h"
class cofactor_elim_term_ite {
struct imp;
imp * m_imp;
params_ref m_params;
public:
cofactor_elim_term_ite(ast_manager & m, params_ref const & p = params_ref());
virtual ~cofactor_elim_term_ite();
void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
void operator()(expr * t, expr_ref & r);
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
void set_cancel(bool f);
void cleanup();
};
#endif

View file

@ -0,0 +1,82 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
cofactor_term_ite_tactic.cpp
Abstract:
Wrap cofactor_elim_term_ite as a tactic.
Eliminate (ground) term if-then-else's using cofactors.
Author:
Leonardo de Moura (leonardo) 2012-02-20.
Revision History:
--*/
#include"tactical.h"
#include"cofactor_elim_term_ite.h"
/**
\brief Wrapper for applying cofactor_elim_term_ite in an assertion set.
*/
class cofactor_term_ite_tactic : public tactic {
params_ref m_params;
cofactor_elim_term_ite m_elim_ite;
void process(goal & g) {
ast_manager & m = g.m();
unsigned sz = g.size();
for (unsigned i = 0; i < sz; i++) {
if (g.inconsistent())
break;
expr * f = g.form(i);
expr_ref new_f(m);
m_elim_ite(f, new_f);
g.update(i, new_f);
}
}
public:
cofactor_term_ite_tactic(ast_manager & m, params_ref const & p):
m_params(p),
m_elim_ite(m, p) {
}
virtual tactic * translate(ast_manager & m) {
return alloc(cofactor_term_ite_tactic, m, m_params);
}
virtual ~cofactor_term_ite_tactic() {}
virtual void updt_params(params_ref const & p) { m_params = p; m_elim_ite.updt_params(p); }
static void get_param_descrs(param_descrs & r) { cofactor_elim_term_ite::get_param_descrs(r); }
virtual void collect_param_descrs(param_descrs & r) { 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());
fail_if_proof_generation("cofactor-term-ite", g);
fail_if_unsat_core_generation("cofactor-term-ite", g);
tactic_report report("cofactor-term-ite", *g);
mc = 0; pc = 0; core = 0;
process(*(g.get()));
g->inc_depth();
result.push_back(g.get());
TRACE("cofactor-term-ite", g->display(tout););
SASSERT(g->is_well_sorted());
}
virtual void cleanup() { return m_elim_ite.cleanup(); }
virtual void set_cancel(bool f) { m_elim_ite.set_cancel(f); }
};
tactic * mk_cofactor_term_ite_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(cofactor_term_ite_tactic, m, p));
}

View file

@ -0,0 +1,29 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
cofactor_term_ite_tactic.h
Abstract:
Wrap cofactor_elim_term_ite as a tactic.
Eliminate (ground) term if-then-else's using cofactors.
Author:
Leonardo de Moura (leonardo) 2012-02-20.
Revision History:
--*/
#ifndef _COFACTOR_TERM_ITE_TACTIC_H_
#define _COFACTOR_TERM_ITE_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_cofactor_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,555 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
ctx_simplify_tactic.cpp
Abstract:
Simple context simplifier for propagating constants.
Author:
Leonardo (leonardo) 2011-10-26
Notes:
--*/
#include"ctx_simplify_tactic.h"
#include"mk_simplified_app.h"
#include"num_occurs_goal.h"
#include"cooperate.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
struct ctx_simplify_tactic::imp {
struct cached_result {
expr * m_to;
unsigned m_lvl;
cached_result * m_next;
cached_result(expr * t, unsigned lvl, cached_result * next):
m_to(t),
m_lvl(lvl),
m_next(next) {
}
};
struct cache_cell {
expr * m_from;
cached_result * m_result;
cache_cell():m_from(0), m_result(0) {}
};
ast_manager & m;
small_object_allocator m_allocator;
obj_map<expr, expr*> m_assertions;
ptr_vector<expr> m_trail;
svector<unsigned> m_scopes;
svector<cache_cell> m_cache;
vector<ptr_vector<expr> > m_cache_undo;
unsigned m_scope_lvl;
unsigned m_depth;
unsigned m_num_steps;
num_occurs_goal m_occs;
mk_simplified_app m_mk_app;
unsigned long long m_max_memory;
unsigned m_max_depth;
unsigned m_max_steps;
bool m_bail_on_blowup;
volatile bool m_cancel;
imp(ast_manager & _m, params_ref const & p):
m(_m),
m_allocator("context-simplifier"),
m_occs(true, true),
m_mk_app(m, p) {
m_cancel = false;
m_scope_lvl = 0;
updt_params(p);
}
void set_cancel(bool f) {
m_cancel = f;
}
~imp() {
pop(m_scope_lvl);
SASSERT(m_scope_lvl == 0);
restore_cache(0);
DEBUG_CODE({
for (unsigned i = 0; i < m_cache.size(); i++) {
CTRACE("ctx_simplify_tactic_bug", m_cache[i].m_from,
tout << "i: " << i << "\n" << mk_ismt2_pp(m_cache[i].m_from, m) << "\n";
tout << "m_result: " << m_cache[i].m_result << "\n";
if (m_cache[i].m_result) tout << "lvl: " << m_cache[i].m_result->m_lvl << "\n";);
SASSERT(m_cache[i].m_from == 0);
SASSERT(m_cache[i].m_result == 0);
}
});
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
m_max_depth = p.get_uint(":max-depth", 1024);
m_bail_on_blowup = p.get_bool(":bail-on-blowup", false);
}
void checkpoint() {
cooperate("ctx_simplify_tactic");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
if (m_cancel)
throw tactic_exception(TACTIC_CANCELED_MSG);
}
bool shared(expr * t) const {
return t->get_ref_count() > 1 && m_occs.get_num_occs(t) > 1;
}
bool check_cache() {
for (unsigned i = 0; i < m_cache.size(); i++) {
cache_cell & cell = m_cache[i];
if (cell.m_from != 0) {
SASSERT(cell.m_result != 0);
cached_result * curr = cell.m_result;
while (curr) {
SASSERT(curr->m_lvl <= scope_level());
curr = curr->m_next;
}
}
}
return true;
}
void cache_core(expr * from, expr * to) {
TRACE("ctx_simplify_tactic_cache", tout << "caching\n" << mk_ismt2_pp(from, m) << "\n--->\n" << mk_ismt2_pp(to, m) << "\n";);
unsigned id = from->get_id();
m_cache.reserve(id+1);
cache_cell & cell = m_cache[id];
void * mem = m_allocator.allocate(sizeof(cached_result));
if (cell.m_from == 0) {
// new_entry
cell.m_from = from;
cell.m_result = new (mem) cached_result(to, m_scope_lvl, 0);
m.inc_ref(from);
m.inc_ref(to);
}
else {
// update
cell.m_result = new (mem) cached_result(to, m_scope_lvl, cell.m_result);
m.inc_ref(to);
}
m_cache_undo.reserve(m_scope_lvl+1);
m_cache_undo[m_scope_lvl].push_back(from);
}
void cache(expr * from, expr * to) {
if (shared(from))
cache_core(from, to);
}
unsigned scope_level() const {
return m_scope_lvl;
}
void push() {
m_scope_lvl++;
m_scopes.push_back(m_trail.size());
}
void restore_cache(unsigned lvl) {
if (lvl >= m_cache_undo.size())
return;
ptr_vector<expr> & keys = m_cache_undo[lvl];
ptr_vector<expr>::iterator it = keys.end();
ptr_vector<expr>::iterator begin = keys.begin();
while (it != begin) {
--it;
expr * key = *it;
unsigned key_id = key->get_id();
cache_cell & cell = m_cache[key_id];
SASSERT(cell.m_from == key);
SASSERT(cell.m_result != 0);
m.dec_ref(cell.m_result->m_to);
cached_result * to_delete = cell.m_result;
SASSERT(to_delete->m_lvl == lvl);
TRACE("ctx_simplify_tactic_cache", tout << "uncaching: " << to_delete->m_lvl << "\n" <<
mk_ismt2_pp(key, m) << "\n--->\n" << mk_ismt2_pp(to_delete->m_to, m) << "\nrestoring:\n";
if (to_delete->m_next) tout << mk_ismt2_pp(to_delete->m_next->m_to, m); else tout << "<null>";
tout << "\n";);
cell.m_result = to_delete->m_next;
if (cell.m_result == 0) {
m.dec_ref(cell.m_from);
cell.m_from = 0;
}
m_allocator.deallocate(sizeof(cached_result), to_delete);
}
keys.reset();
}
void pop(unsigned num_scopes) {
if (num_scopes == 0)
return;
SASSERT(num_scopes <= m_scope_lvl);
SASSERT(m_scope_lvl == m_scopes.size());
// undo assertions
unsigned old_trail_size = m_scopes[m_scope_lvl - num_scopes];
unsigned i = m_trail.size();
while (i > old_trail_size) {
--i;
expr * key = m_trail.back();
m_assertions.erase(key);
m_trail.pop_back();
}
SASSERT(m_trail.size() == old_trail_size);
m_scopes.shrink(m_scope_lvl - num_scopes);
// restore cache
for (unsigned i = 0; i < num_scopes; i++) {
restore_cache(m_scope_lvl);
m_scope_lvl--;
}
CASSERT("ctx_simplify_tactic", check_cache());
}
void assert_eq_core(expr * t, app * val) {
if (m_assertions.contains(t)) {
// This branch can only happen when m_max_depth was reached.
// It can happen when m_assertions contains an entry t->val',
// but (= t val) was not simplified to (= val' val)
// because the simplifier stopped at depth m_max_depth
return;
}
CTRACE("assert_eq_bug", m_assertions.contains(t), tout << "m_depth: " << m_depth << " m_max_depth: " << m_max_depth << "\n"
<< "t:\n" << mk_ismt2_pp(t, m) << "\nval:\n" << mk_ismt2_pp(val, m) << "\n";
expr * old_val = 0;
m_assertions.find(t, old_val);
tout << "old_val:\n" << mk_ismt2_pp(old_val, m) << "\n";);
m_assertions.insert(t, val);
m_trail.push_back(t);
}
void assert_eq_val(expr * t, app * val, bool mk_scope) {
if (shared(t)) {
if (mk_scope)
push();
assert_eq_core(t, val);
}
}
void assert_expr(expr * t, bool sign) {
if (m.is_not(t)) {
t = to_app(t)->get_arg(0);
sign = !sign;
}
bool mk_scope = true;
if (shared(t)) {
push();
mk_scope = false;
assert_eq_core(t, sign ? m.mk_false() : m.mk_true());
}
expr * lhs, * rhs;
if (!sign && m.is_eq(t, lhs, rhs)) {
if (m.is_value(rhs))
assert_eq_val(lhs, to_app(rhs), mk_scope);
else if (m.is_value(lhs))
assert_eq_val(rhs, to_app(lhs), mk_scope);
}
}
bool is_cached(expr * t, expr_ref & r) {
unsigned id = t->get_id();
if (id >= m_cache.size())
return false;
cache_cell & cell = m_cache[id];
SASSERT(cell.m_result == 0 || cell.m_result->m_lvl <= scope_level());
if (cell.m_result != 0 && cell.m_result->m_lvl == scope_level()) {
SASSERT(cell.m_from == t);
SASSERT(cell.m_result->m_to != 0);
r = cell.m_result->m_to;
return true;
}
return false;
}
void simplify(expr * t, expr_ref & r) {
r = 0;
if (m_depth >= m_max_depth || m_num_steps >= m_max_steps || !is_app(t)) {
r = t;
return;
}
checkpoint();
TRACE("ctx_simplify_tactic_detail", tout << "processing: " << mk_bounded_pp(t, m) << "\n";);
expr * _r;
if (m_assertions.find(t, _r)) {
r = _r;
SASSERT(r.get() != 0);
return;
}
if (is_cached(t, r)) {
SASSERT(r.get() != 0);
return;
}
m_num_steps++;
m_depth++;
if (m.is_or(t))
simplify_or_and<true>(to_app(t), r);
else if (m.is_and(t))
simplify_or_and<false>(to_app(t), r);
else if (m.is_ite(t))
simplify_ite(to_app(t), r);
else
simplify_app(to_app(t), r);
m_depth--;
SASSERT(r.get() != 0);
TRACE("ctx_simplify_tactic_detail", tout << "result:\n" << mk_bounded_pp(t, m) << "\n---->\n" << mk_bounded_pp(r, m) << "\n";);
}
template<bool OR>
void simplify_or_and(app * t, expr_ref & r) {
// go forwards
expr_ref_buffer new_args(m);
unsigned old_lvl = scope_level();
bool modified = false;
unsigned num_args = t->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = t->get_arg(i);
expr_ref new_arg(m);
simplify(arg, new_arg);
if (new_arg != arg)
modified = true;
if ((OR && m.is_false(new_arg)) ||
(!OR && m.is_true(new_arg))) {
modified = true;
continue;
}
if ((OR && m.is_true(new_arg)) ||
(!OR && m.is_false(new_arg))) {
r = new_arg;
pop(scope_level() - old_lvl);
cache(t, r);
return;
}
new_args.push_back(new_arg);
if (i < num_args - 1)
assert_expr(new_arg, OR);
}
pop(scope_level() - old_lvl);
// go backwards
expr_ref_buffer new_new_args(m);
unsigned i = new_args.size();
while (i > 0) {
--i;
expr * arg = new_args[i];
expr_ref new_arg(m);
simplify(arg, new_arg);
if (new_arg != arg)
modified = true;
if ((OR && m.is_false(new_arg)) ||
(!OR && m.is_true(new_arg))) {
modified = true;
continue;
}
if ((OR && m.is_true(new_arg)) ||
(!OR && m.is_false(new_arg))) {
r = new_arg;
pop(scope_level() - old_lvl);
cache(t, r);
return;
}
new_new_args.push_back(new_arg);
if (i > 0)
assert_expr(new_arg, OR);
}
pop(scope_level() - old_lvl);
if (!modified) {
r = t;
}
else {
std::reverse(new_new_args.c_ptr(), new_new_args.c_ptr() + new_new_args.size());
m_mk_app(t->get_decl(), new_new_args.size(), new_new_args.c_ptr(), r);
}
cache(t, r);
}
void simplify_ite(app * ite, expr_ref & r) {
expr * c = ite->get_arg(0);
expr * t = ite->get_arg(1);
expr * e = ite->get_arg(2);
expr_ref new_c(m);
unsigned old_lvl = scope_level();
simplify(c, new_c);
if (m.is_true(new_c)) {
simplify(t, r);
}
else if (m.is_false(new_c)) {
simplify(e, r);
}
else {
expr_ref new_t(m);
expr_ref new_e(m);
assert_expr(new_c, false);
simplify(t, new_t);
pop(scope_level() - old_lvl);
assert_expr(new_c, true);
simplify(e, new_e);
pop(scope_level() - old_lvl);
if (c == new_c && t == new_t && e == new_e) {
r = ite;
}
else {
expr * args[3] = { new_c.get(), new_t.get(), new_e.get() };
TRACE("ctx_simplify_tactic_ite_bug",
tout << "mk_ite\n" << mk_ismt2_pp(new_c.get(), m) << "\n" << mk_ismt2_pp(new_t.get(), m)
<< "\n" << mk_ismt2_pp(new_e.get(), m) << "\n";);
m_mk_app(ite->get_decl(), 3, args, r);
}
}
cache(ite, r);
}
void simplify_app(app * t, expr_ref & r) {
if (t->get_num_args() == 0) {
r = t;
return;
}
expr_ref_buffer new_args(m);
bool modified = false;
unsigned num_args = t->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = t->get_arg(i);
expr_ref new_arg(m);
simplify(arg, new_arg);
CTRACE("ctx_simplify_tactic_bug", new_arg.get() == 0, tout << mk_ismt2_pp(arg, m) << "\n";);
SASSERT(new_arg);
if (new_arg != arg)
modified = true;
new_args.push_back(new_arg);
}
if (!modified) {
r = t;
}
else {
m_mk_app(t->get_decl(), new_args.size(), new_args.c_ptr(), r);
}
}
unsigned expr_size(expr* s) {
ast_mark visit;
unsigned sz = 0;
ptr_vector<expr> todo;
todo.push_back(s);
while (!todo.empty()) {
s = todo.back();
todo.pop_back();
if (visit.is_marked(s)) {
continue;
}
visit.mark(s, true);
++sz;
for (unsigned i = 0; is_app(s) && i < to_app(s)->get_num_args(); ++i) {
todo.push_back(to_app(s)->get_arg(i));
}
}
return sz;
}
void process(expr * s, expr_ref & r) {
TRACE("ctx_simplify_tactic", tout << "simplifying:\n" << mk_ismt2_pp(s, m) << "\n";);
SASSERT(m_scope_lvl == 0);
m_depth = 0;
simplify(s, r);
SASSERT(m_scope_lvl == 0);
SASSERT(m_depth == 0);
SASSERT(r.get() != 0);
TRACE("ctx_simplify_tactic", tout << "result\n" << mk_ismt2_pp(r, m) << " :num-steps " << m_num_steps << "\n";
tout << "old size: " << expr_size(s) << " new size: " << expr_size(r) << "\n";);
if (m_bail_on_blowup && expr_size(s) < expr_size(r)) {
r = s;
}
}
void operator()(goal & g) {
SASSERT(g.is_well_sorted());
bool proofs_enabled = g.proofs_enabled();
m_occs.reset();
m_occs(g);
m_num_steps = 0;
expr_ref r(m);
proof * new_pr = 0;
tactic_report report("ctx-simplify", g);
unsigned sz = g.size();
for (unsigned i = 0; i < sz; i++) {
if (g.inconsistent())
return;
expr * t = g.form(i);
process(t, r);
if (proofs_enabled) {
proof * pr = g.pr(i);
new_pr = m.mk_modus_ponens(pr, m.mk_rewrite_star(t, r, 0, 0)); // TODO :-)
}
g.update(i, r, new_pr, g.dep(i));
}
IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-simplify :num-steps " << m_num_steps << ")\n";);
SASSERT(g.is_well_sorted());
}
};
ctx_simplify_tactic::ctx_simplify_tactic(ast_manager & m, params_ref const & p):
m_imp(alloc(imp, m, p)),
m_params(p) {
}
ctx_simplify_tactic::~ctx_simplify_tactic() {
dealloc(m_imp);
}
void ctx_simplify_tactic::updt_params(params_ref const & p) {
m_params = p;
m_imp->updt_params(p);
}
void ctx_simplify_tactic::get_param_descrs(param_descrs & r) {
insert_max_memory(r);
insert_max_steps(r);
r.insert(":max-depth", CPK_UINT, "(default: 1024) maximum term depth.");
}
void ctx_simplify_tactic::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;
(*m_imp)(*(in.get()));
in->inc_depth();
result.push_back(in.get());
}
void ctx_simplify_tactic::set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
void ctx_simplify_tactic::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;
}
}

View file

@ -0,0 +1,56 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
ctx_simplify_tactic.h
Abstract:
Simple context simplifier for propagating constants.
Author:
Leonardo (leonardo) 2011-10-26
Notes:
--*/
#ifndef _CTX_SIMPLIFY_TACTIC_H_
#define _CTX_SIMPLIFY_TACTIC_H_
#include"tactical.h"
class ctx_simplify_tactic : public tactic {
struct imp;
imp * m_imp;
params_ref m_params;
public:
ctx_simplify_tactic(ast_manager & m, params_ref const & p = params_ref());
virtual tactic * translate(ast_manager & m) {
return alloc(ctx_simplify_tactic, m, m_params);
}
virtual ~ctx_simplify_tactic();
virtual void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
virtual void collect_param_descrs(param_descrs & r) { 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);
virtual void cleanup();
protected:
virtual void set_cancel(bool f);
};
inline tactic * mk_ctx_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()) {
return clean(alloc(ctx_simplify_tactic, m, p));
}
#endif

View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
der_tactic.cpp
Abstract:
DER tactic
Author:
Leonardo de Moura (leonardo) 2012-10-20
--*/
#include"der.h"
#include"tactical.h"
class der_tactic : public tactic {
struct imp {
ast_manager & m_manager;
der_rewriter m_r;
imp(ast_manager & m):
m_manager(m),
m_r(m) {
}
ast_manager & m() const { return m_manager; }
void set_cancel(bool f) {
m_r.set_cancel(f);
}
void reset() {
m_r.reset();
}
void operator()(goal & g) {
SASSERT(g.is_well_sorted());
bool proofs_enabled = g.proofs_enabled();
tactic_report report("der", g);
TRACE("before_der", g.display(tout););
expr_ref new_curr(m());
proof_ref new_pr(m());
unsigned size = g.size();
for (unsigned idx = 0; idx < size; idx++) {
if (g.inconsistent())
break;
expr * curr = g.form(idx);
m_r(curr, new_curr, new_pr);
if (proofs_enabled) {
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.elim_redundancies();
TRACE("after_der", g.display(tout););
SASSERT(g.is_well_sorted());
}
};
imp * m_imp;
public:
der_tactic(ast_manager & m) {
m_imp = alloc(imp, m);
}
virtual tactic * translate(ast_manager & m) {
return alloc(der_tactic, m);
}
virtual ~der_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) {
mc = 0; pc = 0; core = 0;
(*m_imp)(*(in.get()));
in->inc_depth();
result.push_back(in.get());
}
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;
}
}
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_der_tactic(ast_manager & m) {
return alloc(der_tactic, m);
}

View file

@ -0,0 +1,25 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
der_tactic.h
Abstract:
DER tactic
Author:
Leonardo de Moura (leonardo) 2012-10-20
--*/
#ifndef _DER_TACTIC_H_
#define _DER_TACTIC_H_
class ast_manager;
class tactic;
tactic * mk_der_tactic(ast_manager & m);
#endif /* _DER_TACTIC_H_ */

View file

@ -0,0 +1,149 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
distribute_forall_tactic.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2012-02-18.
--*/
#include"tactical.h"
#include"rewriter_def.h"
#include"var_subst.h"
class distribute_forall_tactic : public tactic {
struct rw_cfg : public default_rewriter_cfg {
ast_manager & m;
rw_cfg(ast_manager & _m):m(_m) {}
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) {
// (forall X (not (or F1 ... Fn)))
// -->
// (and (forall X (not F1))
// ...
// (forall X (not Fn)))
app * or_e = to_app(to_app(new_body)->get_arg(0));
unsigned num_args = or_e->get_num_args();
expr_ref_buffer new_args(m);
for (unsigned i = 0; i < num_args; i++) {
expr * arg = or_e->get_arg(i);
expr * not_arg = m.mk_not(arg);
quantifier_ref tmp_q(m);
tmp_q = m.update_quantifier(old_q, not_arg);
expr_ref new_q(m);
elim_unused_vars(m, tmp_q, new_q);
new_args.push_back(new_q);
}
result = m.mk_and(new_args.size(), new_args.c_ptr());
return true;
}
if (m.is_and(new_body)) {
// (forall X (and F1 ... Fn))
// -->
// (and (forall X F1)
// ...
// (forall X Fn)
unsigned num_args = to_app(new_body)->get_num_args();
expr_ref_buffer new_args(m);
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(new_body)->get_arg(i);
quantifier_ref tmp_q(m);
tmp_q = m.update_quantifier(old_q, arg);
expr_ref new_q(m);
elim_unused_vars(m, tmp_q, new_q);
new_args.push_back(new_q);
}
result = m.mk_and(new_args.size(), new_args.c_ptr());
return true;
}
return false;
}
};
struct rw : public rewriter_tpl<rw_cfg> {
rw_cfg m_cfg;
rw(ast_manager & m, bool proofs_enabled):
rewriter_tpl<rw_cfg>(m, proofs_enabled, m_cfg),
m_cfg(m) {
}
};
rw * m_rw;
public:
distribute_forall_tactic():m_rw(0) {}
virtual tactic * translate(ast_manager & m) {
return alloc(distribute_forall_tactic);
}
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());
ast_manager & m = g->m();
bool produce_proofs = g->proofs_enabled();
rw r(m, produce_proofs);
#pragma omp critical (tactic_cancel)
{
m_rw = &r;
}
mc = 0; pc = 0; core = 0; result.reset();
tactic_report report("distribute-forall", *g);
expr_ref new_curr(m);
proof_ref new_pr(m);
unsigned size = g->size();
for (unsigned idx = 0; idx < size; idx++) {
if (g->inconsistent())
break;
expr * curr = g->form(idx);
r(curr, new_curr, new_pr);
if (g->proofs_enabled()) {
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("distribute-forall", g->display(tout););
SASSERT(g->is_well_sorted());
#pragma omp critical (tactic_cancel)
{
m_rw = 0;
}
}
virtual void set_cancel(bool f) {
if (m_rw)
m_rw->set_cancel(f);
}
virtual void cleanup() {}
};
tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) {
return alloc(distribute_forall_tactic);
}

View file

@ -0,0 +1,26 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
distribute_forall_tactic.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2012-02-18.
--*/
#ifndef _DISTRIBUTE_FORALL_TACTIC_H_
#define _DISTRIBUTE_FORALL_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p);
#endif

View file

@ -0,0 +1,198 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
elim_term_ite_tactic.cpp
Abstract:
Eliminate term if-then-else by adding
new fresh auxiliary variables.
Author:
Leonardo (leonardo) 2011-12-29
Notes:
--*/
#include"tactical.h"
#include"defined_names.h"
#include"rewriter_def.h"
#include"filter_model_converter.h"
#include"cooperate.h"
class elim_term_ite_tactic : public tactic {
struct rw_cfg : public default_rewriter_cfg {
ast_manager & m;
defined_names m_defined_names;
ref<filter_model_converter> m_mc;
goal * m_goal;
unsigned long long m_max_memory; // in bytes
bool m_produce_models;
unsigned m_num_fresh;
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("elim term ite");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
return false;
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
if (!m.is_term_ite(f))
return BR_FAILED;
expr_ref new_ite(m);
new_ite = m.mk_app(f, num, args);
expr_ref new_def(m);
proof_ref new_def_pr(m);
app_ref _result(m);
if (m_defined_names.mk_name(new_ite, new_def, new_def_pr, _result, result_pr)) {
m_goal->assert_expr(new_def, new_def_pr, 0);
m_num_fresh++;
if (m_produce_models) {
if (!m_mc)
m_mc = alloc(filter_model_converter, m);
m_mc->insert(_result->get_decl());
}
}
result = _result.get();
return BR_DONE;
}
rw_cfg(ast_manager & _m, params_ref const & p):
m(_m),
m_defined_names(m, 0 /* don't use prefix */) {
updt_params(p);
m_goal = 0;
m_num_fresh = 0;
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
};
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);
}
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("elim-term-ite", *g);
bool produce_proofs = g->proofs_enabled();
m_rw.cfg().m_produce_models = g->models_enabled();
m_rw.m_cfg.m_num_fresh = 0;
m_rw.m_cfg.m_goal = g.get();
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));
}
mc = m_rw.m_cfg.m_mc.get();
report_tactic_progress(":elim-term-ite-consts", m_rw.m_cfg.m_num_fresh);
g->inc_depth();
result.push_back(g.get());
TRACE("elim_term_ite", g->display(tout););
SASSERT(g->is_well_sorted());
}
};
imp * m_imp;
params_ref m_params;
public:
elim_term_ite_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(elim_term_ite_tactic, m, m_params);
}
virtual ~elim_term_ite_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) {
insert_max_memory(r);
insert_max_steps(r);
r.insert(":max-args", CPK_UINT,
"(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic.");
}
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_elim_term_ite_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(elim_term_ite_tactic, m, p));
}

View file

@ -0,0 +1,29 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
elim_term_ite_tactic.h
Abstract:
Eliminate term if-then-else by adding
new fresh auxiliary variables.
Author:
Leonardo (leonardo) 2011-12-29
Notes:
--*/
#ifndef _ELIM_TERM_ITE_TACTIC_H_
#define _ELIM_TERM_ITE_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_elim_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
elim_uncnstr_tactic.h
Abstract:
Eliminated applications containing unconstrained variables.
Author:
Leonardo (leonardo) 2011-10-22
Notes:
--*/
#ifndef _ELIM_UNCNSTR_TACTIC_H_
#define _ELIM_UNCNSTR_TACTIC_H_
#include"params.h"
class tactic;
class ast_manager;
tactic * mk_elim_uncnstr_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,126 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
nnf_tactic.cpp
Abstract:
NNF tactic
Author:
Leonardo de Moura (leonardo) 2011-12-28.
Revision History:
--*/
#include"nnf.h"
#include"tactical.h"
class nnf_tactic : public tactic {
params_ref m_params;
nnf * m_nnf;
struct set_nnf {
nnf_tactic & m_owner;
set_nnf(nnf_tactic & owner, nnf & n):
m_owner(owner) {
#pragma omp critical (nnf_tactic)
{
m_owner.m_nnf = &n;
}
}
~set_nnf() {
#pragma omp critical (nnf_tactic)
{
m_owner.m_nnf = 0;
}
}
};
public:
nnf_tactic(params_ref const & p):
m_params(p),
m_nnf(0) {
TRACE("nnf", tout << "nnf_tactic constructor: " << p << "\n";);
}
virtual tactic * translate(ast_manager & m) {
return alloc(nnf_tactic, m_params);
}
virtual ~nnf_tactic() {}
virtual void updt_params(params_ref const & p) { m_params = p; }
virtual void collect_param_descrs(param_descrs & r) { nnf::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) {
TRACE("nnf", tout << "params: " << m_params << "\n"; g->display(tout););
SASSERT(g->is_well_sorted());
mc = 0; pc = 0; core = 0;
tactic_report report("nnf", *g);
bool produce_proofs = g->proofs_enabled();
ast_manager & m = g->m();
defined_names dnames(m);
nnf local_nnf(m, dnames, m_params);
set_nnf setter(*this, local_nnf);
expr_ref_vector defs(m);
proof_ref_vector def_prs(m);
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);
local_nnf(curr, defs, def_prs, new_curr, new_pr);
if (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));
}
sz = defs.size();
for (unsigned i = 0; i < sz; i++) {
if (produce_proofs)
g->assert_expr(defs.get(i), def_prs.get(i), 0);
else
g->assert_expr(defs.get(i), 0, 0);
}
g->inc_depth();
result.push_back(g.get());
TRACE("nnf", g->display(tout););
SASSERT(g->is_well_sorted());
}
virtual void cleanup() {}
virtual void set_cancel(bool f) {
#pragma omp critical (nnf_tactic)
{
if (m_nnf)
m_nnf->set_cancel(f);
}
}
};
tactic * mk_snf_tactic(ast_manager & m, params_ref const & p) {
return alloc(nnf_tactic, p);
}
tactic * mk_nnf_tactic(ast_manager & m, params_ref const & p) {
params_ref new_p(p);
new_p.set_sym(":nnf-mode", symbol("full"));
TRACE("nnf", tout << "mk_nnf: " << new_p << "\n";);
return using_params(mk_snf_tactic(m, p), new_p);
}

View file

@ -0,0 +1,30 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
nnf_tactic.h
Abstract:
NNF tactic
Author:
Leonardo de Moura (leonardo) 2011-12-28.
Revision History:
--*/
#ifndef _NNF_TACTIC_H_
#define _NNF_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_snf_tactic(ast_manager & m, params_ref const & p = params_ref());
tactic * mk_nnf_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,252 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
occf_tactic.cpp
Abstract:
Put clauses in the assertion set in
OOC (one constraint per clause) form.
Constraints occuring in formulas that
are not clauses are ignored.
The formula can be put into CNF by
using mk_sat_preprocessor strategy.
Author:
Leonardo de Moura (leonardo) 2011-12-28.
Revision History:
--*/
#include"tactical.h"
#include"occf_tactic.h"
#include"filter_model_converter.h"
#include"cooperate.h"
class occf_tactic : public tactic {
struct imp {
ast_manager & m;
volatile bool m_cancel;
filter_model_converter * m_mc;
imp(ast_manager & _m):
m(_m) {
m_cancel = false;
}
void set_cancel(bool f) {
m_cancel = f;
}
void checkpoint() {
if (m_cancel)
throw tactic_exception(TACTIC_CANCELED_MSG);
cooperate("occf");
}
bool is_literal(expr * t) const {
expr * atom;
return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom));
}
bool is_constraint(expr * t) const {
return !is_literal(t);
}
bool is_target(app * cls) {
SASSERT(m.is_or(cls));
bool found = false;
unsigned num = cls->get_num_args();
for (unsigned i = 0; i < num; i++) {
if (is_constraint(cls->get_arg(i))) {
if (found)
return true;
found = true;
}
}
return false;
}
struct bvar_info {
expr * m_bvar;
unsigned m_gen_pos:1;
unsigned m_gen_neg:1;
bvar_info():m_bvar(0), m_gen_pos(false), m_gen_neg(false) {}
bvar_info(expr * var, bool sign):
m_bvar(var),
m_gen_pos(!sign),
m_gen_neg(sign) {
}
};
typedef obj_map<expr, bvar_info> cnstr2bvar;
expr * get_aux_lit(cnstr2bvar & c2b, expr * cnstr, goal_ref const & g) {
bool sign = false;
while (m.is_not(cnstr)) {
cnstr = to_app(cnstr)->get_arg(0);
sign = !sign;
}
cnstr2bvar::obj_map_entry * entry = c2b.find_core(cnstr);
if (entry == 0)
return 0;
bvar_info & info = entry->get_data().m_value;
if (sign) {
if (!info.m_gen_neg) {
info.m_gen_neg = true;
g->assert_expr(m.mk_or(info.m_bvar, m.mk_not(cnstr)), 0, 0);
}
return m.mk_not(info.m_bvar);
}
else {
if (!info.m_gen_pos) {
info.m_gen_pos = true;
g->assert_expr(m.mk_or(m.mk_not(info.m_bvar), cnstr), 0, 0);
}
return info.m_bvar;
}
}
expr * mk_aux_lit(cnstr2bvar & c2b, expr * cnstr, bool produce_models, goal_ref const & g) {
bool sign = false;
while (m.is_not(cnstr)) {
cnstr = to_app(cnstr)->get_arg(0);
sign = !sign;
}
SASSERT(!c2b.contains(cnstr));
expr * bvar = m.mk_fresh_const(0, m.mk_bool_sort());
if (produce_models)
m_mc->insert(to_app(bvar)->get_decl());
c2b.insert(cnstr, bvar_info(bvar, sign));
if (sign) {
g->assert_expr(m.mk_or(bvar, m.mk_not(cnstr)), 0, 0);
return m.mk_not(bvar);
}
else {
g->assert_expr(m.mk_or(m.mk_not(bvar), cnstr), 0, 0);
return bvar;
}
}
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;
fail_if_proof_generation("occf", g);
bool produce_models = g->models_enabled();
tactic_report report("occf", *g);
m_mc = 0;
ptr_vector<expr> new_lits;
cnstr2bvar c2b;
unsigned sz = g->size();
for (unsigned i = 0; i < sz; i++) {
checkpoint();
expr * f = g->form(i);
expr_dependency * d = g->dep(i);
if (!m.is_or(f))
continue;
app * cls = to_app(f);
if (!is_target(cls))
continue;
if (produce_models && !m_mc) {
m_mc = alloc(filter_model_converter, m);
mc = m_mc;
}
expr * keep = 0;
new_lits.reset();
unsigned num = cls->get_num_args();
for (unsigned j = 0; j < num; j++) {
expr * l = cls->get_arg(j);
if (is_constraint(l)) {
expr * new_l = get_aux_lit(c2b, l, g);
if (new_l != 0) {
new_lits.push_back(new_l);
}
else if (keep == 0) {
keep = l;
}
else {
new_l = mk_aux_lit(c2b, l, produce_models, g);
new_lits.push_back(new_l);
}
}
else {
new_lits.push_back(l);
}
}
if (keep != 0)
new_lits.push_back(keep);
g->update(i, m.mk_or(new_lits.size(), new_lits.c_ptr()), 0, d);
}
g->inc_depth();
result.push_back(g.get());
TRACE("occf", g->display(tout););
SASSERT(g->is_well_sorted());
}
};
imp * m_imp;
public:
occf_tactic(ast_manager & m) {
m_imp = alloc(imp, m);
}
virtual tactic * translate(ast_manager & m) {
return alloc(occf_tactic, m);
}
virtual ~occf_tactic() {
dealloc(m_imp);
}
virtual void updt_params(params_ref const & p) {}
virtual void collect_param_descrs(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);
#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_occf_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(occf_tactic, m));
}

View file

@ -0,0 +1,34 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
occf_tactic.h
Abstract:
Put clauses in the assertion set in
OOC (one constraint per clause) form.
Constraints occuring in formulas that
are not clauses are ignored.
The formula can be put into CNF by
using mk_sat_preprocessor strategy.
Author:
Leonardo de Moura (leonardo) 2011-12-28.
Revision History:
--*/
#ifndef _OCCF_TACTIC_H_
#define _OCCF_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_occf_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -0,0 +1,275 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
propagate_values_tactic.cpp
Abstract:
Propagate values using equalities of the form (= t v) where v is a value,
and atoms t and (not t)
Author:
Leonardo de Moura (leonardo) 2011-12-28.
Revision History:
--*/
#include"tactical.h"
#include"propagate_values_tactic.h"
#include"th_rewriter.h"
#include"ast_smt2_pp.h"
#include"expr_substitution.h"
#include"goal_shared_occs.h"
class propagate_values_tactic : public tactic {
struct imp {
ast_manager & m_manager;
th_rewriter m_r;
scoped_ptr<expr_substitution> m_subst;
goal * m_goal;
goal_shared_occs m_occs;
unsigned m_idx;
unsigned m_max_rounds;
bool m_modified;
imp(ast_manager & m, params_ref const & p):
m_manager(m),
m_r(m, p),
m_goal(0),
m_occs(m, true /* track atoms */) {
updt_params_core(p);
}
void updt_params_core(params_ref const & p) {
m_max_rounds = p.get_uint(":max-rounds", 4);
}
void updt_params(params_ref const & p) {
m_r.updt_params(p);
updt_params_core(p);
}
ast_manager & m() const { return m_manager; }
void set_cancel(bool f) {
m_r.set_cancel(f);
}
bool is_shared(expr * t) {
return m_occs.is_shared(t);
}
bool is_shared_neg(expr * t, expr * & atom) {
if (!m().is_not(t))
return false;
atom = to_app(t)->get_arg(0);
return is_shared(atom);
}
bool is_shared_eq(expr * t, expr * & lhs, expr * & value) {
if (!m().is_eq(t))
return false;
expr * arg1 = to_app(t)->get_arg(0);
expr * arg2 = to_app(t)->get_arg(1);
if (m().is_value(arg1) && is_shared(arg2)) {
lhs = arg2;
value = arg1;
return true;
}
if (m().is_value(arg2) && is_shared(arg1)) {
lhs = arg1;
value = arg2;
return true;
}
return false;
}
void push_result(expr * new_curr, proof * new_pr) {
if (m_goal->proofs_enabled()) {
proof * pr = m_goal->pr(m_idx);
new_pr = m().mk_modus_ponens(pr, new_pr);
}
expr_dependency_ref new_d(m());
if (m_goal->unsat_core_enabled()) {
new_d = m_goal->dep(m_idx);
expr_dependency * used_d = m_r.get_used_dependencies();
if (used_d != 0) {
new_d = m().mk_join(new_d, used_d);
m_r.reset_used_dependencies();
}
}
m_goal->update(m_idx, new_curr, new_pr, new_d);
if (is_shared(new_curr)) {
m_subst->insert(new_curr, m().mk_true(), m().mk_iff_true(new_pr), new_d);
}
expr * atom;
if (is_shared_neg(new_curr, atom)) {
m_subst->insert(atom, m().mk_false(), m().mk_iff_false(new_pr), new_d);
}
expr * lhs, * value;
if (is_shared_eq(new_curr, lhs, value)) {
TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m()) << "\n";);
m_subst->insert(lhs, value, new_pr, new_d);
}
}
void process_current() {
expr * curr = m_goal->form(m_idx);
expr_ref new_curr(m());
proof_ref new_pr(m());
if (!m_subst->empty()) {
m_r(curr, new_curr, new_pr);
}
else {
new_curr = curr;
if (m().proofs_enabled())
new_pr = m().mk_reflexivity(curr);
}
TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m()) << "\n---->\n" << mk_ismt2_pp(new_curr, m()) << "\n";);
push_result(new_curr, new_pr);
if (new_curr != curr)
m_modified = 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());
mc = 0; pc = 0; core = 0;
tactic_report report("propagate-values", *g);
m_goal = g.get();
bool forward = true;
expr_ref new_curr(m());
proof_ref new_pr(m());
unsigned size = m_goal->size();
m_idx = 0;
m_modified = false;
unsigned round = 0;
if (m_goal->inconsistent())
goto end;
m_subst = alloc(expr_substitution, m(), g->unsat_core_enabled(), g->proofs_enabled());
m_r.set_substitution(m_subst.get());
m_occs(*m_goal);
while (true) {
TRACE("propagate_values", m_goal->display(tout););
if (forward) {
for (; m_idx < size; m_idx++) {
process_current();
if (m_goal->inconsistent())
goto end;
}
if (m_subst->empty() && !m_modified)
goto end;
m_occs(*m_goal);
m_idx = m_goal->size();
forward = false;
m_subst->reset();
m_r.set_substitution(m_subst.get()); // reset, but keep substitution
}
else {
while (m_idx > 0) {
m_idx--;
process_current();
if (m_goal->inconsistent())
goto end;
}
if (!m_modified)
goto end;
m_subst->reset();
m_r.set_substitution(m_subst.get()); // reset, but keep substitution
m_modified = false;
m_occs(*m_goal);
m_idx = 0;
size = m_goal->size();
forward = true;
}
round++;
if (round >= m_max_rounds)
break;
IF_VERBOSE(100, verbose_stream() << "starting new round, goal size: " << m_goal->num_exprs() << std::endl;);
TRACE("propgate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";);
}
end:
m_goal->elim_redundancies();
m_goal->inc_depth();
result.push_back(m_goal);
SASSERT(m_goal->is_well_sorted());
TRACE("propagate_values", m_goal->display(tout););
m_goal = 0;
}
};
imp * m_imp;
params_ref m_params;
public:
propagate_values_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(propagate_values_tactic, m, m_params);
}
virtual ~propagate_values_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(":max-rounds", CPK_UINT, "(default: 2) maximum number of rounds.");
}
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;
}
}
protected:
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(propagate_values_tactic, m, p));
}

View file

@ -0,0 +1,29 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
propagate_values_tactic.h
Abstract:
Propagate values using equalities of the form (= t v) where v is a value,
and atoms t and (not t)
Author:
Leonardo de Moura (leonardo) 2011-12-28.
Revision History:
--*/
#ifndef _PROPAGATE_VALUES_TACTIC_H_
#define _PROPAGATE_VALUES_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

Some files were not shown because too many files have changed in this diff Show more