mirror of
https://github.com/Z3Prover/z3
synced 2025-05-09 00:35:47 +00:00
Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
3f9edad676
commit
e9eab22e5c
1186 changed files with 381859 additions and 0 deletions
337
lib/max_bv_sharing_tactic.cpp
Normal file
337
lib/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));
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue