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