/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_vector_relation.h Abstract: Basic relation with equivalences. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #ifndef _DL_VECTOR_RELATION_H_ #define _DL_VECTOR_RELATION_H_ #include "ast_pp.h" #include "dl_context.h" #include "union_find.h" namespace datalog { typedef std::pair u_pair; template class vector_relation_helper { public: static void mk_project_t(T& t, unsigned_vector const& renaming) {} }; template > class vector_relation : public relation_base { protected: T m_default; vector* m_elems; bool m_empty; union_find_default_ctx m_ctx; union_find<>* m_eqs; friend class vector_relation_plugin; public: vector_relation(relation_plugin& p, relation_signature const& s, bool is_empty, T const& t = T()): relation_base(p, s), m_default(t), m_elems(alloc(vector)), m_empty(is_empty), m_eqs(alloc(union_find<>, m_ctx)) { m_elems->resize(s.size(), t); for (unsigned i = 0; i < s.size(); ++i) { m_eqs->mk_var(); } } virtual ~vector_relation() { dealloc(m_eqs); dealloc(m_elems); } virtual bool can_swap() const { return true; } virtual void swap(relation_base& other) { vector_relation& o = dynamic_cast(other); if (&o == this) return; std::swap(o.m_eqs, m_eqs); std::swap(o.m_empty, m_empty); std::swap(o.m_elems, m_elems); } void copy(vector_relation const& other) { SASSERT(get_signature() == other.get_signature()); if (other.empty()) { set_empty(); return; } m_empty = false; for (unsigned i = 0; i < m_elems->size(); ++i) { (*this)[i] = other[i]; SASSERT(find(i) == i); } for (unsigned i = 0; i < m_elems->size(); ++i) { merge(i, find(i)); } } virtual bool empty() const { return m_empty; } T& operator[](unsigned i) { return (*m_elems)[find(i)]; } T const& operator[](unsigned i) const { return (*m_elems)[find(i)]; } virtual void display_index(unsigned i, T const& t, std::ostream& out) const = 0; virtual void display(std::ostream & out) const { if (empty()) { out << "empty\n"; return; } for (unsigned i = 0; i < m_elems->size(); ++i) { if (i == find(i)) { display_index(i, (*m_elems)[i], out); } else { out << i << " = " << find(i) << "\n"; } } } bool is_subset_of(vector_relation const& other) const { if (empty()) return true; if (other.empty()) return false; for (unsigned i = 0; i < get_signature().size(); ++i) { if (!is_subset_of((*this)[i], other[i])) { return false; } } return true; } void set_empty() { unsigned sz = m_elems->size(); m_empty = true; m_elems->reset(); m_elems->resize(sz, m_default); dealloc(m_eqs); m_eqs = alloc(union_find<>,m_ctx); for (unsigned i = 0; i < sz; ++i) { m_eqs->mk_var(); } } virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const = 0; virtual T mk_widen(T const& t1, T const& t2) const = 0; virtual T mk_unite(T const& t1, T const& t2) const = 0; virtual bool is_subset_of(T const& t1, T const& t2) const = 0; virtual bool is_full(T const& t) const = 0; virtual bool is_empty(unsigned i, T const& t) const = 0; virtual void mk_rename_elem(T& t, unsigned col_cnt, unsigned const* cycle) = 0; virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& neq_eqs, T const& t) const { return t; } void equate(unsigned i, unsigned j) { SASSERT(i < get_signature().size()); SASSERT(j < get_signature().size()); if (!empty() && find(i) != find(j)) { bool isempty; T r = mk_intersect((*this)[i], (*this)[j], isempty); if (isempty || is_empty(find(i),r)) { m_empty = true; } else { merge(i, j); (*this)[i] = r; } } } bool is_full() const { for (unsigned i = 0; i < m_elems->size(); ++i) { if (!is_full((*this)[i])) { return false; } } return true; } void mk_join(vector_relation const& r1, vector_relation const& r2, unsigned num_cols, unsigned const* cols1, unsigned const* cols2) { SASSERT(is_full()); bool is_empty = r1.empty() || r2.empty(); if (is_empty) { m_empty = true; return; } unsigned sz1 = r1.get_signature().size(); unsigned sz2 = r2.get_signature().size(); for (unsigned i = 0; i < sz1; ++i) { (*this)[i] = r1[i]; } for (unsigned i = 0; i < sz2; ++i) { (*this)[sz1+i] = r2[i]; } for (unsigned i = 0; i < num_cols; ++i) { unsigned col1 = cols1[i]; unsigned col2 = cols2[i]; equate(col1, sz1 + col2); } TRACE("dl_relation", r1.display(tout << "r1:\n"); r2.display(tout << "r2:\n"); display(tout << "dst:\n"); ); } void mk_project(vector_relation const& r, unsigned col_cnt, unsigned const* removed_cols) { SASSERT(is_full()); unsigned j = 0, k = 0; unsigned_vector classRep, repNode; unsigned result_size = get_signature().size(); unsigned input_size = r.get_signature().size(); repNode.resize(input_size, UINT_MAX); // initialize vector entries and set class representatives. for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { if (c < col_cnt && removed_cols[c] == i) { ++c; } else { (*this)[j] = r[i]; classRep.push_back(r.find(i)); ++j; } } // merge remaining equivalence classes. for (unsigned i = 0; i < result_size; ++i) { unsigned rep = classRep[i]; if (repNode[rep] == UINT_MAX) { repNode[rep] = i; } else { merge(repNode[rep], i); } } // rename columns in image of vector relation. unsigned_vector renaming; for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { if (c < col_cnt && removed_cols[c] == i) { renaming.push_back(UINT_MAX); ++c; } else { renaming.push_back(find(j)); ++j; } } for (unsigned k = 0; k < result_size; ++k) { Helper::mk_project_t((*this)[k], renaming); } TRACE("dl_relation", ast_manager& m = r.get_plugin().get_ast_manager(); tout << "Signature: "; for (unsigned i = 0; i < r.get_signature().size(); ++i) { tout << mk_pp(r.get_signature()[i], m) << " "; } tout << "Remove: "; for (unsigned i = 0; i < col_cnt; ++i) { tout << removed_cols[i] << " "; } tout << "\n"; r.display(tout); tout << " --> \n"; display(tout);); } void mk_rename(vector_relation const& r, unsigned col_cnt, unsigned const* cycle) { unsigned col1, col2; SASSERT(is_full()); // roundabout way of creating permuted relation. unsigned_vector classRep, repNode; for (unsigned i = 0; i < r.m_elems->size(); ++i) { classRep.push_back(r.find(i)); repNode.push_back(UINT_MAX); (*this)[i] = r[i]; } for (unsigned i = 0; i + 1 < col_cnt; ++i) { col1 = cycle[i]; col2 = cycle[i+1]; (*this)[col2] = (*r.m_elems)[col1]; classRep[col2] = r.find(col1); } col1 = cycle[col_cnt-1]; col2 = cycle[0]; (*this)[col2] = (*r.m_elems)[col1]; classRep[col2] = r.find(col1); for (unsigned i = 0; i < r.m_elems->size(); ++i) { unsigned rep = classRep[i]; if (repNode[rep] == UINT_MAX) { repNode[rep] = i; } else { merge(repNode[rep], i); } } for (unsigned i = 0; i < r.m_elems->size(); ++i) { mk_rename_elem((*m_elems)[i], col_cnt, cycle); } TRACE("dl_relation", ast_manager& m = r.get_plugin().get_ast_manager(); tout << "cycle: "; for (unsigned i = 0; i < col_cnt; ++i) { tout << cycle[i] << " "; } tout << "\nold_sig: "; for (unsigned i = 0; i < r.get_signature().size(); ++i) { tout << mk_pp(r.get_signature()[i], m) << " "; } tout << "\nnew_sig: "; for (unsigned i = 0; i < get_signature().size(); ++i) { tout << mk_pp(get_signature()[i], m) << " "; } tout << "\n"; r.display(tout << "src:\n"); ); } void mk_union(vector_relation const& src, vector_relation* delta, bool is_widen) { TRACE("dl_relation", display(tout << "dst:\n"); src.display(tout << "src:\n");); if (src.empty()) { if (delta) { delta->copy(src); } return; } if (empty()) { copy(src); if (delta) { delta->copy(src); } return; } // find coarsest equivalence class containing joint equalities union_find<>* uf = alloc(union_find<>, m_ctx); unsigned size = get_signature().size(); map, default_eq > mp; bool change = false; bit_vector finds; finds.resize(size, false); for (unsigned i = 0; i < size; ++i) { uf->mk_var(); unsigned w; u_pair p(std::make_pair(find(i), src.find(i))); if (mp.find(p, w)) { uf->merge(i, w); } else { mp.insert(p, i); // detect change if (finds.get(find(i))) { change = true; } else { finds.set(find(i), true); } } } vector* elems = alloc(vector); for (unsigned i = 0; i < size; ++i) { T t1 = mk_eq(*m_eqs, *uf, (*this)[i]); T t2 = mk_eq(*src.m_eqs, *uf, src[i]); if (is_widen) { elems->push_back(mk_widen(t1, t2)); } else { elems->push_back(mk_unite(t1, t2)); } TRACE("dl_relation", tout << t1 << " u " << t2 << " = " << elems->back() << "\n";); change = delta && (change || !((*elems)[i] == (*this)[i])); } dealloc(m_eqs); dealloc(m_elems); m_eqs = uf; m_elems = elems; if (delta && change) { delta->copy(*this); } TRACE("dl_relation", display(tout << "dst':\n");); } unsigned find(unsigned i) const { return m_eqs->find(i); } void merge(unsigned i, unsigned j) { m_eqs->merge(i, j); } }; }; #endif