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:
commit
3da69a4f1b
1502 changed files with 2524 additions and 5113 deletions
1812
src/tactic/aig/aig.cpp
Normal file
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
85
src/tactic/aig/aig.h
Normal 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
|
126
src/tactic/aig/aig_tactic.cpp
Normal file
126
src/tactic/aig/aig_tactic.cpp
Normal 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));
|
||||
}
|
27
src/tactic/aig/aig_tactic.h
Normal file
27
src/tactic/aig/aig_tactic.h
Normal 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
|
164
src/tactic/arith_tactics/add_bounds.cpp
Normal file
164
src/tactic/arith_tactics/add_bounds.cpp
Normal 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;
|
||||
}
|
||||
}
|
50
src/tactic/arith_tactics/add_bounds.h
Normal file
50
src/tactic/arith_tactics/add_bounds.h
Normal 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
|
198
src/tactic/arith_tactics/add_bounds_tactic.cpp
Normal file
198
src/tactic/arith_tactics/add_bounds_tactic.cpp
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
add_bounds_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for bounding unbounded variables.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-10-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"bound_manager.h"
|
||||
|
||||
struct is_unbounded_proc {
|
||||
struct found {};
|
||||
arith_util m_util;
|
||||
bound_manager & m_bm;
|
||||
|
||||
is_unbounded_proc(bound_manager & bm):m_util(bm.m()), m_bm(bm) {}
|
||||
|
||||
void operator()(app * t) {
|
||||
if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t)) && (!m_bm.has_lower(t) || !m_bm.has_upper(t)))
|
||||
throw found();
|
||||
}
|
||||
|
||||
void operator()(var *) {}
|
||||
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
bool is_unbounded(goal const & g) {
|
||||
ast_manager & m = g.m();
|
||||
bound_manager bm(m);
|
||||
bm(g);
|
||||
is_unbounded_proc proc(bm);
|
||||
return test(g, proc);
|
||||
}
|
||||
|
||||
class is_unbounded_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_unbounded(g);
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_is_unbounded_probe() {
|
||||
return alloc(is_unbounded_probe);
|
||||
}
|
||||
|
||||
class add_bounds_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
rational m_lower;
|
||||
rational m_upper;
|
||||
volatile bool m_cancel;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_lower = p.get_rat(":add-bound-lower", rational(-2));
|
||||
m_upper = p.get_rat(":add-bound-upper", rational(2));
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
struct add_bound_proc {
|
||||
arith_util m_util;
|
||||
bound_manager & m_bm;
|
||||
goal & m_goal;
|
||||
rational const & m_lower;
|
||||
rational const & m_upper;
|
||||
unsigned m_num_bounds;
|
||||
|
||||
add_bound_proc(bound_manager & bm, goal & g, rational const & l, rational const & u):
|
||||
m_util(bm.m()),
|
||||
m_bm(bm),
|
||||
m_goal(g),
|
||||
m_lower(l),
|
||||
m_upper(u) {
|
||||
m_num_bounds = 0;
|
||||
}
|
||||
|
||||
void operator()(app * t) {
|
||||
if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t))) {
|
||||
if (!m_bm.has_lower(t)) {
|
||||
m_goal.assert_expr(m_util.mk_le(t, m_util.mk_numeral(m_upper, m_util.is_int(t))));
|
||||
m_num_bounds++;
|
||||
}
|
||||
if (!m_bm.has_upper(t)) {
|
||||
m_goal.assert_expr(m_util.mk_ge(t, m_util.mk_numeral(m_lower, m_util.is_int(t))));
|
||||
m_num_bounds++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(var *) {}
|
||||
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
mc = 0; pc = 0; core = 0;
|
||||
tactic_report report("add-bounds", *g);
|
||||
bound_manager bm(m);
|
||||
expr_fast_mark1 visited;
|
||||
add_bound_proc proc(bm, *(g.get()), m_lower, m_upper);
|
||||
unsigned sz = g->size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
quick_for_each_expr(proc, visited, g->form(i));
|
||||
visited.reset();
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
if (proc.m_num_bounds > 0)
|
||||
g->updt_prec(goal::UNDER);
|
||||
report_tactic_progress(":added-bounds", proc.m_num_bounds);
|
||||
TRACE("add_bounds", g->display(tout););
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
|
||||
public:
|
||||
add_bounds_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(add_bounds_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~add_bounds_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":add-bound-lower", CPK_NUMERAL, "(default: -2) lower bound to be added to unbounded variables.");
|
||||
r.insert(":add-bound-upper", CPK_NUMERAL, "(default: 2) upper bound to be added to unbounded variables.");
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(g, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_add_bounds_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(add_bounds_tactic, m, p));
|
||||
}
|
34
src/tactic/arith_tactics/add_bounds_tactic.h
Normal file
34
src/tactic/arith_tactics/add_bounds_tactic.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
add_bounds.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for bounding unbounded variables.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _ADD_BOUNDS_H_
|
||||
#define _ADD_BOUNDS_H_
|
||||
|
||||
#include"params.h"
|
||||
|
||||
class ast_manager;
|
||||
class goal;
|
||||
class tactic;
|
||||
class probe;
|
||||
|
||||
bool is_unbounded(goal const & g);
|
||||
probe * mk_is_unbounded_probe();
|
||||
|
||||
tactic * mk_add_bounds_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
240
src/tactic/arith_tactics/bound_manager.cpp
Normal file
240
src/tactic/arith_tactics/bound_manager.cpp
Normal 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";
|
||||
}
|
||||
}
|
111
src/tactic/arith_tactics/bound_manager.h
Normal file
111
src/tactic/arith_tactics/bound_manager.h
Normal 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
|
956
src/tactic/arith_tactics/bound_propagator.cpp
Normal file
956
src/tactic/arith_tactics/bound_propagator.cpp
Normal file
|
@ -0,0 +1,956 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_propagator.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Bound propagators for arithmetic.
|
||||
Support class for implementing strategies and search procedures
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"bound_propagator.h"
|
||||
#include<cmath>
|
||||
|
||||
// -------------------------------
|
||||
// Bound Relaxation configuration
|
||||
//
|
||||
// The idea is to minimize errors in floating point computations
|
||||
//
|
||||
// If RELAX_BOUNDS is undefined, then bound relaxation is disabled.
|
||||
// Otherwise, lower bounds l are relaxed using the formula
|
||||
// PRECISION * floor(l * INV_PRECISION + TOLERANCE)
|
||||
// and upper bounds u as:
|
||||
// PRECISION * ceil(u * INV_PRECISION - TOLERANCE)
|
||||
// In the LP literature, the suggested values are
|
||||
// l := 10^-5 * floor(l*10^5 + 10^-6)
|
||||
// u := 10^-5 * ceil(u*10^5 - 10^-6)
|
||||
// I'm using the following values because of strict bounds
|
||||
// l := 10^-6 * floor(l*10^6 + 10^-7)
|
||||
// u := 10^-6 * ceil(u*10^6 - 10^-7)
|
||||
#define RELAX_BOUNDS
|
||||
#define TOLERANCE 0.0000001
|
||||
#define PRECISION 0.000001
|
||||
#define INV_PRECISION 1000000.0
|
||||
// -------------------------------
|
||||
|
||||
bound_propagator::bound::bound(numeral_manager & m,
|
||||
mpq const & k,
|
||||
double approx_k,
|
||||
bool lower,
|
||||
bool strict,
|
||||
unsigned lvl,
|
||||
unsigned ts,
|
||||
bkind bk,
|
||||
unsigned c_idx,
|
||||
assumption a,
|
||||
bound * prev):
|
||||
m_approx_k(approx_k),
|
||||
m_lower(lower),
|
||||
m_strict(strict),
|
||||
m_kind(bk),
|
||||
m_level(lvl),
|
||||
m_timestamp(ts),
|
||||
m_prev(prev) {
|
||||
m.set(m_k, k);
|
||||
if (bk == DERIVED)
|
||||
m_constraint_idx = c_idx;
|
||||
else
|
||||
m_assumption = a;
|
||||
}
|
||||
|
||||
bound_propagator::bound_propagator(numeral_manager & _m, allocator & a, params_ref const & p):
|
||||
m(_m),
|
||||
m_allocator(a),
|
||||
m_eq_manager(m, a) {
|
||||
m_timestamp = 0;
|
||||
m_qhead = 0;
|
||||
m_conflict = null_var;
|
||||
updt_params(p);
|
||||
reset_statistics();
|
||||
}
|
||||
|
||||
bound_propagator::~bound_propagator() {
|
||||
m.del(m_tmp);
|
||||
reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraints_core() {
|
||||
constraint_vector::iterator it = m_constraints.begin();
|
||||
constraint_vector::iterator end = m_constraints.end();
|
||||
for (; it != end; ++it) {
|
||||
del_constraint(*it);
|
||||
}
|
||||
m_constraints.reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraints() {
|
||||
SASSERT(scope_lvl() == 0);
|
||||
if (m_constraints.empty())
|
||||
return;
|
||||
del_constraints_core();
|
||||
m_constraints.finalize();
|
||||
vector<wlist>::iterator it = m_watches.begin();
|
||||
vector<wlist>::iterator end = m_watches.end();
|
||||
for (; it != end; ++it)
|
||||
it->finalize();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraint(constraint & c) {
|
||||
switch (c.m_kind) {
|
||||
case LINEAR:
|
||||
m_eq_manager.del(c.m_eq);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::updt_params(params_ref const & p) {
|
||||
m_max_refinements = p.get_uint(":bound-max-refinements", 16);
|
||||
m_threshold = p.get_double(":bound-threshold", 0.05);
|
||||
m_small_interval = p.get_double(":bound-small-interval", 128);
|
||||
m_strict2double = p.get_double(":strict2double", 0.00001);
|
||||
}
|
||||
|
||||
void bound_propagator::get_param_descrs(param_descrs & r) {
|
||||
r.insert(":bound-max-refinements", CPK_UINT, "(default: 16) maximum number of bound refinements (per round) for unbounded variables.");
|
||||
r.insert(":bound-threshold", CPK_DOUBLE, "(default: 0.05) bound propagation improvement threshold ratio.");
|
||||
}
|
||||
|
||||
void bound_propagator::collect_statistics(statistics & st) const {
|
||||
st.update("bound conflicts", m_conflicts);
|
||||
st.update("bound propagations", m_propagations);
|
||||
st.update("bound false alarms", m_false_alarms);
|
||||
}
|
||||
|
||||
void bound_propagator::reset_statistics() {
|
||||
m_conflicts = 0;
|
||||
m_propagations = 0;
|
||||
m_false_alarms = 0;
|
||||
}
|
||||
|
||||
void bound_propagator::mk_var(var x, bool is_int) {
|
||||
m_is_int.reserve(x+1, false);
|
||||
m_dead.reserve(x+1, true);
|
||||
m_lowers.reserve(x+1, 0);
|
||||
m_uppers.reserve(x+1, 0);
|
||||
m_lower_refinements.reserve(x+1, 0);
|
||||
m_upper_refinements.reserve(x+1, 0);
|
||||
m_watches.reserve(x+1);
|
||||
|
||||
SASSERT(m_dead[x]);
|
||||
|
||||
m_is_int[x] = is_int;
|
||||
m_dead[x] = false;
|
||||
m_lowers[x] = 0;
|
||||
m_uppers[x] = 0;
|
||||
m_lower_refinements[x] = 0;
|
||||
m_upper_refinements[x] = 0;
|
||||
m_watches[x].reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_var(var x) {
|
||||
SASSERT(!m_dead[x]);
|
||||
m_dead[x] = true;
|
||||
// mark constraints containing x as dead.
|
||||
wlist & wl = m_watches[x];
|
||||
wlist::iterator it = wl.begin();
|
||||
wlist::iterator end = wl.end();
|
||||
for (; it != end; ++it) {
|
||||
m_constraints[*it].m_dead = true;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::mk_eq(unsigned sz, mpq * as, var * xs) {
|
||||
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
||||
init_eq(eq);
|
||||
}
|
||||
|
||||
void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) {
|
||||
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
||||
init_eq(eq);
|
||||
}
|
||||
|
||||
void bound_propagator::init_eq(linear_equation * eq) {
|
||||
if (eq == 0)
|
||||
return;
|
||||
unsigned c_idx = m_constraints.size();
|
||||
m_constraints.push_back(constraint());
|
||||
constraint & new_c = m_constraints.back();
|
||||
new_c.m_kind = LINEAR;
|
||||
new_c.m_dead = false;
|
||||
new_c.m_timestamp = 0;
|
||||
new_c.m_act = 0;
|
||||
new_c.m_counter = 0;
|
||||
new_c.m_eq = eq;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m_watches[eq->x(i)].push_back(c_idx);
|
||||
}
|
||||
if (propagate(c_idx) && scope_lvl() > 0)
|
||||
m_reinit_stack.push_back(c_idx);
|
||||
}
|
||||
|
||||
void bound_propagator::push() {
|
||||
m_scopes.push_back(scope());
|
||||
scope & s = m_scopes.back();
|
||||
s.m_trail_limit = m_trail.size();
|
||||
s.m_qhead_old = m_qhead;
|
||||
s.m_reinit_stack_limit = m_reinit_stack.size();
|
||||
s.m_timestamp_old = m_timestamp;
|
||||
s.m_in_conflict = inconsistent();
|
||||
}
|
||||
|
||||
void bound_propagator::undo_trail(unsigned old_sz) {
|
||||
SASSERT(old_sz <= m_trail.size());
|
||||
unsigned i = m_trail.size();
|
||||
while (i > old_sz) {
|
||||
--i;
|
||||
trail_info & info = m_trail.back();
|
||||
var x = info.x();
|
||||
bool is_lower = info.is_lower();
|
||||
m_trail.pop_back();
|
||||
bound * b;
|
||||
if (is_lower) {
|
||||
b = m_lowers[x];
|
||||
m_lowers[x] = b->m_prev;
|
||||
}
|
||||
else {
|
||||
b = m_uppers[x];
|
||||
m_uppers[x] = b->m_prev;
|
||||
}
|
||||
m.del(b->m_k);
|
||||
b->~bound();
|
||||
m_allocator.deallocate(sizeof(bound), b);
|
||||
}
|
||||
SASSERT(m_trail.size() == old_sz);
|
||||
}
|
||||
|
||||
void bound_propagator::pop(unsigned num_scopes) {
|
||||
unsigned lvl = scope_lvl();
|
||||
SASSERT(num_scopes <= lvl);
|
||||
unsigned new_lvl = lvl - num_scopes;
|
||||
scope & s = m_scopes[new_lvl];
|
||||
undo_trail(s.m_trail_limit);
|
||||
m_timestamp = s.m_timestamp_old;
|
||||
m_qhead = s.m_qhead_old;
|
||||
if (!s.m_in_conflict)
|
||||
m_conflict = null_var;
|
||||
unsigned reinit_stack_sz = s.m_reinit_stack_limit;
|
||||
m_scopes.shrink(new_lvl);
|
||||
|
||||
// reinitialize
|
||||
unsigned i = reinit_stack_sz;
|
||||
unsigned j = reinit_stack_sz;
|
||||
unsigned sz = m_reinit_stack.size();
|
||||
for (; i < sz; i++) {
|
||||
unsigned c_idx = m_reinit_stack[i];
|
||||
bool p = propagate(c_idx);
|
||||
if (new_lvl > 0 && p) {
|
||||
m_reinit_stack[j] = c_idx;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
m_reinit_stack.shrink(j);
|
||||
}
|
||||
|
||||
bool bound_propagator::assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
||||
if (is_int(x)) {
|
||||
if (m.is_int(k)) {
|
||||
if (strict)
|
||||
m.inc(k);
|
||||
}
|
||||
else {
|
||||
m.ceil(k, k);
|
||||
}
|
||||
SASSERT(m.is_int(k));
|
||||
strict = false;
|
||||
}
|
||||
TRACE("bound_propagator_detail", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
|
||||
bound * old_lower = m_lowers[x];
|
||||
if (old_lower) {
|
||||
bool improves = m.gt(k, old_lower->m_k) || (!old_lower->m_strict && strict && m.eq(k, old_lower->m_k));
|
||||
if (!improves) {
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
||||
m_false_alarms++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_derived", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
m_propagations++;
|
||||
}
|
||||
|
||||
if (scope_lvl() == 0 && bk == DERIVED)
|
||||
bk = AXIOM; // don't need justification at level 0
|
||||
|
||||
double approx_k = m.get_double(k);
|
||||
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
||||
#ifdef RELAX_BOUNDS
|
||||
approx_k = PRECISION*floor(approx_k*INV_PRECISION + TOLERANCE);
|
||||
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
||||
#endif
|
||||
void * mem = m_allocator.allocate(sizeof(bound));
|
||||
bound * new_lower = new (mem) bound(m, k, approx_k, true, strict, scope_lvl(), m_timestamp, bk, c_idx, a, old_lower);
|
||||
m_timestamp++;
|
||||
m_lowers[x] = new_lower;
|
||||
m_trail.push_back(trail_info(x, true));
|
||||
m_lower_refinements[x]++;
|
||||
check_feasibility(x);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
||||
if (is_int(x)) {
|
||||
if (m.is_int(k)) {
|
||||
if (strict)
|
||||
m.dec(k);
|
||||
}
|
||||
else {
|
||||
m.floor(k, k);
|
||||
}
|
||||
SASSERT(m.is_int(k));
|
||||
strict = false;
|
||||
}
|
||||
|
||||
TRACE("bound_propagator_detail", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
|
||||
bound * old_upper = m_uppers[x];
|
||||
if (old_upper) {
|
||||
bool improves = m.lt(k, old_upper->m_k) || (!old_upper->m_strict && strict && m.eq(k, old_upper->m_k));
|
||||
if (!improves) {
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
||||
m_false_alarms++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bk == DERIVED) {
|
||||
m_propagations++;
|
||||
TRACE("bound_propagator_derived", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
}
|
||||
|
||||
if (scope_lvl() == 0 && bk == DERIVED)
|
||||
bk = AXIOM; // don't need justification at level 0
|
||||
|
||||
double approx_k = m.get_double(k);
|
||||
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
||||
#ifdef RELAX_BOUNDS
|
||||
approx_k = PRECISION*ceil(approx_k*INV_PRECISION - TOLERANCE);
|
||||
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
||||
#endif
|
||||
|
||||
void * mem = m_allocator.allocate(sizeof(bound));
|
||||
bound * new_upper = new (mem) bound(m, k, approx_k, false, strict, scope_lvl(), m_timestamp, bk, c_idx, a, m_uppers[x]);
|
||||
m_timestamp++;
|
||||
m_uppers[x] = new_upper;
|
||||
m_trail.push_back(trail_info(x, false));
|
||||
m_upper_refinements[x]++;
|
||||
check_feasibility(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::get_interval_size(var x, double & r) const {
|
||||
bound * l = m_lowers[x];
|
||||
bound * u = m_uppers[x];
|
||||
if (l && u) {
|
||||
r = u->m_approx_k - l->m_approx_k;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool LOWER>
|
||||
bool bound_propagator::relevant_bound(var x, double new_k) const {
|
||||
TRACE("bound_propagator_detail", tout << "relevant_bound x" << x << " " << new_k << " LOWER: " << LOWER << "\n";
|
||||
if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n";
|
||||
if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";);
|
||||
bound * b = LOWER ? m_lowers[x] : m_uppers[x];
|
||||
if (b == 0)
|
||||
return true; // variable did not have a bound
|
||||
|
||||
double interval_size;
|
||||
bool bounded = get_interval_size(x, interval_size);
|
||||
|
||||
if (!is_int(x)) {
|
||||
// check if the improvement is significant
|
||||
double improvement;
|
||||
double abs_k = b->m_approx_k;
|
||||
if (abs_k < 0.0)
|
||||
abs_k -= abs_k;
|
||||
if (bounded)
|
||||
improvement = m_threshold * std::max(std::min(interval_size, abs_k), 1.0);
|
||||
else
|
||||
improvement = m_threshold * std::max(abs_k, 1.0);
|
||||
|
||||
if (LOWER) {
|
||||
if (new_k <= b->m_approx_k + improvement) {
|
||||
TRACE("bound_propagator", tout << "LOWER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
||||
return false; // improvement is too small
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (new_k >= b->m_approx_k - improvement) {
|
||||
TRACE("bound_propagator", tout << "UPPER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
||||
return false; // improvement is too small
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (LOWER) {
|
||||
if (new_k < b->m_approx_k + 1.0)
|
||||
return false; // no improvement
|
||||
}
|
||||
else {
|
||||
if (new_k > b->m_approx_k - 1.0)
|
||||
return false; // no improvement
|
||||
}
|
||||
}
|
||||
|
||||
if (bounded && interval_size <= m_small_interval)
|
||||
return true;
|
||||
|
||||
if (LOWER)
|
||||
return m_lower_refinements[x] < m_max_refinements;
|
||||
else
|
||||
return m_upper_refinements[x] < m_max_refinements;
|
||||
}
|
||||
|
||||
bool bound_propagator::relevant_lower(var x, double approx_k) const {
|
||||
return relevant_bound<true>(x, approx_k);
|
||||
}
|
||||
|
||||
bool bound_propagator::relevant_upper(var x, double approx_k) const {
|
||||
return relevant_bound<false>(x, approx_k);
|
||||
}
|
||||
|
||||
void bound_propagator::check_feasibility(var x) {
|
||||
if (inconsistent())
|
||||
return;
|
||||
bound * l = m_lowers[x];
|
||||
bound * u = m_uppers[x];
|
||||
if (l && u) {
|
||||
if (m.lt(l->m_k, u->m_k))
|
||||
return;
|
||||
if (!l->m_strict && !u->m_strict && m.eq(l->m_k, u->m_k))
|
||||
return;
|
||||
m_conflict = x;
|
||||
m_conflicts++;
|
||||
SASSERT(inconsistent());
|
||||
TRACE("bound_propagator", tout << "inconsistency detected: x" << x << "\n"; display(tout););
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::propagate() {
|
||||
m_to_reset_ts.reset();
|
||||
|
||||
while (m_qhead < m_trail.size()) {
|
||||
if (inconsistent())
|
||||
break;
|
||||
trail_info & info = m_trail[m_qhead];
|
||||
var x = info.x();
|
||||
bool is_lower = info.is_lower();
|
||||
bound * b = is_lower ? m_lowers[x] : m_uppers[x];
|
||||
SASSERT(b);
|
||||
unsigned ts = b->m_timestamp;
|
||||
TRACE("bound_propagator_detail", tout << "propagating x" << x << "\n";);
|
||||
m_qhead++;
|
||||
wlist const & wl = m_watches[x];
|
||||
wlist::const_iterator it = wl.begin();
|
||||
wlist::const_iterator end = wl.end();
|
||||
for (; it != end; ++it) {
|
||||
unsigned c_idx = *it;
|
||||
constraint & c = m_constraints[c_idx];
|
||||
// We don't need to visit c if it was already propagated using b.
|
||||
// Whenever we visit c we store in c.m_timestamp the current timestamp
|
||||
// So, we know that c was already propagated any bound using bounds with timestamp lower than c.m_timestamp.
|
||||
if (ts >= c.m_timestamp) {
|
||||
if (c.m_timestamp == 0)
|
||||
m_to_reset_ts.push_back(c_idx);
|
||||
c.m_timestamp = m_timestamp;
|
||||
propagate(c_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned_vector::iterator it = m_to_reset_ts.begin();
|
||||
unsigned_vector::iterator end = m_to_reset_ts.end();
|
||||
for (; it != end; ++it)
|
||||
m_constraints[*it].m_timestamp = 0;
|
||||
}
|
||||
|
||||
bool bound_propagator::propagate(unsigned c_idx) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
if (c.m_dead)
|
||||
return false;
|
||||
if (c.m_kind == LINEAR)
|
||||
return propagate_eq(c_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bound_propagator::propagate_eq(unsigned c_idx) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
|
||||
#if 0
|
||||
{
|
||||
static unsigned counter = 0;
|
||||
static unsigned visited = 0;
|
||||
counter++;
|
||||
visited += eq->size();
|
||||
if (counter % 1000 == 0)
|
||||
verbose_stream() << "[bound-propagator] :propagate-eq " << counter << " :visited-vars " << visited << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACE("bound_propagator_detail", tout << "propagating using eq: "; m_eq_manager.display(tout, *eq); tout << "\n";);
|
||||
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
||||
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
||||
unsigned ll_i = UINT_MAX; // position of the variable that couldn't contribute to ll
|
||||
unsigned uu_i = UINT_MAX; // position of the variable that coundn't contribute to uu
|
||||
bool ll_failed = false;
|
||||
bool uu_failed = false;
|
||||
double ll = 0.0;
|
||||
double uu = 0.0;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
double a_i = eq->approx_a(i);
|
||||
bound * l_i = m_lowers[x_i];
|
||||
bound * u_i = m_uppers[x_i];
|
||||
if (a_i < 0.0) {
|
||||
if (!ll_failed) {
|
||||
if (l_i == 0) {
|
||||
if (ll_i == UINT_MAX)
|
||||
ll_i = i;
|
||||
else
|
||||
ll_failed = true;
|
||||
}
|
||||
else {
|
||||
ll -= a_i * l_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed) {
|
||||
if (u_i == 0) {
|
||||
if (uu_i == UINT_MAX)
|
||||
uu_i = i;
|
||||
else
|
||||
uu_failed = true;
|
||||
}
|
||||
else {
|
||||
uu -= a_i * u_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!ll_failed) {
|
||||
if (u_i == 0) {
|
||||
if (ll_i == UINT_MAX)
|
||||
ll_i = i;
|
||||
else
|
||||
ll_failed = true;
|
||||
}
|
||||
else {
|
||||
ll -= a_i * u_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed) {
|
||||
if (l_i == 0) {
|
||||
if (uu_i == UINT_MAX)
|
||||
uu_i = i;
|
||||
else
|
||||
uu_failed = true;
|
||||
}
|
||||
else {
|
||||
uu -= a_i * l_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ll_failed && uu_failed)
|
||||
return false; // nothing to propagate
|
||||
}
|
||||
|
||||
bool propagated = false;
|
||||
|
||||
SASSERT(!ll_failed || !uu_failed);
|
||||
if (ll_i == UINT_MAX || uu_i == UINT_MAX) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
double a_i = eq->approx_a(i);
|
||||
bound * l_i = m_lowers[x_i];
|
||||
bound * u_i = m_uppers[x_i];
|
||||
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
||||
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
||||
if (ll_i == UINT_MAX) {
|
||||
// can propagate a lower bound for a_i*x_i
|
||||
if (a_i > 0.0) {
|
||||
// can propagate a lower bound for x_i
|
||||
double new_k = (ll + a_i * u_i->m_approx_k)/a_i;
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
// a_i < 0.0
|
||||
// can propagate a upper bound for x_i
|
||||
double new_k = (ll + a_i * l_i->m_approx_k)/a_i;
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
if (uu_i == UINT_MAX) {
|
||||
// can propagate an upper bound for a_i*x_i
|
||||
if (a_i > 0.0) {
|
||||
// can propagate a upper bound for x_i
|
||||
double new_k = (uu + a_i * l_i->m_approx_k)/a_i;
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
// a_i < 0.0
|
||||
// can propagate a lower bound for x_i
|
||||
double new_k = (uu + a_i * u_i->m_approx_k)/a_i;
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ll_failed && ll_i != UINT_MAX) {
|
||||
// can propagate a lower bound for the monomial at position ll_i
|
||||
var x_i = eq->x(ll_i);
|
||||
double a_i = eq->approx_a(ll_i);
|
||||
double new_k = ll/a_i;
|
||||
if (a_i > 0.0) {
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, ll_i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, ll_i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed && uu_i != UINT_MAX) {
|
||||
// can propagate a upper bound for the monomial at position uu_i
|
||||
var x_i = eq->x(uu_i);
|
||||
double a_i = eq->approx_a(uu_i);
|
||||
double new_k = uu/a_i;
|
||||
if (a_i > 0.0) {
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, uu_i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, uu_i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return propagated;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Try to propagate a lower bound for the variable stored at position i, using mpq's (rationals).
|
||||
When this method is invoked, we know that all other variables have the "right" bounds, and
|
||||
using doubles we improve the current known bound.
|
||||
*/
|
||||
bool bound_propagator::propagate_lower(unsigned c_idx, unsigned i) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
var x_i = eq->x(i);
|
||||
mpz const & a_i = eq->a(i);
|
||||
unsigned sz = eq->size();
|
||||
mpq k;
|
||||
bool strict = false;
|
||||
bool neg_a_i = m.is_neg(a_i);
|
||||
for (unsigned j = 0; j < sz; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
var x_j = eq->x(j);
|
||||
mpz const & a_j = eq->a(j);
|
||||
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_uppers[x_j] : m_lowers[x_j];
|
||||
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << " b_j->m_k: " << m.to_string(b_j->m_k) <<
|
||||
" a_j: " << m.to_string(a_j) << "\n";);
|
||||
SASSERT(b_j);
|
||||
if (b_j->m_strict)
|
||||
strict = true;
|
||||
m.addmul(k, a_j, b_j->m_k, k);
|
||||
}
|
||||
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << "\n";);
|
||||
m.neg(k);
|
||||
m.div(k, a_i, k);
|
||||
TRACE("bound_propagator_step", tout << "propagating lower x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
||||
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
||||
bool r = assert_lower_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
||||
m.del(k);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Try to propagate a upper bound for the variable stored at position i, using mpq's (rationals).
|
||||
When this method is invoked, we know that all other variables have the "right" bounds, and
|
||||
using doubles we improve the current known bound.
|
||||
*/
|
||||
bool bound_propagator::propagate_upper(unsigned c_idx, unsigned i) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
var x_i = eq->x(i);
|
||||
mpz const & a_i = eq->a(i);
|
||||
unsigned sz = eq->size();
|
||||
mpq k;
|
||||
bool strict = false;
|
||||
bool neg_a_i = m.is_neg(a_i);
|
||||
for (unsigned j = 0; j < sz; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
var x_j = eq->x(j);
|
||||
mpz const & a_j = eq->a(j);
|
||||
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_lowers[x_j] : m_uppers[x_j];
|
||||
SASSERT(b_j);
|
||||
if (b_j->m_strict)
|
||||
strict = true;
|
||||
m.addmul(k, a_j, b_j->m_k, k);
|
||||
}
|
||||
m.neg(k);
|
||||
m.div(k, a_i, k);
|
||||
TRACE("bound_propagator_step", tout << "propagating upper x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
||||
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
||||
bool r = assert_upper_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
||||
m.del(k);
|
||||
return r;
|
||||
}
|
||||
|
||||
void bound_propagator::reset() {
|
||||
undo_trail(0);
|
||||
del_constraints_core();
|
||||
m_constraints.finalize();
|
||||
m_is_int.finalize();
|
||||
m_dead.finalize();
|
||||
m_lowers.finalize();
|
||||
m_uppers.finalize();
|
||||
m_watches.finalize();
|
||||
m_trail.finalize();
|
||||
m_qhead = 0;
|
||||
m_reinit_stack.finalize();
|
||||
m_lower_refinements.finalize();
|
||||
m_upper_refinements.finalize();
|
||||
m_timestamp = 0;
|
||||
m_conflict = null_var;
|
||||
m_scopes.finalize();
|
||||
}
|
||||
|
||||
bool bound_propagator::lower(var x, mpq & k, bool & strict, unsigned & ts) const {
|
||||
bound * b = m_lowers[x];
|
||||
if (!b)
|
||||
return false;
|
||||
m.set(k, b->m_k);
|
||||
strict = b->m_strict;
|
||||
ts = b->m_timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const {
|
||||
bound * b = m_uppers[x];
|
||||
if (!b)
|
||||
return false;
|
||||
m.set(k, b->m_k);
|
||||
strict = b->m_strict;
|
||||
ts = b->m_timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) {
|
||||
bound * r = this;
|
||||
while (r != 0 && r->m_timestamp >= timestamp)
|
||||
r = r->m_prev;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the coefficient of x in eq is positive
|
||||
*/
|
||||
bool bound_propagator::is_a_i_pos(linear_equation const & eq, var x) const {
|
||||
unsigned i = eq.pos(x);
|
||||
if (i == UINT_MAX)
|
||||
return false;
|
||||
return m.is_pos(eq.a(i));
|
||||
}
|
||||
|
||||
void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector & ex) const {
|
||||
if (!b)
|
||||
return;
|
||||
b = b->at(ts);
|
||||
if (!b)
|
||||
return;
|
||||
if (b->m_kind == AXIOM || b->m_kind == DECISION)
|
||||
return;
|
||||
if (b->m_kind == ASSUMPTION) {
|
||||
ex.push_back(b->m_assumption);
|
||||
return;
|
||||
}
|
||||
svector<var_bound> & todo = const_cast<bound_propagator*>(this)->m_todo;
|
||||
todo.reset();
|
||||
unsigned qhead = 0;
|
||||
todo.push_back(var_bound(x, b));
|
||||
b->m_mark = true;
|
||||
while (qhead < todo.size()) {
|
||||
var_bound & vb = todo[qhead];
|
||||
qhead ++;
|
||||
var x = vb.first;
|
||||
bound * b = vb.second;
|
||||
SASSERT(b->kind() == ASSUMPTION || b->kind() == DERIVED);
|
||||
if (b->kind() == ASSUMPTION) {
|
||||
ex.push_back(b->m_assumption);
|
||||
continue;
|
||||
}
|
||||
SASSERT(b->kind() == DERIVED);
|
||||
constraint const & c = m_constraints[b->m_constraint_idx];
|
||||
switch (c.m_kind) {
|
||||
case LINEAR: {
|
||||
linear_equation * eq = c.m_eq;
|
||||
bool is_lower = b->is_lower();
|
||||
if (!is_a_i_pos(*eq, x))
|
||||
is_lower = !is_lower;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
if (x_i == x)
|
||||
continue;
|
||||
bound * b = (m.is_neg(eq->a(i)) == is_lower) ? m_lowers[x_i] : m_uppers[x_i];
|
||||
SASSERT(b);
|
||||
if (b->kind() == DERIVED || b->kind() == ASSUMPTION) {
|
||||
if (!b->m_mark) {
|
||||
b->m_mark = true;
|
||||
todo.push_back(var_bound(x_i, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsigned sz = todo.size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
todo[i].second->m_mark = false;
|
||||
todo.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Compute lower (upper) bound for the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]
|
||||
|
||||
Return false if the lower (upper) bound is -oo (oo)
|
||||
*/
|
||||
template<bool LOWER, typename Numeral>
|
||||
bool bound_propagator::get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const {
|
||||
st = false;
|
||||
m.reset(r);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = xs[i];
|
||||
Numeral const & a_i = as[i];
|
||||
if (m.is_zero(a_i))
|
||||
continue;
|
||||
bound * b = (m.is_neg(a_i) == LOWER) ? m_uppers[x_i] : m_lowers[x_i];
|
||||
if (!b) {
|
||||
m.reset(r);
|
||||
return false;
|
||||
}
|
||||
if (b->m_strict)
|
||||
st = true;
|
||||
m.addmul(r, a_i, b->m_k, r);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
||||
return get_bound<true, mpq>(sz, as, xs, r, st);
|
||||
}
|
||||
|
||||
bool bound_propagator::upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
||||
return get_bound<false, mpq>(sz, as, xs, r, st);
|
||||
}
|
||||
|
||||
void bound_propagator::display_bounds_of(std::ostream & out, linear_equation const & eq) const {
|
||||
for (unsigned i = 0; i < eq.size(); i++) {
|
||||
display_var_bounds(out, eq.x(i));
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_var_bounds(std::ostream & out, var x, bool approx, bool precise) const {
|
||||
if (m_lowers[x]) {
|
||||
if (precise)
|
||||
out << m.to_string(m_lowers[x]->m_k);
|
||||
if (precise && approx)
|
||||
out << " | ";
|
||||
if (approx)
|
||||
out << m_lowers[x]->m_approx_k;
|
||||
out << " " << (m_lowers[x]->m_strict ? "<" : "<=");
|
||||
}
|
||||
else {
|
||||
out << "-oo <";
|
||||
}
|
||||
out << " x" << x << " ";
|
||||
if (m_uppers[x]) {
|
||||
out << (m_uppers[x]->m_strict ? "<" : "<=") << " ";
|
||||
if (precise)
|
||||
out << m.to_string(m_uppers[x]->m_k);
|
||||
if (precise && approx)
|
||||
out << " | ";
|
||||
if (approx)
|
||||
out << m_uppers[x]->m_approx_k;
|
||||
}
|
||||
else {
|
||||
out << "< oo";
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_bounds(std::ostream & out, bool approx, bool precise) const {
|
||||
unsigned num_vars = m_dead.size();
|
||||
for (unsigned x = 0; x < num_vars; x++) {
|
||||
if (!is_dead(x)) {
|
||||
display_var_bounds(out, x, approx, precise);
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_constraints(std::ostream & out) const {
|
||||
constraint_vector::const_iterator it = m_constraints.begin();
|
||||
constraint_vector::const_iterator end = m_constraints.end();
|
||||
for (; it != end; ++it) {
|
||||
constraint const & c = *it;
|
||||
if (c.m_kind == LINEAR) {
|
||||
m_eq_manager.display(out, *(c.m_eq));
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display(std::ostream & out) const {
|
||||
display_bounds(out);
|
||||
display_constraints(out);
|
||||
}
|
||||
|
||||
|
||||
|
265
src/tactic/arith_tactics/bound_propagator.h
Normal file
265
src/tactic/arith_tactics/bound_propagator.h
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_propagator.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Bound propagators for arithmetic.
|
||||
Support class for implementing strategies and search procedures
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _BOUND_PROPAGATOR_H_
|
||||
#define _BOUND_PROPAGATOR_H_
|
||||
|
||||
#include"mpq.h"
|
||||
#include"vector.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
#include"numeral_buffer.h"
|
||||
#include"linear_equation.h"
|
||||
|
||||
class bound_propagator {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
typedef unsigned assumption;
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef unsigned_vector assumption_vector;
|
||||
typedef unsigned constraint_id;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
typedef svector<double> double_vector;
|
||||
static const assumption null_assumption = UINT_MAX;
|
||||
static const var null_var = UINT_MAX;
|
||||
static const unsigned null_constraint_idx = UINT_MAX;
|
||||
class trail_info {
|
||||
unsigned m_x_lower;
|
||||
public:
|
||||
trail_info(var x, bool is_lower):m_x_lower((x << 1) + static_cast<unsigned>(is_lower)) {}
|
||||
trail_info():m_x_lower(UINT_MAX) {}
|
||||
var x() const { return m_x_lower >> 1; }
|
||||
bool is_lower() const { return (m_x_lower & 1) != 0; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
enum ckind { LINEAR // only linear equalities so far.
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Constraints don't need justification.
|
||||
*/
|
||||
class constraint {
|
||||
friend class bound_propagator;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_dead:1;
|
||||
unsigned m_timestamp; // Constraint tried to propagate new bounds using bounds with timestamp < m_timestamp.
|
||||
unsigned m_act; // activity
|
||||
unsigned m_counter; // number of times the constraint propagated
|
||||
union {
|
||||
linear_equation * m_eq;
|
||||
};
|
||||
};
|
||||
|
||||
enum bkind { AXIOM, // doesn't need justification
|
||||
ASSUMPTION, // aka external case-split, it is used to connect with external search engine.
|
||||
DERIVED, // implied
|
||||
DECISION // internal case-split
|
||||
};
|
||||
|
||||
struct bound {
|
||||
mpq m_k;
|
||||
double m_approx_k;
|
||||
unsigned m_lower:1;
|
||||
unsigned m_strict:1;
|
||||
unsigned m_mark:1;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_level:27;
|
||||
unsigned m_timestamp;
|
||||
union {
|
||||
assumption m_assumption;
|
||||
unsigned m_constraint_idx;
|
||||
};
|
||||
bound * m_prev;
|
||||
bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk,
|
||||
unsigned c_idx, assumption a, bound * prev);
|
||||
|
||||
bound * at(unsigned timestamp);
|
||||
bkind kind() const { return static_cast<bkind>(m_kind); }
|
||||
bool is_lower() const { return m_lower != 0; }
|
||||
};
|
||||
|
||||
typedef ptr_vector<bound> var2bound;
|
||||
typedef svector<var> var_vector;
|
||||
typedef svector<constraint> constraint_vector;
|
||||
typedef unsigned_vector c_idx_vector;
|
||||
typedef c_idx_vector wlist;
|
||||
typedef small_object_allocator allocator;
|
||||
typedef linear_equation_manager lin_eq_manager;
|
||||
|
||||
numeral_manager & m;
|
||||
allocator & m_allocator;
|
||||
lin_eq_manager m_eq_manager;
|
||||
constraint_vector m_constraints;
|
||||
char_vector m_is_int;
|
||||
char_vector m_dead;
|
||||
var2bound m_lowers;
|
||||
var2bound m_uppers;
|
||||
vector<wlist> m_watches;
|
||||
svector<trail_info> m_trail;
|
||||
unsigned m_qhead;
|
||||
c_idx_vector m_reinit_stack;
|
||||
unsigned_vector m_lower_refinements; // number of times a lower bound was propagated for each variable (loop prevention)
|
||||
unsigned_vector m_upper_refinements; // number of times a upper bound was propagated for each variable (loop prevention)
|
||||
unsigned m_timestamp;
|
||||
var m_conflict;
|
||||
mpq m_tmp;
|
||||
|
||||
struct scope {
|
||||
unsigned m_trail_limit;
|
||||
unsigned m_qhead_old;
|
||||
unsigned m_reinit_stack_limit;
|
||||
unsigned m_timestamp_old:31;
|
||||
unsigned m_in_conflict:1;
|
||||
};
|
||||
|
||||
svector<scope> m_scopes;
|
||||
|
||||
unsigned_vector m_to_reset_ts; // temp field: ids of the constraints we must reset the field m_timestamp
|
||||
|
||||
// config
|
||||
unsigned m_max_refinements; // maximum number of refinements per round
|
||||
double m_small_interval;
|
||||
double m_threshold; // improvement threshold
|
||||
double m_strict2double;
|
||||
|
||||
// statistics
|
||||
unsigned m_conflicts;
|
||||
unsigned m_propagations;
|
||||
unsigned m_false_alarms;
|
||||
|
||||
void del_constraint(constraint & cnstr);
|
||||
void del_constraints_core();
|
||||
|
||||
template<bool LOWER>
|
||||
bool relevant_bound(var x, double approx_k) const;
|
||||
bool relevant_lower(var x, double approx_k) const;
|
||||
bool relevant_upper(var x, double approx_k) const;
|
||||
bool get_interval_size(var x, double & r) const;
|
||||
void check_feasibility(var x);
|
||||
|
||||
bool assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
bool assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
|
||||
bool propagate(unsigned c_idx);
|
||||
bool propagate_eq(unsigned c_idx);
|
||||
bool propagate_lower(unsigned c_idx, unsigned i);
|
||||
bool propagate_upper(unsigned c_idx, unsigned i);
|
||||
void undo_trail(unsigned old_sz);
|
||||
|
||||
typedef std::pair<var, bound *> var_bound;
|
||||
svector<var_bound> m_todo;
|
||||
void explain(var x, bound * b, unsigned ts, assumption_vector & ex) const;
|
||||
bool is_a_i_pos(linear_equation const & eq, var x) const;
|
||||
|
||||
template<bool LOWER, typename Numeral>
|
||||
bool get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const;
|
||||
|
||||
void init_eq(linear_equation * eq);
|
||||
|
||||
public:
|
||||
bound_propagator(numeral_manager & m, allocator & a, params_ref const & p);
|
||||
~bound_propagator();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
|
||||
double strict2double() const { return m_strict2double; }
|
||||
|
||||
bool is_int(var x) const { return m_is_int[x] != 0; }
|
||||
|
||||
unsigned scope_lvl() const { return m_scopes.size(); }
|
||||
void mk_var(var x, bool is_int);
|
||||
void del_var(var x);
|
||||
bool is_dead(var x) const { return m_dead[x] != 0; }
|
||||
void mk_eq(unsigned sz, mpq * as, var * xs);
|
||||
void mk_eq(unsigned sz, mpz * as, var * xs);
|
||||
void del_constraints();
|
||||
void assert_lower(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_upper(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_decided_lower(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void assert_decided_upper(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void propagate();
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void reset();
|
||||
bool has_lower(var x) const { return m_lowers[x] != 0; }
|
||||
bool has_upper(var x) const { return m_uppers[x] != 0; }
|
||||
bool lower(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool upper(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool is_fixed(var x) const { return has_lower(x) && has_upper(x) && m.eq(m_lowers[x]->m_k, m_uppers[x]->m_k) && !inconsistent(); }
|
||||
mpq const & lower(var x, bool & strict) const { SASSERT(has_lower(x)); bound * b = m_lowers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & upper(var x, bool & strict) const { SASSERT(has_upper(x)); bound * b = m_uppers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_k; }
|
||||
mpq const & upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_k; }
|
||||
double approx_lower(var x) const {
|
||||
SASSERT(has_lower(x));
|
||||
return m_lowers[x]->m_strict ? m_lowers[x]->m_approx_k + m_strict2double : m_lowers[x]->m_approx_k;
|
||||
}
|
||||
double approx_upper(var x) const {
|
||||
SASSERT(has_upper(x));
|
||||
return m_uppers[x]->m_strict ? m_uppers[x]->m_approx_k - m_strict2double : m_uppers[x]->m_approx_k;
|
||||
}
|
||||
bool is_zero(var x) const { return has_lower(x) && has_upper(x) && m.is_zero(lower(x)) && m.is_zero(upper(x)); }
|
||||
void explain_lower(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_lowers[x], ts, ex); }
|
||||
void explain_upper(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_uppers[x], ts, ex); }
|
||||
void explain_lower(var x, assumption_vector & ex) const { explain_lower(x, m_timestamp, ex); }
|
||||
void explain_upper(var x, assumption_vector & ex) const { explain_upper(x, m_timestamp, ex); }
|
||||
var conflict_var() const { return m_conflict; }
|
||||
bool inconsistent() const { return m_conflict != null_var; }
|
||||
|
||||
unsigned trail_size() const { return m_trail.size(); }
|
||||
unsigned qhead() const { return m_qhead; }
|
||||
|
||||
typedef svector<trail_info>::const_iterator trail_iterator;
|
||||
|
||||
trail_iterator begin_trail() const { return m_trail.begin(); }
|
||||
trail_iterator end_trail() const { return m_trail.end(); }
|
||||
|
||||
bool lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
bool upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
void display(std::ostream & out) const;
|
||||
void display_var_bounds(std::ostream & out, var x, bool approx = true, bool precise = true) const;
|
||||
void display_bounds(std::ostream & out, bool approx = true, bool precise = true) const;
|
||||
void display_precise_bounds(std::ostream & out) const { display_bounds(out, false, true); }
|
||||
void display_approx_bounds(std::ostream & out) const { display_bounds(out, true, false); }
|
||||
void display_constraints(std::ostream & out) const;
|
||||
void display_bounds_of(std::ostream & out, linear_equation const & eq) const;
|
||||
|
||||
unsigned get_num_false_alarms() const { return m_false_alarms; }
|
||||
unsigned get_num_propagations() const { return m_propagations; }
|
||||
};
|
||||
|
||||
#endif
|
604
src/tactic/arith_tactics/bv2int_rewriter.cpp
Normal file
604
src/tactic/arith_tactics/bv2int_rewriter.cpp
Normal file
|
@ -0,0 +1,604 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2int_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2int propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include "bv2int_rewriter.h"
|
||||
#include "rewriter_def.h"
|
||||
#include "ast_pp.h"
|
||||
|
||||
void bv2int_rewriter_ctx::update_params(params_ref const& p) {
|
||||
m_max_size = p.get_uint(":max-bv-size", UINT_MAX);
|
||||
}
|
||||
|
||||
struct lt_rational {
|
||||
bool operator()(rational const& a, rational const& b) const { return a < b; }
|
||||
};
|
||||
|
||||
void bv2int_rewriter_ctx::collect_power2(goal const& s) {
|
||||
ast_manager& m = m_trail.get_manager();
|
||||
arith_util arith(m);
|
||||
bv_util bv(m);
|
||||
|
||||
for (unsigned j = 0; j < s.size(); ++j) {
|
||||
expr* f = s.form(j);
|
||||
if (!m.is_or(f)) continue;
|
||||
unsigned sz = to_app(f)->get_num_args();
|
||||
expr* x, *y, *v = 0;
|
||||
rational n;
|
||||
vector<rational> bounds;
|
||||
bool is_int, ok = true;
|
||||
|
||||
for (unsigned i = 0; ok && i < sz; ++i) {
|
||||
expr* e = to_app(f)->get_arg(i);
|
||||
if (!m.is_eq(e, x, y)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (arith.is_numeral(y, n, is_int) && is_int &&
|
||||
(x == v || v == 0)) {
|
||||
v = x;
|
||||
bounds.push_back(n);
|
||||
}
|
||||
else if (arith.is_numeral(x, n, is_int) && is_int &&
|
||||
(y == v || v == 0)) {
|
||||
v = y;
|
||||
bounds.push_back(n);
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok || !v) continue;
|
||||
SASSERT(!bounds.empty());
|
||||
lt_rational lt;
|
||||
// lt is a total order on rationals.
|
||||
std::sort(bounds.begin(), bounds.end(), lt);
|
||||
rational p(1);
|
||||
unsigned num_bits = 0;
|
||||
for (unsigned i = 0; ok && i < bounds.size(); ++i) {
|
||||
ok = (p == bounds[i]);
|
||||
p *= rational(2);
|
||||
++num_bits;
|
||||
}
|
||||
if (!ok) continue;
|
||||
unsigned log2 = 0;
|
||||
for (unsigned i = 1; i <= num_bits; i *= 2) ++log2;
|
||||
if(log2 == 0) continue;
|
||||
expr* logx = m.mk_fresh_const("log2_v", bv.mk_sort(log2));
|
||||
logx = bv.mk_zero_extend(num_bits - log2, logx);
|
||||
m_trail.push_back(logx);
|
||||
TRACE("bv2int_rewriter", tout << mk_pp(v, m) << " |-> " << mk_pp(logx, m) << "\n";);
|
||||
m_power2.insert(v, logx);
|
||||
}
|
||||
}
|
||||
|
||||
bool bv2int_rewriter_ctx::is_power2(expr* x, expr*& log_x) {
|
||||
return m_power2.find(x, log_x);
|
||||
}
|
||||
|
||||
bv2int_rewriter::bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx)
|
||||
:m_manager(m), m_ctx(ctx), m_bv(m), m_arith(m) {
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if(f->get_family_id() == m_arith.get_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_NUM: return BR_FAILED;
|
||||
case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result);
|
||||
case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result);
|
||||
case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result);
|
||||
case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result);
|
||||
case OP_ADD: return mk_add(num_args, args, result);
|
||||
case OP_MUL: return mk_mul(num_args, args, result);
|
||||
case OP_SUB: return mk_sub(num_args, args, result);
|
||||
case OP_DIV: return BR_FAILED;
|
||||
case OP_IDIV: SASSERT(num_args == 2); return mk_idiv(args[0], args[1], result);
|
||||
case OP_MOD: SASSERT(num_args == 2); return mk_mod(args[0], args[1], result);
|
||||
case OP_REM: SASSERT(num_args == 2); return mk_rem(args[0], args[1], result);
|
||||
case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result);
|
||||
case OP_TO_REAL: return BR_FAILED;
|
||||
case OP_TO_INT: return BR_FAILED;
|
||||
case OP_IS_INT: return BR_FAILED;
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
if (f->get_family_id() == m().get_basic_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result);
|
||||
case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result);
|
||||
default: return BR_FAILED;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_le(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_ule(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
// s1 - s2 <= t1 - t2
|
||||
// <=>
|
||||
// s1 + t2 <= t1 + s2
|
||||
//
|
||||
s1 = mk_bv_add(s1, t2, false);
|
||||
t1 = mk_bv_add(t1, s2, false);
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_ule(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = m_bv.mk_sle(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg2, arg1));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
return mk_le(arg2, arg1, result);
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), t1(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_bv2int(m().mk_ite(c, s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = mk_sbv2int(m().mk_ite(c, s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m().mk_eq(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
s1 = mk_bv_add(s1, t2, false);
|
||||
t1 = mk_bv_add(s2, t1, false);
|
||||
align_sizes(s1, t1, false);
|
||||
result = m().mk_eq(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = m().mk_eq(s1, t1);
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
// TBD
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
align_sizes(s1, t1, false);
|
||||
result = m_bv.mk_bv2int(m_bv.mk_bv_urem(s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
//
|
||||
// (s1 - s2) mod t1 = (s1 + (t1 - (s2 mod t1))) mod t1
|
||||
//
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int(t, t1)) {
|
||||
expr_ref u1(m());
|
||||
align_sizes(s1, t1, false);
|
||||
u1 = m_bv.mk_bv_urem(s1, t1);
|
||||
u1 = m_bv.mk_bv_sub(t1, u1);
|
||||
u1 = mk_bv_add(s1, u1, false);
|
||||
align_sizes(u1, t1, false);
|
||||
result = m_bv.mk_bv2int(m_bv.mk_bv_urem(u1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TBD: check semantics
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
result = mk_sbv2int(m_bv.mk_bv_srem(s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
#endif
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
// TBD
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_uminus(expr * s, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m());
|
||||
if (is_bv2int_diff(s, s1, s2)) {
|
||||
result = m_arith.mk_sub(m_bv.mk_bv2int(s2), m_bv.mk_bv2int(s1));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1)) {
|
||||
result = mk_sbv2int(m_bv.mk_bv_neg(s1));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_add(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void bv2int_rewriter::align_sizes(expr_ref& s, expr_ref& t, bool is_signed) {
|
||||
unsigned sz1 = m_bv.get_bv_size(s);
|
||||
unsigned sz2 = m_bv.get_bv_size(t);
|
||||
if (sz1 > sz2 && is_signed) {
|
||||
t = mk_extend(sz1-sz2, t, true);
|
||||
}
|
||||
if (sz1 > sz2 && !is_signed) {
|
||||
t = mk_extend(sz1-sz2, t, false);
|
||||
}
|
||||
if (sz1 < sz2 && is_signed) {
|
||||
s = mk_extend(sz2-sz1, s, true);
|
||||
}
|
||||
if (sz1 < sz2 && !is_signed) {
|
||||
s = mk_extend(sz2-sz1, s, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool bv2int_rewriter::is_zero(expr* n) {
|
||||
rational r;
|
||||
unsigned sz;
|
||||
return m_bv.is_numeral(n, r, sz) && r.is_zero();
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_bv_add(expr* s, expr* t, bool is_signed) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
|
||||
if (is_zero(s)) {
|
||||
return t;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return s;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1, is_signed);
|
||||
s1 = mk_extend(1, s1, is_signed);
|
||||
t1 = mk_extend(1, t1, is_signed);
|
||||
return m_bv.mk_bv_add(s1, t1);
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_add(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
result = m_bv.mk_bv2int(mk_bv_add(s1, t1, false));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
// s1 - s2 + t1 - t2
|
||||
// =
|
||||
// s1 + t1 - (s2 + t2)
|
||||
//
|
||||
t1 = m_bv.mk_bv2int(mk_bv_add(s1, t1, false));
|
||||
t2 = m_bv.mk_bv2int(mk_bv_add(s2, t2, false));
|
||||
result = m_arith.mk_sub(t1, t2);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
result = mk_sbv2int(mk_bv_add(s1, t1, true));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_mul(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_bv_mul(expr* s, expr* t, bool is_signed) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
if (is_zero(s)) {
|
||||
return s;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return t;
|
||||
}
|
||||
rational r;
|
||||
unsigned sz;
|
||||
if (m_bv.is_numeral(s, r, sz) && r.is_one()) {
|
||||
return t;
|
||||
}
|
||||
if (m_bv.is_numeral(t, r, sz) && r.is_one()) {
|
||||
return s;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1, is_signed);
|
||||
unsigned n = m_bv.get_bv_size(t1);
|
||||
unsigned max_bits = m_ctx.get_max_num_bits();
|
||||
bool add_side_conds = 2*n > max_bits;
|
||||
if (n >= max_bits) {
|
||||
//
|
||||
}
|
||||
else if (2*n > max_bits) {
|
||||
s1 = mk_extend(max_bits-n, s1, is_signed);
|
||||
t1 = mk_extend(max_bits-n, t1, is_signed);
|
||||
}
|
||||
else {
|
||||
s1 = mk_extend(n, s1, is_signed);
|
||||
t1 = mk_extend(n, t1, is_signed);
|
||||
}
|
||||
if (add_side_conds) {
|
||||
if (is_signed) {
|
||||
m_ctx.add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1));
|
||||
m_ctx.add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1));
|
||||
}
|
||||
else {
|
||||
m_ctx.add_side_condition(m_bv.mk_bvumul_no_ovfl(s1, t1));
|
||||
}
|
||||
}
|
||||
return m_bv.mk_bv_mul(s1, t1);
|
||||
}
|
||||
|
||||
|
||||
br_status bv2int_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
if ((is_shl1(s, s1) && is_bv2int(t, t1)) ||
|
||||
(is_shl1(t, s1) && is_bv2int(s, t1))) {
|
||||
unsigned n = m_bv.get_bv_size(s1);
|
||||
unsigned m = m_bv.get_bv_size(t1);
|
||||
s1 = mk_extend(m, s1, false);
|
||||
t1 = mk_extend(n, t1, false);
|
||||
result = m_bv.mk_bv2int(m_bv.mk_bv_shl(t1, s1));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_bv2int(s, s1) && is_bv2int(t, t1)) {
|
||||
result = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false));
|
||||
return BR_DONE;
|
||||
}
|
||||
if ((is_bv2int(s, s1) && is_bv2int_diff(t, t1, t2)) ||
|
||||
(is_bv2int(t, s1) && is_bv2int_diff(s, t1, t2))) {
|
||||
t1 = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false));
|
||||
t2 = m_bv.mk_bv2int(mk_bv_mul(s1, t2, false));
|
||||
result = m_arith.mk_sub(t1, t2);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
result = mk_sbv2int(mk_bv_mul(s1, t1, true));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_sub(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
br_status bv2int_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) {
|
||||
// s1 - s2 - (t1 - t2)
|
||||
// =
|
||||
// s1 + t2 - (t1 + s2)
|
||||
//
|
||||
s1 = m_bv.mk_bv2int(mk_bv_add(s1, t2, false));
|
||||
s2 = m_bv.mk_bv2int(mk_bv_add(s2, t1, false));
|
||||
result = m_arith.mk_sub(s1, s2);
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) {
|
||||
align_sizes(s1, t1, true);
|
||||
s1 = m_bv.mk_sign_extend(1, s1);
|
||||
t1 = m_bv.mk_sign_extend(1, t1);
|
||||
result = mk_sbv2int(m_bv.mk_bv_sub(s1, t1));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_bv2int(expr* n, expr_ref& s) {
|
||||
rational k;
|
||||
bool is_int;
|
||||
if (m_bv.is_bv2int(n)) {
|
||||
s = to_app(n)->get_arg(0);
|
||||
return true;
|
||||
}
|
||||
if (m_arith.is_numeral(n, k, is_int) && is_int && !k.is_neg()) {
|
||||
unsigned sz = k.get_num_bits();
|
||||
s = m_bv.mk_numeral(k, m_bv.mk_sort(sz));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_shl1(expr* n, expr_ref& s) {
|
||||
expr* s1, *s2;
|
||||
rational r;
|
||||
unsigned bv_size;
|
||||
if(m_bv.is_bv2int(n, s2) &&
|
||||
m_bv.is_bv_shl(s2, s1, s2) &&
|
||||
m_bv.is_numeral(s1, r, bv_size) &&
|
||||
r.is_one()) {
|
||||
s = s2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_bv2int_diff(expr* n, expr_ref& s, expr_ref& t) {
|
||||
if (is_bv2int(n, s)) {
|
||||
t = m_bv.mk_numeral(0, 1);
|
||||
return true;
|
||||
}
|
||||
rational k;
|
||||
bool is_int;
|
||||
if (m_arith.is_numeral(n, k, is_int) && is_int) {
|
||||
SASSERT(k.is_neg());
|
||||
k.neg();
|
||||
unsigned sz = k.get_num_bits();
|
||||
t = m_bv.mk_numeral(k, m_bv.mk_sort(sz));
|
||||
s = m_bv.mk_numeral(0, 1);
|
||||
return true;
|
||||
}
|
||||
//
|
||||
// bv2int(a) - bv2int(b)
|
||||
//
|
||||
expr *e1, *e2;
|
||||
if (m_arith.is_sub(n, e1, e2) &&
|
||||
is_bv2int(e1, s) &&
|
||||
is_bv2int(e2, t)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2int_rewriter::is_sbv2int(expr* n, expr_ref& s) {
|
||||
if (is_bv2int(n, s)) {
|
||||
s = m_bv.mk_zero_extend(1, s);
|
||||
return true;
|
||||
}
|
||||
expr_ref u1(m()), u2(m());
|
||||
if (is_bv2int_diff(n, u1, u2)) {
|
||||
align_sizes(u1, u2, false);
|
||||
u1 = mk_extend(1, u1, false);
|
||||
u2 = mk_extend(1, u2, false);
|
||||
s = m_bv.mk_bv_sub(u1, u2);
|
||||
return true;
|
||||
}
|
||||
// ite(bv1 == b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2]))
|
||||
expr* c, *t, *e1, *c1, *c2, *c3, *t1, *t2, *e2, *e3;
|
||||
rational k;
|
||||
bool is_int;
|
||||
unsigned lo, hi, lo1, hi1, sz;
|
||||
|
||||
if (m().is_ite(n, c, t, e1) &&
|
||||
m().is_eq(c, c1, c2) &&
|
||||
m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 &&
|
||||
m_bv.is_extract(c2, lo, hi, c3) &&
|
||||
lo == hi && lo == m_bv.get_bv_size(c3) - 1 &&
|
||||
m_arith.is_sub(t, t1, t2) &&
|
||||
e1 == t1 &&
|
||||
m_bv.is_bv2int(e1, e2) &&
|
||||
m_bv.is_extract(e2, lo1, hi1, e3) &&
|
||||
lo1 == 0 && hi1 == hi-1 &&
|
||||
m_arith.is_numeral(t2, k, is_int) && is_int &&
|
||||
k == m_bv.power_of_two(hi)
|
||||
) {
|
||||
s = e3;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// bv2int(b[0:n-2]) - ite(bv1 == b[n-1:n-1], 2^{n-1}, 0)
|
||||
if (m().is_sub(n, e1, e2) &&
|
||||
m_bv.is_bv2int(e1, e3) &&
|
||||
m_bv.is_extract(e3, lo, hi, e4) &&
|
||||
lo == 0 && hi == m_bv.get_bv_size(e4) - 2 &&
|
||||
m().is_ite(e2, t1, t2, t3) &&
|
||||
m().is_eq(t1, c1, c2) &&
|
||||
m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 &&
|
||||
m_bv.is_extract(c2, lo1, hi1, c3) && lo1 == h1 + 1 && hi1 == lo1 &&
|
||||
c3 == e4 &&
|
||||
m_arith.is_numeral(t2, )) {
|
||||
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_sbv2int(expr* b) {
|
||||
//
|
||||
// ite(bit1 = b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2]))
|
||||
//
|
||||
expr* bv1 = m_bv.mk_numeral(1, 1);
|
||||
unsigned n = m_bv.get_bv_size(b);
|
||||
expr* c = m().mk_eq(bv1, m_bv.mk_extract(n-1, n-1, b));
|
||||
expr* e = m_bv.mk_bv2int(m_bv.mk_extract(n-2, 0, b));
|
||||
expr* t = m_arith.mk_sub(e, m_arith.mk_numeral(power(rational(2), n-1), true));
|
||||
return m().mk_ite(c, t, e);
|
||||
}
|
||||
|
||||
expr* bv2int_rewriter::mk_extend(unsigned sz, expr* b, bool is_signed) {
|
||||
if (sz == 0) {
|
||||
return b;
|
||||
}
|
||||
rational r;
|
||||
unsigned bv_sz;
|
||||
if (is_signed) {
|
||||
return m_bv.mk_sign_extend(sz, b);
|
||||
}
|
||||
else if (m_bv.is_numeral(b, r, bv_sz)) {
|
||||
return m_bv.mk_numeral(r, bv_sz + sz);
|
||||
}
|
||||
else {
|
||||
return m_bv.mk_zero_extend(sz, b);
|
||||
}
|
||||
}
|
||||
|
||||
template class rewriter_tpl<bv2int_rewriter_cfg>;
|
||||
|
||||
|
||||
|
||||
|
120
src/tactic/arith_tactics/bv2int_rewriter.h
Normal file
120
src/tactic/arith_tactics/bv2int_rewriter.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2int_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2int propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _BV2INT_REWRITER_H_
|
||||
#define _BV2INT_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter.h"
|
||||
#include"bv_decl_plugin.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"params.h"
|
||||
#include"goal.h"
|
||||
|
||||
class bv2int_rewriter_ctx {
|
||||
unsigned m_max_size;
|
||||
expr_ref_vector m_side_conditions;
|
||||
obj_map<expr, expr*> m_power2;
|
||||
expr_ref_vector m_trail;
|
||||
|
||||
public:
|
||||
bv2int_rewriter_ctx(ast_manager& m, params_ref const& p) :
|
||||
m_side_conditions(m), m_trail(m) { update_params(p); }
|
||||
|
||||
void reset() { m_side_conditions.reset(); m_trail.reset(); m_power2.reset(); }
|
||||
void add_side_condition(expr* e) { m_side_conditions.push_back(e); }
|
||||
unsigned num_side_conditions() const { return m_side_conditions.size(); }
|
||||
expr* const* side_conditions() const { return m_side_conditions.c_ptr(); }
|
||||
unsigned get_max_num_bits() const { return m_max_size; }
|
||||
|
||||
void collect_power2(goal const & s);
|
||||
bool is_power2(expr* x, expr*& log_x);
|
||||
obj_map<expr, expr*> const& power2() const { return m_power2; }
|
||||
|
||||
private:
|
||||
void update_params(params_ref const& p);
|
||||
};
|
||||
|
||||
class bv2int_rewriter {
|
||||
ast_manager & m_manager;
|
||||
bv2int_rewriter_ctx& m_ctx;
|
||||
bv_util m_bv;
|
||||
arith_util m_arith;
|
||||
|
||||
public:
|
||||
bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx);
|
||||
ast_manager & m() const { return m_manager; }
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(f, num_args, args);
|
||||
}
|
||||
private:
|
||||
br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_le(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_idiv(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_mod(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_add(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_mul(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_sub(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_uminus(expr* e, expr_ref & result);
|
||||
|
||||
bool is_bv2int(expr* e, expr_ref& s);
|
||||
bool is_sbv2int(expr* e, expr_ref& s);
|
||||
bool is_bv2int_diff(expr* e, expr_ref& s, expr_ref& t);
|
||||
bool is_zero(expr* e);
|
||||
bool is_shl1(expr* e, expr_ref& s);
|
||||
|
||||
expr* mk_bv_add(expr* s, expr* t, bool is_signed);
|
||||
expr* mk_bv_mul(expr* s, expr* t, bool is_signed);
|
||||
expr* mk_sbv2int(expr* s);
|
||||
expr* mk_extend(unsigned sz, expr* b, bool is_signed);
|
||||
|
||||
void align_sizes(expr_ref& s, expr_ref& t, bool is_signed);
|
||||
|
||||
};
|
||||
|
||||
struct bv2int_rewriter_cfg : public default_rewriter_cfg {
|
||||
bv2int_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
bv2int_rewriter_cfg(ast_manager & m, bv2int_rewriter_ctx& ctx):m_r(m, ctx) {}
|
||||
};
|
||||
|
||||
class bv2int_rewriter_star : public rewriter_tpl<bv2int_rewriter_cfg> {
|
||||
bv2int_rewriter_cfg m_cfg;
|
||||
public:
|
||||
bv2int_rewriter_star(ast_manager & m, bv2int_rewriter_ctx& ctx):
|
||||
rewriter_tpl<bv2int_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(m, ctx) {}
|
||||
};
|
||||
|
||||
#endif
|
695
src/tactic/arith_tactics/bv2real_rewriter.cpp
Normal file
695
src/tactic/arith_tactics/bv2real_rewriter.cpp
Normal file
|
@ -0,0 +1,695 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2real_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2real propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-08-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"bv2real_rewriter.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"ast_pp.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
|
||||
bv2real_util::bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits) :
|
||||
m_manager(m),
|
||||
m_arith(m),
|
||||
m_bv(m),
|
||||
m_decls(m),
|
||||
m_pos_le(m),
|
||||
m_pos_lt(m),
|
||||
m_side_conditions(m),
|
||||
m_default_root(default_root),
|
||||
m_default_divisor(default_divisor),
|
||||
m_max_divisor(rational(2)*default_divisor),
|
||||
m_max_num_bits(max_num_bits) {
|
||||
sort* real = m_arith.mk_real();
|
||||
sort* domain[2] = { real, real };
|
||||
m_pos_lt = m.mk_fresh_func_decl("<","",2,domain,m.mk_bool_sort());
|
||||
m_pos_le = m.mk_fresh_func_decl("<=","",2,domain,m.mk_bool_sort());
|
||||
m_decls.push_back(m_pos_lt);
|
||||
m_decls.push_back(m_pos_le);
|
||||
}
|
||||
|
||||
bool bv2real_util::is_bv2real(func_decl* f) const {
|
||||
return m_decl2sig.contains(f);
|
||||
}
|
||||
|
||||
bool bv2real_util::is_bv2real(func_decl* f, unsigned num_args, expr* const* args,
|
||||
expr*& m, expr*& n, rational& d, rational& r) const {
|
||||
bvr_sig sig;
|
||||
if (!m_decl2sig.find(f, sig)) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(num_args == 2);
|
||||
m = args[0];
|
||||
n = args[1];
|
||||
d = sig.m_d;
|
||||
r = sig.m_r;
|
||||
SASSERT(sig.m_d.is_int() && sig.m_d.is_pos());
|
||||
SASSERT(sig.m_r.is_int() && sig.m_r.is_pos());
|
||||
SASSERT(m_bv.get_bv_size(m) == sig.m_msz);
|
||||
SASSERT(m_bv.get_bv_size(n) == sig.m_nsz);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bv2real_util::is_bv2real(expr* e, expr*& m, expr*& n, rational& d, rational& r) const {
|
||||
if (!is_app(e)) return false;
|
||||
func_decl* f = to_app(e)->get_decl();
|
||||
return is_bv2real(f, to_app(e)->get_num_args(), to_app(e)->get_args(), m, n, d, r);
|
||||
}
|
||||
|
||||
class bv2real_util::contains_bv2real_proc {
|
||||
bv2real_util const& m_util;
|
||||
public:
|
||||
class found {};
|
||||
contains_bv2real_proc(bv2real_util const& u): m_util(u) {}
|
||||
void operator()(app* a) {
|
||||
if (m_util.is_bv2real(a->get_decl())) {
|
||||
throw found();
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
bool bv2real_util::contains_bv2real(expr* e) const {
|
||||
contains_bv2real_proc p(*this);
|
||||
try {
|
||||
for_each_expr(p, e);
|
||||
}
|
||||
catch (contains_bv2real_proc::found) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2real_util::mk_bv2real(expr* _s, expr* _t, rational& d, rational& r, expr_ref& result) {
|
||||
expr_ref s(_s,m()), t(_t,m());
|
||||
if (align_divisor(s, t, d)) {
|
||||
result = mk_bv2real_c(s, t, d, r);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r) {
|
||||
bvr_sig sig;
|
||||
sig.m_msz = m_bv.get_bv_size(s);
|
||||
sig.m_nsz = m_bv.get_bv_size(t);
|
||||
sig.m_d = d;
|
||||
sig.m_r = r;
|
||||
func_decl* f;
|
||||
if (!m_sig2decl.find(sig, f)) {
|
||||
sort* domain[2] = { m_manager.get_sort(s), m_manager.get_sort(t) };
|
||||
sort* real = m_arith.mk_real();
|
||||
f = m_manager.mk_fresh_func_decl("bv2real", "", 2, domain, real);
|
||||
m_decls.push_back(f);
|
||||
m_sig2decl.insert(sig, f);
|
||||
m_decl2sig.insert(f, sig);
|
||||
}
|
||||
return m_manager.mk_app(f, s, t);
|
||||
}
|
||||
|
||||
void bv2real_util::mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result) {
|
||||
expr_ref s1(m()), t1(m()), r1(m());
|
||||
rational num;
|
||||
mk_sbv2real(s, s1);
|
||||
mk_sbv2real(t, t1);
|
||||
mk_div(s1, d, s1);
|
||||
mk_div(t1, d, t1);
|
||||
r1 = a().mk_power(a().mk_numeral(r, false), a().mk_numeral(rational(1,2),false));
|
||||
t1 = a().mk_mul(t1, r1);
|
||||
result = a().mk_add(s1, t1);
|
||||
}
|
||||
|
||||
void bv2real_util::mk_div(expr* e, rational const& d, expr_ref& result) {
|
||||
result = a().mk_div(e, a().mk_numeral(rational(d), false));
|
||||
}
|
||||
|
||||
void bv2real_util::mk_sbv2real(expr* e, expr_ref& result) {
|
||||
rational r;
|
||||
unsigned bv_size = m_bv.get_bv_size(e);
|
||||
rational bsize = power(rational(2), bv_size);
|
||||
expr_ref bvr(a().mk_to_real(m_bv.mk_bv2int(e)), m());
|
||||
expr_ref c(m_bv.mk_sle(m_bv.mk_numeral(rational(0), bv_size), e), m());
|
||||
result = m().mk_ite(c, bvr, a().mk_sub(bvr, a().mk_numeral(bsize, false)));
|
||||
}
|
||||
|
||||
|
||||
expr* bv2real_util::mk_bv_mul(rational const& n, expr* t) {
|
||||
if (n.is_one()) return t;
|
||||
expr* s = mk_sbv(n);
|
||||
return mk_bv_mul(s, t);
|
||||
}
|
||||
|
||||
void bv2real_util::align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2) {
|
||||
if (d1 == d2) {
|
||||
return;
|
||||
}
|
||||
// s/d1 ~ t/d2 <=> lcm*s/d1 ~ lcm*t/d2 <=> (lcm/d1)*s ~ (lcm/d2)*t
|
||||
// s/d1 ~ t/d2 <=> s/gcd*d1' ~ t/gcd*d2' <=> d2'*s/lcm ~ d1'*t/lcm
|
||||
|
||||
rational g = gcd(d1,d2);
|
||||
rational l = lcm(d1,d2);
|
||||
rational d1g = d1/g;
|
||||
rational d2g = d2/g;
|
||||
s1 = mk_bv_mul(d2g, s1);
|
||||
s2 = mk_bv_mul(d2g, s2);
|
||||
t1 = mk_bv_mul(d1g, t1);
|
||||
t2 = mk_bv_mul(d1g, t2);
|
||||
d1 = l;
|
||||
d2 = l;
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv_mul(expr* s, expr* t) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
if (is_zero(s)) {
|
||||
return s;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return t;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1);
|
||||
unsigned n = m_bv.get_bv_size(t1);
|
||||
unsigned max_bits = get_max_num_bits();
|
||||
bool add_side_conds = 2*n > max_bits;
|
||||
if (n >= max_bits) {
|
||||
// nothing
|
||||
}
|
||||
else if (2*n > max_bits) {
|
||||
s1 = mk_extend(max_bits-n, s1);
|
||||
t1 = mk_extend(max_bits-n, t1);
|
||||
}
|
||||
else {
|
||||
s1 = mk_extend(n, s1);
|
||||
t1 = mk_extend(n, t1);
|
||||
}
|
||||
if (add_side_conds) {
|
||||
add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1));
|
||||
add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1));
|
||||
}
|
||||
return m_bv.mk_bv_mul(s1, t1);
|
||||
}
|
||||
|
||||
bool bv2real_util::is_zero(expr* n) {
|
||||
rational r;
|
||||
unsigned sz;
|
||||
return m_bv.is_numeral(n, r, sz) && r.is_zero();
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv_add(expr* s, expr* t) {
|
||||
SASSERT(m_bv.is_bv(s));
|
||||
SASSERT(m_bv.is_bv(t));
|
||||
|
||||
if (is_zero(s)) {
|
||||
return t;
|
||||
}
|
||||
if (is_zero(t)) {
|
||||
return s;
|
||||
}
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1);
|
||||
s1 = mk_extend(1, s1);
|
||||
t1 = mk_extend(1, t1);
|
||||
return m_bv.mk_bv_add(s1, t1);
|
||||
}
|
||||
|
||||
void bv2real_util::align_sizes(expr_ref& s, expr_ref& t) {
|
||||
unsigned sz1 = m_bv.get_bv_size(s);
|
||||
unsigned sz2 = m_bv.get_bv_size(t);
|
||||
if (sz1 > sz2) {
|
||||
t = mk_extend(sz1-sz2, t);
|
||||
}
|
||||
else if (sz1 < sz2) {
|
||||
s = mk_extend(sz2-sz1, s);
|
||||
}
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_sbv(rational const& n) {
|
||||
SASSERT(n.is_int());
|
||||
if (n.is_neg()) {
|
||||
rational m = abs(n);
|
||||
unsigned nb = m.get_num_bits();
|
||||
return m_bv.mk_bv_neg(m_bv.mk_numeral(m, nb+1));
|
||||
}
|
||||
else {
|
||||
unsigned nb = n.get_num_bits();
|
||||
return m_bv.mk_numeral(n, nb+1);
|
||||
}
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_bv_sub(expr* s, expr* t) {
|
||||
expr_ref s1(s, m()), t1(t, m());
|
||||
align_sizes(s1, t1);
|
||||
s1 = mk_extend(1, s1);
|
||||
t1 = mk_extend(1, t1);
|
||||
return m_bv.mk_bv_sub(s1, t1);
|
||||
}
|
||||
|
||||
expr* bv2real_util::mk_extend(unsigned sz, expr* b) {
|
||||
if (sz == 0) {
|
||||
return b;
|
||||
}
|
||||
rational r;
|
||||
unsigned bv_sz;
|
||||
if (m_bv.is_numeral(b, r, bv_sz) &&
|
||||
power(rational(2),bv_sz-1) > r) {
|
||||
return m_bv.mk_numeral(r, bv_sz + sz);
|
||||
}
|
||||
return m_bv.mk_sign_extend(sz, b);
|
||||
}
|
||||
|
||||
|
||||
bool bv2real_util::is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r) {
|
||||
expr* _s, *_t;
|
||||
if (is_bv2real(n, _s, _t, d, r)) {
|
||||
s = _s;
|
||||
t = _t;
|
||||
return true;
|
||||
}
|
||||
rational k;
|
||||
bool is_int;
|
||||
if (m_arith.is_numeral(n, k, is_int) && !is_int) {
|
||||
d = denominator(k);
|
||||
r = default_root();
|
||||
s = mk_sbv(numerator(k));
|
||||
t = mk_sbv(rational(0));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv2real_util::align_divisor(expr_ref& s, expr_ref& t, rational& d) {
|
||||
if (d > max_divisor()) {
|
||||
//
|
||||
// if divisor is over threshold, then divide s and t
|
||||
// add side condition that s, t are divisible.
|
||||
//
|
||||
rational overflow = d / max_divisor();
|
||||
if (!overflow.is_int()) return false;
|
||||
if (!mk_is_divisible_by(s, overflow)) return false;
|
||||
if (!mk_is_divisible_by(t, overflow)) return false;
|
||||
d = max_divisor();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bv2real_util::mk_is_divisible_by(expr_ref& s, rational const& _overflow) {
|
||||
rational overflow(_overflow);
|
||||
SASSERT(overflow.is_int());
|
||||
SASSERT(overflow.is_pos());
|
||||
SASSERT(!overflow.is_one());
|
||||
TRACE("bv2real_rewriter",
|
||||
tout << mk_pp(s, m()) << " " << overflow << "\n";);
|
||||
unsigned power2 = 0;
|
||||
while ((overflow % rational(2)) == rational(0)) {
|
||||
power2++;
|
||||
overflow = div(overflow, rational(2));
|
||||
}
|
||||
|
||||
if (power2 > 0) {
|
||||
unsigned sz = m_bv.get_bv_size(s);
|
||||
if (sz <= power2) {
|
||||
add_side_condition(m().mk_eq(s, m_bv.mk_numeral(rational(0), sz)));
|
||||
s = m_bv.mk_numeral(rational(0), 1);
|
||||
}
|
||||
else {
|
||||
expr* s1 = m_bv.mk_extract(power2-1, 0, s);
|
||||
add_side_condition(m().mk_eq(s1, m_bv.mk_numeral(rational(0), power2)));
|
||||
s = m_bv.mk_extract(sz-1, power2, s);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("bv2real_rewriter",
|
||||
tout << mk_pp(s, m()) << " " << overflow << "\n";);
|
||||
|
||||
return overflow.is_one();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// bv2real_rewriter
|
||||
|
||||
bv2real_rewriter::bv2real_rewriter(ast_manager& m, bv2real_util& util):
|
||||
m_manager(m),
|
||||
m_util(util),
|
||||
m_bv(m),
|
||||
m_arith(m)
|
||||
{}
|
||||
|
||||
|
||||
br_status bv2real_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
TRACE("bv2real_rewriter",
|
||||
tout << mk_pp(f, m()) << " ";
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
tout << mk_pp(args[i], m()) << " ";
|
||||
}
|
||||
tout << "\n";);
|
||||
if(f->get_family_id() == m_arith.get_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_NUM: return BR_FAILED;
|
||||
case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result);
|
||||
case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result);
|
||||
case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result);
|
||||
case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result);
|
||||
case OP_ADD: return mk_add(num_args, args, result);
|
||||
case OP_MUL: return mk_mul(num_args, args, result);
|
||||
case OP_SUB: return mk_sub(num_args, args, result);
|
||||
case OP_DIV: SASSERT(num_args == 2); return mk_div(args[0], args[1], result);
|
||||
case OP_IDIV: return BR_FAILED;
|
||||
case OP_MOD: return BR_FAILED;
|
||||
case OP_REM: return BR_FAILED;
|
||||
case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result);
|
||||
case OP_TO_REAL: return BR_FAILED; // TBD
|
||||
case OP_TO_INT: return BR_FAILED; // TBD
|
||||
case OP_IS_INT: return BR_FAILED; // TBD
|
||||
default: return BR_FAILED;
|
||||
}
|
||||
}
|
||||
if (f->get_family_id() == m().get_basic_family_id()) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result);
|
||||
case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result);
|
||||
default: return BR_FAILED;
|
||||
}
|
||||
}
|
||||
if (u().is_pos_ltf(f)) {
|
||||
SASSERT(num_args == 2);
|
||||
return mk_lt_pos(args[0], args[1], result);
|
||||
}
|
||||
if (u().is_pos_lef(f)) {
|
||||
SASSERT(num_args == 2);
|
||||
return mk_le_pos(args[0], args[1], result);
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool bv2real_rewriter::mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
SASSERT(is_pos || is_neg);
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) &&
|
||||
r1 == r2 && r1 == rational(2)) {
|
||||
//
|
||||
// (s1 + s2*sqrt(2))/d1 <= (t1 + t2*sqrt(2))/d2
|
||||
// <=>
|
||||
//
|
||||
// let s1 = s1*d2-t1*d1, t2 = s2*d2-t2*d1
|
||||
//
|
||||
//
|
||||
// s1 + s2*sqrt(2) <= 0
|
||||
// <=
|
||||
// s1 + s2*approx(sign(s2),sqrt(2)) <= 0
|
||||
// or (s1 = 0 & s2 = 0)
|
||||
//
|
||||
// If s2 is negative use an under-approximation for sqrt(r).
|
||||
// If s2 is positive use an over-approximation for sqrt(r).
|
||||
// e.g., r = 2, then 5/4 and 3/2 are under/over approximations.
|
||||
// Then s1 + s2*approx(sign(s2), r) <= 0 => s1 + s2*sqrt(r) <= 0
|
||||
|
||||
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
s1 = u().mk_bv_sub(s1, t1);
|
||||
s2 = u().mk_bv_sub(s2, t2);
|
||||
unsigned s2_size = m_bv.get_bv_size(s2);
|
||||
expr_ref le_proxy(m().mk_fresh_const("le_proxy",m().mk_bool_sort()), m());
|
||||
u().add_aux_decl(to_app(le_proxy)->get_decl());
|
||||
expr_ref gt_proxy(m().mk_not(le_proxy), m());
|
||||
expr_ref s2_is_nonpos(m_bv.mk_sle(s2, m_bv.mk_numeral(rational(0), s2_size)), m());
|
||||
|
||||
expr_ref under(u().mk_bv_add(u().mk_bv_mul(rational(4), s1), u().mk_bv_mul(rational(5), s2)), m());
|
||||
expr_ref z1(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(under)), m());
|
||||
expr_ref le_under(m_bv.mk_sle(under, z1), m());
|
||||
expr_ref over(u().mk_bv_add(u().mk_bv_mul(rational(2), s1), u().mk_bv_mul(rational(3), s2)), m());
|
||||
expr_ref z2(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(over)), m());
|
||||
expr_ref le_over(m_bv.mk_sle(over, z2), m());
|
||||
|
||||
// predicate may occur in positive polarity.
|
||||
if (is_pos) {
|
||||
// s1 + s2*sqrt(2) <= 0 <== s2 <= 0 & s1 + s2*(5/4) <= 0; 4*s1 + 5*s2 <= 0
|
||||
expr* e1 = m().mk_implies(m().mk_and(le_proxy, s2_is_nonpos), le_under);
|
||||
// s1 + s2*sqrt(2) <= 0 <== s2 > 0 & s1 + s2*(3/2); 0 <=> 2*s1 + 3*s2 <= 0
|
||||
expr* e2 = m().mk_implies(m().mk_and(le_proxy, m().mk_not(s2_is_nonpos)), le_over);
|
||||
u().add_side_condition(e1);
|
||||
u().add_side_condition(e2);
|
||||
}
|
||||
// predicate may occur in negative polarity.
|
||||
if (is_neg) {
|
||||
// s1 + s2*sqrt(2) > 0 <== s2 > 0 & s1 + s2*(5/4) > 0; 4*s1 + 5*s2 > 0
|
||||
expr* e3 = m().mk_implies(m().mk_and(gt_proxy, m().mk_not(s2_is_nonpos)), m().mk_not(le_under));
|
||||
// s1 + s2*sqrt(2) > 0 <== s2 <= 0 & s1 + s2*(3/2) > 0 <=> 2*s1 + 3*s2 > 0
|
||||
expr* e4 = m().mk_implies(m().mk_and(gt_proxy, s2_is_nonpos), m().mk_not(le_over));
|
||||
u().add_side_condition(e3);
|
||||
u().add_side_condition(e4);
|
||||
}
|
||||
|
||||
TRACE("bv2real_rewriter", tout << "mk_le\n";);
|
||||
|
||||
if (is_pos) {
|
||||
result = le_proxy;
|
||||
}
|
||||
else {
|
||||
result = gt_proxy;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_le_pos(expr * s, expr * t, expr_ref & result) {
|
||||
if (mk_le(s, t, true, false, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_lt_pos(expr * s, expr * t, expr_ref & result) {
|
||||
if (mk_le(t, s, false, true, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2real_rewriter::mk_le(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
|
||||
if (mk_le(s, t, true, true, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
//
|
||||
// somewhat expensive approach without having
|
||||
// polarity information for sound approximation.
|
||||
//
|
||||
|
||||
// Convert to:
|
||||
// t1 + t2*sqrt(r) >= 0
|
||||
// then to:
|
||||
//
|
||||
// (t1 >= 0 && t2 <= 0 => t1^2 >= t2^2*r)
|
||||
// (t1 <= 0 && t2 >= 0 => t1^2 <= t2^2*r)
|
||||
// (t1 >= 0 || t2 >= 0)
|
||||
//
|
||||
// A cheaper approach is to approximate > under the assumption
|
||||
// that > occurs in positive polarity.
|
||||
// then if t2 is negative use an over-approximation for sqrt(r)
|
||||
// if t2 is positive use an under-approximation for sqrt(r).
|
||||
// e.g., r = 2, then 5/4 and 3/2 are under/over approximations.
|
||||
// Then t1 + t2*approx(sign(t2), r) > 0 => t1 + t2*sqrt(r) > 0
|
||||
//
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
t1 = u().mk_bv_sub(t1, s1);
|
||||
t2 = u().mk_bv_sub(t2, s2);
|
||||
expr_ref z1(m()), z2(m());
|
||||
z1 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t1));
|
||||
z2 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t2));
|
||||
|
||||
expr* gz1 = m_bv.mk_sle(z1, t1);
|
||||
expr* lz1 = m_bv.mk_sle(t1, z1);
|
||||
expr* gz2 = m_bv.mk_sle(z2, t2);
|
||||
expr* lz2 = m_bv.mk_sle(t2, z2);
|
||||
expr_ref t12(u().mk_bv_mul(t1, t1), m());
|
||||
expr_ref t22(u().mk_bv_mul(r1, u().mk_bv_mul(t2, t2)), m());
|
||||
u().align_sizes(t12, t22);
|
||||
expr* ge = m_bv.mk_sle(t22, t12);
|
||||
expr* le = m_bv.mk_sle(t12, t22);
|
||||
expr* e1 = m().mk_or(gz1, gz2);
|
||||
expr* e2 = m().mk_or(m().mk_not(gz1), m().mk_not(lz2), ge);
|
||||
expr* e3 = m().mk_or(m().mk_not(gz2), m().mk_not(lz1), le);
|
||||
result = m().mk_and(e1, e2, e3);
|
||||
TRACE("bv2real_rewriter", tout << "\n";);
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg2, arg1));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
return mk_le(arg2, arg1, result);
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_arith.mk_le(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
u().align_sizes(s1, t1);
|
||||
u().align_sizes(s2, t2);
|
||||
if (u().mk_bv2real(m().mk_ite(c, s1, t1), m().mk_ite(c, s2, t2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
u().align_sizes(s1, t1);
|
||||
u().align_sizes(s2, t2);
|
||||
result = m().mk_and(m().mk_eq(s1, t1), m().mk_eq(s2, t2));
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_uminus(expr * s, expr_ref & result) {
|
||||
expr_ref s1(m()), s2(m());
|
||||
rational d1, r1;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1)) {
|
||||
s1 = u().mk_extend(1, s1);
|
||||
s2 = u().mk_extend(1, s2);
|
||||
if (u().mk_bv2real(m_bv.mk_bv_neg(s1), m_bv.mk_bv_neg(s2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_add(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_add(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
if (u().mk_bv2real(u().mk_bv_add(s1, t1), u().mk_bv_add(t2, s2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_mul(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_div(expr* s, expr* t, expr_ref& result) {
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) {
|
||||
// TBD: optimize
|
||||
expr_ref s1(m()), t1(m()), s2(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
// s1*t1 + r1*(s2*t2) + (s1*t2 + s2*t2)*r1
|
||||
expr_ref u1(m()), u2(m());
|
||||
u1 = u().mk_bv_add(u().mk_bv_mul(s1, t1), u().mk_bv_mul(r1, u().mk_bv_mul(t2, s2)));
|
||||
u2 = u().mk_bv_add(u().mk_bv_mul(s1, t2), u().mk_bv_mul(s2, t1));
|
||||
rational tmp = d1*d2;
|
||||
if (u().mk_bv2real(u1, u2, tmp, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv2real_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
br_status r = BR_DONE;
|
||||
SASSERT(num_args > 0);
|
||||
result = args[0];
|
||||
for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) {
|
||||
r = mk_sub(result, args[i], result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
br_status bv2real_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) {
|
||||
expr_ref s1(m()), s2(m()), t1(m()), t2(m());
|
||||
rational d1, d2, r1, r2;
|
||||
if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) {
|
||||
u().align_divisors(s1, s2, t1, t2, d1, d2);
|
||||
if (u().mk_bv2real(u().mk_bv_sub(s1, t1), u().mk_bv_sub(s2, t2), d1, r1, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template class rewriter_tpl<bv2real_rewriter_cfg>;
|
||||
|
||||
|
||||
br_status bv2real_elim_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
expr* m, *n;
|
||||
rational d, r;
|
||||
if (m_util.is_bv2real(f, num_args, args, m, n, d, r)) {
|
||||
m_util.mk_bv2real_reduced(m, n, d, r, result);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
template class rewriter_tpl<bv2real_elim_rewriter_cfg>;
|
||||
|
233
src/tactic/arith_tactics/bv2real_rewriter.h
Normal file
233
src/tactic/arith_tactics/bv2real_rewriter.h
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv2real_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for bv2real propagation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-08-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _BV2REAL_REWRITER_H_
|
||||
#define _BV2REAL_REWRITER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"rewriter.h"
|
||||
#include"bv_decl_plugin.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
|
||||
//
|
||||
// bv2real[d,r](n,m) has interpretation:
|
||||
// sbv2int(n)/d + sbv2int(m)/d*sqrt(r)
|
||||
// where
|
||||
// sbv2int is signed bit-vector 2 integer.
|
||||
//
|
||||
class bv2real_util {
|
||||
struct bvr_sig {
|
||||
unsigned m_msz, m_nsz;
|
||||
rational m_d, m_r;
|
||||
};
|
||||
|
||||
struct bvr_eq {
|
||||
bool operator()(bvr_sig const& x, bvr_sig const& y) const {
|
||||
return
|
||||
x.m_msz == y.m_msz &&
|
||||
x.m_nsz == y.m_nsz &&
|
||||
x.m_d == y.m_d &&
|
||||
x.m_r == y.m_r;
|
||||
}
|
||||
};
|
||||
|
||||
struct bvr_hash {
|
||||
unsigned operator()(bvr_sig const& x) const {
|
||||
unsigned a[3] = { x.m_msz, x.m_nsz, x.m_d.hash() };
|
||||
return string_hash((char const*)a, 12, x.m_r.hash());
|
||||
}
|
||||
};
|
||||
|
||||
ast_manager& m_manager;
|
||||
arith_util m_arith;
|
||||
bv_util m_bv;
|
||||
func_decl_ref_vector m_decls;
|
||||
func_decl_ref m_pos_le;
|
||||
func_decl_ref m_pos_lt;
|
||||
expr_ref_vector m_side_conditions;
|
||||
map<bvr_sig, func_decl*, bvr_hash, bvr_eq> m_sig2decl;
|
||||
obj_map<func_decl, bvr_sig> m_decl2sig;
|
||||
rational m_default_root;
|
||||
rational m_default_divisor;
|
||||
rational m_max_divisor;
|
||||
unsigned m_max_num_bits;
|
||||
|
||||
class contains_bv2real_proc;
|
||||
|
||||
public:
|
||||
bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits);
|
||||
|
||||
void reset() { m_side_conditions.reset(); }
|
||||
|
||||
bool is_bv2real(func_decl* f) const;
|
||||
bool is_bv2real(func_decl* f, unsigned num_args, expr* const* args,
|
||||
expr*& m, expr*& n, rational& d, rational& r) const;
|
||||
bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d, rational& r) const;
|
||||
bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d);
|
||||
|
||||
bool contains_bv2real(expr* e) const;
|
||||
|
||||
bool mk_bv2real(expr* s, expr* t, rational& d, rational& r, expr_ref& result);
|
||||
expr* mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r);
|
||||
expr* mk_bv2real(expr* n, expr* m) { return mk_bv2real_c(n, m, default_divisor(), default_root()); }
|
||||
|
||||
void mk_bv2real_reduced(expr* s, expr* t, expr_ref & result) { mk_bv2real_reduced(s, t, default_divisor(), default_root(), result); }
|
||||
void mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result);
|
||||
|
||||
|
||||
//
|
||||
// Positive polarity comparison operators.
|
||||
// Translation of positive polarity comparison requires fewer clauses.
|
||||
//
|
||||
bool is_pos_ltf(func_decl* f) const { return f == m_pos_lt; }
|
||||
bool is_pos_lef(func_decl* f) const { return f == m_pos_le; }
|
||||
bool is_pos_lt(expr const* e) const { return is_app(e) && is_pos_ltf(to_app(e)->get_decl()); }
|
||||
bool is_pos_le(expr const* e) const { return is_app(e) && is_pos_lef(to_app(e)->get_decl()); }
|
||||
MATCH_BINARY(is_pos_lt);
|
||||
MATCH_BINARY(is_pos_le);
|
||||
expr* mk_pos_lt(expr* s, expr* t) { return m().mk_app(m_pos_lt, s, t); }
|
||||
expr* mk_pos_le(expr* s, expr* t) { return m().mk_app(m_pos_le, s, t); }
|
||||
|
||||
rational const& default_root() const { return m_default_root; }
|
||||
rational const& default_divisor() const { return m_default_divisor; }
|
||||
rational const& max_divisor() const { return m_max_divisor; }
|
||||
|
||||
unsigned get_max_num_bits() const { return m_max_num_bits; }
|
||||
|
||||
void add_side_condition(expr* e) { m_side_conditions.push_back(e); }
|
||||
unsigned num_side_conditions() const { return m_side_conditions.size(); }
|
||||
expr* const* side_conditions() const { return m_side_conditions.c_ptr(); }
|
||||
|
||||
bool is_zero(expr* e);
|
||||
|
||||
expr* mk_bv_add(expr* s, expr* t);
|
||||
expr* mk_bv_sub(expr* s, expr* t);
|
||||
expr* mk_bv_mul(expr* s, expr* t);
|
||||
expr* mk_bv_mul(rational const& n, expr* t);
|
||||
expr* mk_extend(unsigned sz, expr* b);
|
||||
expr* mk_sbv(rational const& n);
|
||||
|
||||
void align_sizes(expr_ref& s, expr_ref& t);
|
||||
void align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2);
|
||||
|
||||
bool is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r);
|
||||
bool align_divisor(expr_ref& s, expr_ref& t, rational& d);
|
||||
|
||||
bool mk_is_divisible_by(expr_ref& s, rational const& _overflow);
|
||||
|
||||
void add_aux_decl(func_decl* f) { m_decls.push_back(f); }
|
||||
unsigned num_aux_decls() const { return m_decls.size(); }
|
||||
func_decl* get_aux_decl(unsigned i) const { return m_decls[i]; }
|
||||
|
||||
private:
|
||||
ast_manager & m() const { return m_manager; }
|
||||
arith_util & a() { return m_arith; }
|
||||
void mk_div(expr* e, rational const& d, expr_ref& result);
|
||||
void mk_sbv2real(expr* e, expr_ref& result);
|
||||
};
|
||||
|
||||
|
||||
class bv2real_rewriter {
|
||||
ast_manager & m_manager;
|
||||
bv2real_util& m_util;
|
||||
bv_util m_bv;
|
||||
arith_util m_arith;
|
||||
|
||||
public:
|
||||
bv2real_rewriter(ast_manager & m, bv2real_util& util);
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
private:
|
||||
ast_manager & m() const { return m_manager; }
|
||||
arith_util & a() { return m_arith; }
|
||||
bv2real_util& u() { return m_util; }
|
||||
br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result);
|
||||
bool mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result);
|
||||
br_status mk_le(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_le_pos(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_lt_pos(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result);
|
||||
br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_div(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_add(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_mul(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_sub(expr* s, expr* t, expr_ref& result);
|
||||
br_status mk_uminus(expr* e, expr_ref & result);
|
||||
};
|
||||
|
||||
struct bv2real_rewriter_cfg : public default_rewriter_cfg {
|
||||
bv2real_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
bv2real_rewriter_cfg(ast_manager & m, bv2real_util& u):m_r(m, u) {}
|
||||
};
|
||||
|
||||
class bv2real_rewriter_star : public rewriter_tpl<bv2real_rewriter_cfg> {
|
||||
bv2real_rewriter_cfg m_cfg;
|
||||
public:
|
||||
bv2real_rewriter_star(ast_manager & m, bv2real_util& u):
|
||||
rewriter_tpl<bv2real_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(m, u) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
\brief replace le(bv2real(a),bv2real(b)) by under-approximation.
|
||||
*/
|
||||
|
||||
class bv2real_elim_rewriter {
|
||||
bv2real_util& m_util;
|
||||
public:
|
||||
bv2real_elim_rewriter(bv2real_util& util) : m_util(util) {}
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
};
|
||||
|
||||
|
||||
struct bv2real_elim_rewriter_cfg : public default_rewriter_cfg {
|
||||
bv2real_elim_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
bv2real_elim_rewriter_cfg(bv2real_util& u):m_r(u) {}
|
||||
};
|
||||
|
||||
class bv2real_elim_rewriter_star : public rewriter_tpl<bv2real_elim_rewriter_cfg> {
|
||||
bv2real_elim_rewriter_cfg m_cfg;
|
||||
public:
|
||||
bv2real_elim_rewriter_star(ast_manager & m, bv2real_util& u):
|
||||
rewriter_tpl<bv2real_elim_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(u) {}
|
||||
};
|
||||
|
||||
#endif
|
349
src/tactic/arith_tactics/degree_shift_tactic.cpp
Normal file
349
src/tactic/arith_tactics/degree_shift_tactic.cpp
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
degree_shift_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Simple degree shift procedure.
|
||||
Basic idea: if goal G contains a real variable x, x occurs with degrees
|
||||
d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1.
|
||||
Then, replace x^n with a new fresh variable y.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"cooperate.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"simplify_tactic.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"rewriter_def.h"
|
||||
|
||||
class degree_shift_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
arith_util m_autil;
|
||||
obj_map<app, rational> m_var2degree;
|
||||
obj_map<app, app*> m_var2var;
|
||||
obj_map<app, proof*> m_var2pr;
|
||||
expr_ref_vector m_pinned;
|
||||
ptr_vector<expr> m_todo;
|
||||
rational m_one;
|
||||
bool m_produce_models;
|
||||
bool m_produce_proofs;
|
||||
volatile bool m_cancel;
|
||||
|
||||
expr * mk_power(expr * t, rational const & k) {
|
||||
if (k.is_one())
|
||||
return t;
|
||||
else
|
||||
return m_autil.mk_power(t, m_autil.mk_numeral(k, false));
|
||||
}
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
imp & o;
|
||||
rw_cfg(imp & _o):o(_o) {}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
arith_util & u = o.m_autil;
|
||||
if (!is_decl_of(f, u.get_family_id(), OP_POWER) || !is_app(args[0]))
|
||||
return BR_FAILED;
|
||||
ast_manager & m = o.m;
|
||||
rational g;
|
||||
app * t = to_app(args[0]);
|
||||
if (!o.m_var2degree.find(t, g))
|
||||
return BR_FAILED;
|
||||
SASSERT(g > rational(1));
|
||||
SASSERT(g.is_int());
|
||||
rational k;
|
||||
VERIFY(u.is_numeral(args[1], k));
|
||||
SASSERT(gcd(k, g) == g);
|
||||
rational new_k = div(k, g);
|
||||
expr * new_arg = o.m_var2var.find(t);
|
||||
result = o.mk_power(new_arg, new_k);
|
||||
if (o.m_produce_proofs) {
|
||||
proof * pr = o.m_var2pr.find(t);
|
||||
app * fact = m.mk_eq(m.mk_app(f, num, args), result);
|
||||
result_pr = m.mk_th_lemma(u.get_family_id(), fact, 1, &pr);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
};
|
||||
|
||||
class rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
public:
|
||||
rw(imp & o):
|
||||
rewriter_tpl<rw_cfg>(o.m, o.m_produce_proofs, m_cfg),
|
||||
m_cfg(o) {
|
||||
}
|
||||
};
|
||||
|
||||
scoped_ptr<rw> m_rw;
|
||||
|
||||
imp(ast_manager & _m):
|
||||
m(_m),
|
||||
m_autil(_m),
|
||||
m_pinned(_m),
|
||||
m_one(1),
|
||||
m_rw(0) {
|
||||
m_cancel = false;
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
void checkpoint() {
|
||||
if (m_cancel)
|
||||
throw tactic_exception(TACTIC_CANCELED_MSG);
|
||||
cooperate("degree_shift");
|
||||
}
|
||||
|
||||
void visit(expr * t, expr_fast_mark1 & visited) {
|
||||
if (!visited.is_marked(t)) {
|
||||
visited.mark(t);
|
||||
m_todo.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
void save_degree(expr * t, rational const & k) {
|
||||
SASSERT(k.is_int());
|
||||
if (is_uninterp_const(t) && m_autil.is_real(t)) {
|
||||
rational old_k;
|
||||
if (m_var2degree.find(to_app(t), old_k)) {
|
||||
old_k = gcd(k, old_k);
|
||||
m_var2degree.insert(to_app(t), old_k);
|
||||
}
|
||||
else {
|
||||
m_var2degree.insert(to_app(t), k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit_args(expr * t, expr_fast_mark1 & visited) {
|
||||
if (is_app(t)) {
|
||||
unsigned num_args = to_app(t)->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = to_app(t)->get_arg(i);
|
||||
save_degree(arg, m_one);
|
||||
visit(arg, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect(expr * t, expr_fast_mark1 & visited) {
|
||||
rational k;
|
||||
visit(t, visited);
|
||||
while (!m_todo.empty()) {
|
||||
checkpoint();
|
||||
expr * t = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
if (is_var(t))
|
||||
continue;
|
||||
if (is_quantifier(t)) {
|
||||
unsigned num_children = to_quantifier(t)->get_num_children();
|
||||
for (unsigned i = 0; i < num_children; i ++)
|
||||
visit(to_quantifier(t)->get_child(i), visited);
|
||||
}
|
||||
else {
|
||||
SASSERT(is_app(t));
|
||||
if (m_autil.is_power(t) && m_autil.is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k.is_pos()) {
|
||||
expr * arg = to_app(t)->get_arg(0);
|
||||
save_degree(arg, k);
|
||||
visit_args(arg, visited);
|
||||
}
|
||||
else {
|
||||
visit_args(t, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void display_candidates(std::ostream & out) {
|
||||
out << "candidates:\n";
|
||||
obj_map<app, rational>::iterator it = m_var2degree.begin();
|
||||
obj_map<app, rational>::iterator end = m_var2degree.end();
|
||||
for (; it != end; ++it) {
|
||||
if (!it->m_value.is_one()) {
|
||||
out << "POWER: " << it->m_value << "\n" << mk_ismt2_pp(it->m_key, m) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect(goal const & g) {
|
||||
m_var2degree.reset();
|
||||
expr_fast_mark1 visited;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
collect(g.form(i), visited);
|
||||
}
|
||||
|
||||
TRACE("degree_shift", display_candidates(tout););
|
||||
}
|
||||
|
||||
void discard_non_candidates() {
|
||||
m_pinned.reset();
|
||||
ptr_vector<app> to_delete;
|
||||
obj_map<app, rational>::iterator it = m_var2degree.begin();
|
||||
obj_map<app, rational>::iterator end = m_var2degree.end();
|
||||
for (; it != end; ++it) {
|
||||
if (it->m_value.is_one())
|
||||
to_delete.push_back(it->m_key);
|
||||
else
|
||||
m_pinned.push_back(it->m_key); // make sure it is not deleted during simplifications
|
||||
}
|
||||
ptr_vector<app>::iterator it2 = to_delete.begin();
|
||||
ptr_vector<app>::iterator end2 = to_delete.end();
|
||||
for (; it2 != end2; ++it2)
|
||||
m_var2degree.erase(*it2);
|
||||
}
|
||||
|
||||
void prepare_substitution(model_converter_ref & mc) {
|
||||
SASSERT(!m_var2degree.empty());
|
||||
filter_model_converter * fmc = 0;
|
||||
extension_model_converter * xmc = 0;
|
||||
if (m_produce_models) {
|
||||
fmc = alloc(filter_model_converter, m);
|
||||
xmc = alloc(extension_model_converter, m);
|
||||
mc = concat(fmc, xmc);
|
||||
}
|
||||
obj_map<app, rational>::iterator it = m_var2degree.begin();
|
||||
obj_map<app, rational>::iterator end = m_var2degree.end();
|
||||
for (; it != end; ++it) {
|
||||
SASSERT(it->m_value.is_int());
|
||||
SASSERT(it->m_value >= rational(2));
|
||||
app * fresh = m.mk_fresh_const(0, it->m_key->get_decl()->get_range());
|
||||
m_pinned.push_back(fresh);
|
||||
m_var2var.insert(it->m_key, fresh);
|
||||
if (m_produce_models) {
|
||||
fmc->insert(fresh->get_decl());
|
||||
xmc->insert(it->m_key->get_decl(), mk_power(fresh, rational(1)/it->m_value));
|
||||
}
|
||||
if (m_produce_proofs) {
|
||||
expr * s = mk_power(it->m_key, it->m_value);
|
||||
expr * eq = m.mk_eq(fresh, s);
|
||||
proof * pr1 = m.mk_def_intro(eq);
|
||||
proof * result_pr = m.mk_apply_def(fresh, s, pr1);
|
||||
m_pinned.push_back(result_pr);
|
||||
m_var2pr.insert(it->m_key, result_pr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
mc = 0; pc = 0; core = 0;
|
||||
m_produce_proofs = g->proofs_enabled();
|
||||
m_produce_models = g->models_enabled();
|
||||
tactic_report report("degree_shift", *g);
|
||||
collect(*g);
|
||||
discard_non_candidates();
|
||||
if (!m_var2degree.empty()) {
|
||||
prepare_substitution(mc);
|
||||
m_rw = alloc(rw, *this);
|
||||
|
||||
// substitute
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
checkpoint();
|
||||
expr * curr = g->form(idx);
|
||||
(*m_rw)(curr, new_curr, new_pr);
|
||||
if (m_produce_proofs) {
|
||||
proof * pr = g->pr(idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
g->update(idx, new_curr, new_pr, g->dep(idx));
|
||||
}
|
||||
|
||||
// add >= 0 constraints for variables with even degree
|
||||
obj_map<app, rational>::iterator it = m_var2degree.begin();
|
||||
obj_map<app, rational>::iterator end = m_var2degree.end();
|
||||
for (; it != end; ++it) {
|
||||
SASSERT(it->m_value.is_int());
|
||||
SASSERT(it->m_value >= rational(2));
|
||||
if (it->m_value.is_even()) {
|
||||
app * new_var = m_var2var.find(it->m_key);
|
||||
app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false));
|
||||
proof * new_pr = 0;
|
||||
if (m_produce_proofs) {
|
||||
proof * pr = m_var2pr.find(it->m_key);
|
||||
new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr);
|
||||
}
|
||||
g->assert_expr(new_c, new_pr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("degree_shift", g->display(tout); if (mc) mc->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
public:
|
||||
degree_shift_tactic(ast_manager & m) {
|
||||
m_imp = alloc(imp, m);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(degree_shift_tactic, m);
|
||||
}
|
||||
|
||||
virtual ~degree_shift_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(in, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = 0;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p) {
|
||||
params_ref mul2power_p;
|
||||
mul2power_p.set_bool(":mul-to-power", true);
|
||||
return and_then(using_params(mk_simplify_tactic(m), mul2power_p),
|
||||
clean(alloc(degree_shift_tactic, m)));
|
||||
}
|
||||
|
31
src/tactic/arith_tactics/degree_shift_tactic.h
Normal file
31
src/tactic/arith_tactics/degree_shift_tactic.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
degree_shift_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Simple degree shift procedure.
|
||||
Basic idea: if goal G contains a real variable x, x occurs with degrees
|
||||
d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1.
|
||||
Then, replace x^n with a new fresh variable y.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DEGREE_SHIFT_TACTIC_H_
|
||||
#define _DEGREE_SHIFT_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
429
src/tactic/arith_tactics/diff_neq_tactic.cpp
Normal file
429
src/tactic/arith_tactics/diff_neq_tactic.cpp
Normal file
|
@ -0,0 +1,429 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
diff_neq_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Solver for integer problems that contains literals of the form
|
||||
k <= x
|
||||
x <= k
|
||||
x - y != k
|
||||
And all variables are bounded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"model.h"
|
||||
|
||||
class diff_neq_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
typedef unsigned var;
|
||||
|
||||
expr_ref_vector m_var2expr;
|
||||
obj_map<expr, var> m_expr2var;
|
||||
|
||||
svector<int> m_lower;
|
||||
svector<int> m_upper;
|
||||
struct diseq {
|
||||
var m_y;
|
||||
int m_k;
|
||||
diseq(var y, int k):m_y(y), m_k(k) {}
|
||||
};
|
||||
typedef svector<diseq> diseqs;
|
||||
vector<diseqs> m_var_diseqs;
|
||||
typedef svector<int> decision_stack;
|
||||
decision_stack m_stack;
|
||||
volatile bool m_cancel;
|
||||
|
||||
bool m_produce_models;
|
||||
rational m_max_k;
|
||||
rational m_max_neg_k;
|
||||
|
||||
unsigned m_num_conflicts;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
u(m),
|
||||
m_var2expr(m) {
|
||||
updt_params(p);
|
||||
m_cancel = false;
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_max_k = rational(p.get_uint(":diff-neq-max-k", 1024));
|
||||
m_max_neg_k = -m_max_k;
|
||||
if (m_max_k >= rational(INT_MAX/2))
|
||||
m_max_k = rational(INT_MAX/2);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
void throw_not_supported() {
|
||||
throw tactic_exception("goal is not diff neq");
|
||||
}
|
||||
|
||||
unsigned num_vars() const {
|
||||
return m_upper.size();
|
||||
}
|
||||
|
||||
var mk_var(expr * t) {
|
||||
SASSERT(is_uninterp_const(t));
|
||||
var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = m_upper.size();
|
||||
m_expr2var.insert(t, x);
|
||||
m_var2expr.push_back(t);
|
||||
m_lower.push_back(INT_MIN); // unknown
|
||||
m_upper.push_back(INT_MAX); // unknown
|
||||
m_var_diseqs.push_back(diseqs());
|
||||
return x;
|
||||
}
|
||||
|
||||
void process_le(expr * lhs, expr * rhs) {
|
||||
if (!u.is_int(lhs))
|
||||
throw_not_supported();
|
||||
rational k;
|
||||
if (is_uninterp_const(lhs) && u.is_numeral(rhs, k) && m_max_neg_k <= k && k <= m_max_k) {
|
||||
var x = mk_var(lhs);
|
||||
int _k = static_cast<int>(k.get_int64());
|
||||
m_upper[x] = _k;
|
||||
|
||||
}
|
||||
else if (is_uninterp_const(rhs) && u.is_numeral(lhs, k) && m_max_neg_k <= k && k <= m_max_k) {
|
||||
var x = mk_var(rhs);
|
||||
int _k = static_cast<int>(k.get_int64());
|
||||
m_lower[x] = _k;
|
||||
}
|
||||
else {
|
||||
throw_not_supported();
|
||||
}
|
||||
}
|
||||
|
||||
// process t1 - t2 != k
|
||||
void process_neq_core(expr * t1, expr * t2, int k) {
|
||||
var x1 = mk_var(t1);
|
||||
var x2 = mk_var(t2);
|
||||
if (x1 == x2)
|
||||
throw_not_supported(); // must simplify first
|
||||
if (x1 < x2) {
|
||||
std::swap(x1, x2);
|
||||
k = -k;
|
||||
}
|
||||
m_var_diseqs[x1].push_back(diseq(x2, k));
|
||||
}
|
||||
|
||||
void process_neq(expr * lhs, expr * rhs) {
|
||||
if (!u.is_int(lhs))
|
||||
throw_not_supported();
|
||||
if (is_uninterp_const(lhs) && is_uninterp_const(rhs)) {
|
||||
process_neq_core(lhs, rhs, 0);
|
||||
return;
|
||||
}
|
||||
if (u.is_numeral(lhs))
|
||||
std::swap(lhs, rhs);
|
||||
rational k;
|
||||
if (!u.is_numeral(rhs, k))
|
||||
throw_not_supported();
|
||||
if (!(m_max_neg_k <= k && k <= m_max_k))
|
||||
throw_not_supported();
|
||||
int _k = static_cast<int>(k.get_int64());
|
||||
expr * t1, * t2, * mt1, * mt2;
|
||||
if (u.is_add(lhs, t1, t2)) {
|
||||
if (is_uninterp_const(t1) && u.is_times_minus_one(t2, mt2) && is_uninterp_const(mt2))
|
||||
process_neq_core(t1, mt2, _k);
|
||||
else if (is_uninterp_const(t2) && u.is_times_minus_one(t1, mt1) && is_uninterp_const(mt1))
|
||||
process_neq_core(t2, mt1, _k);
|
||||
else
|
||||
throw_not_supported();
|
||||
}
|
||||
else {
|
||||
throw_not_supported();
|
||||
}
|
||||
}
|
||||
|
||||
// throws exception if contains unbounded variable
|
||||
void check_unbounded() {
|
||||
unsigned num = num_vars();
|
||||
for (var x = 0; x < num; x++) {
|
||||
if (m_lower[x] == INT_MIN || m_upper[x] == INT_MAX)
|
||||
throw_not_supported();
|
||||
// possible extension: support bound normalization here
|
||||
if (m_lower[x] != 0)
|
||||
throw_not_supported(); // use bound normalizer
|
||||
}
|
||||
}
|
||||
|
||||
void compile(goal const & g) {
|
||||
expr * lhs;
|
||||
expr * rhs;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g.form(i);
|
||||
TRACE("diff_neq_tactic", tout << "processing: " << mk_ismt2_pp(f, m) << "\n";);
|
||||
if (u.is_le(f, lhs, rhs))
|
||||
process_le(lhs, rhs);
|
||||
else if (u.is_ge(f, lhs, rhs))
|
||||
process_le(rhs, lhs);
|
||||
else if (m.is_not(f, f) && m.is_eq(f, lhs, rhs))
|
||||
process_neq(lhs, rhs);
|
||||
else
|
||||
throw_not_supported();
|
||||
}
|
||||
check_unbounded();
|
||||
}
|
||||
|
||||
void display(std::ostream & out) {
|
||||
unsigned num = num_vars();
|
||||
for (var x = 0; x < num; x++) {
|
||||
out << m_lower[x] << " <= " << mk_ismt2_pp(m_var2expr.get(x), m) << " <= " << m_upper[x] << "\n";
|
||||
}
|
||||
for (var x = 0; x < num; x++) {
|
||||
diseqs::iterator it = m_var_diseqs[x].begin();
|
||||
diseqs::iterator end = m_var_diseqs[x].end();
|
||||
for (; it != end; ++it) {
|
||||
out << mk_ismt2_pp(m_var2expr.get(x), m) << " != " << mk_ismt2_pp(m_var2expr.get(it->m_y), m) << " + " << it->m_k << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void display_model(std::ostream & out) {
|
||||
unsigned num = m_stack.size();
|
||||
for (var x = 0; x < num; x++) {
|
||||
out << mk_ismt2_pp(m_var2expr.get(x), m) << " := " << m_stack[x] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
svector<bool> m_forbidden;
|
||||
|
||||
// make sure m_forbidden.size() > max upper bound
|
||||
void init_forbidden() {
|
||||
int max = 0;
|
||||
unsigned num = num_vars();
|
||||
for (var x = 0; x < num; x++) {
|
||||
if (m_upper[x] > max)
|
||||
max = m_upper[x];
|
||||
}
|
||||
m_forbidden.reset();
|
||||
m_forbidden.resize(max+1, false);
|
||||
}
|
||||
|
||||
// Return a value v s.t. v >= starting_at and v <= m_upper[x] and all diseqs in m_var_diseqs[x] are satisfied.
|
||||
// Return -1 if such value does not exist.
|
||||
int choose_value(var x, int starting_at) {
|
||||
int max = starting_at-1;
|
||||
int v = starting_at;
|
||||
int upper = m_upper[x];
|
||||
if (starting_at > upper)
|
||||
return -1;
|
||||
diseqs const & ds = m_var_diseqs[x];
|
||||
diseqs::const_iterator it = ds.begin();
|
||||
diseqs::const_iterator end = ds.end();
|
||||
for (; it != end; ++it) {
|
||||
int bad_v = m_stack[it->m_y] + it->m_k;
|
||||
if (bad_v < v)
|
||||
continue;
|
||||
if (bad_v > upper)
|
||||
continue;
|
||||
if (bad_v == v) {
|
||||
while (true) {
|
||||
v++;
|
||||
if (v > upper)
|
||||
return -1;
|
||||
if (!m_forbidden[v])
|
||||
break;
|
||||
m_forbidden[v] = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
SASSERT(bad_v > v && bad_v <= upper);
|
||||
m_forbidden[bad_v] = true;
|
||||
if (bad_v > max)
|
||||
max = bad_v;
|
||||
}
|
||||
// reset forbidden
|
||||
for (int i = starting_at + 1; i <= max; i++)
|
||||
m_forbidden[i] = false;
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 0; i < m_forbidden.size(); i++) {
|
||||
SASSERT(!m_forbidden[i]);
|
||||
}
|
||||
});
|
||||
return v;
|
||||
}
|
||||
|
||||
bool extend_model(var x) {
|
||||
int v = choose_value(x, 0);
|
||||
if (v == -1)
|
||||
return false;
|
||||
m_stack.push_back(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resolve_conflict() {
|
||||
m_num_conflicts++;
|
||||
while (!m_stack.empty()) {
|
||||
int v = m_stack.back();
|
||||
m_stack.pop_back();
|
||||
var x = m_stack.size();
|
||||
v = choose_value(x, v+1);
|
||||
if (v != -1) {
|
||||
m_stack.push_back(v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool search() {
|
||||
m_num_conflicts = 0;
|
||||
init_forbidden();
|
||||
unsigned nvars = num_vars();
|
||||
while (m_stack.size() < nvars) {
|
||||
if (m_cancel)
|
||||
throw tactic_exception(TACTIC_CANCELED_MSG);
|
||||
TRACE("diff_neq_tactic", display_model(tout););
|
||||
var x = m_stack.size();
|
||||
if (extend_model(x))
|
||||
continue;
|
||||
if (!resolve_conflict())
|
||||
return false;
|
||||
}
|
||||
TRACE("diff_neq_tactic", display_model(tout););
|
||||
return true;
|
||||
}
|
||||
|
||||
model * mk_model() {
|
||||
model * md = alloc(model, m);
|
||||
unsigned num = num_vars();
|
||||
SASSERT(m_stack.size() == num);
|
||||
for (var x = 0; x < num; x++) {
|
||||
func_decl * d = to_app(m_var2expr.get(x))->get_decl();
|
||||
md->register_decl(d, u.mk_numeral(rational(m_stack[x]), true));
|
||||
}
|
||||
return md;
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
m_produce_models = g->models_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("diff-neq", *g);
|
||||
fail_if_proof_generation("diff-neq", g);
|
||||
fail_if_unsat_core_generation("diff-neq", g);
|
||||
if (g->inconsistent()) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
compile(*g);
|
||||
TRACE("diff_neq_tactic", g->display(tout); display(tout););
|
||||
bool r = search();
|
||||
report_tactic_progress(":conflicts", m_num_conflicts);
|
||||
if (r) {
|
||||
if (m_produce_models)
|
||||
mc = model2model_converter(mk_model());
|
||||
g->reset();
|
||||
}
|
||||
else {
|
||||
g->assert_expr(m.mk_false());
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("diff_neq", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
diff_neq_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(diff_neq_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~diff_neq_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":diff-neq-max-k", CPK_UINT, "(default: 1024) maximum variable upper bound for diff neq solver.");
|
||||
}
|
||||
|
||||
virtual void collect_statistics(statistics & st) const {
|
||||
st.update("conflicts", m_imp->m_num_conflicts);
|
||||
}
|
||||
|
||||
virtual void reset_statistics() {
|
||||
m_imp->m_num_conflicts = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Fix a DL variable in s to 0.
|
||||
If s is not really in the difference logic fragment, then this is a NOOP.
|
||||
*/
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(in, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
unsigned num_conflicts = m_imp->m_num_conflicts;
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
m_imp->m_num_conflicts = num_conflicts;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(diff_neq_tactic, m, p));
|
||||
}
|
||||
|
||||
|
||||
|
32
src/tactic/arith_tactics/diff_neq_tactic.h
Normal file
32
src/tactic/arith_tactics/diff_neq_tactic.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
diff_neq_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Solver for integer problems that contains literals of the form
|
||||
k <= x
|
||||
x <= k
|
||||
x - y != k
|
||||
And all variables are bounded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DIFF_NEQ_TACTIC_H_
|
||||
#define _DIFF_NEQ_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
174
src/tactic/arith_tactics/elim_term_ite_strategy.cpp
Normal file
174
src/tactic/arith_tactics/elim_term_ite_strategy.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
53
src/tactic/arith_tactics/elim_term_ite_strategy.h
Normal file
53
src/tactic/arith_tactics/elim_term_ite_strategy.h
Normal 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
|
361
src/tactic/arith_tactics/factor_tactic.cpp
Normal file
361
src/tactic/arith_tactics/factor_tactic.cpp
Normal file
|
@ -0,0 +1,361 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
factor_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Polynomial factorization tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-03
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"expr2polynomial.h"
|
||||
#include"rewriter_def.h"
|
||||
|
||||
class factor_tactic : public tactic {
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
ast_manager & m;
|
||||
arith_util m_util;
|
||||
unsynch_mpq_manager m_qm;
|
||||
polynomial::manager m_pm;
|
||||
default_expr2polynomial m_expr2poly;
|
||||
polynomial::factor_params m_fparams;
|
||||
bool m_split_factors;
|
||||
|
||||
rw_cfg(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_util(_m),
|
||||
m_pm(m_qm),
|
||||
m_expr2poly(m, m_pm) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_split_factors = p.get_bool(":split-factors", true);
|
||||
m_fparams.updt_params(p);
|
||||
}
|
||||
|
||||
expr * mk_mul(unsigned sz, expr * const * args) {
|
||||
SASSERT(sz > 0);
|
||||
if (sz == 1)
|
||||
return args[0];
|
||||
return m_util.mk_mul(sz, args);
|
||||
}
|
||||
|
||||
expr * mk_zero_for(expr * arg) {
|
||||
return m_util.mk_numeral(rational(0), m_util.is_int(arg));
|
||||
}
|
||||
|
||||
// p1^k1 * p2^k2 = 0 --> p1*p2 = 0
|
||||
void mk_eq(polynomial::factors const & fs, expr_ref & result) {
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
args.push_back(arg);
|
||||
}
|
||||
result = m.mk_eq(mk_mul(args.size(), args.c_ptr()), mk_zero_for(arg));
|
||||
}
|
||||
|
||||
// p1^k1 * p2^k2 = 0 --> p1 = 0 or p2 = 0
|
||||
void mk_split_eq(polynomial::factors const & fs, expr_ref & result) {
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
args.push_back(m.mk_eq(arg, mk_zero_for(arg)));
|
||||
}
|
||||
if (args.size() == 1)
|
||||
result = args[0];
|
||||
else
|
||||
result = m.mk_or(args.size(), args.c_ptr());
|
||||
}
|
||||
|
||||
decl_kind flip(decl_kind k) {
|
||||
SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE);
|
||||
switch (k) {
|
||||
case OP_LT: return OP_GT;
|
||||
case OP_LE: return OP_GE;
|
||||
case OP_GT: return OP_LT;
|
||||
case OP_GE: return OP_LE;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
// p1^{2*k1} * p2^{2*k2 + 1} >=< 0
|
||||
// -->
|
||||
// (p1^2)*p2 >=<0
|
||||
void mk_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) {
|
||||
SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE);
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
if (fs.get_degree(i) % 2 == 0)
|
||||
arg = m_util.mk_power(arg, m_util.mk_numeral(rational(2), m_util.is_int(arg)));
|
||||
args.push_back(arg);
|
||||
}
|
||||
expr * lhs = mk_mul(args.size(), args.c_ptr());
|
||||
result = m.mk_app(m_util.get_family_id(), k, lhs, mk_zero_for(lhs));
|
||||
}
|
||||
|
||||
// See mk_split_strict_comp and mk_split_nonstrict_comp
|
||||
void split_even_odd(bool strict, polynomial::factors const & fs, expr_ref_buffer & even_eqs, expr_ref_buffer & odd_factors) {
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < fs.distinct_factors(); i++) {
|
||||
m_expr2poly.to_expr(fs[i], true, arg);
|
||||
if (fs.get_degree(i) % 2 == 0) {
|
||||
expr * eq = m.mk_eq(arg, mk_zero_for(arg));
|
||||
if (strict)
|
||||
even_eqs.push_back(m.mk_not(eq));
|
||||
else
|
||||
even_eqs.push_back(eq);
|
||||
}
|
||||
else {
|
||||
odd_factors.push_back(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strict case
|
||||
// p1^{2*k1} * p2^{2*k2 + 1} >< 0
|
||||
// -->
|
||||
// p1 != 0 and p2 >< 0
|
||||
//
|
||||
// Nonstrict
|
||||
// p1^{2*k1} * p2^{2*k2 + 1} >=< 0
|
||||
// -->
|
||||
// p1 = 0 or p2 >=< 0
|
||||
//
|
||||
void mk_split_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) {
|
||||
SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE);
|
||||
bool strict = (k == OP_LT) || (k == OP_GT);
|
||||
expr_ref_buffer args(m);
|
||||
expr_ref_buffer odd_factors(m);
|
||||
split_even_odd(strict, fs, args, odd_factors);
|
||||
if (odd_factors.empty()) {
|
||||
if (k == OP_LT) {
|
||||
result = m.mk_false();
|
||||
return;
|
||||
}
|
||||
if (k == OP_GE) {
|
||||
result = m.mk_true();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
args.push_back(m.mk_app(m_util.get_family_id(), k, mk_mul(odd_factors.size(), odd_factors.c_ptr()), mk_zero_for(odd_factors[0])));
|
||||
}
|
||||
SASSERT(!args.empty());
|
||||
if (args.size() == 1)
|
||||
result = args[0];
|
||||
else if (strict)
|
||||
result = m.mk_and(args.size(), args.c_ptr());
|
||||
else
|
||||
result = m.mk_or(args.size(), args.c_ptr());
|
||||
}
|
||||
|
||||
br_status factor(func_decl * f, expr * lhs, expr * rhs, expr_ref & result) {
|
||||
polynomial_ref p1(m_pm);
|
||||
polynomial_ref p2(m_pm);
|
||||
scoped_mpz d1(m_qm);
|
||||
scoped_mpz d2(m_qm);
|
||||
m_expr2poly.to_polynomial(lhs, p1, d1);
|
||||
m_expr2poly.to_polynomial(rhs, p2, d2);
|
||||
TRACE("factor_tactic_bug",
|
||||
tout << "lhs: " << mk_ismt2_pp(lhs, m) << "\n";
|
||||
tout << "p1: " << p1 << "\n";
|
||||
tout << "d1: " << d1 << "\n";
|
||||
tout << "rhs: " << mk_ismt2_pp(rhs, m) << "\n";
|
||||
tout << "p2: " << p2 << "\n";
|
||||
tout << "d2: " << d2 << "\n";);
|
||||
scoped_mpz lcm(m_qm);
|
||||
m_qm.lcm(d1, d2, lcm);
|
||||
m_qm.div(lcm, d1, d1);
|
||||
m_qm.div(lcm, d2, d2);
|
||||
m_qm.neg(d2);
|
||||
polynomial_ref p(m_pm);
|
||||
p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2);
|
||||
if (is_const(p))
|
||||
return BR_FAILED;
|
||||
polynomial::factors fs(m_pm);
|
||||
TRACE("factor_tactic_bug", tout << "p: " << p << "\n";);
|
||||
m_pm.factor(p, fs, m_fparams);
|
||||
SASSERT(fs.distinct_factors() > 0);
|
||||
TRACE("factor_tactic_bug", tout << "factors:\n"; fs.display(tout); tout << "\n";);
|
||||
if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1)
|
||||
return BR_FAILED;
|
||||
if (m.is_eq(f)) {
|
||||
if (m_split_factors)
|
||||
mk_split_eq(fs, result);
|
||||
else
|
||||
mk_eq(fs, result);
|
||||
}
|
||||
else {
|
||||
decl_kind k = f->get_decl_kind();
|
||||
if (m_qm.is_neg(fs.get_constant()))
|
||||
k = flip(k);
|
||||
|
||||
if (m_split_factors)
|
||||
mk_split_comp(k, fs, result);
|
||||
else
|
||||
mk_comp(k, fs, result);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
if (num != 2)
|
||||
return BR_FAILED;
|
||||
if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1])))
|
||||
return factor(f, args[0], args[1], result);
|
||||
if (f->get_family_id() != m_util.get_family_id())
|
||||
return BR_FAILED;
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_LT:
|
||||
case OP_GT:
|
||||
case OP_LE:
|
||||
case OP_GE:
|
||||
return factor(f, args[0], args[1], result);
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
};
|
||||
|
||||
struct rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
|
||||
rw(ast_manager & m, params_ref const & p):
|
||||
rewriter_tpl<rw_cfg>(m, m.proofs_enabled(), m_cfg),
|
||||
m_cfg(m, p) {
|
||||
}
|
||||
};
|
||||
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
rw m_rw;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_rw(m, p) {
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
m_rw.cfg().m_pm.set_cancel(f);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.cfg().updt_params(p);
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
mc = 0; pc = 0; core = 0;
|
||||
tactic_report report("factor", *g);
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = g->form(idx);
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (produce_proofs) {
|
||||
proof * pr = g->pr(idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
g->update(idx, new_curr, new_pr, g->dep(idx));
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("factor", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
factor_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(factor_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~factor_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->m_rw.cfg().updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":split-factors", CPK_BOOL,
|
||||
"(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0)).");
|
||||
polynomial::factor_params::get_param_descrs(r);
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
try {
|
||||
(*m_imp)(in, result, mc, pc, core);
|
||||
}
|
||||
catch (z3_error & ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (z3_exception & ex) {
|
||||
throw tactic_exception(ex.msg());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = 0;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_factor_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(factor_tactic, m, p));
|
||||
}
|
||||
|
||||
|
||||
|
28
src/tactic/arith_tactics/factor_tactic.h
Normal file
28
src/tactic/arith_tactics/factor_tactic.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
factor_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Polynomial factorization tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-03
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _FACTOR_TACTIC_H_
|
||||
#define _FACTOR_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_factor_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
358
src/tactic/arith_tactics/fix_dl_var_tactic.cpp
Normal file
358
src/tactic/arith_tactics/fix_dl_var_tactic.cpp
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
fix_dl_var_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Fix a difference logic variable to 0.
|
||||
If the problem is in the difference logic fragment, that is, all arithmetic terms
|
||||
are of the form (x + k), and the arithmetic atoms are of the
|
||||
form x - y <= k or x - y = k. Then, we can set one variable to 0.
|
||||
|
||||
This is useful because, many bounds can be exposed after this operation is performed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-19
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class fix_dl_var_tactic : public tactic {
|
||||
|
||||
struct is_target {
|
||||
struct failed {};
|
||||
ast_manager & m;
|
||||
arith_util & m_util;
|
||||
expr_fast_mark1 * m_visited;
|
||||
ptr_vector<expr> m_todo;
|
||||
obj_map<app, unsigned> m_occs;
|
||||
obj_map<app, unsigned> m_non_nested_occs;
|
||||
|
||||
is_target(arith_util & u):
|
||||
m(u.get_manager()),
|
||||
m_util(u) {
|
||||
}
|
||||
|
||||
void throw_failed(expr * ctx1, expr * ctx2 = 0) {
|
||||
TRACE("fix_dl_var", tout << mk_ismt2_pp(ctx1, m) << "\n"; if (ctx2) tout << mk_ismt2_pp(ctx2, m) << "\n";);
|
||||
throw failed();
|
||||
}
|
||||
|
||||
bool is_arith(expr * n) {
|
||||
sort * s = m.get_sort(n);
|
||||
return s->get_family_id() == m_util.get_family_id();
|
||||
}
|
||||
// Return true if n is uninterpreted with respect to arithmetic.
|
||||
bool is_uninterp(expr * n) {
|
||||
return is_app(n) && to_app(n)->get_family_id() != m_util.get_family_id();
|
||||
}
|
||||
|
||||
// Remark: we say an expression is nested, if it occurs inside the boolean structure of the formula.
|
||||
// That is, the expression is not part of an unit clause comprising of a single inequality/equality.
|
||||
|
||||
void inc_occ(expr * n, bool nested) {
|
||||
if (is_uninterp_const(n) && is_arith(n)) {
|
||||
obj_map<app, unsigned>::obj_map_entry * entry = m_occs.insert_if_not_there2(to_app(n), 0);
|
||||
entry->get_data().m_value++;
|
||||
|
||||
if (!nested) {
|
||||
entry = m_non_nested_occs.insert_if_not_there2(to_app(n), 0);
|
||||
entry->get_data().m_value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(expr * n, bool nested) {
|
||||
inc_occ(n, nested);
|
||||
if (!m_visited->is_marked(n)) {
|
||||
m_visited->mark(n);
|
||||
m_todo.push_back(n);
|
||||
}
|
||||
}
|
||||
|
||||
void process_app(app * t) {
|
||||
unsigned num = t->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
visit(t->get_arg(i), false);
|
||||
}
|
||||
|
||||
void process_arith_atom(expr * lhs, expr * rhs, bool nested) {
|
||||
if (is_uninterp(lhs) && is_uninterp(rhs)) {
|
||||
visit(lhs, nested);
|
||||
visit(rhs, nested);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_util.is_numeral(lhs))
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
if (!m_util.is_numeral(rhs))
|
||||
throw_failed(lhs, rhs);
|
||||
|
||||
expr * t, * ms, * s;
|
||||
// check if lhs is of the form: (+ t (* (- 1) s))
|
||||
if (m_util.is_add(lhs, t, ms) && m_util.is_times_minus_one(ms, s) && is_uninterp(t) && is_uninterp(s)) {
|
||||
visit(t, nested);
|
||||
visit(s, nested);
|
||||
}
|
||||
else {
|
||||
CTRACE("fix_dl_var", m_util.is_add(lhs, t, ms),
|
||||
s = 0;
|
||||
tout << "is_times_minus_one: " << m_util.is_times_minus_one(ms, s) << "\n";
|
||||
tout << "is_uninterp(t): " << is_uninterp(t) << "\n";
|
||||
tout << "t.family_id(): " << (is_app(t) ? to_app(t)->get_family_id() : -1) << "\n";
|
||||
tout << "util.family_id: " << m_util.get_family_id() << "\n";
|
||||
if (s) {
|
||||
tout << "is_uninterp(s): " << is_uninterp(s) << "\n";
|
||||
tout << "s.family_id(): " << (is_app(s) ? to_app(s)->get_family_id() : -1) << "\n";
|
||||
});
|
||||
throw_failed(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void process_eq(app * t, bool nested) {
|
||||
if (!is_arith(t->get_arg(0))) {
|
||||
process_app(t);
|
||||
return;
|
||||
}
|
||||
process_arith_atom(t->get_arg(0), t->get_arg(1), nested);
|
||||
}
|
||||
|
||||
void process_arith(app * t, bool nested) {
|
||||
if (m.is_bool(t)) {
|
||||
process_arith_atom(t->get_arg(0), t->get_arg(1), nested);
|
||||
return;
|
||||
}
|
||||
// check if t is of the form c + k
|
||||
expr * c, * k;
|
||||
if (m_util.is_add(t, k, c) && is_uninterp(c) && m_util.is_numeral(k)) {
|
||||
visit(c, nested);
|
||||
}
|
||||
else {
|
||||
throw_failed(t);
|
||||
}
|
||||
}
|
||||
|
||||
void process(expr * n) {
|
||||
if (m_visited->is_marked(n))
|
||||
return;
|
||||
|
||||
while (m.is_not(n, n))
|
||||
;
|
||||
|
||||
if (is_app(n) && to_app(n)->get_family_id() == m_util.get_family_id()) {
|
||||
process_arith(to_app(n), false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_todo.push_back(n);
|
||||
m_visited->mark(n);
|
||||
|
||||
while (!m_todo.empty()) {
|
||||
expr * n = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
|
||||
if (!is_app(n))
|
||||
throw_failed(n);
|
||||
|
||||
app * t = to_app(n);
|
||||
|
||||
if (m.is_eq(t))
|
||||
process_eq(t, true);
|
||||
else if (t->get_family_id() == m_util.get_family_id())
|
||||
process_arith(t, true);
|
||||
else
|
||||
process_app(t);
|
||||
}
|
||||
}
|
||||
|
||||
app * most_occs(obj_map<app, unsigned> & occs, unsigned & best) {
|
||||
app * r = 0;
|
||||
best = 0;
|
||||
obj_map<app, unsigned>::iterator it = occs.begin();
|
||||
obj_map<app, unsigned>::iterator end = occs.end();
|
||||
for (; it != end; ++it) {
|
||||
if (it->m_value > best) {
|
||||
best = it->m_value;
|
||||
r = it->m_key;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// TODO make it a parameter
|
||||
#define NESTED_PENALTY 10
|
||||
|
||||
app * most_occs() {
|
||||
// We try to choose a variable that when set to 0 will generate many bounded variables.
|
||||
// That is why we give preference to variables occuring in non-nested inequalities.
|
||||
unsigned best1, best2;
|
||||
app * r1, * r2;
|
||||
r1 = most_occs(m_non_nested_occs, best1);
|
||||
r2 = most_occs(m_occs, best2);
|
||||
TRACE("fix_dl_var_choice",
|
||||
if (r1) {
|
||||
tout << "r1 occs: " << best1 << "\n";
|
||||
tout << mk_ismt2_pp(r1, m) << "\n";
|
||||
}
|
||||
if (r2) {
|
||||
tout << "r2 occs: " << best2 << "\n";
|
||||
tout << mk_ismt2_pp(r2, m) << "\n";
|
||||
});
|
||||
if (best2 > NESTED_PENALTY * best1)
|
||||
return r2;
|
||||
else
|
||||
return r1;
|
||||
}
|
||||
|
||||
app * operator()(goal const & g) {
|
||||
try {
|
||||
expr_fast_mark1 visited;
|
||||
m_visited = &visited;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
process(g.form(i));
|
||||
}
|
||||
return most_occs();
|
||||
}
|
||||
catch (failed) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
th_rewriter m_rw;
|
||||
bool m_produce_models;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
u(m),
|
||||
m_rw(m, p) {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
mc = 0; pc = 0; core = 0;
|
||||
tactic_report report("fix-dl-var", *g);
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
m_produce_models = g->models_enabled();
|
||||
|
||||
app * var = is_target(u)(*g);
|
||||
if (var != 0) {
|
||||
IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(fixing-at-zero " << var->get_decl()->get_name() << ")\n";);
|
||||
tactic_report report("fix-dl-var", *g);
|
||||
|
||||
expr_substitution subst(m);
|
||||
app * zero = u.mk_numeral(rational(0), u.is_int(var));
|
||||
subst.insert(var, zero);
|
||||
m_rw.set_substitution(&subst);
|
||||
|
||||
if (m_produce_models) {
|
||||
extension_model_converter * _mc = alloc(extension_model_converter, m);
|
||||
_mc->insert(var->get_decl(), zero);
|
||||
mc = _mc;
|
||||
}
|
||||
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = g->form(idx);
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (produce_proofs) {
|
||||
proof * pr = g->pr(idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
g->update(idx, new_curr, new_pr, g->dep(idx));
|
||||
}
|
||||
g->inc_depth();
|
||||
}
|
||||
result.push_back(g.get());
|
||||
TRACE("fix_dl_var", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
fix_dl_var_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(fix_dl_var_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~fix_dl_var_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
th_rewriter::get_param_descrs(r);
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(in, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = 0;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(fix_dl_var_tactic, m, p));
|
||||
}
|
33
src/tactic/arith_tactics/fix_dl_var_tactic.h
Normal file
33
src/tactic/arith_tactics/fix_dl_var_tactic.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
fix_dl_var_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Fix a difference logic variable to 0.
|
||||
If the problem is in the difference logic fragment, that is, all arithmetic terms
|
||||
are of the form (x + k), and the arithmetic atoms are of the
|
||||
form x - y <= k or x - y = k. Then, we can set one variable to 0.
|
||||
|
||||
This is useful because, many bounds can be exposed after this operation is performed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-12-29
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _FIX_DL_VAR_TACTIC_H_
|
||||
#define _FIX_DL_VAR_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
1715
src/tactic/arith_tactics/fm_tactic.cpp
Normal file
1715
src/tactic/arith_tactics/fm_tactic.cpp
Normal file
File diff suppressed because it is too large
Load diff
33
src/tactic/arith_tactics/fm_tactic.h
Normal file
33
src/tactic/arith_tactics/fm_tactic.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
fm_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Use Fourier-Motzkin to eliminate variables.
|
||||
This strategy can handle conditional bounds
|
||||
(i.e., clauses with at most one constraint).
|
||||
|
||||
The strategy mk_occf can be used to put the
|
||||
formula in OCC form.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-04.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _FM_TACTIC_H_
|
||||
#define _FM_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_fm_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
366
src/tactic/arith_tactics/lia2pb_tactic.cpp
Normal file
366
src/tactic/arith_tactics/lia2pb_tactic.cpp
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
lia2pb_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Reduce bounded LIA benchmark into 0-1 LIA benchmark.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"bound_manager.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"for_each_expr.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class lia2pb_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
bound_manager m_bm;
|
||||
arith_util m_util;
|
||||
expr_dependency_ref_vector m_new_deps;
|
||||
th_rewriter m_rw;
|
||||
bool m_produce_models;
|
||||
bool m_produce_unsat_cores;
|
||||
bool m_partial_lia2pb;
|
||||
unsigned m_max_bits;
|
||||
unsigned m_total_bits;
|
||||
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_bm(m),
|
||||
m_util(m),
|
||||
m_new_deps(m),
|
||||
m_rw(m, p) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
m_partial_lia2pb = p.get_bool(":lia2pb-partial", false);
|
||||
m_max_bits = p.get_uint(":lia2pb-max-bits", 32);
|
||||
m_total_bits = p.get_uint(":lia2pb-total-bits", 2048);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
bool is_target_core(expr * n, rational & u) {
|
||||
if (!is_uninterp_const(n))
|
||||
return false;
|
||||
rational l; bool s;
|
||||
if (m_bm.has_lower(n, l, s) &&
|
||||
m_bm.has_upper(n, u, s) &&
|
||||
l.is_zero() &&
|
||||
u.get_num_bits() <= m_max_bits) {
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_bounded(expr * n) {
|
||||
rational u;
|
||||
return is_target_core(n, u);
|
||||
}
|
||||
|
||||
bool is_target(expr * n) {
|
||||
rational u;
|
||||
return is_target_core(n, u) && u > rational(1);
|
||||
}
|
||||
|
||||
struct failed {};
|
||||
|
||||
struct visitor {
|
||||
imp & m_owner;
|
||||
|
||||
visitor(imp & o):m_owner(o) {}
|
||||
|
||||
void throw_failed(expr * n) {
|
||||
TRACE("lia2pb", tout << "Failed at:\n" << mk_ismt2_pp(n, m_owner.m) << "\n";);
|
||||
throw failed();
|
||||
}
|
||||
|
||||
void operator()(var * n) {
|
||||
throw_failed(n);
|
||||
}
|
||||
|
||||
void operator()(app * n) {
|
||||
family_id fid = n->get_family_id();
|
||||
if (fid == m_owner.m.get_basic_family_id()) {
|
||||
// all basic family ops are OK
|
||||
}
|
||||
else if (fid == m_owner.m_util.get_family_id()) {
|
||||
// check if linear
|
||||
switch (n->get_decl_kind()) {
|
||||
case OP_LE: case OP_GE: case OP_LT: case OP_GT:
|
||||
case OP_ADD: case OP_NUM:
|
||||
return;
|
||||
case OP_MUL:
|
||||
if (n->get_num_args() != 2)
|
||||
throw_failed(n);
|
||||
if (!m_owner.m_util.is_numeral(n->get_arg(0)))
|
||||
throw_failed(n);
|
||||
return;
|
||||
default:
|
||||
throw_failed(n);
|
||||
}
|
||||
}
|
||||
else if (is_uninterp_const(n)) {
|
||||
if (m_owner.m_util.is_real(n)) {
|
||||
if (!m_owner.m_partial_lia2pb)
|
||||
throw_failed(n);
|
||||
}
|
||||
else if (m_owner.m_util.is_int(n)) {
|
||||
if (!m_owner.m_partial_lia2pb && !m_owner.is_bounded(n))
|
||||
throw_failed(n);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sort * s = m_owner.m.get_sort(n);
|
||||
if (s->get_family_id() == m_owner.m_util.get_family_id())
|
||||
throw_failed(n);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(quantifier * n) {
|
||||
throw_failed(n);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool check(goal const & g) {
|
||||
try {
|
||||
expr_fast_mark1 visited;
|
||||
visitor proc(*this);
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g.form(i);
|
||||
for_each_expr_core<visitor, expr_fast_mark1, true, true>(proc, visited, f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (failed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_target() {
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
if (is_target(*it))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_num_bits() {
|
||||
unsigned num_bits = 0;
|
||||
rational u;
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
expr * x = *it;
|
||||
if (is_target_core(x, u) && u > rational(1)) {
|
||||
num_bits += u.get_num_bits();
|
||||
if (num_bits > m_total_bits)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
fail_if_proof_generation("lia2pb", g);
|
||||
m_produce_models = g->models_enabled();
|
||||
m_produce_unsat_cores = g->unsat_core_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("lia2pb", *g);
|
||||
m_bm.reset(); m_rw.reset(); m_new_deps.reset();
|
||||
|
||||
if (g->inconsistent()) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
m_bm(*g);
|
||||
|
||||
TRACE("lia2pb", m_bm.display(tout););
|
||||
|
||||
// check if there is some variable to be converted
|
||||
if (!has_target()) {
|
||||
// nothing to be done
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!check(*g))
|
||||
throw tactic_exception("goal is in a fragment unsupported by lia2pb");
|
||||
|
||||
if (!check_num_bits())
|
||||
throw tactic_exception("lia2pb failed, number of necessary bits exceeds specified threshold (use option :lia2pb-total-bits to increase threshold)");
|
||||
|
||||
extension_model_converter * mc1 = 0;
|
||||
filter_model_converter * mc2 = 0;
|
||||
if (m_produce_models) {
|
||||
mc1 = alloc(extension_model_converter, m);
|
||||
mc2 = alloc(filter_model_converter, m);
|
||||
mc = concat(mc2, mc1);
|
||||
}
|
||||
|
||||
expr_ref zero(m);
|
||||
expr_ref one(m);
|
||||
zero = m_util.mk_numeral(rational(0), true);
|
||||
one = m_util.mk_numeral(rational(1), true);
|
||||
|
||||
unsigned num_converted = 0;
|
||||
expr_substitution subst(m, m_produce_unsat_cores, false);
|
||||
rational u;
|
||||
ptr_buffer<expr> def_args;
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
expr * x = *it;
|
||||
if (is_target_core(x, u) && u > rational(1)) {
|
||||
num_converted++;
|
||||
def_args.reset();
|
||||
rational a(1);
|
||||
unsigned num_bits = u.get_num_bits();
|
||||
for (unsigned i = 0; i < num_bits; i++) {
|
||||
app * x_prime = m.mk_fresh_const(0, m_util.mk_int());
|
||||
g->assert_expr(m_util.mk_le(zero, x_prime));
|
||||
g->assert_expr(m_util.mk_le(x_prime, one));
|
||||
if (a.is_one())
|
||||
def_args.push_back(x_prime);
|
||||
else
|
||||
def_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), x_prime));
|
||||
if (m_produce_models)
|
||||
mc2->insert(x_prime->get_decl());
|
||||
a *= rational(2);
|
||||
}
|
||||
SASSERT(def_args.size() > 1);
|
||||
expr * def = m_util.mk_add(def_args.size(), def_args.c_ptr());
|
||||
expr_dependency * dep = 0;
|
||||
if (m_produce_unsat_cores) {
|
||||
dep = m.mk_join(m_bm.lower_dep(x), m_bm.upper_dep(x));
|
||||
if (dep != 0)
|
||||
m_new_deps.push_back(dep);
|
||||
}
|
||||
TRACE("lia2pb", tout << mk_ismt2_pp(x, m) << " -> " << dep << "\n";);
|
||||
subst.insert(x, def, 0, dep);
|
||||
if (m_produce_models)
|
||||
mc1->insert(to_app(x)->get_decl(), def);
|
||||
}
|
||||
}
|
||||
|
||||
report_tactic_progress(":converted-lia2pb", num_converted);
|
||||
|
||||
m_rw.set_substitution(&subst);
|
||||
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = g->form(idx);
|
||||
expr_dependency * dep = 0;
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (m_produce_unsat_cores) {
|
||||
dep = m.mk_join(m_rw.get_used_dependencies(), g->dep(idx));
|
||||
m_rw.reset_used_dependencies();
|
||||
}
|
||||
g->update(idx, new_curr, 0, dep);
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("lia2pb", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
lia2pb_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(lia2pb_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~lia2pb_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":lia2pb-partial", CPK_BOOL, "(default: false) partial lia2pb conversion.");
|
||||
r.insert(":lia2pb-max-bits", CPK_UINT, "(default: 32) maximum number of bits to be used (per variable) in lia2pb.");
|
||||
r.insert(":lia2pb-total-bits", CPK_UINT, "(default: 2048) total number of bits to be used (per problem) in lia2pb.");
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(in, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_lia2pb_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(lia2pb_tactic, m, p));
|
||||
}
|
||||
|
28
src/tactic/arith_tactics/lia2pb_tactic.h
Normal file
28
src/tactic/arith_tactics/lia2pb_tactic.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
lia2pb_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Reduce bounded LIA benchmark into 0-1 LIA benchmark.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _LIA2PB_TACTIC_H_
|
||||
#define _LIA2PB_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_lia2pb_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
279
src/tactic/arith_tactics/linear_equation.cpp
Normal file
279
src/tactic/arith_tactics/linear_equation.cpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"linear_equation.h"
|
||||
|
||||
/**
|
||||
\brief Return the position of variable x_i in the linear equation.
|
||||
Return UINT_MAX, if the variable is not in the linear_equation.
|
||||
*/
|
||||
unsigned linear_equation::pos(unsigned x_i) const {
|
||||
int low = 0;
|
||||
int high = m_size - 1;
|
||||
while (true) {
|
||||
int mid = low + ((high - low) / 2);
|
||||
var x_mid = m_xs[mid];
|
||||
if (x_i > x_mid) {
|
||||
low = mid + 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else if (x_i < x_mid) {
|
||||
high = mid - 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void linear_equation_manager::display(std::ostream & out, linear_equation const & eq) const {
|
||||
unsigned sz = eq.m_size;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (i > 0)
|
||||
out << " + ";
|
||||
out << m.to_string(eq.m_as[i]) << "*x" << eq.m_xs[i];
|
||||
}
|
||||
out << " = 0";
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpq * as, var * xs, bool normalized) {
|
||||
SASSERT(sz > 1);
|
||||
|
||||
// compute lcm of the denominators
|
||||
mpz l;
|
||||
mpz r;
|
||||
m.set(l, as[0].denominator());
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
m.set(r, as[i].denominator());
|
||||
m.lcm(r, l, l);
|
||||
}
|
||||
|
||||
TRACE("linear_equation_mk", tout << "lcm: " << m.to_string(l) << "\n";);
|
||||
|
||||
// copy l * as to m_int_buffer.
|
||||
m_int_buffer.reset();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
TRACE("linear_equation_mk", tout << "before as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
m.mul(l, as[i], as[i]);
|
||||
TRACE("linear_equation_mk", tout << "after as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
SASSERT(m.is_int(as[i]));
|
||||
m_int_buffer.push_back(as[i].numerator());
|
||||
}
|
||||
|
||||
linear_equation * new_eq = mk(sz, m_int_buffer.c_ptr(), xs, normalized);
|
||||
|
||||
m.del(r);
|
||||
m.del(l);
|
||||
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk_core(unsigned sz, mpz * as, var * xs) {
|
||||
SASSERT(sz > 0);
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
SASSERT(xs[i-1] < xs[i]);
|
||||
}
|
||||
});
|
||||
|
||||
TRACE("linear_equation_bug", for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
mpz g;
|
||||
m.set(g, as[0]);
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
if (m.is_one(g))
|
||||
break;
|
||||
if (m.is_neg(as[i])) {
|
||||
m.neg(as[i]);
|
||||
m.gcd(g, as[i], g);
|
||||
m.neg(as[i]);
|
||||
}
|
||||
else {
|
||||
m.gcd(g, as[i], g);
|
||||
}
|
||||
}
|
||||
if (!m.is_one(g)) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m.div(as[i], g, as[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("linear_equation_bug",
|
||||
tout << "g: " << m.to_string(g) << "\n";
|
||||
for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
m.del(g);
|
||||
|
||||
unsigned obj_sz = linear_equation::get_obj_size(sz);
|
||||
void * mem = m_allocator.allocate(obj_sz);
|
||||
linear_equation * new_eq = new (mem) linear_equation();
|
||||
mpz * new_as = reinterpret_cast<mpz*>(reinterpret_cast<char*>(new_eq) + sizeof(linear_equation));
|
||||
double * new_app_as = reinterpret_cast<double*>(reinterpret_cast<char*>(new_as) + sz * sizeof(mpz));
|
||||
var * new_xs = reinterpret_cast<var *>(reinterpret_cast<char*>(new_app_as) + sz * sizeof(double));
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
new (new_as + i) mpz();
|
||||
m.set(new_as[i], as[i]);
|
||||
new_app_as[i] = m.get_double(as[i]);
|
||||
var x_i = xs[i];
|
||||
new_xs[i] = x_i;
|
||||
}
|
||||
new_eq->m_size = sz;
|
||||
new_eq->m_as = new_as;
|
||||
new_eq->m_approx_as = new_app_as;
|
||||
new_eq->m_xs = new_xs;
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpz * as, var * xs, bool normalized) {
|
||||
if (!normalized) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
m_val_buffer.reserve(x+1);
|
||||
|
||||
if (m_mark[x]) {
|
||||
m.add(m_val_buffer[x], as[i], m_val_buffer[x]);
|
||||
}
|
||||
else {
|
||||
m.set(m_val_buffer[x], as[i]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
if (m_mark[x]) {
|
||||
if (!m.is_zero(m_val_buffer[x])) {
|
||||
xs[j] = xs[i];
|
||||
m.set(as[j], m_val_buffer[x]);
|
||||
j++;
|
||||
}
|
||||
m_mark[x] = false;
|
||||
}
|
||||
}
|
||||
sz = j;
|
||||
if (sz <= 1)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
SASSERT(!m_mark[x]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark[x] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_val_buffer.reserve(x+1);
|
||||
m.swap(m_val_buffer[x], as[i]);
|
||||
}
|
||||
std::sort(xs, xs+sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m.swap(as[i], m_val_buffer[x]);
|
||||
}
|
||||
|
||||
return mk_core(sz, as, xs);
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2) {
|
||||
SASSERT(!m.is_zero(b1));
|
||||
SASSERT(!m.is_zero(b2));
|
||||
mpz tmp, new_a;
|
||||
m_int_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
unsigned sz1 = eq1.size();
|
||||
unsigned sz2 = eq2.size();
|
||||
unsigned i1 = 0;
|
||||
unsigned i2 = 0;
|
||||
while (true) {
|
||||
if (i1 == sz1) {
|
||||
// copy remaining entries from eq2
|
||||
while (i2 < sz2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i2 == sz2) {
|
||||
// copy remaining entries from eq1
|
||||
while (i1 < sz1) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
var x1 = eq1.x(i1);
|
||||
var x2 = eq2.x(i2);
|
||||
if (x1 < x2) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
else if (x1 > x2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
else {
|
||||
m.mul(eq1.a(i1), b1, tmp);
|
||||
m.addmul(tmp, b2, eq2.a(i2), new_a);
|
||||
if (!m.is_zero(new_a)) {
|
||||
m_int_buffer.push_back(new_a);
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
}
|
||||
i1++;
|
||||
i2++;
|
||||
}
|
||||
}
|
||||
m.del(tmp);
|
||||
m.del(new_a);
|
||||
SASSERT(m_int_buffer.size() == m_var_buffer.size());
|
||||
if (m_int_buffer.empty())
|
||||
return 0;
|
||||
return mk_core(m_int_buffer.size(), m_int_buffer.c_ptr(), m_var_buffer.c_ptr());
|
||||
}
|
||||
|
||||
void linear_equation_manager::del(linear_equation * eq) {
|
||||
for (unsigned i = 0; i < eq->m_size; i++) {
|
||||
m.del(eq->m_as[i]);
|
||||
}
|
||||
unsigned obj_sz = linear_equation::get_obj_size(eq->m_size);
|
||||
m_allocator.deallocate(obj_sz, eq);
|
||||
}
|
||||
|
85
src/tactic/arith_tactics/linear_equation.h
Normal file
85
src/tactic/arith_tactics/linear_equation.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _LINEAR_EQUATION_H_
|
||||
#define _LINEAR_EQUATION_H_
|
||||
|
||||
#include"mpq.h"
|
||||
#include"small_object_allocator.h"
|
||||
#include"numeral_buffer.h"
|
||||
#include"double_manager.h"
|
||||
|
||||
class linear_equation {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
private:
|
||||
static unsigned get_obj_size(unsigned sz) { return sizeof(linear_equation) + sz * (sizeof(mpz) + sizeof(double) + sizeof(var)); }
|
||||
friend class linear_equation_manager;
|
||||
unsigned m_size;
|
||||
mpz * m_as; // precise coefficients
|
||||
double * m_approx_as; // approximated coefficients
|
||||
var * m_xs; // var ids
|
||||
linear_equation() {}
|
||||
public:
|
||||
unsigned size() const { return m_size; }
|
||||
mpz const & a(unsigned idx) const { SASSERT(idx < m_size); return m_as[idx]; }
|
||||
double approx_a(unsigned idx) const { SASSERT(idx < m_size); return m_approx_as[idx]; }
|
||||
var x(unsigned idx) const { SASSERT(idx < m_size); return m_xs[idx]; }
|
||||
unsigned pos(unsigned x_i) const;
|
||||
void get_a(double_manager & m, unsigned idx, double & r) const { r = m_approx_as[idx]; }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpq & r) const { m.set(r, m_as[idx]); }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpz & r) const { m.set(r, m_as[idx]); }
|
||||
};
|
||||
|
||||
class linear_equation_manager {
|
||||
public:
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef linear_equation::var var;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
private:
|
||||
typedef svector<var> var_buffer;
|
||||
|
||||
small_object_allocator & m_allocator;
|
||||
numeral_manager & m;
|
||||
mpz_buffer m_int_buffer;
|
||||
mpz_buffer m_val_buffer;
|
||||
char_vector m_mark;
|
||||
var_buffer m_var_buffer;
|
||||
|
||||
linear_equation * mk_core(unsigned sz, mpz * as, var * xs);
|
||||
|
||||
public:
|
||||
linear_equation_manager(numeral_manager & _m, small_object_allocator & a):m_allocator(a), m(_m), m_int_buffer(m), m_val_buffer(m) {}
|
||||
~linear_equation_manager() {}
|
||||
|
||||
linear_equation * mk(unsigned sz, mpq * as, var * xs, bool normalized = false);
|
||||
linear_equation * mk(unsigned sz, mpz * as, var * xs, bool normalized = false);
|
||||
void del(linear_equation * eq);
|
||||
|
||||
// Return b1 * eq1 + b2 * eq2
|
||||
// return 0 if the b1 * eq1 + b2 * eq2 == 0
|
||||
linear_equation * mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2);
|
||||
|
||||
void display(std::ostream & out, linear_equation const & eq) const;
|
||||
};
|
||||
|
||||
#endif
|
1792
src/tactic/arith_tactics/lu.cpp
Normal file
1792
src/tactic/arith_tactics/lu.cpp
Normal file
File diff suppressed because it is too large
Load diff
404
src/tactic/arith_tactics/lu.h
Normal file
404
src/tactic/arith_tactics/lu.h
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
lu.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Simple LU factorization module based on the paper:
|
||||
|
||||
"Maintaining LU factors of a General Sparse Matrix"
|
||||
P. E. Gill, W. Murray, M. Saunders, M. Wright
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-09
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _LU_H_
|
||||
#define _LU_H_
|
||||
|
||||
#include"vector.h"
|
||||
#include"mpq.h"
|
||||
#include"double_manager.h"
|
||||
#include"permutation.h"
|
||||
#include"params.h"
|
||||
#include"strategy_exception.h"
|
||||
|
||||
MK_ST_EXCEPTION(lu_exception);
|
||||
|
||||
template<typename NumManager>
|
||||
class lu {
|
||||
public:
|
||||
typedef NumManager manager;
|
||||
typedef typename NumManager::numeral numeral;
|
||||
typedef svector<numeral> numeral_vector;
|
||||
|
||||
private:
|
||||
manager & m_manager;
|
||||
|
||||
// Configuration
|
||||
numeral m_mu; // maximum multiplier when selecting a pivot
|
||||
unsigned m_selection_cutoff;
|
||||
|
||||
// Matrix size
|
||||
unsigned m_sz; // supporting only square matrices
|
||||
|
||||
// Permutations
|
||||
permutation P;
|
||||
permutation Q;
|
||||
|
||||
// L
|
||||
//
|
||||
// It is 3 parallel vectors representing the sequence (product) of matrices
|
||||
// L[0] L[1] ... L[m-1]
|
||||
// where each L[i] is a tuple (A[i], indc[i], indr[i]).
|
||||
// Each tuple represents a triangular factor. That is, an identity matrix
|
||||
// where the position at row indc[i], and column indr[i] contains the value A[i].
|
||||
// Remark: The product L[0] L[1] ... L[n-1] is not really a triangular matrix.
|
||||
struct L_file {
|
||||
numeral_vector A;
|
||||
unsigned_vector indc;
|
||||
unsigned_vector indr;
|
||||
};
|
||||
L_file L;
|
||||
|
||||
|
||||
// U
|
||||
//
|
||||
// It is not really upper triangular, but the product PUQ is.
|
||||
// The rows of U are stored in the parallel vectors (A, indr)
|
||||
// Only the non-zero values are stored at U.
|
||||
// The non-zeros of row i start at position begr[i] and end at
|
||||
// position endr[i] of the parallel vectors (A, indr).
|
||||
// The length of the row is endr[i] - begr[i].
|
||||
// The coefficients are stored in A, and the column ids at indr.
|
||||
//
|
||||
// The factorization of a matrix A is represented as:
|
||||
// L[0] L[1] ... L[m-1] P U Q
|
||||
struct U_file {
|
||||
numeral_vector A;
|
||||
unsigned_vector indr;
|
||||
unsigned_vector begr;
|
||||
unsigned_vector endr;
|
||||
|
||||
unsigned num_entries;
|
||||
U_file():num_entries(0) {}
|
||||
};
|
||||
U_file U;
|
||||
|
||||
// The actual factorization
|
||||
|
||||
|
||||
// T_file: temporary file used for factorization
|
||||
struct T_file {
|
||||
// row list
|
||||
unsigned_vector indr;
|
||||
unsigned_vector begr;
|
||||
unsigned_vector endr;
|
||||
|
||||
// column list
|
||||
numeral_vector A;
|
||||
unsigned_vector indc;
|
||||
unsigned_vector begc;
|
||||
unsigned_vector endc;
|
||||
|
||||
unsigned num_entries;
|
||||
T_file():num_entries(0) {}
|
||||
};
|
||||
T_file T;
|
||||
|
||||
// Auxiliary fields
|
||||
unsigned_vector locw;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Main
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
lu(manager & m, params_ref const & p);
|
||||
~lu();
|
||||
|
||||
manager & m() const { return m_manager; }
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
|
||||
void reset();
|
||||
|
||||
unsigned size() const { return m_sz; }
|
||||
|
||||
protected:
|
||||
void del_nums(numeral_vector & nums);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
// Contract for setting up the initial matrix:
|
||||
// lu.init(size)
|
||||
// - for each row r in the matrix
|
||||
// - for each non-zero (a,x) in the row
|
||||
// lu.add_entry(a, x)
|
||||
// lu.end_row()
|
||||
void init(unsigned size);
|
||||
void add_entry(numeral const & a, unsigned x);
|
||||
void end_row();
|
||||
|
||||
protected:
|
||||
// auxiliary fields used during initialization
|
||||
bool ini; // try if the matrix T is being setup using the protocol above
|
||||
unsigned ini_irow;
|
||||
unsigned fillin_for(unsigned sz);
|
||||
void move_col_to_end(unsigned x);
|
||||
void move_row_to_end(unsigned x);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Factorization
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void fact();
|
||||
|
||||
protected:
|
||||
class todo {
|
||||
unsigned_vector m_elem2len;
|
||||
unsigned_vector m_elem2pos;
|
||||
vector<unsigned_vector> m_elems_per_len;
|
||||
unsigned m_size;
|
||||
public:
|
||||
todo():m_size(0) {}
|
||||
bool contains(unsigned elem) const { return m_elem2pos[elem] != UINT_MAX; }
|
||||
void init(unsigned capacity);
|
||||
void updt_len(unsigned elem, unsigned len);
|
||||
unsigned len(unsigned elem) const { return m_elem2len[elem]; }
|
||||
void erase(unsigned elem);
|
||||
unsigned size() const { return m_size; }
|
||||
void display(std::ostream & out) const;
|
||||
class iterator {
|
||||
todo const & m_todo;
|
||||
unsigned m_i;
|
||||
unsigned m_j;
|
||||
unsigned m_c;
|
||||
void find_next();
|
||||
public:
|
||||
iterator(todo const & t):m_todo(t), m_i(0), m_j(0), m_c(0) { if (!at_end()) find_next(); }
|
||||
bool at_end() const { return m_c == m_todo.m_size; }
|
||||
unsigned curr() const {
|
||||
unsigned_vector const & v_i = m_todo.m_elems_per_len[m_i];
|
||||
return v_i[m_j];
|
||||
}
|
||||
void next() { SASSERT(!at_end()); m_j++; m_c++; find_next(); }
|
||||
};
|
||||
};
|
||||
|
||||
todo m_todo_rows;
|
||||
todo m_todo_cols;
|
||||
svector<bool> m_enabled_rows;
|
||||
svector<bool> m_enabled_cols;
|
||||
|
||||
bool enabled_row(unsigned r) const { return m_enabled_rows[r]; }
|
||||
bool enabled_col(unsigned c) const { return m_enabled_cols[c]; }
|
||||
|
||||
unsigned_vector m_toadd_rows;
|
||||
svector<bool> m_marked_rows;
|
||||
|
||||
// Temporary numerals
|
||||
// I do not use local numerals to avoid memory leaks
|
||||
numeral tol;
|
||||
numeral C_max;
|
||||
numeral A_ij;
|
||||
numeral A_best;
|
||||
numeral A_aux;
|
||||
numeral tmp;
|
||||
numeral mu_best;
|
||||
numeral mu_1;
|
||||
|
||||
void init_fact();
|
||||
bool stability_test(unsigned rin, unsigned cin, bool improvingM);
|
||||
void select_pivot(unsigned & r_out, unsigned & c_out);
|
||||
void process_pivot_core(unsigned r, unsigned c);
|
||||
void process_pivot(unsigned i, unsigned r, unsigned c);
|
||||
bool check_locw() const;
|
||||
void dec_lenr(unsigned r);
|
||||
void inc_lenr(unsigned r);
|
||||
void dec_lenc(unsigned c);
|
||||
void inc_lenc(unsigned c);
|
||||
void del_row_entry(unsigned r, unsigned c);
|
||||
void del_disabled_cols(unsigned r);
|
||||
void add_row_entry(unsigned r, unsigned c);
|
||||
void add_col_entry(unsigned r, unsigned c, numeral const & a);
|
||||
void compress_rows();
|
||||
void compress_columns();
|
||||
void compress_if_needed();
|
||||
void copy_T_to_U();
|
||||
|
||||
bool check_lenr() const;
|
||||
bool check_lenc() const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Solving
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
|
||||
// Temporary vector used to interact with different solvers.
|
||||
// The vector has support for tracking non-zeros.
|
||||
class dense_vector {
|
||||
public:
|
||||
typedef typename lu<NumManager>::manager manager;
|
||||
typedef typename lu<NumManager>::numeral numeral;
|
||||
private:
|
||||
friend class lu;
|
||||
manager & m_manager;
|
||||
unsigned_vector m_non_zeros; // positions that may contain non-zeros. if a position is not here, then it must contain a zero
|
||||
char_vector m_in_non_zeros; // m_in_non_zeros[i] == true if m_non_zeros contains i.
|
||||
numeral_vector m_values;
|
||||
public:
|
||||
dense_vector(manager & m, unsigned sz);
|
||||
~dense_vector();
|
||||
|
||||
manager & m() const { return m_manager; }
|
||||
|
||||
void reset();
|
||||
void reset(unsigned new_sz);
|
||||
|
||||
unsigned size() const { return m_values.size(); }
|
||||
numeral const & operator[](unsigned idx) const { return m_values[idx]; }
|
||||
|
||||
void swap(dense_vector & other) {
|
||||
m_non_zeros.swap(other.m_non_zeros);
|
||||
m_in_non_zeros.swap(other.m_in_non_zeros);
|
||||
m_values.swap(other.m_values);
|
||||
}
|
||||
|
||||
// Get a given position for performing an update.
|
||||
// idx is inserted into m_non_zeros.
|
||||
numeral & get(unsigned idx) {
|
||||
if (!m_in_non_zeros[idx]) {
|
||||
m_in_non_zeros[idx] = true;
|
||||
m_non_zeros.push_back(idx);
|
||||
}
|
||||
return m_values[idx];
|
||||
}
|
||||
|
||||
typedef unsigned_vector::const_iterator iterator;
|
||||
|
||||
// iterator for positions that may contain non-zeros
|
||||
iterator begin_non_zeros() const { return m_non_zeros.begin(); }
|
||||
iterator end_non_zeros() const { return m_non_zeros.end(); }
|
||||
|
||||
void display(std::ostream & out) const;
|
||||
void display_non_zeros(std::ostream & out) const;
|
||||
void display_pol(std::ostream & out) const;
|
||||
|
||||
void elim_zeros();
|
||||
};
|
||||
|
||||
// Solve: Lx = y
|
||||
// The result is stored in y.
|
||||
void solve_Lx_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: PUQx = y
|
||||
// The result is stored in y.
|
||||
void solve_Ux_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: LPUQx = y
|
||||
// The result is stored in y.
|
||||
void solve_Ax_eq_y(dense_vector & y) {
|
||||
solve_Lx_eq_y(y);
|
||||
solve_Ux_eq_y(y);
|
||||
}
|
||||
|
||||
// Solve: xL = y
|
||||
// The result is stored in y.
|
||||
void solve_xL_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: xPUQ = y
|
||||
// The result is stored in y.
|
||||
void solve_xU_eq_y(dense_vector & y);
|
||||
|
||||
// Solve: xA = y
|
||||
// The result is stored in y.
|
||||
void solve_xA_eq_y(dense_vector & y) {
|
||||
solve_xU_eq_y(y);
|
||||
solve_xL_eq_y(y);
|
||||
}
|
||||
|
||||
private:
|
||||
dense_vector m_tmp_xU_vector;
|
||||
dense_vector m_tmp_replace_column_vector;
|
||||
dense_vector m_tmp_vector;
|
||||
dense_vector m_tmp_row;
|
||||
|
||||
public:
|
||||
dense_vector & get_tmp_vector() { return m_tmp_vector; }
|
||||
dense_vector & get_tmp_row(unsigned size) { m_tmp_row.reset(size); return m_tmp_row; }
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Column replacement
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void replace_column(unsigned j, dense_vector & new_col);
|
||||
void replace_U_column(unsigned j, dense_vector & new_col);
|
||||
unsigned get_num_replacements() const { return m_num_replacements; }
|
||||
dense_vector & get_tmp_col() { return m_tmp_col; }
|
||||
|
||||
private:
|
||||
unsigned m_num_replacements;
|
||||
dense_vector m_tmp_col;
|
||||
|
||||
void del_U_row_entry(unsigned r, unsigned c);
|
||||
void compress_U_rows();
|
||||
void compress_U_if_needed();
|
||||
void move_U_row_to_end(unsigned r);
|
||||
void add_U_row_entry(unsigned r, unsigned c, numeral const & a);
|
||||
void add_replace_U_row_entry(unsigned r, unsigned c, numeral const & a);
|
||||
unsigned replace_U_column_core(unsigned j, dense_vector & new_col);
|
||||
bool check_U_except_col(unsigned c) const;
|
||||
bool check_U_except_row(unsigned r) const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Invariants
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
bool check_P() const;
|
||||
bool check_Q() const;
|
||||
bool check_L() const;
|
||||
bool check_U() const;
|
||||
bool T_col_contains(unsigned c, unsigned r) const;
|
||||
bool T_row_contains(unsigned r, unsigned c) const;
|
||||
bool check_T() const;
|
||||
bool check_invariant() const;
|
||||
|
||||
void display_T(std::ostream & out) const;
|
||||
void display_U(std::ostream & out, unsigned_vector const * var_ids = 0) const;
|
||||
void display_L(std::ostream & out) const;
|
||||
void display(std::ostream & out, unsigned_vector const * var_ids = 0) const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Info
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
unsigned L_size() const { return L.indc.size(); }
|
||||
unsigned U_size() const { return U.num_entries; }
|
||||
};
|
||||
|
||||
typedef lu<unsynch_mpq_manager> rational_lu;
|
||||
typedef lu<double_manager> double_lu;
|
||||
|
||||
#endif
|
135
src/tactic/arith_tactics/mip_tactic.cpp
Normal file
135
src/tactic/arith_tactics/mip_tactic.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
mip_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for solvig MIP (mixed integer) problem.
|
||||
This is a temporary tactic. It should be deleted
|
||||
after theory_arith is upgraded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-26
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"smt_solver_exp.h"
|
||||
|
||||
class mip_tactic : public tactic {
|
||||
struct imp;
|
||||
ast_manager & m;
|
||||
params_ref m_params;
|
||||
statistics m_stats;
|
||||
scoped_ptr<smt::solver_exp> m_solver;
|
||||
|
||||
void init_solver() {
|
||||
smt::solver_exp * new_solver = alloc(smt::solver_exp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_solver = new_solver;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
mip_tactic(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_params(p) {
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(mip_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~mip_tactic() {}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
bool produce_models = g->models_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("mip", *g);
|
||||
fail_if_proof_generation("mip", g);
|
||||
fail_if_unsat_core_generation("mip", g);
|
||||
|
||||
g->elim_redundancies();
|
||||
if (g->inconsistent()) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
init_solver();
|
||||
m_solver->assert_goal(*g);
|
||||
|
||||
lbool r;
|
||||
try {
|
||||
r = m_solver->check();
|
||||
}
|
||||
catch (strategy_exception & ex) {
|
||||
// solver_exp uses assertion_sets and strategy_exception's
|
||||
throw tactic_exception(ex.msg());
|
||||
}
|
||||
|
||||
m_solver->collect_statistics(m_stats);
|
||||
|
||||
if (r == l_false) {
|
||||
g->reset();
|
||||
g->assert_expr(m.mk_false());
|
||||
}
|
||||
else if (r == l_true) {
|
||||
g->reset();
|
||||
if (produce_models) {
|
||||
model_ref md;
|
||||
m_solver->get_model(md);
|
||||
mc = model2model_converter(md.get());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// failed
|
||||
}
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("mip", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
if (m_solver)
|
||||
m_solver->collect_statistics(m_stats);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_solver = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void collect_statistics(statistics & st) const {
|
||||
st.copy(m_stats);
|
||||
}
|
||||
|
||||
virtual void reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_solver)
|
||||
m_solver->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_mip_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(mip_tactic, m, p));
|
||||
}
|
30
src/tactic/arith_tactics/mip_tactic.h
Normal file
30
src/tactic/arith_tactics/mip_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
mip_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for solvig MIP (mixed integer) problem.
|
||||
This is a temporary tactic. It should be deleted
|
||||
after theory_arith is upgraded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-26
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _MIP_TACTIC_H_
|
||||
#define _MIP_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_mip_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
480
src/tactic/arith_tactics/nla2bv_tactic.cpp
Normal file
480
src/tactic/arith_tactics/nla2bv_tactic.cpp
Normal file
|
@ -0,0 +1,480 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
nla2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert quantified NIA problems to bounded bit-vector arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-3
|
||||
|
||||
Notes:
|
||||
Ported to tactic framework on 2012-02-28
|
||||
The original file was called qfnla2bv.cpp
|
||||
|
||||
--*/
|
||||
#include "tactical.h"
|
||||
#include "arith_decl_plugin.h"
|
||||
#include "bv_decl_plugin.h"
|
||||
#include "for_each_expr.h"
|
||||
#include "expr_replacer.h"
|
||||
#include "optional.h"
|
||||
#include "bv2int_rewriter.h"
|
||||
#include "bv2real_rewriter.h"
|
||||
#include "extension_model_converter.h"
|
||||
#include "filter_model_converter.h"
|
||||
#include "bound_manager.h"
|
||||
#include "obj_pair_hashtable.h"
|
||||
#include "ast_smt2_pp.h"
|
||||
|
||||
//
|
||||
//
|
||||
// 1. for each variable, determine bounds (s.t., non-negative variables
|
||||
// have unsigned bit-vectors).
|
||||
//
|
||||
// 2. replace uninterpreted variables of sort int by
|
||||
// expressions of the form +- bv2int(b) +- k
|
||||
// where k is a slack.
|
||||
//
|
||||
// 3. simplify resulting assertion set to reduce occurrences of bv2int.
|
||||
//
|
||||
|
||||
class nla2bv_tactic : public tactic {
|
||||
class imp {
|
||||
typedef rational numeral;
|
||||
ast_manager & m_manager;
|
||||
bool m_is_sat_preserving;
|
||||
arith_util m_arith;
|
||||
bv_util m_bv;
|
||||
bv2real_util m_bv2real;
|
||||
bv2int_rewriter_ctx m_bv2int_ctx;
|
||||
bound_manager m_bounds;
|
||||
expr_substitution m_subst;
|
||||
func_decl_ref_vector m_vars;
|
||||
expr_ref_vector m_defs;
|
||||
expr_ref_vector m_trail;
|
||||
unsigned m_num_bits;
|
||||
unsigned m_default_bv_size;
|
||||
ref<filter_model_converter> m_fmc;
|
||||
|
||||
public:
|
||||
imp(ast_manager & m, params_ref const& p):
|
||||
m_manager(m),
|
||||
m_is_sat_preserving(true),
|
||||
m_arith(m),
|
||||
m_bv(m),
|
||||
m_bv2real(m, rational(p.get_uint(":nla2bv-root",2)), rational(p.get_uint(":nla2bv-divisor",2)), p.get_uint(":nla2bv-max-bv-size", UINT_MAX)),
|
||||
m_bv2int_ctx(m, p),
|
||||
m_bounds(m),
|
||||
m_subst(m),
|
||||
m_vars(m),
|
||||
m_defs(m),
|
||||
m_trail(m),
|
||||
m_fmc(0) {
|
||||
m_default_bv_size = m_num_bits = p.get_uint(":nla2bv-bv-size", 4);
|
||||
}
|
||||
|
||||
~imp() {}
|
||||
|
||||
|
||||
void operator()(goal & g, model_converter_ref & mc) {
|
||||
TRACE("nla2bv", g.display(tout);
|
||||
tout << "Muls: " << count_mul(g) << "\n";
|
||||
);
|
||||
m_fmc = alloc(filter_model_converter, m_manager);
|
||||
m_bounds(g);
|
||||
collect_power2(g);
|
||||
if(!collect_vars(g)) {
|
||||
throw tactic_exception("goal is not in the fragment supported by nla2bv");
|
||||
}
|
||||
tactic_report report("nla->bv", g);
|
||||
substitute_vars(g);
|
||||
TRACE("nla2bv", g.display(tout << "substitute vars\n"););
|
||||
reduce_bv2int(g);
|
||||
reduce_bv2real(g);
|
||||
TRACE("nla2bv", g.display(tout << "after reduce\n"););
|
||||
extension_model_converter * evc = alloc(extension_model_converter, m_manager);
|
||||
mc = concat(m_fmc.get(), evc);
|
||||
for (unsigned i = 0; i < m_vars.size(); ++i) {
|
||||
evc->insert(m_vars[i].get(), m_defs[i].get());
|
||||
}
|
||||
for (unsigned i = 0; i < m_bv2real.num_aux_decls(); ++i) {
|
||||
m_fmc->insert(m_bv2real.get_aux_decl(i));
|
||||
}
|
||||
IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(nla->bv :sat-preserving " << m_is_sat_preserving << ")\n";);
|
||||
TRACE("nla2bv_verbose", g.display(tout););
|
||||
TRACE("nla2bv", tout << "Muls: " << count_mul(g) << "\n";);
|
||||
g.inc_depth();
|
||||
if (!is_sat_preserving())
|
||||
g.updt_prec(goal::UNDER);
|
||||
}
|
||||
|
||||
bool const& is_sat_preserving() const { return m_is_sat_preserving; }
|
||||
|
||||
private:
|
||||
void set_satisfiability_preserving(bool f) {
|
||||
m_is_sat_preserving = f;
|
||||
}
|
||||
|
||||
void collect_power2(goal & g) {
|
||||
m_bv2int_ctx.collect_power2(g);
|
||||
obj_map<expr, expr*> const& p2 = m_bv2int_ctx.power2();
|
||||
if (p2.empty()) return;
|
||||
obj_map<expr, expr*>::iterator it = p2.begin(), end = p2.end();
|
||||
for (; it != end; ++it) {
|
||||
expr* v = it->m_value;
|
||||
unsigned num_bits = m_bv.get_bv_size(v);
|
||||
expr* w = m_bv.mk_bv2int(m_bv.mk_bv_shl(m_bv.mk_numeral(1, num_bits), v));
|
||||
m_trail.push_back(w);
|
||||
m_subst.insert(it->m_key, w);
|
||||
TRACE("nla2bv", tout << mk_ismt2_pp(it->m_key, m_manager) << " " << mk_ismt2_pp(w, m_manager) << "\n";);
|
||||
}
|
||||
// eliminate the variables that are power of two.
|
||||
substitute_vars(g);
|
||||
m_subst.reset();
|
||||
}
|
||||
|
||||
|
||||
// eliminate bv2int from formula
|
||||
void reduce_bv2int(goal & g) {
|
||||
bv2int_rewriter_star reduce(m_manager, m_bv2int_ctx);
|
||||
expr_ref r(m_manager);
|
||||
for (unsigned i = 0; i < g.size(); ++i) {
|
||||
reduce(g.form(i), r);
|
||||
g.update(i, r);
|
||||
}
|
||||
assert_side_conditions(g, m_bv2int_ctx.num_side_conditions(),
|
||||
m_bv2int_ctx.side_conditions());
|
||||
}
|
||||
|
||||
// eliminate bv2real from formula
|
||||
void reduce_bv2real(goal & g) {
|
||||
bv2real_rewriter_star reduce(m_manager, m_bv2real);
|
||||
expr_ref r(m_manager);
|
||||
for (unsigned i = 0; i < g.size(); ++i) {
|
||||
reduce(g.form(i), r);
|
||||
if (m_bv2real.contains_bv2real(r)) {
|
||||
throw tactic_exception("nla2bv could not eliminate reals");
|
||||
}
|
||||
g.update(i, r);
|
||||
}
|
||||
assert_side_conditions(g, m_bv2real.num_side_conditions(),
|
||||
m_bv2real.side_conditions());
|
||||
}
|
||||
|
||||
void assert_side_conditions(goal & g, unsigned sz, expr * const * conditions) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
g.assert_expr(conditions[i]);
|
||||
set_satisfiability_preserving(false);
|
||||
}
|
||||
TRACE("nla2bv",
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
tout << mk_ismt2_pp(conditions[i], m_manager) << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
// substitute variables by bit-vectors
|
||||
void substitute_vars(goal & g) {
|
||||
scoped_ptr<expr_replacer> er = mk_default_expr_replacer(m_manager);
|
||||
er->set_substitution(&m_subst);
|
||||
expr_ref r(m_manager);
|
||||
for (unsigned i = 0; i < g.size(); ++i) {
|
||||
(*er)(g.form(i), r);
|
||||
g.update(i, r);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// collect uninterpreted variables in problem.
|
||||
// create a substitution from the variables to
|
||||
// bit-vector terms.
|
||||
//
|
||||
void add_var(app* n) {
|
||||
if (m_arith.is_int(n)) {
|
||||
add_int_var(n);
|
||||
}
|
||||
else {
|
||||
SASSERT(m_arith.is_real(n));
|
||||
add_real_var(n);
|
||||
}
|
||||
}
|
||||
|
||||
void add_int_var(app* n) {
|
||||
expr_ref s_bv(m_manager);
|
||||
sort_ref bv_sort(m_manager);
|
||||
optional<numeral> low, up;
|
||||
numeral tmp;
|
||||
bool is_strict;
|
||||
if (m_bounds.has_lower(n, tmp, is_strict)) {
|
||||
SASSERT(!is_strict);
|
||||
low = tmp;
|
||||
}
|
||||
if (m_bounds.has_upper(n, tmp, is_strict)) {
|
||||
SASSERT(!is_strict);
|
||||
up = tmp;
|
||||
}
|
||||
//
|
||||
// [low .. up]
|
||||
// num_bits = log2(1 + |up - low|) or m_num_bits
|
||||
//
|
||||
unsigned num_bits = m_num_bits;
|
||||
if (up && low) {
|
||||
num_bits = log2(abs(*up - *low)+numeral(1));
|
||||
}
|
||||
else {
|
||||
TRACE("nla2bv", tout << "no bounds for " << mk_ismt2_pp(n, m_manager) << "\n";);
|
||||
set_satisfiability_preserving(false);
|
||||
}
|
||||
bv_sort = m_bv.mk_sort(num_bits);
|
||||
std::string name = n->get_decl()->get_name().str();
|
||||
s_bv = m_manager.mk_fresh_const(name.c_str(), bv_sort);
|
||||
m_fmc->insert(to_app(s_bv)->get_decl());
|
||||
s_bv = m_bv.mk_bv2int(s_bv);
|
||||
if (low) {
|
||||
if (!(*low).is_zero()) {
|
||||
// low <= s_bv
|
||||
// ~>
|
||||
// replace s_bv by s_bv + low
|
||||
// add 'low' to model for n.
|
||||
//
|
||||
s_bv = m_arith.mk_add(s_bv, m_arith.mk_numeral(*low, true));
|
||||
}
|
||||
}
|
||||
else if (up) {
|
||||
// s_bv <= up
|
||||
// ~>
|
||||
// replace s_bv by up - s_bv
|
||||
//
|
||||
s_bv = m_arith.mk_sub(m_arith.mk_numeral(*up, true), s_bv);
|
||||
}
|
||||
else {
|
||||
s_bv = m_arith.mk_sub(s_bv, m_arith.mk_numeral(m_bv.power_of_two(num_bits-1), true));
|
||||
}
|
||||
|
||||
m_trail.push_back(s_bv);
|
||||
m_subst.insert(n, s_bv);
|
||||
m_vars.push_back(n->get_decl());
|
||||
m_defs.push_back(s_bv);
|
||||
}
|
||||
|
||||
void add_real_var(app* n) {
|
||||
expr_ref s_bv(m_manager), s_bvr(m_manager), s(m_manager), t(m_manager);
|
||||
sort_ref bv_sort(m_manager);
|
||||
bv_sort = m_bv.mk_sort(m_num_bits);
|
||||
set_satisfiability_preserving(false);
|
||||
std::string name = n->get_decl()->get_name().str();
|
||||
s = m_manager.mk_fresh_const(name.c_str(), bv_sort);
|
||||
name += "_r";
|
||||
t = m_manager.mk_fresh_const(name.c_str(), bv_sort);
|
||||
m_fmc->insert(to_app(s)->get_decl());
|
||||
m_fmc->insert(to_app(t)->get_decl());
|
||||
s_bv = m_bv2real.mk_bv2real(s, t);
|
||||
m_trail.push_back(s_bv);
|
||||
m_subst.insert(n, s_bv);
|
||||
m_vars.push_back(n->get_decl());
|
||||
|
||||
// use version without bv2real function.
|
||||
m_bv2real.mk_bv2real_reduced(s, t, s_bvr);
|
||||
m_defs.push_back(s_bvr);
|
||||
}
|
||||
|
||||
|
||||
// update number of bits based on the largest constant used.
|
||||
void update_num_bits(app* n) {
|
||||
bool is_int;
|
||||
numeral nm;
|
||||
if (m_arith.is_numeral(n, nm, is_int) && is_int) {
|
||||
nm = abs(nm);
|
||||
unsigned l = log2(nm);
|
||||
if (m_num_bits <= l) {
|
||||
m_num_bits = l+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned log2(rational const& n) {
|
||||
rational pow(1), two(2);
|
||||
unsigned sz = 0;
|
||||
while (pow < n) {
|
||||
++sz;
|
||||
pow *= two;
|
||||
}
|
||||
if (sz == 0) sz = 1;
|
||||
return sz;
|
||||
}
|
||||
|
||||
class get_uninterp_proc {
|
||||
imp& m_imp;
|
||||
ptr_vector<app> m_vars;
|
||||
bool m_in_supported_fragment;
|
||||
public:
|
||||
get_uninterp_proc(imp& s): m_imp(s), m_in_supported_fragment(true) {}
|
||||
ptr_vector<app> const& vars() { return m_vars; }
|
||||
void operator()(var * n) {
|
||||
m_in_supported_fragment = false;
|
||||
}
|
||||
void operator()(app* n) {
|
||||
arith_util& a = m_imp.m_arith;
|
||||
ast_manager& m = a.get_manager();
|
||||
if (a.is_int(n) &&
|
||||
is_uninterp_const(n)) {
|
||||
m_vars.push_back(n);
|
||||
}
|
||||
else if (a.is_real(n) &&
|
||||
is_uninterp_const(n)) {
|
||||
m_vars.push_back(n);
|
||||
}
|
||||
else if (m.is_bool(n) && is_uninterp_const(n)) {
|
||||
|
||||
}
|
||||
else if (!(a.is_mul(n) ||
|
||||
a.is_add(n) ||
|
||||
a.is_sub(n) ||
|
||||
a.is_le(n) ||
|
||||
a.is_lt(n) ||
|
||||
a.is_ge(n) ||
|
||||
a.is_gt(n) ||
|
||||
a.is_numeral(n) ||
|
||||
a.is_uminus(n) ||
|
||||
m_imp.m_bv2real.is_pos_le(n) ||
|
||||
m_imp.m_bv2real.is_pos_lt(n) ||
|
||||
n->get_family_id() == a.get_manager().get_basic_family_id())) {
|
||||
TRACE("nla2bv", tout << "Not supported: " << mk_ismt2_pp(n, a.get_manager()) << "\n";);
|
||||
m_in_supported_fragment = false;
|
||||
}
|
||||
m_imp.update_num_bits(n);
|
||||
}
|
||||
void operator()(quantifier* q) {
|
||||
m_in_supported_fragment = false;
|
||||
}
|
||||
bool is_supported() const { return m_in_supported_fragment; }
|
||||
};
|
||||
|
||||
bool collect_vars(goal const & g) {
|
||||
get_uninterp_proc fe_var(*this);
|
||||
for_each_expr_at(fe_var, g);
|
||||
for (unsigned i = 0; i < fe_var.vars().size(); ++i) {
|
||||
add_var(fe_var.vars()[i]);
|
||||
}
|
||||
return fe_var.is_supported() && !fe_var.vars().empty();
|
||||
}
|
||||
|
||||
class count_mul_proc {
|
||||
imp& m_imp;
|
||||
unsigned m_count;
|
||||
public:
|
||||
count_mul_proc(imp& s): m_imp(s), m_count(0) {}
|
||||
unsigned count() const { return m_count; }
|
||||
void operator()(var * n) {}
|
||||
void operator()(app* n) {
|
||||
if (m_imp.m_arith.is_mul(n)) {
|
||||
m_count += n->get_num_args()-1;
|
||||
}
|
||||
if (m_imp.m_bv.is_bv_mul(n)) {
|
||||
unsigned num_vars = 0;
|
||||
for (unsigned j = 0; j < n->get_num_args(); ++j) {
|
||||
if (!m_imp.m_bv.is_numeral(n->get_arg(j))) {
|
||||
++num_vars;
|
||||
}
|
||||
}
|
||||
if (num_vars > 1) {
|
||||
m_count += num_vars - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
void operator()(quantifier* q) {}
|
||||
};
|
||||
|
||||
unsigned count_mul(goal const & g) {
|
||||
count_mul_proc c(*this);
|
||||
for_each_expr_at(c, g);
|
||||
return c.count();
|
||||
}
|
||||
};
|
||||
|
||||
params_ref m_params;
|
||||
imp * m_imp;
|
||||
|
||||
struct scoped_set_imp {
|
||||
nla2bv_tactic & m_owner;
|
||||
scoped_set_imp(nla2bv_tactic & o, imp & i):
|
||||
m_owner(o) {
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_owner.m_imp = &i;
|
||||
}
|
||||
}
|
||||
|
||||
~scoped_set_imp() {
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_owner.m_imp = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
nla2bv_tactic(params_ref const & p):
|
||||
m_params(p),
|
||||
m_imp(0) {
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(nla2bv_tactic, m_params);
|
||||
}
|
||||
|
||||
virtual ~nla2bv_tactic() {
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":nla2bv-max-bv-size", CPK_UINT, "(default: inf) maximum bit-vector size used by nla2bv tactic");
|
||||
r.insert(":nla2bv-bv-size", CPK_UINT, "(default: 4) default bit-vector size used by nla2bv tactic.");
|
||||
r.insert(":nla2bv-root", CPK_UINT, "(default: 2) nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding.");
|
||||
r.insert(":nla2bv-divisor", CPK_UINT, "(default: 2) nla2bv tactic parameter.");
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Modify a goal to use bounded bit-vector
|
||||
arithmetic in place of non-linear integer arithmetic.
|
||||
\return false if transformation is not possible.
|
||||
*/
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
fail_if_proof_generation("nla2bv", g);
|
||||
fail_if_unsat_core_generation("nla2bv", g);
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
|
||||
imp proc(g->m(), m_params);
|
||||
scoped_set_imp setter(*this, proc);
|
||||
proc(*(g.get()), mc);
|
||||
|
||||
result.push_back(g.get());
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
|
||||
virtual void cleanup(void) {
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p) {
|
||||
return alloc(nla2bv_tactic, p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
30
src/tactic/arith_tactics/nla2bv_tactic.h
Normal file
30
src/tactic/arith_tactics/nla2bv_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
nla2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert quantified NIA problems to bounded bit-vector arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj (nbjorner) 2011-05-3
|
||||
|
||||
Notes:
|
||||
Ported to tactic framework on 2012-02-28
|
||||
|
||||
--*/
|
||||
#ifndef _NLA2BV_TACTIC_H_
|
||||
#define _NLA2BV_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
||||
|
211
src/tactic/arith_tactics/normalize_bounds_tactic.cpp
Normal file
211
src/tactic/arith_tactics/normalize_bounds_tactic.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
normalize_bounds_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Replace x with x' + l, when l <= x
|
||||
where x' is a fresh variable.
|
||||
Note that, after the transformation 0 <= x'.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-10-21.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"bound_manager.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class normalize_bounds_tactic : public tactic {
|
||||
struct imp {
|
||||
ast_manager & m;
|
||||
bound_manager m_bm;
|
||||
arith_util m_util;
|
||||
th_rewriter m_rw;
|
||||
bool m_normalize_int_only;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_bm(m),
|
||||
m_util(m),
|
||||
m_rw(m, p) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
m_normalize_int_only = p.get_bool(":norm-int-only", true);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
bool is_target(expr * var, rational & val) {
|
||||
bool strict;
|
||||
return
|
||||
is_uninterp_const(var) &&
|
||||
(!m_normalize_int_only || m_util.is_int(var)) &&
|
||||
m_bm.has_lower(var, val, strict) &&
|
||||
!val.is_zero();
|
||||
}
|
||||
|
||||
bool is_target(expr * var) {
|
||||
rational val;
|
||||
return is_target(var, val);
|
||||
}
|
||||
|
||||
bool has_lowers() {
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
TRACE("normalize_bounds_tactic",
|
||||
rational val; bool strict;
|
||||
tout << mk_ismt2_pp(*it, m) << " has_lower: " << m_bm.has_lower(*it, val, strict) << " val: " << val << "\n";);
|
||||
if (is_target(*it))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
mc = 0; pc = 0; core = 0;
|
||||
bool produce_models = in->models_enabled();
|
||||
bool produce_proofs = in->proofs_enabled();
|
||||
tactic_report report("normalize-bounds", *in);
|
||||
|
||||
m_bm(*in);
|
||||
|
||||
if (!has_lowers()) {
|
||||
result.push_back(in.get());
|
||||
// did not increase depth since it didn't do anything.
|
||||
return;
|
||||
}
|
||||
|
||||
extension_model_converter * mc1 = 0;
|
||||
filter_model_converter * mc2 = 0;
|
||||
if (produce_models) {
|
||||
mc1 = alloc(extension_model_converter, m);
|
||||
mc2 = alloc(filter_model_converter, m);
|
||||
mc = concat(mc2, mc1);
|
||||
}
|
||||
|
||||
unsigned num_norm_bounds = 0;
|
||||
expr_substitution subst(m);
|
||||
rational val;
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
expr * x = *it;
|
||||
if (is_target(x, val)) {
|
||||
num_norm_bounds++;
|
||||
sort * s = m.get_sort(x);
|
||||
app * x_prime = m.mk_fresh_const(0, s);
|
||||
expr * def = m_util.mk_add(x_prime, m_util.mk_numeral(val, s));
|
||||
subst.insert(x, def);
|
||||
if (produce_models) {
|
||||
mc1->insert(to_app(x)->get_decl(), def);
|
||||
mc2->insert(x_prime->get_decl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report_tactic_progress(":normalized-bounds", num_norm_bounds);
|
||||
|
||||
m_rw.set_substitution(&subst);
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = in->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = in->form(idx);
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
if (produce_proofs) {
|
||||
proof * pr = in->pr(idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
in->update(idx, new_curr, new_pr, in->dep(idx));
|
||||
}
|
||||
TRACE("normalize_bounds_tactic", in->display(tout););
|
||||
in->inc_depth();
|
||||
result.push_back(in.get());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
|
||||
public:
|
||||
normalize_bounds_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(normalize_bounds_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~normalize_bounds_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
insert_produce_models(r);
|
||||
r.insert(":norm-int-only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants.");
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(in, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_normalize_bounds_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(normalize_bounds_tactic, m, p));
|
||||
}
|
30
src/tactic/arith_tactics/normalize_bounds_tactic.h
Normal file
30
src/tactic/arith_tactics/normalize_bounds_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
normalize_bounds_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Replace x with x' + l, when l <= x
|
||||
where x' is a fresh variable.
|
||||
Note that, after the transformation 0 <= x'.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-10-21.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _NORMALIZE_BOUNDS_TACTIC_H_
|
||||
#define _NORMALIZE_BOUNDS_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_normalize_bounds_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
102
src/tactic/arith_tactics/pb2bv_model_converter.cpp
Normal file
102
src/tactic/arith_tactics/pb2bv_model_converter.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_model_converter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Model converter for the pb2bv tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Christoph (cwinter) 2012-02-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"trace.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"model_v2_pp.h"
|
||||
#include"pb2bv_model_converter.h"
|
||||
|
||||
pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m, obj_map<func_decl, expr*> const & c2bit, bound_manager const & bm):
|
||||
m(_m) {
|
||||
obj_map<func_decl, expr*>::iterator it = c2bit.begin();
|
||||
obj_map<func_decl, expr*>::iterator end = c2bit.end();
|
||||
for ( ; it != end; it++) {
|
||||
m_c2bit.push_back(func_decl_pair(it->m_key, to_app(it->m_value)->get_decl()));
|
||||
m.inc_ref(it->m_key);
|
||||
m.inc_ref(to_app(it->m_value)->get_decl());
|
||||
}
|
||||
bound_manager::iterator it2 = bm.begin();
|
||||
bound_manager::iterator end2 = bm.end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
expr * c = *it2;
|
||||
SASSERT(is_uninterp_const(c));
|
||||
func_decl * d = to_app(c)->get_decl();
|
||||
if (!c2bit.contains(d)) {
|
||||
SASSERT(d->get_arity() == 0);
|
||||
m.inc_ref(d);
|
||||
m_c2bit.push_back(func_decl_pair(d, static_cast<func_decl*>(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pb2bv_model_converter::~pb2bv_model_converter() {
|
||||
svector<func_decl_pair>::const_iterator it = m_c2bit.begin();
|
||||
svector<func_decl_pair>::const_iterator end = m_c2bit.end();
|
||||
for (; it != end; ++it) {
|
||||
m.dec_ref(it->first);
|
||||
m.dec_ref(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void pb2bv_model_converter::operator()(model_ref & md) {
|
||||
(*this)(md, 0);
|
||||
}
|
||||
|
||||
void pb2bv_model_converter::operator()(model_ref & md, unsigned goal_idx) {
|
||||
SASSERT(goal_idx == 0);
|
||||
TRACE("pb2bv", tout << "converting model:\n"; model_v2_pp(tout, *md); display(tout););
|
||||
arith_util a_util(m);
|
||||
|
||||
svector<func_decl_pair>::const_iterator it = m_c2bit.begin();
|
||||
svector<func_decl_pair>::const_iterator end = m_c2bit.end();
|
||||
for (; it != end; ++it) {
|
||||
if (it->second) {
|
||||
expr * val = md->get_const_interp(it->second);
|
||||
if (val == 0 || m.is_false(val)) {
|
||||
/* false's and don't cares get the integer 0 solution*/
|
||||
md->register_decl(it->first, a_util.mk_numeral(rational(0), true));
|
||||
}
|
||||
else {
|
||||
md->register_decl(it->first, a_util.mk_numeral(rational(1), true));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// it->first is a don't care.
|
||||
md->register_decl(it->first, a_util.mk_numeral(rational(0), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pb2bv_model_converter::display(std::ostream & out) {
|
||||
out << "(pb2bv-model-converter";
|
||||
svector<func_decl_pair>::const_iterator it = m_c2bit.begin();
|
||||
svector<func_decl_pair>::const_iterator end = m_c2bit.end();
|
||||
for (; it != end; ++it) {
|
||||
out << "\n (" << it->first->get_name() << " ";
|
||||
if (it->second == 0)
|
||||
out << "0";
|
||||
else
|
||||
out << it->second->get_name();
|
||||
out << ")";
|
||||
}
|
||||
out << ")\n";
|
||||
}
|
||||
|
||||
model_converter * pb2bv_model_converter::translate(ast_translation & translator) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
}
|
39
src/tactic/arith_tactics/pb2bv_model_converter.h
Normal file
39
src/tactic/arith_tactics/pb2bv_model_converter.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_model_converter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Model converter for the pb2bv tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Christoph (cwinter) 2012-02-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PB2BV_MODEL_CONVERTER_
|
||||
#define _PB2BV_MODEL_CONVERTER_
|
||||
|
||||
#include"model_converter.h"
|
||||
#include"bound_manager.h"
|
||||
|
||||
class pb2bv_model_converter : public model_converter {
|
||||
typedef std::pair<func_decl *, func_decl *> func_decl_pair;
|
||||
|
||||
ast_manager & m;
|
||||
svector<func_decl_pair> m_c2bit;
|
||||
public:
|
||||
pb2bv_model_converter(ast_manager & _m, obj_map<func_decl, expr*> const & c2bit, bound_manager const & bm);
|
||||
virtual ~pb2bv_model_converter();
|
||||
virtual void operator()(model_ref & md);
|
||||
virtual void operator()(model_ref & md, unsigned goal_idx);
|
||||
virtual void display(std::ostream & out);
|
||||
virtual model_converter * translate(ast_translation & translator);
|
||||
};
|
||||
|
||||
#endif
|
1051
src/tactic/arith_tactics/pb2bv_tactic.cpp
Normal file
1051
src/tactic/arith_tactics/pb2bv_tactic.cpp
Normal file
File diff suppressed because it is too large
Load diff
30
src/tactic/arith_tactics/pb2bv_tactic.h
Normal file
30
src/tactic/arith_tactics/pb2bv_tactic.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for converting Pseudo-Boolean constraints to BV
|
||||
|
||||
Author:
|
||||
|
||||
Christoph (cwinter) 2012-02-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PB2BV_TACTIC_
|
||||
#define _PB2BV_TACTIC_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_pb2bv_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
probe * mk_is_pb_probe();
|
||||
|
||||
#endif
|
421
src/tactic/arith_tactics/probe_arith.cpp
Normal file
421
src/tactic/arith_tactics/probe_arith.cpp
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
probe_arith.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Some probes for arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-03-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"probe.h"
|
||||
#include"expr2polynomial.h"
|
||||
#include"for_each_expr.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"goal_util.h"
|
||||
|
||||
class arith_degree_probe : public probe {
|
||||
struct proc {
|
||||
ast_manager & m;
|
||||
unsynch_mpq_manager m_qm;
|
||||
polynomial::manager m_pm;
|
||||
default_expr2polynomial m_expr2poly;
|
||||
arith_util m_util;
|
||||
unsigned m_max_degree;
|
||||
unsigned long long m_acc_degree;
|
||||
unsigned m_counter;
|
||||
|
||||
proc(ast_manager & _m):m(_m), m_pm(m_qm), m_expr2poly(m, m_pm), m_util(m) {
|
||||
m_max_degree = 0;
|
||||
m_acc_degree = 0;
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
void updt_degree(polynomial_ref const & p) {
|
||||
unsigned deg = m_pm.total_degree(p);
|
||||
if (deg > m_max_degree)
|
||||
m_max_degree = deg;
|
||||
m_acc_degree += deg;
|
||||
m_counter++;
|
||||
}
|
||||
|
||||
void process(app * n) {
|
||||
expr * lhs = n->get_arg(0);
|
||||
expr * rhs = n->get_arg(1);
|
||||
polynomial_ref p1(m_pm);
|
||||
polynomial_ref p2(m_pm);
|
||||
scoped_mpz d1(m_qm);
|
||||
scoped_mpz d2(m_qm);
|
||||
m_expr2poly.to_polynomial(lhs, p1, d1);
|
||||
m_expr2poly.to_polynomial(rhs, p2, d2);
|
||||
updt_degree(p1);
|
||||
updt_degree(p2);
|
||||
}
|
||||
|
||||
void operator()(var * n) {}
|
||||
void operator()(quantifier * n) {}
|
||||
void operator()(app * n) {
|
||||
if (m_util.is_le(n) || m_util.is_lt(n) || m_util.is_gt(n) || m_util.is_ge(n))
|
||||
process(n);
|
||||
if (m.is_eq(n) && m_util.is_int_real(n->get_arg(0)))
|
||||
process(n);
|
||||
}
|
||||
};
|
||||
|
||||
bool m_avg;
|
||||
public:
|
||||
arith_degree_probe(bool avg):m_avg(avg) {}
|
||||
|
||||
virtual result operator()(goal const & g) {
|
||||
proc p(g.m());
|
||||
for_each_expr_at(p, g);
|
||||
if (m_avg)
|
||||
return p.m_counter == 0 ? 0.0 : static_cast<double>(p.m_acc_degree)/static_cast<double>(p.m_counter);
|
||||
else
|
||||
return p.m_max_degree;
|
||||
}
|
||||
};
|
||||
|
||||
class arith_bw_probe : public probe {
|
||||
struct proc {
|
||||
ast_manager & m;
|
||||
arith_util m_util;
|
||||
unsigned m_max_bw;
|
||||
unsigned long long m_acc_bw;
|
||||
unsigned m_counter;
|
||||
|
||||
proc(ast_manager & _m):m(_m), m_util(m) {
|
||||
m_max_bw = 0;
|
||||
m_acc_bw = 0;
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
void operator()(var * n) {}
|
||||
void operator()(quantifier * n) {}
|
||||
void operator()(app * n) {
|
||||
rational val;
|
||||
if (m_util.is_numeral(n, val)) {
|
||||
unsigned bw = val.bitsize();
|
||||
if (bw > m_max_bw)
|
||||
m_max_bw = bw;
|
||||
m_acc_bw += bw;
|
||||
m_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool m_avg;
|
||||
public:
|
||||
arith_bw_probe(bool avg):m_avg(avg) {}
|
||||
|
||||
virtual result operator()(goal const & g) {
|
||||
proc p(g.m());
|
||||
for_each_expr_at(p, g);
|
||||
if (m_avg)
|
||||
return p.m_counter == 0 ? 0.0 : static_cast<double>(p.m_acc_bw)/static_cast<double>(p.m_counter);
|
||||
else
|
||||
return p.m_max_bw;
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_arith_avg_degree_probe() {
|
||||
return alloc(arith_degree_probe, true);
|
||||
}
|
||||
|
||||
probe * mk_arith_max_degree_probe() {
|
||||
return alloc(arith_degree_probe, false);
|
||||
}
|
||||
|
||||
probe * mk_arith_avg_bw_probe() {
|
||||
return alloc(arith_bw_probe, true);
|
||||
}
|
||||
|
||||
probe * mk_arith_max_bw_probe() {
|
||||
return alloc(arith_bw_probe, false);
|
||||
}
|
||||
|
||||
struct is_non_qflira_functor {
|
||||
struct found {};
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
bool m_int;
|
||||
bool m_real;
|
||||
|
||||
is_non_qflira_functor(ast_manager & _m, bool _int, bool _real):m(_m), u(m), m_int(_int), m_real(_real) {}
|
||||
|
||||
void operator()(var *) { throw found(); }
|
||||
|
||||
void operator()(quantifier *) { throw found(); }
|
||||
|
||||
bool compatible_sort(app * n) const {
|
||||
if (m.is_bool(n))
|
||||
return true;
|
||||
if (m_int && u.is_int(n))
|
||||
return true;
|
||||
if (m_real && u.is_real(n))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void operator()(app * n) {
|
||||
if (!compatible_sort(n))
|
||||
throw found();
|
||||
family_id fid = n->get_family_id();
|
||||
if (fid == m.get_basic_family_id())
|
||||
return;
|
||||
if (fid == u.get_family_id()) {
|
||||
switch (n->get_decl_kind()) {
|
||||
case OP_LE: case OP_GE: case OP_LT: case OP_GT:
|
||||
case OP_ADD: case OP_NUM:
|
||||
return;
|
||||
case OP_MUL:
|
||||
if (n->get_num_args() != 2)
|
||||
throw found();
|
||||
if (!u.is_numeral(n->get_arg(0)))
|
||||
throw found();
|
||||
return;
|
||||
case OP_TO_REAL:
|
||||
if (!m_real)
|
||||
throw found();
|
||||
break;
|
||||
default:
|
||||
throw found();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (is_uninterp_const(n))
|
||||
return;
|
||||
throw found();
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qflia(goal const & g) {
|
||||
is_non_qflira_functor p(g.m(), true, false);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qflia_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qflia(g);
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qflra(goal const & g) {
|
||||
is_non_qflira_functor p(g.m(), false, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qflra_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qflra(g);
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qflira(goal const & g) {
|
||||
is_non_qflira_functor p(g.m(), true, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qflira_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qflira(g);
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_lp(goal const & g) {
|
||||
ast_manager & m = g.m();
|
||||
arith_util u(m);
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g.form(i);
|
||||
bool sign = false;
|
||||
while (m.is_not(f, f))
|
||||
sign = !sign;
|
||||
if (m.is_eq(f) && !sign) {
|
||||
if (m.get_sort(to_app(f)->get_arg(0))->get_family_id() != u.get_family_id())
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (u.is_le(f) || u.is_ge(f) || u.is_lt(f) || u.is_gt(f))
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_ilp(goal const & g) {
|
||||
if (!is_qflia(g))
|
||||
return false;
|
||||
if (has_term_ite(g))
|
||||
return false;
|
||||
return is_lp(g);
|
||||
}
|
||||
|
||||
static bool is_mip(goal const & g) {
|
||||
if (!is_qflira(g))
|
||||
return false;
|
||||
if (has_term_ite(g))
|
||||
return false;
|
||||
return is_lp(g);
|
||||
}
|
||||
|
||||
class is_ilp_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_ilp(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_mip_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_mip(g);
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_is_qflia_probe() {
|
||||
return alloc(is_qflia_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_qflra_probe() {
|
||||
return alloc(is_qflra_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_qflira_probe() {
|
||||
return alloc(is_qflira_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_ilp_probe() {
|
||||
return alloc(is_ilp_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_mip_probe() {
|
||||
return alloc(is_mip_probe);
|
||||
}
|
||||
|
||||
|
||||
struct is_non_nira_functor {
|
||||
struct found {};
|
||||
ast_manager & m;
|
||||
arith_util u;
|
||||
bool m_int;
|
||||
bool m_real;
|
||||
bool m_quant;
|
||||
|
||||
is_non_nira_functor(ast_manager & _m, bool _int, bool _real, bool _quant):m(_m), u(m), m_int(_int), m_real(_real), m_quant(_quant) {}
|
||||
|
||||
void operator()(var * x) {
|
||||
if (!m_quant)
|
||||
throw found();
|
||||
sort * s = x->get_sort();
|
||||
if (m_int && u.is_int(s))
|
||||
return;
|
||||
if (m_real && u.is_real(s))
|
||||
return;
|
||||
throw found();
|
||||
}
|
||||
|
||||
void operator()(quantifier *) {
|
||||
if (!m_quant)
|
||||
throw found();
|
||||
}
|
||||
|
||||
bool compatible_sort(app * n) const {
|
||||
if (m.is_bool(n))
|
||||
return true;
|
||||
if (m_int && u.is_int(n))
|
||||
return true;
|
||||
if (m_real && u.is_real(n))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void operator()(app * n) {
|
||||
if (!compatible_sort(n))
|
||||
throw found();
|
||||
family_id fid = n->get_family_id();
|
||||
if (fid == m.get_basic_family_id())
|
||||
return;
|
||||
if (fid == u.get_family_id())
|
||||
return;
|
||||
if (is_uninterp_const(n))
|
||||
return;
|
||||
throw found();
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_qfnia(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), true, false, false);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
static bool is_qfnra(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), false, true, false);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
static bool is_nia(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), true, false, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
static bool is_nra(goal const & g) {
|
||||
is_non_nira_functor p(g.m(), false, true, true);
|
||||
return !test(g, p);
|
||||
}
|
||||
|
||||
class is_qfnia_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qfnia(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_qfnra_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_qfnra(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_nia_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_nia(g);
|
||||
}
|
||||
};
|
||||
|
||||
class is_nra_probe : public probe {
|
||||
public:
|
||||
virtual result operator()(goal const & g) {
|
||||
return is_nra(g);
|
||||
}
|
||||
};
|
||||
|
||||
probe * mk_is_qfnia_probe() {
|
||||
return alloc(is_qfnia_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_qfnra_probe() {
|
||||
return alloc(is_qfnra_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_nia_probe() {
|
||||
return alloc(is_nia_probe);
|
||||
}
|
||||
|
||||
probe * mk_is_nra_probe() {
|
||||
return alloc(is_nra_probe);
|
||||
}
|
39
src/tactic/arith_tactics/probe_arith.h
Normal file
39
src/tactic/arith_tactics/probe_arith.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
probe_arith.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Some probes for arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-03-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _PROBE_ARITH_H_
|
||||
#define _PROBE_ARITH_H_
|
||||
|
||||
class probe;
|
||||
probe * mk_arith_avg_bw_probe();
|
||||
probe * mk_arith_max_bw_probe();
|
||||
probe * mk_arith_avg_degree_probe();
|
||||
probe * mk_arith_max_degree_probe();
|
||||
|
||||
probe * mk_is_qflia_probe();
|
||||
probe * mk_is_qflra_probe();
|
||||
probe * mk_is_qflira_probe();
|
||||
probe * mk_is_ilp_probe();
|
||||
probe * mk_is_mip_probe();
|
||||
|
||||
probe * mk_is_qfnia_probe();
|
||||
probe * mk_is_qfnra_probe();
|
||||
probe * mk_is_nia_probe();
|
||||
probe * mk_is_nra_probe();
|
||||
|
||||
#endif
|
563
src/tactic/arith_tactics/propagate_ineqs_tactic.cpp
Normal file
563
src/tactic/arith_tactics/propagate_ineqs_tactic.cpp
Normal file
|
@ -0,0 +1,563 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
propagate_ineqs_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
This tactic performs the following tasks:
|
||||
|
||||
- Propagate bounds using the bound_propagator.
|
||||
- Eliminate subsumed inequalities.
|
||||
For example:
|
||||
x - y >= 3
|
||||
can be replaced with true if we know that
|
||||
x >= 3 and y <= 0
|
||||
|
||||
- Convert inequalities of the form p <= k and p >= k into p = k,
|
||||
where p is a polynomial and k is a constant.
|
||||
|
||||
This strategy assumes the input is in arith LHS mode.
|
||||
This can be achieved by using option :arith-lhs true in the
|
||||
simplifier.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-19
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"bound_propagator.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"simplify_tactic.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class propagate_ineqs_tactic : public tactic {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
propagate_ineqs_tactic(ast_manager & m, params_ref const & p);
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(propagate_ineqs_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~propagate_ineqs_tactic();
|
||||
|
||||
virtual void updt_params(params_ref const & p);
|
||||
virtual void collect_param_descrs(param_descrs & r) {}
|
||||
|
||||
virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core);
|
||||
|
||||
virtual void cleanup();
|
||||
protected:
|
||||
virtual void set_cancel(bool f);
|
||||
};
|
||||
|
||||
tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(propagate_ineqs_tactic, m, p));
|
||||
}
|
||||
|
||||
struct propagate_ineqs_tactic::imp {
|
||||
ast_manager & m;
|
||||
unsynch_mpq_manager nm;
|
||||
small_object_allocator m_allocator;
|
||||
bound_propagator bp;
|
||||
arith_util m_util;
|
||||
typedef bound_propagator::var a_var;
|
||||
obj_map<expr, a_var> m_expr2var;
|
||||
expr_ref_vector m_var2expr;
|
||||
|
||||
typedef numeral_buffer<mpq, unsynch_mpq_manager> mpq_buffer;
|
||||
typedef svector<a_var> var_buffer;
|
||||
|
||||
mpq_buffer m_num_buffer;
|
||||
var_buffer m_var_buffer;
|
||||
goal_ref m_new_goal;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_allocator("ineq-simplifier"),
|
||||
bp(nm, m_allocator, p),
|
||||
m_util(m),
|
||||
m_var2expr(m),
|
||||
m_num_buffer(nm) {
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
updt_params_core(p);
|
||||
bp.updt_params(p);
|
||||
}
|
||||
|
||||
void display_bounds(std::ostream & out) {
|
||||
unsigned sz = m_var2expr.size();
|
||||
mpq k;
|
||||
bool strict;
|
||||
unsigned ts;
|
||||
for (unsigned x = 0; x < sz; x++) {
|
||||
if (bp.lower(x, k, strict, ts))
|
||||
out << nm.to_string(k) << " " << (strict ? "<" : "<=");
|
||||
else
|
||||
out << "-oo <";
|
||||
out << " " << mk_ismt2_pp(m_var2expr.get(x), m) << " ";
|
||||
if (bp.upper(x, k, strict, ts))
|
||||
out << (strict ? "<" : "<=") << " " << nm.to_string(k);
|
||||
else
|
||||
out << "< oo";
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
a_var mk_var(expr * t) {
|
||||
if (m_util.is_to_real(t))
|
||||
t = to_app(t)->get_arg(0);
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = m_var2expr.size();
|
||||
bp.mk_var(x, m_util.is_int(t));
|
||||
m_var2expr.push_back(t);
|
||||
m_expr2var.insert(t, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
void expr2linear_pol(expr * t, mpq_buffer & as, var_buffer & xs) {
|
||||
mpq c_mpq_val;
|
||||
if (m_util.is_add(t)) {
|
||||
rational c_val;
|
||||
unsigned num = to_app(t)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * mon = to_app(t)->get_arg(i);
|
||||
expr * c, * x;
|
||||
if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) {
|
||||
nm.set(c_mpq_val, c_val.to_mpq());
|
||||
as.push_back(c_mpq_val);
|
||||
xs.push_back(mk_var(x));
|
||||
}
|
||||
else {
|
||||
as.push_back(mpq(1));
|
||||
xs.push_back(mk_var(mon));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
as.push_back(mpq(1));
|
||||
xs.push_back(mk_var(t));
|
||||
}
|
||||
nm.del(c_mpq_val);
|
||||
}
|
||||
|
||||
a_var mk_linear_pol(expr * t) {
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = mk_var(t);
|
||||
if (m_util.is_add(t)) {
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(t, m_num_buffer, m_var_buffer);
|
||||
m_num_buffer.push_back(mpq(-1));
|
||||
m_var_buffer.push_back(x);
|
||||
bp.mk_eq(m_num_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr());
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
enum kind { EQ, LE, GE };
|
||||
|
||||
bool process(expr * t) {
|
||||
bool sign = false;
|
||||
while (m.is_not(t, t))
|
||||
sign = !sign;
|
||||
bool strict = false;
|
||||
kind k;
|
||||
if (m.is_eq(t)) {
|
||||
if (sign)
|
||||
return false;
|
||||
k = EQ;
|
||||
}
|
||||
else if (m_util.is_le(t)) {
|
||||
if (sign) {
|
||||
k = GE;
|
||||
strict = true;
|
||||
}
|
||||
else {
|
||||
k = LE;
|
||||
}
|
||||
}
|
||||
else if (m_util.is_ge(t)) {
|
||||
if (sign) {
|
||||
k = LE;
|
||||
strict = true;
|
||||
}
|
||||
else {
|
||||
k = GE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
expr * lhs = to_app(t)->get_arg(0);
|
||||
expr * rhs = to_app(t)->get_arg(1);
|
||||
if (m_util.is_numeral(lhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
if (k == LE)
|
||||
k = GE;
|
||||
else if (k == GE)
|
||||
k = LE;
|
||||
}
|
||||
|
||||
rational c;
|
||||
if (!m_util.is_numeral(rhs, c))
|
||||
return false;
|
||||
a_var x = mk_linear_pol(lhs);
|
||||
mpq c_prime;
|
||||
nm.set(c_prime, c.to_mpq());
|
||||
if (k == EQ) {
|
||||
SASSERT(!strict);
|
||||
bp.assert_lower(x, c_prime, false);
|
||||
bp.assert_upper(x, c_prime, false);
|
||||
}
|
||||
else if (k == LE) {
|
||||
bp.assert_upper(x, c_prime, strict);
|
||||
}
|
||||
else {
|
||||
SASSERT(k == GE);
|
||||
bp.assert_lower(x, c_prime, strict);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collect_bounds(goal const & g) {
|
||||
bool found = false;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * t = g.form(i);
|
||||
if (process(t))
|
||||
found = true;
|
||||
else
|
||||
m_new_goal->assert_expr(t); // save non-bounds here
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool lower_subsumed(expr * p, mpq const & k, bool strict) {
|
||||
if (!m_util.is_add(p))
|
||||
return false;
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(p, m_num_buffer, m_var_buffer);
|
||||
mpq implied_k;
|
||||
bool implied_strict;
|
||||
bool result =
|
||||
bp.lower(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) &&
|
||||
(nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
|
||||
nm.del(implied_k);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool upper_subsumed(expr * p, mpq const & k, bool strict) {
|
||||
if (!m_util.is_add(p))
|
||||
return false;
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(p, m_num_buffer, m_var_buffer);
|
||||
mpq implied_k;
|
||||
bool implied_strict;
|
||||
bool result =
|
||||
bp.upper(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) &&
|
||||
(nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
|
||||
nm.del(implied_k);
|
||||
return result;
|
||||
}
|
||||
|
||||
void restore_bounds() {
|
||||
mpq l, u;
|
||||
bool strict_l, strict_u, has_l, has_u;
|
||||
unsigned ts;
|
||||
unsigned sz = m_var2expr.size();
|
||||
for (unsigned x = 0; x < sz; x++) {
|
||||
expr * p = m_var2expr.get(x);
|
||||
has_l = bp.lower(x, l, strict_l, ts);
|
||||
has_u = bp.upper(x, u, strict_u, ts);
|
||||
if (!has_l && !has_u)
|
||||
continue;
|
||||
if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) {
|
||||
// l <= p <= l --> p = l
|
||||
m_new_goal->assert_expr(m.mk_eq(p, m_util.mk_numeral(rational(l), m_util.is_int(p))));
|
||||
continue;
|
||||
}
|
||||
if (has_l && !lower_subsumed(p, l, strict_l)) {
|
||||
if (strict_l)
|
||||
m_new_goal->assert_expr(m.mk_not(m_util.mk_le(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))));
|
||||
else
|
||||
m_new_goal->assert_expr(m_util.mk_ge(p, m_util.mk_numeral(rational(l), m_util.is_int(p))));
|
||||
}
|
||||
if (has_u && !upper_subsumed(p, u, strict_u)) {
|
||||
if (strict_u)
|
||||
m_new_goal->assert_expr(m.mk_not(m_util.mk_ge(p, m_util.mk_numeral(rational(u), m_util.is_int(p)))));
|
||||
else
|
||||
m_new_goal->assert_expr(m_util.mk_le(p, m_util.mk_numeral(rational(u), m_util.is_int(p))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_x_minus_y_eq_0(expr * t, expr * & x, expr * & y) {
|
||||
expr * lhs, * rhs, * m1, * m2;
|
||||
if (m.is_eq(t, lhs, rhs) && m_util.is_zero(rhs) && m_util.is_add(lhs, m1, m2)) {
|
||||
if (m_util.is_times_minus_one(m2, y) && is_uninterp_const(m1)) {
|
||||
x = m1;
|
||||
return true;
|
||||
}
|
||||
if (m_util.is_times_minus_one(m1, y) && is_uninterp_const(m2)) {
|
||||
x = m2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_unbounded(expr * t) {
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return !bp.has_lower(x) && !bp.has_upper(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lower(expr * t, mpq & k, bool & strict) {
|
||||
unsigned ts;
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return bp.lower(x, k, strict, ts);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool upper(expr * t, mpq & k, bool & strict) {
|
||||
unsigned ts;
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return bp.upper(x, k, strict, ts);
|
||||
return false;
|
||||
}
|
||||
|
||||
void find_ite_bounds(expr * root) {
|
||||
TRACE("find_ite_bounds_bug", display_bounds(tout););
|
||||
expr * n = root;
|
||||
expr * target = 0;
|
||||
expr * c, * t, * e;
|
||||
expr * x, * y;
|
||||
bool has_l, has_u;
|
||||
mpq l_min, u_max;
|
||||
bool l_strict, u_strict;
|
||||
mpq curr;
|
||||
bool curr_strict;
|
||||
while (true) {
|
||||
TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";);
|
||||
|
||||
if (m.is_ite(n, c, t, e)) {
|
||||
if (is_x_minus_y_eq_0(t, x, y))
|
||||
n = e;
|
||||
else if (is_x_minus_y_eq_0(e, x, y))
|
||||
n = t;
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (is_x_minus_y_eq_0(n, x, y)) {
|
||||
n = 0;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n";
|
||||
if (target) {
|
||||
tout << "target: " << mk_ismt2_pp(target, m) << "\n";
|
||||
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n";
|
||||
});
|
||||
|
||||
if (is_unbounded(y))
|
||||
std::swap(x, y);
|
||||
|
||||
if (!is_unbounded(x)) {
|
||||
TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";);
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == 0) {
|
||||
target = x;
|
||||
if (lower(y, curr, curr_strict)) {
|
||||
has_l = true;
|
||||
nm.set(l_min, curr);
|
||||
l_strict = curr_strict;
|
||||
}
|
||||
else {
|
||||
has_l = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
|
||||
}
|
||||
if (upper(y, curr, curr_strict)) {
|
||||
has_u = true;
|
||||
nm.set(u_max, curr);
|
||||
u_strict = curr_strict;
|
||||
}
|
||||
else {
|
||||
has_u = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
|
||||
}
|
||||
}
|
||||
else if (target == x) {
|
||||
if (has_l) {
|
||||
if (lower(y, curr, curr_strict)) {
|
||||
if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) {
|
||||
nm.set(l_min, curr);
|
||||
l_strict = curr_strict;
|
||||
}
|
||||
}
|
||||
else {
|
||||
has_l = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
|
||||
}
|
||||
}
|
||||
if (has_u) {
|
||||
if (upper(y, curr, curr_strict)) {
|
||||
if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) {
|
||||
nm.set(u_max, curr);
|
||||
u_strict = curr_strict;
|
||||
}
|
||||
}
|
||||
else {
|
||||
has_u = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!has_l && !has_u)
|
||||
break;
|
||||
|
||||
if (n == 0) {
|
||||
TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n";
|
||||
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n";
|
||||
tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n";
|
||||
tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";);
|
||||
a_var x = mk_var(target);
|
||||
if (has_l)
|
||||
bp.assert_lower(x, l_min, l_strict);
|
||||
if (has_u)
|
||||
bp.assert_upper(x, u_max, u_strict);
|
||||
break;
|
||||
}
|
||||
}
|
||||
nm.del(l_min);
|
||||
nm.del(u_max);
|
||||
nm.del(curr);
|
||||
}
|
||||
|
||||
void find_ite_bounds() {
|
||||
unsigned sz = m_new_goal->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = m_new_goal->form(i);
|
||||
if (m.is_ite(f))
|
||||
find_ite_bounds(to_app(f));
|
||||
}
|
||||
bp.propagate();
|
||||
TRACE("find_ite_bounds", display_bounds(tout););
|
||||
}
|
||||
|
||||
void operator()(goal * g, goal_ref & r) {
|
||||
tactic_report report("propagate-ineqs", *g);
|
||||
|
||||
m_new_goal = alloc(goal, *g, true);
|
||||
m_new_goal->inc_depth();
|
||||
r = m_new_goal.get();
|
||||
if (!collect_bounds(*g)) {
|
||||
m_new_goal = 0;
|
||||
r = g;
|
||||
return; // nothing to be done
|
||||
}
|
||||
|
||||
TRACE("propagate_ineqs_tactic", g->display(tout); display_bounds(tout); tout << "bound propagator:\n"; bp.display(tout););
|
||||
|
||||
bp.propagate();
|
||||
|
||||
report_tactic_progress(":bound-propagations", bp.get_num_propagations());
|
||||
report_tactic_progress(":bound-false-alarms", bp.get_num_false_alarms());
|
||||
|
||||
if (bp.inconsistent()) {
|
||||
r->reset();
|
||||
r->assert_expr(m.mk_false());
|
||||
return;
|
||||
}
|
||||
|
||||
// find_ite_bounds(); // did not help
|
||||
|
||||
restore_bounds();
|
||||
|
||||
TRACE("propagate_ineqs_tactic", tout << "after propagation:\n"; display_bounds(tout); bp.display(tout););
|
||||
TRACE("propagate_ineqs_tactic", r->display(tout););
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
// TODO
|
||||
}
|
||||
};
|
||||
|
||||
propagate_ineqs_tactic::propagate_ineqs_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
propagate_ineqs_tactic::~propagate_ineqs_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
fail_if_proof_generation("propagate-ineqs", g);
|
||||
fail_if_unsat_core_generation("propagate-ineqs", g);
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
goal_ref r;
|
||||
(*m_imp)(g.get(), r);
|
||||
result.push_back(r.get());
|
||||
SASSERT(r->is_well_sorted());
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
42
src/tactic/arith_tactics/propagate_ineqs_tactic.h
Normal file
42
src/tactic/arith_tactics/propagate_ineqs_tactic.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
propagate_ineqs_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
This tactic performs the following tasks:
|
||||
|
||||
- Propagate bounds using the bound_propagator.
|
||||
- Eliminate subsumed inequalities.
|
||||
For example:
|
||||
x - y >= 3
|
||||
can be replaced with true if we know that
|
||||
x >= 3 and y <= 0
|
||||
|
||||
- Convert inequalities of the form p <= k and p >= k into p = k,
|
||||
where p is a polynomial and k is a constant.
|
||||
|
||||
This strategy assumes the input is in arith LHS mode.
|
||||
This can be achieved by using option :arith-lhs true in the
|
||||
simplifier.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-19
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PROPAGATE_INEQS_TACTIC_H_
|
||||
#define _PROPAGATE_INEQS_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
909
src/tactic/arith_tactics/purify_arith_tactic.cpp
Normal file
909
src/tactic/arith_tactics/purify_arith_tactic.cpp
Normal file
|
@ -0,0 +1,909 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
purify_arith_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for eliminating arithmetic operators: DIV, IDIV, MOD,
|
||||
TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM).
|
||||
|
||||
This tactic uses the simplifier for also eliminating:
|
||||
OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"algebraic_numbers.h"
|
||||
#include"nnf_tactic.h"
|
||||
#include"simplify_tactic.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
/*
|
||||
----
|
||||
Some of the rules needed in the conversion are implemented in
|
||||
arith_rewriter.cpp. Here is a summary of these rules:
|
||||
|
||||
(^ t (/ p q)) --> (^ (^ t (/ 1 q)) p)
|
||||
|
||||
(^ t n) --> t*...*t
|
||||
when integer power expansion is requested
|
||||
|
||||
(is-int t) --> t = (to-real (to-int t))
|
||||
|
||||
(rem t1 t2) --> ite(t2 >= 0, (mod t1 t2), -(mod t1 t2))
|
||||
|
||||
----
|
||||
The tactic implements a set of transformation rules. These rules
|
||||
create fresh constants or (existential) variables, and add new
|
||||
constraints to the context.
|
||||
The context is the set of asserted formulas or a quantifier.
|
||||
|
||||
A rule is represented as:
|
||||
|
||||
From --> To | C
|
||||
|
||||
It means, any expression that matches From is replaced by To,
|
||||
and the constraints C are added to the context.
|
||||
For clarity reasons, I write the constraints using ad-hoc notation.
|
||||
|
||||
|
||||
Rules
|
||||
(^ t 0) --> k | t != 0 implies k = 1, t = 0 implies k = 0^0
|
||||
where k is fresh
|
||||
0^0 is a constant used to capture the meaning of (^ 0 0).
|
||||
|
||||
(^ t (/ 1 n)) --> k | t = k^n
|
||||
when n is odd
|
||||
where k is fresh
|
||||
|
||||
(^ t (/ 1 n)) --> k | t >= 0 implies t = k^n, t < 0 implies t = neg-root(t, n)
|
||||
when n is even
|
||||
where k is fresh
|
||||
neg-root is a function symbol used to capture the meaning of a negative root
|
||||
|
||||
(root-obj p(x) i) --> k | p(k) = 0, l < k < u
|
||||
when root object elimination is requested
|
||||
where k is fresh
|
||||
(l, u) is an isolating interval for the i-th root of p.
|
||||
|
||||
(to-int t) --> k | 0 <= to-real(k) - t < 1
|
||||
where k is a fresh integer constant/variable
|
||||
|
||||
(/ t1 t2) --> k | t2 != 0 implies k*t2 = t1, t2 = 0 implies k = div-0(t1)
|
||||
where k is fresh
|
||||
div-0 is a function symbol used to capture the meaning of division by 0.
|
||||
|
||||
Remark: If it can be shown that t2 != 0, then the div-0(t1) function application
|
||||
vanishes from the formula.
|
||||
|
||||
(div t1 t2) --> k1 | t2 = 0 \/ t1 = k1 * t2 + k2,
|
||||
t2 = 0 \/ 0 <= k2,
|
||||
t2 = 0 \/ k2 < |t2|,
|
||||
t2 != 0 \/ k1 = idiv-0(t1),
|
||||
t2 != 0 \/ k2 = mod-0(t1)
|
||||
k1 is a fresh name for (div t1 t2)
|
||||
k2 is a fresh name for (mod t1 t2)
|
||||
|
||||
(mod t1 t2) --> k2 | same constraints as above
|
||||
*/
|
||||
|
||||
struct purify_arith_decls {
|
||||
ast_manager & m;
|
||||
func_decl * m_int_0_pw_0_decl; // decl for: int 0^0
|
||||
func_decl * m_real_0_pw_0_decl; // decl for: rel 0^0
|
||||
func_decl * m_neg_root_decl; // decl for: even root of negative (real) number
|
||||
func_decl * m_div_0_decl; // decl for: x/0
|
||||
func_decl * m_idiv_0_decl; // decl for: div(x, 0)
|
||||
func_decl * m_mod_0_decl; // decl for: mod(x, 0)
|
||||
func_decl * m_asin_u_decl; // decl for: asin(x) when x < -1 or x > 1
|
||||
func_decl * m_acos_u_decl; // decl for: acos(x) when x < -1 or x > 1
|
||||
|
||||
void inc_refs() {
|
||||
m.inc_ref(m_int_0_pw_0_decl);
|
||||
m.inc_ref(m_real_0_pw_0_decl);
|
||||
m.inc_ref(m_neg_root_decl);
|
||||
m.inc_ref(m_div_0_decl);
|
||||
m.inc_ref(m_idiv_0_decl);
|
||||
m.inc_ref(m_mod_0_decl);
|
||||
m.inc_ref(m_asin_u_decl);
|
||||
m.inc_ref(m_acos_u_decl);
|
||||
}
|
||||
|
||||
void dec_refs() {
|
||||
m.dec_ref(m_int_0_pw_0_decl);
|
||||
m.dec_ref(m_real_0_pw_0_decl);
|
||||
m.dec_ref(m_neg_root_decl);
|
||||
m.dec_ref(m_div_0_decl);
|
||||
m.dec_ref(m_idiv_0_decl);
|
||||
m.dec_ref(m_mod_0_decl);
|
||||
m.dec_ref(m_asin_u_decl);
|
||||
m.dec_ref(m_acos_u_decl);
|
||||
}
|
||||
|
||||
purify_arith_decls(arith_util & u):
|
||||
m(u.get_manager()) {
|
||||
sort * i = u.mk_int();
|
||||
sort * r = u.mk_real();
|
||||
m_int_0_pw_0_decl = m.mk_const_decl(symbol("0^0-int"), i);
|
||||
m_real_0_pw_0_decl = m.mk_const_decl(symbol("0^0-real"), r);
|
||||
sort * rr[2] = { r, r };
|
||||
m_neg_root_decl = m.mk_func_decl(symbol("neg-root"), 2, rr, r);
|
||||
m_div_0_decl = m.mk_func_decl(symbol("/0"), 1, &r, r);
|
||||
m_idiv_0_decl = m.mk_func_decl(symbol("div0"), 1, &i, i);
|
||||
m_mod_0_decl = m.mk_func_decl(symbol("mod0"), 1, &i, i);
|
||||
m_asin_u_decl = m.mk_func_decl(symbol("asin-u"), 1, &r, r);
|
||||
m_acos_u_decl = m.mk_func_decl(symbol("acos-u"), 1, &r, r);
|
||||
inc_refs();
|
||||
}
|
||||
|
||||
purify_arith_decls(ast_manager & _m,
|
||||
func_decl * int_0_pw_0,
|
||||
func_decl * real_0_pw_0,
|
||||
func_decl * neg_root,
|
||||
func_decl * div_0,
|
||||
func_decl * idiv_0,
|
||||
func_decl * mod_0,
|
||||
func_decl * asin_u,
|
||||
func_decl * acos_u
|
||||
):
|
||||
m(_m),
|
||||
m_int_0_pw_0_decl(int_0_pw_0),
|
||||
m_real_0_pw_0_decl(real_0_pw_0),
|
||||
m_neg_root_decl(neg_root),
|
||||
m_div_0_decl(div_0),
|
||||
m_idiv_0_decl(idiv_0),
|
||||
m_mod_0_decl(mod_0),
|
||||
m_asin_u_decl(asin_u),
|
||||
m_acos_u_decl(acos_u) {
|
||||
inc_refs();
|
||||
}
|
||||
|
||||
~purify_arith_decls() {
|
||||
dec_refs();
|
||||
}
|
||||
};
|
||||
|
||||
struct purify_arith_proc {
|
||||
arith_util & m_util;
|
||||
purify_arith_decls & m_aux_decls;
|
||||
bool m_produce_proofs;
|
||||
bool m_elim_root_objs;
|
||||
bool m_elim_inverses;
|
||||
bool m_complete;
|
||||
|
||||
purify_arith_proc(arith_util & u, purify_arith_decls & d, bool produce_proofs, bool elim_root_objs, bool elim_inverses, bool complete):
|
||||
m_util(u),
|
||||
m_aux_decls(d),
|
||||
m_produce_proofs(produce_proofs),
|
||||
m_elim_root_objs(elim_root_objs),
|
||||
m_elim_inverses(elim_inverses),
|
||||
m_complete(complete) {
|
||||
}
|
||||
|
||||
arith_util & u() {
|
||||
return m_util;
|
||||
}
|
||||
|
||||
ast_manager & m() {
|
||||
return u().get_manager();
|
||||
}
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
purify_arith_proc & m_owner;
|
||||
obj_map<app, expr*> m_app2fresh;
|
||||
obj_map<app, proof*> m_app2pr;
|
||||
expr_ref_vector m_pinned;
|
||||
expr_ref_vector m_new_cnstrs;
|
||||
proof_ref_vector m_new_cnstr_prs;
|
||||
expr_ref m_subst;
|
||||
proof_ref m_subst_pr;
|
||||
bool m_in_q;
|
||||
unsigned m_var_idx;
|
||||
|
||||
rw_cfg(purify_arith_proc & o, bool in_q):
|
||||
m_owner(o),
|
||||
m_pinned(o.m()),
|
||||
m_new_cnstrs(o.m()),
|
||||
m_new_cnstr_prs(o.m()),
|
||||
m_subst(o.m()),
|
||||
m_subst_pr(o.m()),
|
||||
m_in_q(in_q),
|
||||
m_var_idx(0) {
|
||||
}
|
||||
|
||||
ast_manager & m() { return m_owner.m(); }
|
||||
|
||||
arith_util & u() { return m_owner.u(); }
|
||||
|
||||
bool produce_proofs() const { return m_owner.m_produce_proofs; }
|
||||
bool complete() const { return m_owner.m_complete; }
|
||||
bool elim_root_objs() const { return m_owner.m_elim_root_objs; }
|
||||
bool elim_inverses() const { return m_owner.m_elim_inverses; }
|
||||
|
||||
expr * mk_fresh_var(bool is_int) {
|
||||
if (m_in_q) {
|
||||
unsigned idx = m_var_idx;
|
||||
m_var_idx++;
|
||||
return m().mk_var(idx, is_int ? u().mk_int() : u().mk_real());
|
||||
}
|
||||
else {
|
||||
return m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real());
|
||||
}
|
||||
}
|
||||
|
||||
expr * mk_fresh_real_var() { return mk_fresh_var(false); }
|
||||
|
||||
expr * mk_fresh_int_var() { return mk_fresh_var(true); }
|
||||
|
||||
func_decl * div0_decl() { return m_owner.m_aux_decls.m_div_0_decl; }
|
||||
func_decl * idiv0_decl() { return m_owner.m_aux_decls.m_idiv_0_decl; }
|
||||
func_decl * mod0_decl() { return m_owner.m_aux_decls.m_mod_0_decl; }
|
||||
func_decl * int_0_pw_0_decl() { return m_owner.m_aux_decls.m_int_0_pw_0_decl; }
|
||||
func_decl * real_0_pw_0_decl() { return m_owner.m_aux_decls.m_real_0_pw_0_decl; }
|
||||
func_decl * neg_root_decl() { return m_owner.m_aux_decls.m_neg_root_decl; }
|
||||
func_decl * asin_u_decl() { return m_owner.m_aux_decls.m_asin_u_decl; }
|
||||
func_decl * acos_u_decl() { return m_owner.m_aux_decls.m_acos_u_decl; }
|
||||
|
||||
expr * mk_int_zero() { return u().mk_numeral(rational(0), true); }
|
||||
|
||||
expr * mk_real_zero() { return u().mk_numeral(rational(0), false); }
|
||||
|
||||
bool already_processed(app * t, expr_ref & result, proof_ref & result_pr) {
|
||||
expr * r;
|
||||
if (m_app2fresh.find(t, r)) {
|
||||
result = r;
|
||||
if (produce_proofs())
|
||||
result_pr = m_app2pr.find(t);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mk_def_proof(expr * k, expr * def, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
if (produce_proofs()) {
|
||||
expr * eq = m().mk_eq(k, def);
|
||||
proof * pr1 = m().mk_def_intro(eq);
|
||||
result_pr = m().mk_apply_def(k, def, pr1);
|
||||
}
|
||||
}
|
||||
|
||||
void push_cnstr_pr(proof * def_pr) {
|
||||
if (produce_proofs())
|
||||
m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 1, &def_pr));
|
||||
}
|
||||
|
||||
void push_cnstr_pr(proof * def_pr1, proof * def_pr2) {
|
||||
if (produce_proofs()) {
|
||||
proof * prs[2] = { def_pr1, def_pr2 };
|
||||
m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 2, prs));
|
||||
}
|
||||
}
|
||||
|
||||
void push_cnstr(expr * cnstr) {
|
||||
m_new_cnstrs.push_back(cnstr);
|
||||
}
|
||||
|
||||
void cache_result(app * t, expr * r, proof * pr) {
|
||||
m_app2fresh.insert(t, r);
|
||||
m_pinned.push_back(t);
|
||||
m_pinned.push_back(r);
|
||||
if (produce_proofs()) {
|
||||
m_app2pr.insert(t, pr);
|
||||
m_pinned.push_back(pr);
|
||||
}
|
||||
}
|
||||
|
||||
expr * OR(expr * arg1, expr * arg2) { return m().mk_or(arg1, arg2); }
|
||||
expr * AND(expr * arg1, expr * arg2) { return m().mk_and(arg1, arg2); }
|
||||
expr * EQ(expr * lhs, expr * rhs) { return m().mk_eq(lhs, rhs); }
|
||||
expr * NOT(expr * arg) { return m().mk_not(arg); }
|
||||
|
||||
void process_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k = mk_fresh_real_var();
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
expr * y = args[1];
|
||||
// y = 0 \/ y*k = x
|
||||
push_cnstr(OR(EQ(y, mk_real_zero()),
|
||||
EQ(u().mk_mul(y, k), x)));
|
||||
push_cnstr_pr(result_pr);
|
||||
|
||||
if (complete()) {
|
||||
// y != 0 \/ k = div-0(x)
|
||||
push_cnstr(OR(NOT(EQ(y, mk_real_zero())),
|
||||
EQ(k, m().mk_app(div0_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
}
|
||||
|
||||
void process_idiv(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref div_app(m());
|
||||
div_app = m().mk_app(f, num, args);
|
||||
if (already_processed(div_app, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k1 = mk_fresh_int_var();
|
||||
result = k1;
|
||||
mk_def_proof(k1, div_app, result_pr);
|
||||
cache_result(div_app, result, result_pr);
|
||||
|
||||
expr * k2 = mk_fresh_int_var();
|
||||
app_ref mod_app(m());
|
||||
proof_ref mod_pr(m());
|
||||
mod_app = u().mk_mod(args[0], args[1]);
|
||||
mk_def_proof(k2, mod_app, mod_pr);
|
||||
cache_result(mod_app, k2, mod_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
expr * y = args[1];
|
||||
// (div x y) --> k1 | y = 0 \/ x = k1 * y + k2,
|
||||
// y = 0 \/ 0 <= k2,
|
||||
// y = 0 \/ k2 < |y|,
|
||||
// y != 0 \/ k1 = idiv-0(x),
|
||||
// y != 0 \/ k2 = mod-0(x)
|
||||
// We can write y = 0 \/ k2 < |y| as:
|
||||
// y > 0 implies k2 < y ---> y <= 0 \/ k2 < y
|
||||
// y < 0 implies k2 < -y ---> y >= 0 \/ k2 < -y
|
||||
//
|
||||
expr * zero = mk_int_zero();
|
||||
push_cnstr(OR(EQ(y, zero), EQ(x, u().mk_add(u().mk_mul(k1, y), k2))));
|
||||
push_cnstr_pr(result_pr, mod_pr);
|
||||
|
||||
push_cnstr(OR(EQ(y, zero), u().mk_le(zero, k2)));
|
||||
push_cnstr_pr(mod_pr);
|
||||
|
||||
push_cnstr(OR(u().mk_le(y, zero), u().mk_lt(k2, y)));
|
||||
push_cnstr_pr(mod_pr);
|
||||
|
||||
push_cnstr(OR(u().mk_ge(y, zero), u().mk_lt(k2, u().mk_mul(u().mk_numeral(rational(-1), true), y))));
|
||||
push_cnstr_pr(mod_pr);
|
||||
|
||||
if (complete()) {
|
||||
push_cnstr(OR(NOT(EQ(y, zero)), EQ(k1, m().mk_app(idiv0_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
|
||||
push_cnstr(OR(NOT(EQ(y, zero)), EQ(k2, m().mk_app(mod0_decl(), x))));
|
||||
push_cnstr_pr(mod_pr);
|
||||
}
|
||||
}
|
||||
|
||||
void process_mod(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return;
|
||||
process_idiv(f, num, args, result, result_pr); // it will create mod
|
||||
VERIFY(already_processed(t, result, result_pr));
|
||||
}
|
||||
|
||||
void process_to_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k = mk_fresh_int_var();
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
// to-real(k) - x >= 0
|
||||
expr * diff = u().mk_add(u().mk_to_real(k), u().mk_mul(u().mk_numeral(rational(-1), false), x));
|
||||
push_cnstr(u().mk_ge(diff, mk_real_zero()));
|
||||
push_cnstr_pr(result_pr);
|
||||
|
||||
// not(to-real(k) - x >= 1)
|
||||
push_cnstr(NOT(u().mk_ge(diff, u().mk_numeral(rational(1), false))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
|
||||
br_status process_power(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
rational y;
|
||||
if (!u().is_numeral(args[1], y))
|
||||
return BR_FAILED;
|
||||
if (y.is_int() && !y.is_zero())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, num, args);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
bool is_int = u().is_int(args[0]);
|
||||
|
||||
expr * k = mk_fresh_var(is_int);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
expr * x = args[0];
|
||||
expr * zero = u().mk_numeral(rational(0), is_int);
|
||||
expr * one = u().mk_numeral(rational(1), is_int);
|
||||
if (y.is_zero()) {
|
||||
// (^ x 0) --> k | x != 0 implies k = 1, x = 0 implies k = 0^0
|
||||
push_cnstr(OR(EQ(x, zero), EQ(k, one)));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
func_decl * z_pw_z = is_int ? int_0_pw_0_decl() : real_0_pw_0_decl();
|
||||
push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, m().mk_const(z_pw_z))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
}
|
||||
else if (!is_int) {
|
||||
SASSERT(!y.is_int());
|
||||
SASSERT(numerator(y).is_one());
|
||||
rational n = denominator(y);
|
||||
if (!n.is_even()) {
|
||||
// (^ x (/ 1 n)) --> k | x = k^n
|
||||
// when n is odd
|
||||
push_cnstr(EQ(x, u().mk_power(k, u().mk_numeral(n, false))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
else {
|
||||
SASSERT(n.is_even());
|
||||
// (^ x (/ 1 n)) --> k | x >= 0 implies (x = k^n and k >= 0), x < 0 implies k = neg-root(x, n)
|
||||
// when n is even
|
||||
push_cnstr(OR(NOT(u().mk_ge(x, zero)),
|
||||
AND(EQ(x, u().mk_power(k, u().mk_numeral(n, false))),
|
||||
u().mk_ge(k, zero))));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
push_cnstr(OR(u().mk_ge(x, zero),
|
||||
EQ(k, m().mk_app(neg_root_decl(), x, u().mk_numeral(n, false)))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// root not supported for integers.
|
||||
SASSERT(is_int);
|
||||
SASSERT(!y.is_int());
|
||||
return BR_FAILED;
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
void process_irrat(app * s, expr_ref & result, proof_ref & result_pr) {
|
||||
if (already_processed(s, result, result_pr))
|
||||
return;
|
||||
|
||||
expr * k = mk_fresh_real_var();
|
||||
result = k;
|
||||
mk_def_proof(k, s, result_pr);
|
||||
cache_result(s, result, result_pr);
|
||||
|
||||
anum_manager & am = u().am();
|
||||
anum const & a = u().to_irrational_algebraic_numeral(s);
|
||||
scoped_mpz_vector p(am.qm());
|
||||
am.get_polynomial(a, p);
|
||||
rational lower, upper;
|
||||
am.get_lower(a, lower);
|
||||
am.get_upper(a, upper);
|
||||
unsigned sz = p.size();
|
||||
SASSERT(sz > 2);
|
||||
ptr_buffer<expr> args;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (am.qm().is_zero(p[i]))
|
||||
continue;
|
||||
rational coeff = rational(p[i]);
|
||||
if (i == 0) {
|
||||
args.push_back(u().mk_numeral(coeff, false));
|
||||
}
|
||||
else {
|
||||
expr * m;
|
||||
if (i == 1)
|
||||
m = k;
|
||||
else
|
||||
m = u().mk_power(k, u().mk_numeral(rational(i), false));
|
||||
args.push_back(u().mk_mul(u().mk_numeral(coeff, false), m));
|
||||
}
|
||||
}
|
||||
SASSERT(args.size() >= 2);
|
||||
push_cnstr(EQ(u().mk_add(args.size(), args.c_ptr()), mk_real_zero()));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(u().mk_lt(u().mk_numeral(lower, false), k));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(u().mk_lt(k, u().mk_numeral(upper, false)));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
|
||||
br_status process_asin(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) {
|
||||
if (!elim_inverses())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, x);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
expr * k = mk_fresh_var(false);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
// Constraints:
|
||||
// -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2
|
||||
// If complete()
|
||||
// x < -1 implies k = asin_u(x)
|
||||
// x > 1 implies k = asin_u(x)
|
||||
expr * one = u().mk_numeral(rational(1), false);
|
||||
expr * mone = u().mk_numeral(rational(-1), false);
|
||||
expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi());
|
||||
expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi());
|
||||
// -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2
|
||||
push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)),
|
||||
NOT(u().mk_le(x, one))),
|
||||
AND(EQ(x, u().mk_sin(k)),
|
||||
AND(u().mk_ge(k, mpi2),
|
||||
u().mk_le(k, pi2)))));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
// x < -1 implies k = asin_u(x)
|
||||
// x > 1 implies k = asin_u(x)
|
||||
push_cnstr(OR(u().mk_ge(x, mone),
|
||||
EQ(k, m().mk_app(asin_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(OR(u().mk_le(x, one),
|
||||
EQ(k, m().mk_app(asin_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status process_acos(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) {
|
||||
if (!elim_inverses())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, x);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
expr * k = mk_fresh_var(false);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
// Constraints:
|
||||
// -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi
|
||||
// If complete()
|
||||
// x < -1 implies k = acos_u(x)
|
||||
// x > 1 implies k = acos_u(x)
|
||||
expr * one = u().mk_numeral(rational(1), false);
|
||||
expr * mone = u().mk_numeral(rational(-1), false);
|
||||
expr * pi = u().mk_pi();
|
||||
expr * zero = u().mk_numeral(rational(0), false);
|
||||
// -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi
|
||||
push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)),
|
||||
NOT(u().mk_le(x, one))),
|
||||
AND(EQ(x, u().mk_cos(k)),
|
||||
AND(u().mk_ge(k, zero),
|
||||
u().mk_le(k, pi)))));
|
||||
push_cnstr_pr(result_pr);
|
||||
if (complete()) {
|
||||
// x < -1 implies k = acos_u(x)
|
||||
// x > 1 implies k = acos_u(x)
|
||||
push_cnstr(OR(u().mk_ge(x, mone),
|
||||
EQ(k, m().mk_app(acos_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
push_cnstr(OR(u().mk_le(x, one),
|
||||
EQ(k, m().mk_app(acos_u_decl(), x))));
|
||||
push_cnstr_pr(result_pr);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status process_atan(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) {
|
||||
if (!elim_inverses())
|
||||
return BR_FAILED;
|
||||
app_ref t(m());
|
||||
t = m().mk_app(f, x);
|
||||
if (already_processed(t, result, result_pr))
|
||||
return BR_DONE;
|
||||
|
||||
expr * k = mk_fresh_var(false);
|
||||
result = k;
|
||||
mk_def_proof(k, t, result_pr);
|
||||
cache_result(t, result, result_pr);
|
||||
|
||||
// Constraints:
|
||||
// tan(k) = x, -pi/2 < k < pi/2
|
||||
expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi());
|
||||
expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi());
|
||||
push_cnstr(AND(EQ(x, u().mk_tan(k)),
|
||||
AND(u().mk_gt(k, mpi2),
|
||||
u().mk_lt(k, pi2))));
|
||||
push_cnstr_pr(result_pr);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
if (f->get_family_id() != u().get_family_id())
|
||||
return BR_FAILED;
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_DIV:
|
||||
process_div(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_IDIV:
|
||||
process_idiv(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_MOD:
|
||||
process_mod(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_TO_INT:
|
||||
process_to_int(f, num, args, result, result_pr);
|
||||
return BR_DONE;
|
||||
case OP_POWER:
|
||||
return process_power(f, num, args, result, result_pr);
|
||||
case OP_ASIN:
|
||||
return process_asin(f, args[0], result, result_pr);
|
||||
case OP_ACOS:
|
||||
return process_acos(f, args[0], result, result_pr);
|
||||
case OP_ATAN:
|
||||
return process_atan(f, args[0], result, result_pr);
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
|
||||
if (is_quantifier(s)) {
|
||||
m_owner.process_quantifier(to_quantifier(s), m_subst, m_subst_pr);
|
||||
t = m_subst.get();
|
||||
t_pr = m_subst_pr.get();
|
||||
return true;
|
||||
}
|
||||
else if (u().is_irrational_algebraic_numeral(s) && elim_root_objs()) {
|
||||
process_irrat(to_app(s), m_subst, m_subst_pr);
|
||||
t = m_subst.get();
|
||||
t_pr = m_subst_pr.get();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
rw(purify_arith_proc & o, bool in_q):
|
||||
rewriter_tpl<rw_cfg>(o.m(), o.m_produce_proofs, m_cfg),
|
||||
m_cfg(o, in_q) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Return the number of (auxiliary) variables needed for converting an expression.
|
||||
*/
|
||||
struct num_vars_proc {
|
||||
arith_util & m_util;
|
||||
expr_fast_mark1 m_visited;
|
||||
ptr_vector<expr> m_todo;
|
||||
unsigned m_num_vars;
|
||||
bool m_elim_root_objs;
|
||||
|
||||
num_vars_proc(arith_util & u, bool elim_root_objs):
|
||||
m_util(u),
|
||||
m_elim_root_objs(elim_root_objs) {
|
||||
}
|
||||
|
||||
void visit(expr * t) {
|
||||
if (m_visited.is_marked(t))
|
||||
return;
|
||||
m_visited.mark(t);
|
||||
m_todo.push_back(t);
|
||||
}
|
||||
|
||||
void process(app * t) {
|
||||
if (t->get_family_id() == m_util.get_family_id()) {
|
||||
if (m_util.is_power(t)) {
|
||||
rational k;
|
||||
if (m_util.is_numeral(t->get_arg(1), k) && (k.is_zero() || !k.is_int())) {
|
||||
m_num_vars++;
|
||||
}
|
||||
}
|
||||
else if (m_util.is_div(t) ||
|
||||
m_util.is_idiv(t) ||
|
||||
m_util.is_mod(t) ||
|
||||
m_util.is_to_int(t) ||
|
||||
(m_util.is_irrational_algebraic_numeral(t) && m_elim_root_objs)) {
|
||||
m_num_vars++;
|
||||
}
|
||||
}
|
||||
unsigned num_args = t->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
visit(t->get_arg(i));
|
||||
}
|
||||
|
||||
unsigned operator()(expr * t) {
|
||||
m_num_vars = 0;
|
||||
visit(t);
|
||||
while (!m_todo.empty()) {
|
||||
expr * t = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
if (is_app(t))
|
||||
process(to_app(t));
|
||||
}
|
||||
m_visited.reset();
|
||||
return m_num_vars;
|
||||
}
|
||||
};
|
||||
|
||||
void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
num_vars_proc p(u(), m_elim_root_objs);
|
||||
expr_ref body(m());
|
||||
unsigned num_vars = p(q->get_expr());
|
||||
if (num_vars > 0) {
|
||||
// open space for aux vars
|
||||
var_shifter shifter(m());
|
||||
shifter(q->get_expr(), num_vars, body);
|
||||
}
|
||||
else {
|
||||
body = q->get_expr();
|
||||
}
|
||||
|
||||
rw r(*this, true);
|
||||
expr_ref new_body(m());
|
||||
proof_ref new_body_pr(m());
|
||||
r(body, new_body, new_body_pr);
|
||||
TRACE("purify_arith",
|
||||
tout << "num_vars: " << num_vars << "\n";
|
||||
tout << "body: " << mk_ismt2_pp(body, m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";);
|
||||
if (num_vars == 0) {
|
||||
result = m().update_quantifier(q, new_body);
|
||||
if (m_produce_proofs)
|
||||
result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), result_pr);
|
||||
}
|
||||
else {
|
||||
expr_ref_vector & cnstrs = r.cfg().m_new_cnstrs;
|
||||
cnstrs.push_back(new_body);
|
||||
new_body = m().mk_and(cnstrs.size(), cnstrs.c_ptr());
|
||||
ptr_buffer<sort> sorts;
|
||||
buffer<symbol> names;
|
||||
for (unsigned i = 0; i < num_vars; i++) {
|
||||
sorts.push_back(u().mk_real());
|
||||
names.push_back(m().mk_fresh_var_name("x"));
|
||||
}
|
||||
new_body = m().mk_exists(num_vars, sorts.c_ptr(), names.c_ptr(), new_body);
|
||||
result = m().update_quantifier(q, new_body);
|
||||
if (m_produce_proofs) {
|
||||
proof_ref_vector & cnstr_prs = r.cfg().m_new_cnstr_prs;
|
||||
cnstr_prs.push_back(result_pr);
|
||||
// TODO: improve proof
|
||||
result_pr = m().mk_quant_intro(q, to_quantifier(result.get()),
|
||||
m().mk_rewrite_star(q->get_expr(), new_body, cnstr_prs.size(), cnstr_prs.c_ptr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(goal & g, model_converter_ref & mc, bool produce_models) {
|
||||
rw r(*this, false);
|
||||
// purify
|
||||
expr_ref new_curr(m());
|
||||
proof_ref new_pr(m());
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * curr = g.form(i);
|
||||
r(curr, new_curr, new_pr);
|
||||
if (m_produce_proofs) {
|
||||
proof * pr = g.pr(i);
|
||||
new_pr = m().mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
g.update(i, new_curr, new_pr, g.dep(i));
|
||||
}
|
||||
|
||||
// add cnstraints
|
||||
sz = r.cfg().m_new_cnstrs.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
g.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : 0, 0);
|
||||
}
|
||||
|
||||
// add filter_model_converter to eliminate auxiliary variables from model
|
||||
if (produce_models) {
|
||||
filter_model_converter * fmc = alloc(filter_model_converter, m());
|
||||
mc = fmc;
|
||||
obj_map<app, expr*> & f2v = r.cfg().m_app2fresh;
|
||||
obj_map<app, expr*>::iterator it = f2v.begin();
|
||||
obj_map<app, expr*>::iterator end = f2v.end();
|
||||
for (; it != end; ++it) {
|
||||
app * v = to_app(it->m_value);
|
||||
SASSERT(is_uninterp_const(v));
|
||||
fmc->insert(v->get_decl());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class purify_arith_tactic : public tactic {
|
||||
arith_util m_util;
|
||||
purify_arith_decls m_aux_decls;
|
||||
params_ref m_params;
|
||||
public:
|
||||
purify_arith_tactic(ast_manager & m, params_ref const & p):
|
||||
m_util(m),
|
||||
m_aux_decls(m_util),
|
||||
m_params(p) {
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(purify_arith_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~purify_arith_tactic() {
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
r.insert(":complete", CPK_BOOL,
|
||||
"(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a functio. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root");
|
||||
r.insert(":elim-root-objects", CPK_BOOL,
|
||||
"(default: true) eliminate root objects.");
|
||||
r.insert(":elim-inverses", CPK_BOOL,
|
||||
"(default: true) eliminate inverse trigonometric functions (asin, acos, atan).");
|
||||
th_rewriter::get_param_descrs(r);
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
mc = 0; pc = 0; core = 0;
|
||||
tactic_report report("purify-arith", *g);
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
bool produce_models = g->models_enabled();
|
||||
bool elim_root_objs = m_params.get_bool(":elim-root-objects", true);
|
||||
bool elim_inverses = m_params.get_bool(":elim-inverses", true);
|
||||
bool complete = m_params.get_bool(":complete", true);
|
||||
purify_arith_proc proc(m_util, m_aux_decls, produce_proofs, elim_root_objs, elim_inverses, complete);
|
||||
|
||||
proc(*(g.get()), mc, produce_models);
|
||||
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
TRACE("purify_arith", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
}
|
||||
|
||||
virtual void set_cancel(bool f) {
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p) {
|
||||
params_ref elim_rem_p = p;
|
||||
elim_rem_p.set_bool(":elim-rem", true);
|
||||
|
||||
params_ref skolemize_p;
|
||||
skolemize_p.set_bool(":skolemize", false);
|
||||
|
||||
return and_then(using_params(mk_snf_tactic(m, skolemize_p), skolemize_p),
|
||||
using_params(mk_simplify_tactic(m, elim_rem_p), elim_rem_p),
|
||||
alloc(purify_arith_tactic, m, p),
|
||||
mk_simplify_tactic(m, p));
|
||||
}
|
58
src/tactic/arith_tactics/purify_arith_tactic.h
Normal file
58
src/tactic/arith_tactics/purify_arith_tactic.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
purify_arith_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for eliminating arithmetic operators: DIV, IDIV, MOD,
|
||||
TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM).
|
||||
|
||||
This tactic uses the simplifier for also eliminating:
|
||||
OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT.
|
||||
|
||||
Remarks:
|
||||
- The semantics of division by zero is not specified. Thus,
|
||||
uninterpreted functions are used. An ExRCF procedure may
|
||||
treat the unintepreted function applications as fresh
|
||||
constants. Then, in any model produced by this procedure,
|
||||
the interpretation for division by zero must be checked.
|
||||
|
||||
- POWER operator can only be handled if the second argument is a
|
||||
rational value. The tactic has an option for preserving POWER
|
||||
operator where the second argument is an integer.
|
||||
|
||||
- The semantics of (^ t (/ 1 k)) is not specified when t < 0 and
|
||||
k is even. Similarly to the division by zero case,
|
||||
uninterpreted function symbols are created.
|
||||
|
||||
- The semantics of (^ t 0) is not specified if t == 0. Thus,
|
||||
uninterpreted function symbols are created.
|
||||
|
||||
- TO_REAL is not really outside of the RCF language
|
||||
since it is only used for "casting".
|
||||
|
||||
- All quantifiers must occur with positive polarity.
|
||||
The tactic snf (with skolemization disabled) is applied
|
||||
to enforce that.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _PURIFY_ARITH_TACTIC_H_
|
||||
#define _PURIFY_ARITH_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
||||
|
446
src/tactic/arith_tactics/recover_01_tactic.cpp
Normal file
446
src/tactic/arith_tactics/recover_01_tactic.cpp
Normal file
|
@ -0,0 +1,446 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
recover_01_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Recover 01 variables
|
||||
|
||||
Search for clauses of the form
|
||||
p or q or x = 0
|
||||
~p or q or x = k1
|
||||
p or ~q or x = k2
|
||||
~p or ~q or x = k1+k2
|
||||
|
||||
Then, replaces
|
||||
x with k1*y1 + k2*y2
|
||||
p with y1=1
|
||||
q with y2=1
|
||||
where y1 and y2 are fresh 01 variables
|
||||
|
||||
The clauses are also removed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"th_rewriter.h"
|
||||
#include"extension_model_converter.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"dec_ref_util.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
class recover_01_tactic : public tactic {
|
||||
struct imp {
|
||||
typedef obj_map<func_decl, ptr_vector<app> > var2clauses;
|
||||
|
||||
ast_manager & m;
|
||||
var2clauses m_var2clauses;
|
||||
arith_util m_util;
|
||||
th_rewriter m_rw;
|
||||
bool m_produce_models;
|
||||
unsigned m_cls_max_size;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_util(m),
|
||||
m_rw(m, p) {
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
m_cls_max_size = p.get_uint(":recover-01-max-bits", 10);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_rw.updt_params(p);
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_rw.set_cancel(f);
|
||||
}
|
||||
|
||||
bool save_clause(expr * c) {
|
||||
if (!m.is_or(c))
|
||||
return false;
|
||||
func_decl * x = 0;
|
||||
app * cls = to_app(c);
|
||||
if (cls->get_num_args() <= 1 || cls->get_num_args() >= m_cls_max_size)
|
||||
return false;
|
||||
unsigned sz = cls->get_num_args();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * lit = cls->get_arg(i);
|
||||
expr * lhs, * rhs, * arg;
|
||||
if (is_uninterp_const(lit)) {
|
||||
// positive literal
|
||||
}
|
||||
else if (m.is_not(lit, arg) && is_uninterp_const(arg)) {
|
||||
// negative literal
|
||||
}
|
||||
else if (x == 0 && m.is_eq(lit, lhs, rhs)) {
|
||||
// x = k literal
|
||||
if (is_uninterp_const(lhs) && m_util.is_numeral(rhs)) {
|
||||
x = to_app(lhs)->get_decl();
|
||||
}
|
||||
else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs)) {
|
||||
x = to_app(rhs)->get_decl();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x != 0) {
|
||||
var2clauses::obj_map_entry * entry = m_var2clauses.insert_if_not_there2(x, ptr_vector<app>());
|
||||
if (entry->get_data().m_value.empty() || entry->get_data().m_value.back()->get_num_args() == cls->get_num_args()) {
|
||||
entry->get_data().m_value.push_back(cls);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// temporary fields used by operator() and process
|
||||
extension_model_converter * mc1;
|
||||
filter_model_converter * mc2;
|
||||
expr_substitution * subst;
|
||||
goal_ref new_goal;
|
||||
obj_map<expr, expr *> bool2int;
|
||||
|
||||
app * find_zero_cls(func_decl * x, ptr_vector<app> & clauses) {
|
||||
ptr_vector<app>::iterator it = clauses.begin();
|
||||
ptr_vector<app>::iterator end = clauses.end();
|
||||
for (; it != end; ++it) {
|
||||
app * cls = *it;
|
||||
unsigned num = cls->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * lhs, * rhs;
|
||||
if (m.is_eq(cls->get_arg(i), lhs, rhs)) {
|
||||
if (is_uninterp_const(lhs) && m_util.is_zero(rhs))
|
||||
return cls;
|
||||
if (is_uninterp_const(rhs) && m_util.is_zero(lhs))
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find coeff (the k of literal (x = k)) of clause cls.
|
||||
// Store in idx the bit-vector representing the literals.
|
||||
// Example: idx = 101 if cls has three boolean literals p1, p2, p3
|
||||
// where p1 = ~q1, p2 = q2, p3 = ~q3
|
||||
// and q1 q2 q3 are the corresponding literals in the
|
||||
// zero clause.
|
||||
// Return false, if the boolean literals of cls cannot be matched with the literals
|
||||
// of zero_cls
|
||||
bool find_coeff(app * cls, app * zero_cls, unsigned & idx, rational & k) {
|
||||
unsigned num = zero_cls->get_num_args();
|
||||
if (cls->get_num_args() != num)
|
||||
return false;
|
||||
idx = 0;
|
||||
unsigned val = 1;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * lit = zero_cls->get_arg(i);
|
||||
if (m.is_eq(lit))
|
||||
continue;
|
||||
// search for lit or ~lit in cls
|
||||
unsigned j;
|
||||
for (j = 0; j < num; j++) {
|
||||
expr * lit2 = cls->get_arg(j);
|
||||
if (m.is_eq(lit2))
|
||||
continue;
|
||||
if (lit2 == lit)
|
||||
break;
|
||||
if (m.is_complement(lit2, lit)) {
|
||||
idx += val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == num)
|
||||
return false; // cls does not contain literal lit
|
||||
val *= 2;
|
||||
}
|
||||
|
||||
// find k
|
||||
unsigned i;
|
||||
for (i = 0; i < num; i++) {
|
||||
expr * lhs, * rhs;
|
||||
if (m.is_eq(cls->get_arg(i), lhs, rhs) && (m_util.is_numeral(lhs, k) || m_util.is_numeral(rhs, k)))
|
||||
break;
|
||||
}
|
||||
if (i == num)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mk_ivar(expr * lit, expr_ref & def, bool real_ctx) {
|
||||
expr * atom;
|
||||
bool sign;
|
||||
if (m.is_not(lit, atom)) {
|
||||
sign = true;
|
||||
}
|
||||
else {
|
||||
atom = lit;
|
||||
sign = false;
|
||||
}
|
||||
SASSERT(is_uninterp_const(atom));
|
||||
expr * var;
|
||||
if (!bool2int.find(atom, var)) {
|
||||
var = m.mk_fresh_const(0, m_util.mk_int());
|
||||
new_goal->assert_expr(m_util.mk_le(m_util.mk_numeral(rational(0), true), var));
|
||||
new_goal->assert_expr(m_util.mk_le(var, m_util.mk_numeral(rational(1), true)));
|
||||
expr * bool_def = m.mk_eq(var, m_util.mk_numeral(rational(1), true));
|
||||
subst->insert(atom, bool_def);
|
||||
if (m_produce_models) {
|
||||
mc2->insert(to_app(var)->get_decl());
|
||||
mc1->insert(to_app(atom)->get_decl(), bool_def);
|
||||
}
|
||||
m.inc_ref(atom);
|
||||
m.inc_ref(var);
|
||||
bool2int.insert(atom, var);
|
||||
}
|
||||
expr * norm_var = real_ctx ? m_util.mk_to_real(var) : var;
|
||||
if (sign)
|
||||
def = m_util.mk_sub(m_util.mk_numeral(rational(1), !real_ctx), norm_var);
|
||||
else
|
||||
def = norm_var;
|
||||
}
|
||||
|
||||
bool process(func_decl * x, ptr_vector<app> & clauses) {
|
||||
unsigned cls_size = clauses.back()->get_num_args();
|
||||
unsigned expected_num_clauses = 1 << (cls_size - 1);
|
||||
if (clauses.size() < expected_num_clauses) // using < instead of != because we tolerate duplicates
|
||||
return false;
|
||||
app * zero_cls = find_zero_cls(x, clauses);
|
||||
if (zero_cls == 0)
|
||||
return false;
|
||||
|
||||
buffer<bool> found; // marks which idx were found
|
||||
buffer<rational> idx2coeff;
|
||||
found.resize(expected_num_clauses, false);
|
||||
idx2coeff.resize(expected_num_clauses);
|
||||
|
||||
ptr_vector<app>::iterator it = clauses.begin();
|
||||
ptr_vector<app>::iterator end = clauses.end();
|
||||
for (; it != end; ++it) {
|
||||
app * cls = *it;
|
||||
unsigned idx; rational k;
|
||||
if (!find_coeff(cls, zero_cls, idx, k))
|
||||
return false;
|
||||
SASSERT(idx < expected_num_clauses);
|
||||
if (found[idx] && k != idx2coeff[idx])
|
||||
return false;
|
||||
found[idx] = true;
|
||||
idx2coeff[idx] = k;
|
||||
}
|
||||
|
||||
unsigned num_bits = cls_size - 1;
|
||||
// check if idxs are consistent
|
||||
for (unsigned idx = 0; idx < expected_num_clauses; idx++) {
|
||||
if (!found[idx])
|
||||
return false; // case is missing
|
||||
rational expected_k;
|
||||
unsigned idx_aux = idx;
|
||||
unsigned idx_bit = 1;
|
||||
for (unsigned j = 0; j < num_bits; j++) {
|
||||
if (idx_aux % 2 == 1) {
|
||||
expected_k += idx2coeff[idx_bit];
|
||||
}
|
||||
idx_aux /= 2;
|
||||
idx_bit *= 2;
|
||||
}
|
||||
if (idx2coeff[idx] != expected_k)
|
||||
return false;
|
||||
}
|
||||
|
||||
expr_ref_buffer def_args(m);
|
||||
expr_ref def(m);
|
||||
bool real_ctx = m_util.is_real(x->get_range());
|
||||
unsigned idx_bit = 1;
|
||||
for (unsigned i = 0; i < cls_size; i++) {
|
||||
expr * lit = zero_cls->get_arg(i);
|
||||
if (m.is_eq(lit))
|
||||
continue;
|
||||
mk_ivar(lit, def, real_ctx);
|
||||
def_args.push_back(m_util.mk_mul(m_util.mk_numeral(idx2coeff[idx_bit], !real_ctx), def));
|
||||
idx_bit *= 2;
|
||||
}
|
||||
|
||||
expr * x_def;
|
||||
if (def_args.size() == 1)
|
||||
x_def = def_args[0];
|
||||
else
|
||||
x_def = m_util.mk_add(def_args.size(), def_args.c_ptr());
|
||||
|
||||
TRACE("recover_01", tout << x->get_name() << " --> " << mk_ismt2_pp(x_def, m) << "\n";);
|
||||
subst->insert(m.mk_const(x), x_def);
|
||||
if (m_produce_models) {
|
||||
mc1->insert(x, x_def);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
fail_if_proof_generation("recover-01", g);
|
||||
fail_if_unsat_core_generation("recover-01", g);
|
||||
m_produce_models = g->models_enabled();
|
||||
mc = 0; pc = 0; core = 0; result.reset();
|
||||
tactic_report report("recover-01", *g);
|
||||
|
||||
bool saved = false;
|
||||
new_goal = alloc(goal, *g, true);
|
||||
SASSERT(new_goal->depth() == g->depth());
|
||||
SASSERT(new_goal->prec() == g->prec());
|
||||
new_goal->inc_depth();
|
||||
|
||||
unsigned sz = g->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = g->form(i);
|
||||
if (save_clause(f)) {
|
||||
saved = true;
|
||||
}
|
||||
else {
|
||||
new_goal->assert_expr(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!saved) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_produce_models) {
|
||||
mc1 = alloc(extension_model_converter, m);
|
||||
mc2 = alloc(filter_model_converter, m);
|
||||
mc = concat(mc2, mc1);
|
||||
}
|
||||
|
||||
dec_ref_key_values(m, bool2int);
|
||||
|
||||
unsigned counter = 0;
|
||||
bool recovered = false;
|
||||
expr_substitution _subst(m);
|
||||
subst = &_subst;
|
||||
var2clauses::iterator it = m_var2clauses.begin();
|
||||
var2clauses::iterator end = m_var2clauses.end();
|
||||
for (; it != end; ++it) {
|
||||
if (process(it->m_key, it->m_value)) {
|
||||
recovered = true;
|
||||
counter++;
|
||||
}
|
||||
else {
|
||||
ptr_vector<app>::iterator it2 = it->m_value.begin();
|
||||
ptr_vector<app>::iterator end2 = it->m_value.end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
new_goal->assert_expr(*it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!recovered) {
|
||||
result.push_back(g.get());
|
||||
mc = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
report_tactic_progress(":recovered-01-vars", counter);
|
||||
|
||||
m_rw.set_substitution(subst);
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = new_goal->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
expr * curr = new_goal->form(idx);
|
||||
m_rw(curr, new_curr);
|
||||
new_goal->update(idx, new_curr);
|
||||
}
|
||||
result.push_back(new_goal.get());
|
||||
TRACE("recover_01", new_goal->display(tout););
|
||||
SASSERT(new_goal->is_well_sorted());
|
||||
}
|
||||
|
||||
~imp() {
|
||||
dec_ref_key_values(m, bool2int);
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
recover_01_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(recover_01_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual ~recover_01_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
th_rewriter::get_param_descrs(r);
|
||||
r.insert(":recover-01-max-bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause.");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
(*m_imp)(g, result, mc, pc, core);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m;
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_recover_01_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(recover_01_tactic, m, p));
|
||||
}
|
42
src/tactic/arith_tactics/recover_01_tactic.h
Normal file
42
src/tactic/arith_tactics/recover_01_tactic.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
recover_01_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Recover 01 variables
|
||||
|
||||
Search for clauses of the form
|
||||
p or q or x = 0
|
||||
~p or q or x = k1
|
||||
p or ~q or x = k2
|
||||
~p or ~q or x = k1+k2
|
||||
|
||||
Then, replaces
|
||||
x with k1*y1 + k2*y2
|
||||
p with y1=1
|
||||
q with y2=1
|
||||
where y1 and y2 are fresh 01 variables
|
||||
|
||||
The clauses are also removed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _RECOVER_01_TACTIC_H_
|
||||
#define _RECOVER_01_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_recover_01_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
3605
src/tactic/arith_tactics/smt_arith.cpp
Normal file
3605
src/tactic/arith_tactics/smt_arith.cpp
Normal file
File diff suppressed because it is too large
Load diff
56
src/tactic/arith_tactics/smt_arith.h
Normal file
56
src/tactic/arith_tactics/smt_arith.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_arith.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Arithmetic solver for smt::solver
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _SMT_ARITH_H_
|
||||
#define _SMT_ARITH_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"smt_solver_types.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
class model;
|
||||
|
||||
namespace smt {
|
||||
|
||||
class arith {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
arith(ast_manager & m, params_ref const & p);
|
||||
~arith();
|
||||
void updt_params(params_ref const & p);
|
||||
void assert_axiom(expr * t, bool neg);
|
||||
void mk_atom(expr * t, atom_id id);
|
||||
void asserted(atom_id id, bool is_true);
|
||||
bool inconsistent() const;
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void set_cancel(bool f);
|
||||
void simplify();
|
||||
void display(std::ostream & out) const;
|
||||
void reset();
|
||||
void preprocess();
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
lbool check();
|
||||
void mk_model(model * md);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
218
src/tactic/arith_tactics/smt_formula_compiler.cpp
Normal file
218
src/tactic/arith_tactics/smt_formula_compiler.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_formula_compiler.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Auxiliary class for smt::solver
|
||||
Convert Exprs into Internal data-structures.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
|
||||
--*/
|
||||
#include"smt_formula_compiler.h"
|
||||
#include"smt_solver_exp.h"
|
||||
#include"assertion_set_util.h"
|
||||
#include"assertion_set2sat.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
formula_compiler::formula_compiler(solver_exp & _s, params_ref const & p):
|
||||
s(_s),
|
||||
m_a_util(s.m),
|
||||
m_normalizer(s.m),
|
||||
m_elim_ite(s.m) {
|
||||
updt_params(p);
|
||||
|
||||
params_ref s_p;
|
||||
s_p.set_bool(":elim-and", true);
|
||||
s_p.set_bool(":blast-distinct", true);
|
||||
s_p.set_bool(":eq2ineq", true);
|
||||
s_p.set_bool(":arith-lhs", true);
|
||||
s_p.set_bool(":gcd-rounding", true);
|
||||
s_p.set_bool(":sort-sums", true);
|
||||
s_p.set_bool(":som", true);
|
||||
m_normalizer.updt_params(s_p);
|
||||
}
|
||||
|
||||
formula_compiler::~formula_compiler() {
|
||||
|
||||
}
|
||||
|
||||
// mark theory axioms: literals that do not occur in the boolean structure
|
||||
void formula_compiler::mark_axioms(assertion_set const & s, expr_fast_mark2 & axioms) {
|
||||
ast_manager & m = s.m();
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = s.form(i);
|
||||
while (m.is_not(f, f));
|
||||
if (!is_app(f) || to_app(f)->get_family_id() != m.get_basic_family_id()) {
|
||||
axioms.mark(f);
|
||||
continue;
|
||||
}
|
||||
SASSERT(is_app(f));
|
||||
SASSERT(to_app(f)->get_family_id() == m.get_basic_family_id());
|
||||
switch (to_app(f)->get_decl_kind()) {
|
||||
case OP_OR:
|
||||
case OP_IFF:
|
||||
break;
|
||||
case OP_ITE:
|
||||
SASSERT(m.is_bool(to_app(f)->get_arg(1)));
|
||||
break;
|
||||
case OP_EQ:
|
||||
if (!m.is_bool(to_app(f)->get_arg(1)))
|
||||
axioms.mark(f);
|
||||
break;
|
||||
case OP_AND:
|
||||
case OP_XOR:
|
||||
case OP_IMPLIES:
|
||||
case OP_DISTINCT:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct unmark_axioms_proc {
|
||||
expr_fast_mark2 & m_axioms;
|
||||
unmark_axioms_proc(expr_fast_mark2 & axioms):m_axioms(axioms) {}
|
||||
void operator()(quantifier *) {}
|
||||
void operator()(var *) {}
|
||||
void operator()(app * t) {
|
||||
m_axioms.reset_mark(t);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Unmark atoms that occur in the boolean structure.
|
||||
*/
|
||||
void formula_compiler::unmark_nested_atoms(assertion_set const & s, expr_fast_mark2 & axioms) {
|
||||
ast_manager & m = s.m();
|
||||
expr_fast_mark1 visited;
|
||||
unmark_axioms_proc proc(axioms);
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = s.form(i);
|
||||
while (m.is_not(f, f));
|
||||
if (axioms.is_marked(f))
|
||||
continue;
|
||||
quick_for_each_expr(proc, visited, f);
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::assert_axiom(expr * f, bool neg) {
|
||||
if (is_app(f)) {
|
||||
if (to_app(f)->get_family_id() == m_a_util.get_family_id())
|
||||
s.m_arith.assert_axiom(f, neg);
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::register_atom(expr * f, sat::bool_var p) {
|
||||
if (is_app(f)) {
|
||||
if (to_app(f)->get_family_id() == m_a_util.get_family_id())
|
||||
s.m_arith.mk_atom(f, p);
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::compile_formulas(assertion_set const & assertions) {
|
||||
ast_manager & m = assertions.m();
|
||||
expr_fast_mark2 axioms;
|
||||
mark_axioms(assertions, axioms);
|
||||
unmark_nested_atoms(assertions, axioms);
|
||||
ptr_vector<expr> formulas;
|
||||
|
||||
// send axioms to theories, and save formulas to compile
|
||||
unsigned sz = assertions.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = assertions.form(i);
|
||||
bool neg = false;
|
||||
while (m.is_not(f, f))
|
||||
neg = !neg;
|
||||
if (axioms.is_marked(f)) {
|
||||
assert_axiom(f, neg);
|
||||
}
|
||||
else {
|
||||
formulas.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
// compile formulas into sat::solver
|
||||
m_to_sat(m, formulas.size(), formulas.c_ptr(), s.m_params, *(s.m_sat), s.m_atom2bvar);
|
||||
|
||||
// register atoms nested in the boolean structure in the theories
|
||||
atom2bool_var::recent_iterator it = s.m_atom2bvar.begin_recent();
|
||||
atom2bool_var::recent_iterator end = s.m_atom2bvar.end_recent();
|
||||
for (; it != end; ++it) {
|
||||
expr * atom = *it;
|
||||
register_atom(atom, s.m_atom2bvar.to_bool_var(atom));
|
||||
}
|
||||
s.m_atom2bvar.reset_recent();
|
||||
}
|
||||
|
||||
void formula_compiler::normalize() {
|
||||
// make sure that the assertions are in the right format.
|
||||
m_normalizer(s.m_assertions);
|
||||
m_normalizer.cleanup();
|
||||
}
|
||||
|
||||
void formula_compiler::elim_term_ite() {
|
||||
if (has_term_ite(s.m_assertions)) {
|
||||
model_converter_ref mc;
|
||||
m_elim_ite(s.m_assertions, mc);
|
||||
s.m_mc = concat(s.m_mc.get(), mc.get());
|
||||
m_elim_ite.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void formula_compiler::operator()() {
|
||||
if (s.m_assertions.inconsistent())
|
||||
return;
|
||||
// normalization
|
||||
elim_term_ite();
|
||||
normalize();
|
||||
|
||||
TRACE("before_formula_compiler", s.m_assertions.display(tout););
|
||||
|
||||
s.init();
|
||||
|
||||
compile_formulas(s.m_assertions);
|
||||
|
||||
s.m_arith.preprocess();
|
||||
TRACE("after_formula_compiler", s.display_state(tout););
|
||||
}
|
||||
|
||||
void formula_compiler::updt_params(params_ref const & p) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::collect_param_descrs(param_descrs & d) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::collect_statistics(statistics & st) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::reset_statistics() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void formula_compiler::set_cancel(bool f) {
|
||||
m_normalizer.set_cancel(f);
|
||||
m_elim_ite.set_cancel(f);
|
||||
m_to_sat.set_cancel(f);
|
||||
}
|
||||
|
||||
};
|
63
src/tactic/arith_tactics/smt_formula_compiler.h
Normal file
63
src/tactic/arith_tactics/smt_formula_compiler.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_formula_compiler.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Auxiliary class for smt::solver
|
||||
Convert Exprs into Internal data-structures.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _SMT_FORMULA_COMPILER_H_
|
||||
#define _SMT_FORMULA_COMPILER_H_
|
||||
|
||||
#include"smt_solver_types.h"
|
||||
#include"assertion_set_rewriter.h"
|
||||
#include"elim_term_ite_strategy.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"assertion_set2sat.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
class formula_compiler {
|
||||
solver_exp & s;
|
||||
arith_util m_a_util;
|
||||
assertion_set_rewriter m_normalizer;
|
||||
elim_term_ite_strategy m_elim_ite;
|
||||
assertion_set2sat m_to_sat;
|
||||
|
||||
void normalize();
|
||||
void elim_term_ite();
|
||||
void mark_axioms(assertion_set const & s, expr_fast_mark2 & axioms);
|
||||
void unmark_nested_atoms(assertion_set const & s, expr_fast_mark2 & axioms);
|
||||
void assert_axiom(expr * f, bool neg);
|
||||
void register_atom(expr * f, sat::bool_var p);
|
||||
void compile_formulas(assertion_set const & assertions);
|
||||
|
||||
public:
|
||||
formula_compiler(solver_exp & s, params_ref const & p);
|
||||
~formula_compiler();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void collect_param_descrs(param_descrs & d);
|
||||
|
||||
void operator()();
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
void collect_statistics(statistics & st);
|
||||
void reset_statistics();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
232
src/tactic/arith_tactics/smt_solver_exp.cpp
Normal file
232
src/tactic/arith_tactics/smt_solver_exp.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_solver_exp.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT solver using strategies and search on top of sat::solver
|
||||
This solver uses assertion_set strategies during restarts.
|
||||
|
||||
It also uses the sat::solver to handle the Boolean structure of the problem.
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
|
||||
--*/
|
||||
#include"smt_solver_exp.h"
|
||||
#include"sat_solver.h"
|
||||
#include"ast_translation.h"
|
||||
#include"model.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
void solver_exp::bridge::get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r) {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::asserted(sat::literal l) {
|
||||
}
|
||||
|
||||
sat::check_result solver_exp::bridge::check() {
|
||||
return sat::CR_DONE;
|
||||
}
|
||||
|
||||
void solver_exp::bridge::push() {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::pop(unsigned n) {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::simplify() {
|
||||
}
|
||||
|
||||
void solver_exp::bridge::clauses_modifed() {
|
||||
}
|
||||
|
||||
lbool solver_exp::bridge::get_phase(sat::bool_var v) {
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
solver_exp::solver_exp(ast_manager & ext_mng, params_ref const & p):
|
||||
m_ext_mng(ext_mng),
|
||||
m(ext_mng, true /* disable proof gen */),
|
||||
m_compiler(*this, p),
|
||||
m_assertions(m),
|
||||
m_atom2bvar(m),
|
||||
m_arith(m, p),
|
||||
m_bridge(*this) {
|
||||
updt_params_core(p);
|
||||
m_cancel = false;
|
||||
}
|
||||
|
||||
solver_exp::~solver_exp() {
|
||||
}
|
||||
|
||||
void solver_exp::updt_params_core(params_ref const & p) {
|
||||
m_params = p;
|
||||
}
|
||||
|
||||
void solver_exp::updt_params(params_ref const & p) {
|
||||
updt_params_core(p);
|
||||
m_arith.updt_params(p);
|
||||
if (m_sat)
|
||||
m_sat->updt_params(p);
|
||||
}
|
||||
|
||||
void solver_exp::collect_param_descrs(param_descrs & d) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void solver_exp::set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
#pragma omp critical (smt_solver_exp)
|
||||
{
|
||||
if (m_sat) {
|
||||
m_sat->set_cancel(f);
|
||||
}
|
||||
}
|
||||
m_arith.set_cancel(f);
|
||||
m_compiler.set_cancel(f);
|
||||
}
|
||||
|
||||
void solver_exp::init() {
|
||||
m_atom2bvar.reset();
|
||||
if (m_sat)
|
||||
m_sat->collect_statistics(m_stats);
|
||||
#pragma omp critical (smt_solver_exp)
|
||||
{
|
||||
m_sat = alloc(sat::solver, m_params, &m_bridge);
|
||||
}
|
||||
m_arith.collect_statistics(m_stats);
|
||||
m_arith.reset();
|
||||
set_cancel(m_cancel);
|
||||
}
|
||||
|
||||
void solver_exp::assert_expr_core(expr * t, ast_translation & translator) {
|
||||
expr * new_t = translator(t);
|
||||
m_assertions.assert_expr(new_t);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Assert an expression t (owned by the external manager)
|
||||
*/
|
||||
void solver_exp::assert_expr(expr * t) {
|
||||
ast_translation translator(m_ext_mng, m, false);
|
||||
assert_expr_core(t, translator);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Assert an assertion set s (owned by the external manager)
|
||||
*/
|
||||
void solver_exp::assert_set(assertion_set const & s) {
|
||||
SASSERT(&(s.m()) == &m_ext_mng);
|
||||
ast_translation translator(m_ext_mng, m, false);
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
assert_expr_core(s.form(i), translator);
|
||||
}
|
||||
}
|
||||
|
||||
void solver_exp::assert_goal(goal const & g) {
|
||||
SASSERT(&(g.m()) == &m_ext_mng);
|
||||
ast_translation translator(m_ext_mng, m, false);
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
assert_expr_core(g.form(i), translator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Store in r the current set of assertions.
|
||||
r is (owned) by the external assertion set
|
||||
*/
|
||||
void solver_exp::get_assertions(assertion_set & r) {
|
||||
SASSERT(&(r.m()) == &m_ext_mng);
|
||||
ast_translation translator(m, m_ext_mng, false);
|
||||
unsigned sz = m_assertions.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = m_assertions.form(i);
|
||||
r.assert_expr(translator(f));
|
||||
}
|
||||
}
|
||||
|
||||
void solver_exp::get_model_converter(model_converter_ref & mc) {
|
||||
ast_translation translator(m, m_ext_mng, false);
|
||||
if (m_mc)
|
||||
mc = m_mc->translate(translator);
|
||||
else
|
||||
mc = 0;
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Search
|
||||
//
|
||||
// -----------------------
|
||||
lbool solver_exp::check() {
|
||||
compile();
|
||||
lbool r = m_arith.check();
|
||||
if (r == l_false)
|
||||
return r;
|
||||
if (m_sat->num_vars() == 0 && r == l_true) {
|
||||
model_ref md = alloc(model, m);
|
||||
m_arith.mk_model(md.get());
|
||||
if (m_mc)
|
||||
(*m_mc)(md);
|
||||
ast_translation translator(m, m_ext_mng, false);
|
||||
m_model = md->translate(translator);
|
||||
return l_true;
|
||||
}
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
void solver_exp::compile() {
|
||||
m_compiler();
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Pretty Printing
|
||||
//
|
||||
// -----------------------
|
||||
void solver_exp::display(std::ostream & out) const {
|
||||
m_assertions.display(out);
|
||||
}
|
||||
|
||||
void solver_exp::display_state(std::ostream & out) const {
|
||||
if (m_sat) m_sat->display(out);
|
||||
m_arith.display(out);
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Statistics
|
||||
//
|
||||
// -----------------------
|
||||
void solver_exp::collect_statistics(statistics & st) const {
|
||||
solver_exp * _this = const_cast<solver_exp*>(this);
|
||||
if (m_sat) {
|
||||
m_sat->collect_statistics(_this->m_stats);
|
||||
m_sat->reset_statistics();
|
||||
}
|
||||
m_arith.collect_statistics(_this->m_stats);
|
||||
_this->m_arith.reset_statistics();
|
||||
st.copy(m_stats);
|
||||
}
|
||||
|
||||
void solver_exp::reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
130
src/tactic/arith_tactics/smt_solver_exp.h
Normal file
130
src/tactic/arith_tactics/smt_solver_exp.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_solver_exp.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT solver using strategies and search on top of sat::solver
|
||||
This solver uses assertion_set strategies during restarts.
|
||||
|
||||
It also uses the sat::solver to handle the Boolean structure of the problem.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
--*/
|
||||
#ifndef _SMT_SOLVER_EXP_H_
|
||||
#define _SMT_SOLVER_EXP_H_
|
||||
|
||||
#include"smt_solver_types.h"
|
||||
#include"model.h"
|
||||
#include"model_converter.h"
|
||||
#include"smt_formula_compiler.h"
|
||||
#include"smt_arith.h"
|
||||
#include"sat_extension.h"
|
||||
#include"goal.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
class solver_exp {
|
||||
friend class formula_compiler;
|
||||
|
||||
struct bridge : public sat::extension {
|
||||
solver_exp & s;
|
||||
bridge(solver_exp & _s):s(_s) {}
|
||||
virtual void propagate(sat::literal l, sat::ext_constraint_idx idx, bool & keep) {}
|
||||
virtual void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r);
|
||||
virtual void asserted(sat::literal l);
|
||||
virtual sat::check_result check();
|
||||
virtual void push();
|
||||
virtual void pop(unsigned n);
|
||||
virtual void simplify();
|
||||
virtual void clauses_modifed();
|
||||
virtual lbool get_phase(sat::bool_var v);
|
||||
};
|
||||
|
||||
// External ASTs are coming from m_ext_mng
|
||||
ast_manager & m_ext_mng;
|
||||
// The ASTs are translated into the internal manager for the following reasons:
|
||||
// 1. We can run multiple smt::solver_exps in parallel with minimal synchronization
|
||||
// 2. Minimize gaps in the AST ids.
|
||||
ast_manager m; // internal manager
|
||||
params_ref m_params;
|
||||
formula_compiler m_compiler;
|
||||
|
||||
// Set of asserted expressions.
|
||||
// This assertion set belongs to ast_manager m.
|
||||
assertion_set m_assertions;
|
||||
|
||||
model_ref m_model;
|
||||
model_converter_ref m_mc; // chain of model converters
|
||||
|
||||
atom2bool_var m_atom2bvar;
|
||||
scoped_ptr<sat::solver> m_sat;
|
||||
arith m_arith;
|
||||
bridge m_bridge;
|
||||
|
||||
statistics m_stats;
|
||||
|
||||
volatile bool m_cancel;
|
||||
|
||||
void updt_params_core(params_ref const & p);
|
||||
void assert_expr_core(expr * t, ast_translation & translator);
|
||||
|
||||
void init();
|
||||
void compile();
|
||||
|
||||
public:
|
||||
solver_exp(ast_manager & ext_mng, params_ref const & p);
|
||||
~solver_exp();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
void collect_param_descrs(param_descrs & d);
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
void assert_expr(expr * t);
|
||||
void assert_set(assertion_set const & s);
|
||||
void assert_goal(goal const & g);
|
||||
|
||||
void get_assertions(assertion_set & r);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Search
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
lbool check();
|
||||
void get_model(model_ref & m) const { m = m_model.get(); }
|
||||
void get_model_converter(model_converter_ref & mc);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Pretty Printing
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void display(std::ostream & out) const;
|
||||
void display_state(std::ostream & out) const;
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
// Statistics
|
||||
//
|
||||
// -----------------------
|
||||
public:
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
96
src/tactic/arith_tactics/smt_solver_strategy.cpp
Normal file
96
src/tactic/arith_tactics/smt_solver_strategy.cpp
Normal 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);
|
||||
}
|
55
src/tactic/arith_tactics/smt_solver_strategy.h
Normal file
55
src/tactic/arith_tactics/smt_solver_strategy.h
Normal 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
|
47
src/tactic/arith_tactics/smt_solver_types.h
Normal file
47
src/tactic/arith_tactics/smt_solver_types.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
smt_solver_types.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Auxiliary definitions for smt::solver class.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-25.
|
||||
|
||||
Revision History:
|
||||
This was an experiment to rewrite Z3 kernel.
|
||||
It will be deleted after we finish revamping Z3 kernel.
|
||||
|
||||
--*/
|
||||
#ifndef _SMT_SOLVER_TYPES_H_
|
||||
#define _SMT_SOLVER_TYPES_H_
|
||||
|
||||
#include"assertion_set.h"
|
||||
#include"strategy_exception.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
#include"lbool.h"
|
||||
#include"sat_types.h"
|
||||
|
||||
class ast_translation;
|
||||
|
||||
namespace sat {
|
||||
class solver;
|
||||
};
|
||||
|
||||
namespace smt {
|
||||
class solver_exp;
|
||||
class formula_compiler;
|
||||
typedef unsigned atom_id;
|
||||
typedef unsigned_vector atom_id_vector;
|
||||
const atom_id null_atom_id = sat::null_bool_var;
|
||||
};
|
||||
|
||||
MK_ST_EXCEPTION(smt_solver_exception);
|
||||
|
||||
#endif
|
128
src/tactic/bit_blaster/bit_blaster.cpp
Normal file
128
src/tactic/bit_blaster/bit_blaster.cpp
Normal 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) {
|
||||
}
|
65
src/tactic/bit_blaster/bit_blaster.h
Normal file
65
src/tactic/bit_blaster/bit_blaster.h
Normal 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_ */
|
||||
|
187
src/tactic/bit_blaster/bit_blaster_model_converter.cpp
Normal file
187
src/tactic/bit_blaster/bit_blaster_model_converter.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
27
src/tactic/bit_blaster/bit_blaster_model_converter.h
Normal file
27
src/tactic/bit_blaster/bit_blaster_model_converter.h
Normal 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
|
649
src/tactic/bit_blaster/bit_blaster_rewriter.cpp
Normal file
649
src/tactic/bit_blaster/bit_blaster_rewriter.cpp
Normal 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);
|
||||
}
|
||||
|
42
src/tactic/bit_blaster/bit_blaster_rewriter.h
Normal file
42
src/tactic/bit_blaster/bit_blaster_rewriter.h
Normal 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
|
||||
|
135
src/tactic/bit_blaster/bit_blaster_tpl.h
Normal file
135
src/tactic/bit_blaster/bit_blaster_tpl.h
Normal 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
|
1201
src/tactic/bit_blaster/bit_blaster_tpl_def.h
Normal file
1201
src/tactic/bit_blaster/bit_blaster_tpl_def.h
Normal file
File diff suppressed because it is too large
Load diff
508
src/tactic/bit_blaster/bv1_blaster_tactic.cpp
Normal file
508
src/tactic/bit_blaster/bv1_blaster_tactic.cpp
Normal 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);
|
||||
}
|
35
src/tactic/bit_blaster/bv1_blaster_tactic.h
Normal file
35
src/tactic/bit_blaster/bv1_blaster_tactic.h
Normal 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
|
436
src/tactic/bit_blaster/eager_bit_blaster.cpp
Normal file
436
src/tactic/bit_blaster/eager_bit_blaster.cpp
Normal 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);
|
||||
}
|
||||
|
107
src/tactic/bit_blaster/eager_bit_blaster.h
Normal file
107
src/tactic/bit_blaster/eager_bit_blaster.h
Normal 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_ */
|
||||
|
165
src/tactic/bv_tactics/bit_blaster_tactic.cpp
Normal file
165
src/tactic/bv_tactics/bit_blaster_tactic.cpp
Normal 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));
|
||||
}
|
28
src/tactic/bv_tactics/bit_blaster_tactic.h
Normal file
28
src/tactic/bv_tactics/bit_blaster_tactic.h
Normal 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
|
366
src/tactic/bv_tactics/bv_size_reduction_tactic.cpp
Normal file
366
src/tactic/bv_tactics/bv_size_reduction_tactic.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
33
src/tactic/bv_tactics/bv_size_reduction_tactic.h
Normal file
33
src/tactic/bv_tactics/bv_size_reduction_tactic.h
Normal 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
|
337
src/tactic/bv_tactics/max_bv_sharing_tactic.cpp
Normal file
337
src/tactic/bv_tactics/max_bv_sharing_tactic.cpp
Normal 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));
|
||||
}
|
||||
|
32
src/tactic/bv_tactics/max_bv_sharing_tactic.h
Normal file
32
src/tactic/bv_tactics/max_bv_sharing_tactic.h
Normal 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
142
src/tactic/converter.h
Normal 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
|
||||
|
712
src/tactic/core_tactics/cofactor_elim_term_ite.cpp
Normal file
712
src/tactic/core_tactics/cofactor_elim_term_ite.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
44
src/tactic/core_tactics/cofactor_elim_term_ite.h
Normal file
44
src/tactic/core_tactics/cofactor_elim_term_ite.h
Normal 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
|
82
src/tactic/core_tactics/cofactor_term_ite_tactic.cpp
Normal file
82
src/tactic/core_tactics/cofactor_term_ite_tactic.cpp
Normal 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));
|
||||
}
|
29
src/tactic/core_tactics/cofactor_term_ite_tactic.h
Normal file
29
src/tactic/core_tactics/cofactor_term_ite_tactic.h
Normal 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
|
555
src/tactic/core_tactics/ctx_simplify_tactic.cpp
Normal file
555
src/tactic/core_tactics/ctx_simplify_tactic.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
56
src/tactic/core_tactics/ctx_simplify_tactic.h
Normal file
56
src/tactic/core_tactics/ctx_simplify_tactic.h
Normal 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
|
114
src/tactic/core_tactics/der_tactic.cpp
Normal file
114
src/tactic/core_tactics/der_tactic.cpp
Normal 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);
|
||||
}
|
25
src/tactic/core_tactics/der_tactic.h
Normal file
25
src/tactic/core_tactics/der_tactic.h
Normal 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_ */
|
149
src/tactic/core_tactics/distribute_forall_tactic.cpp
Normal file
149
src/tactic/core_tactics/distribute_forall_tactic.cpp
Normal 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);
|
||||
}
|
26
src/tactic/core_tactics/distribute_forall_tactic.h
Normal file
26
src/tactic/core_tactics/distribute_forall_tactic.h
Normal 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
|
198
src/tactic/core_tactics/elim_term_ite_tactic.cpp
Normal file
198
src/tactic/core_tactics/elim_term_ite_tactic.cpp
Normal 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));
|
||||
}
|
29
src/tactic/core_tactics/elim_term_ite_tactic.h
Normal file
29
src/tactic/core_tactics/elim_term_ite_tactic.h
Normal 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
|
1076
src/tactic/core_tactics/elim_uncnstr_tactic.cpp
Normal file
1076
src/tactic/core_tactics/elim_uncnstr_tactic.cpp
Normal file
File diff suppressed because it is too large
Load diff
30
src/tactic/core_tactics/elim_uncnstr_tactic.h
Normal file
30
src/tactic/core_tactics/elim_uncnstr_tactic.h
Normal 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
|
||||
|
126
src/tactic/core_tactics/nnf_tactic.cpp
Normal file
126
src/tactic/core_tactics/nnf_tactic.cpp
Normal 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);
|
||||
}
|
30
src/tactic/core_tactics/nnf_tactic.h
Normal file
30
src/tactic/core_tactics/nnf_tactic.h
Normal 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
|
||||
|
252
src/tactic/core_tactics/occf_tactic.cpp
Normal file
252
src/tactic/core_tactics/occf_tactic.cpp
Normal 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));
|
||||
}
|
||||
|
34
src/tactic/core_tactics/occf_tactic.h
Normal file
34
src/tactic/core_tactics/occf_tactic.h
Normal 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
|
||||
|
275
src/tactic/core_tactics/propagate_values_tactic.cpp
Normal file
275
src/tactic/core_tactics/propagate_values_tactic.cpp
Normal 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));
|
||||
}
|
||||
|
29
src/tactic/core_tactics/propagate_values_tactic.h
Normal file
29
src/tactic/core_tactics/propagate_values_tactic.h
Normal 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
Loading…
Add table
Add a link
Reference in a new issue