z3-z3-4.13.0.src.muz.spacer.spacer_context.h Maven / Gradle / Ivy
The newest version!
/**++
Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel
Module Name:
spacer_context.h
Abstract:
SPACER predicate transformers and search context.
Author:
Arie Gurfinkel
Anvesh Komuravelli
Based on muz/pdr/pdr_context.h by Nikolaj Bjorner (nbjorner)
Notes:
--*/
#pragma once
// clang-format off
#include
#include
#include
#include "muz/spacer/spacer_cluster.h"
#include "muz/spacer/spacer_manager.h"
#include "muz/spacer/spacer_prop_solver.h"
#include "muz/spacer/spacer_sem_matcher.h"
#include "util/scoped_ptr_vector.h"
#include "muz/base/fp_params.hpp"
namespace datalog {
class rule_set;
class context;
}; // namespace datalog
namespace spacer {
class model_search;
class pred_transformer;
class derivation;
class pob_queue;
class context;
class lemma_cluster;
class lemma_cluster_finder;
class lemma_global_generalizer;
typedef obj_map rule2inst;
typedef obj_map decl2rel;
class pob;
typedef ref pob_ref;
typedef sref_vector pob_ref_vector;
typedef sref_buffer pob_ref_buffer;
class reach_fact;
typedef ref reach_fact_ref;
typedef sref_vector reach_fact_ref_vector;
class lemma;
using lemma_ref = ref;
using lemma_ref_vector = sref_vector;
class reach_fact {
unsigned m_ref_count;
expr_ref m_fact;
ptr_vector m_aux_vars;
const datalog::rule &m_rule;
reach_fact_ref_vector m_justification;
// variable used to tag this reach fact in an incremental disjunction
app_ref m_tag;
bool m_init;
public:
reach_fact(ast_manager &m, const datalog::rule &rule, expr *fact,
const ptr_vector &aux_vars, bool init = false)
: m_ref_count(0), m_fact(fact, m), m_aux_vars(aux_vars), m_rule(rule),
m_tag(m), m_init(init) {}
reach_fact(ast_manager &m, const datalog::rule &rule, expr *fact,
bool init = false)
: m_ref_count(0), m_fact(fact, m), m_rule(rule), m_tag(m),
m_init(init) {}
bool is_init() { return m_init; }
const datalog::rule &get_rule() { return m_rule; }
void add_justification(reach_fact *f) { m_justification.push_back(f); }
const reach_fact_ref_vector &get_justifications() {
return m_justification;
}
expr *get() { return m_fact.get(); }
const ptr_vector &aux_vars() { return m_aux_vars; }
app *tag() const {
SASSERT(m_tag);
return m_tag;
}
void set_tag(app *tag) { m_tag = tag; }
void inc_ref() { ++m_ref_count; }
void dec_ref() {
SASSERT(m_ref_count > 0);
--m_ref_count;
if (m_ref_count == 0) { dealloc(this); }
}
};
// a lemma
class lemma {
// clang-format off
unsigned m_ref_count;
ast_manager &m;
expr_ref m_body;
expr_ref_vector m_cube;
app_ref_vector m_zks;
app_ref_vector m_bindings;
pob_ref m_pob;
model_ref m_ctp; // counter-example to pushing
unsigned m_lvl; // current level of the lemma
unsigned m_init_lvl; // level at which lemma was created
unsigned m_bumped:16;
unsigned m_weakness:16;
unsigned m_external:1; // external lemma from another solver
unsigned m_blocked:1; // blocked by CTP
unsigned m_background:1; // background assumed fact
// clang-format on
// clang-format off
void mk_expr_core();
void mk_cube_core();
public:
lemma(ast_manager &manager, expr *fml, unsigned lvl);
lemma(pob_ref const &p);
lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl);
// lemma(const lemma &other) = delete;
ast_manager &get_ast_manager() { return m; }
model_ref &get_ctp() { return m_ctp; }
bool has_ctp() { return !is_inductive() && m_ctp; }
void set_ctp(model_ref &v) { m_ctp = v; }
void reset_ctp() { m_ctp.reset(); }
void bump() { m_bumped++; }
unsigned get_bumped() { return m_bumped; }
expr *get_expr();
bool is_false();
expr_ref_vector const &get_cube();
void update_cube(pob_ref const &p, expr_ref_vector &cube);
bool has_pob() { return !!m_pob; }
pob_ref &get_pob() { return m_pob; }
unsigned weakness() { return m_weakness; }
void add_skolem(app *zk, app *b);
void set_external(bool ext) { m_external = ext; }
bool external() { return m_external; }
void set_background(bool v) { m_background = v; }
bool is_background() { return m_background; }
bool is_blocked() { return m_blocked; }
void set_blocked(bool v) { m_blocked = v; }
bool is_inductive() const { return is_infty_level(m_lvl); }
unsigned level() const { return m_lvl; }
unsigned init_level() const { return m_init_lvl; }
void set_level(unsigned lvl);
app_ref_vector &get_bindings() { return m_bindings; }
bool has_binding(app_ref_vector const &binding);
void add_binding(app_ref_vector const &binding);
void instantiate(expr *const *exprs, expr_ref &result, expr *e = nullptr);
void mk_insts(expr_ref_vector &inst, expr *e = nullptr);
bool is_ground() { return !is_quantifier(get_expr()); }
void inc_ref() { ++m_ref_count; }
void dec_ref() {
SASSERT(m_ref_count > 0);
--m_ref_count;
if (m_ref_count == 0) { dealloc(this); }
}
};
struct lemma_lt_proc {
bool operator()(lemma *a, lemma *b) {
return (a->level() < b->level()) ||
(a->level() == b->level() &&
ast_lt_proc()(a->get_expr(), b->get_expr()));
}
};
//
// Predicate transformer state.
// A predicate transformer corresponds to the
// set of rules that have the same head predicates.
//
class pred_transformer {
struct stats {
// clang-format off
unsigned m_num_propagations; // num of times lemma is pushed higher
unsigned m_num_invariants; // num of infty lemmas found
unsigned m_num_ctp_blocked; // num of time ctp blocked lemma pushing
unsigned m_num_is_invariant; // num of times lemmas are pushed
unsigned m_num_lemma_level_jump; // lemma learned at higher level than
// expected
unsigned m_num_reach_queries;
// clang-format on
// clang-format off
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
/// manager of the lemmas in all the frames
#include "muz/spacer/spacer_legacy_frames.h"
class frames {
private:
// clang-format off
pred_transformer &m_pt; // parent pred_transformer
lemma_ref_vector m_pinned_lemmas; // all created lemmas
lemma_ref_vector m_lemmas; // active lemmas
lemma_ref_vector m_bg_invs; // background (assumed) invariants
unsigned m_size; // num of frames
bool m_sorted; // true if m_lemmas is sorted by m_lt
lemma_lt_proc m_lt; // sort order for m_lemmas
// clang-format on
// clang-format off
void sort();
public:
frames(pred_transformer &pt) : m_pt(pt), m_size(0), m_sorted(true) {}
void simplify_formulas();
pred_transformer &pt() const { return m_pt; }
const lemma_ref_vector &lemmas() const { return m_lemmas; }
void get_frame_lemmas(unsigned level, expr_ref_vector &out) const {
for (auto &lemma : m_lemmas) {
if (lemma->level() == level) {
out.push_back(lemma->get_expr());
}
}
}
void get_frame_geq_lemmas(unsigned level, expr_ref_vector &out,
bool with_bg = false) const {
for (auto &lemma : m_lemmas) {
if (lemma->level() >= level) {
out.push_back(lemma->get_expr());
}
}
if (with_bg) {
for (auto &lemma : m_bg_invs) out.push_back(lemma->get_expr());
}
}
void get_frame_all_lemmas(lemma_ref_vector &out,
bool with_bg = false) const {
for (auto &lemma : m_lemmas) { out.push_back(lemma); }
if (with_bg) {
for (auto &lemma : m_bg_invs) out.push_back(lemma);
}
}
const lemma_ref_vector &get_bg_invs() const { return m_bg_invs; }
unsigned size() const { return m_size; }
unsigned lemma_size() const { return m_lemmas.size(); }
unsigned bg_invs_size() const { return m_bg_invs.size(); }
void add_frame() { m_size++; }
void inherit_frames(frames &other) {
for (auto &other_lemma : other.m_lemmas) {
lemma_ref new_lemma =
alloc(lemma, m_pt.get_ast_manager(),
other_lemma->get_expr(), other_lemma->level());
new_lemma->add_binding(other_lemma->get_bindings());
add_lemma(new_lemma.get());
}
m_sorted = false;
m_bg_invs.append(other.m_bg_invs);
}
bool add_lemma(lemma *new_lemma);
void propagate_to_infinity(unsigned level);
bool propagate_to_next_level(unsigned level);
};
/**
manager of proof-obligations (pob_manager)
Pobs are determined uniquely by their post-condition and a parent pob.
They are managed by pob_manager and remain live through the
life of the manager
*/
class pob_manager {
// a buffer that contains space for one pob and allocates more
// space if needed
typedef ptr_buffer pob_buffer;
// Type for the map from post-conditions to pobs. The common
// case is that each post-condition corresponds to a single
// pob. Other cases are handled by expanding the buffer
typedef obj_map expr2pob_buffer;
// parent predicate transformer
pred_transformer &m_pt;
// map from post-conditions to pobs
expr2pob_buffer m_pobs;
// a store
pob_ref_vector m_pinned;
public:
pob_manager(pred_transformer &pt) : m_pt(pt) {}
pob *mk_pob(pob *parent, unsigned level, unsigned depth, expr *post,
app_ref_vector const &b);
pob *mk_pob(pob *parent, unsigned level, unsigned depth, expr *post) {
app_ref_vector b(m_pt.get_ast_manager());
return mk_pob(parent, level, depth, post, b);
}
unsigned size() const { return m_pinned.size(); }
pob *find_pob(pob *parent, expr *post);
};
class pt_rule {
// clang-format off
const datalog::rule &m_rule;
expr_ref m_trans; // ground version of m_rule
ptr_vector m_auxs; // auxiliary variables in m_trans
app_ref_vector m_reps; // map from fv in m_rule to ground constants
app_ref m_tag; // a unique tag for the rule
// clang-format on
// clang-format off
public:
pt_rule(ast_manager &m, const datalog::rule &r)
: m_rule(r), m_trans(m), m_reps(m), m_tag(m) {}
const datalog::rule &rule() const { return m_rule; }
void set_tag(expr *tag) {
SASSERT(is_app(tag));
set_tag(to_app(tag));
}
void set_tag(app *tag) { m_tag = tag; }
app *tag() const { return m_tag; }
ptr_vector &auxs() { return m_auxs; }
void set_auxs(ptr_vector &v) {
m_auxs.reset();
m_auxs.append(v);
}
void set_reps(app_ref_vector &v) {
m_reps.reset();
m_reps.append(v);
}
void set_trans(expr_ref &v) { m_trans = v; }
expr *trans() const { return m_trans; }
bool is_init() const {
return m_rule.get_uninterpreted_tail_size() == 0;
}
};
class pt_rules {
typedef obj_map rule2ptrule;
typedef obj_map tag2ptrule;
typedef rule2ptrule::iterator iterator;
rule2ptrule m_rules;
tag2ptrule m_tags;
public:
~pt_rules() {
for (auto &kv : m_rules) { dealloc(kv.m_value); }
}
bool find_by_rule(const datalog::rule &r, pt_rule *&ptr) {
return m_rules.find(&r, ptr);
}
bool find_by_tag(const expr *tag, pt_rule *&ptr) {
return m_tags.find(tag, ptr);
}
pt_rule &mk_rule(ast_manager &m, const datalog::rule &r) {
return mk_rule(pt_rule(m, r));
}
pt_rule &mk_rule(const pt_rule &v);
void set_tag(expr *tag, pt_rule &v) {
pt_rule *p;
VERIFY(find_by_rule(v.rule(), p));
p->set_tag(tag);
m_tags.insert(tag, p);
}
bool empty() { return m_rules.empty(); }
iterator begin() { return m_rules.begin(); }
iterator end() { return m_rules.end(); }
};
/// Clusters of lemmas
class cluster_db {
sref_vector m_clusters;
unsigned m_max_cluster_size;
public:
cluster_db() : m_max_cluster_size(0) {}
unsigned get_max_cluster_size() const { return m_max_cluster_size; }
/// Return the smallest cluster than can contain \p lemma
lemma_cluster *can_contain(const lemma_ref &lemma) {
unsigned sz = UINT_MAX;
lemma_cluster *res = nullptr;
for (auto *c : m_clusters) {
if (c->get_gas() > 0 && c->get_size() < sz &&
c->can_contain(lemma)) {
res = c;
sz = res->get_size();
}
}
return res;
}
bool contains(const lemma_ref &lemma) {
for (auto *c : m_clusters) {
if (c->contains(lemma)) { return true; }
}
return false;
}
/// The number of clusters with pattern \p pattern
unsigned clstr_count(const expr_ref &pattern) {
unsigned count = 0;
for (auto c : m_clusters) {
if (c->get_pattern() == pattern) count++;
}
return count;
}
lemma_cluster *mk_cluster(const expr_ref &pattern) {
m_clusters.push_back(alloc(lemma_cluster, pattern));
return m_clusters.back();
}
/// Return the smallest cluster containing \p lemma
lemma_cluster *get_cluster(const lemma_ref &lemma) {
unsigned sz = UINT_MAX;
lemma_cluster *res = nullptr;
for (auto *c : m_clusters) {
if (c->get_size() < sz && c->contains(lemma)) {
res = c;
sz = res->get_size();
}
}
return res;
}
lemma_cluster *get_cluster(const expr *pattern) {
for (lemma_cluster *lc : m_clusters) {
if (lc->get_pattern().get() == pattern) return lc;
}
return nullptr;
}
};
// clang-format off
manager& pm; // spacer::manager
ast_manager& m; // ast_manager
context& ctx; // spacer::context
func_decl_ref m_head; // predicate
func_decl_ref_vector m_sig; // signature
ptr_vector m_use; // places where 'this' is referenced.
pt_rules m_pt_rules; // pt rules used to derive transformer
ptr_vector m_rules; // rules used to derive transformer
scoped_ptr m_solver; // solver context
ref m_reach_solver; // context for reachability facts
pob_manager m_pobs; // proof obligations created so far
frames m_frames; // frames with lemmas
reach_fact_ref_vector m_reach_facts; // reach facts
unsigned m_rf_init_sz; // number of reach fact from INIT
expr_ref_vector m_transition_clause; // extra clause for trans
expr_ref m_transition; // transition relation
expr_ref m_init; // initial condition
app_ref m_extend_lit0; // first literal used to extend initial state
app_ref m_extend_lit; // current literal to extend initial state
bool m_all_init; // true if the pt has no uninterpreted body in any rule
ptr_vector m_predicates; // temp vector used with find_predecessors()
stats m_stats;
stopwatch m_initialize_watch;
stopwatch m_must_reachable_watch;
stopwatch m_ctp_watch;
stopwatch m_mbp_watch;
bool m_has_quantified_frame; // True when a quantified lemma is in the frame
cluster_db m_cluster_db;
// clang-format on
// clang-format off
void init_sig();
app_ref mk_extend_lit();
void ensure_level(unsigned level);
void add_lemma_core(lemma *lemma, bool ground_only = false);
void add_lemma_from_child(pred_transformer &child, lemma *lemma,
unsigned lvl, bool ground_only = false);
void mk_assumptions(func_decl *head, expr *fml, expr_ref_vector &result);
// Initialization
void init_rules(decl2rel const &pts);
void init_rule(decl2rel const &pts, datalog::rule const &rule);
void init_atom(decl2rel const &pts, app *atom, app_ref_vector &var_reprs,
expr_ref_vector &side, unsigned tail_idx);
void simplify_formulas(tactic &tac, expr_ref_vector &fmls);
void add_premises(decl2rel const &pts, unsigned lvl, datalog::rule &rule,
expr_ref_vector &r);
app_ref mk_fresh_rf_tag();
// get tagged formulae of all of the background invariants for all of the
// predecessors of the current transformer
void get_pred_bg_invs(expr_ref_vector &out);
const lemma_ref_vector &get_bg_invs() const {
return m_frames.get_bg_invs();
}
public:
pred_transformer(context &ctx, manager &pm, func_decl *head);
inline bool use_native_mbp();
bool mk_mdl_rf_consistent(const datalog::rule *r, model &mdl);
reach_fact *get_rf(expr *v) {
for (auto *rf : m_reach_facts) {
if (v == rf->get()) { return rf; }
}
return nullptr;
}
void find_predecessors(datalog::rule const &r,
ptr_vector &predicates) const;
void add_rule(datalog::rule *r) { m_rules.push_back(r); }
void add_use(pred_transformer *pt) {
if (!m_use.contains(pt)) { m_use.insert(pt); }
}
void initialize(decl2rel const &pts);
func_decl *head() const { return m_head; }
ptr_vector const &rules() const { return m_rules; }
func_decl *sig(unsigned i) const { return m_sig[i]; } // signature
func_decl *const *sig() { return m_sig.data(); }
unsigned sig_size() const { return m_sig.size(); }
expr *transition() const { return m_transition; }
expr *init() const { return m_init; }
expr *rule2tag(datalog::rule const *r) {
pt_rule *p;
return m_pt_rules.find_by_rule(*r, p) ? p->tag() : nullptr;
}
unsigned get_num_levels() const { return m_frames.size(); }
expr_ref get_cover_delta(func_decl *p_orig, int level);
void add_cover(unsigned level, expr *property, bool bg = false);
expr_ref get_reachable();
std::ostream &display(std::ostream &strm) const;
void collect_statistics(statistics &st) const;
void reset_statistics();
bool is_must_reachable(expr *state, model_ref *model = nullptr);
/// \brief Returns reachability fact active in the given model
/// all determines whether initial reachability facts are included as well
reach_fact *get_used_rf(model &mdl, bool all = true);
/// \brief Returns reachability fact active in the origin of the given model
reach_fact *get_used_origin_rf(model &mdl, unsigned oidx);
/// \brief Collects all the reachable facts used in mdl
void get_all_used_rf(model &mdl, unsigned oidx, reach_fact_ref_vector &res);
void get_all_used_rf(model &mdl, reach_fact_ref_vector &res);
expr_ref get_origin_summary(model &mdl, unsigned level, unsigned oidx,
bool must, const ptr_vector **aux);
bool is_ctp_blocked(lemma *lem);
const datalog::rule *find_rule(model &mdl);
const datalog::rule *find_rule(model &mev, bool &is_concrete,
bool_vector &reach_pred_used,
unsigned &num_reuse_reach);
expr *get_transition(datalog::rule const &r) {
pt_rule *p;
return m_pt_rules.find_by_rule(r, p) ? p->trans() : nullptr;
}
ptr_vector &get_aux_vars(datalog::rule const &r) {
pt_rule *p = nullptr;
VERIFY(m_pt_rules.find_by_rule(r, p));
return p->auxs();
}
bool propagate_to_next_level(unsigned level);
void propagate_to_infinity(unsigned level);
/// \brief Add a lemma to the current context and all users
bool add_lemma(expr *e, unsigned lvl, bool bg);
bool add_lemma(lemma *lem) { return m_frames.add_lemma(lem); }
expr *get_reach_case_var(unsigned idx) const;
bool has_rfs() const { return !m_reach_facts.empty(); }
/// initialize reachability facts using initial rules
void init_rfs();
reach_fact *mk_rf(pob &n, model &mdl, const datalog::rule &r);
void add_rf(reach_fact *fact, bool force = false); // add reachability fact
reach_fact *get_last_rf() const { return m_reach_facts.back(); }
expr *get_last_rf_tag() const;
pob *mk_pob(pob *parent, unsigned level, unsigned depth, expr *post,
app_ref_vector const &b) {
return m_pobs.mk_pob(parent, level, depth, post, b);
}
pob *find_pob(pob *parent, expr *post) {
return m_pobs.find_pob(parent, post);
}
pob *mk_pob(pob *parent, unsigned level, unsigned depth, expr *post) {
return m_pobs.mk_pob(parent, level, depth, post);
}
lbool is_reachable(pob& n, expr_ref_vector* core, model_ref *model,
unsigned& uses_level, bool& is_concrete,
datalog::rule const *&r, bool_vector &reach_pred_used,
unsigned& num_reuse_reach, bool use_iuc = true);
bool is_invariant(unsigned level, lemma *lem, unsigned &solver_level,
expr_ref_vector* core = nullptr);
bool is_invariant(unsigned level, expr *lem, unsigned &solver_level,
expr_ref_vector *core = nullptr) {
// XXX only needed for legacy_frames to compile
UNREACHABLE();
return false;
}
bool check_inductive(unsigned level, expr_ref_vector &state,
unsigned &assumes_level, unsigned weakness = UINT_MAX);
expr_ref get_formulas(unsigned level, bool bg = false) const;
void simplify_formulas();
context &get_context() const { return ctx; }
manager &get_manager() const { return pm; }
ast_manager &get_ast_manager() const { return m; }
void add_premises(decl2rel const &pts, unsigned lvl, expr_ref_vector &r);
void inherit_lemmas(pred_transformer &other);
void ground_free_vars(expr *e, app_ref_vector &vars,
ptr_vector &aux_vars, bool is_init);
/// \brief Adds a given expression to the set of initial rules
app *extend_initial(expr *e);
/// \brief Returns true if the obligation is already blocked by current
/// lemmas
bool is_blocked(pob &n, unsigned &uses_level, model_ref *model = nullptr);
/// \brief Returns true if the obligation is already blocked by current
/// quantified lemmas
bool is_qblocked(pob &n);
/// \brief interface to Model Based Projection
void mbp(app_ref_vector &vars, expr_ref &fml, model &mdl,
bool reduce_all_selects, bool force = false);
void updt_solver(prop_solver *solver);
void updt_solver_with_lemmas(prop_solver *solver,
const pred_transformer &pt, app *rule_tag,
unsigned pos);
// exposing ACTIVE lemmas (alternatively, one can expose `m_pinned_lemmas`
// for ALL lemmas)
void get_all_lemmas(lemma_ref_vector &out, bool with_bg = false) const {
m_frames.get_frame_all_lemmas(out, with_bg);
}
void update_solver_with_rfs(prop_solver *solver, const pred_transformer &pt,
app *rule_tag, unsigned pos);
lemma_cluster *mk_cluster(const expr_ref &pattern) {
return m_cluster_db.mk_cluster(pattern);
}
// Checks whether \p lemma is in any existing cluster
bool clstr_contains(const lemma_ref &lemma) {
return m_cluster_db.contains(lemma);
}
/// The number of clusters with pattern \p pattern
unsigned clstr_count(const expr_ref &pattern) {
return m_cluster_db.clstr_count(pattern);
}
/// Checks whether \p lemma matches any cluster
lemma_cluster *clstr_match(const lemma_ref &lemma) {
lemma_cluster *res = m_cluster_db.get_cluster(lemma);
if (!res) res = m_cluster_db.can_contain(lemma);
return res;
}
/// Returns a cluster with pattern \p pattern
lemma_cluster *get_cluster(const expr *pattern) {
return m_cluster_db.get_cluster(pattern);
}
};
/**
* A proof obligation.
*/
class pob {
// clang-format off
// TBD: remove this
friend class context;
unsigned m_ref_count;
/// parent node
pob_ref m_parent;
/// predicate transformer
pred_transformer& m_pt;
/// post-condition decided by this node
expr_ref m_post;
// if m_post is not ground, then m_binding is an instantiation for
// all quantified variables
app_ref_vector m_binding;
/// new post to be swapped in for m_post
expr_ref m_new_post;
/// level at which to decide the post
unsigned m_level:16;
unsigned m_depth:16;
unsigned m_desired_level:16;
/// whether a concrete answer to the post is found
unsigned m_open:1;
/// whether to use farkas generalizer to construct a lemma blocking this
/// node
unsigned m_use_farkas:1;
/// true if this pob is in pob_queue
unsigned m_in_queue:1;
// true if this pob is a conjecture
unsigned m_is_conjecture:1;
// should do local generalizations on pob
unsigned m_enable_local_gen:1;
// should concretize cube
unsigned m_enable_concretize:1;
// is a subsume pob
unsigned m_is_subsume:1;
// should apply expand bnd generalization on pob
unsigned m_enable_expand_bnd_gen:1;
unsigned m_weakness;
/// derivation representing the position of this node in the parent's rule
scoped_ptr m_derivation;
/// pobs created as children of this pob (at any time, not
/// necessarily currently active)
ptr_vector m_kids;
// lemmas created to block this pob (at any time, not necessarily active)
ptr_vector m_lemmas;
unsigned m_blocked_lvl;
// clang-format on
// clang-format off
// pattern identified for one of its lemmas
expr_ref m_concretize_pat;
// gas decides how much time is spent in blocking this (may) pob
unsigned m_gas;
// additional data used by global (and other) generalizations
scoped_ptr m_data;
public:
pob(pob *parent, pred_transformer &pt, unsigned level, unsigned depth = 0,
bool add_to_parent = true);
// no copy constructor
pob(const pob&) = delete;
// no move constructor
pob(pob &&) = delete;
~pob() {
if (m_parent) { m_parent->erase_child(*this); }
}
void set_data(pob* v) { m_data = v; }
void reset_data() { set_data(nullptr); }
pob* get_data() { return m_data.get(); }
bool has_data() { return m_data; }
// TBD: move into constructor and make private
void set_post(expr *post, app_ref_vector const &binding);
void set_post(expr *post);
unsigned weakness() { return m_weakness; }
void bump_weakness() { m_weakness++; }
void reset_weakness() { m_weakness = 0; }
void inc_level() {
SASSERT(!is_in_queue());
m_level++;
m_depth++;
reset_weakness();
}
void inherit(pob const &p);
void set_derivation(derivation *d) { m_derivation = d; }
bool has_derivation() const { return (bool)m_derivation; }
derivation &get_derivation() const { return *m_derivation.get (); }
void reset_derivation() { set_derivation (nullptr); }
/// detaches derivation from the node without deallocating
derivation* detach_derivation() { return m_derivation.detach (); }
pob* parent() const { return m_parent.get (); }
bool is_conjecture() const { return m_is_conjecture; }
void set_conjecture(bool v = true) { m_is_conjecture = v; }
void disable_expand_bnd_gen() { m_enable_expand_bnd_gen = false; }
bool is_expand_bnd_enabled() { return m_enable_expand_bnd_gen; }
void set_expand_bnd(bool v = true) { m_enable_expand_bnd_gen = v; }
void set_concretize_pattern(const expr_ref &pattern) { m_concretize_pat = pattern; }
const expr_ref &get_concretize_pattern() const { return m_concretize_pat; }
bool is_subsume() const { return m_is_subsume; }
void set_subsume(bool v = true) { m_is_subsume = v; }
bool is_may_pob() const { return is_subsume() || is_conjecture(); }
unsigned get_gas() const { return m_gas; }
void set_gas(unsigned n) { m_gas = n; }
bool is_local_gen_enabled() const { return m_enable_local_gen; }
void disable_local_gen() { m_enable_local_gen = false; }
void get_post_simplified(expr_ref_vector &res);
bool is_concretize_enabled() const { return m_enable_concretize && m_gas > 0; }
void set_concretize(bool v = true) { m_enable_concretize = v; }
pred_transformer& pt() const { return m_pt; }
ast_manager& get_ast_manager() const { return m_pt.get_ast_manager(); }
manager& get_manager() const { return m_pt.get_manager(); }
context& get_context() const { return m_pt.get_context(); }
unsigned level() const { return m_level; }
unsigned depth() const { return m_depth; }
unsigned desired_level() const { return m_desired_level; }
void set_desired_level(unsigned v) { m_desired_level = v; }
unsigned width() const { return m_kids.size(); }
unsigned blocked_at(unsigned lvl = 0) {
return (m_blocked_lvl = std::max(lvl, m_blocked_lvl));
}
bool is_in_queue() const { return m_in_queue; }
void set_in_queue(bool v) { m_in_queue = v; }
bool use_farkas_generalizer() const { return m_use_farkas; }
void set_farkas_generalizer(bool v) { m_use_farkas = v; }
expr *post() const { return m_post.get(); }
bool is_closed() const { return !m_open; }
void close();
const ptr_vector &children() const { return m_kids; }
void add_child(pob &v) { m_kids.push_back(&v); }
void erase_child(pob &v) { m_kids.erase(&v); }
const ptr_vector &lemmas() const { return m_lemmas; }
void add_lemma(lemma *new_lemma) { m_lemmas.push_back(new_lemma); }
bool is_ground() const { return m_binding.empty(); }
unsigned get_free_vars_size() const { return m_binding.size(); }
app_ref_vector const &get_binding() const { return m_binding; }
/*
* 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);
void on_expand() {
if (m_parent.get()) { m_parent.get()->on_expand(); }
}
void off_expand() {
if (m_parent.get()) { m_parent.get()->off_expand(); }
}
void inc_ref() { ++m_ref_count; }
void dec_ref() {
--m_ref_count;
if (m_ref_count == 0) { dealloc(this); }
}
std::ostream &display(std::ostream &out, bool full = false) const;
class on_expand_event {
pob &m_p;
public:
on_expand_event(pob &p) : m_p(p) { m_p.on_expand(); }
~on_expand_event() { m_p.off_expand(); }
};
};
inline std::ostream &operator<<(std::ostream &out, pob const &p) {
return p.display(out);
}
struct pob_lt_proc {
bool operator()(const pob *pn1, const pob *pn2) const;
};
struct pob_gt_proc {
bool operator()(const pob *n1, const pob *n2) const {
return pob_lt_proc()(n2, n1);
}
};
/**
*/
class derivation {
/// a single premise of a derivation
class premise {
pred_transformer &m_pt;
/// origin order in the rule
unsigned m_oidx;
/// summary fact corresponding to the premise
expr_ref m_summary;
/// whether this is a must or may premise
bool m_must;
app_ref_vector m_ovars;
public:
premise(pred_transformer &pt, unsigned oidx, expr *summary, bool must,
const ptr_vector *aux_vars = nullptr);
bool is_must() { return m_must; }
expr *get_summary() { return m_summary.get(); }
app_ref_vector &get_ovars() { return m_ovars; }
unsigned get_oidx() { return m_oidx; }
pred_transformer &pt() { return m_pt; }
/// \brief Updated the summary.
/// The new summary is over n-variables.
void set_summary(expr *summary, bool must,
const ptr_vector *aux_vars = nullptr);
};
// clang-format off
/// parent model node
pob& m_parent;
/// the rule corresponding to this derivation
const datalog::rule &m_rule;
/// the premises
vector m_premises;
/// pointer to the active premise
unsigned m_active;
// transition relation over origin variables
expr_ref m_trans;
// implicitly existentially quantified variables in m_trans
app_ref_vector m_evars;
// clang-format on
// clang-format off
/// -- create next child using given model as the guide
/// -- returns NULL if there is no next child
pob *create_next_child(model &mdl);
/// existentially quantify vars and skolemize the result
void exist_skolemize(expr *fml, app_ref_vector &vars, expr_ref &res);
public:
derivation(pob &parent, datalog::rule const &rule, expr *trans,
app_ref_vector const &evars);
void add_premise(pred_transformer &pt, unsigned oidx, expr *summary,
bool must, const ptr_vector *aux_vars = nullptr);
/// creates the first child. Must be called after all the premises
/// are added. The model must be valid for the premises
/// Returns NULL if no child exits
pob *create_first_child(model &mdl);
/// Create the next child. Must summary of the currently active
/// premise must be consistent with the transition relation
pob *create_next_child();
datalog::rule const &get_rule() const { return m_rule; }
pob &get_parent() const { return m_parent; }
ast_manager &get_ast_manager() const { return m_parent.get_ast_manager(); }
manager &get_manager() const { return m_parent.get_manager(); }
context &get_context() const { return m_parent.get_context(); }
pred_transformer &pt() const { return m_parent.pt(); }
};
class pob_queue {
typedef std::priority_queue, pob_gt_proc>
pob_queue_ty;
pob_ref m_root;
unsigned m_max_level;
unsigned m_min_depth;
pob_queue_ty m_data;
public:
pob_queue() : m_root(nullptr), m_max_level(0), m_min_depth(0) {}
void reset();
pob *top();
void pop();
void push(pob &n);
void inc_level() {
SASSERT(!m_data.empty() || m_root);
m_max_level++;
m_min_depth++;
if (m_root && m_data.empty()) {
SASSERT(!m_root->is_in_queue());
m_root->set_in_queue(true);
m_data.push(m_root.get());
}
}
pob &get_root() const { return *m_root.get(); }
void set_root(pob &n);
bool is_root(pob &n) const { return m_root.get() == &n; }
unsigned max_level() const { return m_max_level; }
unsigned min_depth() const { return m_min_depth; }
size_t size() const { return m_data.size(); }
};
/**
* Generalizers (strengthens) a lemma
*/
class lemma_generalizer {
protected:
context &m_ctx;
public:
lemma_generalizer(context &ctx) : m_ctx(ctx) {}
virtual ~lemma_generalizer() = default;
virtual void operator()(lemma_ref &lemma) = 0;
virtual void collect_statistics(statistics &st) const {}
virtual void reset_statistics() {}
};
class spacer_callback {
protected:
context &m_context;
public:
spacer_callback(context &context) : m_context(context) {}
virtual ~spacer_callback() = default;
context &get_context() { return m_context; }
virtual inline bool new_lemma() { return false; }
virtual void new_lemma_eh(expr *lemma, unsigned level) {}
virtual inline bool predecessor() { return false; }
virtual void predecessor_eh() {}
virtual inline bool unfold() { return false; }
virtual void unfold_eh() {}
virtual inline bool propagate() { return false; }
virtual void propagate_eh() {}
};
// order in which children are processed
enum spacer_children_order {
CO_RULE, // same order as in the rule
CO_REV_RULE, // reverse order of the rule
CO_RANDOM // random shuffle
};
class context {
friend class pred_transformer;
struct stats {
unsigned m_num_queries;
unsigned m_num_reuse_reach;
unsigned m_max_query_lvl;
unsigned m_max_depth;
unsigned m_cex_depth;
unsigned m_expand_pob_undef;
unsigned m_num_lemmas;
unsigned m_num_restarts;
unsigned m_num_lemmas_imported;
unsigned m_num_lemmas_discarded;
unsigned m_num_conj;
unsigned m_num_conj_success;
unsigned m_num_conj_failed;
unsigned m_num_subsume_pobs;
unsigned m_num_subsume_pob_reachable;
unsigned m_num_subsume_pob_blckd;
unsigned m_num_concretize;
unsigned m_num_pob_ofg;
unsigned m_non_local_gen;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
// clang-format off
// stat watches
stopwatch m_solve_watch;
stopwatch m_propagate_watch;
stopwatch m_reach_watch;
stopwatch m_is_reach_watch;
stopwatch m_create_children_watch;
stopwatch m_init_rules_watch;
fp_params const& m_params;
ast_manager& m;
datalog::context* m_context;
manager m_pm;
// three solver pools for different queries
scoped_ptr m_pool0;
scoped_ptr m_pool1;
scoped_ptr m_pool2;
random_gen m_random;
spacer_children_order m_children_order;
decl2rel m_rels; // Map from relation predicate to fp-operator.
func_decl_ref m_query_pred;
pred_transformer* m_query;
mutable pob_queue m_pob_queue;
lbool m_last_result;
unsigned m_inductive_lvl;
unsigned m_expanded_lvl;
ptr_buffer m_lemma_generalizers;
lemma_global_generalizer *m_global_gen;
lemma_generalizer *m_expand_bnd_gen;
lemma_cluster_finder *m_lmma_cluster;
stats m_stats;
model_converter_ref m_mc;
proof_converter_ref m_pc;
bool m_use_native_mbp;
bool m_instantiate;
bool m_use_qlemmas;
bool m_weak_abs;
bool m_use_restarts;
bool m_simplify_pob;
bool m_use_euf_gen;
bool m_use_lim_num_gen;
bool m_use_ctp;
bool m_use_inc_clause;
bool m_use_ind_gen;
bool m_use_array_eq_gen;
bool m_validate_lemmas;
bool m_use_propagate;
bool m_reset_obligation_queue;
bool m_push_pob;
bool m_use_lemma_as_pob;
bool m_elim_aux;
bool m_reach_dnf;
bool m_use_derivations;
bool m_validate_result;
bool m_use_eq_prop;
bool m_ground_pob;
bool m_q3_qgen;
bool m_use_gpdr;
bool m_simplify_formulas_pre;
bool m_simplify_formulas_post;
bool m_pdr_bfs;
bool m_use_bg_invs;
bool m_global;
bool m_expand_bnd;
bool m_gg_conjecture;
bool m_gg_subsume;
bool m_gg_concretize;
bool m_use_iuc;
unsigned m_push_pob_max_depth;
unsigned m_max_level;
unsigned m_restart_initial_threshold;
unsigned m_blast_term_ite_inflation;
scoped_ptr_vector m_callbacks;
std::fstream* m_trace_stream;
// clang-format on
// clang-format off
// Solve using gpdr strategy
lbool gpdr_solve_core();
bool gpdr_check_reachability(unsigned lvl, model_search &ms);
bool gpdr_create_split_children(pob &n, const datalog::rule &r, expr *trans,
model &mdl, pob_ref_buffer &out);
// progress logging
void log_enter_level(unsigned lvl);
void log_propagate();
void log_expand_pob(pob &);
void log_add_lemma(pred_transformer &, lemma &);
// Functions used by search.
lbool solve_core(unsigned from_lvl = 0);
bool is_requeue(pob &n);
bool check_reachability();
bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl,
unsigned full_prop_lvl);
bool is_reachable(pob &n);
lbool expand_pob(pob &n, pob_ref_buffer &out);
bool create_children(pob &n, const datalog::rule &r, model &mdl,
const bool_vector &reach_pred_used,
pob_ref_buffer &out);
/**
\brief Retrieve satisfying assignment with explanation.
*/
expr_ref mk_sat_answer() const {
proof_ref pr = get_ground_refutation();
return expr_ref(pr.get(), pr.get_manager());
}
expr_ref mk_unsat_answer() const;
unsigned get_cex_depth();
// Generate inductive property
void get_level_property(unsigned lvl, expr_ref_vector &res,
vector &rs,
bool with_bg = false) const;
// Initialization
void init_lemma_generalizers();
void reset_lemma_generalizers();
void inherit_lemmas(const decl2rel &rels);
void init_global_smt_params();
void init_rules(datalog::rule_set &rules, decl2rel &transformers);
// (re)initialize context with new relations
void init(const decl2rel &rels);
bool validate();
bool check_invariant(unsigned lvl);
bool check_invariant(unsigned lvl, func_decl *fn);
void checkpoint();
void simplify_formulas();
void predecessor_eh();
void updt_params();
lbool handle_unknown(pob &n, const datalog::rule *r, model &model);
bool mk_mdl_rf_consistent(model &mdl);
public:
/**
Initial values of predicates are stored in corresponding relations in
dctx. We check whether there is some reachable state of the relation
checked_relation.
*/
context(fp_params const ¶ms, ast_manager &m);
~context();
const fp_params &get_params() const { return m_params; }
bool use_eq_prop() const { return m_use_eq_prop; }
bool use_native_mbp() const { return m_use_native_mbp; }
bool use_ground_pob() const { return m_ground_pob; }
bool use_instantiate() const { return m_instantiate; }
bool weak_abs() const { return m_weak_abs; }
bool use_qlemmas() const { return m_use_qlemmas; }
bool use_euf_gen() const { return m_use_euf_gen; }
bool use_lim_num_gen() const { return m_use_lim_num_gen; }
bool simplify_pob() const { return m_simplify_pob; }
bool use_ctp() const { return m_use_ctp; }
bool use_inc_clause() const { return m_use_inc_clause; }
unsigned blast_term_ite_inflation() const {
return m_blast_term_ite_inflation;
}
bool elim_aux() const { return m_elim_aux; }
bool reach_dnf() const { return m_reach_dnf; }
bool use_bg_invs() const { return m_use_bg_invs; }
bool do_subsume() const { return m_gg_subsume; }
ast_manager &get_ast_manager() const { return m; }
manager &get_manager() { return m_pm; }
const manager &get_manager() const { return m_pm; }
decl2rel const &get_pred_transformers() const { return m_rels; }
pred_transformer &get_pred_transformer(func_decl *p) const {
return *m_rels.find(p);
}
datalog::context &get_datalog_context() const {
SASSERT(m_context);
return *m_context;
}
void update_rules(datalog::rule_set &rules);
lbool solve(unsigned from_lvl = 0);
lbool solve_from_lvl(unsigned from_lvl);
expr_ref get_answer();
/**
* get bottom-up (from query) sequence of ground predicate instances
* (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query
*/
expr_ref get_ground_sat_answer() const;
proof_ref get_ground_refutation() const;
void get_rules_along_trace(datalog::rule_ref_vector &rules);
void collect_statistics(statistics &st) const;
void reset_statistics();
void reset();
std::ostream &display(std::ostream &out) const;
void display_certificate(std::ostream &out) const;
pob &get_root() const { return m_pob_queue.get_root(); }
void set_query(func_decl *q) { m_query_pred = q; }
void set_unsat() { m_last_result = l_false; }
void set_model_converter(model_converter_ref &mc) { m_mc = mc; }
model_converter_ref get_model_converter() { return m_mc; }
void set_proof_converter(proof_converter_ref &pc) { m_pc = pc; }
scoped_ptr_vector &callbacks() { return m_callbacks; }
unsigned get_num_levels(func_decl *p);
expr_ref get_cover_delta(int level, func_decl *p_orig, func_decl *p);
void add_cover(int level, func_decl *pred, expr *property, bool bg = false);
expr_ref get_reachable(func_decl *p);
void add_invariant(func_decl *pred, expr *property);
model_ref get_model();
proof_ref get_proof() const { return get_ground_refutation(); }
expr_ref get_constraints(unsigned lvl);
void add_constraint(expr *c, unsigned lvl);
void new_lemma_eh(pred_transformer &pt, lemma *lem);
void new_pob_eh(pob *p);
bool is_inductive();
// close all parents of may pob when gas runs out
void close_all_may_parents(pob_ref node);
// three different solvers with three different sets of parameters
// different solvers are used for different types of queries in spacer
solver *mk_solver0() { return m_pool0->mk_solver(); }
solver *mk_solver1() { return m_pool1->mk_solver(); }
solver *mk_solver2() { return m_pool2->mk_solver(); }
};
inline bool pred_transformer::use_native_mbp() { return ctx.use_native_mbp(); }
} // namespace spacer