/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_product_relation.cpp Abstract: A Relation combinator. Author: Nikolaj Bjorner (nbjorner) 2010-4-11 Revision History: Notes: join = more refined version lets augment the product relation as a consequence of join. join Q = join = u = more refined version: < (R u R') n (R u S') n (R' u S), (S u S') n (S u R') n (S' u R)> proj = < proj R, proj S> & phi = attach S to [R & phi] whenever R & phi can propagate to S [rename] = --*/ #include "dl_sieve_relation.h" #include "dl_table_relation.h" #include "dl_product_relation.h" #include "bool_rewriter.h" #include "ast_pp.h" namespace datalog { // ----------------------------------- // // product_relation_plugin // // ----------------------------------- product_relation_plugin & product_relation_plugin::get_plugin(relation_manager & rmgr) { product_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); if(!res) { res = alloc(product_relation_plugin, rmgr); rmgr.register_plugin(res); } return *res; } product_relation_plugin::product_relation_plugin(relation_manager& m): relation_plugin(product_relation_plugin::get_name(), m, ST_PRODUCT_RELATION), m_spec_store(*this) { } void product_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id product_relation_plugin::get_relation_kind(const relation_signature & sig, const rel_spec & spec) { return m_spec_store.get_relation_kind(sig, spec); } family_id product_relation_plugin::get_relation_kind(const product_relation & r) { return get_relation_kind(r.get_signature(), r.m_spec); } bool product_relation_plugin::can_handle_signature(const relation_signature & s) { return m_spec_store.contains_signature(s); } bool product_relation_plugin::can_handle_signature(const relation_signature & s, family_id k) { return true; } product_relation& product_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } product_relation const & product_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } product_relation* product_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } product_relation const* product_relation_plugin::get(relation_base const* r) { return dynamic_cast(r); } bool product_relation_plugin::is_product_relation(relation_base const& r) { return r.get_plugin().get_name() == product_relation_plugin::get_name(); } bool product_relation_plugin::are_aligned(const product_relation& r1, const product_relation& r2) { unsigned sz = r1.size(); if(sz!=r2.size()) { return false; } for(unsigned i=0; i & rels, rel_spec & res) { vector specs; ptr_vector::const_iterator rit = rels.begin(); ptr_vector::const_iterator rend = rels.end(); for(; rit!=rend; ++rit) { specs.push_back((*rit)->m_spec); } vector::iterator sit = specs.begin(); vector::iterator send = specs.end(); for(; sit!=send; ++sit) { rel_spec & s = *sit; std::sort(s.begin(), s.end()); } res.reset(); for(;;) { family_id next = -1; sit = specs.begin(); for(; sit!=send; ++sit) { rel_spec & s = *sit; if(!s.empty() && s.back()>next) { next = s.back(); } } if(next==-1) { //we're done break; } res.push_back(next); sit = specs.begin(); for(; sit!=send; ++sit) { rel_spec & s = *sit; if(!s.empty() && s.back()==next) { s.pop_back(); } } } } relation_base * product_relation_plugin::mk_empty(const relation_signature & s) { return alloc(product_relation,*this, s); } relation_base * product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; relation_signature sig_empty; m_spec_store.get_relation_spec(sig_empty, kind, spec); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; i m_joins; ptr_vector m_full; unsigned_vector m_offset1; svector m_kind1; unsigned_vector m_offset2; svector m_kind2; const relation_base & get_nonsieve_relation(const relation_base & r) { relation_plugin & rp = r.get_plugin(); if(rp.is_sieve_relation()) { return static_cast(r).get_inner(); } else { return r; } } relation_plugin & get_nonsieve_plugin(const relation_base & r) { return get_nonsieve_relation(r).get_plugin(); } family_id get_nonsieve_kind(const relation_base & r) { return get_nonsieve_relation(r).get_kind(); } /** A tableish relatio is either a table_relation or a sieve_relation with a table_relation inside. */ bool is_tableish_relation(const relation_base & r) { return get_nonsieve_plugin(r).from_table(); } relation_base * get_full_tableish_relation(const relation_signature & sig, func_decl* p, family_id kind) { relation_manager& rmgr = m_plugin.get_manager(); table_signature tsig; if(rmgr.relation_signature_to_table(sig, tsig)) { return rmgr.mk_table_relation(sig, rmgr.get_appropriate_plugin(tsig).mk_full(p, tsig, kind)); } unsigned sz = sig.size(); tsig.reset(); for(unsigned i=0; i relations; unsigned sz = m_joins.size(); relation_base* result = 0; for (unsigned i = 0; i < sz; ++i) { relation_base const& r1 = (m_kind1[i] == T_FULL)?(*m_full[m_offset1[i]]):access(m_offset1[i], _r1); relation_base const& r2 = (m_kind2[i] == T_FULL)?(*m_full[m_offset2[i]]):access(m_offset2[i], _r2); relations.push_back((*m_joins[i])(r1, r2)); } result = alloc(product_relation, m_plugin, get_result_signature(), sz, relations.c_ptr()); TRACE("dl",result->display(tout);); return result; } }; relation_join_fn * product_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (is_product_relation(r1) && is_product_relation(r2)) { return alloc(join_fn, *this, get(r1), get(r2), col_cnt, cols1, cols2); } if (is_product_relation(r1)) { return alloc(join_fn, *this, get(r1), r2, col_cnt, cols1, cols2); } if (is_product_relation(r2)) { return alloc(join_fn, *this, r1, get(r2), col_cnt, cols1, cols2); } if (r1.get_kind() != r2.get_kind()) { return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2); } return 0; } class product_relation_plugin::transform_fn : public relation_transformer_fn { relation_signature m_sig; ptr_vector m_transforms; public: transform_fn(relation_signature s, unsigned num_trans, relation_transformer_fn** trans): m_sig(s), m_transforms(num_trans, trans) {} ~transform_fn() { dealloc_ptr_vector_content(m_transforms); } virtual relation_base * operator()(const relation_base & _r) { product_relation const& r = get(_r); product_relation_plugin& p = r.get_plugin(); SASSERT(m_transforms.size() == r.size()); ptr_vector relations; for (unsigned i = 0; i < r.size(); ++i) { relations.push_back((*m_transforms[i])(r[i])); } relation_base* result = alloc(product_relation, p, m_sig, relations.size(), relations.c_ptr()); TRACE("dl", _r.display(tout); result->display(tout);); return result; } }; relation_transformer_fn * product_relation_plugin::mk_project_fn(const relation_base & _r, unsigned col_cnt, const unsigned * removed_cols) { if (is_product_relation(_r)) { product_relation const& r = get(_r); ptr_vector projs; for (unsigned i = 0; i < r.size(); ++i) { projs.push_back(get_manager().mk_project_fn(r[i], col_cnt, removed_cols)); } relation_signature s; relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, s); return alloc(transform_fn, s, projs.size(), projs.c_ptr()); } return 0; } relation_transformer_fn * product_relation_plugin::mk_rename_fn(const relation_base & _r, unsigned cycle_len, const unsigned * permutation_cycle) { if(is_product_relation(_r)) { ptr_vector trans; product_relation const& r = get(_r); for (unsigned i = 0; i < r.size(); ++i) { trans.push_back(get_manager().mk_rename_fn(r[i], cycle_len, permutation_cycle)); } relation_signature s; relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, s); return alloc(transform_fn, s, trans.size(), trans.c_ptr()); } return 0; } class product_relation_plugin::aligned_union_fn : public relation_union_fn { relation_manager & m_rmgr; bool m_is_widen; //m_union[i][j] is union between i-th and j-th relation. //It can be zero which means that particular union should be skipped. vector > m_unions; void mk_union_fn(unsigned i, unsigned j, relation_base const& r1, relation_base const& r2, const relation_base* delta) { relation_manager& rmgr = r1.get_manager(); relation_union_fn* u = 0; if (m_is_widen) { u = rmgr.mk_widen_fn(r1, r2, delta); } else { u = rmgr.mk_union_fn(r1, r2, delta); } m_unions.back().push_back(u); } void init(const relation_vector & tgts, const relation_vector & srcs, const relation_vector * deltas) { SASSERT(tgts.size()==srcs.size()); unsigned num = tgts.size(); for (unsigned i = 0; i < num; ++i) { relation_base& r1 = *tgts[i]; relation_base* delta = deltas ? (*deltas)[i] : 0; m_unions.push_back(ptr_vector()); for (unsigned j = 0; j < num; ++j) { relation_base& r2 = *srcs[j]; mk_union_fn(i, j, r1, r2, delta); } } } bool can_do_inner_union(unsigned tgt_idx, unsigned src_idx) { return m_unions[tgt_idx][src_idx]!=0; } void do_inner_union(unsigned tgt_idx, unsigned src_idx, relation_base& tgt, relation_base& src, relation_base * delta) { SASSERT(m_unions[tgt_idx][src_idx]); (*m_unions[tgt_idx][src_idx])(tgt, src, delta); } /** If tgt is zero, it is assumed to be a full relation. */ void do_destructive_intersection(scoped_rel& tgt, scoped_rel& src) { if(!src) { return; } if(!tgt) { tgt=src.release(); return; } do_intersection(*tgt, *src); src = 0; } void do_intersection(relation_base& tgt, relation_base& src) { scoped_ptr intersect_fun = m_rmgr.mk_filter_by_intersection_fn(tgt, src); if(!intersect_fun) { warning_msg("intersection does not exist"); return; } (*intersect_fun)(tgt, src); } void do_delta_union(unsigned rel_idx, relation_base& tgt, relation_base& src) { scoped_ptr union_fun = m_rmgr.mk_union_fn(tgt, src); SASSERT(union_fun); (*union_fun)(tgt, src); } public: aligned_union_fn(product_relation const& tgt, product_relation const& src, product_relation const* delta, bool is_widen) : m_rmgr(tgt.get_manager()), m_is_widen(is_widen) { SASSERT(vectors_equal(tgt.m_spec, src.m_spec)); SASSERT(!delta || vectors_equal(tgt.m_spec, delta->m_spec)); init(tgt.m_relations, src.m_relations, delta ? &delta->m_relations : 0); } ~aligned_union_fn() { unsigned sz = m_unions.size(); for(unsigned i=0; i side_results; ptr_vector side_deltas; for (unsigned i = 0; i < num; ++i) { relation_base& itgt = tgt[i]; relation_base* idelta = delta ? &(*delta)[i] : 0; scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; scoped_rel side_result; scoped_rel side_delta; //compute the side unions with which we will intersect the result of the basic one for (unsigned j = 0; j < num; ++j) { if (i == j) { continue; //this is the basic union which we will perform later } if (can_do_inner_union(i, j)) { TRACE("dl", itgt.display(tout << "tgt:\n"); src[j].display(tout << "src:\n");); // union[i][j] scoped_rel one_side_union = itgt.clone(); scoped_rel one_side_delta = fresh_delta ? fresh_delta->clone() : 0; TRACE("dl", one_side_union->display(tout << "union 1:\n"); src[j].display(tout);); do_inner_union(i, j, *one_side_union, src[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); do_destructive_intersection(side_result, one_side_union); TRACE("dl", side_result->display(tout << "inner-union: " << i << " " << j << "\n"); itgt.display(tout << "tgt:\n");); if (one_side_delta) { do_destructive_intersection(side_delta, one_side_delta); } // union[j][i] one_side_union = src[i].clone(); one_side_delta = fresh_delta ? fresh_delta->clone() : 0; TRACE("dl", one_side_union->display(tout << "union 2:\n"); tgt[j].display(tout);); do_inner_union(i, j, *one_side_union, tgt[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); do_destructive_intersection(side_result, one_side_union); TRACE("dl", side_result->display(tout << "inner-union: " << i << " " << j << "\n"); itgt.display(tout << "tgt:\n");); if (one_side_delta) { do_destructive_intersection(side_delta, one_side_delta); } } } side_results.push_back(side_result.release()); side_deltas.push_back(side_delta.release()); } for (unsigned i = 0; i < num; ++i) { relation_base& itgt = tgt[i]; relation_base* idelta = delta ? &(*delta)[i] : 0; scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; scoped_rel side_result(side_results[i]); scoped_rel side_delta(side_deltas[i]); // perform the basic union // assume a relation can always perform union with the relation of the same type VERIFY(can_do_inner_union(i,i)); do_inner_union(i, i, itgt, src[i], fresh_delta.get()); if (side_result) { do_intersection(itgt, *side_result); TRACE("dl", side_result->display(tout << "inner-union-end: " << i << "\n");); } if (fresh_delta) { do_destructive_intersection(fresh_delta,side_delta); SASSERT(idelta); do_delta_union(i, *idelta, *fresh_delta); } } if (num == 0) { //we need to handle product relation of no relations separately if (!src.m_default_empty && tgt.m_default_empty) { tgt.m_default_empty = false; if (delta) { delta->m_default_empty = false; } } } TRACE("dl", _tgt.display(tout << "dst':\n"); if (_delta) _delta->display(tout << "delta:\n"); ;); } }; class product_relation_plugin::unaligned_union_fn : public relation_union_fn { bool m_is_widen; rel_spec m_common_spec; scoped_ptr m_aligned_union_fun; public: unaligned_union_fn(product_relation const& tgt, product_relation const& src, product_relation const* delta, bool is_widen) : m_is_widen(is_widen) { ptr_vector rels; rels.push_back(&tgt); rels.push_back(&src); if(delta) { rels.push_back(delta); } get_common_spec(rels, m_common_spec); } virtual void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) { TRACE("dl", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); product_relation& tgt = get(_tgt); product_relation const& src0 = get(_src); product_relation* delta = _delta ? get(_delta) : 0; tgt.convert_spec(m_common_spec); if(delta) { delta->convert_spec(m_common_spec); } scoped_rel src_scoped; if(src0.get_kind()!=tgt.get_kind()) { src_scoped = src0.clone(); src_scoped->convert_spec(m_common_spec); } product_relation const& src = src_scoped ? *src_scoped : src0; if(!m_aligned_union_fun) { m_aligned_union_fun = alloc(aligned_union_fn, tgt, src, delta, m_is_widen); SASSERT(m_aligned_union_fun); } (*m_aligned_union_fun)(tgt, src, delta); TRACE("dl", _tgt.display(tout << "dst':\n"); if (_delta) _delta->display(tout << "delta:\n");); } }; class product_relation_plugin::single_non_transparent_src_union_fn : public relation_union_fn { unsigned m_single_rel_idx; scoped_ptr m_inner_union_fun; public: single_non_transparent_src_union_fn(unsigned single_rel_idx, relation_union_fn* inner_union_fun) : m_single_rel_idx(single_rel_idx), m_inner_union_fun(inner_union_fun) {} virtual void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) { product_relation const& src = get(_src); (*m_inner_union_fun)(tgt, src[m_single_rel_idx], delta); } }; relation_union_fn * product_relation_plugin::mk_union_w_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta, bool is_widen) { if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { if(are_aligned(get(tgt), get(src)) && (!delta || are_aligned(get(tgt), *get(delta)))) { return alloc(aligned_union_fn, get(tgt), get(src), get(delta), is_widen); } return alloc(unaligned_union_fn, get(tgt), get(src), get(delta), is_widen); } if(check_kind(src)) { const product_relation & p_src = get(src); unsigned single_idx; if(p_src.try_get_single_non_transparent(single_idx)) { relation_union_fn * inner; if(is_widen) { inner = get_manager().mk_widen_fn(tgt, p_src[single_idx], delta); } else { inner = get_manager().mk_union_fn(tgt, p_src[single_idx], delta); } if(inner) { return alloc(single_non_transparent_src_union_fn, single_idx, inner); } } } return 0; } relation_union_fn * product_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_w_fn(tgt, src, delta, false); } relation_union_fn * product_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_w_fn(tgt, src, delta, true); } class product_relation_plugin::mutator_fn : public relation_mutator_fn { ptr_vector m_mutators; public: mutator_fn(unsigned sz, relation_mutator_fn** muts): m_mutators(sz, muts) {} ~mutator_fn() { dealloc_ptr_vector_content(m_mutators); } virtual void operator()(relation_base & _r) { TRACE("dl", _r.display(tout);); product_relation& r = get(_r); SASSERT(m_mutators.size() == r.size()); for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = m_mutators[i]; if (m) { (*m)(r[i]); } } TRACE("dl", _r.display(tout);); } }; relation_mutator_fn * product_relation_plugin::mk_filter_identical_fn( const relation_base & _t, unsigned col_cnt, const unsigned * identical_cols) { if(is_product_relation(_t)) { bool found = false; product_relation const& r = get(_t); ptr_vector mutators; for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = get_manager().mk_filter_identical_fn(r[i], col_cnt, identical_cols); mutators.push_back(m); if (m) found = true; } if (found) { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } return 0; } relation_mutator_fn * product_relation_plugin::mk_filter_equal_fn(const relation_base & _t, const relation_element & value, unsigned col) { if(is_product_relation(_t)) { product_relation const& r = get(_t); ptr_vector mutators; bool found = false; for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = get_manager().mk_filter_equal_fn(r[i], value, col); mutators.push_back(m); if (m) found = true; } if (found) { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } return 0; } class product_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { ptr_vector m_mutators; svector > m_attach; public: filter_interpreted_fn(product_relation const& r, app* cond) { for (unsigned i = 0; i < r.size(); ++i) { m_mutators.push_back(r.get_manager().mk_filter_interpreted_fn(r[i], cond)); } for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn& m1 = *(m_mutators[i]); for (unsigned j = i + 1; j < r.size(); ++j) { relation_mutator_fn& m2 = *(m_mutators[j]); if (m1.supports_attachment(r[j])) { m_attach.push_back(std::make_pair(i,j)); } if (m2.supports_attachment(r[i])) { m_attach.push_back(std::make_pair(j,i)); } } } } ~filter_interpreted_fn() { dealloc_ptr_vector_content(m_mutators); } void operator()(relation_base& _r) { TRACE("dl", _r.display(tout);); product_relation const& r = get(_r); for (unsigned i = 0; i < m_attach.size(); ++i) { m_mutators[m_attach[i].first]->attach(r[m_attach[i].second]); } for (unsigned i = 0; i < m_mutators.size(); ++i) { (*m_mutators[i])(r[i]); } TRACE("dl", _r.display(tout);); } }; relation_mutator_fn * product_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return alloc(filter_interpreted_fn, get(t), condition); } // ----------------------------------- // // product_relation // // ----------------------------------- product_relation::product_relation(product_relation_plugin& p, relation_signature const& s): relation_base(p, s), m_default_empty(true) { ensure_correct_kind(); } product_relation::product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations) : relation_base(p, s), m_default_empty(true) { for (unsigned i = 0; i < num_relations; ++i) { SASSERT(relations[i]->get_signature()==s); m_relations.push_back(relations[i]); } ensure_correct_kind(); } product_relation::~product_relation() { unsigned num_relations = m_relations.size(); for (unsigned i = 0; i < num_relations; ++i) { m_relations[i]->deallocate(); } } product_relation_plugin& product_relation::get_plugin() const { return dynamic_cast(relation_base::get_plugin()); } void product_relation::ensure_correct_kind() { unsigned rel_cnt = m_relations.size(); //the rel_cnt==0 part makes us to update the kind also when the relation is newly created bool spec_changed = rel_cnt!=m_spec.size() || rel_cnt==0; if(spec_changed) { m_spec.resize(rel_cnt); } for(unsigned i=0;iget_kind(); if(spec_changed || m_spec[i]!=rkind) { spec_changed = true; m_spec[i]=rkind; } } if(spec_changed) { family_id new_kind = get_plugin().get_relation_kind(*this); set_kind(new_kind); } } void product_relation::convert_spec(const rel_spec & spec) { func_decl* p = 0; const relation_signature & sig = get_signature(); family_id new_kind = get_plugin().get_relation_kind(sig, spec); if(new_kind==get_kind()) { return; } unsigned old_sz = size(); unsigned new_sz = spec.size(); unsigned old_remain = old_sz; relation_vector new_rels; //the loop is quadratic with the number of relations, maybe we want to fix it for(unsigned i=0; iget_kind()==ikind) { irel = m_relations[j]; m_relations[j] = 0; old_remain--; break; } } if(!irel) { if(old_sz==0 && m_default_empty) { //The relation didn't contain any inner relations but it was empty, //so we make the newly added relations empty as well. irel = get_manager().mk_empty_relation(sig, new_kind); } else { irel = get_manager().mk_full_relation(sig, p, new_kind); } } new_rels.push_back(irel); } SASSERT(old_remain==0); //the new specification must be a superset of the old one m_relations = new_rels; set_kind(new_kind); DEBUG_CODE( ensure_correct_kind(); SASSERT(get_kind()==new_kind); ); } bool product_relation::try_get_single_non_transparent(unsigned & idx) const { unsigned sz = size(); bool found = false; unsigned candidate; for(unsigned i=0; i relations; for (unsigned i = 0; i < size(); ++i) { relations.push_back((*this)[i].clone()); } product_relation_plugin& p = get_plugin(); return alloc(product_relation, p, get_signature(), relations.size(), relations.c_ptr()); } product_relation * product_relation::complement(func_decl*) const { if(m_relations.empty()) { product_relation * res = clone(); res->m_default_empty = !m_default_empty; return res; } UNREACHABLE(); return 0; } bool product_relation::empty() const { if(m_relations.empty()) { return m_default_empty; } for (unsigned i = 0; i < m_relations.size(); ++i) { if (m_relations[i]->empty()) { return true; } } return false; } void product_relation::to_formula(expr_ref& fml) const { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); expr_ref tmp(m); for (unsigned i = 0; i < m_relations.size(); ++i) { m_relations[i]->to_formula(tmp); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), fml); } void product_relation::display(std::ostream & out) const { out<<"Product of the following relations:\n"; for (unsigned i = 0; i < m_relations.size(); ++i) { m_relations[i]->display(out); } } };