Skip to content

Commit 000e485

Browse files
add array selects to basic ackerman reduction improves performance significantly for #2525 as it now uses the SAT solver core instead of SMT core
Signed-off-by: Nikolaj Bjorner <[email protected]>
1 parent 7823117 commit 000e485

25 files changed

+687
-553
lines changed

src/ackermannization/ackermannize_bv_model_converter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@
1818
#include "ackermannization/ackermannize_bv_model_converter.h"
1919

2020
model_converter * mk_ackermannize_bv_model_converter(ast_manager & m, const ackr_info_ref& info) {
21-
return mk_ackr_model_converter(m, info);
21+
return mk_ackr_model_converter(m, info);
2222
}

src/ackermannization/ackr_bound_probe.cpp

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,67 @@
1414
1515
Revision History:
1616
--*/
17+
#include "ast/array_decl_plugin.h"
18+
#include "ast/ast_smt2_pp.h"
1719
#include "ackermannization/ackr_helper.h"
1820
#include "ackermannization/ackr_bound_probe.h"
19-
#include "ast/ast_smt2_pp.h"
2021

2122
/*
2223
For each function f, calculate the number of its occurrences o_f and compute "o_f choose 2".
2324
The probe then sums up these for all functions.
2425
This upper bound might be crude because some congruence lemmas trivially simplify to true.
2526
*/
2627
class ackr_bound_probe : public probe {
28+
2729
struct proc {
2830
typedef ackr_helper::fun2terms_map fun2terms_map;
31+
typedef ackr_helper::sel2terms_map sel2terms_map;
2932
typedef ackr_helper::app_set app_set;
30-
ast_manager& m_m;
33+
ast_manager& m;
3134
fun2terms_map m_fun2terms; // a map from functions to occurrences
35+
sel2terms_map m_sel2terms; // a map from functions to occurrences
3236
ackr_helper m_ackr_helper;
37+
expr_mark m_non_select;
3338

34-
proc(ast_manager & m) : m_m(m), m_ackr_helper(m) { }
39+
proc(ast_manager & m) : m(m), m_ackr_helper(m) { }
3540

3641
~proc() {
37-
fun2terms_map::iterator it = m_fun2terms.begin();
38-
const fun2terms_map::iterator end = m_fun2terms.end();
39-
for (; it != end; ++it) dealloc(it->get_value());
42+
for (auto & kv : m_fun2terms) {
43+
dealloc(kv.m_value);
44+
}
45+
for (auto & kv : m_sel2terms) {
46+
dealloc(kv.m_value);
47+
}
48+
}
49+
50+
void prune_non_select() {
51+
m_ackr_helper.prune_non_select(m_sel2terms, m_non_select);
4052
}
4153

4254
void operator()(quantifier *) {}
4355
void operator()(var *) {}
4456
void operator()(app * a) {
4557
if (a->get_num_args() == 0) return;
46-
if (!m_ackr_helper.should_ackermannize(a)) return;
47-
func_decl* const fd = a->get_decl();
58+
m_ackr_helper.mark_non_select(a, m_non_select);
4859
app_set* ts = nullptr;
49-
if (!m_fun2terms.find(fd, ts)) {
50-
ts = alloc(app_set);
51-
m_fun2terms.insert(fd, ts);
60+
if (m_ackr_helper.is_select(a)) {
61+
app* sel = to_app(a->get_arg(0));
62+
if (!m_sel2terms.find(sel, ts)) {
63+
ts = alloc(app_set);
64+
m_sel2terms.insert(sel, ts);
65+
}
66+
}
67+
else if (m_ackr_helper.is_uninterp_fn(a)) {
68+
func_decl* const fd = a->get_decl();
69+
if (!m_fun2terms.find(fd, ts)) {
70+
ts = alloc(app_set);
71+
m_fun2terms.insert(fd, ts);
72+
}
73+
}
74+
else {
75+
return;
5276
}
77+
5378
ts->insert(a);
5479
}
5580
};
@@ -64,7 +89,8 @@ class ackr_bound_probe : public probe {
6489
for (unsigned i = 0; i < sz; i++) {
6590
for_each_expr_core<proc, expr_fast_mark1, true, true>(p, visited, g.form(i));
6691
}
67-
const double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms);
92+
p.prune_non_select();
93+
double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms, p.m_sel2terms);
6894
TRACE("ackermannize", tout << "total=" << total << std::endl;);
6995
return result(total);
7096
}

src/ackermannization/ackr_helper.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@
1616
--*/
1717
#include "ackermannization/ackr_helper.h"
1818

19-
double ackr_helper::calculate_lemma_bound(ackr_helper::fun2terms_map& occurrences) {
20-
fun2terms_map::iterator it = occurrences.begin();
21-
const fun2terms_map::iterator end = occurrences.end();
19+
double ackr_helper::calculate_lemma_bound(fun2terms_map const& occs1, sel2terms_map const& occs2) {
2220
double total = 0;
23-
for (; it != end; ++it) {
24-
const unsigned fsz = it->m_value->size();
25-
const double n2 = n_choose_2_chk(fsz);
26-
total += n2;
21+
for (auto const& kv : occs1) {
22+
total += n_choose_2_chk(kv.m_value->size());
23+
}
24+
for (auto const& kv : occs2) {
25+
total += n_choose_2_chk(kv.m_value->size());
2726
}
2827
return total;
2928
}

src/ackermannization/ackr_helper.h

Lines changed: 77 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,45 +18,86 @@
1818
#define ACKR_HELPER_H_
1919

2020
#include "ast/bv_decl_plugin.h"
21+
#include "ast/array_decl_plugin.h"
2122

2223
class ackr_helper {
23-
public:
24-
typedef obj_hashtable<app> app_set;
25-
typedef obj_map<func_decl, app_set*> fun2terms_map;
26-
27-
ackr_helper(ast_manager& m) : m_bvutil(m) {}
28-
29-
/**
30-
\brief Determines if a given function should be Ackermannized.
31-
32-
This includes all uninterpreted functions but also "special" functions, e.g. OP_BSMOD0,
33-
which are not marked as uninterpreted but effectively are.
34-
*/
35-
inline bool should_ackermannize(app const * a) const {
36-
if (is_uninterp(a))
37-
return true;
38-
else {
39-
decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id());
40-
return p->is_considered_uninterpreted(a->get_decl());
24+
public:
25+
typedef obj_hashtable<app> app_set;
26+
typedef obj_map<func_decl, app_set*> fun2terms_map;
27+
typedef obj_map<app, app_set*> sel2terms_map;
28+
29+
ackr_helper(ast_manager& m) : m_bvutil(m), m_autil(m) {}
30+
31+
/**
32+
\brief Determines if a given function should be Ackermannized.
33+
34+
This includes all uninterpreted functions but also "special" functions, e.g. OP_BSMOD0,
35+
which are not marked as uninterpreted but effectively are.
36+
*/
37+
inline bool is_uninterp_fn(app const * a) const {
38+
if (is_uninterp(a))
39+
return true;
40+
else {
41+
decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id());
42+
return p->is_considered_uninterpreted(a->get_decl());
43+
}
44+
}
45+
46+
/**
47+
\brief determines if a term is a candidate select for Ackerman reduction
48+
*/
49+
inline bool is_select(app* a) {
50+
return m_autil.is_select(a) && is_uninterp_const(a->get_arg(0));
51+
}
52+
53+
void mark_non_select(app* a, expr_mark& non_select) {
54+
if (m_autil.is_select(a)) {
55+
bool first = true;
56+
for (expr* arg : *a) {
57+
if (first)
58+
first = false;
59+
else
60+
non_select.mark(arg, true);
4161
}
4262
}
43-
44-
inline bv_util& bvutil() { return m_bvutil; }
45-
46-
/**
47-
\brief Calculates an upper bound for congruence lemmas given a map of function of occurrences.
48-
*/
49-
static double calculate_lemma_bound(fun2terms_map& occurrences);
50-
51-
/** \brief Calculate n choose 2. **/
52-
inline static unsigned n_choose_2(unsigned n) { return n & 1 ? (n * (n >> 1)) : (n >> 1) * (n - 1); }
53-
54-
/** \brief Calculate n choose 2 guarded for overflow. Returns infinity if unsafe. **/
55-
inline static double n_choose_2_chk(unsigned n) {
56-
SASSERT(std::numeric_limits<unsigned>().max() & 32);
57-
return n & (1 << 16) ? std::numeric_limits<double>().infinity() : n_choose_2(n);
63+
else {
64+
for (expr* arg : *a) {
65+
non_select.mark(arg, true);
66+
}
67+
}
68+
}
69+
70+
void prune_non_select(obj_map<app, app_set*> & sels, expr_mark& non_select) {
71+
ptr_vector<app> nons;
72+
for (auto& kv : sels) {
73+
if (non_select.is_marked(kv.m_key)) {
74+
nons.push_back(kv.m_key);
75+
dealloc(kv.m_value);
76+
}
77+
}
78+
for (app* s : nons) {
79+
sels.erase(s);
5880
}
59-
private:
60-
bv_util m_bvutil;
81+
}
82+
83+
inline bv_util& bvutil() { return m_bvutil; }
84+
85+
/**
86+
\brief Calculates an upper bound for congruence lemmas given a map of function of occurrences.
87+
*/
88+
static double calculate_lemma_bound(fun2terms_map const& occs1, sel2terms_map const& occs2);
89+
90+
/** \brief Calculate n choose 2. **/
91+
inline static unsigned n_choose_2(unsigned n) { return n & 1 ? (n * (n >> 1)) : (n >> 1) * (n - 1); }
92+
93+
/** \brief Calculate n choose 2 guarded for overflow. Returns infinity if unsafe. **/
94+
inline static double n_choose_2_chk(unsigned n) {
95+
SASSERT(std::numeric_limits<unsigned>().max() & 32);
96+
return n & (1 << 16) ? std::numeric_limits<double>().infinity() : n_choose_2(n);
97+
}
98+
99+
private:
100+
bv_util m_bvutil;
101+
array_util m_autil;
61102
};
62-
#endif /* ACKR_HELPER_H_6475 */
103+
#endif /* ACKR_HELPER_H_ */

src/ackermannization/ackr_info.h

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ Revision History:
1616
#ifndef ACKR_INFO_H_
1717
#define ACKR_INFO_H_
1818

19+
#include "util/ref.h"
1920
#include "util/obj_hashtable.h"
2021
#include "ast/ast.h"
21-
#include "util/ref.h"
2222
#include "ast/rewriter/expr_replacer.h"
2323
#include "ast/ast_translation.h"
2424

@@ -34,18 +34,18 @@ Revision History:
3434
**/
3535
class ackr_info {
3636
public:
37-
ackr_info(ast_manager& m)
38-
: m_m(m)
39-
, m_er(mk_default_expr_replacer(m))
40-
, m_subst(m_m)
41-
, m_ref_count(0)
42-
, m_sealed(false)
37+
ackr_info(ast_manager& m) :
38+
m(m),
39+
m_er(mk_default_expr_replacer(m)),
40+
m_subst(m),
41+
m_ref_count(0),
42+
m_sealed(false)
4343
{}
4444

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

@@ -55,13 +55,15 @@ class ackr_info {
5555
m_t2c.insert(term,c);
5656
m_c2t.insert(c->get_decl(),term);
5757
m_subst.insert(term, c);
58-
m_m.inc_ref(term);
59-
m_m.inc_ref(c);
58+
m.inc_ref(term);
59+
m.inc_ref(c);
6060
}
6161

62-
inline void abstract(expr * e, expr_ref& res) {
62+
inline expr_ref abstract(expr * e) {
63+
expr_ref res(m);
6364
SASSERT(m_sealed);
6465
(*m_er)(e, res);
66+
return res;
6567
}
6668

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

7375
inline app* get_abstr(app* term) const {
74-
app * const rv = m_t2c.find(term);
75-
SASSERT(rv);
76-
return rv;
76+
return m_t2c.find(term);
7777
}
7878

7979
inline void seal() {
80-
m_sealed=true;
80+
m_sealed = true;
8181
m_er->set_substitution(&m_subst);
8282
}
8383

8484
virtual ackr_info * translate(ast_translation & translator) {
8585
ackr_info * const retv = alloc(ackr_info, translator.to());
86-
for (t2ct::iterator i = m_t2c.begin(); i != m_t2c.end(); ++i) {
87-
app * const k = translator(i->m_key);
88-
app * const v = translator(i->m_value);
89-
retv->set_abstr(k, v);
86+
for (auto & kv : m_t2c) {
87+
retv->set_abstr(translator(kv.m_key), translator(kv.m_value));
9088
}
9189
if (m_sealed) retv->seal();
9290
return retv;
@@ -102,10 +100,11 @@ class ackr_info {
102100
dealloc(this);
103101
}
104102
}
103+
105104
private:
106105
typedef obj_map<app, app*> t2ct;
107106
typedef obj_map<func_decl, app*> c2tt;
108-
ast_manager& m_m;
107+
ast_manager& m;
109108

110109
t2ct m_t2c; // terms to constants
111110
c2tt m_c2t; // constants to terms (inversion of m_t2c)

0 commit comments

Comments
 (0)