3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-11 19:53:34 +00:00
z3/lib/dl_mk_rule_inliner.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

885 lines
30 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_rule_inliner.cpp
Abstract:
Rule transformer which simplifies interpreted tails
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
Added linear_inline 2012-9-10 (nbjorner)
Notes:
Resolution transformation (resolve):
P(x) :- Q(y), phi(x,y) Q(y) :- R(z), psi(y,z)
--------------------------------------------------
P(x) :- R(z), phi(x,y), psi(y,z)
Proof converter:
replace assumption (*) by rule and upper assumptions.
Subsumption transformation (remove rule):
P(x) :- Q(y), phi(x,y) Rules
---------------------------------
Rules
Model converter:
P(x) := P(x) or (exists y . Q(y) & phi(x,y))
--*/
#include <sstream>
#include "ast_pp.h"
#include "dl_finite_product_relation.h"
#include "dl_product_relation.h"
#include "dl_sieve_relation.h"
#include "rewriter.h"
#include "rewriter_def.h"
#include "dl_mk_rule_inliner.h"
namespace datalog {
// -----------------------------------
//
// mk_rule_inliner::rule_unifier
//
// -----------------------------------
bool rule_unifier::unify_rules(const rule& tgt, unsigned tgt_idx, const rule& src) {
var_counter& vc = m_rm.get_var_counter();
unsigned var_cnt = std::max(vc.get_max_var(tgt), vc.get_max_var(src))+1;
m_subst.reset();
m_subst.reserve(2, var_cnt);
m_ready = m_unif(tgt.get_tail(tgt_idx), src.get_head(), m_subst);
if (m_ready) {
m_deltas[0] = 0;
m_deltas[1] = var_cnt;
TRACE("dl",
output_predicate(m_context, src.get_head(), tout << "unify rules ");
output_predicate(m_context, tgt.get_head(), tout << "\n");
tout << "\n";);
}
return m_ready;
}
void rule_unifier::apply(app * a, bool is_tgt, app_ref& res) {
expr_ref res_e(m);
TRACE("dl", output_predicate(m_context, a, tout); tout << "\n";);
m_subst.apply(2, m_deltas, expr_offset(a, is_tgt ? 0 : 1), res_e);
SASSERT(is_app(res_e.get()));
res = to_app(res_e.get());
}
void rule_unifier::apply(
rule& r, bool is_tgt, unsigned skipped_index,
app_ref_vector& res, svector<bool>& res_neg) {
unsigned rule_len = r.get_tail_size();
for (unsigned i = 0; i < rule_len; i++) {
if (i != skipped_index) { //i can never be UINT_MAX, so we'll never skip if we're not supposed to
app_ref new_tail_el(m);
apply(r.get_tail(i), is_tgt, new_tail_el);
res.push_back(new_tail_el);
res_neg.push_back(r.is_neg_tail(i));
}
}
}
bool rule_unifier::apply(rule& tgt, unsigned tail_index, rule& src, rule_ref& res) {
SASSERT(m_ready);
app_ref new_head(m);
app_ref_vector tail(m);
svector<bool> tail_neg;
rule_ref simpl_rule(m_rm);
apply(tgt.get_head(), true, new_head);
apply(tgt, true, tail_index, tail, tail_neg);
apply(src, false, UINT_MAX, tail, tail_neg);
mk_rule_inliner::remove_duplicate_tails(tail, tail_neg);
SASSERT(tail.size()==tail_neg.size());
res = m_rm.mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr());
res->set_accounting_parent_object(m_context, &tgt);
res->norm_vars(m_rm);
if (m_context.fix_unbound_vars()) {
m_rm.fix_unbound_vars(res, true);
}
if (m_interp_simplifier.transform_rule(res.get(), simpl_rule)) {
res = simpl_rule;
return true;
}
else {
return false;
}
}
expr_ref_vector rule_unifier::get_rule_subst(const rule& r, bool is_tgt) {
SASSERT(m_ready);
expr_ref_vector result(m);
sort_ref_vector sorts(m);
expr_ref v(m), w(m);
r.get_vars(sorts);
for (unsigned i = 0; i < sorts.size(); ++i) {
if (!sorts[i].get()) {
sorts[i] = m.mk_bool_sort();
}
v = m.mk_var(i, sorts[i].get());
m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w);
result.push_back(w);
}
return result;
}
// -----------------------------------
//
// mk_rule_inliner
//
// -----------------------------------
/**
Inline occurrences of rule src at tail_index in tgt and return the result in res.
*/
bool mk_rule_inliner::try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res)
{
SASSERT(tail_index<tgt.get_positive_tail_size());
SASSERT(!tgt.is_neg_tail(tail_index));
tgt.norm_vars(m_context.get_rule_manager());
if (has_quantifier(src)) {
return false;
}
if (!m_unifier.unify_rules(tgt, tail_index, src)) {
return false;
}
if (m_unifier.apply(tgt, tail_index, src, res)) {
TRACE("dl",
tgt.display(m_context, tout << "tgt (" << tail_index << "): \n");
src.display(m_context, tout << "src:\n");
res->display(m_context, tout << "res\n");
//m_unifier.display(tout << "subst:\n");
);
if (m_pc) {
expr_ref_vector s1 = m_unifier.get_rule_subst(tgt, true);
expr_ref_vector s2 = m_unifier.get_rule_subst(src, false);
datalog::resolve_rule(m_pc, tgt, src, tail_index, s1, s2, *res.get());
}
return true;
}
else {
TRACE("dl", res->display(m_context, tout << "interpreted tail is unsat\n"););
//the interpreted part is unsatisfiable
return false;
}
}
bool mk_rule_inliner::has_quantifier(rule const& r) const {
unsigned utsz = r.get_uninterpreted_tail_size();
for (unsigned i = utsz; i < r.get_tail_size(); ++i) {
if (r.get_tail(i)->has_quantifiers()) return true;
}
return false;
}
void mk_rule_inliner::count_pred_occurrences(rule_set const & orig)
{
m_context.get_rmanager().collect_non_empty_predicates(m_preds_with_facts);
rule_set::iterator rend = orig.end();
for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) {
rule * r = *rit;
func_decl * head_pred = r->get_decl();
m_head_pred_ctr.inc(head_pred);
if (r->get_tail_size()>0) {
m_head_pred_non_empty_tails_ctr.inc(head_pred);
}
unsigned ut_len = r->get_uninterpreted_tail_size();
for (unsigned i=0; i<ut_len; i++) {
func_decl * pred = r->get_decl(i);
m_tail_pred_ctr.inc(pred);
if (r->is_neg_tail(i)) {
m_preds_with_neg_occurrence.insert(pred);
}
}
}
}
bool mk_rule_inliner::inlining_allowed(func_decl * pred)
{
if (//these three conditions are important for soundness
m_context.is_output_predicate(pred) ||
m_preds_with_facts.contains(pred) ||
m_preds_with_neg_occurrence.contains(pred) ||
//this condition is used for breaking of cycles among inlined rules
m_forbidden_preds.contains(pred)) {
return false;
}
//these conditions are optional, they avoid possible exponential increase
//in the size of the problem
return
//m_head_pred_non_empty_tails_ctr.get(pred)<=1
m_head_pred_ctr.get(pred) <= 1
|| (m_tail_pred_ctr.get(pred) <= 1 && m_head_pred_ctr.get(pred) <= 4)
;
}
/** Caller has to dealloc the returned object */
rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig)
{
rule_set * res = alloc(rule_set, m_context);
unsigned rcnt = orig.get_num_rules();
for (unsigned i=0; i<rcnt; i++) {
rule * r = orig.get_rule(i);
if (inlining_allowed(r->get_decl())) {
res->add_rule(r);
}
}
//the rule set should be stratified, since orig (which is its superset) is as well
VERIFY(res->close());
return res;
}
/**
Try to make the set of inlined predicates acyclic by forbidding inlining of one
predicate from each strongly connected component. Return true if we did forbide some
predicate, and false if the set of rules is already acyclic.
*/
bool mk_rule_inliner::forbid_preds_from_cycles(rule_set const & r)
{
SASSERT(r.is_closed());
bool something_forbidden = false;
const rule_stratifier::comp_vector& comps = r.get_stratifier().get_strats();
rule_stratifier::comp_vector::const_iterator cend = comps.end();
for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) {
rule_stratifier::item_set * stratum = *it;
if (stratum->size()==1) {
continue;
}
SASSERT(stratum->size()>1);
func_decl * first_stratum_pred = *stratum->begin();
//we're trying to break cycles by removing one predicate from each of them
m_forbidden_preds.insert(first_stratum_pred);
something_forbidden = true;
}
return something_forbidden;
}
bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig,
rule_set const & proposed_inlined_rules) {
bool something_forbidden = false;
const rule_stratifier::comp_vector& comps =
proposed_inlined_rules.get_stratifier().get_strats();
rule_stratifier::comp_vector::const_iterator cend = comps.end();
for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) {
rule_stratifier::item_set * stratum = *it;
SASSERT(stratum->size()==1);
func_decl * head_pred = *stratum->begin();
bool is_multi_head_pred = m_head_pred_ctr.get(head_pred)>1;
bool is_multi_occurrence_pred = m_tail_pred_ctr.get(head_pred)>1;
const rule_vector& pred_rules = proposed_inlined_rules.get_predicate_rules(head_pred);
rule_vector::const_iterator iend = pred_rules.end();
for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) {
rule * r = *iit;
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti = 0; ti<pt_len; ++ti) {
func_decl * tail_pred = r->get_decl(ti);
if (!inlining_allowed(tail_pred)) {
continue;
}
unsigned tail_pred_head_cnt = m_head_pred_ctr.get(tail_pred);
if (tail_pred_head_cnt<=1) {
continue;
}
if (is_multi_head_pred) {
m_forbidden_preds.insert(head_pred);
something_forbidden = true;
goto process_next_pred;
}
if (is_multi_occurrence_pred) {
m_forbidden_preds.insert(tail_pred);
something_forbidden = true;
}
else {
is_multi_head_pred = true;
m_head_pred_ctr.get(head_pred) =
m_head_pred_ctr.get(head_pred)*tail_pred_head_cnt;
}
}
}
process_next_pred:;
}
unsigned rule_cnt = orig.get_num_rules();
for (unsigned ri=0; ri<rule_cnt; ri++) {
rule * r = orig.get_rule(ri);
func_decl * head_pred = r->get_decl();
if (inlining_allowed(head_pred)) {
//we have already processed inlined rules
continue;
}
bool has_multi_head_pred = false;
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti = 0; ti<pt_len; ++ti) {
func_decl * pred = r->get_decl(ti);
if (!inlining_allowed(pred)) {
continue;
}
if (m_head_pred_ctr.get(pred)<=1) {
continue;
}
if (has_multi_head_pred) {
m_forbidden_preds.insert(pred);
something_forbidden = true;
}
else {
has_multi_head_pred = true;
}
}
}
return something_forbidden;
}
void mk_rule_inliner::plan_inlining(rule_set const & orig)
{
count_pred_occurrences(orig);
scoped_ptr<rule_set> candidate_inlined_set = create_allowed_rule_set(orig);
while (forbid_preds_from_cycles(*candidate_inlined_set)) {
candidate_inlined_set = create_allowed_rule_set(orig);
}
if (forbid_multiple_multipliers(orig, *candidate_inlined_set)) {
candidate_inlined_set = create_allowed_rule_set(orig);
}
TRACE("dl", tout<<"rules to be inlined:\n" << (*candidate_inlined_set); );
// now we start filling in the set of the inlined rules in a topological order,
// so that we inline rules into other rules
SASSERT(m_inlined_rules.get_num_rules()==0);
const rule_stratifier::comp_vector& comps = candidate_inlined_set->get_stratifier().get_strats();
rule_stratifier::comp_vector::const_iterator cend = comps.end();
for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) {
rule_stratifier::item_set * stratum = *it;
SASSERT(stratum->size()==1);
func_decl * pred = *stratum->begin();
const rule_vector& pred_rules = candidate_inlined_set->get_predicate_rules(pred);
rule_vector::const_iterator iend = pred_rules.end();
for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) {
transform_rule(*iit, m_inlined_rules);
}
}
TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; );
}
bool mk_rule_inliner::transform_rule(rule * r0, rule_set& tgt) {
bool modified = false;
rule_ref_vector todo(m_rm);
todo.push_back(r0);
while (!todo.empty()) {
rule_ref r(todo.back(), m_rm);
todo.pop_back();
unsigned pt_len = r->get_positive_tail_size();
unsigned i = 0;
for (; i < pt_len && !inlining_allowed(r->get_decl(i)); ++i) {};
if (has_quantifier(*r.get())) {
continue;
}
if (i == pt_len) {
//there's nothing we can inline in this rule
tgt.add_rule(r);
continue;
}
modified = true;
func_decl * pred = r->get_decl(i);
const rule_vector& pred_rules = m_inlined_rules.get_predicate_rules(pred);
rule_vector::const_iterator iend = pred_rules.end();
for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) {
rule * inl_rule = *iit;
rule_ref inl_result(m_rm);
if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) {
todo.push_back(inl_result);
}
}
}
return modified;
}
bool mk_rule_inliner::transform_rules(const rule_set & orig, rule_set & tgt) {
bool something_done = false;
rule_set::iterator rend = orig.end();
for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) {
rule_ref r(*rit, m_rm);
func_decl * pred = r->get_decl();
// if inlining is allowed, then we are eliminating
// this relation through inlining,
// so we don't add its rules to the result
something_done |= !inlining_allowed(pred) && transform_rule(r, tgt);
}
return something_done;
}
/**
Check whether rule r is oriented in a particular ordering.
This is to avoid infinite cycle of inlining in the eager inliner.
Out ordering is lexicographic, comparing atoms first on stratum they are in,
then on arity and then on ast ID of their func_decl.
*/
bool mk_rule_inliner::is_oriented_rewriter(rule * r, rule_stratifier const& strat) {
func_decl * head_pred = r->get_decl();
unsigned head_strat = strat.get_predicate_strat(head_pred);
unsigned head_arity = head_pred->get_arity();
//var_idx_set head_vars;
//var_idx_set same_strat_vars;
//collect_vars(m, r->get_head(), head_vars);
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti=0; ti<pt_len; ++ti) {
func_decl * pred = r->get_decl(ti);
unsigned pred_strat = strat.get_predicate_strat(pred);
SASSERT(pred_strat<=head_strat);
if (pred_strat==head_strat) {
//collect_vars(m, r->get_head(), same_strat_vars);
if (pred->get_arity()>head_arity
|| (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) {
return false;
}
}
}
return true;
}
bool mk_rule_inliner::do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res) {
SASSERT(rules.is_closed());
const rule_stratifier& strat = rules.get_stratifier();
func_decl * head_pred = r->get_decl();
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti = 0; ti < pt_len; ++ti) {
func_decl * pred = r->get_decl(ti);
if (pred == head_pred || m_preds_with_facts.contains(pred)) { continue; }
const rule_vector& pred_rules = rules.get_predicate_rules(pred);
rule * inlining_candidate = 0;
unsigned rule_cnt = pred_rules.size();
if (rule_cnt == 0) {
inlining_candidate = 0;
}
else if (rule_cnt == 1) {
inlining_candidate = pred_rules[0];
}
else {
inlining_candidate = 0;
for (unsigned ri = 0; ri < rule_cnt; ++ri) {
rule * pred_rule = pred_rules[ri];
if (!m_unifier.unify_rules(*r, ti, *pred_rule)) {
//we skip rules which don't unify with the tail atom
continue;
}
if (inlining_candidate != 0) {
// We have two rules that can be inlined into the current
// tail predicate. In this situation we don't do inlinning
// on this tail atom, as we don't want the overall number
// of rules to increase.
goto process_next_tail;
}
inlining_candidate = pred_rule;
}
}
if (inlining_candidate == 0) {
// nothing unifies with the tail atom, therefore the rule is unsatisfiable
// (we can say this because relation pred doesn't have any ground facts either)
res = 0;
datalog::del_rule(m_mc, *r);
return true;
}
if (!is_oriented_rewriter(inlining_candidate, strat)) {
// The rule which should be used for inlining isn't oriented
// in a simplifying direction. Inlining with such rule might lead to
// infinite loops, so we don't do it.
goto process_next_tail;
}
if (!try_to_inline_rule(*r, *inlining_candidate, ti, res)) {
datalog::del_rule(m_mc, *r);
res = 0;
}
return true;
process_next_tail:;
}
return false;
}
bool mk_rule_inliner::do_eager_inlining(scoped_ptr<rule_set> & rules) {
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
bool done_something = false;
rule_set::iterator rend = rules->end();
for (rule_set::iterator rit = rules->begin(); rit!=rend; ++rit) {
rule_ref r(*rit, m_rm);
rule_ref replacement(m_rm);
while (r && do_eager_inlining(r, *rules, replacement)) {
r = replacement;
done_something = true;
}
if (!r) {
continue;
}
res->add_rule(r);
}
if (done_something) {
rules = res.detach();
}
return done_something;
}
/**
Inline predicates that are known to not be join-points.
P(1,x) :- P(0,y), phi(x,y)
P(0,x) :- P(1,z), psi(x,z)
->
P(1,x) :- P(1,z), phi(x,y), psi(y,z)
whenever P(0,x) is not unifiable with the
body of the rule where it appears (P(1,z))
and P(0,x) is unifiable with at most one (?)
other rule (and it does not occur negatively).
*/
bool mk_rule_inliner::visitor::operator()(expr* e) {
m_unifiers.append(m_positions.find(e));
TRACE("dl",
tout << "unifier: " << (m_unifiers.empty()?0:m_unifiers.back());
tout << " num unifiers: " << m_unifiers.size();
tout << " num positions: " << m_positions.find(e).size() << "\n";
output_predicate(m_context, to_app(e), tout); tout << "\n";);
return true;
}
void mk_rule_inliner::visitor::reset(unsigned sz) {
m_unifiers.reset();
m_can_remove.reset();
m_can_remove.resize(sz, true);
m_can_expand.reset();
m_can_expand.resize(sz, true);
m_positions.reset();
}
unsigned_vector const& mk_rule_inliner::visitor::add_position(expr* e, unsigned j) {
obj_map<expr, unsigned_vector>::obj_map_entry * et = m_positions.insert_if_not_there2(e, unsigned_vector());
et->get_data().m_value.push_back(j);
return et->get_data().m_value;
}
unsigned_vector const& mk_rule_inliner::visitor::del_position(expr* e, unsigned j) {
obj_map<expr, unsigned_vector>::obj_map_entry * et = m_positions.find_core(e);
SASSERT(et && et->get_data().m_value.contains(j));
et->get_data().m_value.erase(j);
return et->get_data().m_value;
}
void mk_rule_inliner::add_rule(rule* r, unsigned i) {
svector<bool>& can_remove = m_head_visitor.can_remove();
svector<bool>& can_expand = m_head_visitor.can_expand();
app* head = r->get_head();
func_decl* headd = head->get_decl();
m_head_visitor.add_position(head, i);
m_head_index.insert(head);
m_pinned.push_back(r);
if (m_context.is_output_predicate(headd) ||
m_preds_with_facts.contains(headd)) {
can_remove.set(i, false);
TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";);
}
unsigned tl_sz = r->get_uninterpreted_tail_size();
for (unsigned j = 0; j < tl_sz; ++j) {
app* tail = r->get_tail(j);
m_tail_visitor.add_position(tail, i);
m_tail_index.insert(tail);
}
bool can_exp =
tl_sz == 1
&& r->get_positive_tail_size() == 1
&& !m_preds_with_facts.contains(r->get_decl(0))
&& !m_context.is_output_predicate(r->get_decl(0));
can_expand.set(i, can_exp);
}
void mk_rule_inliner::del_rule(rule* r, unsigned i) {
app* head = r->get_head();
m_head_visitor.del_position(head, i);
unsigned tl_sz = r->get_uninterpreted_tail_size();
for (unsigned j = 0; j < tl_sz; ++j) {
app* tail = r->get_tail(j);
m_tail_visitor.del_position(tail, i);
}
}
#define PRT(_x_) ((_x_)?"T":"F")
bool mk_rule_inliner::inline_linear(scoped_ptr<rule_set>& rules) {
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
bool done_something = false;
unsigned sz = rules->get_num_rules();
m_head_visitor.reset(sz);
m_tail_visitor.reset(sz);
m_head_index.reset();
m_tail_index.reset();
TRACE("dl", rules->display(tout););
rule_ref_vector acc(m_rm);
for (unsigned i = 0; i < sz; ++i) {
acc.push_back(rules->get_rule(i));
}
// set up unification index.
svector<bool>& can_remove = m_head_visitor.can_remove();
svector<bool>& can_expand = m_head_visitor.can_expand();
for (unsigned i = 0; i < sz; ++i) {
add_rule(acc[i].get(), i);
}
// initialize substitution.
var_counter& vc = m_rm.get_var_counter();
unsigned max_var = 0;
for (unsigned i = 0; i < sz; ++i) {
rule* r = acc[i].get();
max_var = std::max(max_var, vc.get_max_var(r->get_head()));
unsigned tl_sz = r->get_uninterpreted_tail_size();
for (unsigned j = 0; j < tl_sz; ++j) {
max_var = std::max(max_var, vc.get_max_var(r->get_tail(j)));
}
}
m_subst.reset();
m_subst.reserve_vars(max_var+1);
m_subst.reserve_offsets(std::max(m_tail_index.get_approx_num_regs(), m_head_index.get_approx_num_regs()));
svector<bool> valid;
valid.reset();
valid.resize(sz, true);
params_ref const& params = m_context.get_params();
bool allow_branching = params.get_bool(":inline-linear-branch", false);
for (unsigned i = 0; i < sz; ++i) {
while (true) {
rule_ref r(acc[i].get(), m_rm);
TRACE("dl", r->display(m_context, tout << "processing: " << i << "\n"););
if (!valid.get(i)) {
TRACE("dl", tout << "invalid: " << i << "\n";);
break;
}
if (!can_expand.get(i)) {
TRACE("dl", tout << "cannot expand: " << i << "\n";);
break;
}
m_head_visitor.reset();
m_head_index.unify(r->get_tail(0), m_head_visitor);
unsigned num_head_unifiers = m_head_visitor.get_unifiers().size();
if (num_head_unifiers != 1) {
TRACE("dl", tout << "no unique unifier " << num_head_unifiers << "\n";);
break;
}
unsigned j = m_head_visitor.get_unifiers()[0];
if (!can_remove.get(j) || !valid.get(j) || i == j) {
TRACE("dl", tout << PRT(can_remove.get(j)) << " " << PRT(valid.get(j)) << " " << PRT(i != j) << "\n";);
break;
}
rule* r2 = acc[j].get();
// check that the head of r2 only unifies with this single body position.
TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";);
m_tail_visitor.reset();
m_tail_index.unify(r2->get_head(), m_tail_visitor);
unsigned_vector const& tail_unifiers = m_tail_visitor.get_unifiers();
unsigned num_tail_unifiers = tail_unifiers.size();
SASSERT(!tail_unifiers.empty());
if (!allow_branching && num_tail_unifiers != 1) {
TRACE("dl", tout << "too many tails " << num_tail_unifiers << "\n";);
break;
}
rule_ref rl_res(m_rm);
if (!try_to_inline_rule(*r.get(), *r2, 0, rl_res)) {
TRACE("dl", r->display(m_context, tout << "inlining failed\n"); r2->display(m_context, tout); );
break;
}
done_something = true;
TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); );
del_rule(r, i);
add_rule(rl_res.get(), i);
r = rl_res;
acc[i] = r.get();
can_expand.set(i, can_expand.get(j));
if (num_tail_unifiers == 1) {
TRACE("dl", tout << "setting invalid: " << j << "\n";);
valid.set(j, false);
datalog::del_rule(m_mc, *r2);
del_rule(r2, j);
}
max_var = std::max(max_var, vc.get_max_var(*r.get()));
m_subst.reserve_vars(max_var+1);
}
}
if (done_something) {
rules = alloc(rule_set, m_context);
for (unsigned i = 0; i < sz; ++i) {
if (valid.get(i)) {
rules->add_rule(acc[i].get());
}
}
TRACE("dl", rules->display(tout););
}
return done_something;
}
rule_set * mk_rule_inliner::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
bool something_done = false;
ref<horn_subsume_model_converter> hsmc;
ref<replace_proof_converter> hpc;
if (source.get_num_rules() == 0) {
return 0;
}
if (mc) {
hsmc = alloc(horn_subsume_model_converter, m);
}
if (pc) {
hpc = alloc(replace_proof_converter, m);
}
m_mc = hsmc.get();
m_pc = hpc.get();
plan_inlining(source);
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
something_done = transform_rules(source, *res);
VERIFY(res->close()); //this transformation doesn't break the negation stratification
// try eager inlining
if (do_eager_inlining(res)) {
something_done = true;
}
params_ref const& params = m_context.get_params();
if (params.get_bool(":inline-linear", true) && inline_linear(res)) {
something_done = true;
}
if (!something_done) {
res = 0;
}
else {
if (mc) {
mc = concat(mc.get(), hsmc.get());
}
if (pc) {
pc = concat(pc.get(), hpc.get());
}
}
return res.detach();
}
};