Skip to content

Commit

Permalink
add array selects to basic ackerman reduction improves performance si…
Browse files Browse the repository at this point in the history
…gnificantly for #2525 as it now uses the SAT solver core instead of SMT core

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
  • Loading branch information
NikolajBjorner committed Sep 1, 2019
1 parent 7823117 commit 000e485
Show file tree
Hide file tree
Showing 25 changed files with 687 additions and 553 deletions.
2 changes: 1 addition & 1 deletion src/ackermannization/ackermannize_bv_model_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
#include "ackermannization/ackermannize_bv_model_converter.h"

model_converter * mk_ackermannize_bv_model_converter(ast_manager & m, const ackr_info_ref& info) {
return mk_ackr_model_converter(m, info);
return mk_ackr_model_converter(m, info);
}
50 changes: 38 additions & 12 deletions src/ackermannization/ackr_bound_probe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,67 @@

Revision History:
--*/
#include "ast/array_decl_plugin.h"
#include "ast/ast_smt2_pp.h"
#include "ackermannization/ackr_helper.h"
#include "ackermannization/ackr_bound_probe.h"
#include "ast/ast_smt2_pp.h"

/*
For each function f, calculate the number of its occurrences o_f and compute "o_f choose 2".
The probe then sums up these for all functions.
This upper bound might be crude because some congruence lemmas trivially simplify to true.
*/
class ackr_bound_probe : public probe {

struct proc {
typedef ackr_helper::fun2terms_map fun2terms_map;
typedef ackr_helper::sel2terms_map sel2terms_map;
typedef ackr_helper::app_set app_set;
ast_manager& m_m;
ast_manager& m;
fun2terms_map m_fun2terms; // a map from functions to occurrences
sel2terms_map m_sel2terms; // a map from functions to occurrences
ackr_helper m_ackr_helper;
expr_mark m_non_select;

proc(ast_manager & m) : m_m(m), m_ackr_helper(m) { }
proc(ast_manager & m) : m(m), m_ackr_helper(m) { }

~proc() {
fun2terms_map::iterator it = m_fun2terms.begin();
const fun2terms_map::iterator end = m_fun2terms.end();
for (; it != end; ++it) dealloc(it->get_value());
for (auto & kv : m_fun2terms) {
dealloc(kv.m_value);
}
for (auto & kv : m_sel2terms) {
dealloc(kv.m_value);
}
}

void prune_non_select() {
m_ackr_helper.prune_non_select(m_sel2terms, m_non_select);
}

void operator()(quantifier *) {}
void operator()(var *) {}
void operator()(app * a) {
if (a->get_num_args() == 0) return;
if (!m_ackr_helper.should_ackermannize(a)) return;
func_decl* const fd = a->get_decl();
m_ackr_helper.mark_non_select(a, m_non_select);
app_set* ts = nullptr;
if (!m_fun2terms.find(fd, ts)) {
ts = alloc(app_set);
m_fun2terms.insert(fd, ts);
if (m_ackr_helper.is_select(a)) {
app* sel = to_app(a->get_arg(0));
if (!m_sel2terms.find(sel, ts)) {
ts = alloc(app_set);
m_sel2terms.insert(sel, ts);
}
}
else if (m_ackr_helper.is_uninterp_fn(a)) {
func_decl* const fd = a->get_decl();
if (!m_fun2terms.find(fd, ts)) {
ts = alloc(app_set);
m_fun2terms.insert(fd, ts);
}
}
else {
return;
}

ts->insert(a);
}
};
Expand All @@ -64,7 +89,8 @@ class ackr_bound_probe : public probe {
for (unsigned i = 0; i < sz; i++) {
for_each_expr_core<proc, expr_fast_mark1, true, true>(p, visited, g.form(i));
}
const double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms);
p.prune_non_select();
double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms, p.m_sel2terms);
TRACE("ackermannize", tout << "total=" << total << std::endl;);
return result(total);
}
Expand Down
13 changes: 6 additions & 7 deletions src/ackermannization/ackr_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@
--*/
#include "ackermannization/ackr_helper.h"

double ackr_helper::calculate_lemma_bound(ackr_helper::fun2terms_map& occurrences) {
fun2terms_map::iterator it = occurrences.begin();
const fun2terms_map::iterator end = occurrences.end();
double ackr_helper::calculate_lemma_bound(fun2terms_map const& occs1, sel2terms_map const& occs2) {
double total = 0;
for (; it != end; ++it) {
const unsigned fsz = it->m_value->size();
const double n2 = n_choose_2_chk(fsz);
total += n2;
for (auto const& kv : occs1) {
total += n_choose_2_chk(kv.m_value->size());
}
for (auto const& kv : occs2) {
total += n_choose_2_chk(kv.m_value->size());
}
return total;
}
113 changes: 77 additions & 36 deletions src/ackermannization/ackr_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,86 @@
#define ACKR_HELPER_H_

#include "ast/bv_decl_plugin.h"
#include "ast/array_decl_plugin.h"

class ackr_helper {
public:
typedef obj_hashtable<app> app_set;
typedef obj_map<func_decl, app_set*> fun2terms_map;

ackr_helper(ast_manager& m) : m_bvutil(m) {}

/**
\brief Determines if a given function should be Ackermannized.

This includes all uninterpreted functions but also "special" functions, e.g. OP_BSMOD0,
which are not marked as uninterpreted but effectively are.
*/
inline bool should_ackermannize(app const * a) const {
if (is_uninterp(a))
return true;
else {
decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id());
return p->is_considered_uninterpreted(a->get_decl());
public:
typedef obj_hashtable<app> app_set;
typedef obj_map<func_decl, app_set*> fun2terms_map;
typedef obj_map<app, app_set*> sel2terms_map;

ackr_helper(ast_manager& m) : m_bvutil(m), m_autil(m) {}

/**
\brief Determines if a given function should be Ackermannized.

This includes all uninterpreted functions but also "special" functions, e.g. OP_BSMOD0,
which are not marked as uninterpreted but effectively are.
*/
inline bool is_uninterp_fn(app const * a) const {
if (is_uninterp(a))
return true;
else {
decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id());
return p->is_considered_uninterpreted(a->get_decl());
}
}

/**
\brief determines if a term is a candidate select for Ackerman reduction
*/
inline bool is_select(app* a) {
return m_autil.is_select(a) && is_uninterp_const(a->get_arg(0));
}

void mark_non_select(app* a, expr_mark& non_select) {
if (m_autil.is_select(a)) {
bool first = true;
for (expr* arg : *a) {
if (first)
first = false;
else
non_select.mark(arg, true);
}
}

inline bv_util& bvutil() { return m_bvutil; }

/**
\brief Calculates an upper bound for congruence lemmas given a map of function of occurrences.
*/
static double calculate_lemma_bound(fun2terms_map& occurrences);

/** \brief Calculate n choose 2. **/
inline static unsigned n_choose_2(unsigned n) { return n & 1 ? (n * (n >> 1)) : (n >> 1) * (n - 1); }

/** \brief Calculate n choose 2 guarded for overflow. Returns infinity if unsafe. **/
inline static double n_choose_2_chk(unsigned n) {
SASSERT(std::numeric_limits<unsigned>().max() & 32);
return n & (1 << 16) ? std::numeric_limits<double>().infinity() : n_choose_2(n);
else {
for (expr* arg : *a) {
non_select.mark(arg, true);
}
}
}

void prune_non_select(obj_map<app, app_set*> & sels, expr_mark& non_select) {
ptr_vector<app> nons;
for (auto& kv : sels) {
if (non_select.is_marked(kv.m_key)) {
nons.push_back(kv.m_key);
dealloc(kv.m_value);
}
}
for (app* s : nons) {
sels.erase(s);
}
private:
bv_util m_bvutil;
}

inline bv_util& bvutil() { return m_bvutil; }

/**
\brief Calculates an upper bound for congruence lemmas given a map of function of occurrences.
*/
static double calculate_lemma_bound(fun2terms_map const& occs1, sel2terms_map const& occs2);

/** \brief Calculate n choose 2. **/
inline static unsigned n_choose_2(unsigned n) { return n & 1 ? (n * (n >> 1)) : (n >> 1) * (n - 1); }

/** \brief Calculate n choose 2 guarded for overflow. Returns infinity if unsafe. **/
inline static double n_choose_2_chk(unsigned n) {
SASSERT(std::numeric_limits<unsigned>().max() & 32);
return n & (1 << 16) ? std::numeric_limits<double>().infinity() : n_choose_2(n);
}

private:
bv_util m_bvutil;
array_util m_autil;
};
#endif /* ACKR_HELPER_H_6475 */
#endif /* ACKR_HELPER_H_ */
43 changes: 21 additions & 22 deletions src/ackermannization/ackr_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Revision History:
#ifndef ACKR_INFO_H_
#define ACKR_INFO_H_

#include "util/ref.h"
#include "util/obj_hashtable.h"
#include "ast/ast.h"
#include "util/ref.h"
#include "ast/rewriter/expr_replacer.h"
#include "ast/ast_translation.h"

Expand All @@ -34,18 +34,18 @@ Revision History:
**/
class ackr_info {
public:
ackr_info(ast_manager& m)
: m_m(m)
, m_er(mk_default_expr_replacer(m))
, m_subst(m_m)
, m_ref_count(0)
, m_sealed(false)
ackr_info(ast_manager& m) :
m(m),
m_er(mk_default_expr_replacer(m)),
m_subst(m),
m_ref_count(0),
m_sealed(false)
{}

virtual ~ackr_info() {
for (t2ct::iterator i = m_t2c.begin(); i != m_t2c.end(); ++i) {
m_m.dec_ref(i->m_key);
m_m.dec_ref(i->m_value);
for (auto & kv : m_t2c) {
m.dec_ref(kv.m_key);
m.dec_ref(kv.m_value);
}
}

Expand All @@ -55,13 +55,15 @@ class ackr_info {
m_t2c.insert(term,c);
m_c2t.insert(c->get_decl(),term);
m_subst.insert(term, c);
m_m.inc_ref(term);
m_m.inc_ref(c);
m.inc_ref(term);
m.inc_ref(c);
}

inline void abstract(expr * e, expr_ref& res) {
inline expr_ref abstract(expr * e) {
expr_ref res(m);
SASSERT(m_sealed);
(*m_er)(e, res);
return res;
}

inline app* find_term(func_decl* c) const {
Expand All @@ -71,22 +73,18 @@ class ackr_info {
}

inline app* get_abstr(app* term) const {
app * const rv = m_t2c.find(term);
SASSERT(rv);
return rv;
return m_t2c.find(term);
}

inline void seal() {
m_sealed=true;
m_sealed = true;
m_er->set_substitution(&m_subst);
}

virtual ackr_info * translate(ast_translation & translator) {
ackr_info * const retv = alloc(ackr_info, translator.to());
for (t2ct::iterator i = m_t2c.begin(); i != m_t2c.end(); ++i) {
app * const k = translator(i->m_key);
app * const v = translator(i->m_value);
retv->set_abstr(k, v);
for (auto & kv : m_t2c) {
retv->set_abstr(translator(kv.m_key), translator(kv.m_value));
}
if (m_sealed) retv->seal();
return retv;
Expand All @@ -102,10 +100,11 @@ class ackr_info {
dealloc(this);
}
}

private:
typedef obj_map<app, app*> t2ct;
typedef obj_map<func_decl, app*> c2tt;
ast_manager& m_m;
ast_manager& m;

t2ct m_t2c; // terms to constants
c2tt m_c2t; // constants to terms (inversion of m_t2c)
Expand Down
Loading

0 comments on commit 000e485

Please sign in to comment.