From c8f953525105a2c98f55e402faf6c2eb66b96059 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 21:23:16 -0700 Subject: [PATCH] re-organize muz_qe into separate units Signed-off-by: Nikolaj Bjorner --- src/muz/rel/dl_base.cpp | 490 ++++ src/muz/rel/dl_base.h | 1231 ++++++++++ src/muz/rel/dl_bound_relation.cpp | 707 ++++++ src/muz/rel/dl_bound_relation.h | 178 ++ src/muz/rel/dl_check_table.cpp | 439 ++++ src/muz/rel/dl_check_table.h | 135 ++ src/muz/rel/dl_external_relation.cpp | 456 ++++ src/muz/rel/dl_external_relation.h | 154 ++ src/muz/rel/dl_finite_product_relation.cpp | 2377 +++++++++++++++++++ src/muz/rel/dl_finite_product_relation.h | 366 +++ src/muz/rel/dl_interval_relation.cpp | 653 +++++ src/muz/rel/dl_interval_relation.h | 142 ++ src/muz/rel/dl_mk_explanations.cpp | 879 +++++++ src/muz/rel/dl_mk_explanations.h | 86 + src/muz/rel/dl_mk_partial_equiv.cpp | 155 ++ src/muz/rel/dl_mk_partial_equiv.h | 50 + src/muz/rel/dl_mk_similarity_compressor.cpp | 546 +++++ src/muz/rel/dl_mk_similarity_compressor.h | 78 + src/muz/rel/dl_mk_simple_joins.cpp | 742 ++++++ src/muz/rel/dl_mk_simple_joins.h | 63 + src/muz/rel/dl_product_relation.cpp | 1117 +++++++++ src/muz/rel/dl_product_relation.h | 191 ++ src/muz/rel/dl_relation_manager.cpp | 1702 +++++++++++++ src/muz/rel/dl_relation_manager.h | 688 ++++++ src/muz/rel/dl_sieve_relation.cpp | 666 ++++++ src/muz/rel/dl_sieve_relation.h | 198 ++ src/muz/rel/dl_sparse_table.cpp | 1246 ++++++++++ src/muz/rel/dl_sparse_table.h | 480 ++++ src/muz/rel/dl_table.cpp | 773 ++++++ src/muz/rel/dl_table.h | 265 +++ src/muz/rel/dl_table_plugin.h | 193 ++ src/muz/rel/dl_table_relation.cpp | 490 ++++ src/muz/rel/dl_table_relation.h | 133 ++ src/muz/rel/dl_vector_relation.h | 407 ++++ src/muz/rel/karr_relation.cpp | 790 ++++++ src/muz/rel/karr_relation.h | 88 + 36 files changed, 19354 insertions(+) create mode 100644 src/muz/rel/dl_base.cpp create mode 100644 src/muz/rel/dl_base.h create mode 100644 src/muz/rel/dl_bound_relation.cpp create mode 100644 src/muz/rel/dl_bound_relation.h create mode 100644 src/muz/rel/dl_check_table.cpp create mode 100644 src/muz/rel/dl_check_table.h create mode 100644 src/muz/rel/dl_external_relation.cpp create mode 100644 src/muz/rel/dl_external_relation.h create mode 100644 src/muz/rel/dl_finite_product_relation.cpp create mode 100644 src/muz/rel/dl_finite_product_relation.h create mode 100644 src/muz/rel/dl_interval_relation.cpp create mode 100644 src/muz/rel/dl_interval_relation.h create mode 100644 src/muz/rel/dl_mk_explanations.cpp create mode 100644 src/muz/rel/dl_mk_explanations.h create mode 100644 src/muz/rel/dl_mk_partial_equiv.cpp create mode 100644 src/muz/rel/dl_mk_partial_equiv.h create mode 100644 src/muz/rel/dl_mk_similarity_compressor.cpp create mode 100644 src/muz/rel/dl_mk_similarity_compressor.h create mode 100644 src/muz/rel/dl_mk_simple_joins.cpp create mode 100644 src/muz/rel/dl_mk_simple_joins.h create mode 100644 src/muz/rel/dl_product_relation.cpp create mode 100644 src/muz/rel/dl_product_relation.h create mode 100644 src/muz/rel/dl_relation_manager.cpp create mode 100644 src/muz/rel/dl_relation_manager.h create mode 100644 src/muz/rel/dl_sieve_relation.cpp create mode 100644 src/muz/rel/dl_sieve_relation.h create mode 100644 src/muz/rel/dl_sparse_table.cpp create mode 100644 src/muz/rel/dl_sparse_table.h create mode 100644 src/muz/rel/dl_table.cpp create mode 100644 src/muz/rel/dl_table.h create mode 100644 src/muz/rel/dl_table_plugin.h create mode 100644 src/muz/rel/dl_table_relation.cpp create mode 100644 src/muz/rel/dl_table_relation.h create mode 100644 src/muz/rel/dl_vector_relation.h create mode 100644 src/muz/rel/karr_relation.cpp create mode 100644 src/muz/rel/karr_relation.h diff --git a/src/muz/rel/dl_base.cpp b/src/muz/rel/dl_base.cpp new file mode 100644 index 000000000..dc10b5f8e --- /dev/null +++ b/src/muz/rel/dl_base.cpp @@ -0,0 +1,490 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_base.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include"ast_pp.h" +#include"union_find.h" +#include"vector.h" +#include"dl_context.h" +#include"dl_base.h" +#include"bool_rewriter.h" +#include"dl_relation_manager.h" +#include + + +namespace datalog { + + void universal_delete(relation_base* ptr) { + ptr->deallocate(); + } + + void universal_delete(table_base* ptr) { + ptr->deallocate(); + } + + void dealloc_ptr_vector_content(ptr_vector & v) { + ptr_vector::iterator it = v.begin(); + ptr_vector::iterator end = v.end(); + for(; it!=end; ++it) { + (*it)->deallocate(); + } + } + + void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, + expr_ref_vector & renaming_arg) { + ast_manager & m = renaming_arg.get_manager(); + unsigned sz = map.size(); + unsigned ofs = sz-1; + renaming_arg.resize(sz, static_cast(0)); + for(unsigned i=0; i reset_fn = + get_manager().mk_filter_interpreted_fn(static_cast(*this), bottom_ref); + if(!reset_fn) { + NOT_IMPLEMENTED_YET(); + } + (*reset_fn)(*this); + } + + + + void table_signature::from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, table_signature & result) { + result.reset(); + + unsigned s1sz=s1.size(); + unsigned s2sz=s2.size(); + unsigned s1first_func=s1sz-s1.functional_columns(); + unsigned s2first_func=s2sz-s2.functional_columns(); + for(unsigned i=0; i=col_cnt); + result.set_functional_columns(func_cnt-col_cnt); + } + } + + void table_signature::from_project_with_reduce(const table_signature & src, unsigned col_cnt, + const unsigned * removed_cols, table_signature & result) { + signature_base::from_project(src, col_cnt, removed_cols, result); + + unsigned remaining_fun = src.functional_columns(); + unsigned first_src_fun = src.first_functional(); + for(int i=col_cnt-1; i>=0; i--) { + if(removed_cols[i] remaining_in_equivalence_class; + remaining_in_equivalence_class.resize(join_sig_sz, 0); + bool merging_rows_can_happen = false; + + union_find_default_ctx uf_ctx; + union_find<> uf(uf_ctx); //the numbers in uf correspond to column indexes after the join + for(unsigned i=0; icols1[i]) ? cols1[i] : (first_func_ofs+cols1[i]-s1_first_func); + unsigned idx2 = (s2_first_func>cols2[i]) ? (second_ofs+cols2[i]) : (second_func_ofs+cols2[i]-s2_first_func); + uf.merge(idx1, idx2); + } + for(unsigned i=0; i=first_func_ofs) { + //removing functional columns won't make us merge rows + continue; + } + unsigned eq_class_idx = uf.find(rc); + if(remaining_in_equivalence_class[eq_class_idx]>1) { + remaining_in_equivalence_class[eq_class_idx]--; + } + else { + merging_rows_can_happen = true; + break; + } + } + + if(merging_rows_can_happen) { + //this one marks all columns as non-functional + from_project(aux, removed_col_cnt, removed_cols, result); + SASSERT(result.functional_columns()==0); + } + else { + //this one preserves columns to be functional + from_project_with_reduce(aux, removed_col_cnt, removed_cols, result); + } + } + + + + + // ----------------------------------- + // + // table_base + // + // ----------------------------------- + + //here we give generic implementation of table operations using iterators + + bool table_base::empty() const { + return begin()==end(); + } + + void table_base::remove_facts(unsigned fact_cnt, const table_fact * facts) { + for(unsigned i=0; i to_remove; + table_base::iterator it = begin(); + table_base::iterator iend = end(); + table_fact row; + for(; it!=iend; ++it) { + it->get_fact(row); + to_remove.push_back(row); + } + remove_facts(to_remove.size(), to_remove.c_ptr()); + } + + bool table_base::contains_fact(const table_fact & f) const { + iterator it = begin(); + iterator iend = end(); + + table_fact row; + + for(; it!=iend; ++it) { + it->get_fact(row); + if(vectors_equal(row, f)) { + return true; + } + } + return false; + } + + bool table_base::fetch_fact(table_fact & f) const { + if(get_signature().functional_columns()==0) { + return contains_fact(f); + } + else { + unsigned sig_sz = get_signature().size(); + unsigned non_func_cnt = sig_sz-get_signature().functional_columns(); + table_base::iterator it = begin(); + table_base::iterator iend = end(); + table_fact row; + for(; it!=iend; ++it) { + it->get_fact(row); + bool differs = false; + for(unsigned i=0; iget_fact(row); + res->add_new_fact(row); + } + return res; + } + + /** + \brief Default method for complementation. + + It assumes that the compiler creates only tables with + at most one column (0 or 1 columns). + Complementation of tables with more than one columns + is transformed into a cross product of complements and/or + difference. + + */ + table_base * table_base::complement(func_decl* p, const table_element * func_columns) const { + const table_signature & sig = get_signature(); + SASSERT(sig.functional_columns()==0 || func_columns!=0); + SASSERT(sig.first_functional() <= 1); + + table_base * res = get_plugin().mk_empty(sig); + + table_fact fact; + fact.resize(sig.first_functional()); + fact.append(sig.functional_columns(), func_columns); + + if (sig.first_functional() == 0) { + if (empty()) { + res->add_fact(fact); + } + return res; + } + + VERIFY(sig.first_functional() == 1); + + uint64 upper_bound = get_signature()[0]; + bool empty_table = empty(); + + if (upper_bound > (1 << 18)) { + std::ostringstream buffer; + buffer << "creating large table of size " << upper_bound; + if (p) buffer << " for relation " << p->get_name(); + warning_msg(buffer.str().c_str()); + } + + for(table_element i = 0; i < upper_bound; i++) { + fact[0] = i; + if(empty_table || !contains_fact(fact)) { + res->add_fact(fact); + } + } + return res; + } + + void table_base::display(std::ostream & out) const { + out << "table with signature "; + print_container(get_signature(), out); + out << ":\n"; + + iterator it = begin(); + iterator iend = end(); + for(; it!=iend; ++it) { + const row_interface & r = *it; + r.display(out); + } + + out << "\n"; + } + + + class table_base::row_interface::fact_row_iterator : public table_base::row_iterator_core { + const row_interface & m_parent; + unsigned m_index; + protected: + virtual bool is_finished() const { return m_index==m_parent.size(); } + public: + fact_row_iterator(const row_interface & row, bool finished) + : m_parent(row), m_index(finished ? row.size() : 0) {} + + virtual table_element operator*() { + SASSERT(!is_finished()); + return m_parent[m_index]; + } + + virtual void operator++() { + m_index++; + SASSERT(m_index<=m_parent.size()); + } + }; + + table_base::row_iterator table_base::row_interface::begin() const { + return row_iterator(alloc(fact_row_iterator, *this, false)); + } + table_base::row_iterator table_base::row_interface::end() const { + return row_iterator(alloc(fact_row_iterator, *this, true)); + } + + void table_base::row_interface::get_fact(table_fact & result) const { + result.reset(); + unsigned n=size(); + for(unsigned i=0; i + +Author: + + Krystof Hoder (t-khoder) 2010-09-23. + +Revision History: + +--*/ +#ifndef _DL_BASE_H_ +#define _DL_BASE_H_ + +#define DL_LEAK_HUNTING 0 + +#include + +#include"ast.h" +#include"map.h" +#include"vector.h" +#include"ref.h" +#include"dl_util.h" +#include"dl_context.h" + +namespace datalog { + + class context; + class relation_manager; + + ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); + context & get_context_from_rel_manager(const relation_manager & rm); + + typedef func_decl_set decl_set; + +#if DL_LEAK_HUNTING + void leak_guard_check(const symbol & s); +#endif + + void universal_delete(relation_base* ptr); + void universal_delete(table_base* ptr); + void dealloc_ptr_vector_content(ptr_vector & v); + + + /** + Termplate class containing common infrastructure for relations and tables + */ + template + struct tr_infrastructure { + + typedef typename Traits::plugin plugin; + typedef typename Traits::base_object base_object; + typedef typename Traits::element element; + typedef typename Traits::fact fact; + typedef typename Traits::sort sort; + typedef typename Traits::signature_base_base signature_base_base; //this must be a vector-like type + typedef typename Traits::signature signature; //this must be a vector-like type + + /** + The client submits an initial class to be used as a base for signature. Then we excend it by + the common signature methods into a signature_base class which then the client inherits from + to obtain the actual signature class. + */ + class signature_base : public signature_base_base { + public: + bool operator==(const signature & o) const { + unsigned n=signature_base_base::size(); + if(n!=o.size()) { + return false; + } + return memcmp(this->c_ptr(), o.c_ptr(), n*sizeof(sort))==0; + /*for(unsigned i=0; i=2); + result=src; + + permutate_by_cycle(result, cycle_len, permutation_cycle); + } + + /** + \brief Into \c result assign signature \c src with reordered columns. + */ + static void from_permutation_rename(const signature & src, + const unsigned * permutation, signature & result) { + result.reset(); + unsigned n = src.size(); + for(unsigned i=0; i(0)); + } + }; + + /** + \brief Mutator for relations that propagate constraints as a consequence of + combination. + + - supports_attachment + is used to query the mutator if it allows communicating + constraints to relations of the kind of the relation. + + - attach + is used to associate downstream clients. + It assumes that the relation kind is supported (supports_kind returns true) + */ + class mutator_fn : public base_fn { + public: + virtual void operator()(base_object & t) = 0; + + virtual bool supports_attachment(base_object& other) { return false; } + + virtual void attach(base_object& other) { UNREACHABLE(); } + }; + + class intersection_filter_fn : public base_fn { + public: + virtual void operator()(base_object & t, const base_object & intersected_obj) = 0; + }; + + class default_join_project_fn; + + /** + \brief Plugin class providing factory functions for a table and operations on it. + + The functor factory functions (mk_*_fn) may return 0. It means that the plugin + is unable to perform the operation on relations/tables of the particular kind. + */ + class plugin_object { + friend class relation_manager; + friend class check_table_plugin; + + family_id m_kind; + symbol m_name; + relation_manager & m_manager; + protected: + plugin_object(symbol const& name, relation_manager & manager) + : m_kind(null_family_id), m_name(name), m_manager(manager) {} + + /** + \brief Check \c r is of the same kind as the plugin. + */ + bool check_kind(base_object const& r) const { return &r.get_plugin()==this; } + public: + virtual ~plugin_object() {} + + virtual void initialize(family_id fid) { m_kind = fid; } + + family_id get_kind() const { return m_kind; } + + symbol const& get_name() const { return m_name; } + + virtual void set_cancel(bool f) {} + + relation_manager & get_manager() const { return m_manager; } + ast_manager& get_ast_manager() const { return datalog::get_ast_manager_from_rel_manager(m_manager); } + context& get_context() const { return datalog::get_context_from_rel_manager(m_manager); } + + virtual bool can_handle_signature(const signature & s) = 0; + + virtual bool can_handle_signature(const signature & s, family_id kind) { + return can_handle_signature(s); + } + + /** + \brief Create empty table/relation with given signature and return pointer to it. + + Precondition: can_handle_signature(s)==true + */ + virtual base_object * mk_empty(const signature & s) = 0; + + /** + \brief Create empty table/relation with signature \c s and kind \c kind. + + Precondition: &orig.get_plugin()==this + */ + virtual base_object * mk_empty(const signature & s, family_id kind) { + SASSERT(kind==get_kind()); //if plugin uses multiple kinds, this function needs to be overriden + return mk_empty(s); + } + + /** + \brief Create empty table/relation of the same specification as \c orig and return pointer to it. + + Precondition: &orig.get_plugin()==this + */ + virtual base_object * mk_empty(const base_object & orig) { + return mk_empty(orig.get_signature(), orig.get_kind()); + } + + /** + \brief Create full table/relation with given signature and return pointer to it. + + Precondition: can_handle_signature(s)==true + */ + virtual base_object * mk_full(func_decl* p, const signature & s) { + base_object * aux = mk_empty(s); + base_object * res = aux->complement(p); + aux->deallocate(); + return res; + } + + virtual base_object * mk_full(func_decl* p, const signature & s, family_id kind) { + if (kind == get_kind() || kind == null_family_id) { + return mk_full(p, s); + } + base_object * aux = mk_empty(s, kind); + base_object * res = aux->complement(p); + aux->deallocate(); + return res; + } + + protected: + //see \c relation_manager for documentation of the operations + + virtual join_fn * mk_join_fn(const base_object & t1, const base_object & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return 0; } + + virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, + const unsigned * removed_cols) { return 0; } + + virtual join_fn * mk_join_project_fn(const base_object & t1, const base_object & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + + virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { return 0; } + + virtual transformer_fn * mk_permutation_rename_fn(const base_object & t, + const unsigned * permutation) { return 0; } + + public: + virtual union_fn * mk_union_fn(const base_object & tgt, const base_object & src, + const base_object * delta) { return 0; } + protected: + + virtual union_fn * mk_widen_fn(const base_object & tgt, const base_object & src, + const base_object * delta) { return 0; } + + virtual mutator_fn * mk_filter_identical_fn(const base_object & t, unsigned col_cnt, + const unsigned * identical_cols) { return 0; } + + virtual mutator_fn * mk_filter_equal_fn(const base_object & t, const element & value, + unsigned col) { return 0; } + + virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) + { return 0; } + + virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + { return 0; } + + virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, + const element & value, unsigned col) { return 0; } + + virtual intersection_filter_fn * mk_filter_by_intersection_fn(const base_object & t, + const base_object & src, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * src_cols) + { return 0; } + + virtual intersection_filter_fn * mk_filter_by_negation_fn(const base_object & t, + const base_object & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) + { return 0; } + }; + + class base_ancestor { + plugin & m_plugin; + signature m_signature; + family_id m_kind; +#if DL_LEAK_HUNTING + sort_ref m_leak_guard; +#endif + protected: + base_ancestor(plugin & p, const signature & s) + : m_plugin(p), m_signature(s), m_kind(p.get_kind()) +#if DL_LEAK_HUNTING + , m_leak_guard(p.get_ast_manager().mk_fresh_sort(p.get_name().bare_str()), p.get_ast_manager()) +#endif + { +#if DL_LEAK_HUNTING + leak_guard_check(m_leak_guard->get_name()); +#endif + } + +#if DL_LEAK_HUNTING + base_ancestor(const base_ancestor & o) + : m_plugin(o.m_plugin), + m_signature(o.m_signature), + m_kind(o.m_kind), + m_leak_guard(m_plugin.get_ast_manager().mk_fresh_sort(m_plugin.get_name().bare_str()), + m_plugin.get_ast_manager()) { + leak_guard_check(m_leak_guard->get_name()); + } +#endif + + virtual ~base_ancestor() {} + + void set_kind(family_id kind) { SASSERT(kind>=0); m_kind = kind; } + + /** + Since the destructor is protected, we cannot use the \c dealloc macro. + */ + void destroy() { + SASSERT(this); + this->~base_ancestor(); +#if _DEBUG + memory::deallocate(__FILE__, __LINE__, this); +#else + memory::deallocate(this); +#endif + } + public: + /** + Deallocate the current object. + + Pointers and references to the current object become invalid after a call to this function. + */ + virtual void deallocate() { + destroy(); + } + /** + It must hold that operations created for particular table/relation are able to operate on + tables/relations of the same signature and kind. In most cases it is sufficient to use the kind + of the plugin object. + + However, it there is some parameter that is not reflected in the signature, the plugin may need to + allocate different kind numbers to the tables is creates. + */ + family_id get_kind() const { return m_kind; } + const signature & get_signature() const { return m_signature; } + plugin & get_plugin() const { return m_plugin; } + relation_manager & get_manager() const { return get_plugin().get_manager(); } + + virtual bool empty() const = 0; + virtual void add_fact(const fact & f) = 0; + /** + \brief Like \c add_fact, only here the caller guarantees that the fact is not present in + the table yet. + */ + virtual void add_new_fact(const fact & f) { + add_fact(f); + } + virtual bool contains_fact(const fact & f) const = 0; + + virtual void reset() = 0; + + /** + \brief Return table/relation that contains the same data as the current one. + */ + virtual base_object * clone() const = 0; + + virtual bool can_swap(const base_object & o) const { return false; } + + virtual void swap(base_object & o) { + std::swap(m_kind, o.m_kind); +#if DL_LEAK_HUNTING + m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); + o.m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); + leak_guard_check(m_leak_guard->get_name()); + leak_guard_check(o.m_leak_guard->get_name()); +#endif + } + + virtual unsigned get_size_estimate_rows() const { return UINT_MAX; } + virtual unsigned get_size_estimate_bytes() const { return UINT_MAX; } + virtual bool knows_exact_size() const { return false; } + + virtual void display(std::ostream & out) const = 0; + }; + + + class convenient_join_fn : public join_fn { + signature m_result_sig; + protected: + unsigned_vector m_cols1; + unsigned_vector m_cols2; + + convenient_join_fn(const signature & o1_sig, const signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : m_cols1(col_cnt, cols1), + m_cols2(col_cnt, cols2) { + signature::from_join(o1_sig, o2_sig, col_cnt, cols1, cols2, m_result_sig); + } + + const signature & get_result_signature() const { return m_result_sig; } + }; + + class convenient_join_project_fn : public join_fn { + signature m_result_sig; + protected: + unsigned_vector m_cols1; + unsigned_vector m_cols2; + //it is non-const because it needs to be modified in sparse_table version of the join_project operator + unsigned_vector m_removed_cols; + + convenient_join_project_fn(const signature & o1_sig, const signature & o2_sig, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) + : m_cols1(joined_col_cnt, cols1), + m_cols2(joined_col_cnt, cols2), + m_removed_cols(removed_col_cnt, removed_cols) { + + signature::from_join_project(o1_sig, o2_sig, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols, m_result_sig); + } + + const signature & get_result_signature() const { return m_result_sig; } + }; + + class convenient_transformer_fn : public transformer_fn { + signature m_result_sig; + protected: + signature & get_result_signature() { return m_result_sig; } + const signature & get_result_signature() const { return m_result_sig; } + }; + + class convenient_project_fn : public convenient_transformer_fn { + protected: + unsigned_vector m_removed_cols; + + convenient_project_fn(const signature & orig_sig, unsigned col_cnt, const unsigned * removed_cols) + : m_removed_cols(col_cnt, removed_cols) { + signature::from_project(orig_sig, col_cnt, removed_cols, + convenient_transformer_fn::get_result_signature()); + } + }; + + class convenient_rename_fn : public convenient_transformer_fn { + protected: + const unsigned_vector m_cycle; + + convenient_rename_fn(const signature & orig_sig, unsigned cycle_len, + const unsigned * permutation_cycle) + : m_cycle(cycle_len, permutation_cycle) { + signature::from_rename(orig_sig, cycle_len, permutation_cycle, + convenient_transformer_fn::get_result_signature()); + } + }; + + class convenient_negation_filter_fn : public intersection_filter_fn { + protected: + unsigned m_joined_col_cnt; + const unsigned_vector m_cols1; + const unsigned_vector m_cols2; + bool m_all_neg_bound; //all columns are bound at least once + bool m_overlap; //one column in negated table is bound multiple times + svector m_bound; + + convenient_negation_filter_fn(const base_object & tgt, const base_object & neg_t, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) + : m_joined_col_cnt(joined_col_cnt), m_cols1(joined_col_cnt, t_cols), + m_cols2(joined_col_cnt, negated_cols) { + unsigned neg_sig_size = neg_t.get_signature().size(); + m_overlap = false; + m_bound.resize(neg_sig_size, false); + for(unsigned i=0; i + void make_neg_bindings(T & tgt_neg, const U & src) const { + SASSERT(m_all_neg_bound); + SASSERT(!m_overlap); + for(unsigned i=0; i + bool bindings_match(const T & tgt_neg, const U & src) const { + for(unsigned i=0; i renamer_vector; + + unsigned_vector m_permutation; //this is valid only before m_renamers_initialized becomes true + bool m_renamers_initialized; + renamer_vector m_renamers; + public: + default_permutation_rename_fn(const base_object & o, const unsigned * permutation) + : m_permutation(o.get_signature().size(), permutation), + m_renamers_initialized(false) {} + + ~default_permutation_rename_fn() { + dealloc_ptr_vector_content(m_renamers); + } + + base_object * operator()(const base_object & o) { + const base_object * res = &o; + scoped_rel res_scoped; + if(m_renamers_initialized) { + typename renamer_vector::iterator rit = m_renamers.begin(); + typename renamer_vector::iterator rend = m_renamers.end(); + for(; rit!=rend; ++rit) { + res_scoped = (**rit)(*res); + res = res_scoped.get(); + } + } + else { + SASSERT(m_renamers.empty()); + unsigned_vector cycle; + while(try_remove_cycle_from_permutation(m_permutation, cycle)) { + transformer_fn * renamer = o.get_manager().mk_rename_fn(*res, cycle); + SASSERT(renamer); + m_renamers.push_back(renamer); + cycle.reset(); + + res_scoped = (*renamer)(*res); + res = res_scoped.get(); + } + m_renamers_initialized = true; + } + if(res_scoped) { + SASSERT(res==res_scoped.get()); + //we don't want to delete the last one since we'll be returning it + return res_scoped.release(); + } + else { + SASSERT(res==&o); + return res->clone(); + } + } + + }; + + + }; + + + // ----------------------------------- + // + // relation_base + // + // ----------------------------------- + + class relation_signature; + class relation_plugin; + class relation_base; + + typedef ptr_vector relation_signature_base0; + typedef ptr_hash relation_sort_hash; + + + struct relation_traits { + typedef relation_plugin plugin; + typedef relation_base base_object; + typedef relation_element element; + typedef relation_fact fact; + typedef relation_sort sort; + typedef relation_signature_base0 signature_base_base; + typedef relation_signature signature; + }; + + typedef tr_infrastructure relation_infrastructure; + + typedef relation_infrastructure::base_fn base_relation_fn; + typedef relation_infrastructure::join_fn relation_join_fn; + typedef relation_infrastructure::transformer_fn relation_transformer_fn; + typedef relation_infrastructure::union_fn relation_union_fn; + typedef relation_infrastructure::mutator_fn relation_mutator_fn; + typedef relation_infrastructure::intersection_filter_fn relation_intersection_filter_fn; + + typedef relation_infrastructure::convenient_join_fn convenient_relation_join_fn; + typedef relation_infrastructure::convenient_join_project_fn convenient_relation_join_project_fn; + typedef relation_infrastructure::convenient_transformer_fn convenient_relation_transformer_fn; + typedef relation_infrastructure::convenient_project_fn convenient_relation_project_fn; + typedef relation_infrastructure::convenient_rename_fn convenient_relation_rename_fn; + typedef relation_infrastructure::convenient_negation_filter_fn convenient_relation_negation_filter_fn; + typedef relation_infrastructure::identity_transformer_fn identity_relation_transformer_fn; + typedef relation_infrastructure::identity_mutator_fn identity_relation_mutator_fn; + typedef relation_infrastructure::identity_intersection_filter_fn identity_relation_intersection_filter_fn; + typedef relation_infrastructure::default_permutation_rename_fn default_relation_permutation_rename_fn; + + class relation_signature : public relation_infrastructure::signature_base { + public: + bool operator!=(const relation_signature & o) const { + return !(*this==o); + } + + void output(ast_manager & m, std::ostream & out) const; + + struct hash { + unsigned operator()(relation_signature const& s) const { + return obj_vector_hash(s); + } + }; + + struct eq { + bool operator()(relation_signature const& s1, relation_signature const& s2) const { + return s1 == s2; + } + }; + }; + + class relation_plugin : public relation_infrastructure::plugin_object { + protected: + enum special_relation_type { + ST_ORDINARY, + ST_TABLE_RELATION, + ST_FINITE_PRODUCT_RELATION, + ST_PRODUCT_RELATION, + ST_SIEVE_RELATION + }; + private: + special_relation_type m_special_type; + protected: + relation_plugin(symbol const& name, relation_manager & manager, + special_relation_type special_type = ST_ORDINARY) + : plugin_object(name, manager), + m_special_type(special_type) {} + public: + bool from_table() const { return m_special_type==ST_TABLE_RELATION; } + bool is_finite_product_relation() const { return m_special_type==ST_FINITE_PRODUCT_RELATION; } + bool is_product_relation() const { return m_special_type==ST_PRODUCT_RELATION; } + bool is_sieve_relation() const { return m_special_type==ST_SIEVE_RELATION; } + + /** + \brief If true, the relation can contain only one or zero elements. + + Having this zero allows the finite_product_relation to perform some operations in a simpler way. + (KH: I started implementing finite_product_relation::inner_singleton_union_fn that takes advantage of + it, but it's not finished.) + */ + virtual bool is_singleton_relation() const { return false; } + }; + + class relation_base : public relation_infrastructure::base_ancestor { + protected: + relation_base(relation_plugin & plugin, const relation_signature & s) + : base_ancestor(plugin, s) {} + virtual ~relation_base() {} + public: + virtual relation_base * complement(func_decl* p) const = 0; + + virtual void reset(); + + virtual void display_tuples(func_decl & pred, std::ostream & out) const { + out << "Tuples in " << pred.get_name() << ": \n"; + display(out); + } + + virtual void to_formula(expr_ref& fml) const = 0; + + bool from_table() const { return get_plugin().from_table(); } + virtual bool is_precise() const { return true; } + }; + + typedef ptr_vector relation_vector; + + // ----------------------------------- + // + // table_base + // + // ----------------------------------- + + class table_signature; + class table_plugin; + class table_base; + + typedef uint64 table_sort; + typedef svector table_signature_base0; + typedef uint64_hash table_sort_hash; + + typedef uint64_hash table_element_hash; + + struct table_traits { + typedef table_plugin plugin; + typedef table_base base_object; + typedef table_element element; + typedef table_fact fact; + typedef table_sort sort; + typedef table_signature_base0 signature_base_base; + typedef table_signature signature; + }; + + typedef tr_infrastructure table_infrastructure; + + typedef table_infrastructure::base_fn base_table_fn; + typedef table_infrastructure::join_fn table_join_fn; + typedef table_infrastructure::transformer_fn table_transformer_fn; + typedef table_infrastructure::union_fn table_union_fn; + typedef table_infrastructure::mutator_fn table_mutator_fn; + typedef table_infrastructure::intersection_filter_fn table_intersection_filter_fn; + + typedef table_infrastructure::convenient_join_fn convenient_table_join_fn; + typedef table_infrastructure::convenient_join_project_fn convenient_table_join_project_fn; + typedef table_infrastructure::convenient_transformer_fn convenient_table_transformer_fn; + typedef table_infrastructure::convenient_project_fn convenient_table_project_fn; + typedef table_infrastructure::convenient_rename_fn convenient_table_rename_fn; + typedef table_infrastructure::convenient_negation_filter_fn convenient_table_negation_filter_fn; + typedef table_infrastructure::identity_transformer_fn identity_table_transformer_fn; + typedef table_infrastructure::identity_mutator_fn identity_table_mutator_fn; + typedef table_infrastructure::identity_intersection_filter_fn identity_table_intersection_filter_fn; + typedef table_infrastructure::default_permutation_rename_fn default_table_permutation_rename_fn; + + class table_row_mutator_fn { + public: + /** + \brief The function is called for a particular table row. The \c func_columns contains + a pointer to an array of functional column values that can be modified. If the function + returns true, the modification will appear in the table; otherwise the row will be deleted. + + It is possible that one call to the function stands for multiple table rows that share + the same functional column values. + */ + virtual bool operator()(table_element * func_columns) = 0; + }; + + class table_row_pair_reduce_fn { + public: + /** + \brief The function is called for pair of table rows that became duplicit due to projection. + The values that are in the first array after return from the function will be used for the + resulting row. + + It is assumed that the function is idempotent: when the two functional sub-tuples are equal, + the result is assumed to be equal to them as well, so the function may not be evaluated for them. + */ + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) = 0; + }; + + + class table_signature : public table_infrastructure::signature_base { + public: + struct hash { + unsigned operator()(table_signature const& s) const { + return svector_hash()(s); + } + }; + + struct eq { + bool operator()(table_signature const& s1, table_signature const& s2) const { + return s1 == s2; + } + }; + private: + unsigned m_functional_columns; + public: + table_signature() : m_functional_columns(0) {} + + void swap(table_signature & s) { + signature_base::swap(s); + std::swap(m_functional_columns, s.m_functional_columns); + } + + /** + \brief The returned value is the number of last columns that are functional. + + The uniqueness is enforced on non-functional columns. When projection causes two + facts to have equal non-functional parts, it is not defined which one of them is retained. + */ + unsigned functional_columns() const { return m_functional_columns; } + void set_functional_columns(unsigned val) { SASSERT(size()>=val); m_functional_columns = val; } + + /** + \brief Return index of the first functional column, or the size of the signature if there + are no functional columns. + */ + unsigned first_functional() const { return size()-m_functional_columns; } + + bool operator==(const table_signature & o) const { + return signature_base::operator==(o) && m_functional_columns==o.m_functional_columns; + } + bool operator!=(const table_signature & o) const { + return !(*this==o); + } + + /** + \brief return true iof the two signatures are equal when we ignore which columns are functional. + */ + bool equal_up_to_fn_mark(const table_signature & o) const { + return signature_base::operator==(o); + } + + + /** + \brief Into \c result assign signature of result of join of relations with signatures \c s1 + and \c s2. The result is + + (non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2) + */ + static void from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, table_signature & result); + + static void from_join_project(const table_signature & s1, const table_signature & s2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols, table_signature & result); + + + /** + \brief Into \c result assign signature projected from \c src. + + The array of removed columns must be sorted in ascending order. + + If we remove at least one non-functional column, all the columns in the result are non-functional. + */ + static void from_project(const table_signature & src, unsigned col_cnt, + const unsigned * removed_cols, table_signature & result); + + static void from_project_with_reduce(const table_signature & src, unsigned col_cnt, + const unsigned * removed_cols, table_signature & result); + + /** + \brief Into \c result assign signature \c src with reordered columns. + + Permutations between functional and nonfunctional columns are not allowed. + */ + static void from_rename(const table_signature & src, unsigned cycle_len, + const unsigned * permutation_cycle, table_signature & result) { + signature_base::from_rename(src, cycle_len, permutation_cycle, result); + result.set_functional_columns(src.functional_columns()); +#if Z3DEBUG + unsigned first_src_fun = src.size()-src.functional_columns(); + bool in_func = permutation_cycle[0]>=first_src_fun; + for(unsigned i=1;i=first_src_fun)); + } +#endif + } + + /** + \brief Into \c result assign signature \c src with reordered columns. + + Permutations mixing functional and nonfunctional columns are not allowed. + */ + static void from_permutation_rename(const table_signature & src, + const unsigned * permutation, table_signature & result) { + signature_base::from_permutation_rename(src, permutation, result); + result.set_functional_columns(src.functional_columns()); +#if Z3DEBUG + unsigned sz = src.size(); + unsigned first_src_fun = sz-src.functional_columns(); + for(unsigned i=first_src_fun;i=first_src_fun); + } +#endif + } + + }; + + class table_plugin : public table_infrastructure::plugin_object { + friend class relation_manager; + protected: + table_plugin(symbol const& n, relation_manager & manager) : plugin_object(n, manager) {} + public: + + virtual bool can_handle_signature(const table_signature & s) { return s.functional_columns()==0; } + + protected: + /** + If the returned value is non-zero, the returned object must take ownership of \c mapper. + Otherwise \c mapper must remain unmodified. + */ + virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { return 0; } + + /** + If the returned value is non-zero, the returned object must take ownership of \c reducer. + Otherwise \c reducer must remain unmodified. + */ + virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { return 0; } + + }; + + class table_base : public table_infrastructure::base_ancestor { + protected: + table_base(table_plugin & plugin, const table_signature & s) + : base_ancestor(plugin, s) {} + virtual ~table_base() {} + public: + virtual table_base * clone() const; + virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; + virtual bool empty() const; + + /** + \brief Return true if table contains fact that corresponds to \c f in all non-functional + columns. + */ + virtual bool contains_fact(const table_fact & f) const; + + /** + \brief If \c f (i.e. its non-functional part) is not present in the table, + add it and return true. Otherwise update \c f, so that the values of functional + columns correspond to the ones present in the table. + */ + virtual bool suggest_fact(table_fact & f); + + /** + \brief If \c f (i.e. its non-functional part) is not present in the table, + return false. Otherwise update \c f, so that the values of functional + columns correspond to the ones present in the table and return true. + */ + virtual bool fetch_fact(table_fact & f) const; + + /** + \brief Ensure fact \c f is present in the table (including the values of its functional columns). + */ + virtual void ensure_fact(const table_fact & f); + + virtual void remove_fact(const table_fact & fact) { + SASSERT(fact.size() == get_signature().size()); + remove_fact(fact.c_ptr()); } + + virtual void remove_fact(table_element const* fact) = 0; + virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); + virtual void remove_facts(unsigned fact_cnt, const table_element * facts); + virtual void reset(); + + class row_interface; + + virtual void display(std::ostream & out) const; + + /** + \brief Convert table to a formula that encodes the table. + The columns correspond to bound variables indexed as + 0, .., sig.size()-1 + */ + virtual void to_formula(relation_signature const& sig, expr_ref& fml) const; + + protected: + + + class iterator_core { + unsigned m_ref_cnt; + public: + iterator_core() : m_ref_cnt(0) {} + virtual ~iterator_core() {} + + void inc_ref() { m_ref_cnt++; } + void dec_ref() { + SASSERT(m_ref_cnt>0); + m_ref_cnt--; + if(m_ref_cnt==0) { + dealloc(this); + } + } + + virtual bool is_finished() const = 0; + + virtual row_interface & operator*() = 0; + virtual void operator++() = 0; + virtual bool operator==(const iterator_core & it) { + //we worry about the equality operator only because of checking + //the equality with the end() iterator + if(is_finished() && it.is_finished()) { + return true; + } + return false; + } + private: + //private and undefined copy constructor and assignment operator + iterator_core(const iterator_core &); + iterator_core & operator=(const iterator_core &); + }; + + struct row_iterator_core { + unsigned m_ref_cnt; + public: + row_iterator_core() : m_ref_cnt(0) {} + virtual ~row_iterator_core() {} + + void inc_ref() { m_ref_cnt++; } + void dec_ref() { + SASSERT(m_ref_cnt>0); + m_ref_cnt--; + if(m_ref_cnt==0) { + dealloc(this); + } + } + + virtual bool is_finished() const = 0; + + virtual table_element operator*() = 0; + virtual void operator++() = 0; + virtual bool operator==(const row_iterator_core & it) { + //we worry about the equality operator only because of checking + //the equality with the end() iterator + if(is_finished() && it.is_finished()) { + return true; + } + return false; + } + private: + //private and undefined copy constructor and assignment operator + row_iterator_core(const row_iterator_core &); + row_iterator_core & operator=(const row_iterator_core &); + }; + + public: + class iterator { + friend class table_base; + + ref m_core; + + iterator(iterator_core * core) : m_core(core) {} + public: + /** + \brief Return reference to a row_interface object for the current row. + + The reference is valid only until the \c operator++() is called or + until the iterator is invalidated. + */ + row_interface & operator*() + { return *(*m_core); } + row_interface * operator->() + { return &(*(*m_core)); } + iterator & operator++() + { ++(*m_core); return *this; } + bool operator==(const iterator & it) + { return (*m_core)==(*it.m_core); } + bool operator!=(const iterator & it) + { return !operator==(it); } + }; + + class row_iterator { + friend class table_base; + friend class row_interface; + + ref m_core; + + row_iterator(row_iterator_core * core) : m_core(core) {} + public: + table_element operator*() + { return *(*m_core); } + row_iterator & operator++() + { ++(*m_core); return *this; } + bool operator==(const row_iterator & it) + { return (*m_core)==(*it.m_core); } + bool operator!=(const row_iterator & it) + { return !operator==(it); } + }; + + virtual iterator begin() const = 0; + virtual iterator end() const = 0; + + class row_interface { + class fact_row_iterator; + + const table_base & m_parent_table; + public: + typedef row_iterator iterator; + typedef row_iterator const_iterator; + + row_interface(const table_base & parent_table) : m_parent_table(parent_table) {} + virtual ~row_interface() {} + + virtual table_element operator[](unsigned col) const = 0; + + unsigned size() const { return m_parent_table.get_signature().size(); } + virtual void get_fact(table_fact & result) const; + virtual row_iterator begin() const; + virtual row_iterator end() const; + virtual void display(std::ostream & out) const; + }; + + protected: + + class caching_row_interface : public row_interface { + mutable table_fact m_current; + + bool populated() const { return !m_current.empty(); } + void ensure_populated() const { + if(!populated()) { + get_fact(m_current); + } + } + public: + caching_row_interface(const table_base & parent) : row_interface(parent) {} + + virtual void get_fact(table_fact & result) const = 0; + + virtual table_element operator[](unsigned col) const { + ensure_populated(); + return m_current[col]; + } + /** + \brief Resets the cache of the row object. + + Must be called when the row object begins to represent a different row in the table. + */ + void reset() { m_current.reset(); } + }; + + //This function is here to create iterator instances in classes that derive from table_base. + //We do not want to make the constructor of the iterator class public, and being private, the + //inheritor classes cannot see it directly. + static iterator mk_iterator(iterator_core * core) { + return iterator(core); + } + }; + + /** + \brief Populate vector \c renaming_args so that it can be used as an argument to \c var_subst. + The renaming we want is one that transforms variables with numbers of indexes of \c map into the + values of at those indexes. If a value if \c UINT_MAX, it means we do not transform the index + corresponding to it. + */ + void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, + expr_ref_vector & renaming_arg); + + +}; + +#endif /* _DL_BASE_H_ */ + diff --git a/src/muz/rel/dl_bound_relation.cpp b/src/muz/rel/dl_bound_relation.cpp new file mode 100644 index 000000000..182046c1e --- /dev/null +++ b/src/muz/rel/dl_bound_relation.cpp @@ -0,0 +1,707 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_bound_relation.cpp + +Abstract: + + Basic (strict upper) bound relation. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ + +#include "dl_bound_relation.h" +#include "debug.h" +#include "ast_pp.h" + +namespace datalog { + + bound_relation_plugin::bound_relation_plugin(relation_manager& m): + relation_plugin(bound_relation_plugin::get_name(), m), + m_arith(get_ast_manager()), + m_bsimp(get_ast_manager()) { + } + + bool bound_relation_plugin::can_handle_signature(const relation_signature & sig) { + for (unsigned i = 0; i < sig.size(); ++i) { + if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { + return false; + } + } + return true; + } + + bound_relation& bound_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + bound_relation const & bound_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + bound_relation* bound_relation_plugin::get(relation_base* r) { + return dynamic_cast(r); + } + + bool bound_relation_plugin::is_interval_relation(relation_base const& r) { + return symbol("interval_relation") == r.get_plugin().get_name(); + } + + interval_relation& bound_relation_plugin::get_interval_relation(relation_base& r) { + SASSERT(is_interval_relation(r)); + return dynamic_cast(r); + } + + interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) { + SASSERT(is_interval_relation(r)); + return dynamic_cast(r); + } + + relation_base * bound_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(bound_relation, *this, s, true); + } + + relation_base * bound_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + return alloc(bound_relation, *this, s, false); + } + + class bound_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) { + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + bound_relation const& r1 = get(_r1); + bound_relation const& r2 = get(_r2); + bound_relation_plugin& p = r1.get_plugin(); + bound_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + return result; + } + }; + + relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(r1) || !check_kind(r2)) { + return 0; + } + return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); + } + + + class bound_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { + } + + virtual relation_base * operator()(const relation_base & _r) { + bound_relation const& r = get(_r); + bound_relation_plugin& p = r.get_plugin(); + bound_relation* result = get(p.mk_full(0, get_result_signature())); + result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); + return result; + } + }; + + relation_transformer_fn * bound_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + class bound_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { + } + + virtual relation_base * operator()(const relation_base & _r) { + bound_relation const& r = get(_r); + bound_relation_plugin& p = r.get_plugin(); + bound_relation* result = get(p.mk_full(0, get_result_signature())); + result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); + return result; + } + }; + + relation_transformer_fn * bound_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(check_kind(r)) { + return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); + } + return 0; + } + + + class bound_relation_plugin::union_fn : public relation_union_fn { + bool m_is_widen; + public: + union_fn(bool is_widen) : + m_is_widen(is_widen) { + } + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + get(_r).mk_union(get(_src), get(_delta), m_is_widen); + } + }; + + class bound_relation_plugin::union_fn_i : public relation_union_fn { + bool m_is_widen; + public: + union_fn_i(bool is_widen) : + m_is_widen(is_widen) { + } + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen); + TRACE("bound_relation", _r.display(tout << "dst':\n");); + } + }; + + + relation_union_fn * bound_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn_i, false); + } + if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn, false); + } + return 0; + } + + relation_union_fn * bound_relation_plugin::mk_widen_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn_i, true); + } + if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn, true); + } + return 0; + } + + class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_cols; + public: + filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_cols(col_cnt, identical_cols) {} + + virtual void operator()(relation_base & r) { + for (unsigned i = 1; i < m_cols.size(); ++i) { + get(r).equate(m_cols[0], m_cols[i]); + } + } + }; + + relation_mutator_fn * bound_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if(check_kind(t)) { + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + return 0; + } + + class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn { + public: + filter_equal_fn(relation_element const& value, unsigned col) {} + + virtual void operator()(relation_base & r) { } + }; + + relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if (check_kind(r)) { + return alloc(filter_equal_fn, value, col); + } + return 0; + } + + class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + enum kind_t { NOT_APPLICABLE, EQ_VAR, EQ_SUB, LT_VAR, LE_VAR, K_FALSE }; + app_ref m_cond; + app_ref m_lt; + arith_util m_arith; + interval_relation* m_interval; + unsigned_vector m_vars; + kind_t m_kind; + + unsigned get_var(expr* a) { + SASSERT(is_var(a)); + return to_var(a)->get_idx(); + } + + // x = z - y + void mk_sub_eq(expr* x, expr* z, expr* y) { + SASSERT(is_var(x)); + SASSERT(is_var(z)); + SASSERT(is_var(y)); + m_vars.push_back(get_var(x)); + m_vars.push_back(get_var(z)); + m_vars.push_back(get_var(y)); + m_kind = EQ_SUB; + } + + void mk_lt(expr* l, expr* r) { + SASSERT(is_var(l)); + SASSERT(is_var(r)); + m_vars.push_back(get_var(l)); + m_vars.push_back(get_var(r)); + m_lt = m_arith.mk_lt(l, r); + m_kind = LT_VAR; + } + + + void mk_le(expr* l, expr* r) { + SASSERT(is_var(l)); + SASSERT(is_var(r)); + m_vars.push_back(get_var(l)); + m_vars.push_back(get_var(r)); + m_kind = LE_VAR; + } + + void mk_eq(expr* l, expr* r) { + m_vars.push_back(get_var(l)); + m_vars.push_back(get_var(r)); + m_kind = EQ_VAR; + } + + public: + + filter_interpreted_fn(ast_manager& m, app* cond) : + m_cond(cond, m), + m_lt(m), m_arith(m), m_interval(0), m_kind(NOT_APPLICABLE) { + expr* l, *r, *r1, *r2, *c2; + rational n1; + if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) && + is_var(l) && is_var(r)) { + mk_lt(l, r); + } + else if (m.is_not(cond, c2) && + (m_arith.is_ge(c2, l, r) || m_arith.is_le(c2, r, l)) && + is_var(l) && is_var(r)) { + mk_lt(l, r); + } + else if ((m_arith.is_le(cond, l, r) || m_arith.is_ge(cond, r, l)) && + is_var(l) && is_var(r)) { + mk_le(l, r); + } + else if (m.is_not(cond, c2) && + (m_arith.is_gt(c2, l, r) || m_arith.is_lt(c2, r, l)) && + is_var(l) && is_var(r)) { + mk_le(l, r); + } + else if (m.is_false(cond)) { + m_kind = K_FALSE; + } + else if (m.is_eq(cond, l, r) && is_var(l) && is_var(r)) { + mk_eq(l, r); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_sub(r, r1, r2) && + is_var(l) && is_var(r1) && is_var(r2)) { + mk_sub_eq(l, r1, r2); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_sub(l, r1, r2) && + is_var(r) && is_var(r1) && is_var(r2)) { + mk_sub_eq(r, r1, r2); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_add(r, r1, r2) && + m_arith.is_numeral(r1, n1) && + n1.is_pos() && is_var(l) && is_var(r2)) { + mk_lt(r2, l); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_add(r, r1, r2) && + m_arith.is_numeral(r2, n1) && + n1.is_pos() && is_var(l) && is_var(r1)) { + mk_lt(r1, l); + } + else { + + } + } + + // + // x = z - y + // x = y + // x < y + // x <= y + // x < y + z + // + + void operator()(relation_base& t) { + TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); + bound_relation& r = get(t); + switch(m_kind) { + case K_FALSE: + r.set_empty(); + break; + case NOT_APPLICABLE: + break; + case EQ_VAR: + r.equate(m_vars[0], m_vars[1]); + break; + case EQ_SUB: + // TBD + break; + case LT_VAR: + r.mk_lt(m_vars[0], m_vars[1]); + break; + case LE_VAR: + r.mk_le(m_vars[0], m_vars[1]); + break; + default: + UNREACHABLE(); + break; + } + TRACE("dl", t.display(tout << "result\n");); + } + + bool supports_attachment(relation_base& t) { + return is_interval_relation(t); + } + + void attach(relation_base& t) { + SASSERT(is_interval_relation(t)); + interval_relation& r = get_interval_relation(t); + m_interval = &r; + } + }; + + relation_mutator_fn * bound_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + return alloc(filter_interpreted_fn, t.get_plugin().get_ast_manager(), condition); + } + + // ----------------------------- + // bound_relation + + void bound_relation_helper::mk_project_t(uint_set2& t, unsigned_vector const& renaming) { + if (t.lt.empty() && t.le.empty()) { + return; + } + uint_set::iterator it = t.lt.begin(), end = t.lt.end(); + unsigned_vector ltv, lev; + for (; it != end; ++it) { + ltv.push_back(renaming[*it]); + } + it = t.le.begin(), end = t.le.end(); + for (; it != end; ++it) { + lev.push_back(renaming[*it]); + } + TRACE("dl", + tout << "project: "; + for (unsigned i = 0; i < renaming.size(); ++i) + if (renaming[i] == UINT_MAX) tout << i << " "; + tout << ": "; + it = t.lt.begin(); end = t.lt.end(); + for (; it != end; ++it) tout << *it << " "; + tout << " le "; + it = t.le.begin(); end = t.le.end(); + for (; it != end; ++it) tout << *it << " "; + tout << " => "; + for (unsigned i = 0; i < ltv.size(); ++i) tout << ltv[i] << " "; + tout << " le "; + for (unsigned i = 0; i < lev.size(); ++i) tout << lev[i] << " "; + tout << "\n";); + t.lt.reset(); + for (unsigned i = 0; i < ltv.size(); ++i) { + t.lt.insert(ltv[i]); + } + t.le.reset(); + for (unsigned i = 0; i < lev.size(); ++i) { + t.le.insert(lev[i]); + } + } + + bound_relation::bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty): + vector_relation(p, s, is_empty, uint_set2()) + { + } + + + uint_set2 bound_relation::mk_intersect(uint_set2 const& t1, uint_set2 const& t2, bool& is_empty) const { + is_empty = false; + uint_set2 r(t1); + r.lt |= t2.lt; + r.le |= t2.le; + return r; + } + + uint_set2 bound_relation::mk_widen(uint_set2 const& t1, uint_set2 const& t2) const { + return mk_unite(t1, t2); + } + + uint_set2 bound_relation::mk_unite(uint_set2 const& t1, uint_set2 const& t2) const { + uint_set2 s1(t1); + s1.lt &= t2.lt; + s1.le &= t2.le; + return s1; + } + + uint_set2 bound_relation::mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, uint_set2 const& t) const { + unsigned sz = old_eqs.get_num_vars(); + SASSERT(sz == new_eqs.get_num_vars()); + uint_set2 result; + for (unsigned i = 0; i < sz; ++i) { + if (t.lt.contains(i)) { + unsigned j = i; + do { + result.lt.insert(new_eqs.find(j)); + j = old_eqs.next(j); + } + while (j != i); + } + if (t.le.contains(i)) { + unsigned j = i; + do { + result.le.insert(new_eqs.find(j)); + j = old_eqs.next(j); + } + while (j != i); + } + } + return result; + } + + bool bound_relation::is_subset_of(uint_set2 const& t1, uint_set2 const& t2) const { + uint_set2 s1, s2; + normalize(t1, s1); + normalize(t2, s2); + return s1.lt.subset_of(s2.lt) && s1.le.subset_of(s2.le); + } + + void bound_relation::mk_rename_elem(uint_set2& t, unsigned col_cnt, unsigned const* cycle) { + // [ 0 -> 2 -> 3 -> 0] + if (col_cnt == 0) return; + unsigned col1, col2; + col1 = find(cycle[0]); + col2 = find(cycle[col_cnt-1]); + bool has_col2_lt = t.lt.contains(col2); + t.lt.remove(col2); + bool has_col2_le = t.le.contains(col2); + t.le.remove(col2); + for (unsigned i = 0; i + 1 < col_cnt; ++i) { + col1 = find(cycle[i]); + col2 = find(cycle[i+1]); + if (t.lt.contains(col1)) { + t.lt.remove(col1); + t.lt.insert(col2); + } + if (t.le.contains(col1)) { + t.le.remove(col1); + t.le.insert(col2); + } + } + if (has_col2_lt) { + col1 = find(cycle[0]); + t.lt.insert(col1); + } + if (has_col2_le) { + col1 = find(cycle[0]); + t.le.insert(col1); + } + } + + + bool bound_relation::is_full(uint_set2 const& t) const { + return t.lt.empty() && t.le.empty(); + } + + bool bound_relation::is_empty(unsigned index, uint_set2 const& t) const { + return t.lt.contains(find(index)) || t.le.contains(find(index)); + } + + void bound_relation::normalize(uint_set const& src, uint_set& dst) const { + uint_set::iterator it = src.begin(), end = src.end(); + for (; it != end; ++it) { + dst.insert(find(*it)); + } + } + void bound_relation::normalize(uint_set2 const& src, uint_set2& dst) const { + normalize(src.lt, dst.lt); + normalize(src.le, dst.le); + } + + + void bound_relation::mk_lt(unsigned i) { + uint_set2& dst = (*this)[i]; + while (!m_todo.empty()) { + unsigned j = m_todo.back().first; + bool strict = m_todo.back().second; + if (i == j && strict) { + m_todo.reset(); + m_empty = true; + return; + } + m_todo.pop_back(); + if (i == j) { + continue; + } + uint_set2& src = (*m_elems)[j]; + uint_set::iterator it = src.lt.begin(), end = src.lt.end(); + for(; it != end; ++it) { + m_todo.push_back(std::make_pair(*it, true)); + } + it = src.le.begin(), end = src.le.end(); + for(; it != end; ++it) { + m_todo.push_back(std::make_pair(*it, strict)); + } + if (strict) { + dst.lt.insert(j); + } + else { + dst.le.insert(j); + } + } + } + + void bound_relation::mk_lt(unsigned i, unsigned j) { + m_todo.reset(); + i = find(i); + m_todo.push_back(std::make_pair(find(j), true)); + mk_lt(i); + } + + void bound_relation::mk_le(unsigned i, unsigned j) { + m_todo.reset(); + i = find(i); + m_todo.push_back(std::make_pair(find(j), false)); + mk_lt(i); + } + + bool bound_relation::is_lt(unsigned i, unsigned j) const { + return (*this)[i].lt.contains(find(j)); + } + + void bound_relation::add_fact(const relation_fact & f) { + bound_relation r(get_plugin(), get_signature(), false); + for (unsigned i = 0; i < f.size(); ++i) { + scoped_ptr fe = get_plugin().mk_filter_equal_fn(r, f[i], i); + (*fe)(r); + } + mk_union(r, 0, false); + } + + bool bound_relation::contains_fact(const relation_fact & f) const { + if (empty()) { + return false; + } + // this is a very rough approximation. + return true; + } + + bound_relation * bound_relation::clone() const { + bound_relation* result = 0; + if (empty()) { + result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature())); + } + else { + result = bound_relation_plugin::get(get_plugin().mk_full(0, get_signature())); + result->copy(*this); + } + return result; + } + + void bound_relation::mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen) { + unsigned size = get_signature().size(); + for (unsigned i = 0; i < size; ++i) { + if (find(i) != i) { + continue; + } + uint_set2& s = (*this)[i]; + ext_numeral const& lo = src[i].sup(); + if (lo.is_infinite()) { + s.lt.reset(); + s.le.reset(); + continue; + } + uint_set::iterator it = s.lt.begin(), end = s.lt.end(); + for(; it != end; ++it) { + ext_numeral const& hi = src[*it].inf(); + if (hi.is_infinite() || lo.to_rational() >= hi.to_rational()) { + s.lt.remove(*it); + } + } + it = s.le.begin(), end = s.le.end(); + for(; it != end; ++it) { + ext_numeral const& hi = src[*it].inf(); + if (hi.is_infinite() || lo.to_rational() > hi.to_rational()) { + s.le.remove(*it); + } + } + } + } + + bound_relation * bound_relation::complement(func_decl* p) const { + UNREACHABLE(); + return 0; + } + + void bound_relation::to_formula(expr_ref& fml) const { + ast_manager& m = get_plugin().get_ast_manager(); + arith_util& arith = get_plugin().m_arith; + basic_simplifier_plugin& bsimp = get_plugin().m_bsimp; + expr_ref_vector conjs(m); + relation_signature const& sig = get_signature(); + for (unsigned i = 0; i < sig.size(); ++i) { + if (i != find(i)) { + conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)]))); + continue; + } + uint_set2 const& upper = (*this)[i]; + uint_set::iterator it = upper.lt.begin(), end = upper.lt.end(); + for (; it != end; ++it) { + conjs.push_back(arith.mk_lt(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); + } + it = upper.le.begin(), end = upper.le.end(); + for (; it != end; ++it) { + conjs.push_back(arith.mk_le(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); + } + } + bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml); + } + + + void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const { + uint_set::iterator it = src.lt.begin(), end = src.lt.end(); + out << "#" << i; + if (!src.lt.empty()) { + out << " < "; + for(; it != end; ++it) { + out << *it << " "; + } + } + if (!src.le.empty()) { + it = src.le.begin(), end = src.le.end(); + out << " <= "; + for(; it != end; ++it) { + out << *it << " "; + } + } + if (src.lt.empty() && src.le.empty()) { + out << " < oo"; + } + out << "\n"; + } + + bound_relation_plugin& bound_relation::get_plugin() const { + return dynamic_cast(relation_base::get_plugin()); + } + + +}; + + diff --git a/src/muz/rel/dl_bound_relation.h b/src/muz/rel/dl_bound_relation.h new file mode 100644 index 000000000..906ba571a --- /dev/null +++ b/src/muz/rel/dl_bound_relation.h @@ -0,0 +1,178 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_bound_relation.h + +Abstract: + + Basic (strict upper) bound relation. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ +#ifndef _DL_BOUND_RELATION_H_ +#define _DL_BOUND_RELATION_H_ + +#include "dl_context.h" +#include "dl_relation_manager.h" +#include "dl_base.h" +#include "uint_set.h" +#include "dl_vector_relation.h" +#include "dl_interval_relation.h" +#include "arith_decl_plugin.h" +#include "basic_simplifier_plugin.h" + +namespace datalog { + + class bound_relation; + + class bound_relation_plugin : public relation_plugin { + friend class bound_relation; + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class union_fn_i; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class filter_intersection_fn; + arith_util m_arith; + basic_simplifier_plugin m_bsimp; + public: + bound_relation_plugin(relation_manager& m); + virtual bool can_handle_signature(const relation_signature & s); + static symbol get_name() { return symbol("bound_relation"); } + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + + +#if 0 + virtual intersection_filter_fn * mk_filter_by_intersection_fn( + const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * src_cols) { + return 0; + } +#endif + + static bound_relation* get(relation_base* r); + private: + static bound_relation& get(relation_base& r); + static bound_relation const & get(relation_base const& r); + + + static bool is_interval_relation(relation_base const& r); + static interval_relation& get_interval_relation(relation_base& r); + static interval_relation const& get_interval_relation(relation_base const& r); + }; + + struct uint_set2 { + uint_set lt; + uint_set le; + uint_set2(uint_set2 const& other):lt(other.lt), le(other.le) {} + uint_set2() {} + bool operator==(const uint_set2& other) const { + return other.lt == lt && other.le == le; + } + bool operator!=(const uint_set2& other) const { + return other.lt != lt || other.le != le; + } + }; + + inline std::ostream & operator<<(std::ostream & target, const uint_set2 & s) { + return target << s.lt << " " << s.le; + } + + + class bound_relation_helper { + public: + static void mk_project_t(uint_set2& t, unsigned_vector const& renaming); + }; + + class bound_relation : public vector_relation { + friend class bound_relation_plugin; + svector > m_todo; + + public: + bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty); + bound_relation& operator=(bound_relation const& other); + + virtual bool empty() const { return m_empty; } + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual bound_relation * clone() const; + virtual bound_relation * complement(func_decl* p) const; + virtual void to_formula(expr_ref& fml) const; + bound_relation_plugin& get_plugin() const; + + void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen); + + void mk_lt(unsigned i, unsigned j); + + void mk_lt(unsigned i); + + void mk_le(unsigned i, unsigned j); + + bool is_lt(unsigned i, unsigned j) const; + + virtual bool is_precise() const { return false; } + + private: + typedef uint_set2 T; + virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const; + + virtual T mk_widen(T const& t1, T const& t2) const; + + virtual T mk_unite(T const& t1, T const& t2) const; + + virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const; + + virtual void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle); + + + virtual bool is_subset_of(T const& t1, T const& t2) const; + + virtual bool is_full(T const& t) const; + + virtual bool is_empty(unsigned idx, T const& t) const; + + virtual void display_index(unsigned idx, T const& t, std::ostream& out) const; + + void normalize(T const& src, T& dst) const; + + void normalize(uint_set const& src, uint_set& dst) const; + + + + }; + +}; + +#endif + diff --git a/src/muz/rel/dl_check_table.cpp b/src/muz/rel/dl_check_table.cpp new file mode 100644 index 000000000..ea4003e5f --- /dev/null +++ b/src/muz/rel/dl_check_table.cpp @@ -0,0 +1,439 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_check_table.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-11-15 + + +Revision History: + +--*/ + + +#include "dl_check_table.h" +#include "dl_table.h" + + +namespace datalog { + + bool check_table_plugin::can_handle_signature(table_signature const& s) { + return m_tocheck.can_handle_signature(s) && m_checker.can_handle_signature(s); + } + + + check_table & check_table_plugin::get(table_base& r) { + return static_cast(r); + } + + check_table const & check_table_plugin::get(table_base const& r) { + return static_cast(r); + } + + table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; } + table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; } + table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):0; } + table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):0; } + table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; } + table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; } + table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):0; } + table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):0; } + + table_base * check_table_plugin::mk_empty(const table_signature & s) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + table_base* checker = m_checker.mk_empty(s); + table_base* tocheck = m_tocheck.mk_empty(s); + return alloc(check_table, *this, s, tocheck, checker); + } + + class check_table_plugin::join_fn : public table_join_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + join_fn(check_table_plugin& p, + const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + m_tocheck = p.get_manager().mk_join_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2); + m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2); + } + + virtual table_base* operator()(const table_base & t1, const table_base & t2) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); + table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); + check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); + } + + class check_table_plugin::join_project_fn : public table_join_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + + virtual table_base* operator()(const table_base & t1, const table_base & t2) { + table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); + table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); + check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + + class check_table_plugin::union_fn : public table_union_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + union_fn(check_table_plugin& p, table_base const& tgt, const table_base& src, table_base const* delta) { + m_tocheck = p.get_manager().mk_union_fn(tocheck(tgt), tocheck(src), tocheck(delta)); + m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta)); + } + + virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + (*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta)); + (*m_checker)(checker(tgt), checker(src), checker(delta)); + get(tgt).well_formed(); + if (delta) { + get(*delta).well_formed(); + } + } + }; + + table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, *this, tgt, src, delta); + + } + + class check_table_plugin::project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + project_fn(check_table_plugin& p, const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { + m_checker = p.get_manager().mk_project_fn(checker(t), col_cnt, removed_cols); + m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { + if (!check_kind(t)) { + return 0; + } + return alloc(project_fn, *this, t, col_cnt, removed_cols); + } + + class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) { + m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col); + m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + if (!check_kind(t)) { + return 0; + } + return alloc(select_equal_and_project_fn, *this, t, value, col); + } + + class check_table_plugin::rename_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + rename_fn(check_table_plugin& p, const table_base & t, unsigned cycle_len, unsigned const* cycle) { + m_checker = p.get_manager().mk_rename_fn(checker(t), cycle_len, cycle); + m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle); + } + + table_base* operator()(table_base const& src) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { + if (!check_kind(t)) { + return 0; + } + return alloc(rename_fn, *this, t, len, cycle); + } + + class check_table_plugin::filter_identical_fn : public table_mutator_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_identical_fn(check_table_plugin& p, const table_base & t,unsigned cnt, unsigned const* cols) + { + m_checker = p.get_manager().mk_filter_identical_fn(checker(t), cnt, cols); + m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols); + } + + void operator()(table_base & t) { + (*m_checker)(checker(t)); + (*m_tocheck)(tocheck(t)); + get(t).well_formed(); + } + }; + + table_mutator_fn * check_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt, + const unsigned * identical_cols) { + if (check_kind(t)) { + return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols); + } + return 0; + } + + class check_table_plugin::filter_equal_fn : public table_mutator_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_equal_fn(check_table_plugin& p, const table_base & t, const table_element & v, unsigned col) + { + m_checker = p.get_manager().mk_filter_equal_fn(checker(t), v, col); + m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col); + } + + virtual void operator()(table_base& src) { + (*m_checker)(checker(src)); + (*m_tocheck)(tocheck(src)); + get(src).well_formed(); + } + }; + + table_mutator_fn * check_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) { + if (check_kind(t)) { + return alloc(filter_equal_fn, *this, t, value, col); + } + return 0; + } + + class check_table_plugin::filter_interpreted_fn : public table_mutator_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_interpreted_fn(check_table_plugin& p, const table_base & t, app * condition) + { + m_checker = p.get_manager().mk_filter_interpreted_fn(checker(t), condition); + m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition); + } + + virtual void operator()(table_base& src) { + (*m_checker)(checker(src)); + (*m_tocheck)(tocheck(src)); + get(src).well_formed(); + } + }; + + table_mutator_fn * check_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { + if (check_kind(t)) { + return alloc(filter_interpreted_fn, *this, t, condition); + } + return 0; + } + + class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) + { + m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols); + m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (check_kind(t)) { + return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); + } + return 0; + } + + class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_by_negation_fn( + check_table_plugin& p, + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + m_checker = p.get_manager().mk_filter_by_negation_fn(checker(t), checker(negated_obj), joined_col_cnt, t_cols, negated_cols); + m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols); + } + + virtual void operator()(table_base& src, table_base const& negated_obj) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + (*m_checker)(checker(src), checker(negated_obj)); + (*m_tocheck)(tocheck(src), tocheck(negated_obj)); + get(src).well_formed(); + } + + }; + + table_intersection_filter_fn * check_table_plugin::mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + if (check_kind(t) && check_kind(negated_obj)) { + return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + return 0; + } + + // ------------------ + // check_table + + + check_table::check_table(check_table_plugin & p, const table_signature & sig): + table_base(p, sig) { + (well_formed()); + } + + check_table::check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker): + table_base(p, sig), + m_checker(checker), + m_tocheck(tocheck) { + well_formed(); + } + + check_table::~check_table() { + m_tocheck->deallocate(); + m_checker->deallocate(); + } + + bool check_table::well_formed() const { + get_plugin().m_count++; + iterator it = m_tocheck->begin(), end = m_tocheck->end(); + for (; it != end; ++it) { + table_fact fact; + it->get_fact(fact); + if (!m_checker->contains_fact(fact)) { + m_tocheck->display(verbose_stream()); + m_checker->display(verbose_stream()); + verbose_stream() << get_plugin().m_count << "\n"; + UNREACHABLE(); + fatal_error(0); + return false; + } + } + iterator it2 = m_checker->begin(), end2 = m_checker->end(); + for (; it2 != end2; ++it2) { + table_fact fact; + it2->get_fact(fact); + if (!m_tocheck->contains_fact(fact)) { + m_tocheck->display(verbose_stream()); + m_checker->display(verbose_stream()); + verbose_stream() << get_plugin().m_count << "\n"; + UNREACHABLE(); + fatal_error(0); + return false; + } + } + return true; + } + + bool check_table::empty() const { + if (m_tocheck->empty() != m_checker->empty()) { + m_tocheck->display(verbose_stream()); + m_checker->display(verbose_stream()); + verbose_stream() << get_plugin().m_count << "\n"; + fatal_error(0); + } + return m_tocheck->empty(); + } + + + void check_table::add_fact(const table_fact & f) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + m_tocheck->add_fact(f); + m_checker->add_fact(f); + well_formed(); + } + + void check_table::remove_fact(const table_element* f) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + m_tocheck->remove_fact(f); + m_checker->remove_fact(f); + well_formed(); + } + + bool check_table::contains_fact(const table_fact & f) const { + return m_checker->contains_fact(f); + } + + table_base * check_table::clone() const { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->clone(), m_checker->clone()); + return result; + } + + table_base * check_table::complement(func_decl* p, const table_element * func_columns) const { + check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p, func_columns), m_checker->complement(p, func_columns)); + return result; + } + +}; + diff --git a/src/muz/rel/dl_check_table.h b/src/muz/rel/dl_check_table.h new file mode 100644 index 000000000..e4f439590 --- /dev/null +++ b/src/muz/rel/dl_check_table.h @@ -0,0 +1,135 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_check_table.h + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-11-15 + + +Revision History: + +--*/ + +#ifndef _DL_CHECK_TABLE_H_ +#define _DL_CHECK_TABLE_H_ + +#include "dl_base.h" +#include "dl_decl_plugin.h" +#include "dl_relation_manager.h" + +namespace datalog { + class check_table; + + class check_table_plugin : public table_plugin { + friend class check_table; + table_plugin& m_checker; + table_plugin& m_tocheck; + unsigned m_count; + protected: + class join_fn; + class join_project_fn; + class union_fn; + class transformer_fn; + class rename_fn; + class project_fn; + class select_equal_and_project_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class filter_interpreted_and_project_fn; + class filter_by_negation_fn; + + public: + check_table_plugin(relation_manager & manager, symbol const& checker, symbol const& tocheck) + : table_plugin(symbol("check"), manager), + m_checker(*manager.get_table_plugin(checker)), + m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {} + + virtual table_base * mk_empty(const table_signature & s); + + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); + virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, + unsigned col); + virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); + virtual table_intersection_filter_fn * mk_filter_by_negation_fn( + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + + virtual bool can_handle_signature(table_signature const& s); + + private: + static check_table& get(table_base& r); + + static check_table const & get(table_base const& r); + + static table_base& checker(table_base& r); + static table_base const& checker(table_base const& r); + static table_base* checker(table_base* r); + static table_base const* checker(table_base const* r); + static table_base& tocheck(table_base& r); + static table_base const& tocheck(table_base const& r); + static table_base* tocheck(table_base* r); + static table_base const* tocheck(table_base const* r); + }; + + class check_table : public table_base { + friend class check_table_plugin; + + table_base* m_checker; + table_base* m_tocheck; + + check_table(check_table_plugin & p, const table_signature & sig); + check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker); + + virtual ~check_table(); + + bool well_formed() const; + + public: + + check_table_plugin & get_plugin() const { + return static_cast(table_base::get_plugin()); + } + + virtual bool empty() const; + virtual void add_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual bool contains_fact(const table_fact & f) const; + virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; + virtual table_base * clone() const; + + virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); } + virtual iterator end() const { return m_tocheck->end(); } + + virtual unsigned get_size_estimate_rows() const { return m_tocheck->get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return m_tocheck->get_size_estimate_bytes(); } + }; + + }; + + #endif /* _DL_CHECK_TABLE_H_ */ diff --git a/src/muz/rel/dl_external_relation.cpp b/src/muz/rel/dl_external_relation.cpp new file mode 100644 index 000000000..f32509473 --- /dev/null +++ b/src/muz/rel/dl_external_relation.cpp @@ -0,0 +1,456 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_external_relation.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-05-10 + +Revision History: + +--*/ + +#include "debug.h" +#include "ast_pp.h" +#include "dl_context.h" +#include "dl_external_relation.h" +#include "dl_decl_plugin.h" + +namespace datalog { + + external_relation::external_relation(external_relation_plugin & p, const relation_signature & s, expr* r) + : relation_base(p, s), + m_rel(r, p.get_ast_manager()), + m_select_fn(p.get_ast_manager()), + m_store_fn(p.get_ast_manager()), + m_is_empty_fn(p.get_ast_manager()) + { + } + + external_relation::~external_relation() { + } + + void external_relation::mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const { + ast_manager& m = m_rel.get_manager(); + family_id fid = get_plugin().get_family_id(); + ptr_vector args; + args.push_back(m_rel); + for (unsigned i = 0; i < f.size(); ++i) { + args.push_back(f[i]); + } + if (!fn.get()) { + fn = m.mk_func_decl(fid, k, 0, 0, args.size(), args.c_ptr()); + } + if (destructive) { + get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr()); + res = m_rel; + } + else { + get_plugin().reduce(fn, args.size(), args.c_ptr(), res); + } + } + + bool external_relation::empty() const { + ast_manager& m = m_rel.get_manager(); + expr* r = m_rel.get(); + expr_ref res(m); + if (!m_is_empty_fn.get()) { + family_id fid = get_plugin().get_family_id(); + const_cast(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, 0, 1, &r); + } + get_plugin().reduce(m_is_empty_fn, 1, &r, res); + return m.is_true(res); + } + + void external_relation::add_fact(const relation_fact & f) { + mk_accessor(OP_RA_STORE, m_store_fn, f, true, m_rel); + } + + bool external_relation::contains_fact(const relation_fact & f) const { + ast_manager& m = get_plugin().get_ast_manager(); + expr_ref res(m); + mk_accessor(OP_RA_SELECT, const_cast(m_select_fn), f, false, res); + return !m.is_false(res); + } + + external_relation * external_relation::clone() const { + ast_manager& m = m_rel.get_manager(); + family_id fid = get_plugin().get_family_id(); + expr* rel = m_rel.get(); + expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m); + expr* rel_out = res.get(); + func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,0, 1, &rel), m); + get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out); + return alloc(external_relation, get_plugin(), get_signature(), res); + } + + external_relation * external_relation::complement(func_decl* p) const { + ast_manager& m = m_rel.get_manager(); + family_id fid = get_plugin().get_family_id(); + expr_ref res(m); + expr* rel = m_rel; + func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,0, 1, &rel), m); + get_plugin().reduce(fn, 1, &rel, res); + return alloc(external_relation, get_plugin(), get_signature(), res); + } + + void external_relation::display(std::ostream & out) const { + out << mk_pp(m_rel, m_rel.get_manager()) << "\n"; + } + + void external_relation::display_tuples(func_decl & pred, std::ostream & out) const { + display(out); + } + + + external_relation_plugin & external_relation::get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + + // ----------------------------------- + // + // external_relation_plugin + // + // ----------------------------------- + + + external_relation_plugin::external_relation_plugin(external_relation_context& ctx, relation_manager & m) + : relation_plugin(external_relation_plugin::get_name(), m), m_ext(ctx) {} + + external_relation const & external_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + external_relation & external_relation_plugin::get(relation_base & r) { + return dynamic_cast(r); + } + + relation_base * external_relation_plugin::mk_empty(const relation_signature & s) { + ast_manager& m = get_ast_manager(); + sort* r_sort = get_relation_sort(s); + parameter param(r_sort); + family_id fid = get_family_id(); + expr_ref e(m.mk_fresh_const("T", r_sort), m); + expr* args[1] = { e.get() }; + func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)0), m); + reduce_assign(empty_decl, 0, 0, 1, args); + return alloc(external_relation, *this, s, e); + } + + sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) { + vector sorts; + ast_manager& m = get_ast_manager(); + family_id fid = get_family_id(); + for (unsigned i = 0; i < sig.size(); ++i) { + sorts.push_back(parameter(sig[i])); + } + return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr()); + } + + sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) { + SASSERT(s->get_num_parameters() > col); + SASSERT(s->get_parameter(col).is_ast()); + SASSERT(is_sort(s->get_parameter(col).get_ast())); + return to_sort(s->get_parameter(col).get_ast()); + } + + family_id external_relation_plugin::get_family_id() { + return m_ext.get_family_id(); + } + + + void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) { + ast_manager& m = get_ast_manager(); + family_id fid = get_family_id(); + parameter param(condition); + f = m.mk_func_decl(fid, OP_RA_FILTER, 1, ¶m, 1, &s); + } + + class external_relation_plugin::join_fn : public convenient_relation_join_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_join_fn; + expr* m_args[2]; + public: + join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), + m_plugin(p), + m_join_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + vector params; + for (unsigned i = 0; i < col_cnt; ++i) { + params.push_back(parameter(cols1[i])); + params.push_back(parameter(cols2[i])); + } + sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) }; + m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain); + } + + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + expr_ref res(m_plugin.get_ast_manager()); + m_args[0] = get(r1).get_relation(); + m_args[1] = get(r2).get_relation(); + m_plugin.reduce(m_join_fn, 2, m_args, res); + return alloc(external_relation, m_plugin, get_result_signature(), res); + } + }; + + relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(r1) || !check_kind(r2)) { + return 0; + } + return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2); + } + + + class external_relation_plugin::project_fn : public convenient_relation_project_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_project_fn; + public: + project_fn(external_relation_plugin& p, sort* relation_sort, + const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols), + m_plugin(p), + m_project_fn(p.get_ast_manager()) { + vector params; + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + for (unsigned i = 0; i < removed_col_cnt; ++i) { + params.push_back(parameter(removed_cols[i])); + } + m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort); + } + + virtual relation_base * operator()(const relation_base & r) { + expr_ref res(m_plugin.get_ast_manager()); + expr* rel = get(r).get_relation(); + m_plugin.reduce(m_project_fn, 1, &rel, res); + return alloc(external_relation, m_plugin, get_result_signature(), to_app(res)); + } + }; + + relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols); + } + + + class external_relation_plugin::rename_fn : public convenient_relation_rename_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_rename_fn; + expr* m_args[2]; + public: + rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), + m_plugin(p), + m_rename_fn(p.get_ast_manager()) { + + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + vector params; + for (unsigned i = 0; i < cycle_len; ++i) { + SASSERT(cycle[i] < orig_sig.size()); + params.push_back(parameter(cycle[i])); + } + m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort); + } + + virtual relation_base * operator()(const relation_base & r) { + expr* rel = get(r).get_relation(); + expr_ref res(m_plugin.get_ast_manager()); + m_args[0] = rel; + m_plugin.reduce(m_rename_fn, 1, &rel, res); + return alloc(external_relation, m_plugin, get_result_signature(), res); + } + }; + + relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(!check_kind(r)) { + return 0; + } + return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle); + } + + + class external_relation_plugin::union_fn : public relation_union_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_union_fn; + expr* m_args[2]; + expr* m_outs[2]; + + public: + union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort): + m_plugin(p), + m_union_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + sort* domain[2] = { relation_sort, relation_sort }; + m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain); + } + + virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) { + ast_manager& m = m_plugin.get_ast_manager(); + expr_ref_vector res(m); + m_args[0] = get(r).get_relation(); + m_args[1] = get(src).get_relation(); + m_outs[0] = m_args[0]; + unsigned num_out = 1; + if (delta) { + m_outs[1] = get(*delta).get_relation(); + ++num_out; + } + m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs); + } + }; + + relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort()); + } + + relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort()); + } + + class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + external_relation_plugin& m_plugin; + app_ref m_condition; + func_decl_ref m_filter_fn; + public: + filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition) + : m_plugin(p), + m_condition(condition, p.get_ast_manager()), + m_filter_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + p.mk_filter_fn(relation_sort, condition, m_filter_fn); + SASSERT(m.is_bool(condition)); + } + + virtual void operator()(relation_base & r) { + SASSERT(m_plugin.check_kind(r)); + expr* arg = get(r).get_relation(); + m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg); + } + }; + + relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) { + if(!check_kind(r)) { + return 0; + } + return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition); + } + + relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if(!check_kind(r)) { + return 0; + } + ast_manager& m = get_ast_manager(); + app_ref condition(m); + expr_ref var(m); + sort* relation_sort = get(r).get_sort(); + sort* column_sort = get_column_sort(col, relation_sort); + var = m.mk_var(col, column_sort); + condition = m.mk_eq(var, value); + return mk_filter_interpreted_fn(r, condition); + } + + class external_relation_plugin::filter_identical_fn : public relation_mutator_fn { + external_relation_plugin& m_plugin; + func_decl_ref_vector m_filter_fn; + public: + filter_identical_fn(external_relation_plugin& p, sort* relation_sort, + unsigned col_cnt, const unsigned * identical_cols) + : m_plugin(p), m_filter_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + func_decl_ref fn(m); + app_ref eq(m); + if (col_cnt <= 1) { + return; + } + unsigned col = identical_cols[0]; + sort* s = p.get_column_sort(col, relation_sort); + var* v0 = m.mk_var(col, s); + for (unsigned i = 1; i < col_cnt; ++i) { + col = identical_cols[i]; + s = p.get_column_sort(col, relation_sort); + eq = m.mk_eq(v0, m.mk_var(col, s)); + p.mk_filter_fn(relation_sort, eq.get(), fn); + m_filter_fn.push_back(fn); + } + } + + virtual void operator()(relation_base & r) { + expr* r0 = get(r).get_relation(); + for (unsigned i = 0; i < m_filter_fn.size(); ++i) { + m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0); + } + } + }; + + relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r, + unsigned col_cnt, const unsigned * identical_cols) { + if (!check_kind(r)) { + return 0; + } + return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols); + } + + + class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_negated_filter_fn; + expr* m_args[2]; + public: + negation_filter_fn(external_relation_plugin& p, + const relation_base & tgt, const relation_base & neg_t, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : + convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), + m_plugin(p), + m_negated_filter_fn(p.get_ast_manager()) + { + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + vector params; + for (unsigned i = 0; i < joined_col_cnt; ++i) { + params.push_back(parameter(t_cols[i])); + params.push_back(parameter(negated_cols[i])); + } + sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() }; + m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain); + } + + void operator()(relation_base & t, const relation_base & negated_obj) { + m_args[0] = get(t).get_relation(); + m_args[1] = get(negated_obj).get_relation(); + m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args); + } + }; + + relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + if (!check_kind(t) || !check_kind(negated_obj)) { + return 0; + } + return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + +}; diff --git a/src/muz/rel/dl_external_relation.h b/src/muz/rel/dl_external_relation.h new file mode 100644 index 000000000..03838ecc4 --- /dev/null +++ b/src/muz/rel/dl_external_relation.h @@ -0,0 +1,154 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_external_relation.h + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-05-10 + +Revision History: + +--*/ +#ifndef _DL_EXTERNAL_RELATION_H_ +#define _DL_EXTERNAL_RELATION_H_ + +#include "dl_base.h" + +namespace datalog { + + class external_relation; + + class external_relation_context { + public: + virtual ~external_relation_context() {} + + virtual family_id get_family_id() const = 0; + + // reduce arguments. + virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) = 0; + + // overwrite terms passed in outs vector with values computed by function. + virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) = 0; + }; + + class external_relation_plugin : public relation_plugin { + + friend class external_relation; + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class negation_filter_fn; + + external_relation_context& m_ext; + + public: + external_relation_plugin(external_relation_context& ctx, relation_manager & m); + + virtual bool can_handle_signature(const relation_signature & s) { return true; } + + static symbol get_name() { return symbol("external_relation"); } + + virtual relation_base * mk_empty(const relation_signature & s); + + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + + private: + + static external_relation& get(relation_base& r); + static external_relation const & get(relation_base const& r); + + void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { + m_ext.reduce(f, num_args, args, result); + } + + void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { + m_ext.reduce_assign(f, num_args, args, num_out, outs); + } + + sort* get_relation_sort(relation_signature const& sig); + + sort* get_column_sort(unsigned col, sort* relation_sort); + + void mk_filter_fn(sort* s, app* condition, func_decl_ref& f); + + family_id get_family_id(); + }; + + class external_relation : public relation_base { + friend class external_relation_plugin; + friend class external_relation_plugin::join_fn; + friend class external_relation_plugin::project_fn; + friend class external_relation_plugin::rename_fn; + friend class external_relation_plugin::union_fn; + friend class external_relation_plugin::filter_identical_fn; + friend class external_relation_plugin::filter_interpreted_fn; + friend class external_relation_plugin::negation_filter_fn; + + expr_ref m_rel; + func_decl_ref m_select_fn; + func_decl_ref m_store_fn; + func_decl_ref m_is_empty_fn; + + unsigned size() const { return get_signature().size(); } + + sort* get_sort() const { return m_rel.get_manager().get_sort(m_rel); } + + void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const; + + external_relation(external_relation_plugin & p, const relation_signature & s, expr* r); + virtual ~external_relation(); + + public: + external_relation_plugin & get_plugin() const; + + virtual bool empty() const; + + virtual void add_fact(const relation_fact & f); + + virtual bool contains_fact(const relation_fact & f) const; + + virtual external_relation * clone() const; + + virtual external_relation * complement(func_decl*) const; + + virtual void display(std::ostream & out) const; + + virtual void display_tuples(func_decl & pred, std::ostream & out) const; + + expr* get_relation() const { return m_rel.get(); } + + virtual void to_formula(expr_ref& fml) const { fml = get_relation(); } + + }; + + +}; + +#endif diff --git a/src/muz/rel/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp new file mode 100644 index 000000000..86fef433b --- /dev/null +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -0,0 +1,2377 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_finite_product_relation.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include +#include"dl_context.h" +#include"dl_relation_manager.h" +#include"dl_table_relation.h" +#include"dl_finite_product_relation.h" +#include"bool_rewriter.h" + +namespace datalog { + + //static variables + + const table_sort finite_product_relation::s_rel_idx_sort = INT_MAX; + + void universal_delete(finite_product_relation* ptr) { + ptr->deallocate(); + } + + + // ----------------------------------- + // + // finite_product_relation_plugin + // + // ----------------------------------- + + finite_product_relation & finite_product_relation_plugin::get(relation_base & r) { + SASSERT(r.get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + const finite_product_relation & finite_product_relation_plugin::get(const relation_base & r) { + SASSERT(r.get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + finite_product_relation * finite_product_relation_plugin::get(relation_base * r) { + SASSERT(!r || r->get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + const finite_product_relation * finite_product_relation_plugin::get(const relation_base * r) { + SASSERT(!r || r->get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) { + std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str(); + return symbol(str.c_str()); + } + + finite_product_relation_plugin & finite_product_relation_plugin::get_plugin(relation_manager & rmgr, + relation_plugin & inner) { + finite_product_relation_plugin * res; + if(!rmgr.try_get_finite_product_relation_plugin(inner, res)) { + res = alloc(finite_product_relation_plugin, inner, rmgr); + rmgr.register_plugin(res); + } + return *res; + } + + finite_product_relation_plugin::finite_product_relation_plugin(relation_plugin & inner_plugin, + relation_manager & manager) + : relation_plugin(get_name(inner_plugin), manager, ST_FINITE_PRODUCT_RELATION), + m_inner_plugin(inner_plugin), m_spec_store(*this) { + } + + void finite_product_relation_plugin::initialize(family_id fid) { + relation_plugin::initialize(fid); + m_spec_store.add_available_kind(get_kind()); + } + + family_id finite_product_relation_plugin::get_relation_kind(finite_product_relation & r, + const bool * table_columns) { + const relation_signature & sig = r.get_signature(); + svector table_cols_vect(sig.size(), table_columns); + return m_spec_store.get_relation_kind(sig, rel_spec(table_cols_vect)); + } + + void finite_product_relation_plugin::get_all_possible_table_columns(relation_manager & rmgr, + const relation_signature & s, svector & table_columns) { + SASSERT(table_columns.empty()); + unsigned s_sz = s.size(); + for(unsigned i=0; i table_columns; + get_all_possible_table_columns(s, table_columns); +#ifndef _EXTERNAL_RELEASE + unsigned s_sz = s.size(); + unsigned rel_col_cnt = 0; + for(unsigned i=0; icomplement_self(p); + return res; + } + + bool finite_product_relation_plugin::can_convert_to_table_relation(const finite_product_relation & r) { + return r.m_other_sig.empty(); + } + + table_relation * finite_product_relation_plugin::to_table_relation(const finite_product_relation & r) { + SASSERT(can_convert_to_table_relation(r)); + r.garbage_collect(true); + //now all rows in the table will correspond to rows in the resulting table_relation + + const table_base & t = r.get_table(); + + unsigned removed_col = t.get_signature().size()-1; + scoped_ptr project_fun = + get_manager().mk_project_fn(r.get_table(), 1, &removed_col); + + table_base * res_table = (*project_fun)(t); + SASSERT(res_table->get_signature().functional_columns()==0); + return static_cast(get_manager().mk_table_relation(r.get_signature(), res_table)); + } + + + bool finite_product_relation_plugin::can_be_converted(const relation_base & r) { + if(&r.get_plugin()==&get_inner_plugin()) { + //can be converted by mk_from_inner_relation + return true; + } + if(r.from_table()) { + //We can convert directly from table plugin only if the inner plugin can handle empty signatures. + + //TODO: If the inner plugin cannot handle empty signatures, we may try to move some of the + //table columns into the inner relation signature. + return get_inner_plugin().can_handle_signature(relation_signature()); + } + return false; + } + + finite_product_relation * finite_product_relation_plugin::mk_from_table_relation(const table_relation & r) { + func_decl* pred = 0; + const relation_signature & sig = r.get_signature(); + const table_base & t = r.get_table(); + table_plugin & tplugin = r.get_table().get_plugin(); + + relation_signature inner_sig; //empty signature for the inner relation + if(!get_inner_plugin().can_handle_signature(inner_sig)) { + return 0; + } + + table_signature idx_singleton_sig; + idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); + idx_singleton_sig.set_functional_columns(1); + + scoped_rel idx_singleton; + if(tplugin.can_handle_signature(idx_singleton_sig)) { + idx_singleton = tplugin.mk_empty(idx_singleton_sig); + } + else { + idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); + } + table_fact idx_singleton_fact; + idx_singleton_fact.push_back(0); + idx_singleton->add_fact(idx_singleton_fact); + + scoped_ptr join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, 0, 0); + SASSERT(join_fun); + scoped_rel res_table = (*join_fun)(t, *idx_singleton); + + svector table_cols(sig.size(), true); + finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); + + //this one does not need to be deleted -- it will be taken over by \c res in the \c init function + relation_base * inner_rel = get_inner_plugin().mk_full(pred, inner_sig, get_inner_plugin().get_kind()); + + relation_vector rels; + rels.push_back(inner_rel); + + res->init(*res_table, rels, true); + return res; + } + + finite_product_relation * finite_product_relation_plugin::mk_from_inner_relation(const relation_base & r) { + SASSERT(&r.get_plugin()==&get_inner_plugin()); + const relation_signature & sig = r.get_signature(); + + table_signature idx_singleton_sig; + idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); + idx_singleton_sig.set_functional_columns(1); + + scoped_rel idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); + table_fact idx_singleton_fact; + idx_singleton_fact.push_back(0); + idx_singleton->add_fact(idx_singleton_fact); + + svector table_cols(sig.size(), false); + finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); + + relation_vector rels; + rels.push_back(r.clone()); + + res->init(*idx_singleton, rels, true); + return res; + } + + class finite_product_relation_plugin::converting_join_fn : public convenient_relation_join_fn { + finite_product_relation_plugin & m_plugin; + scoped_ptr m_native_join; + + finite_product_relation * convert(const relation_base & r) { + SASSERT(&r.get_plugin()!=&m_plugin); + if(&r.get_plugin()==&m_plugin.get_inner_plugin()) { + return m_plugin.mk_from_inner_relation(r); + } + SASSERT(r.from_table()); + const table_relation & tr = static_cast(r); + finite_product_relation * res = m_plugin.mk_from_table_relation(tr); + SASSERT(res); + return res; + } + + public: + converting_join_fn(finite_product_relation_plugin & plugin, const relation_signature & sig1, + const relation_signature & sig2, unsigned col_cnt, const unsigned * cols1, + const unsigned * cols2) + : convenient_relation_join_fn(sig1, sig2, col_cnt, cols1, cols2), + m_plugin(plugin) {} + + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + scoped_rel r1_conv; + if(&r1.get_plugin()!=&m_plugin) { + r1_conv = convert(r1); + } + scoped_rel r2_conv; + if(&r2.get_plugin()!=&m_plugin) { + r2_conv = convert(r2); + } + + const finite_product_relation & fpr1 = r1_conv ? *r1_conv : get(r1); + const finite_product_relation & fpr2 = r2_conv ? *r2_conv : get(r2); + + SASSERT(&fpr1.get_plugin()==&m_plugin); + SASSERT(&fpr2.get_plugin()==&m_plugin); + + if(!m_native_join) { + m_native_join = m_plugin.get_manager().mk_join_fn(fpr1, fpr2, m_cols1, m_cols2, false); + } + return (*m_native_join)(fpr1, fpr2); + } + }; + + + class finite_product_relation_plugin::join_fn : public convenient_relation_join_fn { + scoped_ptr m_tjoin_fn; + scoped_ptr m_rjoin_fn; + + unsigned_vector m_t_joined_cols1; + unsigned_vector m_t_joined_cols2; + unsigned_vector m_r_joined_cols1; + unsigned_vector m_r_joined_cols2; + + //Column equalities betweet table and inner relations. + //The columns numbers correspont to the columns of the table/inner relation + //in the result of the join operation + unsigned_vector m_tr_table_joined_cols; + unsigned_vector m_tr_rel_joined_cols; + + scoped_ptr m_filter_tr_identities; + + scoped_ptr m_tjoined_second_rel_remover; + + //determines which columns of the result are table columns and which are in the inner relation + svector m_res_table_columns; + + public: + class join_maker : public table_row_mutator_fn { + join_fn & m_parent; + const finite_product_relation & m_r1; + const finite_product_relation & m_r2; + relation_vector & m_rjoins; + public: + join_maker(join_fn & parent, const finite_product_relation & r1, const finite_product_relation & r2, + relation_vector & rjoins) + : m_parent(parent), m_r1(r1), m_r2(r2), m_rjoins(rjoins) {} + + virtual bool operator()(table_element * func_columns) { + const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]); + const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]); + SASSERT(&or1); + SASSERT(&or2); + unsigned new_rel_num = m_rjoins.size(); + m_rjoins.push_back(m_parent.do_rjoin(or1, or2)); + func_columns[0]=new_rel_num; + return true; + } + }; + + join_fn(const finite_product_relation & r1, const finite_product_relation & r2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2) { + unsigned second_table_after_join_ofs = r1.m_table2sig.size(); + unsigned second_inner_rel_after_join_ofs = r1.m_other2sig.size(); + for(unsigned i=0;i tjoined = (*m_tjoin_fn)(r1.get_table(), r2.get_table()); + + relation_vector joined_orelations; + + { + join_maker * mutator = alloc(join_maker, *this, r1, r2, joined_orelations); //dealocated in inner_join_mapper + scoped_ptr inner_join_mapper = rmgr.mk_map_fn(*tjoined, mutator); + (*inner_join_mapper)(*tjoined); + } + + + if(!m_tjoined_second_rel_remover) { + unsigned removed_col = tjoined->get_signature().size()-1; + m_tjoined_second_rel_remover = rmgr.mk_project_fn(*tjoined, 1, &removed_col); + } + //remove the second functional column from tjoined to get a table that corresponds + //to the table signature of the resulting relation + scoped_rel res_table = (*m_tjoined_second_rel_remover)(*tjoined); + + relation_plugin & res_oplugin = + joined_orelations.empty() ? r1.m_other_plugin : joined_orelations.back()->get_plugin(); + + //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. + //It would however need to be somehow inferred for the new signature. + + finite_product_relation * res = alloc(finite_product_relation, r1.get_plugin(), get_result_signature(), + m_res_table_columns.c_ptr(), res_table->get_plugin(), res_oplugin, null_family_id); + + res->init(*res_table, joined_orelations, true); + + if(m_tr_table_joined_cols.size()) { + //There were some shared variables between the table and the relation part. + //We enforce those equalities here. + if(!m_filter_tr_identities) { + m_filter_tr_identities = plugin.mk_filter_identical_pairs(*res, m_tr_table_joined_cols.size(), + m_tr_table_joined_cols.c_ptr(), m_tr_rel_joined_cols.c_ptr()); + SASSERT(m_filter_tr_identities); + } + (*m_filter_tr_identities)(*res); + } + return res; + } + }; + + + + + relation_join_fn * finite_product_relation_plugin::mk_join_fn(const relation_base & rb1, const relation_base & rb2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if(!check_kind(rb1) || !check_kind(rb2)) { + bool r1foreign = &rb1.get_plugin()!=this; + bool r2foreign = &rb2.get_plugin()!=this; + if( (!r1foreign || can_be_converted(rb1)) && (!r2foreign || can_be_converted(rb2))) { + return alloc(converting_join_fn, *this, rb1.get_signature(), rb2.get_signature(), col_cnt, cols1, + cols2); + } + return 0; + } + const finite_product_relation & r1 = get(rb1); + const finite_product_relation & r2 = get(rb2); + + return alloc(join_fn, r1, r2, col_cnt, cols1, cols2); + } + + + class finite_product_relation_plugin::project_fn : public convenient_relation_project_fn { + unsigned_vector m_removed_table_cols; + unsigned_vector m_removed_rel_cols; + + scoped_ptr m_rel_projector; + scoped_ptr m_inner_rel_union; + + //determines which columns of the result are table columns and which are in the inner relation + svector m_res_table_columns; + public: + project_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(r.get_signature(), col_cnt, removed_cols) { + SASSERT(col_cnt>0); + for(unsigned i=0; ii); + m_res_table_columns.push_back(r.is_table_column(i)); + } + } + + class project_reducer : public table_row_pair_reduce_fn { + project_fn & m_parent; + relation_vector & m_relations; + public: + + project_reducer(project_fn & parent, relation_vector & relations) + : m_parent(parent), m_relations(relations) {} + + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + relation_base * tgt = m_relations[static_cast(func_columns[0])]->clone(); + relation_base & src = *m_relations[static_cast(merged_func_columns[0])]; + if(!m_parent.m_inner_rel_union) { + m_parent.m_inner_rel_union = tgt->get_manager().mk_union_fn(*tgt, src); + } + (*m_parent.m_inner_rel_union)(*tgt, src); + + unsigned new_idx = m_relations.size(); + m_relations.push_back(tgt); + func_columns[0] = new_idx; + } + }; + + virtual relation_base * operator()(const relation_base & rb) { + const finite_product_relation & r = get(rb); + finite_product_relation_plugin & plugin = r.get_plugin(); + const table_base & rtable = r.get_table(); + relation_manager & rmgr = plugin.get_manager(); + + r.garbage_collect(false); + relation_vector res_relations; + unsigned orig_rel_cnt = r.m_others.size(); + for(unsigned i=0; iclone() : 0); + } + SASSERT(res_relations.size()==orig_rel_cnt); + + bool shared_res_table = false; + const table_base * res_table; + + if(m_removed_table_cols.empty()) { + shared_res_table = true; + res_table = &rtable; + } + else { + project_reducer * preducer = alloc(project_reducer, *this, res_relations); + scoped_ptr tproject = + rmgr.mk_project_with_reduce_fn(rtable, m_removed_table_cols.size(), m_removed_table_cols.c_ptr(), preducer); + res_table = (*tproject)(rtable); + } + + relation_plugin * res_oplugin = 0; + + if(!m_removed_rel_cols.empty()) { + unsigned res_rel_cnt = res_relations.size(); + for(unsigned i=0; ideallocate(); + if(!res_oplugin) { + res_oplugin = &res_relations[i]->get_plugin(); + } + } + } + + if(!res_oplugin) { + res_oplugin = &r.m_other_plugin; + } + + //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. + //It would however need to be somehow inferred for the new signature. + + finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), + m_res_table_columns.c_ptr(), res_table->get_plugin(), *res_oplugin, null_family_id); + + res->init(*res_table, res_relations, false); + + if(!shared_res_table) { + const_cast(res_table)->deallocate(); + } + + return res; + } + }; + + relation_transformer_fn * finite_product_relation_plugin::mk_project_fn(const relation_base & rb, unsigned col_cnt, + const unsigned * removed_cols) { + if(&rb.get_plugin()!=this) { + return 0; + } + return alloc(project_fn, get(rb), col_cnt, removed_cols); + } + + + + class finite_product_relation_plugin::rename_fn : public convenient_relation_rename_fn { + scoped_ptr m_table_renamer; + scoped_ptr m_rel_renamer; + bool m_rel_identity; + + unsigned_vector m_rel_permutation; + + //determines which columns of the result are table columns and which are in the inner relation + svector m_res_table_columns; + public: + rename_fn(const finite_product_relation & r, unsigned cycle_len, const unsigned * permutation_cycle) + : convenient_relation_rename_fn(r.get_signature(), cycle_len, permutation_cycle) { + SASSERT(cycle_len>1); + + unsigned sig_sz = r.get_signature().size(); + unsigned_vector permutation; + add_sequence(0, sig_sz, permutation); + permutate_by_cycle(permutation, cycle_len, permutation_cycle); + + unsigned_vector table_permutation; + + bool table_identity = true; + m_rel_identity = true; + for(unsigned new_i=0; new_iclone() : 0); + } + + if(!m_rel_identity) { + unsigned res_rel_cnt = res_relations.size(); + for(unsigned i=0; i inner_rel = res_relations[i]; + if(!m_rel_renamer) { + m_rel_renamer = r.get_manager().mk_permutation_rename_fn(*inner_rel, m_rel_permutation); + } + + res_relations[i] = (*m_rel_renamer)(*inner_rel); + } + } + scoped_rel res_table_scoped; + const table_base * res_table = &rtable; + + if(m_table_renamer) { + res_table_scoped = (*m_table_renamer)(*res_table); + res_table = res_table_scoped.get(); + } + + //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. + //It would however need to be somehow inferred for the new signature. + + finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), + m_res_table_columns.c_ptr(), res_table->get_plugin(), r.m_other_plugin, null_family_id); + + res->init(*res_table, res_relations, false); + + return res; + } + }; + + relation_transformer_fn * finite_product_relation_plugin::mk_rename_fn(const relation_base & rb, + unsigned permutation_cycle_len, const unsigned * permutation_cycle) { + + if(&rb.get_plugin()!=this) { + return 0; + } + const finite_product_relation & r = get(rb); + return alloc(rename_fn, r, permutation_cycle_len, permutation_cycle); + } + + + class finite_product_relation_plugin::union_fn : public relation_union_fn { + bool m_use_delta; + unsigned_vector m_data_cols;//non-functional columns in the product-relation table (useful for creating operations) + scoped_ptr m_common_join; //result of the join contains (data columns), tgt_rel_idx, src_rel_idx + scoped_ptr m_rel_union; + scoped_ptr m_table_union; + scoped_ptr m_remove_overlaps; + scoped_ptr m_remove_src_column_from_overlap; + + //this one is populated only if we're doing union with delta + scoped_ptr m_delta_merging_union; + + scoped_ptr m_overlap_delta_table_builder; + public: + union_fn(const finite_product_relation & tgt, bool use_delta) : m_use_delta(use_delta) {} + + relation_union_fn & get_inner_rel_union_op(relation_base & r) { + if(!m_rel_union) { + m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : 0); + } + return *m_rel_union; + } + + class union_mapper : public table_row_mutator_fn { + union_fn & m_parent; + finite_product_relation & m_tgt; + const finite_product_relation & m_src; + table_base * m_delta_indexes; //table with signature (updated tgt rel index, delta_index in m_delta_rels) + relation_vector * m_delta_rels; + table_fact m_di_fact; //auxiliary fact for inserting into \c m_delta_indexes + public: + /** + If \c delta_indexes is 0, it means we are not collecting indexes. + */ + union_mapper(union_fn & parent, finite_product_relation & tgt, const finite_product_relation & src, + table_base * delta_indexes, relation_vector * delta_rels) + : m_parent(parent), + m_tgt(tgt), + m_src(src), + m_delta_indexes(delta_indexes), + m_delta_rels(delta_rels) {} + + virtual ~union_mapper() {} + + virtual bool operator()(table_element * func_columns) { + relation_base & otgt_orig = m_tgt.get_inner_rel(func_columns[0]); + const relation_base & osrc = m_src.get_inner_rel(func_columns[1]); + + relation_base * otgt = otgt_orig.clone(); + unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); + m_tgt.set_inner_rel(new_tgt_idx, otgt); + if(m_delta_indexes) { + relation_base * odelta = otgt->get_plugin().mk_empty(otgt->get_signature()); + m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc, odelta); + + unsigned delta_idx = m_delta_rels->size(); + m_delta_rels->push_back(odelta); + m_di_fact.reset(); + m_di_fact.push_back(new_tgt_idx); + m_di_fact.push_back(delta_idx); + m_delta_indexes->add_fact(m_di_fact); + } + else { + m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc); + } + + func_columns[0]=new_tgt_idx; + return true; + } + }; + + /** + Makes a table whose last column has indexes to relations in \c src into a table + with indexes to relation \c tgt. + */ + class src_copying_mapper : public table_row_mutator_fn { + finite_product_relation & m_tgt; + const finite_product_relation & m_src; + public: + /** + If \c delta_indexes is 0, it means we are not collecting indexes. + */ + src_copying_mapper(finite_product_relation & tgt, const finite_product_relation & src) + : m_tgt(tgt), m_src(src) {} + + virtual bool operator()(table_element * func_columns) { + const relation_base & osrc = m_src.get_inner_rel(func_columns[0]); + unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); + m_tgt.set_inner_rel(new_tgt_idx, osrc.clone()); + func_columns[0]=new_tgt_idx; + return true; + } + }; + + virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + finite_product_relation & tgt = get(tgtb); + const finite_product_relation & src0 = get(srcb); + finite_product_relation * delta = get(deltab); + + relation_manager & rmgr = tgt.get_manager(); + + scoped_rel src_aux_copy; //copy of src in case its specification needs to be modified + + if(!vectors_equal(tgt.m_table2sig, src0.m_table2sig) + || (delta && !vectors_equal(tgt.m_table2sig, delta->m_table2sig)) ) { + src_aux_copy = src0.clone(); + ptr_vector orig_rels; + orig_rels.push_back(src_aux_copy.get()); + orig_rels.push_back(&tgt); + if(delta) { + orig_rels.push_back(delta); + } + if(!finite_product_relation::try_unify_specifications(orig_rels)) { + throw default_exception("finite_product_relation union: cannot convert relations to common specification"); + } + } + + const finite_product_relation & src = src_aux_copy ? *src_aux_copy : src0; + + table_plugin & tplugin = tgt.get_table_plugin(); + + if(!m_common_join) { + unsigned data_cols_cnt = tgt.m_table_sig.size()-1; + for(unsigned i=0; i table_overlap = (*m_common_join)(tgt.get_table(), src.get_table()); + + scoped_rel delta_indexes; + relation_vector delta_rels; + if(m_use_delta) { + table_signature di_sig; + di_sig.push_back(finite_product_relation::s_rel_idx_sort); + di_sig.push_back(finite_product_relation::s_rel_idx_sort); + di_sig.set_functional_columns(1); + delta_indexes = tplugin.mk_empty(di_sig); + } + + { + union_mapper * umapper = alloc(union_mapper, *this, tgt, src, delta_indexes.get(), &delta_rels); + scoped_ptr mapping_fn = rmgr.mk_map_fn(*table_overlap, umapper); + (*mapping_fn)(*table_overlap); + } + + if(!m_remove_src_column_from_overlap) { + unsigned removed_cols[] = { table_overlap->get_signature().size()-1 }; + m_remove_src_column_from_overlap = rmgr.mk_project_fn(*table_overlap, 1, removed_cols); + } + //transform table_overlap into the signature of tgt.get_table(), so that the functional + //column contains indexes of the united relations + scoped_rel regular_overlap = (*m_remove_src_column_from_overlap)(*table_overlap); + + + if(!m_remove_overlaps) { + m_remove_overlaps = rmgr.mk_filter_by_negation_fn(tgt.get_table(), *regular_overlap, m_data_cols, + m_data_cols); + } + + //in tgt keep only the rows that are in tgt only + (*m_remove_overlaps)(tgt.get_table(), *regular_overlap); + + //add rows in which tgt and src overlapped + if(!m_table_union) { + m_table_union = rmgr.mk_union_fn(tgt.get_table(), tgt.get_table()); + } + (*m_table_union)(tgt.get_table(), *regular_overlap); + + scoped_rel src_only = src.get_table().clone(); + (*m_remove_overlaps)(*src_only, *regular_overlap); + + scoped_rel src_only2; //a copy of src_only for use in building the delta + if(m_use_delta) { + src_only2 = src_only->clone(); + } + + { + src_copying_mapper * cpmapper = alloc(src_copying_mapper, tgt, src); + scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only, cpmapper); + (*mapping_fn)(*src_only); + } + + //add rows that were only in src + (*m_table_union)(tgt.get_table(), *src_only); + + if(m_use_delta) { + bool extending_delta = !delta->empty(); + //current delta, we will add it to the deltab argument later if it was not given to us empty + finite_product_relation * cdelta; + if(extending_delta) { + cdelta = delta->get_plugin().mk_empty(*delta); + } + else { + cdelta = delta; + } + + if(!m_overlap_delta_table_builder) { + unsigned table_fn_col = regular_overlap->get_signature().size()-1; + unsigned first_col = 0; + unsigned removed_cols[] = { table_fn_col, table_fn_col+1 }; + m_overlap_delta_table_builder = rmgr.mk_join_project_fn(*regular_overlap, *delta_indexes, 1, + &table_fn_col, &first_col, 2, removed_cols); + } + + scoped_rel overlap_delta_table = + (*m_overlap_delta_table_builder)(*regular_overlap, *delta_indexes); + + cdelta->init(*overlap_delta_table, delta_rels, true); + + { + src_copying_mapper * cpmapper = alloc(src_copying_mapper, *cdelta, src); + scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only2, cpmapper); + (*mapping_fn)(*src_only2); + } + + //add rows that were only in src + (*m_table_union)(cdelta->get_table(), *src_only2); + + if(extending_delta) { + if(!m_delta_merging_union) { + m_delta_merging_union = rmgr.mk_union_fn(*delta, *cdelta); + } + (*m_delta_merging_union)(*delta, *cdelta); + cdelta->deallocate(); + } + } + } + }; + +#if 0 + /** + Union operation taking advantage of the fact that the inner relation of all the arguments + is a singleton relation. + */ + class finite_product_relation_plugin::inner_singleton_union_fn : public relation_union_fn { + + class offset_row_mapper : public table_row_mutator_fn { + public: + unsigned m_ofs; + virtual bool operator()(table_element * func_columns) { + func_columns[0] += m_ofs; + return true; + } + }; + + // [Leo]: gcc complained about the following class. + // It does not have a constructor and uses a reference + + class inner_relation_copier : public table_row_mutator_fn { + finite_product_relation & m_tgt; + const finite_product_relation & m_src; + finite_product_relation * m_delta; + unsigned m_tgt_ofs; + unsigned m_delta_ofs; + public: + virtual bool operator()(table_element * func_columns) { + unsigned src_idx = static_cast(func_columns[0]); + unsigned tgt_idx = src_idx + m_tgt_ofs; + unsigned delta_idx = m_delta ? (src_idx + m_delta_ofs) : 0; + SASSERT(!m_delta || m_tgt.m_others[tgt_idx]==m_delta->m_others[delta_idx]); + SASSERT(m_tgt.m_others[tgt_idx]==0 || m_tgt.m_others[tgt_idx]==m_src.m_others[src_idx]); + if(m_tgt.m_others[tgt_idx]==0) { + m_tgt.m_others[tgt_idx] = m_src.m_others[src_idx]->clone(); + if(m_delta) { + m_delta->m_others[delta_idx] = m_src.m_others[src_idx]->clone(); + } + } + return true; + } + }; + + scoped_ptr m_t_union_fun; + offset_row_mapper * m_offset_mapper_obj; //initialized together with m_offset_mapper_fun, and deallocated by it + scoped_ptr m_offset_mapper_fun; + + + + public: + virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + finite_product_relation & tgt = get(tgtb); + const finite_product_relation & src = get(srcb); + finite_product_relation * delta = get(deltab); + + finite_product_relation_plugin & plugin = tgt.get_plugin(); + relation_manager & rmgr = plugin.get_manager(); + + //we want only non-empty inner relations to remain + tgt.garbage_collect(true); + src.garbage_collect(true); + + table_base & tgt_table = tgt.get_table(); + const table_base & src_table = src.get_table(); + + scoped_rel offsetted_src = src_table.clone(); + + if(!m_offset_mapper_fun) { + m_offset_mapper_obj = alloc(offset_row_mapper); + m_offset_mapper_fun = rmgr.mk_map_fn(*offsetted_src, m_offset_mapper_obj); + } + unsigned src_rel_offset = tgt.m_others.size(); + m_offset_mapper_obj->m_ofs = src_rel_offset; + + (*m_offset_mapper_fun)(*offsetted_src); + + //if we need to generate a delta, we get collect it into an empty relation and then union + //it with the delta passed in as argument. + scoped_rel loc_delta = delta ? get(plugin.mk_empty(*delta)) : 0; + //even if we don't need to generate the delta for the caller, we still want to have a delta + //table to know which relations to copy. + scoped_rel loc_delta_table_scoped; + if(!loc_delta) { + loc_delta_table_scoped = tgt_table.get_plugin().mk_empty(tgt_table); + } + table_base * loc_delta_table = loc_delta ? &loc_delta->get_table() : loc_delta_table_scoped.get(); + + if(!m_t_union_fun) { + m_t_union_fun = rmgr.mk_union_fn(tgt_table, *offsetted_src, loc_delta_table); + } + (*m_t_union_fun)(tgt_table, *offsetted_src, loc_delta_table); + + + //TODO: copy the relations into tgt and (possibly) delta using inner_relation_copier + //TODO: unite the local delta with the delta passed in as an argument + NOT_IMPLEMENTED_YET(); + } + }; +#endif + + class finite_product_relation_plugin::converting_union_fn : public relation_union_fn { + scoped_ptr m_tr_union_fun; + public: + virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + SASSERT(srcb.get_plugin().is_finite_product_relation()); + const finite_product_relation & src = get(srcb); + finite_product_relation_plugin & plugin = src.get_plugin(); + scoped_rel tr_src = plugin.to_table_relation(src); + if(!m_tr_union_fun) { + m_tr_union_fun = plugin.get_manager().mk_union_fn(tgtb, *tr_src, deltab); + SASSERT(m_tr_union_fun); + } + (*m_tr_union_fun)(tgtb, *tr_src, deltab); + } + }; + + relation_union_fn * finite_product_relation_plugin::mk_union_fn(const relation_base & tgtb, const relation_base & srcb, + const relation_base * deltab) { + if(&srcb.get_plugin()!=this) { + return 0; + } + const finite_product_relation & src = get(srcb); + if(&tgtb.get_plugin()!=this || (deltab && &deltab->get_plugin()!=this) ) { + if(can_convert_to_table_relation(src)) { + return alloc(converting_union_fn); + } + return 0; + } + + const finite_product_relation * delta = get(deltab); + + return alloc(union_fn, get(tgtb), delta!=0); + } + + + class finite_product_relation_plugin::filter_identical_fn : public relation_mutator_fn { + //the table and relation columns that should be identical + //the column numbering is local to the table or inner relation + unsigned_vector m_table_cols; + unsigned_vector m_rel_cols; + + scoped_ptr m_table_filter; + scoped_ptr m_rel_filter; + scoped_ptr m_tr_filter; + public: + filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols) + : m_table_filter(0), m_rel_filter(0), m_tr_filter(0) { + finite_product_relation_plugin & plugin = r.get_plugin(); + for(unsigned i=0; i1) { + m_table_filter = r.get_manager().mk_filter_identical_fn(r.get_table(), m_table_cols.size(), + m_table_cols.c_ptr()); + SASSERT(m_table_filter); + } + if(!m_table_cols.empty() && !m_rel_cols.empty()) { + unsigned tr_filter_table_cols[] = { m_table_cols[0] }; + unsigned tr_filter_rel_cols[] = { m_rel_cols[0] }; + m_tr_filter = plugin.mk_filter_identical_pairs(r, 1, tr_filter_table_cols, tr_filter_rel_cols); + SASSERT(m_tr_filter); + } + } + + void ensure_rel_filter(const relation_base & orel) { + SASSERT(m_rel_cols.size()>1); + if(m_rel_filter) { + return; + } + m_rel_filter = orel.get_manager().mk_filter_identical_fn(orel, m_rel_cols.size(), m_rel_cols.c_ptr()); + SASSERT(m_rel_filter); + } + + virtual void operator()(relation_base & rb) { + finite_product_relation & r = get(rb); + + if(m_table_cols.size()>1) { + (*m_table_filter)(r.get_table()); + } + + if(m_rel_cols.size()>1) { + r.garbage_collect(true); + unsigned rel_cnt = r.m_others.size(); + for(unsigned rel_idx=0; rel_idx m_table_filter; + scoped_ptr m_rel_filter; + unsigned m_col; + app_ref m_value; + public: + filter_equal_fn(const finite_product_relation & r, const relation_element & value, unsigned col) + : m_col(col), m_value(value, r.get_context().get_manager()) { + if(r.is_table_column(col)) { + table_element tval; + r.get_manager().relation_to_table(r.get_signature()[col], value, tval); + m_table_filter = r.get_manager().mk_filter_equal_fn(r.get_table(), tval, r.m_sig2table[col]); + } + } + + virtual void operator()(relation_base & rb) { + finite_product_relation & r = get(rb); + + if(m_table_filter) { + (*m_table_filter)(r.get_table()); + return; + } + r.garbage_collect(false); + relation_vector & inner_rels = r.m_others; + unsigned rel_cnt = inner_rels.size(); + for(unsigned i=0; i m_table_filter; + scoped_ptr m_rel_filter; + app_ref m_cond; + + idx_set m_table_cond_columns; + idx_set m_rel_cond_columns; + + //like m_table_cond_columns and m_rel_cond_columns, only the indexes are local to the + //table/relation, not to the signature of the whole relation + idx_set m_table_local_cond_columns; + idx_set m_rel_local_cond_columns; + + /** + If equal to 0, it means the interpreted condition uses all table columns. Then the original + table is used instead of the result of the projection. + */ + scoped_ptr m_table_cond_columns_project; + /** + \brief Column indexes of the global relations to which correspond the data columns in the table + that is result of applying the \c m_table_cond_columns_project functor. + */ + unsigned_vector m_global_origins_of_projected_columns; + + scoped_ptr m_assembling_join_project; + + + /** + \brief Renaming that transforms the variable numbers pointing to the global relation into + variables that point to the inner relation variables. + + The elements that do not correspond to columns of the inner relation (but rather to the table + columns) is modified in \c operator() when evaluating the condition for all the relevant + combinations of table values. + */ + expr_ref_vector m_renaming_for_inner_rel; + public: + filter_interpreted_fn(const finite_product_relation & r, app * condition) + : m_manager(r.get_context().get_manager()), + m_subst(r.get_context().get_var_subst()), + m_cond(condition, m_manager), + m_renaming_for_inner_rel(m_manager) { + relation_manager & rmgr = r.get_manager(); + + rule_manager& rm = r.get_context().get_rule_manager(); + idx_set& cond_columns = rm.collect_vars(m_cond); + + unsigned sig_sz = r.get_signature().size(); + for(unsigned i=0; i tproj_scope; + const table_base * tproj; + if(m_table_cond_columns_project) { + tproj_scope = (*m_table_cond_columns_project)(rtable); + tproj = tproj_scope.get(); + } + else { + tproj = &rtable; + } + unsigned projected_data_cols = tproj->get_signature().size()-1; + SASSERT(m_table_cond_columns.num_elems()==projected_data_cols); + + table_signature filtered_sig = tproj->get_signature(); + filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); + filtered_sig.set_functional_columns(1); + + relation_vector new_rels; + + scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); + table_fact f; + unsigned renaming_ofs = m_renaming_for_inner_rel.size()-1; + table_base::iterator pit = tproj->begin(); + table_base::iterator pend = tproj->end(); + for(; pit!=pend; ++pit) { + pit->get_fact(f); + unsigned old_rel_idx = static_cast(f.back()); + const relation_base & old_rel = r.get_inner_rel(old_rel_idx); + + //put the table values into the substitution + for(unsigned i=0; i filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond)); + (*filter)(*new_rel); + + if(new_rel->empty()) { + new_rel->deallocate(); + continue; + } + + unsigned new_rel_idx = new_rels.size(); + new_rels.push_back(new_rel); + f.push_back(new_rel_idx); + filtered_table->add_fact(f); + } + + if(!m_assembling_join_project) { + unsigned_vector table_cond_columns_vect; + for(unsigned i=0; i new_table = (*m_assembling_join_project)(rtable, *filtered_table); + r.reset(); + r.init(*new_table, new_rels, true); + } + }; + + relation_mutator_fn * finite_product_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, + app * condition) { + if(&rb.get_plugin()!=this) { + return 0; + } + return alloc(filter_interpreted_fn, get(rb), condition); + } + + class finite_product_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { + + unsigned_vector m_r_cols; + unsigned_vector m_neg_cols; + + scoped_ptr m_table_neg_filter; + scoped_ptr m_table_neg_complement_selector; + scoped_ptr m_neg_intersection_join; + scoped_ptr m_table_intersection_join; + scoped_ptr m_table_overlap_union; + scoped_ptr m_table_subtract; + scoped_ptr m_inner_subtract; + scoped_ptr m_overlap_table_last_column_remover; + scoped_ptr m_r_table_union; + + bool m_table_overlaps_only; + + unsigned_vector m_r_shared_table_cols; + unsigned_vector m_neg_shared_table_cols; + + unsigned_vector m_r_shared_rel_cols; + unsigned_vector m_neg_shared_rel_cols; + public: + negation_filter_fn(const finite_product_relation & r, const finite_product_relation & neg, + unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * neg_cols) + : m_r_cols(joined_col_cnt, r_cols), + m_neg_cols(joined_col_cnt, neg_cols), + m_table_overlaps_only(true) { + const table_signature & tsig = r.m_table_sig; + const table_base & rtable = r.get_table(); + relation_manager & rmgr = r.get_manager(); + + for(unsigned i=0; iget_signature().size() , all_rel_cols); + m_parent.m_inner_subtract = m_r.get_manager().mk_filter_by_negation_fn(*r_inner, + inters_inner, all_rel_cols, all_rel_cols); + } + (*m_parent.m_inner_subtract)(*r_inner, inters_inner); + + unsigned new_rel_num = m_r.get_next_rel_idx(); + m_r.set_inner_rel(new_rel_num, r_inner); + func_columns[0]=new_rel_num; + return true; + } + }; + + + virtual void operator()(relation_base & rb, const relation_base & negb) { + finite_product_relation & r = get(rb); + const finite_product_relation & neg = get(negb); + + if(m_table_overlaps_only) { + handle_only_tables_overlap_case(r, neg); + return; + } + + finite_product_relation_plugin & plugin = r.get_plugin(); + if(!m_neg_intersection_join) { + } + scoped_rel intersection = get((*m_neg_intersection_join)(r, neg)); + SASSERT(&intersection->get_plugin()==&plugin); //the result of join is also finite product + SASSERT(r.get_signature()==intersection->get_signature()); + + table_base & r_table = r.get_table(); + table_plugin & tplugin = r_table.get_plugin(); + relation_manager & rmgr = r.get_manager(); + + //we need to do this before we perform the \c m_table_subtract + //the sigrature of the \c table_overlap0 relation is + //(data_columns)(original rel idx)(subtracted rel idx) + scoped_rel table_overlap0 = (*m_table_intersection_join)(r_table, + intersection->get_table()); + + //the rows that don't appear in the table of the intersection are safe to stay + (*m_table_subtract)(r_table, intersection->get_table()); + + //now we will examine the rows that do appear in the intersection + + //There are no functional columns in the \c table_overlap0 relation (because of + //the project we did). We need to make both rel idx columns functional. + //We do not lose any rows, since the data columns by themselves are unique. + + table_signature table_overlap_sig(table_overlap0->get_signature()); + table_overlap_sig.set_functional_columns(2); + scoped_rel table_overlap = tplugin.mk_empty(table_overlap_sig); + + if(!m_table_overlap_union) { + m_table_overlap_union = rmgr.mk_union_fn(*table_overlap, *table_overlap0); + SASSERT(m_table_overlap_union); + } + (*m_table_overlap_union)(*table_overlap, *table_overlap0); + + { + rel_subtractor * mutator = alloc(rel_subtractor, *this, r, *intersection); + scoped_ptr mapper = rmgr.mk_map_fn(*table_overlap, mutator); + (*mapper)(*table_overlap); + } + + if(!m_overlap_table_last_column_remover) { + unsigned removed_col = table_overlap->get_signature().size()-1; + m_overlap_table_last_column_remover = rmgr.mk_project_fn(*table_overlap, 1, &removed_col); + } + scoped_rel final_overlapping_rows_table = + (*m_overlap_table_last_column_remover)(*table_overlap); + + if(!m_r_table_union) { + m_r_table_union = rmgr.mk_union_fn(r_table, *final_overlapping_rows_table); + } + + (*m_r_table_union)(r_table, *final_overlapping_rows_table); + } + }; + + relation_intersection_filter_fn * finite_product_relation_plugin::mk_filter_by_negation_fn(const relation_base & rb, + const relation_base & negb, unsigned joined_col_cnt, + const unsigned * r_cols, const unsigned * negated_cols) { + if(&rb.get_plugin()!=this || &negb.get_plugin()!=this) { + return 0; + } + + return alloc(negation_filter_fn, get(rb), get(negb), joined_col_cnt, r_cols, negated_cols); + } + + + class finite_product_relation_plugin::filter_identical_pairs_fn : public relation_mutator_fn { + scoped_ptr m_tproject_fn; //if zero, no columns need to be projected away + unsigned m_col_cnt; + unsigned_vector m_table_cols; + unsigned_vector m_rel_cols; + + scoped_ptr m_assembling_join_project; + scoped_ptr m_updating_union; + public: + filter_identical_pairs_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, + const unsigned * rel_cols) : + m_col_cnt(col_cnt), + m_table_cols(col_cnt, table_cols), + m_rel_cols(col_cnt, rel_cols) { + SASSERT(col_cnt>0); + const table_signature & tsig = r.m_table_sig; + unsigned t_sz = tsig.size(); + + sort_two_arrays(col_cnt, m_table_cols.begin(), m_rel_cols.begin()); + SASSERT(m_table_cols.back() tproj; + if(m_tproject_fn) { + tproj = (*m_tproject_fn)(r.get_table()); + } + else { + tproj = r.get_table().clone(); + } + SASSERT(m_col_cnt+1==tproj->get_signature().size()); + + table_signature filtered_sig = tproj->get_signature(); + filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); + filtered_sig.set_functional_columns(1); + + relation_vector new_rels; + scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); + table_fact f; + table_base::iterator pit = tproj->begin(); + table_base::iterator pend = tproj->end(); + for(; pit!=pend; ++pit) { + pit->get_fact(f); + unsigned old_rel_idx = static_cast(f.back()); + const relation_base & old_rel = r.get_inner_rel(old_rel_idx); + relation_base * new_rel = old_rel.clone(); + for(unsigned i=0; i filter = rmgr.mk_filter_equal_fn(*new_rel, r_el, m_rel_cols[i]); + (*filter)(*new_rel); + } + + if(new_rel->empty()) { + new_rel->deallocate(); + continue; + } + + unsigned new_rel_idx = new_rels.size(); + new_rels.push_back(new_rel); + f.push_back(new_rel_idx); + filtered_table->add_fact(f); + } + + if(!m_assembling_join_project) { + m_assembling_join_project = mk_assembler_of_filter_result(rtable, *filtered_table, m_table_cols); + } + + scoped_rel new_table = (*m_assembling_join_project)(r.get_table(), *filtered_table); + + r.reset(); + r.init(*new_table, new_rels, true); + } + }; + + relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_pairs(const finite_product_relation & r, + unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) { + return alloc(filter_identical_pairs_fn, r, col_cnt, table_cols, rel_cols); + } + + table_join_fn * finite_product_relation_plugin::mk_assembler_of_filter_result(const table_base & relation_table, + const table_base & filtered_table, const unsigned_vector & selected_columns) { + + table_plugin & tplugin = relation_table.get_plugin(); + const table_signature & rtable_sig = relation_table.get_signature(); + unsigned rtable_sig_sz = rtable_sig.size(); + unsigned selected_col_cnt = selected_columns.size(); + SASSERT(selected_col_cnt+2==filtered_table.get_signature().size()); + SASSERT(rtable_sig.functional_columns()==1); + SASSERT(filtered_table.get_signature().functional_columns()==1); + + unsigned_vector rtable_joined_cols; + rtable_joined_cols.append(selected_col_cnt, selected_columns.c_ptr()); //filtered table cols + rtable_joined_cols.push_back(rtable_sig_sz-1); //unfiltered relation indexes + + unsigned_vector filtered_joined_cols; + add_sequence(0, selected_col_cnt, filtered_joined_cols); //filtered table cols + filtered_joined_cols.push_back(selected_col_cnt); //unfiltered relation indexes + SASSERT(rtable_joined_cols.size()==filtered_joined_cols.size()); + + //the signature after join: + //(all relation table columns)(all filtered relation table columns)(unfiltered rel idx non-func from 'filtered') + // (unfiltered rel idx func from 'rtable')(filtered rel idx) + unsigned_vector removed_cols; + unsigned filtered_nonfunc_ofs = rtable_sig_sz-1; + add_sequence(filtered_nonfunc_ofs, selected_col_cnt, removed_cols); //data columns from 'filtered' + unsigned idx_ofs = filtered_nonfunc_ofs+selected_col_cnt; + removed_cols.push_back(idx_ofs); //unfiltered relation indexes from 'filtered' + removed_cols.push_back(idx_ofs+1); //unfiltered relation indexes from rtable + + table_join_fn * res = tplugin.get_manager().mk_join_project_fn(relation_table, filtered_table, + rtable_joined_cols, filtered_joined_cols, removed_cols); + SASSERT(res); + return res; + } + + // ----------------------------------- + // + // finite_product_relation + // + // ----------------------------------- + + finite_product_relation::finite_product_relation(finite_product_relation_plugin & p, + const relation_signature & s, const bool * table_columns, table_plugin & tplugin, + relation_plugin & oplugin, family_id other_kind) + : relation_base(p, s), + m_other_plugin(oplugin), + m_other_kind(other_kind), + m_full_rel_idx(UINT_MAX) { + const relation_signature & rel_sig = get_signature(); + unsigned sz = rel_sig.size(); + m_sig2table.resize(sz, UINT_MAX); + m_sig2other.resize(sz, UINT_MAX); + for(unsigned i=0; iclone()), + m_others(r.m_others), + m_available_rel_indexes(r.m_available_rel_indexes), + m_full_rel_idx(r.m_full_rel_idx), + m_live_rel_collection_project(), + m_empty_rel_removal_filter() { + //m_others is now just a shallow copy, we need use clone of the relations that in it now + unsigned other_sz = m_others.size(); + for(unsigned i=0; ideallocate(); + relation_vector::iterator it = m_others.begin(); + relation_vector::iterator end = m_others.end(); + for(; it!=end; ++it) { + relation_base * rel= *it; + if(rel) { + rel->deallocate(); + } + } + } + + context & finite_product_relation::get_context() const { + return get_manager().get_context(); + } + + unsigned finite_product_relation::get_next_rel_idx() const { + unsigned res; + if(!m_available_rel_indexes.empty()) { + res = m_available_rel_indexes.back(); + m_available_rel_indexes.pop_back(); + } + else { + res = m_others.size(); + m_others.push_back(0); + } + SASSERT(m_others[res]==0); + return res; + } + + void finite_product_relation::recycle_rel_idx(unsigned idx) const { + SASSERT(m_others[idx]==0); + m_available_rel_indexes.push_back(idx); + } + + unsigned finite_product_relation::get_full_rel_idx() { + if(m_full_rel_idx==UINT_MAX) { + m_full_rel_idx = get_next_rel_idx(); + relation_base * full_other = mk_full_inner(0); + m_others[m_full_rel_idx] = full_other; + } + return m_full_rel_idx; + } + + void finite_product_relation::init(const table_base & table_vals, const relation_vector & others, bool contiguous) { + SASSERT(m_table_sig.equal_up_to_fn_mark(table_vals.get_signature())); + SASSERT(empty()); + if(!m_others.empty()) { + garbage_collect(false); + } + SASSERT(m_others.empty()); + + m_others = others; + scoped_ptr table_union = get_manager().mk_union_fn(get_table(), table_vals); + (*table_union)(get_table(), table_vals); + + if(!contiguous) { + unsigned rel_cnt = m_others.size(); + for(unsigned i=0; isuggest_fact(t_f)) { + SASSERT(t_f.back()==new_rel_idx); + new_rel = mk_empty_inner(); + } else { + new_rel = get_inner_rel(t_f.back()).clone(); + + t_f[t_f.size()-1]=new_rel_idx; + m_table->ensure_fact(t_f); + } + new_rel->add_fact(o_f); + set_inner_rel(new_rel_idx, new_rel); + } + + bool finite_product_relation::contains_fact(const relation_fact & f) const { + table_fact t_f; + extract_table_fact(f, t_f); + + if(!m_table->fetch_fact(t_f)) { + return false; + } + + relation_fact o_f(get_context()); + extract_other_fact(f, o_f); + + const relation_base & other = get_inner_rel(t_f.back()); + + return other.contains_fact(o_f); + } + + bool finite_product_relation::empty() const { + garbage_collect(true); + return m_table->empty(); + } + + finite_product_relation * finite_product_relation::clone() const { + return alloc(finite_product_relation, *this); + } + + void finite_product_relation::complement_self(func_decl* p) { + unsigned other_sz = m_others.size(); + for(unsigned i=0; icomplement(p); + std::swap(m_others[i],r); + r->deallocate(); + } + table_element full_rel_idx = get_full_rel_idx(); + scoped_rel complement_table = m_table->complement(p, &full_rel_idx); + + scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, 0); + SASSERT(u_fn); + (*u_fn)(*m_table, *complement_table, 0); + } + + finite_product_relation * finite_product_relation::complement(func_decl* p) const { + finite_product_relation * res = clone(); + res->complement_self(p); + return res; + } + + class finite_product_relation::live_rel_collection_reducer : public table_row_pair_reduce_fn { + idx_set & m_accumulator; + public: + live_rel_collection_reducer(idx_set & accumulator) : m_accumulator(accumulator) {} + + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + m_accumulator.insert(static_cast(merged_func_columns[0])); + } + }; + + void finite_product_relation::collect_live_relation_indexes(idx_set & res) const { + SASSERT(res.empty()); + unsigned table_data_col_cnt = m_table_sig.size()-1; + + if(table_data_col_cnt==0) { + if(!get_table().empty()) { + table_base::iterator iit = get_table().begin(); + table_base::iterator iend = get_table().end(); + + SASSERT(iit!=iend); + res.insert(static_cast((*iit)[0])); + SASSERT((++iit)==iend); + } + return; + } + + if(!m_live_rel_collection_project) { + buffer removed_cols; + removed_cols.resize(table_data_col_cnt); + for(unsigned i=0; i live_indexes_table = (*m_live_rel_collection_project)(get_table()); + res.swap(m_live_rel_collection_acc); + + SASSERT(live_indexes_table->get_signature().size()==1); + SASSERT(live_indexes_table->get_signature().functional_columns()==1); + if(!live_indexes_table->empty()) { + table_base::iterator iit = live_indexes_table->begin(); + table_base::iterator iend = live_indexes_table->end(); + + SASSERT(iit!=iend); + res.insert(static_cast((*iit)[0])); + SASSERT((++iit)==iend); + } + } + + void finite_product_relation::garbage_collect(bool remove_empty) const { + idx_set live_indexes; + collect_live_relation_indexes(live_indexes); + + scoped_rel empty_rel_indexes; //populated only if \c remove_empty==true + table_fact empty_rel_fact; + + unsigned rel_cnt = m_others.size(); +#if Z3DEBUG + unsigned encountered_live_indexes = 0; +#endif + for(unsigned rel_idx=0; rel_idxempty()) { + continue; + } + if(empty_rel_indexes==0) { + table_signature empty_rel_indexes_sig; + empty_rel_indexes_sig.push_back(s_rel_idx_sort); + empty_rel_indexes = get_table_plugin().mk_empty(empty_rel_indexes_sig); + } + empty_rel_fact.reset(); + empty_rel_fact.push_back(rel_idx); + empty_rel_indexes->add_fact(empty_rel_fact); + } + m_others[rel_idx]->deallocate(); + m_others[rel_idx] = 0; + if(rel_idx==m_full_rel_idx) { + m_full_rel_idx = UINT_MAX; + } + recycle_rel_idx(rel_idx); + } + SASSERT(encountered_live_indexes==live_indexes.num_elems()); + + if(m_available_rel_indexes.size()==m_others.size()) { + m_available_rel_indexes.reset(); + m_others.reset(); + } + + if(empty_rel_indexes) { + SASSERT(remove_empty); + + if(!m_empty_rel_removal_filter) { + unsigned t_joined_cols[] = { m_table_sig.size()-1 }; + unsigned ei_joined_cols[] = { 0 }; + m_empty_rel_removal_filter = get_manager().mk_filter_by_negation_fn(get_table(), *empty_rel_indexes, + 1, t_joined_cols, ei_joined_cols); + } + + (*m_empty_rel_removal_filter)(*m_table, *empty_rel_indexes); + } + } + + bool finite_product_relation::try_unify_specifications(ptr_vector & rels) { + if(rels.empty()) { + return true; + } + unsigned sig_sz = rels.back()->get_signature().size(); + svector table_cols(sig_sz, true); + + ptr_vector::iterator it = rels.begin(); + ptr_vector::iterator end = rels.end(); + for(; it!=end; ++it) { + finite_product_relation & rel = **it; + for(unsigned i=0; i pr_fun = get_manager().mk_project_fn(get_table(), to_project_away); + table_base * moved_cols_table = (*pr_fun)(get_table()); //gets destroyed inside moved_cols_trel + scoped_rel moved_cols_trel = + rmgr.get_table_relation_plugin(moved_cols_table->get_plugin()).mk_from_table(moved_cols_sig, moved_cols_table); + + svector moved_cols_table_flags(moved_cols_sig.size(), false); + + scoped_rel moved_cols_rel = get_plugin().mk_empty(moved_cols_sig, + moved_cols_table_flags.c_ptr()); + + scoped_ptr union_fun = + get_manager().mk_union_fn(*moved_cols_rel, *moved_cols_trel); + SASSERT(union_fun); //the table_relation should be able to be 'unioned into' any relation + + (*union_fun)(*moved_cols_rel, *moved_cols_trel); + + unsigned_vector all_moved_cols_indexes; + add_sequence(0, moved_cols_sig.size(), all_moved_cols_indexes); + + scoped_ptr join_fun = get_manager().mk_join_project_fn(*this, *moved_cols_rel, new_rel_columns, + all_moved_cols_indexes, new_rel_columns, false); + + scoped_rel unordered_rel = (*join_fun)(*this, *moved_cols_rel); + SASSERT(unordered_rel->get_signature().size()==sig_sz); //the signature size should be the same as original + + //now we need to reorder the columns in the \c new_rel to match the original table + unsigned_vector permutation; + unsigned moved_cols_cnt = new_rel_columns.size(); + unsigned next_replaced_idx = 0; + unsigned next_orig_idx = 0; + for(unsigned i=0; i perm_fun = get_manager().mk_rename_fn(*unordered_rel, cycle); + //the scoped_rel wrapper does the destruction of the old object + unordered_rel = (*perm_fun)(*unordered_rel); + cycle.reset(); + } + + finite_product_relation & new_rel = finite_product_relation_plugin::get(*unordered_rel); + + //Swap the content of the current object and new_rel. On exitting the function new_rel will be destroyed + //since it points to the content of scoped_rel unordered_rel. + swap(new_rel); + + return true; + } + + void finite_product_relation::display(std::ostream & out) const { + + garbage_collect(true); + + out << "finite_product_relation:\n"; + + out << " table:\n"; + get_table().display(out); + + unsigned other_sz = m_others.size(); + for(unsigned i=0; iget_fact(tfact); + + const table_relation & orel = static_cast(get_inner_rel(tfact[rel_idx_col])); + const table_base & otable = orel.get_table(); + table_base::iterator oit = otable.begin(); + table_base::iterator oend = otable.end(); + for(; oit!=oend; ++oit) { + oit->get_fact(ofact); + + out << "\t("; + for(unsigned i=0; iget_fact(fact); + conjs.reset(); + SASSERT(fact.size() == fact_sz); + unsigned rel_idx = static_cast(fact[fact_sz-1]); + m_others[rel_idx]->to_formula(tmp); + for (unsigned i = 0; i + 1 < fact_sz; ++i) { + conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i]))); + } + sh(tmp, fact_sz-1, tmp); + conjs.push_back(tmp); + disjs.push_back(m.mk_and(conjs.size(), conjs.c_ptr())); + } + bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); + } + +}; + diff --git a/src/muz/rel/dl_finite_product_relation.h b/src/muz/rel/dl_finite_product_relation.h new file mode 100644 index 000000000..d5181d122 --- /dev/null +++ b/src/muz/rel/dl_finite_product_relation.h @@ -0,0 +1,366 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_finite_product_relation.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ +#ifndef _DL_FINITE_PRODUCT_RELATION_H_ +#define _DL_FINITE_PRODUCT_RELATION_H_ + + +#include "dl_base.h" +#include "dl_relation_manager.h" +#include "dl_table_relation.h" + +namespace datalog { + + class finite_product_relation; + + void universal_delete(finite_product_relation* ptr); + + + class finite_product_relation_plugin : public relation_plugin { + friend class finite_product_relation; + public: + struct rel_spec { + family_id m_inner_kind; //null_family_id means we don't care about the kind + svector m_table_cols; + + rel_spec() : m_inner_kind(null_family_id) {} + rel_spec(const svector& table_cols) + : m_inner_kind(null_family_id), m_table_cols(table_cols) {} + + bool operator==(const rel_spec & o) const { + return m_inner_kind==o.m_inner_kind && vectors_equal(m_table_cols, o.m_table_cols); + } + struct hash { + unsigned operator()(const rel_spec & o) const { + return o.m_inner_kind^svector_hash()(o.m_table_cols); + } + }; + }; + private: + + class join_fn; + class converting_join_fn; + class project_fn; + class rename_fn; + class union_fn; + class inner_singleton_union_fn; + class converting_union_fn; + class filter_identical_fn; + class filter_equal_fn; + class filter_interpreted_fn; + class negation_filter_fn; + class filter_identical_pairs_fn; + + relation_plugin & m_inner_plugin; + + rel_spec_store > m_spec_store; + + static symbol get_name(relation_plugin & inner_plugin); + family_id get_relation_kind(finite_product_relation & r, const bool * table_columns); + + static void get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s, + svector & table_columns); + void get_all_possible_table_columns(const relation_signature & s, svector & table_columns) { + get_all_possible_table_columns(get_manager(), s, table_columns); + } + + void split_signatures(const relation_signature & s, table_signature & table_sig, + relation_signature & remaining_sig); + void split_signatures(const relation_signature & s, const bool * table_columns, + table_signature & table_sig, relation_signature & remaining_sig); + public: + static finite_product_relation & get(relation_base & r); + static const finite_product_relation & get(const relation_base & r); + static finite_product_relation * get(relation_base * r); + static const finite_product_relation * get(const relation_base * r); + + static finite_product_relation_plugin & get_plugin(relation_manager & rmgr, relation_plugin & inner); + + finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager); + + virtual void initialize(family_id fid); + + relation_plugin & get_inner_plugin() const { return m_inner_plugin; } + + virtual bool can_handle_signature(const relation_signature & s); + + virtual relation_base * mk_empty(const relation_signature & s); + /** + \c inner_kind==null_family_id means we don't care about the kind of the inner relation + */ + finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns, + family_id inner_kind=null_family_id); + finite_product_relation * mk_empty(const finite_product_relation & original); + virtual relation_base * mk_empty(const relation_base & original); + virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + + /** + \brief Return true if \c r can be converted to \c finite_product_relation_plugin either + by \c mk_from_table_relation or by \c mk_from_inner_relation. + */ + bool can_be_converted(const relation_base & r); + + /** + If the conversion cannot be performed, 0 is returned. + */ + finite_product_relation * mk_from_table_relation(const table_relation & r); + finite_product_relation * mk_from_inner_relation(const relation_base & r); + + bool can_convert_to_table_relation(const finite_product_relation & r); + table_relation * to_table_relation(const finite_product_relation & r); + + protected: + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + + private: + /** + \brief Create a filter that enforces equality between pairs of table and relation columns + + The column numbers in arrays \c table_cols and \c rel_cols must be local to the table/inner relation. + */ + relation_mutator_fn * mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt, + const unsigned * table_cols, const unsigned * rel_cols); + + /** + \brief Create a join-project operation that creates a table according to \c relation_table + but with references to relations updated and removed according to the content of \c filtered_table. + \c selected_columns contains sorted indexes of data columns in \c relation_table that are also in + the \c filtered_table (so that the first column in \c filtered_table corresponds to + \c selected_columns[0] -th column in \c relation_table etc...) + + Signature of \c relation_table: + (data columns)(functional column with indexes of relation objects) + + Signature of \c filtered_table: + (selected data columns)(non-functional column with original relation object indexes) + (functional column with indexes of filtered relation objects) + + */ + static table_join_fn * mk_assembler_of_filter_result(const table_base & relation_table, + const table_base & filtered_table, const unsigned_vector & selected_columns); + + }; + + class finite_product_relation : public relation_base { + friend class finite_product_relation_plugin; + friend class finite_product_relation_plugin::join_fn; + friend class finite_product_relation_plugin::project_fn; + friend class finite_product_relation_plugin::union_fn; + friend class finite_product_relation_plugin::rename_fn; + friend class finite_product_relation_plugin::inner_singleton_union_fn; + friend class finite_product_relation_plugin::filter_equal_fn; + friend class finite_product_relation_plugin::filter_identical_pairs_fn; + + class live_rel_collection_reducer; + + + public: + /** + Size of this sort determines how many different relation objects can we refer to. + */ + static const table_sort s_rel_idx_sort; + + + /** + \brief The last column in the signature is a functional column with index of the + associated inner relation. The other columns correspond to the relation signature + according to \c m_table2sig. + + It holds that \c m_table_sig.size()-1==m_table2sig.size() + */ + + table_signature m_table_sig; + unsigned_vector m_table2sig; // (ordered list) + unsigned_vector m_sig2table; //index of corresponding table column or UINT_MAX + private: + relation_signature m_other_sig; + unsigned_vector m_other2sig; // (ordered list) + public: + unsigned_vector m_sig2other; //index of corresponding other relation column or UINT_MAX + private: + relation_plugin & m_other_plugin; + family_id m_other_kind; + + mutable table_base * m_table; + public: + mutable relation_vector m_others; + private: + mutable unsigned_vector m_available_rel_indexes; + + /** + \c UINT_MAX means uninitialized. + If we can get away with it, we want to have a single full relation to refer to. + */ + mutable unsigned m_full_rel_idx; + + mutable idx_set m_live_rel_collection_acc; + mutable scoped_ptr m_live_rel_collection_project; + + mutable scoped_ptr m_empty_rel_removal_filter; + + void recycle_rel_idx(unsigned idx) const; + + // creates a full relation if it does not exist. + unsigned get_full_rel_idx(); + + + + public: + relation_base & get_inner_rel(table_element idx) + { SASSERT(idx(idx)); } + relation_base & get_inner_rel(unsigned idx) { SASSERT(m_others[idx]); return *m_others[idx]; } + const relation_base & get_inner_rel(unsigned idx) const + { return const_cast(*this).get_inner_rel(idx); } + + unsigned get_next_rel_idx() const; + + /** + The relation takes ownership of the \c inner object. + */ + void set_inner_rel(table_element idx, relation_base * inner) + { SASSERT(idx(idx), inner); } + /** + The relation takes ownership of the \c inner object. + */ + void set_inner_rel(unsigned idx, relation_base * inner) { + SASSERT(!m_others[idx]); + SASSERT(inner); + m_others[idx] = inner; + } + table_base & get_table() { return *m_table; } + + table_plugin & get_table_plugin() const { return get_table().get_plugin(); } + + void garbage_collect(bool remove_empty) const; + + /** + \brief Initialize an empty relation with table \c table_vals and relations in \c others. + + The relation object takes ownership of relations inside the \c others vector. + + If \c contiguous is true, it can be assumed that there are no zero elements in the \c others array. + */ + void init(const table_base & table_vals, const relation_vector & others, bool contiguous); + + private: + + + /** + \brief Extract the values of table non-functional columns from the relation fact. + The value of the functional column which determines index of the inner relation is undefined. + */ + void extract_table_fact(const relation_fact rf, table_fact & tf) const; + /** + \brief Extract the values of the inner relation columns from the relation fact. + */ + void extract_other_fact(const relation_fact rf, relation_fact & of) const; + + relation_base * mk_empty_inner(); + relation_base * mk_full_inner(func_decl* pred); + + + void complement_self(func_decl* pred); + + void collect_live_relation_indexes(idx_set & res) const; + + + /** + \brief Try to modify relations in \c rels so that they have the same columns corresponding to the table + and the inner relation (so that the union can be perofrmed on theim in a straightforward way). + + Relations in \c rels must all have equal signature. + + Even if the function fails and false is returned, some relations may already be modified. They are + in a valid state, but with different specification. + */ + static bool try_unify_specifications(ptr_vector & rels); + + bool try_modify_specification(const bool * table_cols); + + virtual bool can_swap(const relation_base & r) const + { return &get_plugin()==&r.get_plugin(); } + + /** + \brief Swap content of the current relation with the content of \c r. + + Both relations must come from the same plugin and be of the same signature. + */ + virtual void swap(relation_base & r); + + /** + \brief Create a \c finite_product_relation object. + */ + finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, + const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind); + finite_product_relation(const finite_product_relation & r); + virtual ~finite_product_relation(); + public: + context & get_context() const; + finite_product_relation_plugin & get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + bool is_table_column(unsigned col_idx) const { return m_sig2table[col_idx]!=UINT_MAX; } + + const table_base & get_table() const { return *m_table; } + + const relation_base & get_inner_rel(table_element idx) const + { SASSERT(idx(idx)); } + + /** + The function calls garbage_collect, so the internal state may change when it is called. + */ + virtual bool empty() const; + void reset() { m_table->reset(); garbage_collect(false); } + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + + virtual finite_product_relation * clone() const; + virtual finite_product_relation * complement(func_decl* p) const; + + virtual void display(std::ostream & out) const; + virtual void display_tuples(func_decl & pred, std::ostream & out) const; + + virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } + + virtual void to_formula(expr_ref& fml) const; + }; + +}; + +#endif /* _DL_FINITE_PRODUCT_RELATION_H_ */ + diff --git a/src/muz/rel/dl_interval_relation.cpp b/src/muz/rel/dl_interval_relation.cpp new file mode 100644 index 000000000..c04269b02 --- /dev/null +++ b/src/muz/rel/dl_interval_relation.cpp @@ -0,0 +1,653 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_interval_relation.cpp + +Abstract: + + Basic interval reatlion. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ + +#include "debug.h" +#include "optional.h" +#include "ast_pp.h" +#include "dl_interval_relation.h" +#include "dl_relation_manager.h" +#include "bool_rewriter.h" + + +namespace datalog { + // ------------------------- + // interval_relation_plugin + + interval_relation_plugin::interval_relation_plugin(relation_manager& m): + relation_plugin(interval_relation_plugin::get_name(), m), + m_empty(m_dep), + m_arith(get_ast_manager()) { + } + + bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) { + for (unsigned i = 0; i < sig.size(); ++i) { + if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { + return false; + } + } + return true; + } + + + relation_base * interval_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(interval_relation, *this, s, true); + } + + relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + return alloc(interval_relation, *this, s, false); + } + + class interval_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + interval_relation const& r1 = get(_r1); + interval_relation const& r2 = get(_r2); + interval_relation_plugin& p = r1.get_plugin(); + interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + return result; + } + }; + + relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(r1) || !check_kind(r2)) { + return 0; + } + return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); + } + + + class interval_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { + } + + virtual relation_base * operator()(const relation_base & _r) { + interval_relation const& r = get(_r); + interval_relation_plugin& p = r.get_plugin(); + interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); + return result; + } + }; + + relation_transformer_fn * interval_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { + } + + virtual relation_base * operator()(const relation_base & _r) { + interval_relation const& r = get(_r); + interval_relation_plugin& p = r.get_plugin(); + interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); + return result; + } + }; + + relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(!check_kind(r)) { + return 0; + } + return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); + } + + interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { + bool l_open = src1.is_lower_open(); + bool r_open = src1.is_upper_open(); + ext_numeral low = src1.inf(); + ext_numeral high = src1.sup(); + if (src2.inf() < low || (src2.inf() == low && l_open)) { + low = src2.inf(); + l_open = src2.is_lower_open(); + } + if (src2.sup() > high || (src2.sup() == high && r_open)) { + high = src2.sup(); + r_open = src2.is_upper_open(); + } + return interval(dep(), low, l_open, 0, high, r_open, 0); + } + + interval interval_relation_plugin::widen(interval const& src1, interval const& src2) { + bool l_open = src1.is_lower_open(); + bool r_open = src1.is_upper_open(); + ext_numeral low = src1.inf(); + ext_numeral high = src1.sup(); + + if (src2.inf() < low || (low == src2.inf() && l_open && !src2.is_lower_open())) { + low = ext_numeral(false); + l_open = true; + } + if (high < src2.sup() || (src2.sup() == high && !r_open && src2.is_upper_open())) { + high = ext_numeral(true); + r_open = true; + } + return interval(dep(), low, l_open, 0, high, r_open, 0); + } + + interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) { + isempty = false; + if (is_empty(0, src1) || is_infinite(src2)) { + return src1; + } + if (is_empty(0, src2) || is_infinite(src1)) { + return src2; + } + bool l_open = src1.is_lower_open(); + bool r_open = src1.is_upper_open(); + ext_numeral low = src1.inf(); + ext_numeral high = src1.sup(); + if (src2.inf() > low || (src2.inf() == low && !l_open)) { + low = src2.inf(); + l_open = src2.is_lower_open(); + } + if (src2.sup() < high || (src2.sup() == high && !r_open)) { + high = src2.sup(); + r_open = src2.is_upper_open(); + } + if (low > high || (low == high && (l_open || r_open))) { + isempty = true; + return interval(dep()); + } + else { + return interval(dep(), low, l_open, 0, high, r_open, 0); + } + } + + bool interval_relation_plugin::is_infinite(interval const& i) { + return i.plus_infinity() && i.minus_infinity(); + } + + bool interval_relation_plugin::is_empty(unsigned, interval const& i) { + return i.sup() < i.inf(); + } + + class interval_relation_plugin::union_fn : public relation_union_fn { + bool m_is_widen; + public: + union_fn(bool is_widen) : + m_is_widen(is_widen) { + } + + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + + TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + + interval_relation& r = get(_r); + interval_relation const& src = get(_src); + if (_delta) { + interval_relation& d = get(*_delta); + r.mk_union(src, &d, m_is_widen); + } + else { + r.mk_union(src, 0, m_is_widen); + } + } + }; + + relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, false); + } + + relation_union_fn * interval_relation_plugin::mk_widen_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, true); + } + + class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_identical_cols; + public: + filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_identical_cols(col_cnt, identical_cols) {} + + virtual void operator()(relation_base & r) { + interval_relation & pr = get(r); + for (unsigned i = 1; i < m_identical_cols.size(); ++i) { + unsigned c1 = m_identical_cols[0]; + unsigned c2 = m_identical_cols[i]; + pr.equate(c1, c2); + } + } + }; + + relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if(!check_kind(t)) { + return 0; + } + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + + + class interval_relation_plugin::filter_equal_fn : public relation_mutator_fn { + unsigned m_col; + rational m_value; + public: + filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) + : m_col(col) { + arith_util arith(m.get_context().get_manager()); + VERIFY(arith.is_numeral(value, m_value)); + } + + virtual void operator()(relation_base & _r) { + interval_relation & r = get(_r); + interval_relation_plugin & p = r.get_plugin(); + r.mk_intersect(m_col, interval(p.dep(), m_value)); + TRACE("interval_relation", tout << m_value << "\n"; r.display(tout);); + } + }; + + relation_mutator_fn * interval_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if(check_kind(r)) { + return alloc(filter_equal_fn, get_manager(), value, col); + } + return 0; + } + + + class interval_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + app_ref m_cond; + public: + filter_interpreted_fn(interval_relation const& t, app* cond): + m_cond(cond, t.get_plugin().get_ast_manager()) { + } + + void operator()(relation_base& t) { + get(t).filter_interpreted(m_cond); + TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); + } + }; + + relation_mutator_fn * interval_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + if (check_kind(t)) { + return alloc(filter_interpreted_fn, get(t), condition); + } + return 0; + } + + interval_relation& interval_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + interval_relation const & interval_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + // ----------------------- + // interval_relation + + interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty): + vector_relation(p, s, is_empty, interval(p.dep())) + { + TRACE("interval_relation", tout << s.size() << "\n";); + } + + void interval_relation::add_fact(const relation_fact & f) { + interval_relation r(get_plugin(), get_signature(), false); + ast_manager& m = get_plugin().get_ast_manager(); + for (unsigned i = 0; i < f.size(); ++i) { + app_ref eq(m); + expr* e = f[i]; + eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e); + r.filter_interpreted(eq.get()); + } + mk_union(r, 0, false); + } + + bool interval_relation::contains_fact(const relation_fact & f) const { + SASSERT(f.size() == get_signature().size()); + interval_relation_plugin& p = get_plugin(); + + for (unsigned i = 0; i < f.size(); ++i) { + if (f[i] != f[find(i)]) { + return false; + } + interval const& iv = (*this)[i]; + if (p.is_infinite(iv)) { + continue; + } + rational v; + if (p.m_arith.is_numeral(f[i], v)) { + if (!iv.contains(v)) { + return false; + } + } + else { + // TBD: may or must? + } + } + return true; + } + + interval_relation * interval_relation::clone() const { + interval_relation* result = alloc(interval_relation, get_plugin(), get_signature(), empty()); + result->copy(*this); + return result; + } + + interval_relation * interval_relation::complement(func_decl*) const { + UNREACHABLE(); + return 0; + } + + void interval_relation::to_formula(expr_ref& fml) const { + ast_manager& m = get_plugin().get_ast_manager(); + arith_util& arith = get_plugin().m_arith; + expr_ref_vector conjs(m); + relation_signature const& sig = get_signature(); + for (unsigned i = 0; i < sig.size(); ++i) { + if (i != find(i)) { + conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), + m.mk_var(find(i), sig[find(i)]))); + continue; + } + interval const& iv = (*this)[i]; + sort* ty = sig[i]; + expr_ref var(m.mk_var(i, ty), m); + if (!iv.minus_infinity()) { + expr* lo = arith.mk_numeral(iv.get_lower_value(), ty); + if (iv.is_lower_open()) { + conjs.push_back(arith.mk_lt(lo, var)); + } + else { + conjs.push_back(arith.mk_le(lo, var)); + } + } + if (!iv.plus_infinity()) { + expr* hi = arith.mk_numeral(iv.get_upper_value(), ty); + if (iv.is_upper_open()) { + conjs.push_back(arith.mk_lt(var, hi)); + } + else { + conjs.push_back(arith.mk_le(var, hi)); + } + } + } + bool_rewriter br(m); + br.mk_and(conjs.size(), conjs.c_ptr(), fml); + } + + + void interval_relation::display_index(unsigned i, interval const& j, std::ostream & out) const { + out << i << " in " << j << "\n"; + } + + interval_relation_plugin& interval_relation::get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + void interval_relation::mk_intersect(unsigned idx, interval const& i) { + bool isempty; + (*this)[idx] = mk_intersect((*this)[idx], i, isempty); + if (isempty || is_empty(idx, (*this)[idx])) { + set_empty(); + } + } + + void interval_relation::mk_rename_elem(interval& i, unsigned, unsigned const* ) { + + } + + void interval_relation::filter_interpreted(app* cond) { + interval_relation_plugin& p = get_plugin(); + rational k; + unsigned x, y; + if (p.is_lt(cond, x, k, y)) { + // 0 < x - y + k + if (x == UINT_MAX) { + // y < k + mk_intersect(y, interval(p.dep(), k, true, false, 0)); + return; + } + if (y == UINT_MAX) { + // -k < x + mk_intersect(x, interval(p.dep(), -k, true, true, 0)); + return; + } + // y < x + k + ext_numeral x_hi = (*this)[x].sup(); + ext_numeral y_lo = (*this)[y].inf(); + if (!x_hi.is_infinite()) { + mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, 0)); + } + if (!y_lo.is_infinite()) { + mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, 0)); + } + return; + } + bool is_int = false; + if (p.is_le(cond, x, k, y, is_int)) { + // 0 <= x - y + k + if (x == UINT_MAX) { + // y <= k + mk_intersect(y, interval(p.dep(), k, false, false, 0)); + return; + } + if (y == UINT_MAX) { + // -k <= x + mk_intersect(x, interval(p.dep(), -k, false, true, 0)); + return; + } + ext_numeral x_hi = (*this)[x].sup(); + ext_numeral y_lo = (*this)[y].inf(); + if (!x_hi.is_infinite()) { + mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, 0)); + } + if (!y_lo.is_infinite()) { + mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, 0)); + } + return; + } + if (p.is_eq(cond, x, k, y)) { + // y = x + k + if (x == UINT_MAX) { + SASSERT(y != UINT_MAX); + mk_intersect(y, interval(p.dep(), k)); + return; + } + if (y == UINT_MAX) { + // x = - k + SASSERT(x != UINT_MAX); + mk_intersect(x, interval(p.dep(), -k)); + return; + } + interval x_i = (*this)[x]; + interval y_i = (*this)[y]; + x_i += interval(p.dep(), k); + y_i -= interval(p.dep(), k); + mk_intersect(x, y_i); + mk_intersect(y, x_i); + } + if (get_plugin().get_ast_manager().is_false(cond)) { + set_empty(); + } + } + + bool interval_relation_plugin::is_linear(expr* e, unsigned& neg, unsigned& pos, rational& k, bool is_pos) const { +#define SET_VAR(_idx_) \ + if (is_pos &&pos == UINT_MAX) { \ + pos = _idx_; \ + return true; \ + } \ + if (!is_pos && neg == UINT_MAX) { \ + neg = _idx_; \ + return true; \ + } \ + else { \ + return false; \ + } + + if (is_var(e)) { + SET_VAR(to_var(e)->get_idx()); + } + if (!is_app(e)) { + return false; + } + app* a = to_app(e); + + if (m_arith.is_add(e)) { + for (unsigned i = 0; i < a->get_num_args(); ++i) { + if (!is_linear(a->get_arg(i), neg, pos, k, is_pos)) return false; + } + return true; + } + if (m_arith.is_sub(e)) { + SASSERT(a->get_num_args() == 2); + return + is_linear(a->get_arg(0), neg, pos, k, is_pos) && + is_linear(a->get_arg(1), neg, pos, k, !is_pos); + } + rational k1; + SASSERT(!m_arith.is_mul(e) || a->get_num_args() == 2); + if (m_arith.is_mul(e) && + m_arith.is_numeral(a->get_arg(0), k1) && + k1.is_minus_one() && + is_var(a->get_arg(1))) { + SET_VAR(to_var(a->get_arg(1))->get_idx()); + } + + if (m_arith.is_numeral(e, k1)) { + if (is_pos) { + k += k1; + } + else { + k -= k1; + } + return true; + } + return false; + } + + // 0 <= x - y + k + bool interval_relation_plugin::is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const { + ast_manager& m = get_ast_manager(); + k.reset(); + x = UINT_MAX; + y = UINT_MAX; + + if (m_arith.is_le(cond)) { + is_int = m_arith.is_int(cond->get_arg(0)); + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_ge(cond)) { + is_int = m_arith.is_int(cond->get_arg(0)); + if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_lt(cond) && m_arith.is_int(cond->get_arg(0))) { + is_int = true; + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + k -= rational::one(); + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_gt(cond) && m_arith.is_int(cond->get_arg(0))) { + is_int = true; + if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; + k += rational::one(); + return (x != UINT_MAX || y != UINT_MAX); + } + if (m.is_not(cond) && is_app(cond->get_arg(0))) { + // not (0 <= x - y + k) + // <=> + // 0 > x - y + k + // <=> + // 0 <= y - x - k - 1 + if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) { + k.neg(); + k -= rational::one(); + std::swap(x, y); + return true; + } + // not (0 < x - y + k) + // <=> + // 0 >= x - y + k + // <=> + // 0 <= y - x - k + if (is_lt(to_app(cond->get_arg(0)), x, k, y)) { + is_int = false; + k.neg(); + std::swap(x, y); + return true; + } + } + return false; + } + + // 0 < x - y + k + bool interval_relation_plugin::is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const { + k.reset(); + x = UINT_MAX; + y = UINT_MAX; + if (m_arith.is_lt(cond) && m_arith.is_real(cond->get_arg(0))) { + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_gt(cond) && m_arith.is_real(cond->get_arg(0))) { + if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + return false; + } + + // 0 = x - y + k + bool interval_relation_plugin::is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const { + ast_manager& m = get_ast_manager(); + k.reset(); + x = UINT_MAX; + y = UINT_MAX; + if (m.is_eq(cond)) { + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + return false; + } + +}; + diff --git a/src/muz/rel/dl_interval_relation.h b/src/muz/rel/dl_interval_relation.h new file mode 100644 index 000000000..703cb43c5 --- /dev/null +++ b/src/muz/rel/dl_interval_relation.h @@ -0,0 +1,142 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_interval_relation.h + +Abstract: + + Basic interval reatlion. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ +#ifndef _DL_INTERVAL_RELATION_H_ +#define _DL_INTERVAL_RELATION_H_ + + +#include "dl_context.h" +#include "dl_relation_manager.h" +#include "dl_base.h" +#include "old_interval.h" +#include "dl_vector_relation.h" +#include "arith_decl_plugin.h" +#include "basic_simplifier_plugin.h" + +namespace datalog { + + class interval_relation; + + class interval_relation_plugin : public relation_plugin { + v_dependency_manager m_dep; + interval m_empty; + arith_util m_arith; + + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + friend class interval_relation; + + interval unite(interval const& src1, interval const& src2); + interval widen(interval const& src1, interval const& src2); + interval meet(interval const& src1, interval const& src2, bool& is_empty); + + v_dependency_manager & dep() const { return const_cast(m_dep); } + + public: + interval_relation_plugin(relation_manager& m); + virtual bool can_handle_signature(const relation_signature & s); + static symbol get_name() { return symbol("interval_relation"); } + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + static bool is_empty(unsigned idx, interval const& i); + static bool is_infinite(interval const& i); + + private: + static interval_relation& get(relation_base& r); + static interval_relation const & get(relation_base const& r); + + bool is_linear(expr* e, unsigned& pos, unsigned& neg, rational& k, bool is_pos) const; + + // x + k <= y + bool is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const; + // x + k < y + bool is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const; + // x + k = y + bool is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const; + }; + + + class interval_relation : public vector_relation { + friend class interval_relation_plugin; + friend class interval_relation_plugin::filter_equal_fn; + public: + interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty); + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual interval_relation * clone() const; + virtual interval_relation * complement(func_decl*) const; + virtual void to_formula(expr_ref& fml) const; + interval_relation_plugin& get_plugin() const; + + void filter_interpreted(app* cond); + virtual bool is_precise() const { return false; } + + private: + + virtual interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const { + return get_plugin().meet(t1, t2, is_empty); + } + + virtual interval mk_unite(interval const& t1, interval const& t2) const { return get_plugin().unite(t1,t2); } + + virtual interval mk_widen(interval const& t1, interval const& t2) const { return get_plugin().widen(t1,t2); } + + virtual bool is_subset_of(interval const& t1, interval const& t2) const { NOT_IMPLEMENTED_YET(); return false; } + + virtual bool is_full(interval const& t) const { + return interval_relation_plugin::is_infinite(t); + } + + virtual bool is_empty(unsigned idx, interval const& t) const { + return interval_relation_plugin::is_empty(idx, t); + } + + virtual void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle); + + virtual void display_index(unsigned idx, interval const & i, std::ostream& out) const; + + void mk_intersect(unsigned idx, interval const& i); + + }; + +}; + +#endif + diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp new file mode 100644 index 000000000..60a10190a --- /dev/null +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -0,0 +1,879 @@ +/*++ +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_pp.h" +#include"ast_smt_pp.h" +#include"dl_finite_product_relation.h" +#include"dl_product_relation.h" +#include"dl_sieve_relation.h" +#include"dl_mk_explanations.h" + +namespace datalog { + + // ----------------------------------- + // + // explanation_relation_plugin declaration + // + // ----------------------------------- + + class explanation_relation; + + class explanation_relation_plugin : public relation_plugin { + friend class explanation_relation; + + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class foreign_union_fn; + class assignment_filter_fn; + class negation_filter_fn; + class intersection_filter_fn; + + bool m_relation_level_explanations; + + func_decl_ref m_union_decl; + + vector > m_pool; + + + app * mk_union(app * a1, app * a2) { + return get_ast_manager().mk_app(m_union_decl, a1, a2); + } + + public: + static symbol get_name(bool relation_level) { + return symbol(relation_level ? "relation_explanation" : "fact_explanation"); + } + + explanation_relation_plugin(bool relation_level, relation_manager & manager) + : relation_plugin(get_name(relation_level), manager), + m_relation_level_explanations(relation_level), + m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {} + + ~explanation_relation_plugin() { + for (unsigned i = 0; i < m_pool.size(); ++i) { + for (unsigned j = 0; j < m_pool[i].size(); ++j) { + dealloc(m_pool[i][j]); + } + } + } + + virtual bool can_handle_signature(const relation_signature & s) { + unsigned n=s.size(); + for (unsigned i=0; i(relation_base::get_plugin()); + } + + virtual void to_formula(expr_ref& fml) const { + ast_manager& m = fml.get_manager(); + fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]); + } + + bool is_undefined(unsigned col_idx) const { + return m_data[col_idx]==0; + } + bool no_undefined() const { + if (empty()) { + return true; + } + unsigned n = get_signature().size(); + for (unsigned i=0; i(get_plugin().mk_empty(get_signature())); + res->m_empty = m_empty; + SASSERT(res->m_data.empty()); + res->m_data.append(m_data); + return res; + } + + virtual relation_base * complement(func_decl* pred) const { + explanation_relation * res = static_cast(get_plugin().mk_empty(get_signature())); + if (empty()) { + res->set_undefined(); + } + return res; + } + + void display_explanation(app * expl, std::ostream & out) const { + if (expl) { + //TODO: some nice explanation output + ast_smt_pp pp(get_plugin().get_ast_manager()); + pp.display_expr_smt2(out, expl); + } + else { + out << ""; + } + } + + virtual void display(std::ostream & out) const { + if (empty()) { + out << "\n"; + return; + } + unsigned sz = get_signature().size(); + for (unsigned i=0; i s.size() && !m_pool[s.size()].empty()) { + explanation_relation* r = m_pool[s.size()].back(); + m_pool[s.size()].pop_back(); + r->m_empty = true; + r->m_data.reset(); + return r; + } + return alloc(explanation_relation, *this, s); + } + + void explanation_relation_plugin::recycle(explanation_relation* r) { + relation_signature const& sig = r->get_signature(); + if (m_pool.size() <= sig.size()) { + m_pool.resize(sig.size()+1); + } + m_pool[sig.size()].push_back(r); + } + + + class explanation_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & sig1, const relation_signature & sig2) + : convenient_relation_join_fn(sig1, sig2, 0, 0, 0) {} + + virtual relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) { + const explanation_relation & r1 = static_cast(r1_0); + const explanation_relation & r2 = static_cast(r2_0); + explanation_relation_plugin & plugin = r1.get_plugin(); + + explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); + if (!r1.empty() && !r2.empty()) { + res->m_empty = false; + SASSERT(res->m_data.empty()); + res->m_data.append(r1.m_data); + res->m_data.append(r2.m_data); + } + return res; + } + }; + + relation_join_fn * explanation_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) { + return 0; + } + if (col_cnt!=0) { + return 0; + } + return alloc(join_fn, r1.get_signature(), r2.get_signature()); + } + + + class explanation_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(sig, col_cnt, removed_cols) {} + + virtual relation_base * operator()(const relation_base & r0) { + const explanation_relation & r = static_cast(r0); + explanation_relation_plugin & plugin = r.get_plugin(); + + explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); + if (!r.empty()) { + relation_fact proj_data = r.m_data; + project_out_vector_columns(proj_data, m_removed_cols); + res->assign_data(proj_data); + } + return res; + } + }; + + relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, + const unsigned * removed_cols) { + if (&r.get_plugin()!=this) { + return 0; + } + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + + class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) + : convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {} + + virtual relation_base * operator()(const relation_base & r0) { + const explanation_relation & r = static_cast(r0); + explanation_relation_plugin & plugin = r.get_plugin(); + + explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); + if (!r.empty()) { + relation_fact permutated_data = r.m_data; + permutate_by_cycle(permutated_data, m_cycle); + res->assign_data(permutated_data); + } + return res; + } + }; + + relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned permutation_cycle_len, const unsigned * permutation_cycle) { + return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle); + } + + + class explanation_relation_plugin::union_fn : public relation_union_fn { + scoped_ptr m_delta_union_fun; + public: + virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) { + explanation_relation & tgt = static_cast(tgt0); + const explanation_relation & src = static_cast(src0); + explanation_relation * delta = delta0 ? static_cast(delta0) : 0; + explanation_relation_plugin & plugin = tgt.get_plugin(); + + if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { + UNREACHABLE(); + } + if (src.empty()) { + return; + } + if (plugin.m_relation_level_explanations) { + tgt.unite_with_data(src.m_data); + if (delta) { + if (!m_delta_union_fun) { + m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); + SASSERT(m_delta_union_fun); + } + (*m_delta_union_fun)(*delta, src); + } + } + else { + if (tgt.empty()) { + tgt.assign_data(src.m_data); + if (delta && delta->empty()) { + delta->assign_data(src.m_data); + } + } + } + } + }; + + class explanation_relation_plugin::foreign_union_fn : public relation_union_fn { + scoped_ptr m_delta_union_fun; + public: + virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) { + explanation_relation & tgt = static_cast(tgt0); + explanation_relation * delta = delta0 ? static_cast(delta0) : 0; + + if (src.empty()) { + return; + } + tgt.set_undefined(); + if (delta) { + delta->set_undefined(); + } + } + }; + + relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || (delta && !check_kind(*delta))) { + return 0; + } + if (!check_kind(src)) { + //this is to handle the product relation + return alloc(foreign_union_fn); + } + return alloc(union_fn); + } + + class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn { + ast_manager & m_manager; + var_subst & m_subst; + unsigned m_col_idx; + app_ref m_new_rule; + public: + assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule) + : m_manager(ctx.get_manager()), + m_subst(ctx.get_var_subst()), + m_col_idx(col_idx), + m_new_rule(new_rule) {} + + virtual void operator()(relation_base & r0) { + explanation_relation & r = static_cast(r0); + + if (!r.is_undefined(m_col_idx)) { + UNREACHABLE(); + } + + unsigned sz = r.get_signature().size(); + ptr_vector subst_arg; + subst_arg.resize(sz, 0); + unsigned ofs = sz-1; + for (unsigned i=0; iget_arg(0); + expr * arg2 = cond->get_arg(1); + + if (is_var(arg2)) { + std::swap(arg1, arg2); + } + + if (!is_var(arg1) || !is_app(arg2)) { + return 0; + } + var * col_var = to_var(arg1); + app * new_rule = to_app(arg2); + if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { + return 0; + } + unsigned col_idx = col_var->get_idx(); + + return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager())); + } + + + class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { + public: + virtual void operator()(relation_base & r, const relation_base & neg) { + if (!neg.empty()) { + r.reset(); + } + } + }; + + relation_intersection_filter_fn * explanation_relation_plugin::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) { + if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { + return 0; + } + return alloc(negation_filter_fn); + } + + class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn { + func_decl_ref m_union_decl; + public: + intersection_filter_fn(explanation_relation_plugin & plugin) + : m_union_decl(plugin.m_union_decl) {} + + virtual void operator()(relation_base & tgt0, const relation_base & src0) { + explanation_relation & tgt = static_cast(tgt0); + const explanation_relation & src = static_cast(src0); + + if (src.empty()) { + tgt.reset(); + return; + } + if (tgt.empty()) { + return; + } + unsigned sz = tgt.get_signature().size(); + for (unsigned i=0; iget_decl()==m_union_decl.get()) { + if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { + tgt.m_data.set(i, curr_src); + continue; + } + } + //the intersection is imprecise because we do nothing here, but it is good enough for + //the purpose of explanations + } + } + }; + + relation_intersection_filter_fn * explanation_relation_plugin::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) { + if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { + return 0; + } + //this checks the join is one to one on all columns + if (tgt.get_signature()!=src.get_signature() + || joined_col_cnt!=tgt.get_signature().size() + || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { + return 0; + } + counter ctr; + ctr.count(joined_col_cnt, tgt_cols); + if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { + return 0; + } + return alloc(intersection_filter_fn, *this); + } + + + // ----------------------------------- + // + // mk_explanations + // + // ----------------------------------- + + + mk_explanations::mk_explanations(context & ctx) + : plugin(50000), + m_manager(ctx.get_manager()), + m_context(ctx), + m_decl_util(ctx.get_decl_util()), + m_relation_level(ctx.explanations_on_relation_level()), + m_pinned(m_manager) { + m_e_sort = m_decl_util.mk_rule_sort(); + m_pinned.push_back(m_e_sort); + + relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); + symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); + m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); + if (!m_er_plugin) { + m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr); + rmgr.register_plugin(m_er_plugin); + if (!m_relation_level) { + DEBUG_CODE( + finite_product_relation_plugin * dummy; + SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); + ); + rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr)); + } + } + DEBUG_CODE( + if (!m_relation_level) { + finite_product_relation_plugin * dummy; + SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); + } + ); + } + + func_decl * mk_explanations::get_union_decl(context & ctx) { + ast_manager & m = ctx.get_manager(); + sort_ref s(ctx.get_decl_util().mk_rule_sort(), m); + //can it happen that the function name would collide with some other symbol? + //if functions can be overloaded by their ranges, it should be fine. + return m.mk_func_decl(symbol("e_union"), s, s, s); + } + + void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) { + SASSERT(m_relation_level); + + relation_manager & rmgr = m_context.get_rel_context()->get_rmanager(); + unsigned sz = e_decl->get_arity(); + relation_signature sig; + rmgr.from_predicate(e_decl, sig); + + svector inner_sieve(sz-1, true); + inner_sieve.push_back(false); + + svector expl_sieve(sz-1, false); + expl_sieve.push_back(true); + + sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr); + + family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id + family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind); + family_id expl_kind = m_er_plugin->get_kind(); + family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind); + + product_relation_plugin::rel_spec product_spec; + product_spec.push_back(inner_sieve_kind); + product_spec.push_back(expl_sieve_kind); + + family_id pred_kind = + product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec); + + rmgr.set_predicate_kind(e_decl, pred_kind); + } + + func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) { + decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0); + if (e->get_data().m_value==0) { + relation_signature e_domain; + e_domain.append(orig_decl->get_arity(), orig_decl->get_domain()); + e_domain.push_back(m_e_sort); + func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"), + e_domain.size(), e_domain.c_ptr(), orig_decl); + m_pinned.push_back(new_decl); + e->get_data().m_value = new_decl; + + if (m_relation_level) { + assign_rel_level_kind(new_decl, orig_decl); + } + } + return e->get_data().m_value; + } + + app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) { + expr_ref_vector args(m_manager); + func_decl * e_decl = get_e_decl(lit->get_decl()); + args.append(lit->get_num_args(), lit->get_args()); + args.push_back(m_manager.mk_var(e_var_idx, m_e_sort)); + return m_manager.mk_app(e_decl, args.c_ptr()); + } + + symbol mk_explanations::get_rule_symbol(rule * r) { + if (r->name() == symbol::null) { + std::stringstream sstm; + r->display(m_context, sstm); + std::string res = sstm.str(); + res = res.substr(0, res.find_last_not_of('\n')+1); + return symbol(res.c_str()); + } + else { + return r->name(); + } + } + + rule * mk_explanations::get_e_rule(rule * r) { + rule_counter ctr; + ctr.count_rule_vars(m_manager, r); + unsigned max_var; + unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0; + unsigned head_var = next_var++; + app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager); + + app_ref_vector e_tail(m_manager); + svector neg_flags; + unsigned pos_tail_sz = r->get_positive_tail_size(); + for (unsigned i=0; iget_tail(i), e_var)); + neg_flags.push_back(false); + } + unsigned tail_sz = r->get_tail_size(); + for (unsigned i=pos_tail_sz; iget_tail(i)); + neg_flags.push_back(r->is_neg_tail(i)); + } + + symbol rule_repr = get_rule_symbol(r); + + expr_ref_vector rule_expr_args(m_manager); + for (unsigned tail_idx=0; tail_idxget_arg(tail->get_num_args()-1)); + } + else { + //this adds argument values and the explanation term + //(values will be substituted for variables at runtime by the finite_product_relation) + rule_expr_args.append(tail->get_num_args(), tail->get_args()); + } + } + //rule_expr contains rule function with string representation of the rule as symbol and + //for each positive uninterpreted tail it contains its argument values and its explanation term + expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr()); + + app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager); + e_tail.push_back(e_record); + neg_flags.push_back(false); + SASSERT(e_tail.size()==neg_flags.size()); + + return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr()); + } + + void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) { + rule_set::iterator rit = src.begin(); + rule_set::iterator rend = src.end(); + for (; rit!=rend; ++rit) { + rule * e_rule = get_e_rule(*rit); + dst.add_rule(e_rule); + } + + //add rules that will (for output predicates) copy facts from explained relations back to + //the original ones + expr_ref_vector lit_args(m_manager); + decl_set::iterator pit = src.get_output_predicates().begin(); + decl_set::iterator pend = src.get_output_predicates().end(); + for (; pit != pend; ++pit) { + func_decl * orig_decl = *pit; + + lit_args.reset(); + unsigned arity = orig_decl->get_arity(); + for (unsigned i=0; iget_domain(i))); + } + app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); + app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); + app * tail[] = { e_lit.get() }; + dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); + } + } + + void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, + relation_base & e_rel) { + SASSERT(m_e_fact_relation); + SASSERT(e_rel.get_plugin().is_product_relation()); + + product_relation & prod_rel = static_cast(e_rel); + SASSERT(prod_rel.size()==2); + SASSERT(prod_rel[0].get_plugin().is_sieve_relation()); + SASSERT(prod_rel[1].get_plugin().is_sieve_relation()); + sieve_relation * srels[] = { + static_cast(&prod_rel[0]), + static_cast(&prod_rel[1]) }; + if (&srels[0]->get_inner().get_plugin()==m_er_plugin) { + std::swap(srels[0], srels[1]); + } + SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin()); + SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin); + + relation_base & new_orig = srels[0]->get_inner(); + explanation_relation & expl_rel = static_cast(srels[1]->get_inner()); + + { + scoped_ptr orig_union_fun = rmgr.mk_union_fn(new_orig, orig); + SASSERT(orig_union_fun); + (*orig_union_fun)(new_orig, orig); + } + + { + scoped_ptr expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation); + SASSERT(expl_union_fun); + (*expl_union_fun)(expl_rel, *m_e_fact_relation); + } + } + + void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { + + if (!m_e_fact_relation) { + relation_signature expl_singleton_sig; + expl_singleton_sig.push_back(m_e_sort); + + relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind()); + relation_fact es_fact(m_manager); + es_fact.push_back(m_decl_util.mk_fact(symbol("fact"))); + expl_singleton->add_fact(es_fact); + + SASSERT(&expl_singleton->get_plugin()==m_er_plugin); + m_e_fact_relation = static_cast(expl_singleton); + } + func_decl_set const& predicates = m_context.get_predicates(); + decl_set::iterator it = predicates.begin(); + decl_set::iterator end = predicates.end(); + for (; it!=end; ++it) { + func_decl * orig_decl = *it; + func_decl * e_decl = get_e_decl(orig_decl); + + if (!rmgr.try_get_relation(orig_decl) && + !src.contains(orig_decl)) { + // there are no facts or rules for this predicate + continue; + } + + dst.inherit_predicate(src, orig_decl, e_decl); + + relation_base & orig_rel = rmgr.get_relation(orig_decl); + relation_base & e_rel = rmgr.get_relation(e_decl); + SASSERT(e_rel.empty()); //the e_rel should be a new relation + + if (m_relation_level) { + translate_rel_level_relation(rmgr, orig_rel, e_rel); + } + else { + scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0); + SASSERT(product_fun); + scoped_rel aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation); + scoped_ptr union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel); + SASSERT(union_fun); + (*union_fun)(e_rel, *aux_extended_rel); + } + } + } + + rule_set * mk_explanations::operator()(rule_set const & source) { + + if (source.empty()) { + return 0; + } + if (!m_context.generate_explanations()) { + return 0; + } + rule_set * res = alloc(rule_set, m_context); + transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); + transform_rules(source, *res); + return res; + } + +}; + diff --git a/src/muz/rel/dl_mk_explanations.h b/src/muz/rel/dl_mk_explanations.h new file mode 100644 index 000000000..9e4d705c3 --- /dev/null +++ b/src/muz/rel/dl_mk_explanations.h @@ -0,0 +1,86 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_explanations.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-11-08. + +Revision History: + +--*/ + +#ifndef _DL_MK_EXPLANATIONS_H_ +#define _DL_MK_EXPLANATIONS_H_ + +#include "dl_context.h" +#include "dl_rule_transformer.h" + +namespace datalog { + + class explanation_relation; + class explanation_relation_plugin; + + class mk_explanations : public rule_transformer::plugin { + + typedef obj_map decl_map; + + ast_manager & m_manager; + context & m_context; + dl_decl_util & m_decl_util; + bool m_relation_level; + ast_ref_vector m_pinned; + explanation_relation_plugin * m_er_plugin; + sort * m_e_sort; + scoped_rel m_e_fact_relation; + + decl_map m_e_decl_map; + + symbol get_rule_symbol(rule * r); + + app * get_e_lit(app * lit, unsigned e_var_idx); + rule * get_e_rule(rule * r); + + /** + If \c m_relation_level is true, ensure \c e_decl predicate will be represented by + the right relation object. \c orig is the predicate corresponding to \c e_decl without + the explanation column. + */ + void assign_rel_level_kind(func_decl * e_decl, func_decl * orig); + void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel); + + void transform_rules(const rule_set & src, rule_set & dst); + + void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst); + public: + /** + If relation_level is true, the explanation will not be stored for each fact, + but we will rather store history of the whole relation. + */ + mk_explanations(context & ctx); + + /** + \brief Return explanation predicate that corresponds to \c orig_decl. + */ + func_decl * get_e_decl(func_decl * orig_decl); + + static func_decl * get_union_decl(context & ctx); + func_decl * get_union_decl() const { + return get_union_decl(m_context); + } + + rule_set * operator()(rule_set const & source); + + static expr* get_explanation(relation_base const& r); + }; +}; + +#endif /* _DL_MK_EXPLANATIONS_H_ */ + diff --git a/src/muz/rel/dl_mk_partial_equiv.cpp b/src/muz/rel/dl_mk_partial_equiv.cpp new file mode 100644 index 000000000..d79a46720 --- /dev/null +++ b/src/muz/rel/dl_mk_partial_equiv.cpp @@ -0,0 +1,155 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + dl_mk_partial_equiv.cpp + +Abstract: + + Rule transformer which identifies predicates that are partial equivalence relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-05-14 + +Revision History: + +--*/ + +#include "dl_mk_partial_equiv.h" +#include "dl_relation_manager.h" +#include "ast_pp.h" + +namespace datalog { + + bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) { + func_decl* p = r->get_decl(); + return + p->get_arity() == 2 && + p->get_domain(0) == p->get_domain(1) && + r->get_tail_size() == 1 && + r->get_tail(0)->get_decl() == p && + r->get_head()->get_arg(0) == r->get_tail(0)->get_arg(1) && + r->get_head()->get_arg(1) == r->get_tail(0)->get_arg(0) && + is_var(r->get_head()->get_arg(0)) && + is_var(r->get_head()->get_arg(1)) && + r->get_head()->get_arg(0) != r->get_head()->get_arg(1); + } + + + bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { + func_decl* p = r->get_decl(); + if (p->get_arity() != 2 || + p->get_domain(0) != p->get_domain(1) || + r->get_tail_size() != 2 || + r->get_tail(0)->get_decl() != p || + r->get_tail(1)->get_decl() != p) { + return false; + } + app* h = r->get_head(); + app* a = r->get_tail(0); + app* b = r->get_tail(1); + expr* x1 = h->get_arg(0); + expr* x2 = h->get_arg(1); + expr* a1 = a->get_arg(0); + expr* a2 = a->get_arg(1); + expr* b1 = b->get_arg(0); + expr* b2 = b->get_arg(1); + + if (!(is_var(x1) && is_var(x2) && is_var(a1) && is_var(a2) && is_var(b1) && is_var(b2))) { + return false; + } + if (x1 == x2 || a1 == a2 || b1 == b2) { + return false; + } + if (a2 == b1) { + if (x1 == b2 && x2 == a1) { + return true; + } + if (x1 == a1 && x2 == b2) { + return true; + } + return false; + } + if (a1 == b2) { + if (x1 == b1 && x2 == a2) { + return true; + } + if (x1 == a2 && x2 == b1) { + return true; + } + return false; + } + + return false; +; + } + + + rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) { + // TODO mc + + if (source.get_num_rules() == 0) { + return 0; + } + + if (m_context.get_engine() != DATALOG_ENGINE) { + return 0; + } + + relation_manager & rm = m_context.get_rel_context()->get_rmanager(); + rule_set::decl2rules::iterator it = source.begin_grouped_rules(); + rule_set::decl2rules::iterator end = source.end_grouped_rules(); + + rule_set* res = alloc(rule_set, m_context); + + for (; it != end; ++it) { + func_decl* p = it->m_key; + rule_vector const& rv = *(it->m_value); + bool has_symmetry = false; + bool has_transitivity = false; + unsigned i_symmetry, i_transitivity; + family_id kind = rm.get_requested_predicate_kind(p); + for (unsigned i = 0; i < rv.size(); ++i) { + + if (kind != null_family_id) { + res->add_rule(rv[i]); + } + else if (is_symmetry(rv[i])) { + i_symmetry = i; + has_symmetry = true; + } + else if (is_transitivity(rv[i])) { + i_transitivity = i; + has_transitivity = true; + } + else { + res->add_rule(rv[i]); + } + } + if (has_symmetry && !has_transitivity) { + res->add_rule(rv[i_symmetry]); + } + else if (!has_symmetry && has_transitivity) { + res->add_rule(rv[i_transitivity]); + } + else if (has_symmetry && has_transitivity) { + TRACE("dl", tout << "updating predicate " << mk_pp(p, m) << " to partial equivalence\n";); + SASSERT(kind == null_family_id); + rm.set_predicate_kind(p, rm.get_table_plugin(symbol("equivalence"))->get_kind()); + } + } + + if (res->get_num_rules() == source.get_num_rules()) { + dealloc(res); + return 0; + } + res->inherit_predicates(source); + + return res; + } + +}; + + diff --git a/src/muz/rel/dl_mk_partial_equiv.h b/src/muz/rel/dl_mk_partial_equiv.h new file mode 100644 index 000000000..54a70b3c0 --- /dev/null +++ b/src/muz/rel/dl_mk_partial_equiv.h @@ -0,0 +1,50 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + dl_mk_partial_equiv.h + +Abstract: + + Rule transformer which identifies predicates that are partial equivalence relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-05-14 + +Revision History: + +--*/ + + +#ifndef _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ +#define _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ + +#include "dl_context.h" +#include "dl_rule_transformer.h" + +namespace datalog { + + class mk_partial_equivalence_transformer : public rule_transformer::plugin { + ast_manager & m; + context & m_context; + public: + mk_partial_equivalence_transformer(context & ctx, unsigned priority=45000) + : plugin(priority), + m(ctx.get_manager()), + m_context(ctx) {} + + rule_set * operator()(rule_set const & source); + + private: + + bool is_symmetry(rule const* r); + bool is_transitivity(rule const* r); + }; + +}; + +#endif /* _DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */ + + diff --git a/src/muz/rel/dl_mk_similarity_compressor.cpp b/src/muz/rel/dl_mk_similarity_compressor.cpp new file mode 100644 index 000000000..aa2fe8ab9 --- /dev/null +++ b/src/muz/rel/dl_mk_similarity_compressor.cpp @@ -0,0 +1,546 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_similarity_compressor.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-10-22. + +Revision History: + +--*/ + +#include +#include +#include"dl_mk_similarity_compressor.h" +#include"dl_relation_manager.h" + +namespace datalog { + + mk_similarity_compressor::mk_similarity_compressor(context & ctx) : + plugin(5000), + m_context(ctx), + m_manager(ctx.get_manager()), + m_threshold_count(ctx.similarity_compressor_threshold()), + m_result_rules(ctx.get_rule_manager()), + m_modified(false), + m_pinned(m_manager) { + SASSERT(m_threshold_count>1); + } + + void mk_similarity_compressor::reset() { + m_rules.reset(); + m_result_rules.reset(); + m_pinned.reset(); + } + + /** + Allows to traverse head and positive tails in a single for loop starting from -1 + */ + static app * get_by_tail_index(rule * r, int idx) { + if (idx < 0) { + return r->get_head(); + } + SASSERT(idx < static_cast(r->get_positive_tail_size())); + return r->get_tail(idx); + } + + template + static int aux_compare(T a, T b) { + return (a>b) ? 1 : ( (a==b) ? 0 : -1); + } + + template + static int aux_compare(T* a, T* b); + + static int compare_var_args(app* t1, app* t2) { + SASSERT(t1->get_num_args()==t2->get_num_args()); + int res; + unsigned n = t1->get_num_args(); + for (unsigned i = 0; i < n; i++) { + expr * a1 = t1->get_arg(i); + expr * a2 = t2->get_arg(i); + res = aux_compare(is_var(a1), is_var(a2)); + if (res != 0) { + return res; + } + if (is_var(a1)) { + res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx()); + if (res != 0) { + return res; + } + } + } + return 0; + } + + static int compare_args(app* t1, app* t2, int & skip_countdown) { + SASSERT(t1->get_num_args()==t2->get_num_args()); + int res; + unsigned n = t1->get_num_args(); + for (unsigned i=0; iget_arg(i))) { + SASSERT(t1->get_arg(i) == t2->get_arg(i)); + continue; + } + if ((skip_countdown--) == 0) { + continue; + } + res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); + if (res!=0) { return res; } + } + return 0; + } + + /** + \brief Return 0 if r1 and r2 could be similar. If the rough similarity + equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1. + + Two rules are in the same rough similarity class if they differ only in constant arguments + of positive uninterpreted predicates. + */ + static int rough_compare(rule * r1, rule * r2) { + int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); + if (res!=0) { return res; } + res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); + if (res!=0) { return res; } + res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); + if (res!=0) { return res; } + + int pos_tail_sz = r1->get_positive_tail_size(); + for (int i=-1; iget_decl()->get_id(), t2->get_decl()->get_id()); + if (res!=0) { return res; } + res = compare_var_args(t1, t2); + if (res!=0) { return res; } + } + + unsigned tail_sz = r1->get_tail_size(); + for (unsigned i=pos_tail_sz; iget_tail(i)->get_id(), r2->get_tail(i)->get_id()); + if (res!=0) { return res; } + } + + return 0; + } + + /** + \c r1 and \c r2 must be equal according to the \c rough_compare function for this function + to be called. + */ + static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { + SASSERT(rough_compare(r1, r2)==0); + int pos_tail_sz = r1->get_positive_tail_size(); + for (int i=-1; i info_vector; + + static void collect_const_indexes(app * t, int tail_index, info_vector & res) { + unsigned n = t->get_num_args(); + for (unsigned i=0; iget_arg(i))) { + continue; + } + res.push_back(const_info(tail_index, i)); + } + } + + static void collect_const_indexes(rule * r, info_vector & res) { + collect_const_indexes(r->get_head(), -1, res); + unsigned pos_tail_sz = r->get_positive_tail_size(); + for (unsigned i=0; iget_tail(i), i, res); + } + } + + template + static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { + unsigned const_cnt = const_infos.size(); + tgt.reset(); + for (unsigned i=0; iget_arg(inf.arg_index()))); + SASSERT(tgt.back()->get_num_args()==0); + } + } + template + static void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { + unsigned const_cnt = const_infos.size(); + tgt.reset(); + for (unsigned i=0; iget_decl()->get_domain(inf.arg_index())); + } + } + + /** + \brief From the \c tail_indexes and \c arg_indexes remove elements corresponding to constants + that are the same in rules \c *first ... \c *(after_last-1). + */ + static void remove_stable_constants(rule_vector::iterator first, rule_vector::iterator after_last, + info_vector & const_infos) { + SASSERT(after_last-first>1); + unsigned const_cnt = const_infos.size(); + ptr_vector vals; + rule * r = *(first++); + collect_orphan_consts(r, const_infos, vals); + SASSERT(vals.size()==const_cnt); + rule_vector::iterator it = first; + for (; it!=after_last; ++it) { + for (unsigned i=0; iget_arg(const_infos[i].arg_index())); + if (vals[i]!=val) { + vals[i] = 0; + } + } + } + unsigned removed_cnt = 0; + for (unsigned i=0; i vals; + ptr_vector sorts; + rule * r = *(first++); + collect_orphan_consts(r, const_infos, vals); + collect_orphan_sorts(r, const_infos, sorts); + SASSERT(vals.size()==const_cnt); + vector possible_parents(const_cnt); + for (unsigned i=1; iget_head()->get_num_args() - count_variable_arguments(r->get_head()); + unsigned pos_tail_sz = r->get_positive_tail_size(); + for (unsigned i=0; iget_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i)); + } + return res; + } + + static bool initial_comparator(rule * r1, rule * r2) { + int res = rough_compare(r1, r2); + if (res!=0) { return res>0; } + return total_compare(r1, r2)>0; + } + + class arg_ignoring_comparator { + unsigned m_ignored_index; + public: + arg_ignoring_comparator(unsigned ignored_index) : m_ignored_index(ignored_index) {} + bool operator()(rule * r1, rule * r2) const { + return total_compare(r1, r2, m_ignored_index)>0; + } + bool eq(rule * r1, rule * r2) const { + return total_compare(r1, r2, m_ignored_index)==0; + } + }; + + void mk_similarity_compressor::merge_class(rule_vector::iterator first, + rule_vector::iterator after_last) { + SASSERT(after_last-first>1); + info_vector const_infos; + rule * r = *first; //an arbitrary representative of the class + collect_const_indexes(r, const_infos); + remove_stable_constants(first, after_last, const_infos); + + unsigned const_cnt = const_infos.size(); + SASSERT(const_cnt>0); + + detect_equal_constants(first, after_last, const_infos); + + + //The aux relation contains column for each constant which does not have an earlier constant + //that it is equal to (i.e. only has no parent) + ptr_vector aux_domain; + collect_orphan_sorts(r, const_infos, aux_domain); + + func_decl* head_pred = r->get_decl(); + symbol const& name_prefix = head_pred->get_name(); + std::string name_suffix = "sc_" + to_string(const_cnt); + func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()), + aux_domain.size(), aux_domain.c_ptr(), head_pred); + m_pinned.push_back(aux_pred); + + relation_fact val_fact(m_manager, const_cnt); + rule_vector::iterator it = first; + for (; it!=after_last; ++it) { + collect_orphan_consts(*it, const_infos, val_fact); + m_context.add_fact(aux_pred, val_fact); + } + m_context.get_rel_context()->get_rmanager().mark_saturated(aux_pred); + + app * new_head = r->get_head(); + ptr_vector new_tail; + svector new_negs; + unsigned tail_sz = r->get_tail_size(); + for (unsigned i=0; iget_tail(i)); + new_negs.push_back(r->is_neg_tail(i)); + } + + rule_counter ctr; + ctr.count_rule_vars(m_manager, r); + unsigned max_var_idx, new_var_idx_base; + if (ctr.get_max_positive(max_var_idx)) { + new_var_idx_base = max_var_idx+1; + } + else { + new_var_idx_base = 0; + } + + ptr_vector const_vars; //variables at indexes of their corresponding constants + expr_ref_vector aux_vars(m_manager); //variables as arguments for the auxiliary predicate + + unsigned aux_column_index = 0; + + for (unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); + + for (; iget_decl(), mod_args.c_ptr()); + m_pinned.push_back(upd_tail); + mod_tail = upd_tail; + } + + app_ref aux_tail(m_manager.mk_app(aux_pred, aux_vars.c_ptr()), m_manager); + new_tail.push_back(aux_tail); + new_negs.push_back(false); + + rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), + new_negs.c_ptr()); + m_result_rules.push_back(new_rule); + + //TODO: allow for a rule to have multiple parent objects + new_rule->set_accounting_parent_object(m_context, r); + m_modified = true; + } + + void mk_similarity_compressor::process_class(rule_set const& source, rule_vector::iterator first, + rule_vector::iterator after_last) { + SASSERT(first!=after_last); + //remove duplicates + { + rule_vector::iterator it = first; + rule_vector::iterator prev = it; + ++it; + while(it!=after_last) { + if (it!=after_last && total_compare(*prev, *it)==0) { + --after_last; + std::swap(*it, *after_last); + m_modified = true; + } + else { + prev = it; + ++it; + } + } + } + SASSERT(first!=after_last); + + unsigned const_cnt = get_constant_count(*first); +#if 0 + for (unsigned ignored_index=0; ignored_indexm_threshold_count) { + merge_class(grp_begin, it); + //group was processed, so we remove it from the class + if (it==after_last) { + after_last=grp_begin; + it=after_last; + } + else { + while(it!=grp_begin) { + std::swap(*--it, *--after_last); + } + } + } + grp_begin = it; + grp_size = 0; + } + } + } +#endif + //TODO: compress also rules with pairs (or tuples) of equal constants + +#if 1 + if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) { + unsigned rule_cnt = static_cast(after_last-first); + if (rule_cnt>m_threshold_count) { + merge_class(first, after_last); + return; + } + } +#endif + + //put rules which weren't merged into result + rule_vector::iterator it = first; + for (; it!=after_last; ++it) { + m_result_rules.push_back(*it); + } + } + + rule_set * mk_similarity_compressor::operator()(rule_set const & source) { + // TODO mc + m_modified = false; + unsigned init_rule_cnt = source.get_num_rules(); + SASSERT(m_rules.empty()); + for (unsigned i=0; i(0); + if (m_modified) { + result = alloc(rule_set, m_context); + unsigned fin_rule_cnt = m_result_rules.size(); + for (unsigned i=0; iadd_rule(m_result_rules.get(i)); + } + result->inherit_predicates(source); + } + reset(); + return result; + } +}; diff --git a/src/muz/rel/dl_mk_similarity_compressor.h b/src/muz/rel/dl_mk_similarity_compressor.h new file mode 100644 index 000000000..34b76e7e1 --- /dev/null +++ b/src/muz/rel/dl_mk_similarity_compressor.h @@ -0,0 +1,78 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_similarity_compressor.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-10-22. + +Revision History: + +--*/ +#ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_ +#define _DL_MK_SIMILARITY_COMPRESSOR_H_ + +#include + +#include"map.h" +#include"obj_pair_hashtable.h" + +#include"dl_context.h" +#include"dl_rule_set.h" +#include"dl_rule_transformer.h" + +namespace datalog { + + /** + \brief Functor for merging groups of similar rules. + + A rule sequence + + P("1",x):-Q(x). + ... + P("N",x):-Q(x). + + will be replaced by + + P(y,x):-Q(x), Aux(y). + + and a set of facts + + Aux("1"). + ... + Aux("N"). + + Similar transformation is performed when the varying constant appears in the positive tail. + */ + class mk_similarity_compressor : public rule_transformer::plugin { + + context & m_context; + ast_manager & m_manager; + /** number of similar rules necessary for a group to be introduced */ + unsigned m_threshold_count; + rule_vector m_rules; + rule_ref_vector m_result_rules; + bool m_modified; + ast_ref_vector m_pinned; + + void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); + void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last); + + void reset(); + public: + mk_similarity_compressor(context & ctx); + + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_SIMILARITY_COMPRESSOR_H_ */ + diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp new file mode 100644 index 000000000..4b9ce582a --- /dev/null +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -0,0 +1,742 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_simple_joins.cpp + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2010-05-20. + +Revision History: + +--*/ + +#include +#include +#include +#include"dl_mk_simple_joins.h" +#include"dl_relation_manager.h" +#include"ast_pp.h" +#include"trace.h" + + +namespace datalog { + + mk_simple_joins::mk_simple_joins(context & ctx): + plugin(1000), + m_context(ctx), + rm(ctx.get_rule_manager()) { + } + + class join_planner { + typedef float cost; + + class pair_info { + cost m_total_cost; + /** + \brief Number of rules longer than two that contain this pair. + + This number is being updated by \c add_rule and \remove rule. Even though between + adding a rule and removing it, the length of a rule can decrease without this pair + being notified about it, it will surely see the decrease from length 3 to 2 which + the threshold for rule being counted in this counter. + */ + unsigned m_consumers; + bool m_stratified; + unsigned m_src_stratum; + public: + var_idx_set m_all_nonlocal_vars; + rule_vector m_rules; + + pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {} + + bool can_be_joined() const { + return m_consumers>0; + } + + cost get_cost() const { + /*if(m_instantiated) { + return std::numeric_limits::min(); + }*/ + SASSERT(m_consumers>0); + cost amortized = m_total_cost/m_consumers; + if(m_stratified) { + return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f); + } + else { + return amortized; + } + } + + /** + \brief Add rule \c r among rules interested in current predicate pair. + + The \c pl.m_rule_content entry of the rule has to be properly filled in + by the time of a call to this function + */ + void add_rule(join_planner & pl, app * t1, app * t2, rule * r, + const var_idx_set & non_local_vars_normalized) { + if(m_rules.empty()) { + m_total_cost = pl.compute_cost(t1, t2); + m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl())); + } + m_rules.push_back(r); + if(pl.m_rules_content.find_core(r)->get_data().m_value.size()>2) { + m_consumers++; + } + if(m_stratified) { + unsigned head_stratum = pl.get_stratum(r->get_decl()); + SASSERT(head_stratum>=m_src_stratum); + if(head_stratum==m_src_stratum) { + m_stratified = false; + } + } + idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized); + } + /** + \brief Remove rule from the pair record. Return true if no rules remain + in the pair, and so it should be removed. + */ + bool remove_rule(rule * r, unsigned original_length) { + TRUSTME( remove_from_vector(m_rules, r) ); + if(original_length>2) { + SASSERT(m_consumers>0); + m_consumers--; + } + SASSERT(!m_rules.empty() || m_consumers==0); + return m_rules.empty(); + } + private: + pair_info & operator=(const pair_info &); //to avoid the implicit one + }; + typedef std::pair app_pair; + typedef map, obj_ptr_hash >, default_eq > cost_map; + typedef map, ptr_hash, ptr_eq > rule_pred_map; + + context & m_context; + ast_manager & m; + rule_manager & rm; + var_subst & m_var_subst; + rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels + + cost_map m_costs; + ptr_vector m_interpreted; + rule_pred_map m_rules_content; + rule_ref_vector m_introduced_rules; + ptr_hashtable, ptr_eq > m_modified_rules; + + ast_ref_vector m_pinned; + mutable ptr_vector m_vars; + + public: + join_planner(context & ctx, rule_set & rs_aux_copy) + : m_context(ctx), m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_var_subst(ctx.get_var_subst()), + m_rs_aux_copy(rs_aux_copy), + m_introduced_rules(ctx.get_rule_manager()), + m_pinned(ctx.get_manager()) + { + } + + ~join_planner() + { + cost_map::iterator it = m_costs.begin(); + cost_map::iterator end = m_costs.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + m_costs.reset(); + } + private: + + void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const { + SASSERT(result.size()>0); + unsigned res_ofs = result.size()-1; + unsigned n=t->get_num_args(); + for(unsigned i=0; iget_arg(i))); + var * v = to_var(t->get_arg(i)); + unsigned var_idx = v->get_idx(); + if(result[res_ofs-var_idx]==0) { + result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort()); + next_var++; + } + } + } + + void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const { + SASSERT(result.empty()); + if(t1->get_num_args()==0 && t2->get_num_args()==0) { + return; //nothing to normalize + } + SASSERT(!t1->is_ground() || !t2->is_ground()); + + unsigned max_var_idx = 0; + { + var_idx_set& orig_var_set = rm.collect_vars(t1, t2); + var_idx_set::iterator ovit = orig_var_set.begin(); + var_idx_set::iterator ovend = orig_var_set.end(); + for(; ovit!=ovend; ++ovit) { + unsigned var_idx = *ovit; + if(var_idx>max_var_idx) { + max_var_idx = var_idx; + } + } + } + + if(t1->get_decl()!=t2->get_decl()) { + if(t1->get_decl()->get_id()get_decl()->get_id()) { + std::swap(t1, t2); + } + } + else { + int_vector norm1(max_var_idx+1, -1); + int_vector norm2(max_var_idx+1, -1); + unsigned n=t1->get_num_args(); + SASSERT(n==t2->get_num_args()); + for(unsigned i=0; iget_arg(i)); + var * v2 = to_var(t2->get_arg(i)); + if(v1->get_sort()!=v2->get_sort()) { + //different sorts mean we can distinguish the two terms + if(v1->get_sort()->get_id()get_sort()->get_id()) { + std::swap(t1, t2); + } + break; + } + unsigned v1_idx = v1->get_idx(); + unsigned v2_idx = v2->get_idx(); + //since the rules already went through the mk_filter_rules transformer, + //variables must be linear + SASSERT(norm1[v1_idx]==-1); + SASSERT(norm2[v2_idx]==-1); + + if(norm2[v1_idx]!=norm1[v2_idx]) { + //now we can distinguish the two terms + if(norm2[v1_idx](0)); + unsigned next_var = 0; + get_normalizer(t1, next_var, result); + get_normalizer(t2, next_var, result); + } + + app_pair get_key(app * t1, app * t2) { + expr_ref_vector norm_subst(m); + get_normalizer(t1, t2, norm_subst); + expr_ref t1n_ref(m); + expr_ref t2n_ref(m); + m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr(), t1n_ref); + m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr(), t2n_ref); + app * t1n = to_app(t1n_ref); + app * t2n = to_app(t2n_ref); + if(t1n>t2n) { + std::swap(t1n, t2n); + } + m_pinned.push_back(t1n); + m_pinned.push_back(t2n); + + /* + IF_VERBOSE(0, + print_renaming(norm_subst, verbose_stream()); + display_predicate(m_context, t1, verbose_stream()); + display_predicate(m_context, t2, verbose_stream()); + display_predicate(m_context, t1n, verbose_stream()); + display_predicate(m_context, t2n, verbose_stream());); + */ + return app_pair(t1n, t2n); + } + + /** + \brief Add rule \c r among rules interested in predicate pair \c t1, \c t2. + + The \c m_rule_content entry of the rule \c r has to be properly filled in + by the time of a call to this function + */ + void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { + TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n"; + r->display(m_context, tout); tout << "\n";); + SASSERT(t1!=t2); + cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); + pair_info * & ptr_inf = e->get_data().m_value; + if(ptr_inf==0) { + ptr_inf = alloc(pair_info); + } + pair_info & inf = *ptr_inf; + + expr_ref_vector normalizer(m); + get_normalizer(t1, t2, normalizer); + unsigned norm_ofs = normalizer.size()-1; + var_idx_set normalized_vars; + var_idx_set::iterator vit = non_local_vars.begin(); + var_idx_set::iterator vend = non_local_vars.end(); + for(; vit!=vend; ++vit) { + unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx(); + normalized_vars.insert(norm_var); + } + + inf.add_rule(*this, t1, t2, r, normalized_vars); + } + + pair_info & get_pair(app_pair key) const { + cost_map::entry * e = m_costs.find_core(key); + SASSERT(e); + return *e->get_data().m_value; + } + + void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { + pair_info * ptr = &get_pair(key); + if(ptr->remove_rule(r, original_len)) { + SASSERT(ptr->m_rules.empty()); + m_costs.remove(key); + dealloc(ptr); + } + } + + void register_rule(rule * r) { + rule_counter counter; + counter.count_rule_vars(m, r, 1); + + ptr_vector & rule_content = + m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; + SASSERT(rule_content.empty()); + + unsigned pos_tail_size=r->get_positive_tail_size(); + for(unsigned i=0; iget_tail(i)); + } + for(unsigned i=0; iget_tail(i); + var_idx_set t1_vars = rm.collect_vars(t1); + counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter + for(unsigned j=i+1; jget_tail(j); + counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter + var_idx_set scope_vars = rm.collect_vars(t2); + scope_vars |= t1_vars; + var_idx_set non_local_vars; + counter.collect_positive(non_local_vars); + counter.count_vars(m, t2, 1); //restore t2 variables in counter + set_intersection(non_local_vars, scope_vars); + register_pair(t1, t2, r, non_local_vars); + } + counter.count_vars(m, t1, 1); //restore t1 variables in counter + } + } + + bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args, + ptr_vector & domain) { + unsigned n=t->get_num_args(); + for(unsigned i=0; iget_arg(i)); + if(v->get_idx()==var_idx) { + args.push_back(v); + domain.push_back(m.get_sort(v)); + return true; + } + } + return false; + } + + void join_pair(app_pair pair_key) { + app * t1 = pair_key.first; + app * t2 = pair_key.second; + pair_info & inf = get_pair(pair_key); + SASSERT(!inf.m_rules.empty()); + var_idx_set & output_vars = inf.m_all_nonlocal_vars; + expr_ref_vector args(m); + ptr_vector domain; + + unsigned arity = output_vars.num_elems(); + idx_set::iterator ovit=output_vars.begin(); + idx_set::iterator ovend=output_vars.end(); + //TODO: improve quadratic complexity + for(;ovit!=ovend;++ovit) { + unsigned var_idx=*ovit; + + bool found=extract_argument_info(var_idx, t1, args, domain); + if(!found) { + found=extract_argument_info(var_idx, t2, args, domain); + } + SASSERT(found); + } + + SASSERT(args.size()==arity); + SASSERT(domain.size()==arity); + + rule * one_parent = inf.m_rules.back(); + + func_decl* parent_head = one_parent->get_decl(); + const char * one_parent_name = parent_head->get_name().bare_str(); + std::string parent_name; + if(inf.m_rules.size()>1) { + parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1); + } + else { + parent_name = one_parent_name; + } + + func_decl * decl = m_context.mk_fresh_head_predicate( + symbol(parent_name.c_str()), symbol("split"), + arity, domain.c_ptr(), parent_head); + + app_ref head(m.mk_app(decl, arity, args.c_ptr()), m); + + app * tail[] = {t1, t2}; + + rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, 0); + + //TODO: update accounting so that it can handle multiple parents + new_rule->set_accounting_parent_object(m_context, one_parent); + + m_introduced_rules.push_back(new_rule); + + //here we copy the inf.m_rules vector because inf.m_rules will get changed + //in the iteration. Also we use hashtable instead of vector because we do + //not want to process one rule twice. + typedef ptr_hashtable, default_eq > rule_hashtable; + rule_hashtable relevant_rules; + insert_into_set(relevant_rules, inf.m_rules); + rule_hashtable::iterator rit = relevant_rules.begin(); + rule_hashtable::iterator rend = relevant_rules.end(); + for(; rit!=rend; ++rit) { + apply_binary_rule(*rit, pair_key, head); + } + + // SASSERT(!m_costs.contains(pair_key)); + } + + void replace_edges(rule * r, const ptr_vector & removed_tails, + const ptr_vector & added_tails0, const ptr_vector & rule_content) { + SASSERT(removed_tails.size()>=added_tails0.size()); + unsigned len = rule_content.size(); + unsigned original_len = len+removed_tails.size()-added_tails0.size(); + ptr_vector added_tails(added_tails0); //we need a copy since we'll be modifying it + + unsigned rt_sz = removed_tails.size(); + //remove edges between removed tails + for(unsigned i=0; iget_head(); + + var_counter counter; + counter.count_vars(m, head, 1); + + unsigned tail_size=r->get_tail_size(); + unsigned pos_tail_size=r->get_positive_tail_size(); + + for(unsigned i=pos_tail_size; iget_tail(i), 1); + } + for(unsigned i=0; i & rule_content = m_rules_content.find_core(r)->get_data().m_value; + unsigned len = rule_content.size(); + if(len==1) { + return; + } + + func_decl * t1_pred = t1->get_decl(); + func_decl * t2_pred = t2->get_decl(); + ptr_vector removed_tails; + ptr_vector added_tails; + for(unsigned i1=0; i1get_decl()!=t1_pred) { + continue; + } + unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0; + for(unsigned i2=i2start; i2get_decl()!=t2_pred) { + continue; + } + if(get_key(rt1, rt2)!=pair_key) { + continue; + } + expr_ref_vector normalizer(m); + get_normalizer(rt1, rt2, normalizer); + expr_ref_vector denormalizer(m); + reverse_renaming(m, normalizer, denormalizer); + expr_ref new_transf(m); + m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr(), new_transf); + app * new_lit = to_app(new_transf); + + m_pinned.push_back(new_lit); + rule_content[i1]=new_lit; + rule_content[i2]=rule_content.back(); + rule_content.pop_back(); + len--; //here the bound of both loops changes!!! + removed_tails.push_back(rt1); + removed_tails.push_back(rt2); + added_tails.push_back(new_lit); + //this exits the inner loop, the outer one continues in case there will + //be other matches + break; + } + } + SASSERT(!removed_tails.empty()); + SASSERT(!added_tails.empty()); + m_modified_rules.insert(r); + replace_edges(r, removed_tails, added_tails, rule_content); + } + + cost get_domain_size(func_decl * pred, unsigned arg_index) const { + relation_sort sort = pred->get_domain(arg_index); + return static_cast(m_context.get_sort_size_estimate(sort)); + //unsigned sz; + //if(!m_context.get_sort_size(sort, sz)) { + // sz=UINT_MAX; + //} + //return static_cast(sz); + } + + unsigned get_stratum(func_decl * pred) const { + return m_rs_aux_copy.get_predicate_strat(pred); + } + + cost estimate_size(app * t) const { + func_decl * pred = t->get_decl(); + unsigned n=pred->get_arity(); + rel_context_base* rel = m_context.get_rel_context(); + if (!rel) { + return cost(1); + } + relation_manager& rm = rel->get_rmanager(); + if( (m_context.saturation_was_run() && rm.try_get_relation(pred)) + || rm.is_saturated(pred)) { + SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist + unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); + if(rel_size_int!=0) { + cost rel_size = static_cast(rel_size_int); + cost curr_size = rel_size; + for(unsigned i=0; iget_arg(i))) { + curr_size /= get_domain_size(pred, i); + } + } + return curr_size; + } + } + cost res = 1; + for(unsigned i=0; iget_arg(i))) { + res *= get_domain_size(pred, i); + } + } + return res; + } + + cost compute_cost(app * t1, app * t2) const { + func_decl * t1_pred = t1->get_decl(); + func_decl * t2_pred = t2->get_decl(); + cost inters_size = 1; + variable_intersection vi(m_context.get_manager()); + vi.populate(t1, t2); + unsigned n = vi.size(); + for(unsigned i=0; i0) { + res /= 2; + } + else { + res *= 2; + } + } + }*/ + + TRACE("report_costs", + display_predicate(m_context, t1, tout); + display_predicate(m_context, t2, tout); + tout << res << "\n";); + return res; + } + + + bool pick_best_pair(app_pair & p) { + app_pair best; + bool found = false; + cost best_cost; + + cost_map::iterator it = m_costs.begin(); + cost_map::iterator end = m_costs.end(); + for(; it!=end; ++it) { + app_pair key = it->m_key; + pair_info & inf = *it->m_value; + if(!inf.can_be_joined()) { + continue; + } + cost c = inf.get_cost(); + if(!found || cm_key; + ptr_vector content = rcit->m_value; + SASSERT(content.size()<=2); + if(content.size()==orig_r->get_positive_tail_size()) { + //rule did not change + result->add_rule(orig_r); + continue; + } + + ptr_vector tail(content); + svector negs(tail.size(), false); + unsigned or_len = orig_r->get_tail_size(); + for(unsigned i=orig_r->get_positive_tail_size(); iget_tail(i)); + negs.push_back(orig_r->is_neg_tail(i)); + } + + rule * new_rule = m_context.get_rule_manager().mk(orig_r->get_head(), tail.size(), tail.c_ptr(), + negs.c_ptr()); + + new_rule->set_accounting_parent_object(m_context, orig_r); + m_context.get_rule_manager().mk_rule_rewrite_proof(*orig_r, *new_rule); + result->add_rule(new_rule); + } + while (!m_introduced_rules.empty()) { + result->add_rule(m_introduced_rules.back()); + m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back()); + m_introduced_rules.pop_back(); + } + result->inherit_predicates(source); + return result; + } + }; + + rule_set * mk_simple_joins::operator()(rule_set const & source) { + rule_set rs_aux_copy(m_context); + rs_aux_copy.replace_rules(source); + if(!rs_aux_copy.is_closed()) { + rs_aux_copy.close(); + } + + join_planner planner(m_context, rs_aux_copy); + + return planner.run(source); + } + + +}; + diff --git a/src/muz/rel/dl_mk_simple_joins.h b/src/muz/rel/dl_mk_simple_joins.h new file mode 100644 index 000000000..36eb08dd5 --- /dev/null +++ b/src/muz/rel/dl_mk_simple_joins.h @@ -0,0 +1,63 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_simple_joins.h + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2010-05-20. + +Revision History: + +--*/ +#ifndef _DL_MK_SIMPLE_JOINS_H_ +#define _DL_MK_SIMPLE_JOINS_H_ + +#include"map.h" +#include"obj_pair_hashtable.h" + +#include"dl_context.h" +#include"dl_rule_set.h" +#include"dl_rule_transformer.h" + +namespace datalog { + + /** + \brief Functor for creating rules that contain simple joins. + A simple join is the join of two tables. + + After applying this transformation, every rule has at most one join. + So, the rules will have the form + + HEAD :- TAIL. + HEAD :- TAIL_1, TAIL_2. + + We also assume a rule may contain interpreted expressions that work as filtering conditions. + So, we may also have: + + HEAD :- TAIL, C_1, ..., C_n. + HEAD :- TAIL_1, TAIL_2, C_1, ..., C_n. + + Where the C_i's are interpreted expressions. + + We say that a rule containing C_i's is a rule with a "big tail". + */ + class mk_simple_joins : public rule_transformer::plugin { + context & m_context; + rule_manager & rm; + public: + mk_simple_joins(context & ctx); + + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_SIMPLE_JOINS_H_ */ + diff --git a/src/muz/rel/dl_product_relation.cpp b/src/muz/rel/dl_product_relation.cpp new file mode 100644 index 000000000..48cd666e6 --- /dev/null +++ b/src/muz/rel/dl_product_relation.cpp @@ -0,0 +1,1117 @@ +/*++ +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; + m_spec_store.get_relation_spec(s, 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); + } + } + +}; + + + diff --git a/src/muz/rel/dl_product_relation.h b/src/muz/rel/dl_product_relation.h new file mode 100644 index 000000000..0633ddbf1 --- /dev/null +++ b/src/muz/rel/dl_product_relation.h @@ -0,0 +1,191 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_product_relation.h + +Abstract: + + A Relation relation combinator. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-4-11 + +Revision History: + +--*/ +#ifndef _DL_PRODUCT_RELATION_H_ +#define _DL_PRODUCT_RELATION_H_ + + +#include "dl_context.h" +#include "dl_relation_manager.h" + +namespace datalog { + + class product_relation; + + class product_relation_plugin : public relation_plugin { + friend class product_relation; + public: + typedef svector rel_spec; + private: + class join_fn; + class transform_fn; + class mutator_fn; + class aligned_union_fn; + class unaligned_union_fn; + class single_non_transparent_src_union_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + struct fid_hash { + typedef family_id data; + unsigned operator()(data x) const { return static_cast(x); } + }; + + rel_spec_store > m_spec_store; + + family_id get_relation_kind(const product_relation & r); + + bool is_product_relation(relation_base * r) { return r->get_plugin().is_product_relation(); } + + public: + static product_relation_plugin& get_plugin(relation_manager & rmgr); + + product_relation_plugin(relation_manager& m); + + virtual void initialize(family_id fid); + + virtual bool can_handle_signature(const relation_signature & s); + virtual bool can_handle_signature(const relation_signature & s, family_id kind); + + static symbol get_name() { return symbol("product_relation"); } + + family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec); + + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind); + + protected: + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + static bool is_product_relation(relation_base const& r); + + private: + static product_relation& get(relation_base& r); + static product_relation const & get(relation_base const& r); + static product_relation* get(relation_base* r); + static product_relation const* get(relation_base const* r); + + relation_union_fn * mk_union_w_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta, bool is_widen); + + bool are_aligned(const product_relation& r1, const product_relation& r2); + static void get_common_spec(const ptr_vector & rels, rel_spec & res); + }; + + + class product_relation : public relation_base { + friend class product_relation_plugin; + + friend class product_relation_plugin::join_fn; + friend class product_relation_plugin::transform_fn; + friend class product_relation_plugin::mutator_fn; + friend class product_relation_plugin::aligned_union_fn; + friend class product_relation_plugin::unaligned_union_fn; + friend class product_relation_plugin::single_non_transparent_src_union_fn; + friend class product_relation_plugin::filter_equal_fn; + friend class product_relation_plugin::filter_identical_fn; + friend class product_relation_plugin::filter_interpreted_fn; + + + + typedef product_relation_plugin::rel_spec rel_spec; + + /** + If m_relations is empty, value of this determines whether the relation is empty or full. + */ + bool m_default_empty; + + /** + There must not be two relations of the same kind + */ + ptr_vector m_relations; + + /** + Array of kinds of inner relations. + + If two product relations have equal signature and specification, their + m_relations arrays contain corresponding relations at the same indexes. + + The value returned by get_kind() depends uniquely on the specification. + */ + rel_spec m_spec; + + /** + \brief Ensure the kind assigned to this relation reflects the types of inner relations. + */ + void ensure_correct_kind(); + /** + The current specification must be a subset of the new one. + */ + void convert_spec(const rel_spec & spec); + public: + product_relation(product_relation_plugin& p, relation_signature const& s); + product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations); + + ~product_relation(); + + virtual bool empty() const; + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual product_relation * clone() const; + virtual product_relation * complement(func_decl* p) const; + virtual void display(std::ostream & out) const; + virtual void to_formula(expr_ref& fml) const; + product_relation_plugin& get_plugin() const; + + unsigned size() const { return m_relations.size(); } + relation_base& operator[](unsigned i) const { return *m_relations[i]; } + + /** + If all relations except one are sieve_relations with no inner columns, + return true and into \c idx assign index of that relation. Otherwise return + false. + */ + bool try_get_single_non_transparent(unsigned & idx) const; + + virtual bool is_precise() const { + for (unsigned i = 0; i < m_relations.size(); ++i) { + if (!m_relations[i]->is_precise()) { + return false; + } + } + return true; + } + }; + +}; + +#endif + diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp new file mode 100644 index 000000000..457ef28c0 --- /dev/null +++ b/src/muz/rel/dl_relation_manager.cpp @@ -0,0 +1,1702 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_relation_manager.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include +#include"ast_pp.h" +#include"dl_check_table.h" +#include"dl_context.h" +#include"dl_finite_product_relation.h" +#include"dl_product_relation.h" +#include"dl_sieve_relation.h" +#include"dl_table_relation.h" +#include"dl_relation_manager.h" + +namespace datalog { + + relation_manager::~relation_manager() { + reset(); + } + + + void relation_manager::reset_relations() { + relation_map::iterator it=m_relations.begin(); + relation_map::iterator end=m_relations.end(); + for(;it!=end;++it) { + func_decl * pred = it->m_key; + get_context().get_manager().dec_ref(pred); //inc_ref in get_relation + relation_base * r=(*it).m_value; + r->deallocate(); + } + m_relations.reset(); + } + + void relation_manager::reset() { + reset_relations(); + + m_favourite_table_plugin = static_cast(0); + m_favourite_relation_plugin = static_cast(0); + dealloc_ptr_vector_content(m_table_plugins); + m_table_plugins.reset(); + dealloc_ptr_vector_content(m_relation_plugins); + m_relation_plugins.reset(); + m_next_table_fid = 0; + m_next_relation_fid = 0; + } + + dl_decl_util & relation_manager::get_decl_util() const { + return get_context().get_decl_util(); + } + + family_id relation_manager::get_next_relation_fid(relation_plugin & claimer) { + unsigned res = m_next_relation_fid++; + m_kind2plugin.insert(res, &claimer); + return res; + } + + void relation_manager::set_predicate_kind(func_decl * pred, family_id kind) { + SASSERT(!m_relations.contains(pred)); + m_pred_kinds.insert(pred, kind); + } + + family_id relation_manager::get_requested_predicate_kind(func_decl * pred) { + family_id res; + if(m_pred_kinds.find(pred, res)) { + return res; + } + else { + return null_family_id; + } + } + + relation_base & relation_manager::get_relation(func_decl * pred) { + relation_base * res = try_get_relation(pred); + if(!res) { + relation_signature sig; + from_predicate(pred, sig); + family_id rel_kind = get_requested_predicate_kind(pred); + res = mk_empty_relation(sig, rel_kind); + store_relation(pred, res); + } + return *res; + } + + relation_base * relation_manager::try_get_relation(func_decl * pred) const { + relation_base * res = 0; + if(!m_relations.find(pred, res)) { + return 0; + } + SASSERT(res); + return res; + } + + void relation_manager::store_relation(func_decl * pred, relation_base * rel) { + SASSERT(rel); + relation_map::entry * e = m_relations.insert_if_not_there2(pred, 0); + if (e->get_data().m_value) { + e->get_data().m_value->deallocate(); + } + else { + get_context().get_manager().inc_ref(pred); //dec_ref in reset + } + e->get_data().m_value = rel; + } + + void relation_manager::collect_non_empty_predicates(decl_set & res) const { + relation_map::iterator it = m_relations.begin(); + relation_map::iterator end = m_relations.end(); + for(; it!=end; ++it) { + if(!it->m_value->empty()) { + res.insert(it->m_key); + } + } + } + + void relation_manager::restrict_predicates(const decl_set & preds) { + typedef ptr_vector fd_vector; + fd_vector to_remove; + + relation_map::iterator rit = m_relations.begin(); + relation_map::iterator rend = m_relations.end(); + for(; rit!=rend; ++rit) { + func_decl * pred = rit->m_key; + if (!preds.contains(pred)) { + to_remove.insert(pred); + } + } + + fd_vector::iterator pit = to_remove.begin(); + fd_vector::iterator pend = to_remove.end(); + for(; pit!=pend; ++pit) { + func_decl * pred = *pit; + relation_base * rel; + VERIFY( m_relations.find(pred, rel) ); + rel->deallocate(); + m_relations.remove(pred); + get_context().get_manager().dec_ref(pred); + } + + set_intersection(m_saturated_rels, preds); + } + + void relation_manager::register_plugin(table_plugin * plugin) { + plugin->initialize(get_next_table_fid()); + m_table_plugins.push_back(plugin); + + if(plugin->get_name()==get_context().default_table()) { + m_favourite_table_plugin = plugin; + } + + table_relation_plugin * tr_plugin = alloc(table_relation_plugin, *plugin, *this); + register_relation_plugin_impl(tr_plugin); + m_table_relation_plugins.insert(plugin, tr_plugin); + + symbol checker_name = get_context().default_table_checker(); + if(get_context().default_table_checked() && get_table_plugin(checker_name)) { + if( m_favourite_table_plugin && + (plugin==m_favourite_table_plugin || plugin->get_name()==checker_name) ) { + symbol checked_name = get_context().default_table(); + //the plugins we need to create the checking plugin were just added + SASSERT(m_favourite_table_plugin->get_name()==get_context().default_table()); + table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); + register_plugin(checking_plugin); + m_favourite_table_plugin = checking_plugin; + } + if(m_favourite_relation_plugin && m_favourite_relation_plugin->from_table()) { + table_relation_plugin * fav_rel_plugin = + static_cast(m_favourite_relation_plugin); + if(&fav_rel_plugin->get_table_plugin()==plugin || plugin->get_name()==checker_name) { + //the plugins we need to create the checking table_relation_plugin were just added + SASSERT(m_favourite_relation_plugin->get_name() == + get_context().default_relation()); + symbol checked_name = fav_rel_plugin->get_table_plugin().get_name(); + table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); + register_plugin(checking_plugin); + + table_relation_plugin * checking_tr_plugin = + alloc(table_relation_plugin, *checking_plugin, *this); + register_relation_plugin_impl(checking_tr_plugin); + m_table_relation_plugins.insert(checking_plugin, checking_tr_plugin); + m_favourite_relation_plugin = checking_tr_plugin; + } + } + } + + } + + void relation_manager::register_relation_plugin_impl(relation_plugin * plugin) { + m_relation_plugins.push_back(plugin); + plugin->initialize(get_next_relation_fid(*plugin)); + if (plugin->get_name() == get_context().default_relation()) { + m_favourite_relation_plugin = plugin; + } + if(plugin->is_finite_product_relation()) { + finite_product_relation_plugin * fprp = static_cast(plugin); + relation_plugin * inner = &fprp->get_inner_plugin(); + m_finite_product_relation_plugins.insert(inner, fprp); + } + } + + relation_plugin * relation_manager::try_get_appropriate_plugin(const relation_signature & s) { + if(m_favourite_relation_plugin && m_favourite_relation_plugin->can_handle_signature(s)) { + return m_favourite_relation_plugin; + } + relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); + relation_plugin_vector::iterator rpend = m_relation_plugins.end(); + for(; rpit!=rpend; ++rpit) { + if((*rpit)->can_handle_signature(s)) { + return *rpit; + } + } + return 0; + } + + relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { + relation_plugin * res = try_get_appropriate_plugin(s); + if (!res) { + throw default_exception("no suitable plugin found for given relation signature"); + } + return *res; + } + + table_plugin * relation_manager::try_get_appropriate_plugin(const table_signature & t) { + if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { + return m_favourite_table_plugin; + } + table_plugin_vector::iterator tpit = m_table_plugins.begin(); + table_plugin_vector::iterator tpend = m_table_plugins.end(); + for(; tpit!=tpend; ++tpit) { + if((*tpit)->can_handle_signature(t)) { + return *tpit; + } + } + return 0; + } + + table_plugin & relation_manager::get_appropriate_plugin(const table_signature & t) { + table_plugin * res = try_get_appropriate_plugin(t); + if(!res) { + throw default_exception("no suitable plugin found for given table signature"); + } + return *res; + } + + relation_plugin * relation_manager::get_relation_plugin(symbol const& s) { + relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); + relation_plugin_vector::iterator rpend = m_relation_plugins.end(); + for(; rpit!=rpend; ++rpit) { + if((*rpit)->get_name()==s) { + return *rpit; + } + } + return 0; + } + + relation_plugin & relation_manager::get_relation_plugin(family_id kind) { + SASSERT(kind>=0); + SASSERT(kindget_name()==k) { + return *tpit; + } + } + return 0; + } + + table_relation_plugin & relation_manager::get_table_relation_plugin(table_plugin & tp) { + table_relation_plugin * res; + VERIFY( m_table_relation_plugins.find(&tp, res) ); + return *res; + } + + bool relation_manager::try_get_finite_product_relation_plugin(const relation_plugin & inner, + finite_product_relation_plugin * & res) { + return m_finite_product_relation_plugins.find(&inner, res); + } + + table_base * relation_manager::mk_empty_table(const table_signature & s) { + return get_appropriate_plugin(s).mk_empty(s); + } + + + bool relation_manager::is_non_explanation(relation_signature const& s) const { + dl_decl_util & decl_util = get_context().get_decl_util(); + unsigned n = s.size(); + for(unsigned i = 0; i < n; i++) { + if(decl_util.is_rule_sort(s[i])) { + return false; + } + } + return true; + } + + relation_base * relation_manager::mk_empty_relation(const relation_signature & s, func_decl* pred) { + return mk_empty_relation(s, get_requested_predicate_kind(pred)); + } + + relation_base * relation_manager::mk_empty_relation(const relation_signature & s, family_id kind) { + if (kind != null_family_id) { + relation_plugin & plugin = get_relation_plugin(kind); + if (plugin.can_handle_signature(s, kind)) + return plugin.mk_empty(s, kind); + } + relation_base * res; + relation_plugin* p = m_favourite_relation_plugin; + + if (p && p->can_handle_signature(s)) { + return p->mk_empty(s); + } + + if (mk_empty_table_relation(s, res)) { + return res; + } + + for (unsigned i = 0; i < m_relation_plugins.size(); ++i) { + p = m_relation_plugins[i]; + if (p->can_handle_signature(s)) { + return p->mk_empty(s); + } + } + + //If there is no plugin to handle the signature, we just create an empty product relation and + //stuff will be added to it by later operations. + return product_relation_plugin::get_plugin(*this).mk_empty(s); + } + + + relation_base * relation_manager::mk_table_relation(const relation_signature & s, table_base * table) { + SASSERT(s.size()==table->get_signature().size()); + return get_table_relation_plugin(table->get_plugin()).mk_from_table(s, table); + } + + bool relation_manager::mk_empty_table_relation(const relation_signature & s, relation_base * & result) { + table_signature tsig; + if(!relation_signature_to_table(s, tsig)) { + return false; + } + table_base * table = mk_empty_table(tsig); + result = mk_table_relation(s, table); + return true; + } + + + relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* p, family_id kind) { + if (kind != null_family_id) { + relation_plugin & plugin = get_relation_plugin(kind); + if (plugin.can_handle_signature(s, kind)) { + return plugin.mk_full(p, s, kind); + } + } + return get_appropriate_plugin(s).mk_full(p, s, null_family_id); + } + + relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* pred) { + family_id kind = get_requested_predicate_kind(pred); + return mk_full_relation(s, pred, kind); + } + + void relation_manager::relation_to_table(const relation_sort & sort, const relation_element & from, + table_element & to) { + SASSERT(from->get_num_args()==0); + VERIFY(get_context().get_decl_util().is_numeral_ext(from, to)); + } + + void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, + relation_element & to) { + to = get_decl_util().mk_numeral(from, sort); + } + + void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, + relation_element_ref & to) { + relation_element rel_el; + table_to_relation(sort, from, rel_el); + to = rel_el; + } + + void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, + const relation_fact::el_proxy & to) { + relation_element rel_el; + table_to_relation(sort, from, rel_el); + to = rel_el; + } + + bool relation_manager::relation_sort_to_table(const relation_sort & from, table_sort & to) { + return get_context().get_decl_util().try_get_size(from, to); + } + + void relation_manager::from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result) { + result = pred->get_domain(arg_index); + } + + void relation_manager::from_predicate(func_decl * pred, relation_signature & result) { + result.reset(); + unsigned arg_num=pred->get_arity(); + for(unsigned i=0;iset_cancel(f); + } + } + + std::string relation_manager::to_nice_string(const relation_element & el) const { + uint64 val; + std::stringstream stm; + if(get_context().get_decl_util().is_numeral_ext(el, val)) { + stm << val; + } + else { + stm << mk_pp(el, get_context().get_manager()); + } + return stm.str(); + } + + std::string relation_manager::to_nice_string(const relation_sort & s, const relation_element & el) const { + std::stringstream stm; + uint64 val; + if(get_context().get_decl_util().is_numeral_ext(el, val)) { + get_context().print_constant_name(s, val, stm); + } + else { + stm << mk_pp(el, get_context().get_manager()); + } + return stm.str(); + } + + std::string relation_manager::to_nice_string(const relation_sort & s) const { + return std::string(s->get_name().bare_str()); + } + + std::string relation_manager::to_nice_string(const relation_signature & s) const { + std::string res("["); + bool first = true; + relation_signature::const_iterator it = s.begin(); + relation_signature::const_iterator end = s.end(); + for(; it!=end; ++it) { + if(first) { + first = false; + } + else { + res+=','; + } + res+=to_nice_string(*it); + } + res+=']'; + + return res; + } + + void relation_manager::display(std::ostream & out) const { + relation_map::iterator it=m_relations.begin(); + relation_map::iterator end=m_relations.end(); + for(;it!=end;++it) { + out << "Table " << it->m_key->get_name() << "\n"; + it->m_value->display(out); + } + } + + void relation_manager::display_relation_sizes(std::ostream & out) const { + relation_map::iterator it=m_relations.begin(); + relation_map::iterator end=m_relations.end(); + for(;it!=end;++it) { + out << "Relation " << it->m_key->get_name() << " has size " + << it->m_value->get_size_estimate_rows() << "\n"; + } + } + + void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { + const decl_set & output_preds = rules.get_output_predicates(); + decl_set::iterator it=output_preds.begin(); + decl_set::iterator end=output_preds.end(); + for(; it!=end; ++it) { + func_decl * pred = *it; + relation_base * rel = try_get_relation(pred); + if(!rel) { + out << "Tuples in " << pred->get_name() << ": \n"; + continue; + } + rel->display_tuples(*pred, out); + } + } + + + // ----------------------------------- + // + // relation operations + // + // ----------------------------------- + + class relation_manager::empty_signature_relation_join_fn : public relation_join_fn { + public: + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + TRACE("dl", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << "\n";); + if(r1.get_signature().empty()) { + if(r1.empty()) { + return r2.get_manager().mk_empty_relation(r2.get_signature(), r2.get_kind()); + } + else { + return r2.clone(); + } + } + else { + SASSERT(r2.get_signature().empty()); + if(r2.empty()) { + return r1.get_manager().mk_empty_relation(r1.get_signature(), r1.get_kind()); + } + else { + return r1.clone(); + } + } + } + }; + + relation_join_fn * relation_manager::mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation) { + relation_plugin * p1 = &t1.get_plugin(); + relation_plugin * p2 = &t2.get_plugin(); + + relation_join_fn * res = p1->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + if(!res && p1!=p2) { + res = p2->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + + if(!res && (t1.get_signature().empty() || t2.get_signature().empty())) { + res = alloc(empty_signature_relation_join_fn); + } + + finite_product_relation_plugin * fprp; + if(!res && p1->from_table() && try_get_finite_product_relation_plugin(*p2, fprp)) { + //we downcast here to relation_plugin so that we don't have to declare + //relation_manager as a friend class of finite_product_relation_plugin + res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + if(!res && p2->from_table() && try_get_finite_product_relation_plugin(*p1, fprp)) { + res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + + if(!res && allow_product_relation) { + relation_plugin & product_plugin = product_relation_plugin::get_plugin(*this); + res = product_plugin.mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + + return res; + } + + relation_transformer_fn * relation_manager::mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + return t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); + } + + class relation_manager::default_relation_filter_interpreted_and_project_fn : public relation_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + unsigned_vector m_removed_cols; + public: + /** + This constructor should be used only if we know that the projection operation + exists for the result of the join. + */ + default_relation_filter_interpreted_and_project_fn( + relation_mutator_fn* filter, + unsigned removed_col_cnt, + const unsigned * removed_cols) + : m_filter(filter), + m_project(0), + m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual relation_base * operator()(const relation_base & t) { + scoped_rel t1 = t.clone(); + (*m_filter)(*t1); + if( !m_project) { + relation_manager & rmgr = t1->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*t1, m_removed_cols.size(), m_removed_cols.c_ptr()); + if (!m_project) { + throw default_exception("projection does not exist"); + } + } + return (*m_project)(*t1); + } + }; + + relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn( + const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) { + + relation_transformer_fn* res = + t.get_plugin().mk_filter_interpreted_and_project_fn( + t, + condition, + removed_col_cnt, + removed_cols); + + if (!res) { + relation_mutator_fn* filter_fn = mk_filter_interpreted_fn(t, condition); + if (filter_fn) { + res = alloc(default_relation_filter_interpreted_and_project_fn, + filter_fn, + removed_col_cnt, + removed_cols); + } + } + return res; + } + + + class relation_manager::default_relation_join_project_fn : public relation_join_fn { + scoped_ptr m_join; + scoped_ptr m_project; + + unsigned_vector m_removed_cols; + public: + /** + This constructor should be used only if we know that the projection operation + exists for the result of the join. + */ + default_relation_join_project_fn(join_fn * join, unsigned removed_col_cnt, + const unsigned * removed_cols) + : m_join(join), m_project(0), m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { + scoped_rel aux = (*m_join)(t1, t2); + if(!m_project) { + relation_manager & rmgr = aux->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr()); + if(!m_project) { + throw default_exception("projection does not exist"); + } + } + relation_base * res = (*m_project)(*aux); + return res; + } + }; + + + relation_join_fn * relation_manager::mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join) { + relation_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + if(!res && &t1.get_plugin()!=&t2.get_plugin()) { + res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, + removed_cols); + } + if(!res) { + relation_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2, allow_product_relation_join); + if(join) { + res = alloc(default_relation_join_project_fn, join, removed_col_cnt, removed_cols); + } + } + return res; + + } + + relation_transformer_fn * relation_manager::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + return t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); + } + + relation_transformer_fn * relation_manager::mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation) { + relation_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); + if(!res) { + res = alloc(default_relation_permutation_rename_fn, t, permutation); + } + return res; + } + + + relation_union_fn * relation_manager::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + relation_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_union_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_union_fn(tgt, src, delta); + } + return res; + } + + relation_union_fn * relation_manager::mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + relation_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res) { + res = mk_union_fn(tgt, src, delta); + } + return res; + } + + relation_mutator_fn * relation_manager::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) { + return t.get_plugin().mk_filter_identical_fn(t, col_cnt, identical_cols); + } + + relation_mutator_fn * relation_manager::mk_filter_equal_fn(const relation_base & t, + const relation_element & value, unsigned col) { + + return t.get_plugin().mk_filter_equal_fn(t, value, col); + } + + relation_mutator_fn * relation_manager::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + return t.get_plugin().mk_filter_interpreted_fn(t, condition); + } + + class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + public: + default_relation_select_equal_and_project_fn(relation_mutator_fn * filter, relation_transformer_fn * project) + : m_filter(filter), m_project(project) {} + + virtual relation_base * operator()(const relation_base & t1) { + TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); + scoped_rel aux = t1.clone(); + (*m_filter)(*aux); + relation_base * res = (*m_project)(*aux); + return res; + } + }; + + relation_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col) { + relation_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); + if(!res) { + relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); + if(selector) { + relation_transformer_fn * projector = mk_project_fn(t, 1, &col); + if(projector) { + res = alloc(default_relation_select_equal_and_project_fn, selector, projector); + } + else { + dealloc(selector); + } + } + } + return res; + } + + + class relation_manager::default_relation_intersection_filter_fn : public relation_intersection_filter_fn { + scoped_ptr m_join_fun; + scoped_ptr m_union_fun; + public: + + default_relation_intersection_filter_fn(relation_join_fn * join_fun, relation_union_fn * union_fun) + : m_join_fun(join_fun), m_union_fun(union_fun) {} + + virtual void operator()(relation_base & tgt, const relation_base & intersected_obj) { + scoped_rel filtered_rel = (*m_join_fun)(tgt, intersected_obj); + TRACE("dl", + tgt.display(tout << "tgt:\n"); + intersected_obj.display(tout << "intersected:\n"); + filtered_rel->display(tout << "filtered:\n"); + ); + if(!m_union_fun) { + SASSERT(tgt.can_swap(*filtered_rel)); + tgt.swap(*filtered_rel); + } + tgt.reset(); + TRACE("dl", tgt.display(tout << "target reset:\n"); ); + (*m_union_fun)(tgt, *filtered_rel); + TRACE("dl", tgt.display(tout << "intersected target:\n"); ); + } + + }; + + relation_intersection_filter_fn * relation_manager::try_mk_default_filter_by_intersection_fn( + const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, + const unsigned * tgt_cols, const unsigned * src_cols) { + TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); + unsigned_vector join_removed_cols; + add_sequence(tgt.get_signature().size(), src.get_signature().size(), join_removed_cols); + scoped_rel join_fun = mk_join_project_fn(tgt, src, joined_col_cnt, tgt_cols, src_cols, + join_removed_cols.size(), join_removed_cols.c_ptr(), false); + if(!join_fun) { + return 0; + } + //we perform the join operation here to see what the result is + scoped_rel join_res = (*join_fun)(tgt, src); + if(tgt.can_swap(*join_res)) { + return alloc(default_relation_intersection_filter_fn, join_fun.release(), 0); + } + if(join_res->get_plugin().is_product_relation()) { + //we cannot have the product relation here, since it uses the intersection operation + //for unions and therefore we would get into an infinite recursion + return 0; + } + scoped_rel union_fun = mk_union_fn(tgt, *join_res); + if(!union_fun) { + return 0; + } + return alloc(default_relation_intersection_filter_fn, join_fun.release(), union_fun.release()); + } + + + relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { + TRACE("dl_verbose", tout << t.get_plugin().get_name() << "\n";); + relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, + t_cols, src_cols); + if(!res && &t.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); + } + if(!res) { + res = try_mk_default_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); + } + return res; + } + + relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & tgt, + const relation_base & src) { + TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); + SASSERT(tgt.get_signature()==src.get_signature()); + unsigned sz = tgt.get_signature().size(); + unsigned_vector cols; + add_sequence(0, sz, cols); + return mk_filter_by_intersection_fn(tgt, src, cols, cols); + } + + + relation_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + TRACE("dl", tout << t.get_plugin().get_name() << "\n";); + relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, + t_cols, negated_cols); + if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { + res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, + negated_cols); + } + return res; + } + + + + + + // ----------------------------------- + // + // table operations + // + // ----------------------------------- + + class relation_manager::default_table_join_fn : public convenient_table_join_fn { + unsigned m_col_cnt; + public: + default_table_join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_col_cnt(col_cnt) {} + + virtual table_base * operator()(const table_base & t1, const table_base & t2) { + table_plugin * plugin = &t1.get_plugin(); + + const table_signature & res_sign = get_result_signature(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &t2.get_plugin(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &t1.get_manager().get_appropriate_plugin(res_sign); + } + } + SASSERT(plugin->can_handle_signature(res_sign)); + table_base * res = plugin->mk_empty(res_sign); + + unsigned t1cols = t1.get_signature().size(); + unsigned t2cols = t2.get_signature().size(); + unsigned t1first_func = t1.get_signature().first_functional(); + unsigned t2first_func = t2.get_signature().first_functional(); + + table_base::iterator els1it = t1.begin(); + table_base::iterator els1end = t1.end(); + table_base::iterator els2end = t2.end(); + + table_fact acc; + + for(; els1it!=els1end; ++els1it) { + const table_base::row_interface & row1 = *els1it; + + table_base::iterator els2it = t2.begin(); + for(; els2it!=els2end; ++els2it) { + const table_base::row_interface & row2 = *els2it; + + bool match=true; + for(unsigned i=0; iadd_fact(acc); + } + } + return res; + } + }; + + table_join_fn * relation_manager::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + table_join_fn * res = t1.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); + if(!res && &t1.get_plugin()!=&t2.get_plugin()) { + res = t2.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + if(!res) { + table_signature sig; + table_signature::from_join(t1.get_signature(), t2.get_signature(), + col_cnt, cols1, cols2, sig); + res = alloc(default_table_join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + return res; + } + + + class relation_manager::auxiliary_table_transformer_fn { + table_fact m_row; + public: + virtual ~auxiliary_table_transformer_fn() {} + virtual const table_signature & get_result_signature() const = 0; + virtual void modify_fact(table_fact & f) const = 0; + + table_base * operator()(const table_base & t) { + table_plugin & plugin = t.get_plugin(); + const table_signature & res_sign = get_result_signature(); + SASSERT(plugin.can_handle_signature(res_sign)); + table_base * res = plugin.mk_empty(res_sign); + + table_base::iterator it = t.begin(); + table_base::iterator end = t.end(); + + for(; it!=end; ++it) { + it->get_fact(m_row); + modify_fact(m_row); + res->add_fact(m_row); + } + return res; + } + }; + + class relation_manager::default_table_project_fn + : public convenient_table_project_fn, auxiliary_table_transformer_fn { + public: + default_table_project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols) { + SASSERT(removed_col_cnt>0); + } + + virtual const table_signature & get_result_signature() const { + return convenient_table_project_fn::get_result_signature(); + } + + virtual void modify_fact(table_fact & f) const { + project_out_vector_columns(f, m_removed_cols); + } + + virtual table_base * operator()(const table_base & t) { + return auxiliary_table_transformer_fn::operator()(t); + } + }; + + class relation_manager::null_signature_table_project_fn : public table_transformer_fn { + const table_signature m_empty_sig; + public: + null_signature_table_project_fn() : m_empty_sig() {} + virtual table_base * operator()(const table_base & t) { + relation_manager & m = t.get_plugin().get_manager(); + table_base * res = m.mk_empty_table(m_empty_sig); + if(!t.empty()) { + table_fact el; + res->add_fact(el); + } + return res; + } + }; + + + + table_transformer_fn * relation_manager::mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + table_transformer_fn * res = t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); + if(!res && col_cnt==t.get_signature().size()) { + //all columns are projected out + res = alloc(null_signature_table_project_fn); + } + if(!res) { + res = alloc(default_table_project_fn, t.get_signature(), col_cnt, removed_cols); + } + return res; + } + + + class relation_manager::default_table_join_project_fn : public convenient_table_join_project_fn { + scoped_ptr m_join; + scoped_ptr m_project; + + unsigned_vector m_removed_cols; + public: + default_table_join_project_fn(join_fn * join, const table_base & t1, const table_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), joined_col_cnt, cols1, + cols2, removed_col_cnt, removed_cols), + m_join(join), + m_removed_cols(removed_col_cnt, removed_cols) {} + + class unreachable_reducer : public table_row_pair_reduce_fn { + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + //we do project_with_reduce only if we are sure there will be no reductions + //(see code of the table_signature::from_join_project function) + UNREACHABLE(); + } + }; + + virtual table_base * operator()(const table_base & t1, const table_base & t2) { + table_base * aux = (*m_join)(t1, t2); + if(m_project==0) { + relation_manager & rmgr = aux->get_plugin().get_manager(); + if(get_result_signature().functional_columns()!=0) { + //to preserve functional columns we need to do the project_with_reduction + unreachable_reducer * reducer = alloc(unreachable_reducer); + m_project = rmgr.mk_project_with_reduce_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr(), reducer); + } + else { + m_project = rmgr.mk_project_fn(*aux, m_removed_cols); + } + if(!m_project) { + throw default_exception("projection for table does not exist"); + } + } + table_base * res = (*m_project)(*aux); + aux->deallocate(); + return res; + } + }; + + table_join_fn * relation_manager::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + table_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + if(!res && &t1.get_plugin()!=&t2.get_plugin()) { + res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, + removed_cols); + } + if(!res) { + table_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2); + if(join) { + res = alloc(default_table_join_project_fn, join, t1, t2, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + } + } + return res; + + } + + class relation_manager::default_table_rename_fn + : public convenient_table_rename_fn, auxiliary_table_transformer_fn { + public: + default_table_rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { + SASSERT(permutation_cycle_len>=2); + } + + virtual const table_signature & get_result_signature() const { + return convenient_table_rename_fn::get_result_signature(); + } + + virtual void modify_fact(table_fact & f) const { + permutate_by_cycle(f, m_cycle); + } + + virtual table_base * operator()(const table_base & t) { + return auxiliary_table_transformer_fn::operator()(t); + } + + }; + + table_transformer_fn * relation_manager::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + table_transformer_fn * res = t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); + if(!res) { + res = alloc(default_table_rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); + } + return res; + } + + table_transformer_fn * relation_manager::mk_permutation_rename_fn(const table_base & t, + const unsigned * permutation) { + table_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); + if(!res) { + res = alloc(default_table_permutation_rename_fn, t, permutation); + } + return res; + } + + + class relation_manager::default_table_union_fn : public table_union_fn { + table_fact m_row; + public: + virtual void operator()(table_base & tgt, const table_base & src, table_base * delta) { + table_base::iterator it = src.begin(); + table_base::iterator iend = src.end(); + + for(; it!=iend; ++it) { + it->get_fact(m_row); + + if(delta) { + if(!tgt.contains_fact(m_row)) { + tgt.add_new_fact(m_row); + delta->add_fact(m_row); + } + } + else { + //if there's no delta, we don't need to know whether we are actually adding a new fact + tgt.add_fact(m_row); + } + } + } + }; + + table_union_fn * relation_manager::mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta) { + table_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_union_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_union_fn(tgt, src, delta); + } + if(!res) { + res = alloc(default_table_union_fn); + } + return res; + } + + table_union_fn * relation_manager::mk_widen_fn(const table_base & tgt, const table_base & src, + const table_base * delta) { + table_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res) { + res = mk_union_fn(tgt, src, delta); + } + return res; + } + + + /** + An auixiliary class for functors that perform filtering. It performs the table traversal + and only asks for each individual row whether it should be removed. + + When using this class in multiple inheritance, this class should not be inherited publicly + and should be mentioned as last. This should ensure that deteletion of the object will + go well when initiated from a pointer to the first ancestor. + */ + class relation_manager::auxiliary_table_filter_fn { + table_fact m_row; + svector m_to_remove; + public: + virtual ~auxiliary_table_filter_fn() {} + virtual bool should_remove(const table_fact & f) const = 0; + + void operator()(table_base & r) { + m_to_remove.reset(); + unsigned sz = 0; + table_base::iterator it = r.begin(); + table_base::iterator iend = r.end(); + for(; it!=iend; ++it) { + it->get_fact(m_row); + if(should_remove(m_row)) { + m_to_remove.append(m_row.size(), m_row.c_ptr()); + ++sz; + } + } + r.remove_facts(sz, m_to_remove.c_ptr()); + } + }; + + class relation_manager::default_table_filter_identical_fn : public table_mutator_fn, auxiliary_table_filter_fn { + const unsigned m_col_cnt; + const unsigned_vector m_identical_cols; + public: + default_table_filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_col_cnt(col_cnt), + m_identical_cols(col_cnt, identical_cols) { + SASSERT(col_cnt>=2); + } + + virtual bool should_remove(const table_fact & f) const { + table_element val=f[m_identical_cols[0]]; + for(unsigned i=1; iget_arg(0); + if (!m.is_eq(condition)) { + return 0; + } + expr* x = to_app(condition)->get_arg(0); + expr* y = to_app(condition)->get_arg(1); + if (!is_var(x)) { + std::swap(x, y); + } + if (!is_var(x)) { + return 0; + } + dl_decl_util decl_util(m); + uint64 value = 0; + if (!decl_util.is_numeral_ext(y, value)) { + return 0; + } + return alloc(default_table_filter_not_equal_fn, ctx, to_var(x)->get_idx(), value); + } + }; + + + + class relation_manager::default_table_filter_interpreted_fn + : public table_mutator_fn, auxiliary_table_filter_fn { + ast_manager & m_ast_manager; + var_subst & m_vs; + dl_decl_util & m_decl_util; + th_rewriter & m_simp; + app_ref m_condition; + ptr_vector m_var_sorts; + expr_ref_vector m_args; + public: + default_table_filter_interpreted_fn(context & ctx, unsigned col_cnt, app* condition) + : m_ast_manager(ctx.get_manager()), + m_vs(ctx.get_var_subst()), + m_decl_util(ctx.get_decl_util()), + m_simp(ctx.get_rewriter()), + m_condition(condition, ctx.get_manager()), + m_args(ctx.get_manager()) { + m_var_sorts.resize(col_cnt); + get_free_vars(m_condition, m_var_sorts); + } + + virtual bool should_remove(const table_fact & f) const { + expr_ref_vector& args = const_cast(m_args); + + args.reset(); + //arguments need to be in reverse order for the substitution + unsigned col_cnt = f.size(); + for(int i=col_cnt-1;i>=0;i--) { + sort * var_sort = m_var_sorts[i]; + if(!var_sort) { + args.push_back(0); + continue; //this variable does not occur in the condition; + } + + table_element el = f[i]; + args.push_back(m_decl_util.mk_numeral(el, var_sort)); + } + + expr_ref ground(m_ast_manager); + m_vs(m_condition.get(), args.size(), args.c_ptr(), ground); + m_simp(ground); + + return m_ast_manager.is_false(ground); + } + + virtual void operator()(table_base & t) { + auxiliary_table_filter_fn::operator()(t); + } + }; + + table_mutator_fn * relation_manager::mk_filter_interpreted_fn(const table_base & t, app * condition) { + context & ctx = get_context(); + table_mutator_fn * res = t.get_plugin().mk_filter_interpreted_fn(t, condition); + if (!res) { + res = default_table_filter_not_equal_fn::mk(ctx, condition); + } + if(!res) { + res = alloc(default_table_filter_interpreted_fn, ctx, t.get_signature().size(), condition); + } + return res; + } + + + class relation_manager::default_table_filter_interpreted_and_project_fn + : public table_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + app_ref m_condition; + unsigned_vector m_removed_cols; + public: + default_table_filter_interpreted_and_project_fn(context & ctx, table_mutator_fn * filter, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + : m_filter(filter), m_condition(condition, ctx.get_manager()), + m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual table_base* operator()(const table_base & tb) { + table_base *t2 = tb.clone(); + (*m_filter)(*t2); + if (!m_project) { + relation_manager & rmgr = t2->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*t2, m_removed_cols.size(), m_removed_cols.c_ptr()); + if (!m_project) { + throw default_exception("projection does not exist"); + } + } + return (*m_project)(*t2); + } + }; + + table_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + table_transformer_fn * res = t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); + if (res) + return res; + + table_mutator_fn * filter = mk_filter_interpreted_fn(t, condition); + SASSERT(filter); + res = alloc(default_table_filter_interpreted_and_project_fn, get_context(), filter, condition, removed_col_cnt, removed_cols); + return res; + } + + + table_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const table_base & t, + const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { + table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, + t_cols, src_cols); + if(!res && &t.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, t_cols, src_cols); + } + return res; + } + + + + class relation_manager::default_table_negation_filter_fn : public convenient_table_negation_filter_fn, + auxiliary_table_filter_fn { + const table_base * m_negated_table; + mutable table_fact m_aux_fact; + public: + default_table_negation_filter_fn(const table_base & tgt, const table_base & neg_t, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) + : convenient_table_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), + m_negated_table(0) { + m_aux_fact.resize(neg_t.get_signature().size()); + } + + virtual bool should_remove(const table_fact & f) const { + if(!m_all_neg_bound || m_overlap) { + table_base::iterator nit = m_negated_table->begin(); + table_base::iterator nend = m_negated_table->end(); + for(; nit!=nend; ++nit) { + const table_base::row_interface & nrow = *nit; + if(bindings_match(nrow, f)) { + return true; + } + } + return false; + } + else { + make_neg_bindings(m_aux_fact, f); + return m_negated_table->contains_fact(m_aux_fact); + } + } + + virtual void operator()(table_base & tgt, const table_base & negated_table) { + SASSERT(m_negated_table==0); + flet flet_neg_table(m_negated_table, &negated_table); + auxiliary_table_filter_fn::operator()(tgt); + } + + }; + + table_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, + t_cols, negated_cols); + if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { + res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, + negated_cols); + } + if(!res) { + res = alloc(default_table_negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + return res; + } + + + class relation_manager::default_table_select_equal_and_project_fn : public table_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + public: + default_table_select_equal_and_project_fn(table_mutator_fn * filter, table_transformer_fn * project) + : m_filter(filter), m_project(project) {} + + virtual table_base * operator()(const table_base & t1) { + TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); + scoped_rel aux = t1.clone(); + (*m_filter)(*aux); + table_base * res = (*m_project)(*aux); + return res; + } + }; + + table_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + table_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); + if(!res) { + table_mutator_fn * selector = mk_filter_equal_fn(t, value, col); + SASSERT(selector); + table_transformer_fn * projector = mk_project_fn(t, 1, &col); + SASSERT(projector); + res = alloc(default_table_select_equal_and_project_fn, selector, projector); + } + return res; + } + + + class relation_manager::default_table_map_fn : public table_mutator_fn { + scoped_ptr m_mapper; + unsigned m_first_functional; + scoped_rel m_aux_table; + scoped_ptr m_union_fn; + table_fact m_curr_fact; + public: + default_table_map_fn(const table_base & t, table_row_mutator_fn * mapper) + : m_mapper(mapper), m_first_functional(t.get_signature().first_functional()) { + SASSERT(t.get_signature().functional_columns()>0); + table_plugin & plugin = t.get_plugin(); + m_aux_table = plugin.mk_empty(t.get_signature()); + m_union_fn = plugin.mk_union_fn(t, *m_aux_table, static_cast(0)); + } + + virtual void operator()(table_base & t) { + SASSERT(t.get_signature()==m_aux_table->get_signature()); + if(!m_aux_table->empty()) { + m_aux_table->reset(); + } + + + table_base::iterator it = t.begin(); + table_base::iterator iend = t.end(); + for(; it!=iend; ++it) { + it->get_fact(m_curr_fact); + if((*m_mapper)(m_curr_fact.c_ptr()+m_first_functional)) { + m_aux_table->add_fact(m_curr_fact); + } + } + + t.reset(); + (*m_union_fn)(t, *m_aux_table, static_cast(0)); + } + }; + + table_mutator_fn * relation_manager::mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { + SASSERT(t.get_signature().functional_columns()>0); + table_mutator_fn * res = t.get_plugin().mk_map_fn(t, mapper); + if(!res) { + res = alloc(default_table_map_fn, t, mapper); + } + return res; + } + + + class relation_manager::default_table_project_with_reduce_fn : public convenient_table_transformer_fn { + unsigned_vector m_removed_cols; + const unsigned m_inp_col_cnt; + const unsigned m_removed_col_cnt; + const unsigned m_result_col_cnt; + scoped_ptr m_reducer; + unsigned m_res_first_functional; + table_fact m_row; + table_fact m_former_row; + public: + default_table_project_with_reduce_fn(const table_signature & orig_sig, unsigned removed_col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) + : m_removed_cols(removed_col_cnt, removed_cols), + m_inp_col_cnt(orig_sig.size()), + m_removed_col_cnt(removed_col_cnt), + m_result_col_cnt(orig_sig.size()-removed_col_cnt), + m_reducer(reducer) { + SASSERT(removed_col_cnt>0); + table_signature::from_project_with_reduce(orig_sig, removed_col_cnt, removed_cols, + get_result_signature()); + m_res_first_functional = get_result_signature().first_functional(); + m_row.resize(get_result_signature().size()); + m_former_row.resize(get_result_signature().size()); + } + + virtual void modify_fact(table_fact & f) const { + unsigned ofs=1; + unsigned r_i=1; + for(unsigned i=m_removed_cols[0]+1; isuggest_fact(m_former_row)) { + (*m_reducer)(m_former_row.c_ptr()+m_res_first_functional, m_row.c_ptr()+m_res_first_functional); + res->ensure_fact(m_former_row); + } + } + return res; + } + }; + + table_transformer_fn * relation_manager::mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { + SASSERT(t.get_signature().functional_columns()>0); + table_transformer_fn * res = t.get_plugin().mk_project_with_reduce_fn(t, col_cnt, removed_cols, reducer); + if(!res) { + res = alloc(default_table_project_with_reduce_fn, t.get_signature(), col_cnt, removed_cols, reducer); + } + return res; + } + +}; + diff --git a/src/muz/rel/dl_relation_manager.h b/src/muz/rel/dl_relation_manager.h new file mode 100644 index 000000000..9f12b4bb6 --- /dev/null +++ b/src/muz/rel/dl_relation_manager.h @@ -0,0 +1,688 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_relation_manager.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ +#ifndef _DL_RELATION_MANAGER_H_ +#define _DL_RELATION_MANAGER_H_ + + +#include"map.h" +#include"vector.h" +#include"dl_base.h" + +namespace datalog { + + class context; + class dl_decl_util; + class table_relation; + class table_relation_plugin; + class finite_product_relation; + class finite_product_relation_plugin; + class sieve_relation; + class sieve_relation_plugin; + class rule_set; + + + class relation_manager { + class empty_signature_relation_join_fn; + class default_relation_join_project_fn; + class default_relation_select_equal_and_project_fn; + class default_relation_intersection_filter_fn; + class default_relation_filter_interpreted_and_project_fn; + + class auxiliary_table_transformer_fn; + class auxiliary_table_filter_fn; + + class default_table_join_fn; + class default_table_project_fn; + class null_signature_table_project_fn; + class default_table_join_project_fn; + class default_table_rename_fn; + class default_table_union_fn; + class default_table_filter_equal_fn; + class default_table_filter_identical_fn; + class default_table_filter_interpreted_fn; + class default_table_filter_interpreted_and_project_fn; + class default_table_negation_filter_fn; + class default_table_filter_not_equal_fn; + class default_table_select_equal_and_project_fn; + class default_table_map_fn; + class default_table_project_with_reduce_fn; + + typedef obj_map decl2kind_map; + + typedef u_map kind2plugin_map; + + typedef map, + ptr_eq > tp2trp_map; + typedef map, + ptr_eq > rp2fprp_map; + + typedef map, ptr_eq > relation_map; + typedef ptr_vector table_plugin_vector; + typedef ptr_vector relation_plugin_vector; + + context & m_context; + table_plugin_vector m_table_plugins; + relation_plugin_vector m_relation_plugins; + //table_relation_plugins corresponding to table_plugins + tp2trp_map m_table_relation_plugins; + rp2fprp_map m_finite_product_relation_plugins; + + kind2plugin_map m_kind2plugin; + + table_plugin * m_favourite_table_plugin; + + relation_plugin * m_favourite_relation_plugin; + + relation_map m_relations; + + decl_set m_saturated_rels; + + family_id m_next_table_fid; + family_id m_next_relation_fid; + + /** + Map specifying what kind of relation should be used to represent particular predicate. + */ + decl2kind_map m_pred_kinds; + + void register_relation_plugin_impl(relation_plugin * plugin); + + relation_manager(const relation_manager &); //private and undefined copy constructor + relation_manager & operator=(const relation_manager &); //private and undefined operator= + public: + relation_manager(context & ctx) : + m_context(ctx), + m_favourite_table_plugin(0), + m_favourite_relation_plugin(0), + m_next_table_fid(0), + m_next_relation_fid(0) {} + + virtual ~relation_manager(); + + void reset(); + void reset_relations(); + + context & get_context() const { return m_context; } + dl_decl_util & get_decl_util() const; + + family_id get_next_table_fid() { return m_next_table_fid++; } + family_id get_next_relation_fid(relation_plugin & claimer); + + + /** + Set what kind of relation is going to be used to represent the predicate \c pred. + + This function can be called only before the relation object for \c pred is created + (i.e. before the \c get_relation function is called with \c pred as argument for the + first time). + */ + void set_predicate_kind(func_decl * pred, family_id kind); + /** + Return the relation kind that was requested to represent the predicate \c pred by + \c set_predicate_kind. If there was no such request, return \c null_family_id. + */ + family_id get_requested_predicate_kind(func_decl * pred); + relation_base & get_relation(func_decl * pred); + relation_base * try_get_relation(func_decl * pred) const; + /** + \brief Store the relation \c rel under the predicate \c pred. The \c relation_manager + takes over the relation object. + */ + void store_relation(func_decl * pred, relation_base * rel); + + bool is_saturated(func_decl * pred) const { return m_saturated_rels.contains(pred); } + void mark_saturated(func_decl * pred) { m_saturated_rels.insert(pred); } + void reset_saturated_marks() { + if(!m_saturated_rels.empty()) { + m_saturated_rels.reset(); + } + } + + void collect_non_empty_predicates(decl_set & res) const; + void restrict_predicates(const decl_set & preds); + + void register_plugin(table_plugin * plugin); + /** + table_relation_plugins should not be passed to this function since they are + created automatically when registering a table plugin. + */ + void register_plugin(relation_plugin * plugin) { + SASSERT(!plugin->from_table()); + register_relation_plugin_impl(plugin); + } + + table_plugin & get_appropriate_plugin(const table_signature & t); + relation_plugin & get_appropriate_plugin(const relation_signature & t); + table_plugin * try_get_appropriate_plugin(const table_signature & t); + relation_plugin * try_get_appropriate_plugin(const relation_signature & t); + + table_plugin * get_table_plugin(symbol const& s); + relation_plugin * get_relation_plugin(symbol const& s); + relation_plugin & get_relation_plugin(family_id kind); + table_relation_plugin & get_table_relation_plugin(table_plugin & tp); + bool try_get_finite_product_relation_plugin(const relation_plugin & inner, + finite_product_relation_plugin * & res); + + table_base * mk_empty_table(const table_signature & s); + relation_base * mk_implicit_relation(const relation_signature & s, app * expr); + + relation_base * mk_empty_relation(const relation_signature & s, family_id kind); + relation_base * mk_empty_relation(const relation_signature & s, func_decl* pred); + + relation_base * mk_full_relation(const relation_signature & s, func_decl* pred, family_id kind); + relation_base * mk_full_relation(const relation_signature & s, func_decl* pred); + + relation_base * mk_table_relation(const relation_signature & s, table_base * table); + bool mk_empty_table_relation(const relation_signature & s, relation_base * & result); + + bool is_non_explanation(relation_signature const& s) const; + + + /** + \brief Convert relation value to table one. + + This function can be called only for the relation sorts that have a table counterpart. + */ + void relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to); + + void table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to); + void table_to_relation(const relation_sort & sort, const table_element & from, + const relation_fact::el_proxy & to); + void table_to_relation(const relation_sort & sort, const table_element & from, + relation_element_ref & to); + + bool relation_sort_to_table(const relation_sort & from, table_sort & to); + void from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result); + void from_predicate(func_decl * pred, relation_signature & result); + + /** + \brief Convert relation signature to table signature and return true if successful. If false + is returned, the value of \c to is undefined. + */ + bool relation_signature_to_table(const relation_signature & from, table_signature & to); + + void relation_fact_to_table(const relation_signature & s, const relation_fact & from, + table_fact & to); + void table_fact_to_relation(const relation_signature & s, const table_fact & from, + relation_fact & to); + + + void set_cancel(bool f); + + + // ----------------------------------- + // + // relation operations + // + // ----------------------------------- + + //TODO: If multiple operation implementations are available, we may want to do something to + //select the best one here. + + /** + If \c allow_product_relation is true, we will create a join that builds a product relation, + if there is no other way to do the join. If \c allow_product_relation is false, we will return + zero in that case. + */ + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation=true); + + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2, bool allow_product_relation=true) { + SASSERT(cols1.size()==cols2.size()); + return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), allow_product_relation); + } + + /** + \brief Return functor that transforms a table into one that lacks columns listed in + \c removed_cols array. + + The \c removed_cols cotains columns of table \c t in strictly ascending order. + */ + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + + relation_transformer_fn * mk_project_fn(const relation_base & t, const unsigned_vector & removed_cols) { + return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); + } + + /** + \brief Return an operation that is a composition of a join an a project operation. + */ + relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join=true); + + relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2, + const unsigned_vector & removed_cols, bool allow_product_relation_join=true) { + return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), + removed_cols.c_ptr(), allow_product_relation_join); + } + + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + relation_transformer_fn * mk_rename_fn(const relation_base & t, const unsigned_vector & permutation_cycle) { + return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); + } + + /** + Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array + of column number. + */ + relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation); + relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, + const unsigned_vector permutation) { + SASSERT(t.get_signature().size()==permutation.size()); + return mk_permutation_rename_fn(t, permutation.c_ptr()); + } + + + /** + The post-condition for an ideal union operation is be + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) + + A required post-condition is + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + tgt_1==tgt_0 => delta_1==delta_0 + delta_0 \subset delta_1 + delta_1 \subset (delta_0 \union tgt_1) + ( tgt_1 \setminus tgt_0 ) \subset delta_1 + + So that a sufficient implementation is + + Union(tgt, src, delta) { + oldTgt:=tgt.clone(); + tgt:=tgt \union src + if(tgt!=oldTgt) { + delta:=delta \union src //also ?delta \union tgt? would work + } + } + + If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient + post-condition is + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty + */ + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src) { + return mk_union_fn(tgt, src, static_cast(0)); + } + + /** + Similar to union, but this one should be used inside loops to allow for abstract + domain convergence. + */ + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector identical_cols) { + return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); + } + + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + + /** + \brief Operations that returns all rows of \c t for which is column \c col equal to \c value + with the column \c col removed. + + This operation can often be efficiently implemented and is useful for evaluating rules + of the form + + F(x):-P("c",x). + */ + relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col); + + + relation_intersection_filter_fn * 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); + relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, + const relation_base & src, const unsigned_vector & tgt_cols, const unsigned_vector & src_cols) { + SASSERT(tgt_cols.size()==src_cols.size()); + return mk_filter_by_intersection_fn(tgt, src, tgt_cols.size(), tgt_cols.c_ptr(), src_cols.c_ptr()); + } + relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, + const relation_base & src); + + /** + The filter_by_negation postcondition: + filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, + corresponding columns in neg: d1,...,dN): + tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } + */ + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, const unsigned_vector & t_cols, + const unsigned_vector & negated_cols) { + SASSERT(t_cols.size()==negated_cols.size()); + return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); + } + + + // ----------------------------------- + // + // table operations + // + // ----------------------------------- + + + table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + + table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2) { + SASSERT(cols1.size()==cols2.size()); + return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); + } + + /** + \brief Return functor that transforms a table into one that lacks columns listed in + \c removed_cols array. + + The \c removed_cols cotains columns of table \c t in strictly ascending order. + + If a project operation removes a non-functional column, all functional columns become + non-functional (so that none of the values in functional columns are lost) + */ + table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + + table_transformer_fn * mk_project_fn(const table_base & t, const unsigned_vector & removed_cols) { + return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); + } + + /** + \brief Return an operation that is a composition of a join an a project operation. + + This operation is equivalent to the two operations performed separately, unless functional + columns are involved. + + The ordinary project would make all of the functional columns into non-functional if any + non-functional column was removed. In function, however, we group columns into equivalence + classes (according to the equalities in \c cols1 and \c cols2) and make everything non-functional + only if some equivalence class of non-functional columns would have no non-functional columns + remain after the removal. + + This behavior is implemented in the \c table_signature::from_join_project function. + */ + table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols); + + table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2, + const unsigned_vector & removed_cols) { + return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), + removed_cols.c_ptr()); + } + + table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + table_transformer_fn * mk_rename_fn(const table_base & t, const unsigned_vector & permutation_cycle) { + return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); + } + + /** + Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array + of column number. + */ + table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned * permutation); + table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector permutation) { + SASSERT(t.get_signature().size()==permutation.size()); + return mk_permutation_rename_fn(t, permutation.c_ptr()); + } + + + /** + The post-condition for an ideal union operation is be + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) + + A required post-condition is + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + tgt_1==tgt_0 => delta_1==delta_0 + delta_0 \subset delta_1 + delta_1 \subset (delta_0 \union tgt_1) + ( tgt_1 \setminus tgt_0 ) \subset delta_1 + + So that a sufficient implementation is + + Union(tgt, src, delta) { + oldTgt:=tgt.clone(); + tgt:=tgt \union src + if(tgt!=oldTgt) { + delta:=delta \union src //also ?delta \union tgt? would work + } + } + + If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient + post-condition is + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty + */ + table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + + table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src) { + return mk_union_fn(tgt, src, static_cast(0)); + } + + /** + Similar to union, but this one should be used inside loops to allow for abstract + domain convergence. + */ + table_union_fn * mk_widen_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + + table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, + const unsigned * identical_cols); + table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector identical_cols) { + return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); + } + + table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, + unsigned col); + + table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + + table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + + /** + \brief Operations that returns all rows of \c t for which is column \c col equal to \c value + with the column \c col removed. + + This operation can often be efficiently implemented and is useful for evaluating rules + of the form + + F(x):-P("c",x). + */ + table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); + + table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, + const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); + table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, + const table_base & src, const unsigned_vector & t_cols, const unsigned_vector & src_cols) { + SASSERT(t_cols.size()==src_cols.size()); + return mk_filter_by_intersection_fn(t, src, t_cols.size(), t_cols.c_ptr(), src_cols.c_ptr()); + } + + /** + The filter_by_negation postcondition: + filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, + corresponding columns in neg: d1,...,dN): + tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } + */ + table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); + table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, + const unsigned_vector & t_cols, const unsigned_vector & negated_cols) { + SASSERT(t_cols.size()==negated_cols.size()); + return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); + } + + /** + \c t must contain at least one functional column. + + Created object takes ownership of the \c mapper object. + */ + virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper); + + /** + \c t must contain at least one functional column. + + Created object takes ownership of the \c mapper object. + */ + virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer); + + + + + // ----------------------------------- + // + // output functions + // + // ----------------------------------- + + + std::string to_nice_string(const relation_element & el) const; + /** + This one may give a nicer representation of \c el than the + \c to_nice_string(const relation_element & el) function, by unsing the information about the sort + of the element. + */ + std::string to_nice_string(const relation_sort & s, const relation_element & el) const; + std::string to_nice_string(const relation_sort & s) const; + std::string to_nice_string(const relation_signature & s) const; + + void display(std::ostream & out) const; + void display_relation_sizes(std::ostream & out) const; + void display_output_tables(rule_set const& rules, std::ostream & out) const; + + private: + relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * src_cols); + + }; + + /** + This is a helper class for relation_plugins whose relations can be of various kinds. + */ + template > + class rel_spec_store { + typedef relation_signature::hash r_hash; + typedef relation_signature::eq r_eq; + + typedef map family_id_idx_store; + typedef map sig2store; + + typedef u_map family_id2spec; + typedef map sig2spec_store; + + relation_plugin & m_parent; + svector m_allocated_kinds; + sig2store m_kind_assignment; + sig2spec_store m_kind_specs; + + + relation_manager & get_manager() { return m_parent.get_manager(); } + + void add_new_kind() { + add_available_kind(get_manager().get_next_relation_fid(m_parent)); + } + + public: + rel_spec_store(relation_plugin & parent) : m_parent(parent) {} + + ~rel_spec_store() { + reset_dealloc_values(m_kind_assignment); + reset_dealloc_values(m_kind_specs); + } + + void add_available_kind(family_id k) { + m_allocated_kinds.push_back(k); + } + + bool contains_signature(relation_signature const& sig) const { + return m_kind_assignment.contains(sig); + } + + family_id get_relation_kind(const relation_signature & sig, const Spec & spec) { + typename sig2store::entry * e = m_kind_assignment.find_core(sig); + if(!e) { + e = m_kind_assignment.insert_if_not_there2(sig, alloc(family_id_idx_store)); + m_kind_specs.insert(sig, alloc(family_id2spec)); + } + family_id_idx_store & ids = *e->get_data().m_value; + + unsigned res_idx; + if(!ids.find(spec, res_idx)) { + res_idx = ids.size(); + if(res_idx==m_allocated_kinds.size()) { + add_new_kind(); + } + SASSERT(res_idxinsert(m_allocated_kinds[res_idx], spec); + } + return m_allocated_kinds[res_idx]; + } + + void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) { + family_id2spec * idspecs = m_kind_specs.find(sig); + spec = idspecs->find(kind); + } + + }; + +}; + +#endif /* _DL_RELATION_MANAGER_H_ */ + diff --git a/src/muz/rel/dl_sieve_relation.cpp b/src/muz/rel/dl_sieve_relation.cpp new file mode 100644 index 000000000..9f9419089 --- /dev/null +++ b/src/muz/rel/dl_sieve_relation.cpp @@ -0,0 +1,666 @@ +/*++ +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_pp.h" +#include"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); + get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr(), fml); + } + + + 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, + svector & inner_columns) { + SASSERT(inner_columns.size()==s.size()); + unsigned n = s.size(); + relation_signature inner_sig_singleton; + for(unsigned i=0; i & inner_columns, relation_signature & inner_sig) { + SASSERT(inner_columns.size()==s.size()); + inner_sig.reset(); + unsigned n = s.size(); + for(unsigned i=0; i inner_cols(s.size()); + extract_inner_columns(s, inner_cols.c_ptr()); + collect_inner_signature(s, inner_cols, inner_sig); +#endif + } + + bool sieve_relation_plugin::can_handle_signature(const relation_signature & s) { + //we do not want this plugin to handle anything by default + return false; +#if 0 + relation_signature inner_sig; + extract_inner_signature(s, inner_sig); + SASSERT(inner_sig.size()<=s.size()); + return !inner_sig.empty() && inner_sig.size()!=s.size(); +#endif + } + + sieve_relation * sieve_relation_plugin::mk_from_inner(const relation_signature & s, const bool * inner_columns, + relation_base * inner_rel) { + SASSERT(!inner_rel->get_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.c_ptr(), inner); + } + + + relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) { + UNREACHABLE(); + return 0; +#if 0 + svector 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 + svector 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); + svector inner_cols; + inner_cols.resize(s.size(), false); + return mk_from_inner(s, inner_cols, inner); + } + + sieve_relation * sieve_relation_plugin::mk_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 + svector 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; + svector 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) : 0; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + 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); + } + } + + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + 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) : 0; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + 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.c_ptr(), 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 0; + } + 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) : 0; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + 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 0; + } + 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 { + svector 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; + } + + virtual relation_base * operator()(const relation_base & r0) { + 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.c_ptr(), 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 0; + } + const sieve_relation & r = static_cast(r0); + unsigned_vector inner_removed_cols; + + for(unsigned i=0; i result_inner_cols = r.m_inner_cols; + project_out_vector_columns(result_inner_cols, col_cnt, removed_cols); + + relation_signature result_sig; + relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, result_sig); + + relation_transformer_fn * inner_fun; + if(inner_removed_cols.empty()) { + inner_fun = alloc(identity_relation_transformer_fn); + } + else { + inner_fun = get_manager().mk_project_fn(r.get_inner(), inner_removed_cols); + } + + if(!inner_fun) { + return 0; + } + return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); + } + + relation_transformer_fn * sieve_relation_plugin::mk_rename_fn(const relation_base & r0, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(&r0.get_plugin()!=this) { + return 0; + } + const sieve_relation & r = static_cast(r0); + + unsigned sig_sz = r.get_signature().size(); + unsigned_vector permutation; + add_sequence(0, sig_sz, permutation); + permutate_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); + + svector result_inner_cols = r.m_inner_cols; + permutate_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 0; + } + return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); + } + + + 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) {} + + virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + 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) : 0; + const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; + sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; + 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 0; + } + + 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) : 0; + const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; + const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; + 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 0; + } + } + 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 0; + } + } + + relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta); + if(!union_fun) { + return 0; + } + + 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) {} + + virtual void operator()(relation_base & r0) { + 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 0; + } + 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 0; + } + 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 0; + } + 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) {} + + virtual void operator()(relation_base & r, const relation_base & neg) { + 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) : 0; + const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; + 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 0; + } + 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) : 0; + const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; + 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 0; + } + return alloc(negation_filter_fn, inner_fun); + } + + +}; diff --git a/src/muz/rel/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h new file mode 100644 index 000000000..48402cd6d --- /dev/null +++ b/src/muz/rel/dl_sieve_relation.h @@ -0,0 +1,198 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_sieve_relation.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-11-11. + +Revision History: + +--*/ + +#ifndef _DL_SIEVE_RELATION_H_ +#define _DL_SIEVE_RELATION_H_ + +#include "dl_context.h" +#include "dl_relation_manager.h" + +namespace datalog { + + class sieve_relation; + + class sieve_relation_plugin : public relation_plugin { + friend class sieve_relation; + public: + struct rel_spec { + svector m_inner_cols; + family_id m_inner_kind; + + /** + Create uninitialized rel_spec. + */ + rel_spec() {} + /** + \c inner_kind==null_family_id means we will not specify a relation kind when requesting + the relation object from the relation_manager. + + \c inner_kind==null_family_id cannot hold in a specification of existing relation object. + */ + rel_spec(unsigned sig_sz, const bool * inner_cols, family_id inner_kind=null_family_id) + : m_inner_cols(sig_sz, inner_cols), m_inner_kind(inner_kind) {} + + bool operator==(const rel_spec & o) const { + return m_inner_kind==o.m_inner_kind && vectors_equal(m_inner_cols, o.m_inner_cols); + } + + struct hash { + unsigned operator()(const rel_spec & s) const { + return svector_hash()(s.m_inner_cols)^s.m_inner_kind; + } + }; + }; + private: + + class join_fn; + class transformer_fn; + class union_fn; + class filter_fn; + class negation_filter_fn; + + rel_spec_store > m_spec_store; + + family_id get_relation_kind(sieve_relation & r, const bool * inner_columns); + + void extract_inner_columns(const relation_signature & s, relation_plugin & inner, + svector & inner_columns); + void extract_inner_signature(const relation_signature & s, relation_signature & inner_sig); + void collect_inner_signature(const relation_signature & s, const svector & inner_columns, + relation_signature & inner_sig); + public: + static symbol get_name() { return symbol("sieve_relation"); } + static sieve_relation_plugin& get_plugin(relation_manager & rmgr); + + static sieve_relation& get(relation_base& r); + static sieve_relation const & get(relation_base const& r); + static sieve_relation* get(relation_base* r); + static sieve_relation const* get(relation_base const* r); + + sieve_relation_plugin(relation_manager & manager); + + virtual void initialize(family_id fid); + + family_id get_relation_kind(const relation_signature & sig, const bool * inner_columns, + family_id inner_kind); + family_id get_relation_kind(const relation_signature & sig, const svector & inner_columns, + family_id inner_kind) { + SASSERT(sig.size()==inner_columns.size()); + return get_relation_kind(sig, inner_columns.c_ptr(), inner_kind); + } + + virtual bool can_handle_signature(const relation_signature & s); + + virtual relation_base * mk_empty(const relation_signature & s); + sieve_relation * mk_empty(const sieve_relation & original); + virtual relation_base * mk_empty(const relation_base & original); + virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + sieve_relation * mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin); + + + sieve_relation * mk_from_inner(const relation_signature & s, const bool * inner_columns, + relation_base * inner_rel); + sieve_relation * mk_from_inner(const relation_signature & s, const svector inner_columns, + relation_base * inner_rel) { + SASSERT(inner_columns.size()==s.size()); + return mk_from_inner(s, inner_columns.c_ptr(), inner_rel); + } + + protected: + + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + }; + + + // ----------------------------------- + // + // sieve_relation + // + // ----------------------------------- + + class sieve_relation : public relation_base { + friend class sieve_relation_plugin; + friend class sieve_relation_plugin::join_fn; + friend class sieve_relation_plugin::transformer_fn; + friend class sieve_relation_plugin::union_fn; + friend class sieve_relation_plugin::filter_fn; + + svector m_inner_cols; + + unsigned_vector m_sig2inner; + unsigned_vector m_inner2sig; + unsigned_vector m_ignored_cols; //in ascending order, so that it can be used in project-like functions + + scoped_rel m_inner; + + + sieve_relation(sieve_relation_plugin & p, const relation_signature & s, + const bool * inner_columns, relation_base * inner); + + public: + sieve_relation_plugin & get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + bool is_inner_col(unsigned idx) const { return m_sig2inner[idx]!=UINT_MAX; } + unsigned get_inner_col(unsigned idx) const { + SASSERT(is_inner_col(idx)); + return m_sig2inner[idx]; + } + bool no_sieved_columns() const { return m_ignored_cols.size()==0; } + bool no_inner_columns() const { return m_ignored_cols.size()==get_signature().size(); } + + relation_base & get_inner() { return *m_inner; } + const relation_base & get_inner() const { return *m_inner; } + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual sieve_relation * clone() const; + virtual relation_base * complement(func_decl*p) const; + virtual void to_formula(expr_ref& fml) const; + + virtual bool empty() const { return get_inner().empty(); } + virtual void reset() { get_inner().reset(); } + virtual unsigned get_size_estimate_rows() const { return get_inner().get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return get_inner().get_size_estimate_bytes(); } + + virtual void display(std::ostream & out) const; + }; + + +}; + +#endif /* _DL_SIEVE_RELATION_H_ */ + diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp new file mode 100644 index 000000000..52d9618b8 --- /dev/null +++ b/src/muz/rel/dl_sparse_table.cpp @@ -0,0 +1,1246 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_sparse_table.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ + +#include +#include"dl_context.h" +#include"dl_util.h" +#include"dl_sparse_table.h" + +namespace datalog { + + // ----------------------------------- + // + // entry_storage + // + // ----------------------------------- + + entry_storage::store_offset entry_storage::insert_or_get_reserve_content() { + SASSERT(has_reserve()); + store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); + if (m_reserve == entry_ofs) { + //entry inserted, so reserve is no longer a reserve + m_reserve = NO_RESERVE; + } + return entry_ofs; + } + bool entry_storage::insert_reserve_content() { + SASSERT(has_reserve()); + store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); + if (m_reserve == entry_ofs) { + //entry inserted, so reserve is no longer a reserve + m_reserve = NO_RESERVE; + return true; + } + return false; + } + + bool entry_storage::remove_reserve_content() { + SASSERT(has_reserve()); + store_offset entry_ofs; + if (!find_reserve_content(entry_ofs)) { + //the fact was not in the table + return false; + } + remove_offset(entry_ofs); + return true; + } + + void entry_storage::remove_offset(store_offset ofs) { + m_data_indexer.remove(ofs); + store_offset last_ofs = after_last_offset() - m_entry_size; + if (ofs!=last_ofs) { + SASSERT(ofs + m_entry_size <= last_ofs); + //we don't want any holes, so we put the last element at the place + //of the removed one + m_data_indexer.remove(last_ofs); + char * base = &m_data.get(0); + memcpy(base+ofs, base+last_ofs, m_entry_size); + m_data_indexer.insert(ofs); + } + if (has_reserve()) { + //we already have a reserve, so we need to shrink a little to keep having just one + resize_data(m_data_size-m_entry_size); + } + m_reserve=last_ofs; + } + + unsigned entry_storage::get_size_estimate_bytes() const { + unsigned sz = m_data.capacity(); + sz += m_data_indexer.capacity()*sizeof(storage_indexer::entry); + return sz; + } + + // ----------------------------------- + // + // sparse_table::column_layout + // + // ----------------------------------- + + unsigned get_domain_length(uint64 dom_size) { + SASSERT(dom_size>0); + + unsigned length = 0; + + unsigned dom_size_sm; + if (dom_size>UINT_MAX) { + dom_size_sm = static_cast(dom_size>>32); + length += 32; + if ( (dom_size&UINT_MAX)!=0 && dom_size_sm!=UINT_MAX ) { + dom_size_sm++; + } + } + else { + dom_size_sm=static_cast(dom_size); + } + if (dom_size_sm == 1) { + length += 1; //unary domains + } + else if (dom_size_sm > 0x80000000u) { + length += 32; + } + else { + length += get_num_1bits(next_power_of_two(dom_size_sm)-1); //ceil(log2(dom_size)) + } + return length; + } + + sparse_table::column_layout::column_layout(const table_signature & sig) + : m_functional_col_cnt(sig.functional_columns()) { + SASSERT(sig.size() > 0); + unsigned ofs = 0; + unsigned sig_sz = sig.size(); + unsigned first_functional = sig_sz-m_functional_col_cnt; + for (unsigned i=0; i0); + SASSERT(length<=64); + + if (size() > 0 && (length > 54 || i == first_functional)) { + //large domains must start byte-aligned, as well as functional columns + make_byte_aligned_end(size()-1); + ofs = back().next_ofs(); + } + + push_back(column_info(ofs, length)); + ofs += length; + } + make_byte_aligned_end(size()-1); + SASSERT(back().next_ofs()%8 == 0);//the entries must be aligned to whole bytes + m_entry_size = back().next_ofs()/8; + if (m_functional_col_cnt) { + SASSERT((*this)[first_functional].m_offset%8 == 0); + m_functional_part_size = m_entry_size - (*this)[first_functional].m_offset/8; + } + else { + m_functional_part_size = 0; + } + } + + void sparse_table::column_layout::make_byte_aligned_end(unsigned col_index0) { + unsigned ofs = (*this)[col_index0].next_ofs(); + unsigned ofs_bit_part = ofs%8; + unsigned rounded_ofs = (ofs_bit_part == 0) ? ofs : (ofs+8-ofs_bit_part); + + if (rounded_ofs!=ofs) { + SASSERT(rounded_ofs>ofs); + int diff = rounded_ofs-ofs; + unsigned col_idx = col_index0+1; + while(diff!=0) { + //we should always be able to fix the alignment by the time we reach zero + SASSERT(col_idx>0); + col_idx--; + column_info & ci = (*this)[col_idx]; + unsigned new_length = ci.m_length; + if (ci.m_length < 64) { + unsigned swallowed = std::min(64-static_cast(ci.m_length), diff); + diff -= swallowed; + new_length += swallowed; + } + unsigned new_ofs = ci.m_offset+diff; + ci = column_info(new_ofs, new_length); + } + } + + SASSERT(rounded_ofs%8 == 0); + SASSERT((*this)[col_index0].next_ofs()%8 == 0); + } + + // ----------------------------------- + // + // sparse_table + // + // ----------------------------------- + + class sparse_table::our_iterator_core : public iterator_core { + + class our_row : public row_interface { + const our_iterator_core & m_parent; + public: + our_row(const sparse_table & t, const our_iterator_core & parent) : + row_interface(t), + m_parent(parent) {} + + virtual table_element operator[](unsigned col) const { + return m_parent.m_layout.get(m_parent.m_ptr, col); + } + + }; + + const char * m_end; + const char * m_ptr; + unsigned m_fact_size; + our_row m_row_obj; + const column_layout & m_layout; + + public: + our_iterator_core(const sparse_table & t, bool finished) : + m_end(t.m_data.after_last()), + m_ptr(finished ? m_end : t.m_data.begin()), + m_fact_size(t.m_fact_size), + m_row_obj(t, *this), + m_layout(t.m_column_layout) {} + + virtual bool is_finished() const { + return m_ptr == m_end; + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + virtual void operator++() { + SASSERT(!is_finished()); + m_ptr+=m_fact_size; + } + }; + + class sparse_table::key_indexer { + protected: + unsigned_vector m_key_cols; + public: + typedef const store_offset * offset_iterator; + + /** + Iterators returned by \c begin() and \c end() are valid only as long as the \c query_result + object that returned them exists. + */ + struct query_result { + private: + bool m_singleton; + union { + store_offset m_single_result; + struct { + offset_iterator begin; + offset_iterator end; + } m_many; + }; + public: + /** + \brief Empty result. + */ + query_result() : m_singleton(false) { + m_many.begin = 0; + m_many.end = 0; + } + query_result(offset_iterator begin, offset_iterator end) : m_singleton(false) { + m_many.begin = begin; + m_many.end = end; + } + query_result(store_offset single_result) : m_singleton(true), m_single_result(single_result) {} + + offset_iterator begin() const { return m_singleton ? &m_single_result : m_many.begin; } + offset_iterator end() const { return m_singleton ? (&m_single_result+1) : m_many.end; } + bool empty() const { return begin() == end(); } + }; + + key_indexer(unsigned key_len, const unsigned * key_cols) + : m_key_cols(key_len, key_cols) {} + + virtual ~key_indexer() {} + + virtual void update(const sparse_table & t) {} + + virtual query_result get_matching_offsets(const key_value & key) const = 0; + }; + + + class sparse_table::general_key_indexer : public key_indexer { + typedef svector offset_vector; + typedef u_map index_map; + + index_map m_map; + mutable entry_storage m_keys; + store_offset m_first_nonindexed; + + + void key_to_reserve(const key_value & key) const { + m_keys.ensure_reserve(); + m_keys.write_into_reserve(reinterpret_cast(key.c_ptr())); + } + + offset_vector & get_matching_offset_vector(const key_value & key) { + key_to_reserve(key); + store_offset ofs = m_keys.insert_or_get_reserve_content(); + index_map::entry * e = m_map.find_core(ofs); + if (!e) { + TRACE("dl_table_relation", tout << "inserting\n";); + e = m_map.insert_if_not_there2(ofs, offset_vector()); + } + return e->get_data().m_value; + } + public: + general_key_indexer(unsigned key_len, const unsigned * key_cols) + : key_indexer(key_len, key_cols), + m_keys(key_len*sizeof(table_element)), + m_first_nonindexed(0) {} + + virtual void update(const sparse_table & t) { + if (m_first_nonindexed == t.m_data.after_last_offset()) { + return; + } + SASSERT(m_first_nonindexedinsert(ofs); + } + + m_first_nonindexed = t.m_data.after_last_offset(); + } + + virtual query_result get_matching_offsets(const key_value & key) const { + key_to_reserve(key); + store_offset ofs; + if (!m_keys.find_reserve_content(ofs)) { + return query_result(); + } + index_map::entry * e = m_map.find_core(ofs); + if (!e) { + return query_result(); + } + const offset_vector & res = e->get_data().m_value; + return query_result(res.begin(), res.end()); + } + }; + + /** + When doing lookup using this index, the content of the reserve in sparse_table::m_data changes. + */ + class sparse_table::full_signature_key_indexer : public key_indexer { + const sparse_table & m_table; + + /** + Permutation of key columns to make it into table facts. If empty, no permutation is necessary. + */ + unsigned_vector m_permutation; + mutable table_fact m_key_fact; + public: + + static bool can_handle(unsigned key_len, const unsigned * key_cols, const sparse_table & t) { + unsigned non_func_cols = t.get_signature().first_functional(); + if (key_len!=non_func_cols) { + return false; + } + counter ctr; + ctr.count(key_len, key_cols); + if (ctr.get_max_counter_value()!=1 || ctr.get_max_positive()!=non_func_cols-1) { + return false; + } + SASSERT(ctr.get_positive_count() == non_func_cols); + return true; + } + + full_signature_key_indexer(unsigned key_len, const unsigned * key_cols, const sparse_table & t) + : key_indexer(key_len, key_cols), + m_table(t) { + SASSERT(can_handle(key_len, key_cols, t)); + + m_permutation.resize(key_len); + for (unsigned i=0; i(m_table); + t.write_into_reserve(m_key_fact.c_ptr()); + + store_offset res; + if (!t.m_data.find_reserve_content(res)) { + return query_result(); + } + return query_result(res); + } + }; + + sparse_table::sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity) + : table_base(p, sig), + m_column_layout(sig), + m_fact_size(m_column_layout.m_entry_size), + m_data(m_fact_size, m_column_layout.m_functional_part_size, init_capacity) {} + + sparse_table::sparse_table(const sparse_table & t) + : table_base(t.get_plugin(), t.get_signature()), + m_column_layout(t.m_column_layout), + m_fact_size(t.m_fact_size), + m_data(t.m_data) {} + + table_base * sparse_table::clone() const { + return get_plugin().mk_clone(*this); + } + + sparse_table::~sparse_table() { + reset_indexes(); + } + + void sparse_table::reset() { + reset_indexes(); + m_data.reset(); + } + + table_base::iterator sparse_table::begin() const { + return mk_iterator(alloc(our_iterator_core, *this, false)); + } + + table_base::iterator sparse_table::end() const { + return mk_iterator(alloc(our_iterator_core, *this, true)); + } + + sparse_table::key_indexer& sparse_table::get_key_indexer(unsigned key_len, + const unsigned * key_cols) const { +#if Z3DEBUG + //We allow indexes only on non-functional columns because we want to be able to modify them + //without having to worry about updating indexes. + //Maybe we might keep a list of indexes that contain functional columns and on an update reset + //only those. + SASSERT(key_len == 0 || + counter().count(key_len, key_cols).get_max_positive()get_data().m_value) { + if (full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { + key_map_entry->get_data().m_value = alloc(full_signature_key_indexer, key_len, key_cols, *this); + } + else { + key_map_entry->get_data().m_value = alloc(general_key_indexer, key_len, key_cols); + } + } + key_indexer & indexer = *key_map_entry->get_data().m_value; + indexer.update(*this); + return indexer; + } + + void sparse_table::reset_indexes() { + key_index_map::iterator kmit = m_key_indexes.begin(); + key_index_map::iterator kmend = m_key_indexes.end(); + for (; kmit!=kmend; ++kmit) { + dealloc((*kmit).m_value); + } + m_key_indexes.reset(); + } + + void sparse_table::write_into_reserve(const table_element* f) { + TRACE("dl_table_relation", tout << "\n";); + m_data.ensure_reserve(); + char * reserve = m_data.get_reserve_ptr(); + unsigned col_cnt = m_column_layout.size(); + for (unsigned i = 0; i < col_cnt; ++i) { + SASSERT(f[i] < get_signature()[i]); //the value fits into the table signature + m_column_layout.set(reserve, i, f[i]); + } + } + + bool sparse_table::add_fact(const char * data) { + m_data.write_into_reserve(data); + return add_reserve_content(); + } + + void sparse_table::add_fact(const table_fact & f) { + write_into_reserve(f.c_ptr()); + add_reserve_content(); + } + + bool sparse_table::add_reserve_content() { + return m_data.insert_reserve_content(); + } + + bool sparse_table::contains_fact(const table_fact & f) const { + sparse_table & t = const_cast(*this); + t.write_into_reserve(f.c_ptr()); + unsigned func_col_cnt = get_signature().functional_columns(); + if (func_col_cnt == 0) { + return t.m_data.reserve_content_already_present(); + } + else { + store_offset ofs; + if (!t.m_data.find_reserve_content(ofs)) { + return false; + } + unsigned sz = get_signature().size(); + for (unsigned i=func_col_cnt; i(*this); + t.write_into_reserve(f.c_ptr()); + store_offset ofs; + if (!t.m_data.find_reserve_content(ofs)) { + return false; + } + unsigned sz = sig.size(); + for (unsigned i=sig.first_functional(); ipre_projection_idx); + dest_layout.set(dest, dest_idx++, src_layout.get(src, i)); + } + } + + void sparse_table::concatenate_rows(const column_layout & layout1, const column_layout & layout2, + const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, + const unsigned * removed_cols) { + unsigned t1non_func = layout1.size()-layout1.m_functional_col_cnt; + unsigned t2non_func = layout2.size()-layout2.m_functional_col_cnt; + unsigned t1cols = layout1.size(); + unsigned t2cols = layout2.size(); + unsigned orig_i = 0; + unsigned res_i = 0; + const unsigned * next_removed = removed_cols; + copy_columns(layout1, layout_res, 0, t1non_func, ptr1, res, res_i, orig_i, next_removed); + copy_columns(layout2, layout_res, 0, t2non_func, ptr2, res, res_i, orig_i, next_removed); + copy_columns(layout1, layout_res, t1non_func, t1cols, ptr1, res, res_i, orig_i, next_removed); + copy_columns(layout2, layout_res, t2non_func, t2cols, ptr2, res, res_i, orig_i, next_removed); + } + + void sparse_table::garbage_collect() { + if (memory::above_high_watermark()) { + get_plugin().garbage_collect(); + } + if (memory::above_high_watermark()) { + IF_VERBOSE(1, verbose_stream() << "Ran out of memory while filling table of size: " << get_size_estimate_rows() << " rows " << get_size_estimate_bytes() << " bytes\n";); + throw out_of_memory_error(); + } + } + + void sparse_table::self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, + unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, + const unsigned * removed_cols, bool tables_swapped, sparse_table & result) { + + unsigned t1_entry_size = t1.m_fact_size; + unsigned t2_entry_size = t2.m_fact_size; + + unsigned t1idx = 0; + unsigned t1end = t1.m_data.after_last_offset(); + + TRACE("dl_table_relation", + tout << "joined_col_cnt: " << joined_col_cnt << "\n"; + tout << "t1_entry_size: " << t1_entry_size << "\n"; + tout << "t2_entry_size: " << t2_entry_size << "\n"; + t1.display(tout); + t2.display(tout); + tout << (&t1) << " " << (&t2) << " " << (&result) << "\n"; + ); + + if (joined_col_cnt == 0) { + unsigned t2idx = 0; + unsigned t2end = t2.m_data.after_last_offset(); + + for (; t1idx!=t1end; t1idx+=t1_entry_size) { + for (t2idx = 0; t2idx != t2end; t2idx += t2_entry_size) { + result.m_data.ensure_reserve(); + result.garbage_collect(); + char * res_reserve = result.m_data.get_reserve_ptr(); + char const* t1ptr = t1.get_at_offset(t1idx); + char const* t2ptr = t2.get_at_offset(t2idx); + if (tables_swapped) { + concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, + t2ptr, t1ptr, res_reserve, removed_cols); + } else { + concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, + t1ptr, t2ptr, res_reserve, removed_cols); + } + result.add_reserve_content(); + } + } + return; + } + + key_value t1_key; + t1_key.resize(joined_col_cnt); + key_indexer& t2_indexer = t2.get_key_indexer(joined_col_cnt, t2_joined_cols); + + bool key_modified = true; + key_indexer::query_result t2_offsets; + + for (; t1idx != t1end; t1idx += t1_entry_size) { + for (unsigned i = 0; i < joined_col_cnt; i++) { + table_element val = t1.m_column_layout.get(t1.get_at_offset(t1idx), t1_joined_cols[i]); + TRACE("dl_table_relation", tout << "val: " << val << " " << t1idx << " " << t1_joined_cols[i] << "\n";); + if (t1_key[i] != val) { + t1_key[i] = val; + key_modified = true; + } + } + if (key_modified) { + t2_offsets = t2_indexer.get_matching_offsets(t1_key); + key_modified = false; + } + + if (t2_offsets.empty()) { + continue; + } + + key_indexer::offset_iterator t2ofs_it = t2_offsets.begin(); + key_indexer::offset_iterator t2ofs_end = t2_offsets.end(); + for (; t2ofs_it != t2ofs_end; ++t2ofs_it) { + store_offset t2ofs = *t2ofs_it; + result.m_data.ensure_reserve(); + result.garbage_collect(); + char * res_reserve = result.m_data.get_reserve_ptr(); + char const * t1ptr = t1.get_at_offset(t1idx); + char const * t2ptr = t2.get_at_offset(t2ofs); + if (tables_swapped) { + concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, + t2ptr, t1ptr, res_reserve, removed_cols); + } else { + concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, + t1ptr, t2ptr, res_reserve, removed_cols); + } + result.add_reserve_content(); + } + } + } + + + // ----------------------------------- + // + // sparse_table_plugin + // + // ----------------------------------- + + sparse_table_plugin::sparse_table_plugin(relation_manager & manager) + : table_plugin(symbol("sparse"), manager) {} + + sparse_table_plugin::~sparse_table_plugin() { + reset(); + } + + void sparse_table_plugin::reset() { + table_pool::iterator it = m_pool.begin(); + table_pool::iterator end = m_pool.end(); + for (; it!=end; ++it) { + sp_table_vector * vect = it->m_value; + sp_table_vector::iterator it = vect->begin(); + sp_table_vector::iterator end = vect->end(); + for (; it!=end; ++it) { + (*it)->destroy(); //calling deallocate() would only put the table back into the pool + } + dealloc(vect); + } + m_pool.reset(); + } + + void sparse_table_plugin::garbage_collect() { + IF_VERBOSE(2, verbose_stream() << "garbage collecting "<< memory::get_allocation_size() << " bytes down to ";); + reset(); + IF_VERBOSE(2, verbose_stream() << memory::get_allocation_size() << " bytes\n";); + } + + void sparse_table_plugin::recycle(sparse_table * t) { + const table_signature & sig = t->get_signature(); + t->reset(); + + table_pool::entry * e = m_pool.insert_if_not_there2(sig, 0); + sp_table_vector * & vect = e->get_data().m_value; + if (vect == 0) { + vect = alloc(sp_table_vector); + } + IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); + + vect->push_back(t); + } + + table_base * sparse_table_plugin::mk_empty(const table_signature & s) { + SASSERT(can_handle_signature(s)); + + sp_table_vector * vect; + if (!m_pool.find(s, vect) || vect->empty()) { + return alloc(sparse_table, *this, s); + } + sparse_table * res = vect->back(); + vect->pop_back(); + return res; + } + + sparse_table * sparse_table_plugin::mk_clone(const sparse_table & t) { + sparse_table * res = static_cast(mk_empty(t.get_signature())); + res->m_data = t.m_data; + return res; + } + + + bool sparse_table_plugin::join_involves_functional(const table_signature & s1, const table_signature & s2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (col_cnt == 0) { + return false; + } + return counter().count(col_cnt, cols1).get_max_positive()>=s1.first_functional() + || counter().count(col_cnt, cols2).get_max_positive()>=s2.first_functional(); + } + + + class sparse_table_plugin::join_project_fn : public convenient_table_join_project_fn { + public: + join_project_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, + removed_col_cnt, removed_cols) { + m_removed_cols.push_back(UINT_MAX); + } + + virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { + + const sparse_table & t1 = static_cast(tb1); + const sparse_table & t2 = static_cast(tb2); + + sparse_table_plugin & plugin = t1.get_plugin(); + + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + //If we join with some intersection, want to iterate over the smaller table and + //do indexing into the bigger one. If we simply do a product, we want the bigger + //one to be at the outer iteration (then the small one will hopefully fit into + //the cache) + if ( (t1.row_count() > t2.row_count()) == (!m_cols1.empty()) ) { + sparse_table::self_agnostic_join_project(t2, t1, m_cols1.size(), m_cols2.c_ptr(), + m_cols1.c_ptr(), m_removed_cols.c_ptr(), true, *res); + } + else { + sparse_table::self_agnostic_join_project(t1, t2, m_cols1.size(), m_cols1.c_ptr(), + m_cols2.c_ptr(), m_removed_cols.c_ptr(), false, *res); + } + TRACE("dl_table_relation", tb1.display(tout); tb2.display(tout); res->display(tout); ); + return res; + } + }; + + table_join_fn * sparse_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + const table_signature & sig1 = t1.get_signature(); + const table_signature & sig2 = t2.get_signature(); + if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() + || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { + //We also don't allow indexes on functional columns (and they are needed for joins) + return 0; + } + return mk_join_project_fn(t1, t2, col_cnt, cols1, cols2, 0, static_cast(0)); + } + + table_join_fn * sparse_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + const table_signature & sig1 = t1.get_signature(); + const table_signature & sig2 = t2.get_signature(); + if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() + || removed_col_cnt == t1.get_signature().size()+t2.get_signature().size() + || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { + //We don't allow sparse tables with zero signatures (and project on all columns leads to such) + //We also don't allow indexes on functional columns. + return 0; + } + return alloc(join_project_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + } + + class sparse_table_plugin::union_fn : public table_union_fn { + public: + virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { + + sparse_table & tgt = static_cast(tgt0); + const sparse_table & src = static_cast(src0); + sparse_table * delta = static_cast(delta0); + + unsigned fact_size = tgt.m_fact_size; + const char* ptr = src.m_data.begin(); + const char* after_last=src.m_data.after_last(); + for (; ptradd_fact(ptr); + } + } + } + }; + + table_union_fn * sparse_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta) { + if (tgt.get_kind()!=get_kind() || src.get_kind()!=get_kind() + || (delta && delta->get_kind()!=get_kind()) + || tgt.get_signature()!=src.get_signature() + || (delta && delta->get_signature()!=tgt.get_signature())) { + return 0; + } + return alloc(union_fn); + } + + class sparse_table_plugin::project_fn : public convenient_table_project_fn { + const unsigned m_inp_col_cnt; + const unsigned m_removed_col_cnt; + const unsigned m_result_col_cnt; + public: + project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols), + m_inp_col_cnt(orig_sig.size()), + m_removed_col_cnt(removed_col_cnt), + m_result_col_cnt(orig_sig.size()-removed_col_cnt) { + SASSERT(removed_col_cnt>0); + } + + virtual void transform_row(const char * src, char * tgt, + const sparse_table::column_layout & src_layout, + const sparse_table::column_layout & tgt_layout) { + unsigned r_idx=0; + unsigned tgt_i=0; + for (unsigned i=0; i(tb); + + unsigned t_fact_size = t.m_fact_size; + + sparse_table_plugin & plugin = t.get_plugin(); + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + const sparse_table::column_layout & src_layout = t.m_column_layout; + const sparse_table::column_layout & tgt_layout = res->m_column_layout; + + const char* t_ptr = t.m_data.begin(); + const char* t_end = t.m_data.after_last(); + for (; t_ptr!=t_end; t_ptr+=t_fact_size) { + SASSERT(t_ptrm_data.ensure_reserve(); + char * res_ptr = res->m_data.get_reserve_ptr(); + transform_row(t_ptr, res_ptr, src_layout, tgt_layout); + res->m_data.insert_reserve_content(); + } + return res; + } + }; + + table_transformer_fn * sparse_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if (col_cnt == t.get_signature().size()) { + return 0; + } + return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); + } + + + class sparse_table_plugin::select_equal_and_project_fn : public convenient_table_transformer_fn { + const unsigned m_col; + sparse_table::key_value m_key; + public: + select_equal_and_project_fn(const table_signature & orig_sig, table_element val, unsigned col) + : m_col(col) { + table_signature::from_project(orig_sig, 1, &col, get_result_signature()); + m_key.push_back(val); + } + + virtual table_base * operator()(const table_base & tb) { + const sparse_table & t = static_cast(tb); + + sparse_table_plugin & plugin = t.get_plugin(); + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + const sparse_table::column_layout & t_layout = t.m_column_layout; + const sparse_table::column_layout & res_layout = res->m_column_layout; + unsigned t_cols = t_layout.size(); + + sparse_table::key_indexer & indexer = t.get_key_indexer(1, &m_col); + sparse_table::key_indexer::query_result t_offsets = indexer.get_matching_offsets(m_key); + if (t_offsets.empty()) { + //no matches + return res; + } + sparse_table::key_indexer::offset_iterator ofs_it=t_offsets.begin(); + sparse_table::key_indexer::offset_iterator ofs_end=t_offsets.end(); + + for (; ofs_it!=ofs_end; ++ofs_it) { + sparse_table::store_offset t_ofs = *ofs_it; + const char * t_ptr = t.get_at_offset(t_ofs); + + res->m_data.ensure_reserve(); + char * res_reserve = res->m_data.get_reserve_ptr(); + + unsigned res_i = 0; + for (unsigned i=0; iadd_reserve_content(); + } + return res; + } + }; + + table_transformer_fn * sparse_table_plugin::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + if (t.get_kind()!=get_kind() || t.get_signature().size() == 1 || col>=t.get_signature().first_functional()) { + //We don't allow sparse tables with zero signatures (and project on a single + //column table produces one). + //We also don't allow indexes on functional columns. And our implementation of + //select_equal_and_project uses index on \c col. + return 0; + } + return alloc(select_equal_and_project_fn, t.get_signature(), value, col); + } + + + class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { + unsigned_vector m_out_of_cycle; + public: + rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { + SASSERT(permutation_cycle_len>=2); + idx_set cycle_cols; + for (unsigned i=0; i < permutation_cycle_len; ++i) { + cycle_cols.insert(permutation_cycle[i]); + } + for (unsigned i=0; i < orig_sig.size(); ++i) { + if (!cycle_cols.contains(i)) { + m_out_of_cycle.push_back(i); + } + } + } + + void transform_row(const char * src, char * tgt, + const sparse_table::column_layout & src_layout, + const sparse_table::column_layout & tgt_layout) { + + for (unsigned i=1; i < m_cycle.size(); ++i) { + tgt_layout.set(tgt, m_cycle[i-1], src_layout.get(src, m_cycle[i])); + } + tgt_layout.set(tgt, m_cycle[m_cycle.size()-1], src_layout.get(src, m_cycle[0])); + + unsigned_vector::const_iterator it = m_out_of_cycle.begin(); + unsigned_vector::const_iterator end = m_out_of_cycle.end(); + for (; it!=end; ++it) { + unsigned col = *it; + tgt_layout.set(tgt, col, src_layout.get(src, col)); + } + } + + virtual table_base * operator()(const table_base & tb) { + + const sparse_table & t = static_cast(tb); + + unsigned t_fact_size = t.m_fact_size; + + sparse_table_plugin & plugin = t.get_plugin(); + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + unsigned res_fact_size = res->m_fact_size; + unsigned res_data_size = res_fact_size*t.row_count(); + + res->m_data.resize_data(res_data_size); + + //here we can separate data creatin and insertion into hashmap, since we know + //that no row will become duplicit + + //create the data + const char* t_ptr = t.m_data.begin(); + char* res_ptr = res->m_data.begin(); + char* res_end = res_ptr+res_data_size; + for (; res_ptr!=res_end; t_ptr+=t_fact_size, res_ptr+=res_fact_size) { + transform_row(t_ptr, res_ptr, t.m_column_layout, res->m_column_layout); + } + + //and insert them into the hash-map + for (unsigned i=0; i!=res_data_size; i+=res_fact_size) { + TRUSTME(res->m_data.insert_offset(i)); + } + + return res; + } + }; + + table_transformer_fn * sparse_table_plugin::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + if (t.get_kind()!=get_kind()) { + return 0; + } + return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); + } + + class sparse_table_plugin::negation_filter_fn : public convenient_table_negation_filter_fn { + typedef sparse_table::store_offset store_offset; + typedef sparse_table::key_value key_value; + typedef sparse_table::key_indexer key_indexer; + + bool m_joining_neg_non_functional; + + /** + Used by \c collect_intersection_offsets function. + If tgt_is_first is false, contains the same items as \c res. + */ + idx_set m_intersection_content; + + public: + negation_filter_fn(const table_base & tgt, const table_base & neg, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) + : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { + unsigned neg_fisrt_func = neg.get_signature().first_functional(); + counter ctr; + ctr.count(m_cols2); + m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 + && ctr.get_positive_count() == neg_fisrt_func + && (neg_fisrt_func == 0 || ctr.get_max_positive() == neg_fisrt_func-1); + } + + /** + Collect offsets of rows in \c t1 or \c t2 (depends on whether \c tgt_is_first is true or false) + that have a match in the other table into \c res. Offsets in \c res are in ascending order. + */ + void collect_intersection_offsets(const sparse_table & t1, const sparse_table & t2, + bool tgt_is_first, svector & res) { + SASSERT(res.empty()); + + if (!tgt_is_first) { + m_intersection_content.reset(); + } + + unsigned joined_col_cnt = m_cols1.size(); + unsigned t1_entry_size = t1.m_data.entry_size(); + + const unsigned * cols1 = tgt_is_first ? m_cols1.c_ptr() : m_cols2.c_ptr(); + const unsigned * cols2 = tgt_is_first ? m_cols2.c_ptr() : m_cols1.c_ptr(); + + key_value t1_key; + t1_key.resize(joined_col_cnt); + key_indexer & t2_indexer = t2.get_key_indexer(joined_col_cnt, cols2); + + bool key_modified=true; + key_indexer::query_result t2_offsets; + store_offset t1_after_last = t1.m_data.after_last_offset(); + for (store_offset t1_ofs=0; t1_ofs(tgt0); + const sparse_table & neg = static_cast(neg0); + + if (m_cols1.size() == 0) { + if (!neg.empty()) { + tgt.reset(); + } + return; + } + + svector to_remove; //offsets here are in increasing order + + //We don't do just the simple tgt.row_count()>neg.row_count() because the swapped case is + //more expensive. The constant 4 is, however, just my guess what the ratio might be. + if (tgt.row_count()/4>neg.row_count()) { + collect_intersection_offsets(neg, tgt, false, to_remove); + } + else { + collect_intersection_offsets(tgt, neg, true, to_remove); + } + + if (to_remove.empty()) { + return; + } + + //the largest offsets are at the end, so we can remove them one by one + while(!to_remove.empty()) { + store_offset removed_ofs = to_remove.back(); + to_remove.pop_back(); + tgt.m_data.remove_offset(removed_ofs); + } + tgt.reset_indexes(); + } + + }; + + table_intersection_filter_fn * sparse_table_plugin::mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + if (!check_kind(t) || !check_kind(negated_obj) + || join_involves_functional(t.get_signature(), negated_obj.get_signature(), joined_col_cnt, + t_cols, negated_cols) ) { + return 0; + } + return alloc(negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + + unsigned sparse_table::get_size_estimate_bytes() const { + unsigned sz = 0; + sz += m_data.get_size_estimate_bytes(); + sz += m_key_indexes.capacity()*8; // TBD + return sz; + } + + +}; + diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h new file mode 100644 index 000000000..010277b6b --- /dev/null +++ b/src/muz/rel/dl_sparse_table.h @@ -0,0 +1,480 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-01. + +Revision History: + +--*/ + +#ifndef _DL_SPARSE_TABLE_H_ +#define _DL_SPARSE_TABLE_H_ + +#include +#include +#include + +#include "ast.h" +#include "bit_vector.h" +#include "buffer.h" +#include "hashtable.h" +#include "map.h" +#include "ref_vector.h" +#include "vector.h" + +#include "dl_base.h" + + +namespace datalog { + class sparse_table; + + class sparse_table_plugin : public table_plugin { + friend class sparse_table; + protected: + class join_project_fn; + class union_fn; + class transformer_fn; + class rename_fn; + class project_fn; + class negation_filter_fn; + class select_equal_and_project_fn; + + typedef ptr_vector sp_table_vector; + typedef map table_pool; + + table_pool m_pool; + + void recycle(sparse_table * t); + + void garbage_collect(); + + void reset(); + + static bool join_involves_functional(const table_signature & s1, const table_signature & s2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + + public: + typedef sparse_table table; + + sparse_table_plugin(relation_manager & manager); + ~sparse_table_plugin(); + + virtual bool can_handle_signature(const table_signature & s) + { return s.size()>0; } + + virtual table_base * mk_empty(const table_signature & s); + sparse_table * mk_clone(const sparse_table & t); + + protected: + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); + virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + }; + + class entry_storage { + public: + typedef unsigned store_offset; + private: + typedef svector storage; + + class offset_hash_proc { + storage & m_storage; + unsigned m_unique_entry_size; + public: + offset_hash_proc(storage & s, unsigned unique_entry_sz) + : m_storage(s), m_unique_entry_size(unique_entry_sz) {} + unsigned operator()(store_offset ofs) const { + return string_hash(m_storage.c_ptr()+ofs, m_unique_entry_size, 0); + } + }; + + class offset_eq_proc { + storage & m_storage; + unsigned m_unique_entry_size; + public: + offset_eq_proc(storage & s, unsigned unique_entry_sz) + : m_storage(s), m_unique_entry_size(unique_entry_sz) {} + bool operator()(store_offset o1, store_offset o2) const { + const char * base = m_storage.c_ptr(); + return memcmp(base+o1, base+o2, m_unique_entry_size)==0; + } + }; + + typedef hashtable storage_indexer; + + static const store_offset NO_RESERVE = UINT_MAX; + + unsigned m_entry_size; + unsigned m_unique_part_size; + unsigned m_data_size; + /** + Invariant: Every or all but one blocks of length \c m_entry_size in the \c m_data vector + are unique sequences of bytes and have their offset stored in the \c m_data_indexer hashtable. + If the offset of the last block is not stored in the hashtable, it is stored in the \c m_reserve + variable. Otherwise \c m_reserve==NO_RESERVE. + + The size of m_data is actually 8 bytes larger than stated in m_data_size, so that we may + deref an uint64 pointer at the end of the array. + */ + storage m_data; + storage_indexer m_data_indexer; + store_offset m_reserve; + public: + entry_storage(unsigned entry_size, unsigned functional_size = 0, unsigned init_size = 0) + : m_entry_size(entry_size), + m_unique_part_size(entry_size-functional_size), + m_data_indexer(next_power_of_two(std::max(8u,init_size)), + offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), + m_reserve(NO_RESERVE) { + SASSERT(entry_size>0); + SASSERT(functional_size<=entry_size); + resize_data(init_size); + resize_data(0); + } + entry_storage(const entry_storage &s) + : m_entry_size(s.m_entry_size), + m_unique_part_size(s.m_unique_part_size), + m_data_size(s.m_data_size), + m_data(s.m_data), + m_data_indexer(next_power_of_two(std::max(8u,s.entry_count())), + offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), + m_reserve(s.m_reserve) { + store_offset after_last=after_last_offset(); + for(store_offset i=0; i(this)->get(ofs); } + + unsigned entry_count() const { return m_data_indexer.size(); } + + store_offset after_last_offset() const { + return (m_reserve==NO_RESERVE) ? m_data_size : m_reserve; + } + + char * begin() { return get(0); } + const char * begin() const { return get(0); } + const char * after_last() const { return get(after_last_offset()); } + + + bool has_reserve() const { return m_reserve!=NO_RESERVE; } + store_offset reserve() const { SASSERT(has_reserve()); return m_reserve; } + + void ensure_reserve() { + if(has_reserve()) { + SASSERT(m_reserve==m_data_size-m_entry_size); + return; + } + m_reserve=m_data_size; + resize_data(m_data_size+m_entry_size); + } + + /** + \brief Return pointer to the reserve. + + The reserve must exist when the function is called. + */ + char * get_reserve_ptr() { + SASSERT(has_reserve()); + return &m_data.get(reserve()); + } + + bool reserve_content_already_present() const { + SASSERT(has_reserve()); + return m_data_indexer.contains(reserve()); + } + + bool find_reserve_content(store_offset & result) const { + SASSERT(has_reserve()); + storage_indexer::entry * indexer_entry = m_data_indexer.find_core(reserve()); + if(!indexer_entry) { + return false; + } + result = indexer_entry->get_data(); + return true; + } + + /** + \brief Write fact \c f into the reserve at the end of the \c m_data storage. + + If the reserve does not exist, this function creates it. + */ + void write_into_reserve(const char * data) { + ensure_reserve(); + memcpy(get_reserve_ptr(), data, m_entry_size); + } + + /** + \brief If the fact in reserve is not in the table, insert it there and return true; + otherwise return false. + + When a fact is inserted into the table, the reserve becomes part of the table and + is no longer a reserve. + */ + bool insert_reserve_content(); + store_offset insert_or_get_reserve_content(); + bool remove_reserve_content(); + /** + Remove data at the offset \c ofs. + + Data with offset lower than \c ofs are not be modified by this function, data with + higher offset may be moved. + */ + void remove_offset(store_offset ofs); + + + //the following two operations allow breaking of the object invariant! + void resize_data(unsigned sz) { + m_data_size = sz; + m_data.resize(sz + sizeof(uint64)); + } + + bool insert_offset(store_offset ofs) { + return m_data_indexer.insert_if_not_there(ofs)==ofs; + } + }; + + class sparse_table : public table_base { + friend class sparse_table_plugin; + friend class sparse_table_plugin::join_project_fn; + friend class sparse_table_plugin::union_fn; + friend class sparse_table_plugin::transformer_fn; + friend class sparse_table_plugin::rename_fn; + friend class sparse_table_plugin::project_fn; + friend class sparse_table_plugin::negation_filter_fn; + friend class sparse_table_plugin::select_equal_and_project_fn; + + class our_iterator_core; + class key_indexer; + class general_key_indexer; + class full_signature_key_indexer; + typedef entry_storage::store_offset store_offset; + + + class column_info { + unsigned m_big_offset; + unsigned m_small_offset; + uint64 m_mask; + uint64 m_write_mask; + public: + unsigned m_offset; //!< in bits + unsigned m_length; //!< in bits + + column_info(unsigned offset, unsigned length) \ + : m_big_offset(offset/8), + m_small_offset(offset%8), + m_mask( length==64 ? ULLONG_MAX : (static_cast(1)<(rec+m_big_offset); + uint64 res = *ptr; + res>>=m_small_offset; + res&=m_mask; + return res; + } + void set(char * rec, table_element val) const { + SASSERT( (val&~m_mask)==0 ); //the value fits into the column + uint64 * ptr = reinterpret_cast(rec+m_big_offset); + *ptr&=m_write_mask; + *ptr|=val< { + + void make_byte_aligned_end(unsigned col_index); + public: + + unsigned m_entry_size; + /** + Number of last bytes which correspond to functional columns in the signature. + */ + unsigned m_functional_part_size; + unsigned m_functional_col_cnt; + + column_layout(const table_signature & sig); + + table_element get(const char * rec, unsigned col) const { + return (*this)[col].get(rec); + } + void set(char * rec, unsigned col, table_element val) const { + return (*this)[col].set(rec, val); + } + }; + + + typedef svector key_spec; //sequence of columns in a key + typedef svector key_value; //values of key columns + typedef map, + vector_eq_proc > key_index_map; + + static const store_offset NO_RESERVE = UINT_MAX; + + column_layout m_column_layout; + unsigned m_fact_size; + entry_storage m_data; + mutable key_index_map m_key_indexes; + + + const char * get_at_offset(store_offset i) const { + return m_data.get(i); + } + + table_element get_cell(store_offset ofs, unsigned column) const { + return m_column_layout.get(m_data.get(ofs), column); + } + + void set_cell(store_offset ofs, unsigned column, table_element val) { + m_column_layout.set(m_data.get(ofs), column, val); + } + + void write_into_reserve(const table_element* f); + + /** + \brief Return reference to an indexer over columns in \c key_cols. + + An indexer can retrieve a sequence of offsets that with \c key_cols columns equal to + the specified key. Indexers are populated lazily -- they remember the position of the + last fact they contain, and when an indexer is retrieved by the \c get_key_indexer function, + all the new facts are added into the indexer. + + When a fact is removed from the table, all indexers are destroyed. This is not an extra + expense in the current use scenario, because we first perform all fact removals and do the + joins only after that (joins are the only operations that lead to index construction). + */ + key_indexer& get_key_indexer(unsigned key_len, const unsigned * key_cols) const; + + void reset_indexes(); + + static void copy_columns(const column_layout & src_layout, const column_layout & dest_layout, + unsigned start_index, unsigned after_last, const char * src, char * dest, + unsigned & dest_idx, unsigned & pre_projection_idx, const unsigned * & next_removed); + + /** + \c array \c removed_cols contains column indexes to be removed in ascending order and + is terminated by a number greated than the highest column index of a join the the two tables. + This is to simplify the traversal of the array when building facts. + */ + static void concatenate_rows(const column_layout & layout1, const column_layout & layout2, + const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, + const unsigned * removed_cols); + + /** + \brief Perform join-project between t1 and t2 iterating through t1 and retrieving relevant + columns from t2 using indexing. + + \c array \c removed_cols contains column indexes to be removed in ascending order and + is terminated by a number greated than the highest column index of a join the the two tables. + This is to simplify the traversal of the array when building facts. + + \c tables_swapped value means that the resulting facts should contain facts from t2 first, + instead of the default behavior that would concatenate the two facts as \c (t1,t2). + + \remark The function is called \c self_agnostic_join since, unlike the virtual method + \c join, it is static and therefore allows to easily swap the roles of the two joined + tables (the indexed and iterated one) in a way that is expected to give better performance. + */ + static void self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, + unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, + const unsigned * removed_cols, bool tables_swapped, sparse_table & result); + + + /** + If the fact at \c data (in table's native representation) is not in the table, + add it and return true. Otherwise return false. + */ + bool add_fact(const char * data); + + bool add_reserve_content(); + + void garbage_collect(); + + sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity=0); + sparse_table(const sparse_table & t); + virtual ~sparse_table(); + public: + + virtual void deallocate() { + get_plugin().recycle(this); + } + + unsigned row_count() const { return m_data.entry_count(); } + + sparse_table_plugin & get_plugin() const + { return static_cast(table_base::get_plugin()); } + + virtual bool empty() const { return row_count()==0; } + virtual void add_fact(const table_fact & f); + virtual bool contains_fact(const table_fact & f) const; + virtual bool fetch_fact(table_fact & f) const; + virtual void ensure_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual void reset(); + + virtual table_base * clone() const; + + virtual table_base::iterator begin() const; + virtual table_base::iterator end() const; + + virtual unsigned get_size_estimate_rows() const { return row_count(); } + virtual unsigned get_size_estimate_bytes() const; + virtual bool knows_exact_size() const { return true; } + }; + + }; + + #endif /* _DL_SPARSE_TABLE_H_ */ diff --git a/src/muz/rel/dl_table.cpp b/src/muz/rel/dl_table.cpp new file mode 100644 index 000000000..0b8fc0388 --- /dev/null +++ b/src/muz/rel/dl_table.cpp @@ -0,0 +1,773 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-01. + +Revision History: + +--*/ + +#include"dl_context.h" +#include"dl_util.h" +#include"dl_table.h" +#include"dl_relation_manager.h" + +namespace datalog { + + // ----------------------------------- + // + // hashtable_table + // + // ----------------------------------- + + table_base * hashtable_table_plugin::mk_empty(const table_signature & s) { + SASSERT(can_handle_signature(s)); + return alloc(hashtable_table, *this, s); + } + + + class hashtable_table_plugin::join_fn : public convenient_table_join_fn { + unsigned m_joined_col_cnt; + public: + join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) + : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), + m_joined_col_cnt(col_cnt) {} + + virtual table_base * operator()(const table_base & t1, const table_base & t2) { + + const hashtable_table & ht1 = static_cast(t1); + const hashtable_table & ht2 = static_cast(t2); + + hashtable_table_plugin & plugin = ht1.get_plugin(); + + hashtable_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + hashtable_table::storage::iterator els1it = ht1.m_data.begin(); + hashtable_table::storage::iterator els1end = ht1.m_data.end(); + hashtable_table::storage::iterator els2end = ht2.m_data.end(); + + table_fact acc; + + for(; els1it!=els1end; ++els1it) { + const table_fact & row1 = *els1it; + + hashtable_table::storage::iterator els2it = ht2.m_data.begin(); + for(; els2it!=els2end; ++els2it) { + const table_fact & row2 = *els2it; + + bool match=true; + for(unsigned i=0; im_data.insert(acc); + } + } + return res; + } + }; + + table_join_fn * hashtable_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind()) { + return 0; + } + return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + + + class hashtable_table::our_iterator_core : public iterator_core { + const hashtable_table & m_parent; + storage::iterator m_inner; + storage::iterator m_end; + + class our_row : public row_interface { + const our_iterator_core & m_parent; + public: + our_row(const our_iterator_core & parent) : row_interface(parent.m_parent), m_parent(parent) {} + + virtual void get_fact(table_fact & result) const { + result = *m_parent.m_inner; + } + virtual table_element operator[](unsigned col) const { + return (*m_parent.m_inner)[col]; + } + + }; + + our_row m_row_obj; + + public: + our_iterator_core(const hashtable_table & t, bool finished) : + m_parent(t), m_inner(finished ? t.m_data.end() : t.m_data.begin()), + m_end(t.m_data.end()), m_row_obj(*this) {} + + virtual bool is_finished() const { + return m_inner==m_end; + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + virtual void operator++() { + SASSERT(!is_finished()); + ++m_inner; + } + }; + + + + table_base::iterator hashtable_table::begin() const { + return mk_iterator(alloc(our_iterator_core, *this, false)); + } + + table_base::iterator hashtable_table::end() const { + return mk_iterator(alloc(our_iterator_core, *this, true)); + } + + // ----------------------------------- + // + // bitvector_table + // + // ----------------------------------- + + bool bitvector_table_plugin::can_handle_signature(const table_signature & sig) { + if(sig.functional_columns()!=0) { + return false; + } + unsigned cols = sig.size(); + unsigned shift = 0; + for (unsigned i = 0; i < cols; ++i) { + unsigned s = static_cast(sig[i]); + if (s != sig[i] || !is_power_of_two(s)) { + return false; + } + unsigned num_bits = 0; + unsigned bit_pos = 1; + for (num_bits = 1; num_bits < 32; ++num_bits) { + if (bit_pos & s) { + break; + } + bit_pos <<= 1; + } + shift += num_bits; + if (shift >= 32) { + return false; + } + } + return true; + } + + table_base * bitvector_table_plugin::mk_empty(const table_signature & s) { + SASSERT(can_handle_signature(s)); + return alloc(bitvector_table, *this, s); + } + + class bitvector_table::bv_iterator : public iterator_core { + + bitvector_table const& m_bv; + unsigned m_offset; + + class our_row : public caching_row_interface { + const bv_iterator& m_parent; + public: + our_row(const bv_iterator & p) : caching_row_interface(p.m_bv), m_parent(p) {} + virtual void get_fact(table_fact& result) const { + if (result.size() < size()) { + result.resize(size(), 0); + } + m_parent.m_bv.offset2fact(m_parent.m_offset, result); + } + }; + our_row m_row_obj; + + public: + bv_iterator(const bitvector_table& bv, bool end): + m_bv(bv), m_offset(end?m_bv.m_bv.size():0), m_row_obj(*this) + { + if (!is_finished() && !m_bv.m_bv.get(m_offset)) { + ++(*this); + } + } + + virtual bool is_finished() const { + return m_offset == m_bv.m_bv.size(); + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + virtual void operator++() { + SASSERT(!is_finished()); + ++m_offset; + while (!is_finished() && !m_bv.m_bv.get(m_offset)) { + ++m_offset; + } + m_row_obj.reset(); + } + }; + + bitvector_table::bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig) + : table_base(plugin, sig) { + SASSERT(plugin.can_handle_signature(sig)); + + m_num_cols = sig.size(); + unsigned shift = 0; + for (unsigned i = 0; i < m_num_cols; ++i) { + unsigned s = static_cast(sig[i]); + if (s != sig[i] || !is_power_of_two(s)) { + throw default_exception("bit-vector table is specialized to small domains that are powers of two"); + } + m_shift.push_back(shift); + m_mask.push_back(s - 1); + unsigned num_bits = 0; + unsigned bit_pos = 1; + for (num_bits = 1; num_bits < 32; ++num_bits) { + if (bit_pos & s) { + break; + } + bit_pos <<= 1; + } + shift += num_bits; + if (shift >= 32) { + throw default_exception("bit-vector table is specialized to small domains that are powers of two"); + } + m_bv.reserve(1 << shift); + } + } + + unsigned bitvector_table::fact2offset(const table_element* f) const { + unsigned result = 0; + for (unsigned i = 0; i < m_num_cols; ++i) { + SASSERT(f[i]> m_shift[i]); + } + } + + void bitvector_table::add_fact(const table_fact & f) { + m_bv.set(fact2offset(f.c_ptr())); + } + + void bitvector_table::remove_fact(const table_element* fact) { + m_bv.unset(fact2offset(fact)); + } + + bool bitvector_table::contains_fact(const table_fact & f) const { + return m_bv.get(fact2offset(f.c_ptr())); + } + + table_base::iterator bitvector_table::begin() const { + return mk_iterator(alloc(bv_iterator, *this, false)); + } + + table_base::iterator bitvector_table::end() const { + return mk_iterator(alloc(bv_iterator, *this, true)); + } + + + + + // ----------------------------------- + // + // equivalence_table + // + // ----------------------------------- + + bool equivalence_table_plugin::can_handle_signature(const table_signature & sig) { + return sig.functional_columns() == 0 && sig.size() == 2 && sig[0] < UINT_MAX && sig[0] == sig[1]; + } + + bool equivalence_table_plugin::is_equivalence_table(table_base const& tbl) const { + if (tbl.get_kind() != get_kind()) return false; + equivalence_table const& t = static_cast(tbl); + return !t.is_sparse(); + } + + table_base * equivalence_table_plugin::mk_empty(const table_signature & s) { + TRACE("dl", for (unsigned i = 0; i < s.size(); ++i) tout << s[i] << " "; tout << "\n";); + SASSERT(can_handle_signature(s)); + return alloc(equivalence_table, *this, s); + } + + class equivalence_table_plugin::select_equal_and_project_fn : public table_transformer_fn { + unsigned m_val; + table_sort m_sort; + public: + select_equal_and_project_fn(const table_signature & sig, table_element val, unsigned col) + : m_val(static_cast(val)), + m_sort(sig[0]) { + SASSERT(val <= UINT_MAX); + SASSERT(col == 0 || col == 1); + SASSERT(sig.functional_columns() == 0); + SASSERT(sig.size() == 2); + SASSERT(sig[0] < UINT_MAX && sig[0] == sig[1]); + } + + virtual table_base* operator()(const table_base& tb) { + TRACE("dl", tout << "\n";); + table_plugin & plugin = tb.get_plugin(); + table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); + SASSERT(rp); + table_signature sig; + sig.push_back(m_sort); + table_base* result = rp->mk_empty(sig); + equivalence_table const& eq_table = static_cast(tb); + if (eq_table.is_valid(m_val)) { + table_fact fact; + fact.resize(1); + unsigned r = m_val; + do { + fact[0] = r; + result->add_fact(fact); + r = eq_table.m_uf.next(r); + } + while (r != m_val); + } + TRACE("dl", tb.display(tout << "src:\n"); result->display(tout << "result\n");); + return result; + } + }; + + table_transformer_fn * equivalence_table_plugin::mk_select_equal_and_project_fn( + const table_base & t, const table_element & value, unsigned col) { + return alloc(select_equal_and_project_fn, t.get_signature(), value, col); + } + + class equivalence_table_plugin::union_fn : public table_union_fn { + + equivalence_table_plugin& m_plugin; + + + void mk_union1(equivalence_table & tgt, const equivalence_table & src, table_base * delta) { + unsigned num_vars = src.m_uf.get_num_vars(); + table_fact fact; + fact.resize(2); + for (unsigned i = 0; i < num_vars; ++i) { + if (src.is_valid(i) && src.m_uf.find(i) == i) { + fact[0] = i; + equivalence_table::class_iterator it = src.class_begin(i); + equivalence_table::class_iterator end = src.class_end(i); + for (; it != end; ++it) { + fact[1] = *it; + if (!tgt.contains_fact(fact)) { + tgt.add_fact(fact); + if (delta) { + delta->add_fact(fact); + } + } + } + } + } + } + + void mk_union2(equivalence_table & tgt, const table_base & src, table_base * delta) { + table_fact fact; + table_base::iterator it = src.begin(), end = src.end(); + for (; it != end; ++it) { + it->get_fact(fact); + if (!tgt.contains_fact(fact)) { + tgt.add_fact(fact); + if (delta) { + delta->add_fact(fact); + TRACE("dl", + tout << "Add: "; + for (unsigned i = 0; i < fact.size(); ++i) tout << fact[i] << " "; + tout << "\n";); + } + } + } + } + + public: + union_fn(equivalence_table_plugin& p) : m_plugin(p) {} + + virtual void operator()(table_base & tgt0, const table_base & src, table_base * delta) { + TRACE("dl", tout << "union\n";); + equivalence_table & tgt = static_cast(tgt0); + if (m_plugin.is_equivalence_table(src)) { + mk_union1(tgt, static_cast(src), delta); + } + else { + mk_union2(tgt, src, delta); + } + TRACE("dl", src.display(tout << "src\n"); tgt.display(tout << "tgt\n"); + if (delta) delta->display(tout << "delta\n");); + } + }; + + table_union_fn * equivalence_table_plugin::mk_union_fn( + const table_base & tgt, const table_base & src, const table_base * delta) { + if (!is_equivalence_table(tgt) || + tgt.get_signature() != src.get_signature() || + (delta && delta->get_signature() != tgt.get_signature())) { + return 0; + } + return alloc(union_fn,*this); + } + + class equivalence_table_plugin::join_project_fn : public convenient_table_join_project_fn { + equivalence_table_plugin& m_plugin; + public: + join_project_fn( + equivalence_table_plugin& plugin, const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), + m_plugin(plugin) { + m_removed_cols.push_back(UINT_MAX); + } + + virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { + SASSERT(m_cols1.size() == 1); + const table_signature & res_sign = get_result_signature(); + table_plugin * plugin = &tb1.get_plugin(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &tb2.get_plugin(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &tb1.get_manager().get_appropriate_plugin(res_sign); + } + } + SASSERT(plugin->can_handle_signature(res_sign)); + table_base * result = plugin->mk_empty(res_sign); + + if (m_plugin.is_equivalence_table(tb1)) { + mk_join(0, m_cols1[0], static_cast(tb1), + 2, m_cols2[0], tb2, result); + } + else if (m_plugin.is_equivalence_table(tb2)) { + mk_join(tb1.get_signature().size(), m_cols2[0], static_cast(tb2), + 0, m_cols1[0], tb1, result); + } + else { + UNREACHABLE(); + } + TRACE("dl", tb1.display(tout << "tb1\n"); tb2.display(tout << "tb2\n"); result->display(tout << "result\n");); + return result; + } + + private: + table_base * mk_join(unsigned offs1, unsigned col1, equivalence_table const & t1, + unsigned offs2, unsigned col2, table_base const& t2, table_base* res) { + table_base::iterator els2it = t2.begin(); + table_base::iterator els2end = t2.end(); + + table_fact acc, proj; + acc.resize(t1.get_signature().size() + t2.get_signature().size()); + + for(; els2it != els2end; ++els2it) { + const table_base::row_interface & row2 = *els2it; + table_element const& e2 = row2[col2]; + equivalence_table::class_iterator it = t1.class_begin(e2); + equivalence_table::class_iterator end = t1.class_end(e2); + if (it != end) { + for (unsigned i = 0; i < row2.size(); ++i) { + acc[i+offs2] = row2[i]; + } + } + for (; it != end; ++it) { + acc[offs1+col1] = e2; + acc[offs1+1-col1] = *it; + mk_project(acc, proj); + TRACE("dl", for (unsigned i = 0; i < proj.size(); ++i) tout << proj[i] << " "; tout << "\n";); + res->add_fact(proj); + } + } + return res; + } + + virtual void mk_project(table_fact const & f, table_fact & p) const { + unsigned sz = f.size(); + p.reset(); + for (unsigned i = 0, r = 0; i < sz; ++i) { + if (r < m_removed_cols.size() && m_removed_cols[r] == i) { + ++r; + } + else { + p.push_back(f[i]); + } + } + } + + + }; + + table_join_fn * equivalence_table_plugin::mk_join_project_fn( + const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + if (col_cnt != 1) { + TRACE("dl", tout << "WARNING: join_project on multiple columns is not implemented\n";); + return 0; + } + if (is_equivalence_table(t1) || is_equivalence_table(t2)) { + return alloc(join_project_fn, *this, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + } + return 0; + } + + class equivalence_table::eq_iterator : public iterator_core { + + equivalence_table const& m_eq; + unsigned m_last; + unsigned m_current; + unsigned m_next; + + class our_row : public caching_row_interface { + const eq_iterator& m_parent; + public: + our_row(const eq_iterator & p) : caching_row_interface(p.m_eq), m_parent(p) {} + + virtual void get_fact(table_fact& result) const { + if (result.size() < size()) { + result.resize(size(), 0); + } + result[0] = m_parent.m_current; + result[1] = m_parent.m_next; + } + + virtual table_element operator[](unsigned col) const { + if (col == 0) return m_parent.m_current; + if (col == 1) return m_parent.m_next; + UNREACHABLE(); + return 0; + } + + }; + our_row m_row_obj; + + public: + eq_iterator(const equivalence_table& eq, bool end): + m_eq(eq), + m_last(eq.m_uf.get_num_vars()), + m_current(end?m_last:0), + m_next(0), + m_row_obj(*this) + { + while (m_current < m_last && !m_eq.is_valid(m_current)) { + m_current++; + m_next = m_current; + } + } + + virtual bool is_finished() const { + return m_current == m_last; + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + + virtual void operator++() { + SASSERT(!is_finished()); + m_next = m_eq.m_uf.next(m_next); + if (m_next == m_current) { + do { + m_current++; + m_next = m_current; + } + while (m_current < m_last && !m_eq.is_valid(m_current)); + } + } + }; + + equivalence_table::equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig) + : table_base(plugin, sig), m_uf(m_ctx), m_sparse(0) { + SASSERT(plugin.can_handle_signature(sig)); + } + + equivalence_table::~equivalence_table() { + if (is_sparse()) { + m_sparse->deallocate(); + } + } + + + void equivalence_table::add_fact(const table_fact & f) { + if (is_sparse()) { + add_fact_sparse(f); + } + else { + TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); + while (first(f) >= m_uf.get_num_vars()) m_uf.mk_var(); + while (second(f) >= m_uf.get_num_vars()) m_uf.mk_var(); + m_uf.merge(first(f), second(f)); + m_valid.reserve(m_uf.get_num_vars()); + m_valid.set(first(f)); + m_valid.set(second(f)); + } + } + + void equivalence_table::remove_fact(const table_element* fact) { + mk_sparse(); + m_sparse->remove_fact(fact); + } + + void equivalence_table::mk_sparse() { + if (m_sparse) return; + + TRACE("dl",tout << "\n";); + table_plugin & plugin = get_plugin(); + table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); + SASSERT(rp); + table_base* result = rp->mk_empty(get_signature()); + table_base::iterator it = begin(), e = end(); + table_fact fact; + for (; it != e; ++it) { + it->get_fact(fact); + result->add_fact(fact); + } + m_sparse = result; + } + + void equivalence_table::add_fact_sparse(table_fact const& f) { + table_base::iterator it = m_sparse->begin(), end = m_sparse->end(); + vector to_add; + to_add.push_back(f); + table_fact f1(f); + + f1[0] = f[1]; + f1[1] = f[0]; + to_add.push_back(f1); + + f1[0] = f[1]; + f1[1] = f[1]; + to_add.push_back(f1); + + f1[0] = f[0]; + f1[1] = f[0]; + to_add.push_back(f1); + + for (; it != end; ++it) { + if ((*it)[0] == f[0]) { + f1[0] = f[1]; + f1[1] = (*it)[1]; + to_add.push_back(f1); + std::swap(f1[0],f1[1]); + to_add.push_back(f1); + } + } + for (unsigned i = 0; i < to_add.size(); ++i) { + m_sparse->add_fact(to_add[i]); + } + } + + bool equivalence_table::contains_fact(const table_fact & f) const { + TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); + if (is_sparse()) { + return m_sparse->contains_fact(f); + } + return + is_valid(first(f)) && + is_valid(second(f)) && + m_uf.find(first(f)) == m_uf.find(second(f)); + } + + table_base* equivalence_table::clone() const { + if (is_sparse()) { + return m_sparse->clone(); + } + TRACE("dl",tout << "\n";); + table_plugin & plugin = get_plugin(); + table_base* result = plugin.mk_empty(get_signature()); + table_fact fact; + fact.resize(2); + for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { + if (m_valid.get(i) && m_uf.find(i) == i) { + unsigned n = m_uf.next(i); + fact[0] = i; + while (n != i) { + fact[1] = n; + result->add_fact(fact); + n = m_uf.next(n); + } + } + } + return result; + } + + table_base::iterator equivalence_table::begin() const { + if (is_sparse()) return m_sparse->begin(); + return mk_iterator(alloc(eq_iterator, *this, false)); + } + + table_base::iterator equivalence_table::end() const { + if (is_sparse()) return m_sparse->end(); + return mk_iterator(alloc(eq_iterator, *this, true)); + } + + equivalence_table::class_iterator equivalence_table::class_begin(table_element const& _e) const { + SASSERT(!is_sparse()); + unsigned e = static_cast(_e); + return class_iterator(*this, e, !is_valid(e)); + } + + equivalence_table::class_iterator equivalence_table::class_end(table_element const& _e) const { + SASSERT(!is_sparse()); + unsigned e = static_cast(_e); + return class_iterator(*this, e, true); + } + + void equivalence_table::display(std::ostream& out) const { + if (is_sparse()) { + m_sparse->display(out); + return; + } + for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { + if (is_valid(i) && m_uf.find(i) == i) { + unsigned j = i, last = i; + do { + out << "<" << i << " " << j << ">\n"; + j = m_uf.next(j); + } + while (last != j); + } + } + } + + unsigned equivalence_table::get_size_estimate_rows() const { + if (is_sparse()) return m_sparse->get_size_estimate_rows(); + return static_cast(get_signature()[0]); + } + + unsigned equivalence_table::get_size_estimate_bytes() const { + if (is_sparse()) return m_sparse->get_size_estimate_bytes(); + return static_cast(get_signature()[0]); + } + + bool equivalence_table::knows_exact_size() const { + return (!is_sparse() || m_sparse->knows_exact_size()); + } + +}; + diff --git a/src/muz/rel/dl_table.h b/src/muz/rel/dl_table.h new file mode 100644 index 000000000..3a240c337 --- /dev/null +++ b/src/muz/rel/dl_table.h @@ -0,0 +1,265 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-01. + +Revision History: + +--*/ +#ifndef _DL_TABLE_H_ +#define _DL_TABLE_H_ + +#include +#include +#include + +#include "ast.h" +#include "bit_vector.h" +#include "buffer.h" +#include "hashtable.h" +#include "map.h" +#include "ref_vector.h" +#include "vector.h" +#include "union_find.h" +#include "dl_base.h" +#include "dl_util.h" +#include "bit_vector.h" + + +namespace datalog { + + class context; + class variable_intersection; + + + + // ----------------------------------- + // + // hashtable_table + // + // ----------------------------------- + + class hashtable_table; + + class hashtable_table_plugin : public table_plugin { + friend class hashtable_table; + protected: + class join_fn; + public: + typedef hashtable_table table; + + hashtable_table_plugin(relation_manager & manager) + : table_plugin(symbol("hashtable"), manager) {} + + virtual table_base * mk_empty(const table_signature & s); + + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + }; + + class hashtable_table : public table_base { + friend class hashtable_table_plugin; + friend class hashtable_table_plugin::join_fn; + + class our_iterator_core; + + typedef hashtable, + vector_eq_proc > storage; + + storage m_data; + + hashtable_table(hashtable_table_plugin & plugin, const table_signature & sig) + : table_base(plugin, sig) {} + public: + hashtable_table_plugin & get_plugin() const + { return static_cast(table_base::get_plugin()); } + + virtual void add_fact(const table_fact & f) { + m_data.insert(f); + } + virtual void remove_fact(const table_element* fact) { + table_fact f(get_signature().size(), fact); + m_data.remove(f); + } + virtual bool contains_fact(const table_fact & f) const { + return m_data.contains(f); + } + + virtual iterator begin() const; + virtual iterator end() const; + + virtual unsigned get_size_estimate_rows() const { return m_data.size(); } + virtual unsigned get_size_estimate_bytes() const { return m_data.size()*get_signature().size()*8; } + virtual bool knows_exact_size() const { return true; } + }; + + // ----------------------------------- + // + // bitvector_table + // + // ----------------------------------- + + class bitvector_table; + + class bitvector_table_plugin : public table_plugin { + public: + typedef bitvector_table table; + + bitvector_table_plugin(relation_manager & manager) + : table_plugin(symbol("bitvector"), manager) {} + + virtual bool can_handle_signature(const table_signature & s); + + virtual table_base * mk_empty(const table_signature & s); + }; + + class bitvector_table : public table_base { + friend class bitvector_table_plugin; + + class bv_iterator; + bit_vector m_bv; + unsigned m_num_cols; + unsigned_vector m_shift; + unsigned_vector m_mask; + + unsigned fact2offset(const table_element* f) const; + void offset2fact(unsigned offset, table_fact& f) const; + + bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig); + public: + virtual void add_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual bool contains_fact(const table_fact & f) const; + virtual iterator begin() const; + virtual iterator end() const; + }; + + // ------------------------------------------- + // Equivalence table. + // Really: partial equivalence relation table. + // ------------------------------------------- + + class equivalence_table; + + class equivalence_table_plugin : public table_plugin { + class union_fn; + class select_equal_and_project_fn; + class join_project_fn; + + bool is_equivalence_table(table_base const& tbl) const; + + public: + typedef equivalence_table table; + + equivalence_table_plugin(relation_manager & manager) + : table_plugin(symbol("equivalence"), manager) {} + + virtual bool can_handle_signature(const table_signature & s); + + virtual table_base * mk_empty(const table_signature & s); + + protected: + virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + virtual table_transformer_fn * mk_select_equal_and_project_fn( + const table_base & t, + const table_element & value, unsigned col); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + + +#if 0 + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + const table_element & value, unsigned col); + virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); +#endif + }; + + class equivalence_table : public table_base { + friend class equivalence_table_plugin; + + class eq_iterator; + union_find_default_ctx m_ctx; + bit_vector m_valid; + union_find<> m_uf; + table_base* m_sparse; + + equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig); + virtual ~equivalence_table(); + + unsigned first(table_fact const& f) const { return static_cast(f[0]); } + unsigned second(table_fact const& f) const { return static_cast(f[1]); } + + bool is_valid(unsigned entry) const { return entry < m_valid.size() && m_valid.get(entry); } + bool is_sparse() const { return m_sparse != 0; } + + // iterator over equivalence class of 'n'. + class class_iterator { + equivalence_table const& m_parent; + unsigned m_current; + unsigned m_last; + bool m_end; + public: + class_iterator(equivalence_table const& s, unsigned n, bool end): + m_parent(s), m_current(n), m_last(n), m_end(end) {} + + unsigned operator*() { return m_current; } + + class_iterator& operator++() { + m_current = m_parent.m_uf.next(m_current); + m_end = (m_current == m_last); + return *this; + } + + bool operator==(const class_iterator & it) const { + return + (m_end && it.m_end) || + (!m_end && !it.m_end && m_current == it.m_current); + } + bool operator!=(const class_iterator & it) const { return !operator==(it); } + + }; + class_iterator class_begin(table_element const& e) const; + class_iterator class_end(table_element const& e) const; + + void add_fact_sparse(table_fact const& f); + void mk_sparse(); + + + public: + virtual void add_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual bool contains_fact(const table_fact & f) const; + virtual table_base* clone() const; + virtual iterator begin() const; + virtual iterator end() const; + virtual unsigned get_size_estimate_rows() const; + virtual unsigned get_size_estimate_bytes() const; + virtual bool knows_exact_size() const; + virtual void display(std::ostream & out) const; + + }; + + +}; + +#endif /* _DL_TABLE_H_ */ + diff --git a/src/muz/rel/dl_table_plugin.h b/src/muz/rel/dl_table_plugin.h new file mode 100644 index 000000000..134389b61 --- /dev/null +++ b/src/muz/rel/dl_table_plugin.h @@ -0,0 +1,193 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table_plugin.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-23. + +Revision History: + +--*/ +#ifndef _DL_TABLE_PLUGIN_H_ +#define _DL_TABLE_PLUGIN_H_ + +#include"ast.h" +#include"map.h" +#include"vector.h" + +#include"dl_table_ops.h" + +namespace datalog { + + /** + Termplate class containing common infrastructure for relations and tables + */ + template + struct tr_infrastructure { + + typedef typename Traits::base_object base_object; + typedef typename Traits::signature signature; + typedef typename Traits::element element; + typedef typename Traits::fact fact; + typedef typename Traits::kind kind; + + class base_fn { + public: + virtual ~base_fn() {} + }; + + class join_fn : public base_fn { + public: + virtual base_object * operator()(const base_object & t1, const base_object & t2); + }; + + class transformer_fn : public base_fn { + public: + virtual base_object * operator()(const base_object & t); + }; + + class union_fn : public base_fn { + public: + virtual void operator()(base_object & tgt, const base_object & src, base_object * delta); + }; + + class mutator_fn : public base_fn { + public: + virtual void operator()(base_object & t); + }; + + class negation_filter_fn : public base_fn { + public: + virtual void operator()(base_object & t, const base_object & negated_obj); + }; + + class plugin_object { + const kind m_kind; + protected: + plugin_object(kind k) : m_kind(k) {} + public: + kind get_kind(); + + virtual base_object * mk_empty(const signature & s) = 0; + + virtual join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + NOT_IMPLEMENTED_YET(); + } + + virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, + const unsigned * removed_cols) = 0 + + virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) = 0; + + virtual union_fn * mk_union_fn(base_object & tgt, const base_object & src, base_object * delta) = 0; + + virtual mutator_fn * mk_filter_identical_fn(base_object & t, unsigned col_cnt, + const unsigned * identical_cols) = 0; + + virtual mutator_fn * mk_filter_equal_fn(base_object & t, const element & value, + unsigned col) = 0; + + virtual mutator_fn * mk_filter_interpreted_fn(base_object & t, app * condition) = 0; + + virtual negation_filter_fn * mk_filter_interpreted_fn(base_object & t, + const base_object & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) = 0; + + }; + + class base_ancestor { + const kind m_kind; + protected: + relation_manager & m_manager; + signature m_signature; + + base_ancestor(kind k, relation_manager & m, const signature & s) + : m_kind(k), m_manager(m), m_signature(s) {} + public: + virtual ~base_ancestor() {} + + kind get_kind() const { return m_kind; } + relation_manager & get_manager() const { return m_manager; } + const signature & get_signature() const { return m_signature; } + + virtual bool empty() const = 0; + virtual void add_fact(const fact & f) = 0; + virtual bool contains_fact(const fact & f) const = 0; + + /** + \brief Return table that contains the same data as the current one. + */ + virtual base_object * clone() const; + + }; + }; + + + // ----------------------------------- + // + // relation_base + // + // ----------------------------------- + + class relation_base1; + + enum relation_kind { + RK_UNKNOWN, + RK_TABLE + }; + + struct relation_traits { + typedef relation_base1 base_object; + typedef relation_signature signature; + typedef app * element; + typedef ptr_vector fact; + typedef relation_kind kind; + }; + + typedef tr_infrastructure relation_infrastructure; + + typedef relation_infrastructure::plugin_object relation_plugin_base; + + class relation_base1 : public relation_infrastructure::base_ancestor { + + }; + + + // ----------------------------------- + // + // table_base + // + // ----------------------------------- + + class table_base1; + + struct table_traits { + typedef table_base1 base_object; + typedef table_signature signature; + typedef unsigned element; + typedef unsigned_vector fact; + typedef table_kind kind; + }; + + typedef tr_infrastructure table_infrastructure; + + typedef table_infrastructure::plugin_object table_plugin_base; + + class table_base1 : public table_infrastructure::base_ancestor { + + }; + +}; + +#endif /* _DL_TABLE_PLUGIN_H_ */ + diff --git a/src/muz/rel/dl_table_relation.cpp b/src/muz/rel/dl_table_relation.cpp new file mode 100644 index 000000000..3c30c58bb --- /dev/null +++ b/src/muz/rel/dl_table_relation.cpp @@ -0,0 +1,490 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table_relation.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include +#include"dl_context.h" +#include"dl_relation_manager.h" +#include"dl_table_relation.h" + + +namespace datalog { + + // ----------------------------------- + // + // table_relation_plugin + // + // ----------------------------------- + + symbol table_relation_plugin::create_plugin_name(const table_plugin &p) { + std::string name = std::string("tr_") + p.get_name().bare_str(); + return symbol(name.c_str()); + } + + bool table_relation_plugin::can_handle_signature(const relation_signature & s) { + table_signature tsig; + if(!get_manager().relation_signature_to_table(s, tsig)) { + return false; + } + return m_table_plugin.can_handle_signature(tsig); + } + + + relation_base * table_relation_plugin::mk_empty(const relation_signature & s) { + table_signature tsig; + if(!get_manager().relation_signature_to_table(s, tsig)) { + return 0; + } + table_base * t = m_table_plugin.mk_empty(tsig); + return alloc(table_relation, *this, s, t); + } + + relation_base * table_relation_plugin::mk_full(const relation_signature & s, func_decl* p, family_id kind) { + table_signature tsig; + if(!get_manager().relation_signature_to_table(s, tsig)) { + return 0; + } + table_base * t = m_table_plugin.mk_full(p, tsig, kind); + return alloc(table_relation, *this, s, t); + } + + relation_base * table_relation_plugin::mk_from_table(const relation_signature & s, table_base * t) { + if (&t->get_plugin() == &m_table_plugin) + return alloc(table_relation, *this, s, t); + table_relation_plugin& other = t->get_manager().get_table_relation_plugin(t->get_plugin()); + return alloc(table_relation, other, s, t); + } + + class table_relation_plugin::tr_join_project_fn : public convenient_relation_join_project_fn { + scoped_ptr m_tfun; + public: + tr_join_project_fn(const relation_signature & s1, const relation_signature & s2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols, table_join_fn * tfun) + : convenient_relation_join_project_fn(s1, s2, col_cnt, cols1, cols2, removed_col_cnt, + removed_cols), m_tfun(tfun) {} + + virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { + SASSERT(t1.from_table()); + SASSERT(t2.from_table()); + table_relation_plugin & plugin = static_cast(t1.get_plugin()); + + const table_relation & tr1 = static_cast(t1); + const table_relation & tr2 = static_cast(t2); + + table_base * tres = (*m_tfun)(tr1.get_table(), tr2.get_table()); + + TRACE("dl_table_relation", tout << "# join => "; tres->display(tout);); + if(&tres->get_plugin()!=&plugin.m_table_plugin) { + //Operation returned a table of different type than the one which is associated with + //this plugin. We need to get a correct table_relation_plugin and create the relation + //using it. + return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) + .mk_from_table(get_result_signature(), tres); + } + return plugin.mk_from_table(get_result_signature(), tres); + } + }; + + relation_join_fn * table_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if(!r1.from_table() || !r2.from_table()) { + return 0; + } + const table_relation & tr1 = static_cast(r1); + const table_relation & tr2 = static_cast(r2); + + table_join_fn * tfun = get_manager().mk_join_fn(tr1.get_table(), tr2.get_table(), col_cnt, cols1, cols2); + if(!tfun) { + return 0; + } + + return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, + cols2, 0, static_cast(0), tfun); + } + + relation_join_fn * table_relation_plugin::mk_join_project_fn(const relation_base & r1, + const relation_base & r2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + if(!r1.from_table() || !r2.from_table()) { + return 0; + } + const table_relation & tr1 = static_cast(r1); + const table_relation & tr2 = static_cast(r2); + + table_join_fn * tfun = get_manager().mk_join_project_fn(tr1.get_table(), tr2.get_table(), joined_col_cnt, + cols1, cols2, removed_col_cnt, removed_cols); + SASSERT(tfun); + + return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), joined_col_cnt, cols1, + cols2, removed_col_cnt, removed_cols, tfun); + } + + + class table_relation_plugin::tr_transformer_fn : public convenient_relation_transformer_fn { + scoped_ptr m_tfun; + public: + tr_transformer_fn(const relation_signature & rsig, table_transformer_fn * tfun) + : m_tfun(tfun) { get_result_signature() = rsig; } + + virtual relation_base * operator()(const relation_base & t) { + SASSERT(t.from_table()); + table_relation_plugin & plugin = static_cast(t.get_plugin()); + + const table_relation & tr = static_cast(t); + + table_base * tres = (*m_tfun)(tr.get_table()); + + TRACE("dl_table_relation", tout << "# transform => "; tres->display(tout);); + if(&tres->get_plugin()!=&plugin.m_table_plugin) { + //Transformation returned a table of different type than the one which is associated with this plugin. + //We need to get a correct table_relation_plugin and create the relation using it. + return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) + .mk_from_table(get_result_signature(), tres); + } + return plugin.mk_from_table(get_result_signature(), tres); + } + }; + + relation_transformer_fn * table_relation_plugin::mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_transformer_fn * tfun = get_manager().mk_project_fn(tr.get_table(), col_cnt, removed_cols); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_project(t.get_signature(), col_cnt, removed_cols, sig); + + return alloc(tr_transformer_fn, sig, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_transformer_fn * tfun = get_manager().mk_rename_fn(tr.get_table(), permutation_cycle_len, permutation_cycle); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_rename(t.get_signature(), permutation_cycle_len, permutation_cycle, sig); + + return alloc(tr_transformer_fn, sig, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_transformer_fn * tfun = get_manager().mk_permutation_rename_fn(tr.get_table(), permutation); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_permutation_rename(t.get_signature(), permutation, sig); + + return alloc(tr_transformer_fn, sig, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_element tvalue; + get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); + + table_transformer_fn * tfun = get_manager().mk_select_equal_and_project_fn(tr.get_table(), tvalue, col); + SASSERT(tfun); + relation_signature res_sig; + relation_signature::from_project(t.get_signature(), 1, &col, res_sig); + return alloc(tr_transformer_fn, res_sig, tfun); + } + + /** + Union functor that can unite table relation into any other relation (using any delta relation) + by iterating through the table and calling \c add_fact of the target relation. + */ + class table_relation_plugin::universal_target_union_fn : public relation_union_fn { + virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + SASSERT(src.from_table()); + + const table_relation & tr_src = static_cast(src); + relation_manager & rmgr = tr_src.get_manager(); + relation_signature sig = tr_src.get_signature(); + SASSERT(tgt.get_signature()==sig); + SASSERT(!delta || delta->get_signature()==sig); + + table_base::iterator it = tr_src.get_table().begin(); + table_base::iterator end = tr_src.get_table().end(); + + table_fact tfact; + relation_fact rfact(rmgr.get_context()); + for (; it != end; ++it) { + it->get_fact(tfact); + rmgr.table_fact_to_relation(sig, tfact, rfact); + if(delta) { + if(!tgt.contains_fact(rfact)) { + tgt.add_new_fact(rfact); + delta->add_fact(rfact); + } + } + else { + tgt.add_fact(rfact); + } + } + TRACE("dl_table_relation", tout << "# universal union => "; tgt.display(tout);); + } + }; + + class table_relation_plugin::tr_union_fn : public relation_union_fn { + scoped_ptr m_tfun; + public: + tr_union_fn(table_union_fn * tfun) : m_tfun(tfun) {} + + virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + SASSERT(tgt.from_table()); + SASSERT(src.from_table()); + SASSERT(!delta || delta->from_table()); + + table_relation & tr_tgt = static_cast(tgt); + const table_relation & tr_src = static_cast(src); + table_relation * tr_delta = static_cast(delta); + + (*m_tfun)(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : 0); + + TRACE("dl_table_relation", tout << "# union => "; tr_tgt.get_table().display(tout);); + } + }; + + relation_union_fn * table_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if(!src.from_table()) { + return 0; + } + if(!tgt.from_table() || (delta && !delta->from_table())) { + return alloc(universal_target_union_fn); + } + const table_relation & tr_tgt = static_cast(tgt); + const table_relation & tr_src = static_cast(src); + const table_relation * tr_delta = static_cast(delta); + + table_union_fn * tfun = get_manager().mk_union_fn(tr_tgt.get_table(), tr_src.get_table(), + tr_delta ? &tr_delta->get_table() : 0); + SASSERT(tfun); + + return alloc(tr_union_fn, tfun); + } + + + class table_relation_plugin::tr_mutator_fn : public relation_mutator_fn { + scoped_ptr m_tfun; + public: + tr_mutator_fn(table_mutator_fn * tfun) : m_tfun(tfun) {} + + virtual void operator()(relation_base & r) { + SASSERT(r.from_table()); + table_relation & tr = static_cast(r); + (*m_tfun)(tr.get_table()); + TRACE("dl_table_relation", tout << "# mutator => "; tr.get_table().display(tout);); + } + }; + + relation_mutator_fn * table_relation_plugin::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_mutator_fn * tfun = get_manager().mk_filter_identical_fn(tr.get_table(), col_cnt, identical_cols); + SASSERT(tfun); + return alloc(tr_mutator_fn, tfun); + } + + relation_mutator_fn * table_relation_plugin::mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_element tvalue; + get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); + + table_mutator_fn * tfun = get_manager().mk_filter_equal_fn(tr.get_table(), tvalue, col); + SASSERT(tfun); + return alloc(tr_mutator_fn, tfun); + } + + relation_mutator_fn * table_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + bool condition_needs_transforming = false; + if(!t.from_table() || condition_needs_transforming) { + return 0; + } + const table_relation & tr = static_cast(t); + table_mutator_fn * tfun = get_manager().mk_filter_interpreted_fn(tr.get_table(), condition); + SASSERT(tfun); + return alloc(tr_mutator_fn, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (!t.from_table()) + return 0; + + const table_relation & tr = static_cast(t); + table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), + condition, removed_col_cnt, removed_cols); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_project(t.get_signature(), removed_col_cnt, removed_cols, sig); + return alloc(tr_transformer_fn, sig, tfun); + } + + class table_relation_plugin::tr_intersection_filter_fn : public relation_intersection_filter_fn { + scoped_ptr m_tfun; + public: + tr_intersection_filter_fn(table_intersection_filter_fn * tfun) : m_tfun(tfun) {} + + virtual void operator()(relation_base & r, const relation_base & src) { + SASSERT(r.from_table()); + SASSERT(src.from_table()); + + table_relation & tr = static_cast(r); + const table_relation & tr_src = static_cast(src); + + (*m_tfun)(tr.get_table(), tr_src.get_table()); + TRACE("dl_table_relation", tout << "# negation_filter => "; tr.get_table().display(tout);); + } + }; + + relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_intersection_fn(const relation_base & r, + const relation_base & src, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * src_cols) { + if(!r.from_table() || !src.from_table()) { + return 0; + } + const table_relation & tr = static_cast(r); + const table_relation & tr_neg = static_cast(src); + table_intersection_filter_fn * tfun = get_manager().mk_filter_by_intersection_fn(tr.get_table(), + tr_neg.get_table(), joined_col_cnt, r_cols, src_cols); + if(!tfun) { + return 0; + } + + return alloc(tr_intersection_filter_fn, tfun); + } + + + relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, + const relation_base & negated_rel, unsigned joined_col_cnt, + const unsigned * r_cols, const unsigned * negated_cols) { + if(!r.from_table() || !negated_rel.from_table()) { + return 0; + } + const table_relation & tr = static_cast(r); + const table_relation & tr_neg = static_cast(negated_rel); + table_intersection_filter_fn * tfun = get_manager().mk_filter_by_negation_fn(tr.get_table(), + tr_neg.get_table(), joined_col_cnt, r_cols, negated_cols); + SASSERT(tfun); + + return alloc(tr_intersection_filter_fn, tfun); + } + + + // ----------------------------------- + // + // table_relation + // + // ----------------------------------- + + void table_relation::add_table_fact(const table_fact & f) { + get_table().add_fact(f); + } + + void table_relation::add_fact(const relation_fact & f) { + SASSERT(f.size()==get_signature().size()); + table_fact vals; + get_manager().relation_fact_to_table(get_signature(), f, vals); + get_table().add_fact(vals); + TRACE("dl_table_relation", tout << "# add fact => "; get_table().display(tout);); + } + + bool table_relation::contains_fact(const relation_fact & f) const { + table_fact vals; + get_manager().relation_fact_to_table(get_signature(), f, vals); + return get_table().contains_fact(vals); + } + + relation_base * table_relation::clone() const { + table_base * tres = get_table().clone(); + return get_plugin().mk_from_table(get_signature(), tres); + } + + relation_base * table_relation::complement(func_decl* p) const { + table_base * tres = get_table().complement(p); + return get_plugin().mk_from_table(get_signature(), tres); + } + + void table_relation::display_tuples(func_decl & pred, std::ostream & out) const { + context & ctx = get_manager().get_context(); + unsigned arity = pred.get_arity(); + + out << "Tuples in " << pred.get_name() << ": \n"; + + table_base::iterator it = get_table().begin(); + table_base::iterator end = get_table().end(); + + table_fact fact; + for (; it != end; ++it) { + it->get_fact(fact); + + out << "\t("; + + for(unsigned i=0;i + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ +#ifndef _DL_TABLE_RELATION_H_ +#define _DL_TABLE_RELATION_H_ + + +#include "dl_base.h" +#include "dl_util.h" + +namespace datalog { + + class table_relation; + + class table_relation_plugin : public relation_plugin { + friend class table_relation; + + class tr_join_project_fn; + class tr_transformer_fn; + class universal_target_union_fn; + class tr_union_fn; + class tr_mutator_fn; + class tr_intersection_filter_fn; + + table_plugin & m_table_plugin; + + static symbol create_plugin_name(const table_plugin & p); + public: + table_relation_plugin(table_plugin & tp, relation_manager & manager) + : relation_plugin(create_plugin_name(tp), manager, ST_TABLE_RELATION), m_table_plugin(tp) {} + + table_plugin & get_table_plugin() { return m_table_plugin; } + + virtual bool can_handle_signature(const relation_signature & s); + + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(const relation_signature & s, func_decl* p, family_id kind); + relation_base * mk_from_table(const relation_signature & s, table_base * t); + + protected: + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); + virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + virtual relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col); + }; + + class table_relation : public relation_base { + friend class table_relation_plugin; + friend class table_relation_plugin::tr_join_project_fn; + friend class table_relation_plugin::tr_transformer_fn; + + scoped_rel m_table; + + /** + \brief Create a \c table_relation object. + + The newly created object takes ownership of the \c table object. + */ + table_relation(table_relation_plugin & p, const relation_signature & s, table_base * table) + : relation_base(p, s), m_table(table) { + SASSERT(s.size()==table->get_signature().size()); + } + public: + + table_relation_plugin & get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + table_base & get_table() { return *m_table; } + const table_base & get_table() const { return *m_table; } + + virtual bool empty() const { return m_table->empty(); } + + void add_table_fact(const table_fact & f); + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual relation_base * clone() const; + virtual relation_base * complement(func_decl* p) const; + virtual void to_formula(expr_ref& fml) const { get_table().to_formula(get_signature(), fml); } + + virtual void display(std::ostream & out) const { + get_table().display(out); + } + virtual void display_tuples(func_decl & pred, std::ostream & out) const; + + virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } + virtual bool knows_exact_size() const { return m_table->knows_exact_size(); } + }; + +}; + +#endif /* _DL_TABLE_RELATION_H_ */ + diff --git a/src/muz/rel/dl_vector_relation.h b/src/muz/rel/dl_vector_relation.h new file mode 100644 index 000000000..114f4ca43 --- /dev/null +++ b/src/muz/rel/dl_vector_relation.h @@ -0,0 +1,407 @@ +/*++ +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_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 + diff --git a/src/muz/rel/karr_relation.cpp b/src/muz/rel/karr_relation.cpp new file mode 100644 index 000000000..436cd8598 --- /dev/null +++ b/src/muz/rel/karr_relation.cpp @@ -0,0 +1,790 @@ +#include "karr_relation.h" +#include "bool_rewriter.h" + +namespace datalog { + class karr_relation : public relation_base { + friend class karr_relation_plugin; + friend class karr_relation_plugin::filter_equal_fn; + + karr_relation_plugin& m_plugin; + ast_manager& m; + mutable arith_util a; + func_decl_ref m_fn; + mutable bool m_empty; + mutable matrix m_ineqs; + mutable bool m_ineqs_valid; + mutable matrix m_basis; + mutable bool m_basis_valid; + + public: + karr_relation(karr_relation_plugin& p, func_decl* f, relation_signature const& s, bool is_empty): + relation_base(p, s), + m_plugin(p), + m(p.get_ast_manager()), + a(m), + m_fn(f, m), + m_empty(is_empty), + m_ineqs_valid(!is_empty), + m_basis_valid(false) + { + } + + virtual bool empty() const { + return m_empty; + } + + virtual bool is_precise() const { return false; } + + virtual void add_fact(const relation_fact & f) { + SASSERT(m_empty); + SASSERT(!m_basis_valid); + m_empty = false; + m_ineqs_valid = true; + for (unsigned i = 0; i < f.size(); ++i) { + rational n; + if (a.is_numeral(f[i], n) && n.is_int()) { + vector row; + row.resize(f.size()); + row[i] = rational(1); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(-n); + m_ineqs.eq.push_back(true); + } + } + } + + virtual bool contains_fact(const relation_fact & f) const { + UNREACHABLE(); + return false; + } + + virtual void display(std::ostream & out) const { + if (m_fn) { + out << m_fn->get_name() << "\n"; + } + if (empty()) { + out << "empty\n"; + } + else { + if (m_ineqs_valid) { + m_ineqs.display(out << "ineqs:\n"); + } + if (m_basis_valid) { + m_basis.display(out << "basis:\n"); + } + } + } + + virtual karr_relation * clone() const { + karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty); + result->copy(*this); + return result; + } + + virtual karr_relation * complement(func_decl*) const { + UNREACHABLE(); + return 0; + } + + virtual void to_formula(expr_ref& fml) const { + if (empty()) { + fml = m.mk_false(); + } + else { + matrix const& M = get_ineqs(); + expr_ref_vector conj(m); + for (unsigned i = 0; i < M.size(); ++i) { + to_formula(M.A[i], M.b[i], M.eq[i], conj); + } + bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), fml); + } + } + + karr_relation_plugin& get_plugin() const { return m_plugin; } + + void filter_interpreted(app* cond) { + rational one(1), mone(-1); + expr* e1, *e2, *en; + var* v, *w; + rational n1, n2; + expr_ref_vector conjs(m); + qe::flatten_and(cond, conjs); + matrix& M = get_ineqs(); + unsigned num_columns = get_signature().size(); + + for (unsigned i = 0; i < conjs.size(); ++i) { + expr* e = conjs[i].get(); + rational b(0); + vector row; + row.resize(num_columns, rational(0)); + bool processed = true; + if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(true); + } + else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(false); + } + else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b - rational(1)); + M.eq.push_back(false); + } + else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(false); + } + else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b - rational(1)); + M.eq.push_back(false); + } + else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) { + if (n1 > n2) { + std::swap(n1, n2); + } + SASSERT(n1 <= n2); + row[v->get_idx()] = rational(1); + // v - n1 >= 0 + M.A.push_back(row); + M.b.push_back(-n1); + M.eq.push_back(false); + // -v + n2 >= 0 + row[v->get_idx()] = rational(-1); + M.A.push_back(row); + M.b.push_back(n2); + M.eq.push_back(false); + } + else { + processed = false; + } + TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n"; + if (processed) matrix::display_ineq(tout, row, M.b.back(), M.eq.back()); + ); + } + TRACE("dl", display(tout);); + } + + void mk_join(karr_relation const& r1, karr_relation const& r2, + unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) { + if (r1.empty() || r2.empty()) { + m_empty = true; + return; + } + matrix const& M1 = r1.get_ineqs(); + matrix const& M2 = r2.get_ineqs(); + unsigned sig1_size = r1.get_signature().size(); + unsigned sig_size = get_signature().size(); + m_ineqs.reset(); + for (unsigned i = 0; i < M1.size(); ++i) { + vector row; + row.append(M1.A[i]); + row.resize(sig_size); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(M1.b[i]); + m_ineqs.eq.push_back(M1.eq[i]); + } + for (unsigned i = 0; i < M2.size(); ++i) { + vector row; + row.resize(sig_size); + for (unsigned j = 0; j < M2.A[i].size(); ++j) { + row[sig1_size + j] = M2.A[i][j]; + } + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(M2.b[i]); + m_ineqs.eq.push_back(M2.eq[i]); + } + for (unsigned i = 0; i < col_cnt; ++i) { + vector row; + row.resize(sig_size); + row[cols1[i]] = rational(1); + row[sig1_size + cols2[i]] = rational(-1); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(rational(0)); + m_ineqs.eq.push_back(true); + } + m_ineqs_valid = true; + m_basis_valid = false; + m_empty = false; + if (r1.m_fn) { + m_fn = r1.m_fn; + } + if (r2.m_fn) { + m_fn = r2.m_fn; + } + } + + void mk_project(karr_relation const& r, unsigned cnt, unsigned const* cols) { + if (r.m_empty) { + m_empty = true; + return; + } + matrix const& M = r.get_basis(); + m_basis.reset(); + for (unsigned i = 0; i < M.size(); ++i) { + vector row; + unsigned k = 0; + for (unsigned j = 0; j < M.A[i].size(); ++j) { + if (k < cnt && j == cols[k]) { + ++k; + } + else { + row.push_back(M.A[i][j]); + } + } + SASSERT(row.size() + cnt == M.A[i].size()); + SASSERT(M.eq[i]); + m_basis.A.push_back(row); + m_basis.b.push_back(M.b[i]); + m_basis.eq.push_back(true); + } + m_basis_valid = true; + m_ineqs_valid = false; + m_empty = false; + m_fn = r.m_fn; + + TRACE("dl", + for (unsigned i = 0; i < cnt; ++i) { + tout << cols[i] << " "; + } + tout << "\n"; + r.display(tout); + display(tout);); + } + + void mk_rename(const karr_relation & r, unsigned col_cnt, const unsigned * cols) { + if (r.empty()) { + m_empty = true; + return; + } + m_ineqs.reset(); + m_basis.reset(); + m_ineqs_valid = r.m_ineqs_valid; + m_basis_valid = r.m_basis_valid; + if (m_ineqs_valid) { + m_ineqs.append(r.m_ineqs); + mk_rename(m_ineqs, col_cnt, cols); + } + if (m_basis_valid) { + m_basis.append(r.m_basis); + mk_rename(m_basis, col_cnt, cols); + } + m_fn = r.m_fn; + TRACE("dl", r.display(tout); display(tout);); + } + + void mk_union(karr_relation const& src, karr_relation* delta) { + if (src.empty()) { + if (delta) { + delta->m_empty = true; + } + return; + } + matrix const& M = src.get_basis(); + if (empty()) { + m_basis = M; + m_basis_valid = true; + m_empty = false; + m_ineqs_valid = false; + if (delta) { + delta->copy(*this); + } + return; + } + matrix& N = get_basis(); + unsigned N_size = N.size(); + for (unsigned i = 0; i < M.size(); ++i) { + bool found = false; + for (unsigned j = 0; !found && j < N_size; ++j) { + found = + same_row(M.A[i], N.A[j]) && + M.b[i] == N.b[j] && + M.eq[i] == N.eq[j]; + } + if (!found) { + N.A.push_back(M.A[i]); + N.b.push_back(M.b[i]); + N.eq.push_back(M.eq[i]); + } + } + m_ineqs_valid = false; + if (N_size != N.size()) { + if (delta) { + delta->copy(*this); + } + } + } + + matrix const& get_basis() const { + init_basis(); + return m_basis; + } + + matrix& get_basis() { + init_basis(); + return m_basis; + } + + matrix const& get_ineqs() const { + init_ineqs(); + return m_ineqs; + } + + matrix & get_ineqs() { + init_ineqs(); + return m_ineqs; + } + + private: + + void copy(karr_relation const& other) { + m_ineqs = other.m_ineqs; + m_basis = other.m_basis; + m_basis_valid = other.m_basis_valid; + m_ineqs_valid = other.m_ineqs_valid; + m_empty = other.m_empty; + } + + bool same_row(vector const& r1, vector const& r2) const { + SASSERT(r1.size() == r2.size()); + for (unsigned i = 0; i < r1.size(); ++i) { + if (r1[i] != r2[i]) { + return false; + } + } + return true; + } + + void mk_rename(matrix& M, unsigned col_cnt, unsigned const* cols) { + for (unsigned j = 0; j < M.size(); ++j) { + vector & row = M.A[j]; + rational tmp = row[cols[0]]; + for (unsigned i = 0; i + 1 < col_cnt; ++i) { + row[cols[i]] = row[cols[i+1]]; + } + row[cols[col_cnt-1]] = tmp; + } + } + + bool is_eq(expr* e, var*& v, rational& n) { + expr* e1, *e2; + if (!m.is_eq(e, e1, e2)) { + return false; + } + if (!is_var(e1)) { + std::swap(e1, e2); + } + if (!is_var(e1)) { + return false; + } + v = to_var(e1); + if (!a.is_numeral(e2, n)) { + return false; + } + return true; + } + + bool is_linear(expr* e, vector& row, rational& b, rational const& mul) { + if (!a.is_int(e)) { + return false; + } + if (is_var(e)) { + row[to_var(e)->get_idx()] += mul; + return true; + } + if (!is_app(e)) { + return false; + } + rational n; + if (a.is_numeral(e, n)) { + b += mul*n; + return true; + } + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) { + return false; + } + } + return true; + } + expr* e1, *e2; + if (a.is_sub(e, e1, e2)) { + return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul); + } + if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) { + return is_linear(e2, row, b, mul*n); + } + if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) { + return is_linear(e1, row, b, mul*n); + } + if (a.is_uminus(e, e1)) { + return is_linear(e1, row, b, -mul); + } + return false; + } + + void init_ineqs() const { + if (!m_ineqs_valid) { + SASSERT(m_basis_valid); + m_plugin.dualizeH(m_ineqs, m_basis); + m_ineqs_valid = true; + } + } + + void init_basis() const { + if (!m_basis_valid) { + SASSERT(m_ineqs_valid); + if (m_plugin.dualizeI(m_basis, m_ineqs)) { + m_basis_valid = true; + } + else { + m_empty = true; + } + } + } + + void to_formula(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) const { + expr_ref_vector sum(m); + expr_ref zero(m), lhs(m); + zero = a.mk_numeral(rational(0), true); + + for (unsigned i = 0; i < row.size(); ++i) { + if (row[i].is_zero()) { + continue; + } + var* var = m.mk_var(i, a.mk_int()); + if (row[i].is_one()) { + sum.push_back(var); + } + else { + sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); + } + } + if (!b.is_zero()) { + sum.push_back(a.mk_numeral(b, true)); + } + lhs = a.mk_add(sum.size(), sum.c_ptr()); + if (is_eq) { + conj.push_back(m.mk_eq(lhs, zero)); + } + else { + conj.push_back(a.mk_ge(lhs, zero)); + } + } + }; + + + karr_relation& karr_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + karr_relation const & karr_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + void karr_relation_plugin::set_cancel(bool f) { + m_hb.set_cancel(f); + } + + relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(karr_relation, *this, 0, s, true); + } + + relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + return alloc(karr_relation, *this, p, s, false); + } + + class karr_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + karr_relation const& r1 = get(_r1); + karr_relation const& r2 = get(_r2); + karr_relation_plugin& p = r1.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + return result; + } + }; + + relation_join_fn * karr_relation_plugin::mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + + + class karr_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { + } + + virtual relation_base * operator()(const relation_base & _r) { + karr_relation const& r = get(_r); + karr_relation_plugin& p = r.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); + return result; + } + }; + + relation_transformer_fn * karr_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + class karr_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {} + + virtual relation_base * operator()(const relation_base & _r) { + karr_relation const& r = get(_r); + karr_relation_plugin& p = r.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); + return result; + } + }; + + relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if (!check_kind(r)) { + return 0; + } + return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); + } + + bool karr_relation_plugin::dualizeI(matrix& dst, matrix const& src) { + dst.reset(); + m_hb.reset(); + for (unsigned i = 0; i < src.size(); ++i) { + if (src.eq[i]) { + m_hb.add_eq(src.A[i], -src.b[i]); + } + else { + m_hb.add_ge(src.A[i], -src.b[i]); + } + } + for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) { + m_hb.set_is_int(i); + } + lbool is_sat = l_undef; + + try { + is_sat = m_hb.saturate(); + } + catch (...) { + is_sat = l_undef; + } + TRACE("dl_verbose", m_hb.display(tout);); + if (is_sat == l_false) { + return false; + } + if (is_sat == l_undef) { + return true; + } + unsigned basis_size = m_hb.get_basis_size(); + bool first_initial = true; + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + m_hb.get_basis_solution(i, soln, is_initial); + if (is_initial && first_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(1)); + dst.eq.push_back(true); + first_initial = false; + } + else if (!is_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(0)); + dst.eq.push_back(true); + } + } + return true; + } + + void karr_relation_plugin::dualizeH(matrix& dst, matrix const& src) { + dst.reset(); + if (src.size() == 0) { + return; + } + m_hb.reset(); + for (unsigned i = 0; i < src.size(); ++i) { + vector v(src.A[i]); + v.push_back(src.b[i]); + if (src.eq[i]) { + m_hb.add_eq(v, rational(0)); + } + else { + m_hb.add_ge(v, rational(0)); + } + } + for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { + m_hb.set_is_int(i); + } + lbool is_sat = l_undef; + try { + is_sat = m_hb.saturate(); + } + catch (...) { + is_sat = l_undef; + } + if (is_sat != l_true) { + return; + } + TRACE("dl_verbose", m_hb.display(tout);); + SASSERT(is_sat == l_true); + unsigned basis_size = m_hb.get_basis_size(); + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + m_hb.get_basis_solution(i, soln, is_initial); + if (!is_initial) { + dst.b.push_back(soln.back()); + dst.eq.push_back(true); + soln.pop_back(); + dst.A.push_back(soln); + } + } + } + + + class karr_relation_plugin::union_fn : public relation_union_fn { + public: + union_fn() {} + + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + + karr_relation& r = get(_r); + karr_relation const& src = get(_src); + TRACE("dl", r.display(tout << "dst:\n"); src.display(tout << "src:\n");); + + if (_delta) { + karr_relation& d = get(*_delta); + r.mk_union(src, &d); + } + else { + r.mk_union(src, 0); + } + TRACE("dl", r.display(tout << "result:\n");); + } + }; + + relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn); + } + + class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_identical_cols; + public: + filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_identical_cols(col_cnt, identical_cols) {} + + virtual void operator()(relation_base & _r) { + karr_relation & r = get(_r); + TRACE("dl", r.display(tout << "src:\n");); + r.get_ineqs(); + for (unsigned i = 1; i < m_identical_cols.size(); ++i) { + unsigned c1 = m_identical_cols[0]; + unsigned c2 = m_identical_cols[i]; + vector row; + row.resize(r.get_signature().size()); + row[c1] = rational(1); + row[c2] = rational(-1); + r.m_ineqs.A.push_back(row); + r.m_ineqs.b.push_back(rational(0)); + r.m_ineqs.eq.push_back(true); + r.m_basis_valid = false; + } + TRACE("dl", r.display(tout << "result:\n");); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if(!check_kind(t)) { + return 0; + } + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + + + class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { + unsigned m_col; + rational m_value; + bool m_valid; + public: + filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) + : m_col(col) { + arith_util arith(m.get_context().get_manager()); + m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); + } + + virtual void operator()(relation_base & _r) { + karr_relation & r = get(_r); + if (m_valid) { + r.get_ineqs(); + vector row; + row.resize(r.get_signature().size()); + row[m_col] = rational(1); + r.m_ineqs.A.push_back(row); + r.m_ineqs.b.push_back(rational(-1)); + r.m_ineqs.eq.push_back(true); + r.m_basis_valid = false; + } + TRACE("dl", tout << m_value << "\n"; r.display(tout);); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if (check_kind(r)) { + return alloc(filter_equal_fn, get_manager(), value, col); + } + return 0; + } + + + class karr_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + app_ref m_cond; + public: + filter_interpreted_fn(karr_relation const& t, app* cond): + m_cond(cond, t.get_plugin().get_ast_manager()) { + } + + void operator()(relation_base& t) { + get(t).filter_interpreted(m_cond); + TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + if (check_kind(t)) { + return alloc(filter_interpreted_fn, get(t), condition); + } + return 0; + } +}; diff --git a/src/muz/rel/karr_relation.h b/src/muz/rel/karr_relation.h new file mode 100644 index 000000000..00a92748a --- /dev/null +++ b/src/muz/rel/karr_relation.h @@ -0,0 +1,88 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + karr_relation.h + +Abstract: + + Extract integer linear invariants. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-08 + +Revision History: + +--*/ +#ifndef _KARR_RELATION_H_ +#define _KARR_RELATION_H_ + +#include"dl_mk_karr_invariants.h" +#include"dl_relation_manager.h" + +namespace datalog { + + class karr_relation; + + class karr_relation_plugin : public relation_plugin { + arith_util a; + hilbert_basis m_hb; + + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + friend class karr_relation; + public: + karr_relation_plugin(relation_manager& rm): + relation_plugin(karr_relation_plugin::get_name(), rm), + a(get_ast_manager()) + {} + + virtual bool can_handle_signature(const relation_signature & sig) { + return true; + } + + static symbol get_name() { return symbol("karr_relation"); } + + virtual void set_cancel(bool f); + + virtual relation_base * mk_empty(const relation_signature & s); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + + static karr_relation& get(relation_base& r); + static karr_relation const & get(relation_base const& r); + + virtual relation_join_fn * mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + private: + bool dualizeI(matrix& dst, matrix const& src); + void dualizeH(matrix& dst, matrix const& src); + + + }; + + +}; + +#endif /* _DL_MK_KARR_INVARIANTS_H_ */ +