mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 18:05:21 +00:00
Initial commit of QGen
Controlled by fixedpoint.spacer.use_quanti_generalizer measure cumulative time, number of invocations, and number of failed SMT calls Relaxing equality in a pattern: if a variable equals a numeral, relax with GE pob::get_skolems() returns all skolems that might appear in the pob. New skolems must be added above the largest index in that map, even if they are not used in the pob itself. pattern generalization should be done before the pattern is skolemized and added into the new cube.
This commit is contained in:
parent
a1efb88318
commit
23a8e59493
|
@ -187,7 +187,8 @@ def_module_params('fixedpoint',
|
|||
('spacer.reuse_pobs', BOOL, True, 'reuse POBs'),
|
||||
('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'),
|
||||
('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes'),
|
||||
('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints')
|
||||
('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints'),
|
||||
('spacer.use_quant_generalizer', BOOL, False, 'use quantified lemma generalizer'),
|
||||
))
|
||||
|
||||
|
||||
|
|
|
@ -1894,7 +1894,6 @@ void pob::set_post(expr* post, app_ref_vector const &b) {
|
|||
expr_safe_replace sub(m);
|
||||
for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) {
|
||||
expr* e;
|
||||
|
||||
e = m_binding.get(i);
|
||||
pinned.push_back (mk_zk_const (m, i, get_sort(e)));
|
||||
sub.insert (e, pinned.back());
|
||||
|
@ -1939,7 +1938,6 @@ void pob::close () {
|
|||
void pob::get_skolems(app_ref_vector &v) {
|
||||
for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) {
|
||||
expr* e;
|
||||
|
||||
e = m_binding.get(i);
|
||||
v.push_back (mk_zk_const (get_ast_manager(), i, get_sort(e)));
|
||||
}
|
||||
|
@ -2273,6 +2271,11 @@ void context::init_lemma_generalizers(datalog::rule_set& rules)
|
|||
fparams.m_ng_lift_ite = LI_FULL;
|
||||
}
|
||||
|
||||
if (m_params.spacer_use_quant_generalizer()) {
|
||||
m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, false));
|
||||
m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this));
|
||||
}
|
||||
|
||||
if (get_params().spacer_use_eqclass()) {
|
||||
m_lemma_generalizers.push_back (alloc(lemma_eq_generalizer, *this));
|
||||
}
|
||||
|
|
|
@ -527,7 +527,9 @@ public:
|
|||
unsigned get_free_vars_size() { return m_binding.size(); }
|
||||
app_ref_vector const &get_binding() const {return m_binding;}
|
||||
/*
|
||||
* Return skolem variables that appear in post
|
||||
* Returns a map from variable id to skolems that implicitly
|
||||
* represent them in the pob. Note that only some (or none) of the
|
||||
* skolems returned actually appear in the post of the pob.
|
||||
*/
|
||||
void get_skolems(app_ref_vector& v);
|
||||
|
||||
|
|
|
@ -326,4 +326,6 @@ void lemma_eq_generalizer::operator() (lemma_ref &lemma)
|
|||
lemma->update_cube(lemma->get_pob(), core);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -97,6 +97,54 @@ public:
|
|||
void operator()(lemma_ref &lemma) override;
|
||||
};
|
||||
|
||||
class lemma_quantifier_generalizer : public lemma_generalizer {
|
||||
struct stats {
|
||||
unsigned count;
|
||||
unsigned num_failures;
|
||||
stopwatch watch;
|
||||
stats() {reset();}
|
||||
void reset() {count = 0; num_failures = 0; watch.reset();}
|
||||
};
|
||||
|
||||
ast_manager &m;
|
||||
arith_util m_arith;
|
||||
stats m_st;
|
||||
public:
|
||||
lemma_quantifier_generalizer(context &ctx);
|
||||
virtual ~lemma_quantifier_generalizer() {}
|
||||
virtual void operator()(lemma_ref &lemma);
|
||||
|
||||
virtual void collect_statistics(statistics& st) const;
|
||||
virtual void reset_statistics() {m_st.reset();}
|
||||
private:
|
||||
bool match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk);
|
||||
void cleanup(expr_ref_vector& cube, app_ref_vector const &zks, expr_ref &bind);
|
||||
void generalize_pattern_lits(expr_ref_vector &pats);
|
||||
void find_candidates(
|
||||
expr *e,
|
||||
app_ref_vector &candidate);
|
||||
void generate_patterns(
|
||||
expr_ref_vector const &cube,
|
||||
app_ref_vector const &candidates,
|
||||
var_ref_vector &subs,
|
||||
expr_ref_vector &patterns,
|
||||
unsigned offset);
|
||||
void find_matching_expressions(
|
||||
expr_ref_vector const &cube,
|
||||
var_ref_vector const &subs,
|
||||
expr_ref_vector &patterns,
|
||||
vector<expr_ref_vector> &idx_instances,
|
||||
vector<bool> &dirty);
|
||||
void find_guards(
|
||||
expr_ref_vector const &indices,
|
||||
expr_ref &lower,
|
||||
expr_ref &upper);
|
||||
void add_lower_bounds(
|
||||
var_ref_vector const &subs,
|
||||
app_ref_vector const &zks,
|
||||
vector<expr_ref_vector> const &idx_instances,
|
||||
expr_ref_vector &cube);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -340,22 +340,27 @@ app* mk_zk_const(ast_manager &m, unsigned idx, sort *s) {
|
|||
|
||||
namespace find_zk_const_ns {
|
||||
struct proc {
|
||||
int m_max;
|
||||
app_ref_vector &m_out;
|
||||
proc (app_ref_vector &out) : m_out(out) {}
|
||||
proc (app_ref_vector &out) : m_max(-1), m_out(out) {}
|
||||
void operator() (var const * n) const {}
|
||||
void operator() (app *n) const {
|
||||
if (is_uninterp_const(n) &&
|
||||
n->get_decl()->get_name().str().compare (0, 3, "sk!") == 0) {
|
||||
m_out.push_back (n);
|
||||
void operator() (app *n) {
|
||||
int idx;
|
||||
if (is_zk_const(n, idx)) {
|
||||
m_out.push_back(n);
|
||||
if (idx > m_max) {
|
||||
m_max = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
void operator() (quantifier const *n) const {}
|
||||
};
|
||||
}
|
||||
|
||||
void find_zk_const(expr *e, app_ref_vector &res) {
|
||||
int find_zk_const(expr *e, app_ref_vector &res) {
|
||||
find_zk_const_ns::proc p(res);
|
||||
for_each_expr (p, e);
|
||||
return p.m_max;
|
||||
}
|
||||
|
||||
namespace has_zk_const_ns {
|
||||
|
@ -363,8 +368,8 @@ struct found {};
|
|||
struct proc {
|
||||
void operator() (var const *n) const {}
|
||||
void operator() (app const *n) const {
|
||||
if (is_uninterp_const(n) &&
|
||||
n->get_decl()->get_name().str().compare(0, 3, "sk!") == 0) {
|
||||
int idx;
|
||||
if (is_zk_const(n, idx)) {
|
||||
throw found();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,8 +339,12 @@ public:
|
|||
};
|
||||
|
||||
app* mk_zk_const (ast_manager &m, unsigned idx, sort *s);
|
||||
void find_zk_const(expr* e, app_ref_vector &out);
|
||||
int find_zk_const(expr* e, app_ref_vector &out);
|
||||
inline int find_zk_const(expr_ref_vector const &v, app_ref_vector &out) {
|
||||
return find_zk_const (mk_and(v), out);
|
||||
}
|
||||
bool has_zk_const(expr* e);
|
||||
bool is_zk_const (const app *a, int &n);
|
||||
|
||||
struct sk_lt_proc {
|
||||
bool operator()(const app* a1, const app* a2);
|
||||
|
|
499
src/muz/spacer/spacer_quant_generalizer.cpp
Normal file
499
src/muz/spacer/spacer_quant_generalizer.cpp
Normal file
|
@ -0,0 +1,499 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_quant_generalizer.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Quantified lemma generalizer.
|
||||
|
||||
Author:
|
||||
|
||||
|
||||
Yakir Vizel
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "muz/spacer/spacer_generalizers.h"
|
||||
#include "muz/spacer/spacer_manager.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/factor_equivs.h"
|
||||
#include "muz/spacer/spacer_term_graph.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "ast/substitution/matcher.h"
|
||||
#include "ast/expr_functors.h"
|
||||
|
||||
#include "muz/spacer/spacer_sem_matcher.h"
|
||||
|
||||
using namespace spacer;
|
||||
|
||||
namespace {
|
||||
struct index_lt_proc : public std::binary_function<app*, app *, bool> {
|
||||
arith_util m_arith;
|
||||
index_lt_proc(ast_manager &m) : m_arith(m) {}
|
||||
bool operator() (app *a, app *b) {
|
||||
// XXX This order is a bit strange.
|
||||
// XXX It does the job in our current application, but only because
|
||||
// XXX we assume that we only compare expressions of the form (B + k),
|
||||
// XXX where B is fixed and k is a number.
|
||||
// XXX Might be better to specialize just for that specific use case.
|
||||
rational val1, val2;
|
||||
bool is_num1 = m_arith.is_numeral(a, val1);
|
||||
bool is_num2 = m_arith.is_numeral(b, val2);
|
||||
|
||||
if (is_num1 && is_num2) {
|
||||
return val1 < val2;
|
||||
}
|
||||
else if (is_num1 != is_num2) {
|
||||
return is_num1;
|
||||
}
|
||||
|
||||
is_num1 = false;
|
||||
is_num2 = false;
|
||||
// compare the first numeric argument of a to first numeric argument of b
|
||||
// if available
|
||||
for (unsigned i = 0, sz = a->get_num_args(); !is_num1 && i < sz; ++i) {
|
||||
is_num1 = m_arith.is_numeral (a->get_arg(i), val1);
|
||||
}
|
||||
for (unsigned i = 0, sz = b->get_num_args(); !is_num2 && i < sz; ++i) {
|
||||
is_num2 = m_arith.is_numeral(b->get_arg(i), val2);
|
||||
}
|
||||
|
||||
if (is_num1 && is_num2) {
|
||||
return val1 < val2;
|
||||
}
|
||||
else if (is_num1 != is_num2) {
|
||||
return is_num1;
|
||||
}
|
||||
else {
|
||||
return a->get_id() < b->get_id();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace spacer {
|
||||
|
||||
lemma_quantifier_generalizer::lemma_quantifier_generalizer(context &ctx) :
|
||||
lemma_generalizer(ctx), m(ctx.get_ast_manager()), m_arith(m) {}
|
||||
|
||||
void lemma_quantifier_generalizer::collect_statistics(statistics &st) const
|
||||
{
|
||||
st.update("time.spacer.solve.reach.gen.quant", m_st.watch.get_seconds());
|
||||
st.update("quantifier gen", m_st.count);
|
||||
st.update("quantifier gen failures", m_st.num_failures);
|
||||
}
|
||||
|
||||
// XXX What does this do?
|
||||
void lemma_quantifier_generalizer::find_candidates(
|
||||
expr *e, app_ref_vector &candidates)
|
||||
{
|
||||
if (!contains_selects(e, m)) return;
|
||||
|
||||
app_ref_vector indices(m);
|
||||
get_select_indices(e, indices, m);
|
||||
|
||||
app_ref_vector extra(m);
|
||||
expr_sparse_mark marked_args;
|
||||
|
||||
// Make sure not to try and quantify already-quantified indices
|
||||
for (unsigned idx=0, sz = indices.size(); idx < sz; idx++) {
|
||||
// skip expressions that already contain a quantified variable
|
||||
if (has_zk_const(indices.get(idx))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
app *index = indices.get(idx);
|
||||
TRACE ("spacer_qgen",
|
||||
tout << "Candidate: "<< mk_pp(index, m)
|
||||
<< " in " << mk_pp(e, m) << "\n";);
|
||||
extra.push_back(index);
|
||||
// XXX expand one level of arguments. Might want to go deeper.
|
||||
for (unsigned j = 0, asz = index->get_num_args(); j < asz; j++) {
|
||||
expr *arg = index->get_arg(j);
|
||||
if (!is_app(arg) || marked_args.is_marked(arg)) {continue;}
|
||||
marked_args.mark(arg);
|
||||
candidates.push_back (to_app(arg));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(candidates.c_ptr(), candidates.c_ptr() + candidates.size(),
|
||||
index_lt_proc(m));
|
||||
// keep actual select indecies in the order found at the back of
|
||||
// candidate list
|
||||
|
||||
// XXX this corresponds to current implementation. Probably should
|
||||
// XXX be sorted together with the rest of candidates
|
||||
candidates.append(extra);
|
||||
}
|
||||
|
||||
/* subs are the variables that might appear in the patterns */
|
||||
void lemma_quantifier_generalizer::generate_patterns(
|
||||
expr_ref_vector const &cube, app_ref_vector const &candidates,
|
||||
var_ref_vector &subs, expr_ref_vector &patterns, unsigned offset)
|
||||
{
|
||||
if (candidates.empty()) return;
|
||||
|
||||
expr_safe_replace ses(m);
|
||||
|
||||
// Setup substitution
|
||||
for (unsigned i=0; i < candidates.size(); i++) {
|
||||
expr *cand = candidates.get(i);
|
||||
var *var = m.mk_var(i+offset, get_sort(cand));
|
||||
ses.insert(cand, var);
|
||||
rational val;
|
||||
if (m_arith.is_numeral(cand, val)) {
|
||||
bool is_int = val.is_int();
|
||||
ses.insert(
|
||||
m_arith.mk_numeral(rational(val+1), is_int),
|
||||
m_arith.mk_add(var, m_arith.mk_numeral(rational(1), is_int)));
|
||||
ses.insert(
|
||||
m_arith.mk_numeral(rational(-1*(val+1)), is_int),
|
||||
m_arith.mk_add(m_arith.mk_sub(m_arith.mk_numeral(rational(0), is_int),var), m_arith.mk_numeral(rational(-1), is_int)));
|
||||
}
|
||||
subs.push_back(var);
|
||||
}
|
||||
|
||||
// Generate patterns
|
||||
|
||||
// for every literal
|
||||
for (unsigned j=0; j < cube.size(); j++) {
|
||||
// abstract terms by variables
|
||||
expr_ref pat(m);
|
||||
ses(cube.get(j), pat);
|
||||
|
||||
if (pat.get() != cube.get(j)) {
|
||||
// if abstraction is not identity
|
||||
TRACE("spacer_qgen",
|
||||
tout << "Pattern is: " << mk_pp(pat, m) << "\n";);
|
||||
|
||||
// store the pattern
|
||||
patterns.push_back(pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lemma_quantifier_generalizer::find_matching_expressions(
|
||||
expr_ref_vector const &cube,
|
||||
var_ref_vector const &subs, expr_ref_vector &patterns,
|
||||
vector<expr_ref_vector> &idx_instances,
|
||||
vector<bool> &dirty)
|
||||
{
|
||||
idx_instances.resize(subs.size(), expr_ref_vector(m));
|
||||
// -- for every pattern
|
||||
for (unsigned p = 0; p < patterns.size(); p++) {
|
||||
expr *pattern = patterns.get(p);
|
||||
// -- for every literal
|
||||
for (unsigned j = 0; j < cube.size(); j++) {
|
||||
if (dirty[j] || !is_app(cube.get(j))) continue;
|
||||
app *lit = to_app(cube.get(j));
|
||||
|
||||
sem_matcher match(m);
|
||||
bool pos;
|
||||
substitution v_sub(m);
|
||||
v_sub.reserve(2, subs.size()+1);
|
||||
|
||||
if (!match(pattern, lit, v_sub, pos)) {
|
||||
continue;
|
||||
}
|
||||
// expect positive matches only
|
||||
if (!pos) {continue;}
|
||||
|
||||
dirty[j] = true;
|
||||
for (unsigned v = 0; v < subs.size(); v++) {
|
||||
expr_offset idx;
|
||||
if (v_sub.find(subs.get(v), 0, idx)) {
|
||||
TRACE ("spacer_qgen", tout << "INSTANCE IS: " << mk_pp(idx.get_expr(), m) << "\n";);
|
||||
idx_instances[v].push_back(idx.get_expr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lemma_quantifier_generalizer::find_guards(
|
||||
expr_ref_vector const &indices,
|
||||
expr_ref &lower, expr_ref &upper)
|
||||
{
|
||||
if (indices.empty()) return;
|
||||
|
||||
auto minmax = std::minmax_element((app * *) indices.c_ptr(),
|
||||
(app * *) indices.c_ptr() + indices.size(),
|
||||
index_lt_proc(m));
|
||||
lower = *minmax.first;
|
||||
upper = *minmax.second;
|
||||
}
|
||||
|
||||
void lemma_quantifier_generalizer::add_lower_bounds(
|
||||
var_ref_vector const &subs,
|
||||
app_ref_vector const &zks,
|
||||
vector<expr_ref_vector> const &idx_instances,
|
||||
expr_ref_vector &cube)
|
||||
{
|
||||
for (unsigned v = 0; v < subs.size(); v++) {
|
||||
var *var = subs.get(v);
|
||||
if (idx_instances[v].empty()) continue;
|
||||
TRACE("spacer_qgen",
|
||||
tout << "Finding lower bounds for " << mk_pp(var, m) << "\n";);
|
||||
expr_ref low(m);
|
||||
expr_ref up(m);
|
||||
find_guards(idx_instances[v], low, up);
|
||||
cube.push_back(m_arith.mk_ge(zks.get(var->get_idx()), low));
|
||||
}
|
||||
}
|
||||
|
||||
/// returns true if expression e contains a sub-expression of the form (select A idx) where
|
||||
/// idx contains exactly one skolem from zks. Returns idx and the skolem
|
||||
bool lemma_quantifier_generalizer::match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk) {
|
||||
if (zks.size() != 1) return false;
|
||||
contains_app has_zk(m, zks.get(0));
|
||||
|
||||
if (!contains_selects(e, m)) return false;
|
||||
app_ref_vector indicies(m);
|
||||
get_select_indices(e, indicies, m);
|
||||
if (indicies.size() > 2) return false;
|
||||
|
||||
unsigned i=0;
|
||||
if (indicies.size() == 1) {
|
||||
if (!has_zk(indicies.get(0))) return false;
|
||||
}
|
||||
else {
|
||||
if (has_zk(indicies.get(0)) && !has_zk(indicies.get(1)))
|
||||
i = 0;
|
||||
else if (!has_zk(indicies.get(0)) && has_zk(indicies.get(1)))
|
||||
i = 1;
|
||||
else if (!has_zk(indicies.get(0)) && !has_zk(indicies.get(1)))
|
||||
return false;
|
||||
}
|
||||
|
||||
idx = indicies.get(i);
|
||||
sk = zks.get(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
expr* times_minus_one(expr *e, arith_util &arith) {
|
||||
expr *r;
|
||||
if (arith.is_times_minus_one (e, r)) { return r; }
|
||||
|
||||
return arith.mk_mul(arith.mk_numeral(rational(-1), arith.is_int(get_sort(e))), e);
|
||||
}
|
||||
|
||||
/** Attempts to rewrite a cube so that quantified variable appears as
|
||||
a top level argument of select-term
|
||||
|
||||
Find sub-expression of the form (select A (+ sk!0 t)) and replaces
|
||||
(+ sk!0 t) --> sk!0 and sk!0 --> (+ sk!0 (* (- 1) t))
|
||||
|
||||
Current implementation is an ugly hack for one special case
|
||||
*/
|
||||
void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector const &zks, expr_ref &bind) {
|
||||
if (zks.size() != 1) return;
|
||||
|
||||
arith_util arith(m);
|
||||
expr *idx = nullptr;
|
||||
app *sk = nullptr;
|
||||
expr_ref rep(m);
|
||||
|
||||
for (expr *e : cube) {
|
||||
if (match_sk_idx(e, zks, idx, sk)) {
|
||||
CTRACE("spacer_qgen", idx != sk,
|
||||
tout << "Possible cleanup of " << mk_pp(idx, m) << " in "
|
||||
<< mk_pp(e, m) << " on " << mk_pp(sk, m) << "\n";);
|
||||
|
||||
if (!arith.is_add(idx)) continue;
|
||||
app *a = to_app(idx);
|
||||
bool found = false;
|
||||
expr_ref_vector kids(m);
|
||||
expr_ref_vector kids_bind(m);
|
||||
for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) {
|
||||
if (a->get_arg(i) == sk) {
|
||||
found = true;
|
||||
kids.push_back(a->get_arg(i));
|
||||
kids_bind.push_back(bind);
|
||||
}
|
||||
else {
|
||||
kids.push_back (times_minus_one(a->get_arg(i), arith));
|
||||
kids_bind.push_back (times_minus_one(a->get_arg(i), arith));
|
||||
}
|
||||
}
|
||||
if (!found) continue;
|
||||
|
||||
rep = arith.mk_add(kids.size(), kids.c_ptr());
|
||||
bind = arith.mk_add(kids_bind.size(), kids_bind.c_ptr());
|
||||
TRACE("spacer_qgen",
|
||||
tout << "replace " << mk_pp(idx, m) << " with " << mk_pp(rep, m) << "\n";);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rep) {
|
||||
expr_safe_replace rw(m);
|
||||
rw.insert(sk, rep);
|
||||
rw.insert(idx, sk);
|
||||
rw(cube);
|
||||
TRACE("spacer_qgen",
|
||||
tout << "Cleaned cube to: " << mk_and(cube) << "\n";);
|
||||
}
|
||||
}
|
||||
|
||||
void lemma_quantifier_generalizer::generalize_pattern_lits(expr_ref_vector &pats) {
|
||||
// generalize pattern literals using 'x=t' --> 'x>=t'
|
||||
for (unsigned i = 0, sz = pats.size(); i < sz; ++i) {
|
||||
expr *e1, *e2;
|
||||
expr *p = pats.get(i);
|
||||
if (m.is_eq(p, e1, e2) && (is_var(e1) || is_var(e2))) {
|
||||
if (m_arith.is_numeral(e1)) {
|
||||
p = m_arith.mk_ge(e2, e1);
|
||||
}
|
||||
else if (m_arith.is_numeral(e2)) {
|
||||
p = m_arith.mk_ge(e1, e2);
|
||||
}
|
||||
}
|
||||
if (p != pats.get(i)) {
|
||||
pats.set(i, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) {
|
||||
if (lemma->get_cube().empty()) return;
|
||||
if (!lemma->has_pob()) return;
|
||||
|
||||
m_st.count++;
|
||||
scoped_watch _w_(m_st.watch);
|
||||
|
||||
expr_ref_vector cube(m);
|
||||
cube.append(lemma->get_cube());
|
||||
|
||||
TRACE("spacer_qgen",
|
||||
tout << "initial cube: " << mk_and(lemma->get_cube()) << "\n";);
|
||||
if (false) {
|
||||
// -- re-normalize the cube
|
||||
expr_ref c(m);
|
||||
c = mk_and(cube);
|
||||
normalize(c, c, true, true);
|
||||
cube.reset();
|
||||
flatten_and(c, cube);
|
||||
}
|
||||
|
||||
app_ref_vector skolems(m);
|
||||
lemma->get_pob()->get_skolems(skolems);
|
||||
int offset = skolems.size();
|
||||
|
||||
// for every literal
|
||||
for (unsigned i=0; i < cube.size(); i++) {
|
||||
expr *r = cube.get(i);
|
||||
|
||||
// generate candidates
|
||||
app_ref_vector candidates(m);
|
||||
find_candidates(r, candidates);
|
||||
|
||||
// XXX Can candidates be empty and arguments not empty?
|
||||
// XXX Why separate candidates and arguments?
|
||||
// XXX Why are arguments processed before candidates?
|
||||
if (candidates.empty()) continue;
|
||||
|
||||
|
||||
// for every candidate
|
||||
for (unsigned arg=0, sz = candidates.size(); arg < sz; arg++) {
|
||||
app_ref_vector cand(m);
|
||||
cand.push_back(to_app(candidates.get(arg)));
|
||||
var_ref_vector subs(m);
|
||||
expr_ref_vector patterns(m);
|
||||
// generate patterns for a candidate
|
||||
generate_patterns(cube, cand, subs, patterns, offset);
|
||||
|
||||
// Currently, only support one variable per pattern
|
||||
SASSERT(subs.size() == cand.size());
|
||||
SASSERT(cand.size() == 1);
|
||||
|
||||
// Find matching expressions
|
||||
vector<bool> dirty;
|
||||
dirty.resize(cube.size(), false);
|
||||
vector<expr_ref_vector> idx_instances;
|
||||
find_matching_expressions(cube, subs, patterns, idx_instances, dirty);
|
||||
|
||||
expr_ref_vector new_cube(m);
|
||||
|
||||
// move all unmatched expressions to the new cube
|
||||
for (unsigned c=0; c < cube.size(); c++) {
|
||||
if (dirty[c] == false) {
|
||||
new_cube.push_back(cube.get(c));
|
||||
}
|
||||
}
|
||||
|
||||
// everything moved, nothing left
|
||||
if (new_cube.size() == cube.size()) continue;
|
||||
|
||||
// -- generalize equality in patterns
|
||||
generalize_pattern_lits(patterns);
|
||||
|
||||
// ground quantified patterns in skolems
|
||||
expr_ref gnd(m);
|
||||
app_ref_vector zks(m);
|
||||
ground_expr(mk_and(patterns), gnd, zks);
|
||||
flatten_and(gnd, new_cube);
|
||||
|
||||
// compute lower bounds for quantified variables
|
||||
add_lower_bounds(subs, zks, idx_instances, new_cube);
|
||||
|
||||
TRACE("spacer_qgen",
|
||||
tout << "New CUBE is: " << mk_pp(mk_and(new_cube),m) << "\n";);
|
||||
|
||||
// check if the result is a lemma
|
||||
unsigned uses_level = 0;
|
||||
unsigned weakness = lemma->weakness();
|
||||
pred_transformer &pt = lemma->get_pob()->pt();
|
||||
if (pt.check_inductive(lemma->level(), new_cube, uses_level, weakness)) {
|
||||
TRACE("spacer",
|
||||
tout << "Quantifier Generalization Succeeded!\n"
|
||||
<< "New CUBE is: " << mk_pp(mk_and(new_cube),m) << "\n";);
|
||||
SASSERT(zks.size() >= offset);
|
||||
SASSERT(cand.size() == 1);
|
||||
|
||||
// lift quantified variables to top of select
|
||||
expr_ref bind(m);
|
||||
bind = cand.get(0);
|
||||
cleanup(new_cube, zks, bind);
|
||||
|
||||
// XXX better do that check before changing bind in cleanup()
|
||||
// XXX Or not because substitution might introduce _n variable into bind
|
||||
if (m_ctx.get_manager().is_n_formula(bind))
|
||||
// XXX this creates an instance, but not necessarily the needed one
|
||||
|
||||
// XXX This is sound because any instance of
|
||||
// XXX universal quantifier is sound
|
||||
|
||||
// XXX needs better long term solution. leave
|
||||
// comment here for the future
|
||||
m_ctx.get_manager().formula_n2o(bind, bind, 0);
|
||||
|
||||
lemma->update_cube(lemma->get_pob(), new_cube);
|
||||
lemma->set_level(uses_level);
|
||||
|
||||
// XXX Assumes that offset + 1 == zks.size();
|
||||
for (unsigned sk=offset; sk < zks.size(); sk++)
|
||||
lemma->add_skolem(zks.get(sk), to_app(bind));
|
||||
return;
|
||||
}
|
||||
else {
|
||||
++m_st.num_failures;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1317,9 +1317,9 @@ bool contains_selects(expr* fml, ast_manager& m)
|
|||
return false;
|
||||
}
|
||||
|
||||
void get_select_indices(expr* fml, app_ref_vector& indices, ast_manager& m)
|
||||
void get_select_indices(expr* fml, app_ref_vector &indices, ast_manager& m)
|
||||
{
|
||||
array_util a_util(m);
|
||||
array_util a_util(m);
|
||||
if (!is_app(fml)) { return; }
|
||||
ast_mark done;
|
||||
ptr_vector<app> todo;
|
||||
|
|
|
@ -144,7 +144,10 @@ void compute_implicant_literals (model_evaluator_util &mev,
|
|||
void simplify_bounds (expr_ref_vector &lemmas);
|
||||
void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false);
|
||||
|
||||
/** ground expression by replacing all free variables by skolem constants */
|
||||
/** Ground expression by replacing all free variables by skolem
|
||||
** constants. On return, out is the resulting expression, and vars is
|
||||
** a map from variable ids to corresponding skolem constants.
|
||||
*/
|
||||
void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars);
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue