mirror of
https://github.com/Z3Prover/z3
synced 2025-06-02 12:21:21 +00:00
experiment with arithmetic core generalizers
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
23c5c94311
commit
2e2fa84d40
5 changed files with 195 additions and 2 deletions
|
@ -44,6 +44,7 @@ def_module_params('fixedpoint',
|
||||||
('coalesce_rules', BOOL, False, "BMC: coalesce rules"),
|
('coalesce_rules', BOOL, False, "BMC: coalesce rules"),
|
||||||
('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"),
|
('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"),
|
||||||
('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"),
|
('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"),
|
||||||
|
('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"),
|
||||||
('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"),
|
('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"),
|
||||||
('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when "
|
('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when "
|
||||||
"checking for reachability (not only during cube weakening)"),
|
"checking for reachability (not only during cube weakening)"),
|
||||||
|
|
|
@ -1477,6 +1477,10 @@ namespace pdr {
|
||||||
if (m_params.inductive_reachability_check()) {
|
if (m_params.inductive_reachability_check()) {
|
||||||
m_core_generalizers.push_back(alloc(core_induction_generalizer, *this));
|
m_core_generalizers.push_back(alloc(core_induction_generalizer, *this));
|
||||||
}
|
}
|
||||||
|
if (m_params.use_arith_inductive_generalizer()) {
|
||||||
|
m_core_generalizers.push_back(alloc(core_arith_inductive_generalizer, *this));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector<relation_info>& rs) const {
|
void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector<relation_info>& rs) const {
|
||||||
|
|
|
@ -28,7 +28,9 @@ Revision History:
|
||||||
namespace pdr {
|
namespace pdr {
|
||||||
|
|
||||||
|
|
||||||
//
|
// ------------------------
|
||||||
|
// core_bool_inductive_generalizer
|
||||||
|
|
||||||
// main propositional induction generalizer.
|
// main propositional induction generalizer.
|
||||||
// drop literals one by one from the core and check if the core is still inductive.
|
// drop literals one by one from the core and check if the core is still inductive.
|
||||||
//
|
//
|
||||||
|
@ -97,6 +99,9 @@ namespace pdr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------
|
||||||
|
// core_farkas_generalizer
|
||||||
|
|
||||||
//
|
//
|
||||||
// for each disjunct of core:
|
// for each disjunct of core:
|
||||||
// weaken predecessor.
|
// weaken predecessor.
|
||||||
|
@ -142,6 +147,158 @@ namespace pdr {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------
|
||||||
|
// core_arith_inductive_generalizer
|
||||||
|
|
||||||
|
core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx):
|
||||||
|
core_generalizer(ctx),
|
||||||
|
m(ctx.get_manager()),
|
||||||
|
a(m),
|
||||||
|
m_refs(m) {}
|
||||||
|
|
||||||
|
void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||||
|
if (core.size() <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
expr_ref e(m), t1(m), t2(m), t3(m);
|
||||||
|
rational r;
|
||||||
|
|
||||||
|
TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; });
|
||||||
|
|
||||||
|
svector<eq> eqs;
|
||||||
|
get_eqs(core, eqs);
|
||||||
|
|
||||||
|
for (unsigned eq = 0; eq < eqs.size(); ++eq) {
|
||||||
|
rational r = eqs[eq].m_value;
|
||||||
|
expr* x = eqs[eq].m_term;
|
||||||
|
unsigned k = eqs[eq].m_i;
|
||||||
|
unsigned l = eqs[eq].m_j;
|
||||||
|
|
||||||
|
expr_ref_vector new_core(m);
|
||||||
|
for (unsigned i = 0; i < core.size(); ++i) {
|
||||||
|
if (i == k || k == l) {
|
||||||
|
new_core.push_back(m.mk_true());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!substitute_alias(r, x, core[i].get(), e)) {
|
||||||
|
e = core[i].get();
|
||||||
|
}
|
||||||
|
new_core.push_back(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (abs(r) >= rational(2) && a.is_int(x)) {
|
||||||
|
new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(abs(r), true)), a.mk_numeral(rational(0), true));
|
||||||
|
new_core[l] = a.mk_ge(x, a.mk_numeral(r, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level);
|
||||||
|
|
||||||
|
IF_VERBOSE(1,
|
||||||
|
verbose_stream() << (inductive?"":"non") << "inductive\n";
|
||||||
|
for (unsigned j = 0; j < new_core.size(); ++j) {
|
||||||
|
verbose_stream() << mk_pp(new_core[j].get(), m) << "\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (inductive) {
|
||||||
|
core.reset();
|
||||||
|
core.append(new_core);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) {
|
||||||
|
if (r.is_neg()) {
|
||||||
|
expr_ref e(m);
|
||||||
|
e = a.mk_uminus(x);
|
||||||
|
m_refs.push_back(e);
|
||||||
|
x = e;
|
||||||
|
is_lower = !is_lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_lower) {
|
||||||
|
m_lb.insert(abs(r), std::make_pair(x, i));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_ub.insert(abs(r), std::make_pair(x, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void core_arith_inductive_generalizer::reset() {
|
||||||
|
m_refs.reset();
|
||||||
|
m_lb.reset();
|
||||||
|
m_ub.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector<eq>& eqs) {
|
||||||
|
expr* e1, *x, *y;
|
||||||
|
expr_ref e(m);
|
||||||
|
rational r;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < core.size(); ++i) {
|
||||||
|
e = core[i];
|
||||||
|
if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
|
||||||
|
// not (<= x r) <=> x >= r + 1
|
||||||
|
insert_bound(true, x, r + rational(1), i);
|
||||||
|
}
|
||||||
|
else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
|
||||||
|
// not (>= x r) <=> x <= r - 1
|
||||||
|
insert_bound(false, x, r - rational(1), i);
|
||||||
|
}
|
||||||
|
else if (a.is_le(e, x, y) && a.is_numeral(y, r)) {
|
||||||
|
insert_bound(false, x, r, i);
|
||||||
|
}
|
||||||
|
else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) {
|
||||||
|
insert_bound(true, x, r, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bounds_t::iterator it = m_lb.begin(), end = m_lb.end();
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
rational r = it->m_key;
|
||||||
|
vector<term_loc_t> & terms1 = it->m_value;
|
||||||
|
vector<term_loc_t> terms2;
|
||||||
|
if (r >= rational(2) && m_ub.find(r, terms2)) {
|
||||||
|
bool done = false;
|
||||||
|
for (unsigned i = 0; !done && i < terms1.size(); ++i) {
|
||||||
|
for (unsigned j = 0; !done && j < terms2.size(); ++j) {
|
||||||
|
expr* t1 = terms1[i].first;
|
||||||
|
expr* t2 = terms2[j].first;
|
||||||
|
if (t1 == t2) {
|
||||||
|
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
e = m.mk_eq(t1, t2);
|
||||||
|
th_rewriter rw(m);
|
||||||
|
rw(e);
|
||||||
|
if (m.is_true(e)) {
|
||||||
|
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) {
|
||||||
|
rational r2;
|
||||||
|
expr* y, *z, *e1;
|
||||||
|
if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) {
|
||||||
|
result = m.mk_not(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a.is_le(e, y, z) && a.is_numeral(z, r2) && r == r2) {
|
||||||
|
result = a.mk_le(y, x);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a.is_ge(e, y, z) && a.is_numeral(z, r2) && r == r2) {
|
||||||
|
result = a.mk_ge(y, x);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -33,6 +33,37 @@ namespace pdr {
|
||||||
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
|
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class r_map : public map<rational, T, rational::hash_proc, rational::eq_proc> {
|
||||||
|
};
|
||||||
|
|
||||||
|
class core_arith_inductive_generalizer : public core_generalizer {
|
||||||
|
typedef std::pair<expr*, unsigned> term_loc_t;
|
||||||
|
typedef r_map<vector<term_loc_t> > bounds_t;
|
||||||
|
|
||||||
|
ast_manager& m;
|
||||||
|
arith_util a;
|
||||||
|
expr_ref_vector m_refs;
|
||||||
|
bounds_t m_lb;
|
||||||
|
bounds_t m_ub;
|
||||||
|
|
||||||
|
struct eq {
|
||||||
|
expr* m_term;
|
||||||
|
rational m_value;
|
||||||
|
unsigned m_i;
|
||||||
|
unsigned m_j;
|
||||||
|
eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {}
|
||||||
|
};
|
||||||
|
void reset();
|
||||||
|
void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i);
|
||||||
|
void get_eqs(expr_ref_vector const& core, svector<eq>& eqs);
|
||||||
|
bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result);
|
||||||
|
public:
|
||||||
|
core_arith_inductive_generalizer(context& ctx);
|
||||||
|
virtual ~core_arith_inductive_generalizer() {}
|
||||||
|
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
|
||||||
|
};
|
||||||
|
|
||||||
class core_farkas_generalizer : public core_generalizer {
|
class core_farkas_generalizer : public core_generalizer {
|
||||||
farkas_learner m_farkas_learner;
|
farkas_learner m_farkas_learner;
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -88,7 +88,6 @@ namespace pdr {
|
||||||
for (unsigned i = 0; i < assumptions.size(); ++i) {
|
for (unsigned i = 0; i < assumptions.size(); ++i) {
|
||||||
pp.add_assumption(assumptions[i].get());
|
pp.add_assumption(assumptions[i].get());
|
||||||
}
|
}
|
||||||
pp.display_smt2(tout, m.mk_true());
|
|
||||||
|
|
||||||
static unsigned lemma_id = 0;
|
static unsigned lemma_id = 0;
|
||||||
std::ostringstream strm;
|
std::ostringstream strm;
|
||||||
|
@ -97,6 +96,7 @@ namespace pdr {
|
||||||
pp.display_smt2(out, m.mk_true());
|
pp.display_smt2(out, m.mk_true());
|
||||||
out.close();
|
out.close();
|
||||||
lemma_id++;
|
lemma_id++;
|
||||||
|
tout << "pdr_check: " << strm.str() << "\n";
|
||||||
});
|
});
|
||||||
lbool result = m_context.check(assumptions.size(), assumptions.c_ptr());
|
lbool result = m_context.check(assumptions.size(), assumptions.c_ptr());
|
||||||
if (!m.is_true(m_pred)) {
|
if (!m.is_true(m_pred)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue