/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_explanations.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-11-08. Revision History: --*/ #include #include "ast/ast_pp.h" #include "muz/rel/dl_sieve_relation.h" namespace datalog { // ----------------------------------- // // sieve_relation // // ----------------------------------- sieve_relation::sieve_relation(sieve_relation_plugin & p, const relation_signature & s, const bool * inner_columns, relation_base * inner) : relation_base(p, s), m_inner_cols(s.size(), inner_columns), m_inner(inner) { unsigned n = s.size(); for(unsigned i=0; i 0; ) { --i; unsigned idx = m_inner2sig[i]; s.push_back(m.mk_var(idx, sig[i])); } get_inner().to_formula(tmp); fml = get_plugin().get_context().get_var_subst()(tmp, sz, s.data()); } void sieve_relation::display(std::ostream & out) const { out << "Sieve relation "; print_container(m_inner_cols, out); out <<"\n"; get_inner().display(out); } // ----------------------------------- // // sieve_relation_plugin // // ----------------------------------- sieve_relation_plugin & sieve_relation_plugin::get_plugin(relation_manager & rmgr) { sieve_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); if(!res) { res = alloc(sieve_relation_plugin, rmgr); rmgr.register_plugin(res); } return *res; } sieve_relation& sieve_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } sieve_relation const & sieve_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } sieve_relation* sieve_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } sieve_relation const* sieve_relation_plugin::get(relation_base const* r) { return dynamic_cast(r); } sieve_relation_plugin::sieve_relation_plugin(relation_manager & manager) : relation_plugin(get_name(), manager, ST_SIEVE_RELATION), m_spec_store(*this) {} void sieve_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id sieve_relation_plugin::get_relation_kind(const relation_signature & sig, const bool * inner_columns, family_id inner_kind) { rel_spec spec(sig.size(), inner_columns, inner_kind); return m_spec_store.get_relation_kind(sig, spec); } family_id sieve_relation_plugin::get_relation_kind(sieve_relation & r, const bool * inner_columns) { const relation_signature & sig = r.get_signature(); return get_relation_kind(sig, inner_columns, r.get_inner().get_kind()); } void sieve_relation_plugin::extract_inner_columns(const relation_signature & s, relation_plugin & inner, bool_vector & inner_columns) { SASSERT(inner_columns.size()==s.size()); unsigned n = s.size(); relation_signature inner_sig_singleton; for(unsigned i=0; iget_plugin().is_sieve_relation()); //it does not make sense to make a sieve of a sieve return alloc(sieve_relation, *this, s, inner_columns, inner_rel); } sieve_relation * sieve_relation_plugin::mk_empty(const sieve_relation & original) { return static_cast(mk_empty(original.get_signature(), original.get_kind())); } relation_base * sieve_relation_plugin::mk_empty(const relation_base & original) { return mk_empty(static_cast(original)); } relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); relation_signature inner_sig; collect_inner_signature(s, spec.m_inner_cols, inner_sig); relation_base * inner = get_manager().mk_empty_relation(inner_sig, spec.m_inner_kind); return mk_from_inner(s, spec.m_inner_cols.data(), inner); } relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) { UNREACHABLE(); return nullptr; #if 0 bool_vector inner_cols(s.size()); extract_inner_columns(s, inner_cols.c_ptr()); return mk_empty(s, inner_cols.c_ptr()); #endif } sieve_relation * sieve_relation_plugin::mk_empty(const relation_signature & s, relation_plugin & inner_plugin) { SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve bool_vector inner_cols(s.size()); extract_inner_columns(s, inner_plugin, inner_cols); relation_signature inner_sig; collect_inner_signature(s, inner_cols, inner_sig); relation_base * inner_rel = inner_plugin.mk_empty(inner_sig); return mk_from_inner(s, inner_cols, inner_rel); } relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_signature empty_sig; relation_plugin& plugin = get_manager().get_appropriate_plugin(s); relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id); bool_vector inner_cols; inner_cols.resize(s.size(), false); return mk_from_inner(s, inner_cols, inner); } sieve_relation * sieve_relation_plugin::full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin) { SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve bool_vector inner_cols(s.size()); extract_inner_columns(s, inner_plugin, inner_cols); relation_signature inner_sig; collect_inner_signature(s, inner_cols, inner_sig); relation_base * inner_rel = inner_plugin.mk_full(p, inner_sig, null_family_id); return mk_from_inner(s, inner_cols, inner_rel); } class sieve_relation_plugin::join_fn : public convenient_relation_join_fn { sieve_relation_plugin & m_plugin; unsigned_vector m_inner_cols_1; unsigned_vector m_inner_cols_2; bool_vector m_result_inner_cols; scoped_ptr m_inner_join_fun; public: join_fn(sieve_relation_plugin & p, const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, relation_join_fn * inner_join_fun) : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2), m_plugin(p), m_inner_join_fun(inner_join_fun) { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; if(r1_sieved) { m_result_inner_cols.append(sr1->m_inner_cols); } else { m_result_inner_cols.resize(r1.get_signature().size(), true); } if(r2_sieved) { m_result_inner_cols.append(sr2->m_inner_cols); } else { m_result_inner_cols.resize(m_result_inner_cols.size() + r2.get_signature().size(), true); } } relation_base * operator()(const relation_base & r1, const relation_base & r2) override { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); SASSERT(r1_sieved || r2_sieved); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; relation_base * inner_res = (*m_inner_join_fun)(inner1, inner2); return m_plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.data(), inner_res); } }; relation_join_fn * sieve_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if( &r1.get_plugin()!=this && &r2.get_plugin()!=this ) { //we create just operations that involve the current plugin return nullptr; } bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; unsigned_vector inner_cols1; unsigned_vector inner_cols2; for(unsigned i=0; iis_inner_col(cols1[i])) { continue; } if(r2_sieved && !sr2->is_inner_col(cols2[i])) { continue; } inner_cols1.push_back( r1_sieved ? sr1->get_inner_col(cols1[i]) : cols1[i] ); inner_cols2.push_back( r2_sieved ? sr2->get_inner_col(cols2[i]) : cols2[i] ); } relation_join_fn * inner_join_fun = get_manager().mk_join_fn(inner1, inner2, inner_cols1, inner_cols2, false); if(!inner_join_fun) { return nullptr; } return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2, inner_join_fun); } class sieve_relation_plugin::transformer_fn : public convenient_relation_transformer_fn { bool_vector m_result_inner_cols; scoped_ptr m_inner_fun; public: transformer_fn(relation_transformer_fn * inner_fun, const relation_signature & result_sig, const bool * result_inner_cols) : m_result_inner_cols(result_sig.size(), result_inner_cols), m_inner_fun(inner_fun) { get_result_signature() = result_sig; } relation_base * operator()(const relation_base & r0) override { SASSERT(r0.get_plugin().is_sieve_relation()); const sieve_relation & r = static_cast(r0); sieve_relation_plugin & plugin = r.get_plugin(); relation_base * inner_res = (*m_inner_fun)(r.get_inner()); return plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.data(), inner_res); } }; relation_transformer_fn * sieve_relation_plugin::mk_project_fn(const relation_base & r0, unsigned col_cnt, const unsigned * removed_cols) { if(&r0.get_plugin()!=this) { return nullptr; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_removed_cols; for(unsigned i=0; i(r0); unsigned sig_sz = r.get_signature().size(); unsigned_vector permutation; add_sequence(0, sig_sz, permutation); permute_by_cycle(permutation, cycle_len, permutation_cycle); bool inner_identity; unsigned_vector inner_permutation; collect_sub_permutation(permutation, r.m_sig2inner, inner_permutation, inner_identity); bool_vector result_inner_cols = r.m_inner_cols; permute_by_cycle(result_inner_cols, cycle_len, permutation_cycle); relation_signature result_sig; relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, result_sig); relation_transformer_fn * inner_fun = get_manager().mk_permutation_rename_fn(r.get_inner(), inner_permutation); if(!inner_fun) { return nullptr; } return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.data()); } class sieve_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_union_fun; public: union_fn(relation_union_fn * union_fun) : m_union_fun(union_fun) {} void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) override { bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : nullptr; const sieve_relation * ssrc = src_sieved ? static_cast(&src) : nullptr; sieve_relation * sdelta = delta_sieved ? static_cast(delta) : nullptr; relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; (*m_union_fun)(itgt, isrc, idelta); } }; relation_union_fn * sieve_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if(&tgt.get_plugin()!=this && &src.get_plugin()!=this && (delta && &delta->get_plugin()!=this)) { //we create the operation only if it involves this plugin return nullptr; } bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); const sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : nullptr; const sieve_relation * ssrc = src_sieved ? static_cast(&src) : nullptr; const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : nullptr; const relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; const relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; //Now we require that the sieved and inner columns must match on all relations. //We may want to allow for some cases of misalignment even though it could introcude imprecision if( tgt_sieved && src_sieved && (!delta || delta_sieved) ) { if( !vectors_equal(stgt->m_inner_cols, ssrc->m_inner_cols) || (delta && !vectors_equal(stgt->m_inner_cols, sdelta->m_inner_cols)) ) { return nullptr; } } else { if( (stgt && !stgt->no_sieved_columns()) || (ssrc && !ssrc->no_sieved_columns()) || (sdelta && !sdelta->no_sieved_columns()) ) { //We have an unsieved relation and then some relation with some sieved columns, //which means there is an misalignment. return nullptr; } } relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta); if(!union_fun) { return nullptr; } return alloc(union_fn, union_fun); } class sieve_relation_plugin::filter_fn : public relation_mutator_fn { scoped_ptr m_inner_fun; public: filter_fn(relation_mutator_fn * inner_fun) : m_inner_fun(inner_fun) {} void operator()(relation_base & r0) override { SASSERT(r0.get_plugin().is_sieve_relation()); sieve_relation & r = static_cast(r0); (*m_inner_fun)(r.get_inner()); } }; relation_mutator_fn * sieve_relation_plugin::mk_filter_identical_fn(const relation_base & r0, unsigned col_cnt, const unsigned * identical_cols) { if(&r0.get_plugin()!=this) { return nullptr; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_icols; //we ignore the columns which do not belong to the inner relation (which introduces imprecision) for(unsigned i=0; i(r0); if(!r.is_inner_col(col)) { //if the column which do not belong to the inner relation, we do nothing (which introduces imprecision) return alloc(identity_relation_mutator_fn); } unsigned inner_col = r.get_inner_col(col); relation_mutator_fn * inner_fun = get_manager().mk_filter_equal_fn(r.get_inner(), value, inner_col); if(!inner_fun) { return nullptr; } return alloc(filter_fn, inner_fun); } relation_mutator_fn * sieve_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, app * condition) { if(&rb.get_plugin()!=this) { return nullptr; } ast_manager & m = get_ast_manager(); const sieve_relation & r = static_cast(rb); const relation_signature sig = r.get_signature(); unsigned sz = sig.size(); var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition); expr_ref_vector subst_vect(m); subst_vect.resize(sz); unsigned subst_ofs = sz-1; for(unsigned i=0; i m_inner_fun; public: negation_filter_fn(relation_intersection_filter_fn * inner_fun) : m_inner_fun(inner_fun) {} void operator()(relation_base & r, const relation_base & neg) override { bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); sieve_relation * sr = r_sieved ? static_cast(&r) : nullptr; const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : nullptr; relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; (*m_inner_fun)(inner_r, inner_neg); } }; relation_intersection_filter_fn * sieve_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & neg, unsigned col_cnt, const unsigned * r_cols, const unsigned * neg_cols) { if(&r.get_plugin()!=this && &neg.get_plugin()!=this) { //we create just operations that involve the current plugin return nullptr; } bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); const sieve_relation * sr = r_sieved ? static_cast(&r) : nullptr; const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : nullptr; const relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; unsigned_vector ir_cols; unsigned_vector ineg_cols; for(unsigned i=0; iis_inner_col(r_cols[i]); bool neg_col_inner = neg_sieved && !sneg->is_inner_col(neg_cols[i]); if(r_col_inner && neg_col_inner) { ir_cols.push_back( r_sieved ? sr->get_inner_col(i) : i ); ineg_cols.push_back( neg_sieved ? sneg->get_inner_col(i) : i ); } else if(!r_col_inner && neg_col_inner) { //Sieved (i.e. full) column in r is matched on an inner column in neg. //If we assume the column in neg is not full, no rows from the inner relation of //r would be removed. So in this case we perform no operation at cost of a little //impresicion. return alloc(identity_relation_intersection_filter_fn); } else { //Inner or sieved column in r must match a sieved column in neg. //Since sieved columns are full, this is always true so we can skip the equality. continue; } } relation_intersection_filter_fn * inner_fun = get_manager().mk_filter_by_negation_fn(inner_r, inner_neg, ir_cols, ineg_cols); if(!inner_fun) { return nullptr; } return alloc(negation_filter_fn, inner_fun); } };