3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-12 20:18: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

@ -358,6 +358,23 @@ namespace datalog {
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()) {
VERIFY(close()); VERIFY(close());

View file

@ -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.
@ -280,4 +284,3 @@ namespace datalog {
}; };
#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_ */