3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-12 12:08:18 +00:00

Merge pull request #1809 from agurfinkel/deep_space

Fix performance regression
This commit is contained in:
Nikolaj Bjorner 2018-09-04 20:35:46 -07:00 committed by GitHub
commit 1e11b62bc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 702 additions and 84 deletions

View file

@ -31,7 +31,7 @@ namespace datalog {
rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed): rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed):
m_context(o.m_context) { m_context(o.m_context) {
if (reversed) { if (reversed) {
for (auto & kv : o) { for (auto & kv : o) {
func_decl * pred = kv.m_key; func_decl * pred = kv.m_key;
item_set & orig_items = *kv.get_value(); item_set & orig_items = *kv.get_value();
@ -114,8 +114,8 @@ namespace datalog {
app* a = to_app(e); app* a = to_app(e);
d = a->get_decl(); d = a->get_decl();
if (m_context.is_predicate(d)) { if (m_context.is_predicate(d)) {
// insert d and ensure the invariant // insert d and ensure the invariant
// that every predicate is present as // that every predicate is present as
// a key in m_data // a key in m_data
s.insert(d); s.insert(d);
ensure_key(d); ensure_key(d);
@ -148,7 +148,7 @@ namespace datalog {
item_set& itms = *kv.get_value(); item_set& itms = *kv.get_value();
set_intersection(itms, allowed); set_intersection(itms, allowed);
} }
for (func_decl* f : to_remove) for (func_decl* f : to_remove)
remove_m_data_entry(f); remove_m_data_entry(f);
} }
@ -253,18 +253,18 @@ namespace datalog {
// //
// ----------------------------------- // -----------------------------------
rule_set::rule_set(context & ctx) rule_set::rule_set(context & ctx)
: m_context(ctx), : m_context(ctx),
m_rule_manager(ctx.get_rule_manager()), m_rule_manager(ctx.get_rule_manager()),
m_rules(m_rule_manager), m_rules(m_rule_manager),
m_deps(ctx), m_deps(ctx),
m_stratifier(nullptr), m_stratifier(nullptr),
m_refs(ctx.get_manager()) { m_refs(ctx.get_manager()) {
} }
rule_set::rule_set(const rule_set & other) rule_set::rule_set(const rule_set & other)
: m_context(other.m_context), : m_context(other.m_context),
m_rule_manager(other.m_rule_manager), m_rule_manager(other.m_rule_manager),
m_rules(m_rule_manager), m_rules(m_rule_manager),
m_deps(other.m_context), m_deps(other.m_context),
m_stratifier(nullptr), m_stratifier(nullptr),
@ -353,10 +353,27 @@ namespace datalog {
break; \ break; \
} \ } \
} \ } \
DEL_VECTOR(*rules); DEL_VECTOR(*rules);
DEL_VECTOR(m_rules); DEL_VECTOR(m_rules);
} }
void rule_set::replace_rule(rule * r, rule * other) {
TRACE("dl", r->display(m_context, tout << "replace:"););
func_decl* d = r->get_decl();
rule_vector* rules = m_head2rules.find(d);
#define REPLACE_VECTOR(_v) \
for (unsigned i = (_v).size(); i > 0; ) { \
--i; \
if ((_v)[i] == r) { \
(_v)[i] = other; \
break; \
} \
} \
REPLACE_VECTOR(*rules);
REPLACE_VECTOR(m_rules);
}
void rule_set::ensure_closed() { void rule_set::ensure_closed() {
if (!is_closed()) { if (!is_closed()) {
@ -365,7 +382,7 @@ namespace datalog {
} }
bool rule_set::close() { bool rule_set::close() {
SASSERT(!is_closed()); //the rule_set is not already closed SASSERT(!is_closed()); //the rule_set is not already closed
m_deps.populate(*this); m_deps.populate(*this);
m_stratifier = alloc(rule_stratifier, m_deps); m_stratifier = alloc(rule_stratifier, m_deps);
if (!stratified_negation()) { if (!stratified_negation()) {
@ -426,7 +443,7 @@ namespace datalog {
inherit_predicates(src); inherit_predicates(src);
} }
const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const { const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const {
decl2rules::obj_map_entry * e = m_head2rules.find_core(pred); decl2rules::obj_map_entry * e = m_head2rules.find_core(pred);
if (!e) { if (!e) {
return m_empty_rule_vector; return m_empty_rule_vector;
@ -519,7 +536,7 @@ namespace datalog {
out << "\n"; out << "\n";
non_empty = false; non_empty = false;
} }
for (func_decl * first : *strat) { for (func_decl * first : *strat) {
const func_decl_set & deps = m_deps.get_deps(first); const func_decl_set & deps = m_deps.get_deps(first);
for (func_decl * dep : deps) { for (func_decl * dep : deps) {
@ -545,8 +562,8 @@ namespace datalog {
unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const { unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const {
unsigned num; unsigned num;
if (!m_pred_strat_nums.find(pred, num)) { if (!m_pred_strat_nums.find(pred, num)) {
//the number of the predicate is not stored, therefore it did not appear //the number of the predicate is not stored, therefore it did not appear
//in the algorithm and therefore it does not depend on anything and nothing //in the algorithm and therefore it does not depend on anything and nothing
//depends on it. So it is safe to assign zero strate to it, although it is //depends on it. So it is safe to assign zero strate to it, although it is
//not strictly true. //not strictly true.
num = 0; num = 0;
@ -641,7 +658,7 @@ namespace datalog {
} }
// We put components whose indegree is zero to m_strats and assign its // We put components whose indegree is zero to m_strats and assign its
// m_components entry to zero. // m_components entry to zero.
unsigned comp_cnt = m_components.size(); unsigned comp_cnt = m_components.size();
for (unsigned i = 0; i < comp_cnt; i++) { for (unsigned i = 0; i < comp_cnt; i++) {
@ -680,7 +697,7 @@ namespace datalog {
strats_index++; strats_index++;
} }
//we have managed to topologicaly order all the components //we have managed to topologicaly order all the components
SASSERT(std::find_if(m_components.begin(), m_components.end(), SASSERT(std::find_if(m_components.begin(), m_components.end(),
std::bind1st(std::not_equal_to<item_set*>(), (item_set*)0)) == m_components.end()); std::bind1st(std::not_equal_to<item_set*>(), (item_set*)0)) == m_components.end());
//reverse the strats array, so that the only the later components would depend on earlier ones //reverse the strats array, so that the only the later components would depend on earlier ones
@ -713,7 +730,7 @@ namespace datalog {
} }
out << "\n"; out << "\n";
} }
} }
}; };

View file

@ -77,7 +77,7 @@ namespace datalog {
\brief Number of predicates that depend on \c f. \brief Number of predicates that depend on \c f.
*/ */
unsigned out_degree(func_decl * f) const; unsigned out_degree(func_decl * f) const;
/** /**
\brief If the rependency graph is acyclic, put all elements into \c res \brief If the rependency graph is acyclic, put all elements into \c res
ordered so that elements can depend only on elements that are before them. ordered so that elements can depend only on elements that are before them.
@ -131,7 +131,7 @@ namespace datalog {
it must exist for the whole lifetime of the \c stratifier object. it must exist for the whole lifetime of the \c stratifier object.
*/ */
rule_stratifier(const rule_dependencies & deps) rule_stratifier(const rule_dependencies & deps)
: m_deps(deps), m_next_preorder(0) : m_deps(deps), m_next_preorder(0)
{ {
process(); process();
} }
@ -145,7 +145,7 @@ namespace datalog {
const comp_vector & get_strats() const { return m_strats; } const comp_vector & get_strats() const { return m_strats; }
unsigned get_predicate_strat(func_decl * pred) const; unsigned get_predicate_strat(func_decl * pred) const;
void display( std::ostream & out ) const; void display( std::ostream & out ) const;
}; };
@ -203,6 +203,10 @@ namespace datalog {
\brief Remove rule \c r from the rule set. \brief Remove rule \c r from the rule set.
*/ */
void del_rule(rule * r); void del_rule(rule * r);
/**
\brief Replace a rule \c r with the rule \c other
*/
void replace_rule(rule * r, rule * other);
/** /**
\brief Add all rules from a different rule_set. \brief Add all rules from a different rule_set.
@ -276,8 +280,7 @@ namespace datalog {
inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; } inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; }
}; };
#endif /* DL_RULE_SET_H_ */ #endif /* DL_RULE_SET_H_ */

View file

@ -177,5 +177,6 @@ def_module_params('fp',
('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'), ('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'),
('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'), ('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'),
('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'), ('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'),
('spacer.use_bg_invs', BOOL, False, 'Enable external background invariants'),
)) ))

View file

@ -493,7 +493,8 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) :
m_pob(nullptr), m_ctp(nullptr), m_pob(nullptr), m_ctp(nullptr),
m_lvl(lvl), m_init_lvl(m_lvl), m_lvl(lvl), m_init_lvl(m_lvl),
m_bumped(0), m_weakness(WEAKNESS_MAX), m_bumped(0), m_weakness(WEAKNESS_MAX),
m_external(false), m_blocked(false) { m_external(false), m_blocked(false),
m_background(false) {
SASSERT(m_body); SASSERT(m_body);
normalize(m_body, m_body); normalize(m_body, m_body);
} }
@ -505,7 +506,8 @@ lemma::lemma(pob_ref const &p) :
m_pob(p), m_ctp(nullptr), m_pob(p), m_ctp(nullptr),
m_lvl(p->level()), m_init_lvl(m_lvl), m_lvl(p->level()), m_init_lvl(m_lvl),
m_bumped(0), m_weakness(p->weakness()), m_bumped(0), m_weakness(p->weakness()),
m_external(false), m_blocked(false) { m_external(false), m_blocked(false),
m_background(false) {
SASSERT(m_pob); SASSERT(m_pob);
m_pob->get_skolems(m_zks); m_pob->get_skolems(m_zks);
add_binding(m_pob->get_binding()); add_binding(m_pob->get_binding());
@ -519,8 +521,8 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) :
m_pob(p), m_ctp(nullptr), m_pob(p), m_ctp(nullptr),
m_lvl(p->level()), m_init_lvl(m_lvl), m_lvl(p->level()), m_init_lvl(m_lvl),
m_bumped(0), m_weakness(p->weakness()), m_bumped(0), m_weakness(p->weakness()),
m_external(false), m_blocked(false) m_external(false), m_blocked(false),
{ m_background(false) {
if (m_pob) { if (m_pob) {
m_pob->get_skolems(m_zks); m_pob->get_skolems(m_zks);
add_binding(m_pob->get_binding()); add_binding(m_pob->get_binding());
@ -921,10 +923,10 @@ void pred_transformer::simplify_formulas()
{m_frames.simplify_formulas ();} {m_frames.simplify_formulas ();}
expr_ref pred_transformer::get_formulas(unsigned level) const expr_ref pred_transformer::get_formulas(unsigned level, bool bg) const
{ {
expr_ref_vector res(m); expr_ref_vector res(m);
m_frames.get_frame_geq_lemmas (level, res); m_frames.get_frame_geq_lemmas (level, res, bg);
return mk_and(res); return mk_and(res);
} }
@ -935,6 +937,7 @@ bool pred_transformer::propagate_to_next_level (unsigned src_level)
/// \brief adds a lemma to the solver and to child solvers /// \brief adds a lemma to the solver and to child solvers
void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only)
{ {
SASSERT(!lemma->is_background());
unsigned lvl = lemma->level(); unsigned lvl = lemma->level();
expr* l = lemma->get_expr(); expr* l = lemma->get_expr();
SASSERT(!lemma->is_ground() || is_clause(m, l)); SASSERT(!lemma->is_ground() || is_clause(m, l));
@ -975,8 +978,9 @@ void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only)
next_level(lvl), ground_only); } next_level(lvl), ground_only); }
} }
bool pred_transformer::add_lemma (expr *e, unsigned lvl) { bool pred_transformer::add_lemma (expr *e, unsigned lvl, bool bg) {
lemma_ref lem = alloc(lemma, m, e, lvl); lemma_ref lem = alloc(lemma, m, e, lvl);
lem->set_background(bg);
return m_frames.add_lemma(lem.get()); return m_frames.add_lemma(lem.get());
} }
@ -1217,15 +1221,18 @@ expr_ref pred_transformer::get_origin_summary (model &mdl,
} }
void pred_transformer::add_cover(unsigned level, expr* property) void pred_transformer::add_cover(unsigned level, expr* property, bool bg)
{ {
SASSERT(!bg || is_infty_level(level));
// replace bound variables by local constants. // replace bound variables by local constants.
expr_ref result(property, m), v(m), c(m); expr_ref result(property, m), v(m), c(m);
expr_substitution sub(m); expr_substitution sub(m);
proof_ref pr(m);
pr = m.mk_asserted(m.mk_true());
for (unsigned i = 0; i < sig_size(); ++i) { for (unsigned i = 0; i < sig_size(); ++i) {
c = m.mk_const(pm.o2n(sig(i), 0)); c = m.mk_const(pm.o2n(sig(i), 0));
v = m.mk_var(i, sig(i)->get_range()); v = m.mk_var(i, sig(i)->get_range());
sub.insert(v, c); sub.insert(v, c, pr);
} }
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m); scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
rep->set_substitution(&sub); rep->set_substitution(&sub);
@ -1236,13 +1243,38 @@ void pred_transformer::add_cover(unsigned level, expr* property)
expr_ref_vector lemmas(m); expr_ref_vector lemmas(m);
flatten_and(result, lemmas); flatten_and(result, lemmas);
for (unsigned i = 0, sz = lemmas.size(); i < sz; ++i) { for (unsigned i = 0, sz = lemmas.size(); i < sz; ++i) {
add_lemma(lemmas.get(i), level); add_lemma(lemmas.get(i), level, bg);
} }
} }
void pred_transformer::propagate_to_infinity (unsigned level) void pred_transformer::propagate_to_infinity (unsigned level)
{m_frames.propagate_to_infinity (level);} {m_frames.propagate_to_infinity (level);}
// compute a conjunction of all background facts
void pred_transformer::get_pred_bg_invs(expr_ref_vector& out) {
expr_ref inv(m), tmp1(m), tmp2(m);
ptr_vector<func_decl> preds;
for (auto kv : m_pt_rules) {
expr* tag = kv.m_value->tag();
datalog::rule const &r = kv.m_value->rule();
find_predecessors (r, preds);
for (unsigned i = 0, preds_sz = preds.size(); i < preds_sz; i++) {
func_decl* pre = preds[i];
pred_transformer &pt = ctx.get_pred_transformer(pre);
const lemma_ref_vector &invs = pt.get_bg_invs();
CTRACE("spacer", !invs.empty(),
tout << "add-bg-invariant: " << mk_pp (pre, m) << "\n";);
for (auto inv : invs) {
// tag -> inv1 ... tag -> invn
tmp1 = m.mk_implies(tag, inv->get_expr());
pm.formula_n2o(tmp1, tmp2, i);
out.push_back(tmp2);
TRACE("spacer", tout << tmp2 << "\n";);
}
}
}
}
/// \brief Returns true if the obligation is already blocked by current lemmas /// \brief Returns true if the obligation is already blocked by current lemmas
@ -1344,6 +1376,14 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core,
expr_ref_vector post (m), reach_assumps (m); expr_ref_vector post (m), reach_assumps (m);
post.push_back (n.post ()); post.push_back (n.post ());
flatten_and(post);
// if equality propagation is disabled in arithmetic, expand
// equality literals into two inequalities to increase the space
// for interpolation
if (!ctx.use_eq_prop()) {
expand_literals(m, post);
}
// populate reach_assumps // populate reach_assumps
if (n.level () > 0 && !m_all_init) { if (n.level () > 0 && !m_all_init) {
@ -1472,7 +1512,7 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem,
expr_ref lemma_expr(m); expr_ref lemma_expr(m);
lemma_expr = lem->get_expr(); lemma_expr = lem->get_expr();
expr_ref_vector conj(m), aux(m); expr_ref_vector cand(m), aux(m), conj(m);
expr_ref gnd_lemma(m); expr_ref gnd_lemma(m);
@ -1482,8 +1522,8 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem,
lemma_expr = gnd_lemma.get(); lemma_expr = gnd_lemma.get();
} }
conj.push_back(mk_not(m, lemma_expr)); cand.push_back(mk_not(m, lemma_expr));
flatten_and (conj); flatten_and (cand);
prop_solver::scoped_level _sl(*m_solver, level); prop_solver::scoped_level _sl(*m_solver, level);
prop_solver::scoped_subset_core _sc (*m_solver, true); prop_solver::scoped_subset_core _sc (*m_solver, true);
@ -1494,9 +1534,12 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem,
if (ctx.use_ctp()) {mdl_ref_ptr = &mdl;} if (ctx.use_ctp()) {mdl_ref_ptr = &mdl;}
m_solver->set_core(core); m_solver->set_core(core);
m_solver->set_model(mdl_ref_ptr); m_solver->set_model(mdl_ref_ptr);
expr * bg = m_extend_lit.get ();
lbool r = m_solver->check_assumptions (conj, aux, m_transition_clause, conj.push_back(m_extend_lit);
1, &bg, 1); if (ctx.use_bg_invs()) get_pred_bg_invs(conj);
lbool r = m_solver->check_assumptions (cand, aux, m_transition_clause,
conj.size(), conj.c_ptr(), 1);
if (r == l_false) { if (r == l_false) {
solver_level = m_solver->uses_level (); solver_level = m_solver->uses_level ();
lem->reset_ctp(); lem->reset_ctp();
@ -1527,6 +1570,7 @@ bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state,
m_solver->set_core(&core); m_solver->set_core(&core);
m_solver->set_model (nullptr); m_solver->set_model (nullptr);
expr_ref_vector aux (m); expr_ref_vector aux (m);
if (ctx.use_bg_invs()) get_pred_bg_invs(conj);
conj.push_back (m_extend_lit); conj.push_back (m_extend_lit);
lbool res = m_solver->check_assumptions (state, aux, lbool res = m_solver->check_assumptions (state, aux,
m_transition_clause, m_transition_clause,
@ -1941,14 +1985,27 @@ void pred_transformer::update_solver_with_rfs(prop_solver *solver,
} }
/// pred_transformer::frames /// pred_transformer::frames
bool pred_transformer::frames::add_lemma(lemma *new_lemma) bool pred_transformer::frames::add_lemma(lemma *new_lemma)
{ {
TRACE("spacer", tout << "add-lemma: " << pp_level(new_lemma->level()) << " " TRACE("spacer", tout << "add-lemma: " << pp_level(new_lemma->level()) << " "
<< m_pt.head()->get_name() << " " << m_pt.head()->get_name() << " "
<< mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); << mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";);
if (new_lemma->is_background()) {
SASSERT (is_infty_level(new_lemma->level()));
for (auto &l : m_bg_invs) {
if (l->get_expr() == new_lemma->get_expr()) return false;
}
TRACE("spacer", tout << "add-external-lemma: "
<< pp_level(new_lemma->level()) << " "
<< m_pt.head()->get_name() << " "
<< mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";);
m_bg_invs.push_back(new_lemma);
return true;
}
unsigned i = 0; unsigned i = 0;
for (auto *old_lemma : m_lemmas) { for (auto *old_lemma : m_lemmas) {
if (old_lemma->get_expr() == new_lemma->get_expr()) { if (old_lemma->get_expr() == new_lemma->get_expr()) {
@ -2295,6 +2352,7 @@ void context::updt_params() {
m_use_restarts = m_params.spacer_restarts(); m_use_restarts = m_params.spacer_restarts();
m_restart_initial_threshold = m_params.spacer_restart_initial_threshold(); m_restart_initial_threshold = m_params.spacer_restart_initial_threshold();
m_pdr_bfs = m_params.spacer_gpdr_bfs(); m_pdr_bfs = m_params.spacer_gpdr_bfs();
m_use_bg_invs = m_params.spacer_use_bg_invs();
if (m_use_gpdr) { if (m_use_gpdr) {
// set options to be compatible with GPDR // set options to be compatible with GPDR
@ -2423,36 +2481,38 @@ expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p)
if (m_rels.find(p, pt)) { if (m_rels.find(p, pt)) {
return pt->get_cover_delta(p_orig, level); return pt->get_cover_delta(p_orig, level);
} else { } else {
IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); IF_VERBOSE(10, verbose_stream() << "did not find predicate "
<< p->get_name() << "\n";);
return expr_ref(m.mk_true(), m); return expr_ref(m.mk_true(), m);
} }
} }
void context::add_cover(int level, func_decl* p, expr* property) void context::add_cover(int level, func_decl* p, expr* property, bool bg)
{ {
scoped_proof _pf_(m);
pred_transformer* pt = nullptr; pred_transformer* pt = nullptr;
if (!m_rels.find(p, pt)) { if (!m_rels.find(p, pt)) {
pt = alloc(pred_transformer, *this, get_manager(), p); pt = alloc(pred_transformer, *this, get_manager(), p);
m_rels.insert(p, pt); m_rels.insert(p, pt);
IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); IF_VERBOSE(10, verbose_stream() << "did not find predicate "
<< p->get_name() << "\n";);
} }
unsigned lvl = (level == -1)?infty_level():((unsigned)level); unsigned lvl = (level == -1)?infty_level():((unsigned)level);
pt->add_cover(lvl, property); pt->add_cover(lvl, property, bg);
} }
void context::add_invariant (func_decl *p, expr *property) void context::add_invariant (func_decl *p, expr *property)
{add_cover (infty_level(), p, property);} {add_cover (infty_level(), p, property, true);}
expr_ref context::get_reachable(func_decl *p) expr_ref context::get_reachable(func_decl *p) {
{
pred_transformer* pt = nullptr; pred_transformer* pt = nullptr;
if (!m_rels.find(p, pt)) if (!m_rels.find(p, pt))
{ return expr_ref(m.mk_false(), m); } { return expr_ref(m.mk_false(), m); }
return pt->get_reachable(); return pt->get_reachable();
} }
bool context::validate() bool context::validate() {
{
if (!m_validate_result) { return true; } if (!m_validate_result) { return true; }
std::stringstream msg; std::stringstream msg;
@ -2483,7 +2543,7 @@ bool context::validate()
model_ref model; model_ref model;
vector<relation_info> rs; vector<relation_info> rs;
model_converter_ref mc; model_converter_ref mc;
get_level_property(m_inductive_lvl, refs, rs); get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
inductive_property ex(m, mc, rs); inductive_property ex(m, mc, rs);
ex.to_model(model); ex.to_model(model);
var_subst vs(m, false); var_subst vs(m, false);
@ -2624,13 +2684,13 @@ void context::init_lemma_generalizers()
} }
void context::get_level_property(unsigned lvl, expr_ref_vector& res, void context::get_level_property(unsigned lvl, expr_ref_vector& res,
vector<relation_info>& rs) const { vector<relation_info>& rs, bool with_bg) const {
for (auto const& kv : m_rels) { for (auto const& kv : m_rels) {
pred_transformer* r = kv.m_value; pred_transformer* r = kv.m_value;
if (r->head() == m_query_pred) { if (r->head() == m_query_pred) {
continue; continue;
} }
expr_ref conj = r->get_formulas(lvl); expr_ref conj = r->get_formulas(lvl, with_bg);
m_pm.formula_n2o(0, false, conj); m_pm.formula_n2o(0, false, conj);
res.push_back(conj); res.push_back(conj);
ptr_vector<func_decl> sig(r->head()->get_arity(), r->sig()); ptr_vector<func_decl> sig(r->head()->get_arity(), r->sig());
@ -2662,7 +2722,7 @@ lbool context::solve(unsigned from_lvl)
IF_VERBOSE(1, { IF_VERBOSE(1, {
expr_ref_vector refs(m); expr_ref_vector refs(m);
vector<relation_info> rs; vector<relation_info> rs;
get_level_property(m_inductive_lvl, refs, rs); get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
model_converter_ref mc; model_converter_ref mc;
inductive_property ex(m, mc, rs); inductive_property ex(m, mc, rs);
verbose_stream() << ex.to_string(); verbose_stream() << ex.to_string();
@ -2844,7 +2904,7 @@ model_ref context::get_model()
model_ref model; model_ref model;
expr_ref_vector refs(m); expr_ref_vector refs(m);
vector<relation_info> rs; vector<relation_info> rs;
get_level_property(m_inductive_lvl, refs, rs); get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs); inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs);
ex.to_model (model); ex.to_model (model);
return model; return model;
@ -2877,7 +2937,7 @@ expr_ref context::mk_unsat_answer() const
{ {
expr_ref_vector refs(m); expr_ref_vector refs(m);
vector<relation_info> rs; vector<relation_info> rs;
get_level_property(m_inductive_lvl, refs, rs); get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs); inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs);
return ex.to_expr(); return ex.to_expr();
} }

View file

@ -128,8 +128,9 @@ class lemma {
unsigned m_init_lvl; // level at which lemma was created unsigned m_init_lvl; // level at which lemma was created
unsigned m_bumped:16; unsigned m_bumped:16;
unsigned m_weakness:16; unsigned m_weakness:16;
unsigned m_external:1; unsigned m_external:1; // external lemma from another solver
unsigned m_blocked:1; unsigned m_blocked:1; // blocked by CTP
unsigned m_background:1; // background assumed fact
void mk_expr_core(); void mk_expr_core();
void mk_cube_core(); void mk_cube_core();
@ -163,6 +164,9 @@ public:
void set_external(bool ext){m_external = ext;} void set_external(bool ext){m_external = ext;}
bool external() { return m_external;} 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;} bool is_blocked() {return m_blocked;}
void set_blocked(bool v) {m_blocked=v;} void set_blocked(bool v) {m_blocked=v;}
@ -222,6 +226,7 @@ class pred_transformer {
pred_transformer &m_pt; // parent pred_transformer pred_transformer &m_pt; // parent pred_transformer
lemma_ref_vector m_pinned_lemmas; // all created lemmas lemma_ref_vector m_pinned_lemmas; // all created lemmas
lemma_ref_vector m_lemmas; // active lemmas lemma_ref_vector m_lemmas; // active lemmas
lemma_ref_vector m_bg_invs; // background (assumed) invariants
unsigned m_size; // num of frames unsigned m_size; // num of frames
bool m_sorted; // true if m_lemmas is sorted by m_lt bool m_sorted; // true if m_lemmas is sorted by m_lt
@ -230,7 +235,8 @@ class pred_transformer {
void sort (); void sort ();
public: public:
frames (pred_transformer &pt) : m_pt (pt), m_size(0), m_sorted (true) {} frames (pred_transformer &pt) : m_pt (pt),
m_size(0), m_sorted (true) {}
~frames() {} ~frames() {}
void simplify_formulas (); void simplify_formulas ();
@ -245,17 +251,25 @@ class pred_transformer {
} }
} }
} }
void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) const { void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out,
bool with_bg = false) const {
for (auto &lemma : m_lemmas) { for (auto &lemma : m_lemmas) {
if (lemma->level() >= level) { if (lemma->level() >= level) {
out.push_back(lemma->get_expr()); out.push_back(lemma->get_expr());
} }
} }
if (with_bg) {
for (auto &lemma : m_bg_invs)
out.push_back(lemma->get_expr());
}
} }
unsigned size () const {return m_size;} const lemma_ref_vector& get_bg_invs() const {return m_bg_invs;}
unsigned lemma_size () const {return m_lemmas.size ();} unsigned size() const {return m_size;}
void add_frame () {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) { void inherit_frames (frames &other) {
for (auto &other_lemma : other.m_lemmas) { for (auto &other_lemma : other.m_lemmas) {
lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(), lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(),
@ -265,6 +279,7 @@ class pred_transformer {
add_lemma(new_lemma.get()); add_lemma(new_lemma.get());
} }
m_sorted = false; m_sorted = false;
m_bg_invs.append(other.m_bg_invs);
} }
bool add_lemma (lemma *new_lemma); bool add_lemma (lemma *new_lemma);
@ -418,6 +433,11 @@ class pred_transformer {
app_ref mk_fresh_rf_tag (); 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: public:
pred_transformer(context& ctx, manager& pm, func_decl* head); pred_transformer(context& ctx, manager& pm, func_decl* head);
~pred_transformer() {} ~pred_transformer() {}
@ -448,7 +468,7 @@ public:
} }
unsigned get_num_levels() const {return m_frames.size ();} unsigned get_num_levels() const {return m_frames.size ();}
expr_ref get_cover_delta(func_decl* p_orig, int level); expr_ref get_cover_delta(func_decl* p_orig, int level);
void add_cover(unsigned level, expr* property); void add_cover(unsigned level, expr* property, bool bg = false);
expr_ref get_reachable(); expr_ref get_reachable();
std::ostream& display(std::ostream& strm) const; std::ostream& display(std::ostream& strm) const;
@ -484,7 +504,7 @@ public:
bool propagate_to_next_level(unsigned level); bool propagate_to_next_level(unsigned level);
void propagate_to_infinity(unsigned level); void propagate_to_infinity(unsigned level);
/// \brief Add a lemma to the current context and all users /// \brief Add a lemma to the current context and all users
bool add_lemma(expr * lemma, unsigned lvl); bool add_lemma(expr * e, unsigned lvl, bool bg);
bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);} bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);}
expr* get_reach_case_var (unsigned idx) const; expr* get_reach_case_var (unsigned idx) const;
bool has_rfs () const { return !m_reach_facts.empty () ;} bool has_rfs () const { return !m_reach_facts.empty () ;}
@ -527,7 +547,7 @@ public:
bool check_inductive(unsigned level, expr_ref_vector& state, bool check_inductive(unsigned level, expr_ref_vector& state,
unsigned& assumes_level, unsigned weakness = UINT_MAX); unsigned& assumes_level, unsigned weakness = UINT_MAX);
expr_ref get_formulas(unsigned level) const; expr_ref get_formulas(unsigned level, bool bg = false) const;
void simplify_formulas(); void simplify_formulas();
@ -958,6 +978,7 @@ class context {
bool m_simplify_formulas_pre; bool m_simplify_formulas_pre;
bool m_simplify_formulas_post; bool m_simplify_formulas_post;
bool m_pdr_bfs; bool m_pdr_bfs;
bool m_use_bg_invs;
unsigned m_push_pob_max_depth; unsigned m_push_pob_max_depth;
unsigned m_max_level; unsigned m_max_level;
unsigned m_restart_initial_threshold; unsigned m_restart_initial_threshold;
@ -992,7 +1013,8 @@ class context {
// Generate inductive property // Generate inductive property
void get_level_property(unsigned lvl, expr_ref_vector& res, void get_level_property(unsigned lvl, expr_ref_vector& res,
vector<relation_info> & rs) const; vector<relation_info> & rs,
bool with_bg = false) const;
// Initialization // Initialization
@ -1027,18 +1049,20 @@ public:
const fp_params &get_params() const { return m_params; } const fp_params &get_params() const { return m_params; }
bool use_native_mbp () {return m_use_native_mbp;} bool use_eq_prop() const {return m_use_eq_prop;}
bool use_ground_pob () {return m_ground_pob;} bool use_native_mbp() const {return m_use_native_mbp;}
bool use_instantiate () {return m_instantiate;} bool use_ground_pob() const {return m_ground_pob;}
bool weak_abs() {return m_weak_abs;} bool use_instantiate() const {return m_instantiate;}
bool use_qlemmas () {return m_use_qlemmas;} bool weak_abs() const {return m_weak_abs;}
bool use_euf_gen() {return m_use_euf_gen;} bool use_qlemmas() const {return m_use_qlemmas;}
bool simplify_pob() {return m_simplify_pob;} bool use_euf_gen() const {return m_use_euf_gen;}
bool use_ctp() {return m_use_ctp;} bool simplify_pob() const {return m_simplify_pob;}
bool use_inc_clause() {return m_use_inc_clause;} bool use_ctp() const {return m_use_ctp;}
unsigned blast_term_ite_inflation() {return m_blast_term_ite_inflation;} bool use_inc_clause() const {return m_use_inc_clause;}
bool elim_aux() {return m_elim_aux;} unsigned blast_term_ite_inflation() const {return m_blast_term_ite_inflation;}
bool reach_dnf() {return m_reach_dnf;} 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;}
ast_manager& get_ast_manager() const {return m;} ast_manager& get_ast_manager() const {return m;}
manager& get_manager() {return m_pm;} manager& get_manager() {return m_pm;}
@ -1081,7 +1105,7 @@ public:
unsigned get_num_levels(func_decl* p); unsigned get_num_levels(func_decl* p);
expr_ref get_cover_delta(int level, func_decl* p_orig, 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); void add_cover(int level, func_decl* pred, expr* property, bool bg = false);
expr_ref get_reachable (func_decl* p); expr_ref get_reachable (func_decl* p);
void add_invariant (func_decl *pred, expr* property); void add_invariant (func_decl *pred, expr* property);
model_ref get_model(); model_ref get_model();

View file

@ -94,6 +94,7 @@ void prop_solver::add_level()
void prop_solver::ensure_level(unsigned lvl) void prop_solver::ensure_level(unsigned lvl)
{ {
if (is_infty_level(lvl)) return;
while (lvl >= level_cnt()) { while (lvl >= level_cnt()) {
add_level(); add_level();
} }

View file

@ -48,7 +48,8 @@ namespace spacer {
} }
inline bool is_infty_level(unsigned lvl) { inline bool is_infty_level(unsigned lvl) {
return lvl == infty_level (); // XXX: level is 16 bits in class pob
return lvl >= 65535;
} }
inline unsigned next_level(unsigned lvl) { inline unsigned next_level(unsigned lvl) {

View file

@ -25,6 +25,7 @@ z3_add_component(transforms
dl_mk_array_eq_rewrite.cpp dl_mk_array_eq_rewrite.cpp
dl_mk_array_instantiation.cpp dl_mk_array_instantiation.cpp
dl_mk_elim_term_ite.cpp dl_mk_elim_term_ite.cpp
dl_mk_synchronize.cpp
COMPONENT_DEPENDENCIES COMPONENT_DEPENDENCIES
dataflow dataflow
hilbert hilbert

View file

@ -0,0 +1,376 @@
/*++
Copyright (c) 2017-2018 Saint-Petersburg State University
Module Name:
dl_mk_synchronize.h
Abstract:
Rule transformer that attempts to merge recursive iterations
relaxing the shape of the inductive invariant.
Author:
Dmitry Mordvinov (dvvrd) 2017-05-24
Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20
Revision History:
--*/
#include "muz/transforms/dl_mk_synchronize.h"
#include <algorithm>
namespace datalog {
typedef mk_synchronize::item_set_vector item_set_vector;
mk_synchronize::mk_synchronize(context& ctx, unsigned priority):
rule_transformer::plugin(priority, false),
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager())
{}
bool mk_synchronize::is_recursive(rule &r, func_decl &decl) const {
func_decl *hdecl = r.get_head()->get_decl();
// AG: shouldn't decl appear in the body?
if (hdecl == &decl) return true;
auto & strata = m_stratifier->get_strats();
unsigned num_of_stratum = m_stratifier->get_predicate_strat(hdecl);
return strata[num_of_stratum]->contains(&decl);
}
bool mk_synchronize::has_recursive_premise(app * app) const {
func_decl* app_decl = app->get_decl();
if (m_deps->get_deps(app_decl).contains(app_decl)) {
return true;
}
rule_stratifier::comp_vector const & strata = m_stratifier->get_strats();
unsigned num_of_stratum = m_stratifier->get_predicate_strat(app_decl);
return strata[num_of_stratum]->size() > 1;
}
item_set_vector mk_synchronize::add_merged_decls(ptr_vector<app> & apps) {
unsigned sz = apps.size();
item_set_vector merged_decls;
merged_decls.resize(sz);
auto & strata = m_stratifier->get_strats();
for (unsigned j = 0; j < sz; ++j) {
unsigned nos;
nos = m_stratifier->get_predicate_strat(apps[j]->get_decl());
merged_decls[j] = strata[nos];
}
return merged_decls;
}
void mk_synchronize::add_new_rel_symbols(unsigned idx,
item_set_vector const & decls,
ptr_vector<func_decl> & decls_buf,
bool & was_added) {
if (idx >= decls.size()) {
string_buffer<> buffer;
ptr_vector<sort> domain;
for (auto &d : decls_buf) {
buffer << d->get_name() << "!!";
domain.append(d->get_arity(), d->get_domain());
}
symbol new_name = symbol(buffer.c_str());
if (!m_cache.contains(new_name)) {
was_added = true;
func_decl* orig = decls_buf[0];
func_decl* product_pred = m_ctx.mk_fresh_head_predicate(new_name,
symbol::null, domain.size(), domain.c_ptr(), orig);
m_cache.insert(new_name, product_pred);
}
return;
}
// -- compute Cartesian product of decls, and create a new
// -- predicate for each element of the product
for (auto &p : *decls[idx]) {
decls_buf[idx] = p;
add_new_rel_symbols(idx + 1, decls, decls_buf, was_added);
}
}
void mk_synchronize::replace_applications(rule & r, rule_set & rules,
ptr_vector<app> & apps) {
app_ref replacing = product_application(apps);
ptr_vector<app> new_tail;
svector<bool> new_tail_neg;
unsigned n = r.get_tail_size() - apps.size() + 1;
unsigned tail_idx = 0;
new_tail.resize(n);
new_tail_neg.resize(n);
new_tail[0] = replacing;
new_tail_neg[0] = false;
for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) {
app* tail = r.get_tail(i);
if (!apps.contains(tail)) {
++tail_idx;
new_tail[tail_idx] = tail;
new_tail_neg[tail_idx] = false;
}
}
for (unsigned i = r.get_positive_tail_size(); i < r.get_uninterpreted_tail_size(); ++i) {
++tail_idx;
new_tail[tail_idx] = r.get_tail(i);
new_tail_neg[tail_idx] = true;
}
for (unsigned i = r.get_uninterpreted_tail_size(); i < r.get_tail_size(); ++i) {
++tail_idx;
new_tail[tail_idx] = r.get_tail(i);
new_tail_neg[tail_idx] = false;
}
rule_ref new_rule(rm);
new_rule = rm.mk(r.get_head(), tail_idx + 1,
new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol::null, false);
rules.replace_rule(&r, new_rule.get());
}
rule_ref mk_synchronize::rename_bound_vars_in_rule(rule * r,
unsigned & var_idx) {
// AG: shift all variables in a rule so that lowest var index is var_idx?
// AG: update var_idx in the process?
ptr_vector<sort> sorts;
r->get_vars(m, sorts);
expr_ref_vector revsub(m);
revsub.resize(sorts.size());
for (unsigned i = 0; i < sorts.size(); ++i) {
if (sorts[i]) {
revsub[i] = m.mk_var(var_idx++, sorts[i]);
}
}
rule_ref new_rule(rm);
new_rule = rm.mk(r);
rm.substitute(new_rule, revsub.size(), revsub.c_ptr());
return new_rule;
}
vector<rule_ref_vector> mk_synchronize::rename_bound_vars(item_set_vector const & heads,
rule_set & rules) {
// AG: is every item_set in heads corresponds to rules that are merged?
// AG: why are bound variables renamed in the first place?
// AG: the data structure seems too complex
vector<rule_ref_vector> result;
unsigned var_idx = 0;
for (auto item : heads) {
rule_ref_vector dst_vector(rm);
for (auto *head : *item) {
for (auto *r : rules.get_predicate_rules(head)) {
rule_ref new_rule = rename_bound_vars_in_rule(r, var_idx);
dst_vector.push_back(new_rule.get());
}
}
result.push_back(dst_vector);
}
return result;
}
void mk_synchronize::add_rec_tail(vector< ptr_vector<app> > & recursive_calls,
app_ref_vector & new_tail,
svector<bool> & new_tail_neg,
unsigned & tail_idx) {
unsigned max_sz = 0;
for (auto &rc : recursive_calls)
max_sz= std::max(rc.size(), max_sz);
unsigned n = recursive_calls.size();
ptr_vector<app> merged_recursive_calls;
for (unsigned j = 0; j < max_sz; ++j) {
merged_recursive_calls.reset();
merged_recursive_calls.resize(n);
for (unsigned i = 0; i < n; ++i) {
unsigned sz = recursive_calls[i].size();
merged_recursive_calls[i] =
j < sz ? recursive_calls[i][j] : recursive_calls[i][sz - 1];
}
++tail_idx;
new_tail[tail_idx] = product_application(merged_recursive_calls);
new_tail_neg[tail_idx] = false;
}
}
void mk_synchronize::add_non_rec_tail(rule & r, app_ref_vector & new_tail,
svector<bool> & new_tail_neg,
unsigned & tail_idx) {
for (unsigned i = 0, sz = r.get_positive_tail_size(); i < sz; ++i) {
app* tail = r.get_tail(i);
if (!is_recursive(r, *tail)) {
++tail_idx;
new_tail[tail_idx] = tail;
new_tail_neg[tail_idx] = false;
}
}
for (unsigned i = r.get_positive_tail_size(),
sz = r.get_uninterpreted_tail_size() ; i < sz; ++i) {
++tail_idx;
new_tail[tail_idx] = r.get_tail(i);
new_tail_neg[tail_idx] = true;
}
for (unsigned i = r.get_uninterpreted_tail_size(),
sz = r.get_tail_size(); i < sz; ++i) {
++tail_idx;
new_tail[tail_idx] = r.get_tail(i);
new_tail_neg[tail_idx] = r.is_neg_tail(i);
}
}
app_ref mk_synchronize::product_application(ptr_vector<app> const &apps) {
unsigned args_num = 0;
string_buffer<> buffer;
// AG: factor out into mk_name
for (auto *app : apps) {
buffer << app->get_decl()->get_name() << "!!";
args_num += app->get_num_args();
}
symbol name = symbol(buffer.c_str());
SASSERT(m_cache.contains(name));
func_decl * pred = m_cache[name];
ptr_vector<expr> args;
args.resize(args_num);
unsigned idx = 0;
for (auto *a : apps) {
for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i, ++idx)
args[idx] = a->get_arg(i);
}
return app_ref(m.mk_app(pred, args_num, args.c_ptr()), m);
}
rule_ref mk_synchronize::product_rule(rule_ref_vector const & rules) {
unsigned n = rules.size();
string_buffer<> buffer;
bool first_rule = true;
for (auto it = rules.begin(); it != rules.end(); ++it, first_rule = false) {
if (!first_rule) {
buffer << "+";
}
buffer << (*it)->name();
}
ptr_vector<app> heads;
heads.resize(n);
for (unsigned i = 0; i < n; ++i) {
heads[i] = rules[i]->get_head();
}
app_ref product_head = product_application(heads);
unsigned product_tail_length = 0;
bool has_recursion = false;
vector< ptr_vector<app> > recursive_calls;
recursive_calls.resize(n);
for (unsigned i = 0; i < n; ++i) {
rule& rule = *rules[i];
product_tail_length += rule.get_tail_size();
for (unsigned j = 0; j < rule.get_positive_tail_size(); ++j) {
app* tail = rule.get_tail(j);
if (is_recursive(rule, *tail)) {
has_recursion = true;
recursive_calls[i].push_back(tail);
}
}
if (recursive_calls[i].empty()) {
recursive_calls[i].push_back(rule.get_head());
}
}
app_ref_vector new_tail(m);
svector<bool> new_tail_neg;
new_tail.resize(product_tail_length);
new_tail_neg.resize(product_tail_length);
unsigned tail_idx = -1;
if (has_recursion) {
add_rec_tail(recursive_calls, new_tail, new_tail_neg, tail_idx);
}
for (rule_vector::const_iterator it = rules.begin(); it != rules.end(); ++it) {
rule& rule = **it;
add_non_rec_tail(rule, new_tail, new_tail_neg, tail_idx);
}
rule_ref new_rule(rm);
new_rule = rm.mk(product_head, tail_idx + 1,
new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol(buffer.c_str()), false);
rm.fix_unbound_vars(new_rule, false);
return new_rule;
}
void mk_synchronize::merge_rules(unsigned idx, rule_ref_vector & buf,
vector<rule_ref_vector> const & merged_rules,
rule_set & all_rules) {
if (idx >= merged_rules.size()) {
rule_ref product = product_rule(buf);
all_rules.add_rule(product.get());
return;
}
for (auto *r : merged_rules[idx]) {
buf[idx] = r;
merge_rules(idx + 1, buf, merged_rules, all_rules);
}
}
void mk_synchronize::merge_applications(rule & r, rule_set & rules) {
ptr_vector<app> non_recursive_preds;
obj_hashtable<app> apps;
for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) {
app* t = r.get_tail(i);
if (!is_recursive(r, *t) && has_recursive_premise(t)) {
apps.insert(t);
}
}
if (apps.size() < 2) return;
for (auto *a : apps) non_recursive_preds.push_back(a);
item_set_vector merged_decls = add_merged_decls(non_recursive_preds);
unsigned n = non_recursive_preds.size();
ptr_vector<func_decl> decls_buf;
decls_buf.resize(n);
bool was_added = false;
add_new_rel_symbols(0, merged_decls, decls_buf, was_added);
if (was_added){
rule_ref_vector rules_buf(rm);
rules_buf.resize(n);
vector<rule_ref_vector> renamed_rules = rename_bound_vars(merged_decls, rules);
merge_rules(0, rules_buf, renamed_rules, rules);
}
replace_applications(r, rules, non_recursive_preds);
m_deps->populate(rules);
m_stratifier = alloc(rule_stratifier, *m_deps);
}
rule_set * mk_synchronize::operator()(rule_set const & source) {
rule_set* rules = alloc(rule_set, m_ctx);
rules->inherit_predicates(source);
for (auto *r : source) { rules->add_rule(r); }
m_deps = alloc(rule_dependencies, m_ctx);
m_deps->populate(*rules);
m_stratifier = alloc(rule_stratifier, *m_deps);
unsigned current_rule = 0;
while (current_rule < rules->get_num_rules()) {
rule *r = rules->get_rule(current_rule);
merge_applications(*r, *rules);
++current_rule;
}
return rules;
}
};

View file

@ -0,0 +1,134 @@
/*++
Copyright (c) 2017-2018 Saint-Petersburg State University
Module Name:
dl_mk_synchronize.h
Abstract:
Rule transformer that attempts to merge recursive iterations
relaxing the shape of the inductive invariant.
Example:
Q(z) :- A(x), B(y), phi1(x,y,z).
A(x) :- A(x'), phi2(x,x').
A(x) :- phi3(x).
B(y) :- C(y'), phi4(y,y').
C(y) :- B(y'), phi5(y,y').
B(y) :- phi6(y).
Transformed clauses:
Q(z) :- AB(x,y), phi1(x,y,z).
AB(x,y) :- AC(x',y'), phi2(x,x'), phi4(y,y').
AC(x,y) :- AB(x',y'), phi2(x,x'), phi5(y,y').
AB(x,y) :- AC(x, y'), phi3(x), phi4(y,y').
AC(x,y) :- AB(x, y'), phi3(x), phi5(y,y').
AB(x,y) :- AB(x',y), phi2(x,x'), phi6(y).
AB(x,y) :- phi3(x), phi6(y).
Author:
Dmitry Mordvinov (dvvrd) 2017-05-24
Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20
Revision History:
--*/
#ifndef DL_MK_SYNCHRONIZE_H_
#define DL_MK_SYNCHRONIZE_H_
#include"muz/base/dl_context.h"
#include"muz/base/dl_rule_set.h"
#include"util/uint_set.h"
#include"muz/base/dl_rule_transformer.h"
#include"muz/transforms/dl_mk_rule_inliner.h"
namespace datalog {
/**
\brief Implements a synchronous product transformation.
*/
class mk_synchronize : public rule_transformer::plugin {
public:
typedef ptr_vector<rule_stratifier::item_set> item_set_vector;
private:
context& m_ctx;
ast_manager& m;
rule_manager& rm;
scoped_ptr<rule_dependencies> m_deps;
scoped_ptr<rule_stratifier> m_stratifier;
// symbol table that maps new predicate names to corresponding
// func_decl
map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> m_cache;
/// returns true if decl is recursive in the given rule
/// requires that decl appears in the tail of r
bool is_recursive(rule &r, func_decl &decl) const;
bool is_recursive(rule &r, expr &e) const {
SASSERT(is_app(&e));
return is_app(&e) && is_recursive(r, *to_app(&e)->get_decl());
}
/// returns true if the top-level predicate of app is recursive
bool has_recursive_premise(app * app) const;
item_set_vector add_merged_decls(ptr_vector<app> & apps);
/**
Compute Cartesian product of decls and create a new
predicate for each element. For example, if decls is
( (a, b), (c, d) ) )
create new predicates: a!!c, a!!d, b!!c, and b!!d
*/
void add_new_rel_symbols(unsigned idx, item_set_vector const & decls,
ptr_vector<func_decl> & buf, bool & was_added);
/**
Convert vector of predicate apps into a single app. For example,
(Foo a) (Bar b) becomes (Foo!!Bar a b)
*/
app_ref product_application(ptr_vector<app> const & apps);
/**
Replace a given rule by a rule in which conjunction of
predicates is replaced by a single product predicate
*/
void replace_applications(rule & r, rule_set & rules,
ptr_vector<app> & apps);
rule_ref rename_bound_vars_in_rule(rule * r, unsigned & var_idx);
vector<rule_ref_vector> rename_bound_vars(item_set_vector const & heads,
rule_set & rules);
void add_rec_tail(vector< ptr_vector<app> > & recursive_calls,
app_ref_vector & new_tail,
svector<bool> & new_tail_neg, unsigned & tail_idx);
void add_non_rec_tail(rule & r, app_ref_vector & new_tail,
svector<bool> & new_tail_neg,
unsigned & tail_idx);
rule_ref product_rule(rule_ref_vector const & rules);
void merge_rules(unsigned idx, rule_ref_vector & buf,
vector<rule_ref_vector> const & merged_rules, rule_set & all_rules);
void merge_applications(rule & r, rule_set & rules);
public:
/**
\brief Create synchronous product transformer.
*/
mk_synchronize(context & ctx, unsigned priority = 22500);
rule_set * operator()(rule_set const & source);
};
};
#endif /* DL_MK_SYNCHRONIZE_H_ */