1 /*++
2 Copyright (c) 2006 Microsoft Corporation
3 
4 Module Name:
5 
6     dl_mk_explanations.cpp
7 
8 Abstract:
9 
10     <abstract>
11 
12 Author:
13 
14     Krystof Hoder (t-khoder) 2010-11-08.
15 
16 Revision History:
17 
18 --*/
19 
20 
21 #include <sstream>
22 #include "ast/ast_pp.h"
23 #include "ast/ast_smt_pp.h"
24 #include "muz/rel/dl_finite_product_relation.h"
25 #include "muz/rel/dl_product_relation.h"
26 #include "muz/rel/dl_sieve_relation.h"
27 #include "muz/rel/dl_mk_explanations.h"
28 
29 namespace datalog {
30 
31     // -----------------------------------
32     //
33     // explanation_relation_plugin declaration
34     //
35     // -----------------------------------
36 
37     class explanation_relation;
38 
39     class explanation_relation_plugin : public relation_plugin {
40         friend class explanation_relation;
41 
42         class join_fn;
43         class project_fn;
44         class rename_fn;
45         class union_fn;
46         class foreign_union_fn;
47         class assignment_filter_fn;
48         class negation_filter_fn;
49         class intersection_filter_fn;
50 
51         bool m_relation_level_explanations;
52 
53         func_decl_ref m_union_decl;
54 
55         vector<ptr_vector<explanation_relation> > m_pool;
56 
57 
mk_union(app * a1,app * a2)58         app * mk_union(app * a1, app * a2) {
59             return get_ast_manager().mk_app(m_union_decl, a1, a2);
60         }
61 
62     public:
get_name(bool relation_level)63         static symbol get_name(bool relation_level) {
64             return symbol(relation_level ? "relation_explanation" : "fact_explanation");
65         }
66 
explanation_relation_plugin(bool relation_level,relation_manager & manager)67         explanation_relation_plugin(bool relation_level, relation_manager & manager)
68             : relation_plugin(get_name(relation_level), manager),
69             m_relation_level_explanations(relation_level),
70             m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {}
71 
~explanation_relation_plugin()72         ~explanation_relation_plugin() override {
73             for (unsigned i = 0; i < m_pool.size(); ++i) {
74                 for (unsigned j = 0; j < m_pool[i].size(); ++j) {
75                     dealloc(m_pool[i][j]);
76                 }
77             }
78         }
79 
can_handle_signature(const relation_signature & s)80         bool can_handle_signature(const relation_signature & s) override {
81             unsigned n=s.size();
82             for (unsigned i=0; i<n; i++) {
83                 if (!get_context().get_decl_util().is_rule_sort(s[i])) {
84                     return false;
85                 }
86             }
87             return true;
88         }
89 
90         relation_base * mk_empty(const relation_signature & s) override;
91 
92         void recycle(explanation_relation* r);
93 
94     protected:
95 
96         relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
97             unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override;
98         relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
99             const unsigned * removed_cols) override;
100         relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
101             const unsigned * permutation_cycle) override;
102         relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
103             const relation_base * delta) override;
104         relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override;
105         relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
106             const relation_base & negated_obj, unsigned joined_col_cnt,
107             const unsigned * t_cols, const unsigned * negated_cols) override;
108         relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t,
109                 const relation_base & src, unsigned joined_col_cnt,
110                 const unsigned * t_cols, const unsigned * src_cols) override;
111 
112     };
113 
114 
115     // -----------------------------------
116     //
117     // explanation_relation
118     //
119     // -----------------------------------
120 
121     class explanation_relation : public relation_base {
122         friend class explanation_relation_plugin;
123         friend class explanation_relation_plugin::join_fn;
124         friend class explanation_relation_plugin::project_fn;
125         friend class explanation_relation_plugin::rename_fn;
126         friend class explanation_relation_plugin::union_fn;
127         friend class explanation_relation_plugin::foreign_union_fn;
128         friend class explanation_relation_plugin::assignment_filter_fn;
129         friend class explanation_relation_plugin::intersection_filter_fn;
130 
131         bool m_empty;
132         /**
133            Valid only if \c !m_empty.
134 
135            Zero elements mean undefined.
136         */
137         relation_fact m_data;
138 
explanation_relation(explanation_relation_plugin & p,const relation_signature & s)139         explanation_relation(explanation_relation_plugin & p, const relation_signature & s)
140                 : relation_base(p, s), m_empty(true), m_data(p.get_ast_manager()) {
141 
142             DEBUG_CODE(
143                 unsigned sz = s.size();
144                 for (unsigned i=0;i<sz; i++) {
145                     SASSERT( p.get_context().get_decl_util().is_rule_sort(s[i]) );
146                 }
147                 );
148         }
149 
assign_data(const relation_fact & f)150         void assign_data(const relation_fact & f) {
151             m_empty = false;
152 
153             unsigned n=get_signature().size();
154             SASSERT(f.size()==n);
155             m_data.reset();
156             m_data.append(n, f.c_ptr());
157         }
set_undefined()158         void set_undefined() {
159             m_empty = false;
160             m_data.reset();
161             m_data.resize(get_signature().size());
162         }
unite_with_data(const relation_fact & f)163         void unite_with_data(const relation_fact & f) {
164             if (empty()) {
165                 assign_data(f);
166                 return;
167             }
168             unsigned n=get_signature().size();
169             SASSERT(f.size()==n);
170             for (unsigned i=0; i<n; i++) {
171                 SASSERT(!is_undefined(i));
172                 m_data[i] = get_plugin().mk_union(m_data[i], f[i]);
173             }
174         }
175 
deallocate()176         void deallocate() override {
177             get_plugin().recycle(this);
178         }
179 
180     public:
181 
get_plugin() const182         explanation_relation_plugin & get_plugin() const {
183             return static_cast<explanation_relation_plugin &>(relation_base::get_plugin());
184         }
185 
to_formula(expr_ref & fml) const186         void to_formula(expr_ref& fml) const override {
187             ast_manager& m = fml.get_manager();
188             fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]);
189         }
190 
is_undefined(unsigned col_idx) const191         bool is_undefined(unsigned col_idx) const {
192             return m_data[col_idx]==nullptr;
193         }
no_undefined() const194         bool no_undefined() const {
195             if (empty()) {
196                 return true;
197             }
198             unsigned n = get_signature().size();
199             for (unsigned i=0; i<n; i++) {
200                 if (is_undefined(i)) {
201                     return false;
202                 }
203             }
204             return true;
205         }
206 
empty() const207         bool empty() const override { return m_empty; }
208 
reset()209         void reset() override {
210             m_empty = true;
211         }
212 
add_fact(const relation_fact & f)213         void add_fact(const relation_fact & f) override {
214             SASSERT(empty());
215             assign_data(f);
216         }
217 
contains_fact(const relation_fact & f) const218         bool contains_fact(const relation_fact & f) const override {
219             UNREACHABLE();
220             throw 0;
221         }
222 
clone() const223         explanation_relation * clone() const override {
224             explanation_relation * res = static_cast<explanation_relation *>(get_plugin().mk_empty(get_signature()));
225             res->m_empty = m_empty;
226             SASSERT(res->m_data.empty());
227             res->m_data.append(m_data);
228             return res;
229         }
230 
complement(func_decl * pred) const231         relation_base * complement(func_decl* pred) const override {
232             explanation_relation * res = static_cast<explanation_relation *>(get_plugin().mk_empty(get_signature()));
233             if (empty()) {
234                 res->set_undefined();
235             }
236             return res;
237         }
238 
display_explanation(app * expl,std::ostream & out) const239         void display_explanation(app * expl, std::ostream & out) const {
240             if (expl) {
241                 //TODO: some nice explanation output
242                 ast_smt_pp pp(get_plugin().get_ast_manager());
243                 pp.display_expr_smt2(out, expl);
244             }
245             else {
246                 out << "<undefined>";
247             }
248         }
249 
display(std::ostream & out) const250         void display(std::ostream & out) const override {
251             if (empty()) {
252                 out << "<empty explanation relation>\n";
253                 return;
254             }
255             unsigned sz = get_signature().size();
256             for (unsigned i=0; i<sz; i++) {
257                 if (i!=0) {
258                     out << ", ";
259                 }
260                 display_explanation(m_data[0], out);
261             }
262             out << "\n";
263         }
264 
get_size_estimate() const265         virtual unsigned get_size_estimate() const { return empty() ? 0 : 1; }
266     };
267 
268 
269     // -----------------------------------
270     //
271     // explanation_relation_plugin
272     //
273     // -----------------------------------
274 
275 
mk_empty(const relation_signature & s)276     relation_base * explanation_relation_plugin::mk_empty(const relation_signature & s) {
277         if (m_pool.size() > s.size() && !m_pool[s.size()].empty()) {
278             explanation_relation* r = m_pool[s.size()].back();
279             m_pool[s.size()].pop_back();
280             r->m_empty = true;
281             r->m_data.reset();
282             return r;
283         }
284         return alloc(explanation_relation, *this, s);
285     }
286 
recycle(explanation_relation * r)287     void explanation_relation_plugin::recycle(explanation_relation* r) {
288         relation_signature const& sig = r->get_signature();
289         if (m_pool.size() <= sig.size()) {
290             m_pool.resize(sig.size()+1);
291         }
292         m_pool[sig.size()].push_back(r);
293     }
294 
295 
296     class explanation_relation_plugin::join_fn : public convenient_relation_join_fn {
297     public:
join_fn(const relation_signature & sig1,const relation_signature & sig2)298         join_fn(const relation_signature & sig1, const relation_signature & sig2)
299             : convenient_relation_join_fn(sig1, sig2, 0, nullptr, nullptr) {}
300 
operator ()(const relation_base & r1_0,const relation_base & r2_0)301         relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) override {
302             const explanation_relation & r1 = static_cast<const explanation_relation &>(r1_0);
303             const explanation_relation & r2 = static_cast<const explanation_relation &>(r2_0);
304             explanation_relation_plugin & plugin = r1.get_plugin();
305 
306             explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
307             if (!r1.empty() && !r2.empty()) {
308                 res->m_empty = false;
309                 SASSERT(res->m_data.empty());
310                 res->m_data.append(r1.m_data);
311                 res->m_data.append(r2.m_data);
312             }
313             return res;
314         }
315     };
316 
mk_join_fn(const relation_base & r1,const relation_base & r2,unsigned col_cnt,const unsigned * cols1,const unsigned * cols2)317     relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
318             unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
319         if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) {
320             return nullptr;
321         }
322         if (col_cnt!=0) {
323             return nullptr;
324         }
325         return alloc(join_fn, r1.get_signature(), r2.get_signature());
326     }
327 
328 
329     class explanation_relation_plugin::project_fn : public convenient_relation_project_fn {
330     public:
project_fn(const relation_signature & sig,unsigned col_cnt,const unsigned * removed_cols)331         project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols)
332             : convenient_relation_project_fn(sig, col_cnt, removed_cols) {}
333 
operator ()(const relation_base & r0)334         relation_base * operator()(const relation_base & r0) override {
335             const explanation_relation & r = static_cast<const explanation_relation &>(r0);
336             explanation_relation_plugin & plugin = r.get_plugin();
337 
338             explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
339             if (!r.empty()) {
340                 relation_fact proj_data = r.m_data;
341                 project_out_vector_columns(proj_data, m_removed_cols);
342                 res->assign_data(proj_data);
343             }
344             return res;
345         }
346     };
347 
mk_project_fn(const relation_base & r,unsigned col_cnt,const unsigned * removed_cols)348     relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt,
349             const unsigned * removed_cols) {
350         if (&r.get_plugin()!=this) {
351             return nullptr;
352         }
353         return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
354     }
355 
356 
357     class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn {
358     public:
rename_fn(const relation_signature & sig,unsigned permutation_cycle_len,const unsigned * permutation_cycle)359         rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle)
360             : convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {}
361 
operator ()(const relation_base & r0)362         relation_base * operator()(const relation_base & r0) override {
363             const explanation_relation & r = static_cast<const explanation_relation &>(r0);
364             explanation_relation_plugin & plugin = r.get_plugin();
365 
366             explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
367             if (!r.empty()) {
368                 relation_fact permutated_data = r.m_data;
369                 permutate_by_cycle(permutated_data, m_cycle);
370                 res->assign_data(permutated_data);
371             }
372             return res;
373         }
374     };
375 
mk_rename_fn(const relation_base & r,unsigned permutation_cycle_len,const unsigned * permutation_cycle)376     relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r,
377             unsigned permutation_cycle_len, const unsigned * permutation_cycle) {
378         return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle);
379     }
380 
381 
382     class explanation_relation_plugin::union_fn : public relation_union_fn {
383         scoped_ptr<relation_union_fn> m_delta_union_fun;
384     public:
operator ()(relation_base & tgt0,const relation_base & src0,relation_base * delta0)385         void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) override {
386             explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
387             const explanation_relation & src = static_cast<const explanation_relation &>(src0);
388             explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(delta0) : nullptr;
389             explanation_relation_plugin & plugin = tgt.get_plugin();
390 
391             if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) {
392                 throw default_exception("explanations are not supported with undefined predicates");
393             }
394             if (src.empty()) {
395                 return;
396             }
397             if (plugin.m_relation_level_explanations) {
398                 tgt.unite_with_data(src.m_data);
399                 if (delta) {
400                     if (!m_delta_union_fun) {
401                         m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src);
402                         SASSERT(m_delta_union_fun);
403                     }
404                     (*m_delta_union_fun)(*delta, src);
405                 }
406             }
407             else {
408                 if (tgt.empty()) {
409                     tgt.assign_data(src.m_data);
410                     if (delta && delta->empty()) {
411                         delta->assign_data(src.m_data);
412                     }
413                 }
414             }
415         }
416     };
417 
418     class explanation_relation_plugin::foreign_union_fn : public relation_union_fn {
419         scoped_ptr<relation_union_fn> m_delta_union_fun;
420     public:
operator ()(relation_base & tgt0,const relation_base & src,relation_base * delta0)421         void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) override {
422             explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
423             explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(delta0) : nullptr;
424 
425             if (src.empty()) {
426                 return;
427             }
428             tgt.set_undefined();
429             if (delta) {
430                 delta->set_undefined();
431             }
432         }
433     };
434 
mk_union_fn(const relation_base & tgt,const relation_base & src,const relation_base * delta)435     relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
436             const relation_base * delta) {
437         if (!check_kind(tgt) || (delta && !check_kind(*delta))) {
438             return nullptr;
439         }
440         if (!check_kind(src)) {
441             //this is to handle the product relation
442             return alloc(foreign_union_fn);
443         }
444         return alloc(union_fn);
445     }
446 
447     class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn {
448         ast_manager & m_manager;
449         var_subst & m_subst;
450         unsigned m_col_idx;
451         app_ref m_new_rule;
452     public:
assignment_filter_fn(context & ctx,unsigned col_idx,app_ref new_rule)453         assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule)
454             : m_manager(ctx.get_manager()),
455               m_subst(ctx.get_var_subst()),
456               m_col_idx(col_idx),
457               m_new_rule(std::move(new_rule)) {}
458 
not_handled()459         void not_handled() {
460             throw default_exception("explanations are not supported with undefined predicates");
461         }
462 
operator ()(relation_base & r0)463         void operator()(relation_base & r0) override {
464             explanation_relation & r = static_cast<explanation_relation &>(r0);
465 
466             if (!r.is_undefined(m_col_idx)) {
467                 not_handled();
468             }
469 
470             unsigned sz = r.get_signature().size();
471             ptr_vector<expr> subst_arg;
472             subst_arg.resize(sz);
473             unsigned ofs = sz-1;
474             for (unsigned i=0; i<sz; i++) {
475                 if (r.is_undefined(i) && contains_var(m_new_rule, i))
476                     not_handled();
477                 subst_arg[ofs-i] = r.m_data.get(i);
478             }
479             expr_ref res = m_subst(m_new_rule, subst_arg.size(), subst_arg.c_ptr());
480             r.m_data[m_col_idx] = to_app(res);
481         }
482     };
483 
mk_filter_interpreted_fn(const relation_base & r,app * cond)484     relation_mutator_fn * explanation_relation_plugin::mk_filter_interpreted_fn(const relation_base & r,
485             app * cond) {
486         if (&r.get_plugin() != this) {
487             TRACE("dl", tout << "not same plugin\n";);
488             return nullptr;
489         }
490         ast_manager & m = get_ast_manager();
491         if (!m.is_eq(cond)) {
492             TRACE("dl", tout << "not equality " << mk_pp(cond, m) << "\n";);
493             return nullptr;
494         }
495         expr * arg1 = cond->get_arg(0);
496         expr * arg2 = cond->get_arg(1);
497 
498         if (is_var(arg2)) {
499             std::swap(arg1, arg2);
500         }
501 
502         if (!is_var(arg1) || !is_app(arg2)) {
503             TRACE("dl", tout << "not variable assignemnt\n";);
504             return nullptr;
505         }
506         var * col_var = to_var(arg1);
507         app * new_rule = to_app(arg2);
508         if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) {
509             TRACE("dl", tout << "not rule sort\n";);
510             return nullptr;
511         }
512         unsigned col_idx = col_var->get_idx();
513 
514         return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager()));
515     }
516 
517 
518     class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn {
519     public:
operator ()(relation_base & r,const relation_base & neg)520         void operator()(relation_base & r, const relation_base & neg) override {
521             if (!neg.empty()) {
522                 r.reset();
523             }
524         }
525     };
526 
mk_filter_by_negation_fn(const relation_base & r,const relation_base & neg,unsigned joined_col_cnt,const unsigned * t_cols,const unsigned * negated_cols)527     relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r,
528             const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols,
529             const unsigned * negated_cols) {
530         if (&r.get_plugin()!=this || &neg.get_plugin()!=this) {
531             return nullptr;
532         }
533         return alloc(negation_filter_fn);
534     }
535 
536     class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn {
537         func_decl_ref m_union_decl;
538     public:
intersection_filter_fn(explanation_relation_plugin & plugin)539         intersection_filter_fn(explanation_relation_plugin & plugin)
540             : m_union_decl(plugin.m_union_decl) {}
541 
operator ()(relation_base & tgt0,const relation_base & src0)542         void operator()(relation_base & tgt0, const relation_base & src0) override {
543             explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
544             const explanation_relation & src = static_cast<const explanation_relation &>(src0);
545 
546             if (src.empty()) {
547                 tgt.reset();
548                 return;
549             }
550             if (tgt.empty()) {
551                 return;
552             }
553             unsigned sz = tgt.get_signature().size();
554             for (unsigned i=0; i<sz; i++) {
555                 if (src.is_undefined(i)) {
556                     continue;
557                 }
558                 app * curr_src = src.m_data.get(i);
559                 if (tgt.is_undefined(i)) {
560                     tgt.m_data.set(i, curr_src);
561                     continue;
562                 }
563                 app * curr_tgt = tgt.m_data.get(i);
564                 if (curr_tgt->get_decl()==m_union_decl.get()) {
565                     if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) {
566                         tgt.m_data.set(i, curr_src);
567                         continue;
568                     }
569                 }
570                 //the intersection is imprecise because we do nothing here, but it is good enough for
571                 //the purpose of explanations
572             }
573         }
574     };
575 
mk_filter_by_intersection_fn(const relation_base & tgt,const relation_base & src,unsigned joined_col_cnt,const unsigned * tgt_cols,const unsigned * src_cols)576     relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn(
577             const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt,
578             const unsigned * tgt_cols, const unsigned * src_cols) {
579         if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) {
580             return nullptr;
581         }
582         //this checks the join is one to one on all columns
583         if (tgt.get_signature()!=src.get_signature()
584             || joined_col_cnt!=tgt.get_signature().size()
585             || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) {
586             return nullptr;
587         }
588         counter ctr;
589         ctr.count(joined_col_cnt, tgt_cols);
590         if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) {
591             return nullptr;
592         }
593         return alloc(intersection_filter_fn, *this);
594     }
595 
596 
597     // -----------------------------------
598     //
599     // mk_explanations
600     //
601     // -----------------------------------
602 
603 
mk_explanations(context & ctx)604     mk_explanations::mk_explanations(context & ctx)
605         : plugin(50000),
606           m_manager(ctx.get_manager()),
607           m_context(ctx),
608           m_decl_util(ctx.get_decl_util()),
609           m_relation_level(ctx.explanations_on_relation_level()),
610           m_pinned(m_manager) {
611         m_e_sort = m_decl_util.mk_rule_sort();
612         m_pinned.push_back(m_e_sort);
613 
614         relation_manager & rmgr = ctx.get_rel_context()->get_rmanager();
615         symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level);
616         m_er_plugin = static_cast<explanation_relation_plugin *>(rmgr.get_relation_plugin(er_symbol));
617         if (!m_er_plugin) {
618             m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr);
619             rmgr.register_plugin(m_er_plugin);
620             if (!m_relation_level) {
621                 DEBUG_CODE(
622                     finite_product_relation_plugin * dummy;
623                     SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy));
624                     );
625                 rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr));
626             }
627         }
628         DEBUG_CODE(
629             if (!m_relation_level) {
630                 finite_product_relation_plugin * dummy;
631                 SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy));
632             }
633         );
634     }
635 
~mk_explanations()636     mk_explanations::~mk_explanations() {
637     }
638 
get_union_decl(context & ctx)639     func_decl * mk_explanations::get_union_decl(context & ctx) {
640         ast_manager & m = ctx.get_manager();
641         sort_ref s(ctx.get_decl_util().mk_rule_sort(), m);
642         //can it happen that the function name would collide with some other symbol?
643         //if functions can be overloaded by their ranges, it should be fine.
644         return m.mk_func_decl(symbol("e_union"), s, s, s);
645     }
646 
assign_rel_level_kind(func_decl * e_decl,func_decl * orig)647     void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) {
648         SASSERT(m_relation_level);
649 
650         relation_manager & rmgr = m_context.get_rel_context()->get_rmanager();
651         unsigned sz = e_decl->get_arity();
652         relation_signature sig;
653         rmgr.from_predicate(e_decl, sig);
654 
655         bool_vector inner_sieve(sz-1, true);
656         inner_sieve.push_back(false);
657 
658         bool_vector expl_sieve(sz-1, false);
659         expl_sieve.push_back(true);
660 
661         sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr);
662 
663         family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id
664         family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind);
665         family_id expl_kind = m_er_plugin->get_kind();
666         family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind);
667 
668         rel_spec product_spec;
669         product_spec.push_back(inner_sieve_kind);
670         product_spec.push_back(expl_sieve_kind);
671 
672         family_id pred_kind =
673             product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec);
674 
675         rmgr.set_predicate_kind(e_decl, pred_kind);
676     }
677 
get_e_decl(func_decl * orig_decl)678     func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) {
679         auto& value = m_e_decl_map.insert_if_not_there(orig_decl, 0);
680         if (value == nullptr) {
681             relation_signature e_domain;
682             e_domain.append(orig_decl->get_arity(), orig_decl->get_domain());
683             e_domain.push_back(m_e_sort);
684             func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"),
685                 e_domain.size(), e_domain.c_ptr(), orig_decl);
686             m_pinned.push_back(new_decl);
687             value = new_decl;
688 
689             if (m_relation_level) {
690                 assign_rel_level_kind(new_decl, orig_decl);
691             }
692         }
693         return value;
694     }
695 
get_e_lit(app * lit,unsigned e_var_idx)696     app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) {
697         expr_ref_vector args(m_manager);
698         func_decl * e_decl = get_e_decl(lit->get_decl());
699         args.append(lit->get_num_args(), lit->get_args());
700         args.push_back(m_manager.mk_var(e_var_idx, m_e_sort));
701         return m_manager.mk_app(e_decl, args.c_ptr());
702     }
703 
get_rule_symbol(rule * r)704     symbol mk_explanations::get_rule_symbol(rule * r) {
705         if (r->name() == symbol::null) {
706             std::stringstream sstm;
707             r->display(m_context, sstm);
708             std::string res = sstm.str();
709             res = res.substr(0, res.find_last_not_of('\n')+1);
710             return symbol(res.c_str());
711         }
712         else {
713             return r->name();
714         }
715     }
716 
get_e_rule(rule * r)717     rule * mk_explanations::get_e_rule(rule * r) {
718         rule_counter ctr;
719         ctr.count_rule_vars(r);
720         unsigned max_var;
721         unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0;
722         unsigned head_var = next_var++;
723         app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager);
724 
725         app_ref_vector e_tail(m_manager);
726         bool_vector neg_flags;
727         unsigned pos_tail_sz = r->get_positive_tail_size();
728         for (unsigned i=0; i<pos_tail_sz; i++) {
729             unsigned e_var = next_var++;
730             e_tail.push_back(get_e_lit(r->get_tail(i), e_var));
731             neg_flags.push_back(false);
732         }
733         unsigned tail_sz = r->get_tail_size();
734         for (unsigned i=pos_tail_sz; i<tail_sz; i++) {
735             e_tail.push_back(r->get_tail(i));
736             neg_flags.push_back(r->is_neg_tail(i));
737         }
738 
739         symbol rule_repr = get_rule_symbol(r);
740 
741         expr_ref_vector rule_expr_args(m_manager);
742         for (unsigned tail_idx=0; tail_idx<pos_tail_sz; tail_idx++) {
743             app * tail = e_tail.get(tail_idx);
744             if (true || m_relation_level) {
745                 //this adds the explanation term of the tail
746                 rule_expr_args.push_back(tail->get_arg(tail->get_num_args()-1));
747             }
748             else {
749                 //this adds argument values and the explanation term
750                 //(values will be substituted for variables at runtime by the finite_product_relation)
751                 rule_expr_args.append(tail->get_num_args(), tail->get_args());
752             }
753         }
754         //rule_expr contains rule function with string representation of the rule as symbol and
755         //for each positive uninterpreted tail it contains its argument values and its explanation term
756         expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr());
757 
758         app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager);
759         e_tail.push_back(e_record);
760         neg_flags.push_back(false);
761         SASSERT(e_tail.size()==neg_flags.size());
762 
763         return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr());
764     }
765 
transform_rules(const rule_set & src,rule_set & dst)766     void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) {
767         rule_set::iterator rit = src.begin();
768         rule_set::iterator rend = src.end();
769         for (; rit!=rend; ++rit) {
770             rule * e_rule = get_e_rule(*rit);
771             dst.add_rule(e_rule);
772         }
773 
774         //add rules that will (for output predicates) copy facts from explained relations back to
775         //the original ones
776         expr_ref_vector lit_args(m_manager);
777         decl_set::iterator pit = src.get_output_predicates().begin();
778         decl_set::iterator pend = src.get_output_predicates().end();
779         for (; pit != pend; ++pit) {
780             func_decl * orig_decl = *pit;
781 
782             lit_args.reset();
783             unsigned arity = orig_decl->get_arity();
784             for (unsigned i=0; i<arity; i++) {
785                 lit_args.push_back(m_manager.mk_var(i, orig_decl->get_domain(i)));
786             }
787             app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager);
788             app_ref e_lit(get_e_lit(orig_lit, arity), m_manager);
789             app * tail[] = { e_lit.get() };
790             dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, nullptr));
791         }
792     }
793 
translate_rel_level_relation(relation_manager & rmgr,relation_base & orig,relation_base & e_rel)794     void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig,
795             relation_base & e_rel) {
796         SASSERT(m_e_fact_relation);
797         SASSERT(e_rel.get_plugin().is_product_relation());
798 
799         product_relation & prod_rel = static_cast<product_relation &>(e_rel);
800         SASSERT(prod_rel.size()==2);
801 
802         if (!prod_rel[0].get_plugin().is_sieve_relation())
803             throw default_exception("explanations are not supported with undefined predicates");
804         if (!prod_rel[1].get_plugin().is_sieve_relation())
805             throw default_exception("explanations are not supported with undefined predicates");
806         sieve_relation * srels[] = {
807             static_cast<sieve_relation *>(&prod_rel[0]),
808             static_cast<sieve_relation *>(&prod_rel[1]) };
809         if (&srels[0]->get_inner().get_plugin()==m_er_plugin) {
810             std::swap(srels[0], srels[1]);
811         }
812         SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin());
813         SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin);
814 
815         relation_base & new_orig = srels[0]->get_inner();
816         explanation_relation & expl_rel = static_cast<explanation_relation &>(srels[1]->get_inner());
817 
818         {
819             scoped_ptr<relation_union_fn> orig_union_fun = rmgr.mk_union_fn(new_orig, orig);
820             SASSERT(orig_union_fun);
821             (*orig_union_fun)(new_orig, orig);
822         }
823 
824         {
825             scoped_ptr<relation_union_fn> expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation);
826             SASSERT(expl_union_fun);
827             (*expl_union_fun)(expl_rel, *m_e_fact_relation);
828         }
829     }
830 
transform_facts(relation_manager & rmgr,rule_set const & src,rule_set & dst)831     void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) {
832 
833         if (!m_e_fact_relation) {
834             relation_signature expl_singleton_sig;
835             expl_singleton_sig.push_back(m_e_sort);
836 
837             relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind());
838             relation_fact es_fact(m_manager);
839             es_fact.push_back(m_decl_util.mk_fact(symbol("fact")));
840             expl_singleton->add_fact(es_fact);
841 
842             SASSERT(&expl_singleton->get_plugin()==m_er_plugin);
843             m_e_fact_relation = static_cast<explanation_relation *>(expl_singleton);
844         }
845         func_decl_set predicates(m_context.get_predicates());
846         for (func_decl* orig_decl : predicates) {
847             TRACE("dl", tout << mk_pp(orig_decl, m_manager) << "\n";);
848             func_decl * e_decl = get_e_decl(orig_decl);
849 
850             if (!rmgr.try_get_relation(orig_decl) &&
851                 !src.contains(orig_decl)) {
852                 // there are no facts or rules for this predicate
853                 continue;
854             }
855 
856             dst.inherit_predicate(src, orig_decl, e_decl);
857 
858             relation_base & orig_rel = rmgr.get_relation(orig_decl);
859             relation_base & e_rel = rmgr.get_relation(e_decl);
860             SASSERT(e_rel.empty()); //the e_rel should be a new relation
861 
862             if (m_relation_level) {
863                 translate_rel_level_relation(rmgr, orig_rel, e_rel);
864             }
865             else {
866                 scoped_ptr<relation_join_fn> product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, nullptr, nullptr);
867                 SASSERT(product_fun);
868                 scoped_rel<relation_base> aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation);
869                 TRACE("dl", tout << aux_extended_rel << " " << aux_extended_rel->get_plugin().get_name() << "\n";
870                       tout << e_rel.get_plugin().get_name() << "\n";);
871                 scoped_ptr<relation_union_fn> union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel);
872                 TRACE("dl", tout << union_fun << "\n";);
873                 SASSERT(union_fun);
874                 (*union_fun)(e_rel, *aux_extended_rel);
875             }
876         }
877     }
878 
operator ()(rule_set const & source)879     rule_set * mk_explanations::operator()(rule_set const & source) {
880 
881         if (source.empty()) {
882             return nullptr;
883         }
884         if (!m_context.generate_explanations()) {
885             return nullptr;
886         }
887         scoped_ptr<rule_set> res = alloc(rule_set, m_context);
888         transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res);
889         transform_rules(source, *res);
890         return res.detach();
891     }
892 
893 };
894 
895