/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_base.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include "ast/ast_pp.h" #include "util/union_find.h" #include "util/vector.h" #include "muz/base/dl_context.h" #include "muz/rel/dl_base.h" #include "ast/rewriter/bool_rewriter.h" #include "muz/rel/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) { for (auto& r : v) { r->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(nullptr)); for (unsigned i = 0; i < sz; i++) { if (map[i] != UINT_MAX) { renaming_arg.set(ofs-i, m.mk_var(map[i], orig_sig[i])); } } } context & get_context_from_rel_manager(const relation_manager & rm) { return rm.get_context(); } ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm) { return rm.get_context().get_manager(); } #if DL_LEAK_HUNTING void leak_guard_check(const symbol & s) { } #endif void relation_signature::output(ast_manager & m, std::ostream & out) const { unsigned sz = size(); out << "("; for (unsigned i = 0; i < sz; i++) { if (i != 0) out << ","; out << mk_pp((*this)[i], m); } out << ")"; } relation_fact::relation_fact(context & ctx) : app_ref_vector(ctx.get_manager()) {} void relation_base::reset() { ast_manager & m = get_plugin().get_ast_manager(); app_ref bottom_ref(m.mk_false(), m); scoped_ptr reset_fn = get_manager().mk_filter_interpreted_fn(*this, bottom_ref); if (!reset_fn) { throw default_exception("filter function does not exist"); } (*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 < fact_cnt; i++) { remove_fact(facts[i]); } } void table_base::remove_facts(unsigned fact_cnt, const table_element * facts) { for (unsigned i = 0; i < fact_cnt; i++) { remove_fact(facts + i*get_signature().size()); } } void table_base::reset() { vector to_remove; table_fact row; for (auto& k : *this) { k.get_fact(row); to_remove.push_back(row); } remove_facts(to_remove.size(), to_remove.data()); } bool table_base::contains_fact(const table_fact & f) const { table_fact row; for (auto const& k : *this) { k.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_fact row; for (auto& k : *this) { k.get_fact(row); bool differs = false; for (unsigned i=0; iadd_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_t 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(); auto str = buffer.str(); warning_msg("%s", 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"; for (auto& k : *this) { k.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: bool is_finished() const override { 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) {} table_element operator*() override { SASSERT(!is_finished()); return m_parent[m_index]; } void operator++() override { 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 < n; i++) { result.push_back((*this)[i]); } } void table_base::row_interface::display(std::ostream & out) const { table_fact fact; get_fact(fact); print_container(fact, out); out << "\n"; } void table_base::to_formula(relation_signature const& sig, expr_ref& fml) const { // iterate over rows and build disjunction ast_manager & m = fml.get_manager(); expr_ref_vector disjs(m); expr_ref_vector conjs(m); dl_decl_util util(m); bool_rewriter brw(m); table_fact fact; for (row_interface const& r : *this) { r.get_fact(fact); conjs.reset(); for (unsigned i = 0; i < fact.size(); ++i) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i]))); } brw.mk_and(conjs.size(), conjs.data(), fml); disjs.push_back(fml); } brw.mk_or(disjs.size(), disjs.data(), fml); } }