mirror of
https://github.com/Z3Prover/z3
synced 2025-04-08 02:15:19 +00:00
re-organize muz_qe into separate units
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
0d56499e2d
commit
c8f9535251
490
src/muz/rel/dl_base.cpp
Normal file
490
src/muz/rel/dl_base.cpp
Normal file
|
@ -0,0 +1,490 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_base.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<sstream>
|
||||
|
||||
|
||||
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<relation_base> & v) {
|
||||
ptr_vector<relation_base>::iterator it = v.begin();
|
||||
ptr_vector<relation_base>::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<expr *>(0));
|
||||
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) { 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<relation_mutator_fn> reset_fn =
|
||||
get_manager().mk_filter_interpreted_fn(static_cast<relation_base &>(*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<s1first_func; i++) {
|
||||
result.push_back(s1[i]);
|
||||
}
|
||||
for(unsigned i=0; i<s2first_func; i++) {
|
||||
result.push_back(s2[i]);
|
||||
}
|
||||
for(unsigned i=s1first_func; i<s1sz; i++) {
|
||||
result.push_back(s1[i]);
|
||||
}
|
||||
for(unsigned i=s2first_func; i<s2sz; i++) {
|
||||
result.push_back(s2[i]);
|
||||
}
|
||||
result.set_functional_columns(s1.functional_columns()+s2.functional_columns());
|
||||
}
|
||||
|
||||
void table_signature::from_project(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 func_cnt = src.functional_columns();
|
||||
|
||||
if(removed_cols==0) {
|
||||
result.set_functional_columns(func_cnt);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned first_src_fun = src.first_functional();
|
||||
if(removed_cols[0]<first_src_fun) {
|
||||
//if we remove at least one non-functional column, all the columns in the result are non-functional
|
||||
result.set_functional_columns(0);
|
||||
}
|
||||
else {
|
||||
//all columns we are removing are functional
|
||||
SASSERT(func_cnt>=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]<first_src_fun) {
|
||||
break;
|
||||
}
|
||||
remaining_fun--;
|
||||
}
|
||||
result.set_functional_columns(remaining_fun);
|
||||
}
|
||||
|
||||
void table_signature::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) {
|
||||
table_signature aux;
|
||||
from_join(s1, s2, joined_col_cnt, cols1, cols2, aux);
|
||||
|
||||
//after the join the column order is
|
||||
//(non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2)
|
||||
|
||||
if(s1.functional_columns()==0 && s2.functional_columns()==0) {
|
||||
from_project(aux, removed_col_cnt, removed_cols, result);
|
||||
SASSERT(result.functional_columns()==0);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned join_sig_sz = s1.size()+s2.size();
|
||||
unsigned s1_first_func = s1.first_functional();
|
||||
unsigned s2_first_func = s2.first_functional();
|
||||
unsigned second_ofs = s1_first_func;
|
||||
unsigned first_func_ofs = second_ofs + s2_first_func;
|
||||
unsigned second_func_ofs = second_ofs + s1.functional_columns();
|
||||
|
||||
svector<unsigned> 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; i<join_sig_sz; i++) {
|
||||
unsigned v = uf.mk_var();
|
||||
SASSERT(v==i);
|
||||
}
|
||||
|
||||
for(unsigned i=0; i<joined_col_cnt; i++) {
|
||||
unsigned idx1 = (s1_first_func>cols1[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; i++) { //we only count the non-functional columns
|
||||
remaining_in_equivalence_class[uf.find(i)]++;
|
||||
}
|
||||
|
||||
for(unsigned i=0; i<removed_col_cnt; i++) {
|
||||
unsigned rc = removed_cols[i];
|
||||
if(rc>=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<table_fact> 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; i<non_func_cnt; i++) {
|
||||
if(row[i]!=f[i]) {
|
||||
differs = true;
|
||||
}
|
||||
}
|
||||
if(differs) {
|
||||
continue;
|
||||
}
|
||||
for(unsigned i=non_func_cnt; i<sig_sz; i++) {
|
||||
f[i]=row[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool table_base::suggest_fact(table_fact & f) {
|
||||
if(get_signature().functional_columns()==0) {
|
||||
if(contains_fact(f)) {
|
||||
return false;
|
||||
}
|
||||
add_new_fact(f);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if(fetch_fact(f)) {
|
||||
return false;
|
||||
}
|
||||
add_new_fact(f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void table_base::ensure_fact(const table_fact & f) {
|
||||
if(get_signature().functional_columns()==0) {
|
||||
add_fact(f);
|
||||
}
|
||||
else {
|
||||
remove_fact(f);
|
||||
add_fact(f);
|
||||
}
|
||||
}
|
||||
|
||||
table_base * table_base::clone() const {
|
||||
table_base * res = get_plugin().mk_empty(get_signature());
|
||||
|
||||
iterator it = begin();
|
||||
iterator iend = end();
|
||||
|
||||
table_fact row;
|
||||
|
||||
for(; it!=iend; ++it) {
|
||||
it->get_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<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;
|
||||
iterator it = begin();
|
||||
iterator iend = end();
|
||||
for(; it != iend; ++it) {
|
||||
const row_interface & r = *it;
|
||||
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.c_ptr(), fml);
|
||||
disjs.push_back(fml);
|
||||
}
|
||||
brw.mk_or(disjs.size(), disjs.c_ptr(), fml);
|
||||
}
|
||||
|
||||
}
|
1231
src/muz/rel/dl_base.h
Normal file
1231
src/muz/rel/dl_base.h
Normal file
File diff suppressed because it is too large
Load diff
707
src/muz/rel/dl_bound_relation.cpp
Normal file
707
src/muz/rel/dl_bound_relation.cpp
Normal file
|
@ -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<bound_relation&>(r);
|
||||
}
|
||||
|
||||
bound_relation const & bound_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<bound_relation const&>(r);
|
||||
}
|
||||
|
||||
bound_relation* bound_relation_plugin::get(relation_base* r) {
|
||||
return dynamic_cast<bound_relation*>(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<interval_relation&>(r);
|
||||
}
|
||||
|
||||
interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) {
|
||||
SASSERT(is_interval_relation(r));
|
||||
return dynamic_cast<interval_relation const&>(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<bound_relation*>(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<uint_set2, bound_relation_helper>(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<relation_mutator_fn> 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<bound_relation_plugin&>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
178
src/muz/rel/dl_bound_relation.h
Normal file
178
src/muz/rel/dl_bound_relation.h
Normal file
|
@ -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<uint_set2, bound_relation_helper> {
|
||||
friend class bound_relation_plugin;
|
||||
svector<std::pair<unsigned, bool> > 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
|
||||
|
439
src/muz/rel/dl_check_table.cpp
Normal file
439
src/muz/rel/dl_check_table.cpp
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_check_table.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<check_table&>(r);
|
||||
}
|
||||
|
||||
check_table const & check_table_plugin::get(table_base const& r) {
|
||||
return static_cast<check_table const &>(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<table_join_fn> m_tocheck;
|
||||
scoped_ptr<table_join_fn> 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<table_join_fn> m_tocheck;
|
||||
scoped_ptr<table_join_fn> 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<table_union_fn> m_tocheck;
|
||||
scoped_ptr<table_union_fn> 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<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> 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<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> 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<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> 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<table_mutator_fn> m_checker;
|
||||
scoped_ptr<table_mutator_fn> 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<table_mutator_fn> m_checker;
|
||||
scoped_ptr<table_mutator_fn> 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<table_mutator_fn> m_checker;
|
||||
scoped_ptr<table_mutator_fn> 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<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> 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<table_intersection_filter_fn> m_checker;
|
||||
scoped_ptr<table_intersection_filter_fn> 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
135
src/muz/rel/dl_check_table.h
Normal file
135
src/muz/rel/dl_check_table.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_check_table.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<check_table_plugin &>(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_ */
|
456
src/muz/rel/dl_external_relation.cpp
Normal file
456
src/muz/rel/dl_external_relation.cpp
Normal file
|
@ -0,0 +1,456 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_external_relation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<expr> 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<func_decl_ref&>(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<func_decl_ref&>(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<external_relation_plugin &>(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<external_relation const&>(r);
|
||||
}
|
||||
|
||||
external_relation & external_relation_plugin::get(relation_base & r) {
|
||||
return dynamic_cast<external_relation&>(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<parameter> 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<parameter> 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<parameter> 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<parameter> 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<parameter> 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);
|
||||
}
|
||||
|
||||
};
|
154
src/muz/rel/dl_external_relation.h
Normal file
154
src/muz/rel/dl_external_relation.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_external_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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
|
2377
src/muz/rel/dl_finite_product_relation.cpp
Normal file
2377
src/muz/rel/dl_finite_product_relation.cpp
Normal file
File diff suppressed because it is too large
Load diff
366
src/muz/rel/dl_finite_product_relation.h
Normal file
366
src/muz/rel/dl_finite_product_relation.h
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_finite_product_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<bool> m_table_cols;
|
||||
|
||||
rel_spec() : m_inner_kind(null_family_id) {}
|
||||
rel_spec(const svector<bool>& 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<bool_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<rel_spec, rel_spec::hash, default_eq<rel_spec> > 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<bool> & table_columns);
|
||||
void get_all_possible_table_columns(const relation_signature & s, svector<bool> & 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<table_transformer_fn> m_live_rel_collection_project;
|
||||
|
||||
mutable scoped_ptr<table_intersection_filter_fn> 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<UINT_MAX); return get_inner_rel(static_cast<unsigned>(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<finite_product_relation &>(*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<UINT_MAX); return set_inner_rel(static_cast<unsigned>(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<finite_product_relation> & 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<finite_product_relation_plugin &>(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<UINT_MAX); return get_inner_rel(static_cast<unsigned>(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_ */
|
||||
|
653
src/muz/rel/dl_interval_relation.cpp
Normal file
653
src/muz/rel/dl_interval_relation.cpp
Normal file
|
@ -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<interval_relation*>(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<interval_relation*>(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<interval_relation*>(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<interval_relation&>(r);
|
||||
}
|
||||
|
||||
interval_relation const & interval_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<interval_relation const&>(r);
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
// interval_relation
|
||||
|
||||
interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty):
|
||||
vector_relation<interval>(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<interval_relation_plugin &>(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;
|
||||
}
|
||||
|
||||
};
|
||||
|
142
src/muz/rel/dl_interval_relation.h
Normal file
142
src/muz/rel/dl_interval_relation.h
Normal file
|
@ -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<v_dependency_manager&>(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<interval> {
|
||||
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
|
||||
|
879
src/muz/rel/dl_mk_explanations.cpp
Normal file
879
src/muz/rel/dl_mk_explanations.cpp
Normal file
|
@ -0,0 +1,879 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_explanations.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-11-08.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#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<ptr_vector<explanation_relation> > 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<n; i++) {
|
||||
if (!get_context().get_decl_util().is_rule_sort(s[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
|
||||
void recycle(explanation_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_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);
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// explanation_relation
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class explanation_relation : public relation_base {
|
||||
friend class explanation_relation_plugin;
|
||||
friend class explanation_relation_plugin::join_fn;
|
||||
friend class explanation_relation_plugin::project_fn;
|
||||
friend class explanation_relation_plugin::rename_fn;
|
||||
friend class explanation_relation_plugin::union_fn;
|
||||
friend class explanation_relation_plugin::foreign_union_fn;
|
||||
friend class explanation_relation_plugin::assignment_filter_fn;
|
||||
friend class explanation_relation_plugin::intersection_filter_fn;
|
||||
|
||||
bool m_empty;
|
||||
/**
|
||||
Valid only if \c !m_empty.
|
||||
|
||||
Zero elements mean undefined.
|
||||
*/
|
||||
relation_fact m_data;
|
||||
|
||||
explanation_relation(explanation_relation_plugin & p, const relation_signature & s)
|
||||
: relation_base(p, s), m_empty(true), m_data(p.get_ast_manager()) {
|
||||
|
||||
DEBUG_CODE(
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i=0;i<sz; i++) {
|
||||
SASSERT( p.get_context().get_decl_util().is_rule_sort(s[i]) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void assign_data(const relation_fact & f) {
|
||||
m_empty = false;
|
||||
|
||||
unsigned n=get_signature().size();
|
||||
SASSERT(f.size()==n);
|
||||
m_data.reset();
|
||||
m_data.append(n, f.c_ptr());
|
||||
}
|
||||
void set_undefined() {
|
||||
m_empty = false;
|
||||
m_data.reset();
|
||||
m_data.resize(get_signature().size());
|
||||
}
|
||||
void unite_with_data(const relation_fact & f) {
|
||||
if (empty()) {
|
||||
assign_data(f);
|
||||
return;
|
||||
}
|
||||
unsigned n=get_signature().size();
|
||||
SASSERT(f.size()==n);
|
||||
for (unsigned i=0; i<n; i++) {
|
||||
SASSERT(!is_undefined(i));
|
||||
m_data[i] = get_plugin().mk_union(m_data[i], f[i]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void deallocate() {
|
||||
get_plugin().recycle(this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explanation_relation_plugin & get_plugin() const {
|
||||
return static_cast<explanation_relation_plugin &>(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<n; i++) {
|
||||
if (is_undefined(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool empty() const { return m_empty; }
|
||||
|
||||
virtual void reset() {
|
||||
m_empty = true;
|
||||
}
|
||||
|
||||
virtual void add_fact(const relation_fact & f) {
|
||||
SASSERT(empty());
|
||||
assign_data(f);
|
||||
}
|
||||
|
||||
virtual bool contains_fact(const relation_fact & f) const {
|
||||
UNREACHABLE();
|
||||
throw 0;
|
||||
}
|
||||
|
||||
virtual explanation_relation * clone() const {
|
||||
explanation_relation * res = static_cast<explanation_relation *>(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<explanation_relation *>(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 << "<undefined>";
|
||||
}
|
||||
}
|
||||
|
||||
virtual void display(std::ostream & out) const {
|
||||
if (empty()) {
|
||||
out << "<empty explanation relation>\n";
|
||||
return;
|
||||
}
|
||||
unsigned sz = get_signature().size();
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
if (i!=0) {
|
||||
out << ", ";
|
||||
}
|
||||
display_explanation(m_data[0], out);
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
virtual unsigned get_size_estimate() const { return empty() ? 0 : 1; }
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// explanation_relation_plugin
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
|
||||
relation_base * explanation_relation_plugin::mk_empty(const relation_signature & s) {
|
||||
if (m_pool.size() > 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<const explanation_relation &>(r1_0);
|
||||
const explanation_relation & r2 = static_cast<const explanation_relation &>(r2_0);
|
||||
explanation_relation_plugin & plugin = r1.get_plugin();
|
||||
|
||||
explanation_relation * res = static_cast<explanation_relation *>(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<const explanation_relation &>(r0);
|
||||
explanation_relation_plugin & plugin = r.get_plugin();
|
||||
|
||||
explanation_relation * res = static_cast<explanation_relation *>(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<const explanation_relation &>(r0);
|
||||
explanation_relation_plugin & plugin = r.get_plugin();
|
||||
|
||||
explanation_relation * res = static_cast<explanation_relation *>(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<relation_union_fn> m_delta_union_fun;
|
||||
public:
|
||||
virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) {
|
||||
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
|
||||
const explanation_relation & src = static_cast<const explanation_relation &>(src0);
|
||||
explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(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<relation_union_fn> m_delta_union_fun;
|
||||
public:
|
||||
virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) {
|
||||
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
|
||||
explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(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<explanation_relation &>(r0);
|
||||
|
||||
if (!r.is_undefined(m_col_idx)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
unsigned sz = r.get_signature().size();
|
||||
ptr_vector<expr> subst_arg;
|
||||
subst_arg.resize(sz, 0);
|
||||
unsigned ofs = sz-1;
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
SASSERT(!r.is_undefined(i) || !contains_var(m_new_rule, i));
|
||||
subst_arg[ofs-i] = r.m_data.get(i);
|
||||
}
|
||||
expr_ref res(m_manager);
|
||||
m_subst(m_new_rule, subst_arg.size(), subst_arg.c_ptr(), res);
|
||||
r.m_data[m_col_idx] = to_app(res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * explanation_relation_plugin::mk_filter_interpreted_fn(const relation_base & r,
|
||||
app * cond) {
|
||||
if (&r.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
ast_manager & m = get_ast_manager();
|
||||
if (!m.is_eq(cond)) {
|
||||
return 0;
|
||||
}
|
||||
expr * arg1 = cond->get_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<explanation_relation &>(tgt0);
|
||||
const explanation_relation & src = static_cast<const explanation_relation &>(src0);
|
||||
|
||||
if (src.empty()) {
|
||||
tgt.reset();
|
||||
return;
|
||||
}
|
||||
if (tgt.empty()) {
|
||||
return;
|
||||
}
|
||||
unsigned sz = tgt.get_signature().size();
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
if (src.is_undefined(i)) {
|
||||
continue;
|
||||
}
|
||||
app * curr_src = src.m_data.get(i);
|
||||
if (tgt.is_undefined(i)) {
|
||||
tgt.m_data.set(i, curr_src);
|
||||
continue;
|
||||
}
|
||||
app * curr_tgt = tgt.m_data.get(i);
|
||||
if (curr_tgt->get_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<explanation_relation_plugin *>(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<bool> inner_sieve(sz-1, true);
|
||||
inner_sieve.push_back(false);
|
||||
|
||||
svector<bool> 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<bool> neg_flags;
|
||||
unsigned pos_tail_sz = r->get_positive_tail_size();
|
||||
for (unsigned i=0; i<pos_tail_sz; i++) {
|
||||
unsigned e_var = next_var++;
|
||||
e_tail.push_back(get_e_lit(r->get_tail(i), e_var));
|
||||
neg_flags.push_back(false);
|
||||
}
|
||||
unsigned tail_sz = r->get_tail_size();
|
||||
for (unsigned i=pos_tail_sz; i<tail_sz; i++) {
|
||||
e_tail.push_back(r->get_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_idx<pos_tail_sz; tail_idx++) {
|
||||
app * tail = e_tail.get(tail_idx);
|
||||
if (true || m_relation_level) {
|
||||
//this adds the explanation term of the tail
|
||||
rule_expr_args.push_back(tail->get_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; i<arity; i++) {
|
||||
lit_args.push_back(m_manager.mk_var(i, orig_decl->get_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<product_relation &>(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<sieve_relation *>(&prod_rel[0]),
|
||||
static_cast<sieve_relation *>(&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<explanation_relation &>(srels[1]->get_inner());
|
||||
|
||||
{
|
||||
scoped_ptr<relation_union_fn> orig_union_fun = rmgr.mk_union_fn(new_orig, orig);
|
||||
SASSERT(orig_union_fun);
|
||||
(*orig_union_fun)(new_orig, orig);
|
||||
}
|
||||
|
||||
{
|
||||
scoped_ptr<relation_union_fn> 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<explanation_relation *>(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<relation_join_fn> product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0);
|
||||
SASSERT(product_fun);
|
||||
scoped_rel<relation_base> aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation);
|
||||
scoped_ptr<relation_union_fn> 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
86
src/muz/rel/dl_mk_explanations.h
Normal file
86
src/muz/rel/dl_mk_explanations.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_explanations.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<func_decl, func_decl *> 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<explanation_relation> 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_ */
|
||||
|
155
src/muz/rel/dl_mk_partial_equiv.cpp
Normal file
155
src/muz/rel/dl_mk_partial_equiv.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
50
src/muz/rel/dl_mk_partial_equiv.h
Normal file
50
src/muz/rel/dl_mk_partial_equiv.h
Normal file
|
@ -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_ */
|
||||
|
||||
|
546
src/muz/rel/dl_mk_similarity_compressor.cpp
Normal file
546
src/muz/rel/dl_mk_similarity_compressor.cpp
Normal file
|
@ -0,0 +1,546 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_similarity_compressor.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-10-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include<utility>
|
||||
#include<sstream>
|
||||
#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<int>(r->get_positive_tail_size()));
|
||||
return r->get_tail(idx);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static int aux_compare(T a, T b) {
|
||||
return (a>b) ? 1 : ( (a==b) ? 0 : -1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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; i<n; i++) {
|
||||
if (is_var(t1->get_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; i<pos_tail_sz; i++) {
|
||||
app * t1 = get_by_tail_index(r1, i);
|
||||
app * t2 = get_by_tail_index(r2, i);
|
||||
res = aux_compare(t1->get_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; i<tail_sz; i++) {
|
||||
res = aux_compare(r1->get_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<pos_tail_sz; i++) {
|
||||
int res = compare_args(get_by_tail_index(r1, i), get_by_tail_index(r2, i), skipped_arg_index);
|
||||
if (res!=0) { return res; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class const_info {
|
||||
int m_tail_index;
|
||||
unsigned m_arg_index;
|
||||
bool m_has_parent;
|
||||
/** Parent is a constant that appears earlier in the rule and has always the same value
|
||||
as this constant. */
|
||||
unsigned m_parent_index;
|
||||
public:
|
||||
|
||||
const_info(int tail_index, unsigned arg_index)
|
||||
: m_tail_index(tail_index), m_arg_index(arg_index), m_has_parent(false) {}
|
||||
|
||||
int tail_index() const { return m_tail_index; }
|
||||
unsigned arg_index() const { return m_arg_index; }
|
||||
bool has_parent() const { return m_has_parent; }
|
||||
unsigned parent_index() const { SASSERT(has_parent()); return m_parent_index; }
|
||||
|
||||
void set_parent_index(unsigned idx) {
|
||||
SASSERT(!m_has_parent);
|
||||
m_has_parent = true;
|
||||
m_parent_index = idx;
|
||||
}
|
||||
};
|
||||
|
||||
typedef svector<const_info> 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; i<n; i++) {
|
||||
if (is_var(t->get_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; i<pos_tail_sz; i++) {
|
||||
collect_const_indexes(r->get_tail(i), i, res);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
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; i<const_cnt; i++) {
|
||||
const_info inf = const_infos[i];
|
||||
if (inf.has_parent()) {
|
||||
continue;
|
||||
}
|
||||
app * pred = get_by_tail_index(r, inf.tail_index());
|
||||
tgt.push_back(to_app(pred->get_arg(inf.arg_index())));
|
||||
SASSERT(tgt.back()->get_num_args()==0);
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
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; i<const_cnt; i++) {
|
||||
const_info inf = const_infos[i];
|
||||
if (inf.has_parent()) {
|
||||
continue;
|
||||
}
|
||||
app * pred = get_by_tail_index(r, inf.tail_index());
|
||||
tgt.push_back(pred->get_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<app> 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; i<const_cnt; i++) {
|
||||
app * pred = get_by_tail_index(*it, const_infos[i].tail_index());
|
||||
app * val = to_app(pred->get_arg(const_infos[i].arg_index()));
|
||||
if (vals[i]!=val) {
|
||||
vals[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned removed_cnt = 0;
|
||||
for (unsigned i=0; i<const_cnt; i++) {
|
||||
if (vals[i]!=0) {
|
||||
removed_cnt++;
|
||||
}
|
||||
else if (removed_cnt!=0) {
|
||||
const_infos[i-removed_cnt] = const_infos[i];
|
||||
}
|
||||
}
|
||||
if (removed_cnt!=0) {
|
||||
const_infos.shrink(const_cnt-removed_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief When function returns, \c parents will contain for each constant the index of the
|
||||
first constant that is equal to it in all the rules. If there is no such, it will contain
|
||||
its own index.
|
||||
*/
|
||||
static void detect_equal_constants(rule_vector::iterator first, rule_vector::iterator after_last,
|
||||
info_vector & const_infos) {
|
||||
SASSERT(first!=after_last);
|
||||
unsigned const_cnt = const_infos.size();
|
||||
ptr_vector<app> vals;
|
||||
ptr_vector<sort> sorts;
|
||||
rule * r = *(first++);
|
||||
collect_orphan_consts(r, const_infos, vals);
|
||||
collect_orphan_sorts(r, const_infos, sorts);
|
||||
SASSERT(vals.size()==const_cnt);
|
||||
vector<unsigned_vector> possible_parents(const_cnt);
|
||||
for (unsigned i=1; i<const_cnt; i++) {
|
||||
for (unsigned j=0; j<i; j++) {
|
||||
if (vals[i]==vals[j] && sorts[i]==sorts[j]) {
|
||||
possible_parents[i].push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
rule_vector::iterator it = first;
|
||||
for (; it!=after_last; ++it) {
|
||||
collect_orphan_consts(*it, const_infos, vals);
|
||||
for (unsigned i=1; i<const_cnt; i++) {
|
||||
unsigned_vector & ppars = possible_parents[i];
|
||||
unsigned j=0;
|
||||
while(j<ppars.size()) {
|
||||
if (vals[i]!=vals[ppars[j]]) {
|
||||
ppars[j] = ppars.back();
|
||||
ppars.pop_back();
|
||||
}
|
||||
else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned i=0; i<const_cnt; i++) {
|
||||
unsigned parent = i;
|
||||
unsigned_vector & ppars = possible_parents[i];
|
||||
unsigned ppars_sz = ppars.size();
|
||||
for (unsigned j=0; j<ppars_sz; j++) {
|
||||
if (ppars[j]<parent) {
|
||||
parent = ppars[j];
|
||||
}
|
||||
}
|
||||
if (parent!=i) {
|
||||
const_infos[i].set_parent_index(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned get_constant_count(rule * r) {
|
||||
unsigned res = r->get_head()->get_num_args() - count_variable_arguments(r->get_head());
|
||||
unsigned pos_tail_sz = r->get_positive_tail_size();
|
||||
for (unsigned i=0; i<pos_tail_sz; i++) {
|
||||
res+= r->get_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<sort> 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<app> new_tail;
|
||||
svector<bool> new_negs;
|
||||
unsigned tail_sz = r->get_tail_size();
|
||||
for (unsigned i=0; i<tail_sz; i++) {
|
||||
new_tail.push_back(r->get_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<var> 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<const_cnt; ) {
|
||||
int tail_idx = const_infos[i].tail_index();
|
||||
app * & mod_tail = (tail_idx==-1) ? new_head : new_tail[tail_idx];
|
||||
ptr_vector<expr> mod_args(mod_tail->get_num_args(), mod_tail->get_args());
|
||||
|
||||
for (; i<const_cnt && const_infos[i].tail_index()==tail_idx; i++) { //here the outer loop counter is modified
|
||||
const_info & inf = const_infos[i];
|
||||
var * mod_var;
|
||||
if (!inf.has_parent()) {
|
||||
mod_var = m_manager.mk_var(new_var_idx_base+aux_column_index,
|
||||
aux_domain[aux_column_index]);
|
||||
aux_column_index++;
|
||||
aux_vars.push_back(mod_var);
|
||||
}
|
||||
else {
|
||||
mod_var = const_vars[inf.parent_index()];
|
||||
}
|
||||
const_vars.push_back(mod_var);
|
||||
mod_args[inf.arg_index()] = mod_var;
|
||||
}
|
||||
|
||||
app * upd_tail = m_manager.mk_app(mod_tail->get_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_index<const_cnt; ignored_index++) {
|
||||
arg_ignoring_comparator comparator(ignored_index);
|
||||
std::sort(first, after_last, comparator);
|
||||
|
||||
rule_vector::iterator it = first;
|
||||
rule_vector::iterator grp_begin = it;
|
||||
unsigned grp_size=0;
|
||||
while(it!=after_last) {
|
||||
rule_vector::iterator prev = it;
|
||||
++it;
|
||||
grp_size++;
|
||||
if (it==after_last || !comparator.eq(*prev, *it)) {
|
||||
if (grp_size>m_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<unsigned>(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<init_rule_cnt; i++) {
|
||||
m_rules.push_back(source.get_rule(i));
|
||||
}
|
||||
|
||||
std::sort(m_rules.begin(), m_rules.end(), initial_comparator);
|
||||
|
||||
rule_vector::iterator it = m_rules.begin();
|
||||
rule_vector::iterator end = m_rules.end();
|
||||
rule_vector::iterator cl_begin = it;
|
||||
while(it!=end) {
|
||||
rule_vector::iterator prev = it;
|
||||
++it;
|
||||
if (it==end || rough_compare(*prev, *it)!=0) {
|
||||
process_class(source, cl_begin, it);
|
||||
cl_begin = it;
|
||||
}
|
||||
}
|
||||
|
||||
rule_set * result = static_cast<rule_set *>(0);
|
||||
if (m_modified) {
|
||||
result = alloc(rule_set, m_context);
|
||||
unsigned fin_rule_cnt = m_result_rules.size();
|
||||
for (unsigned i=0; i<fin_rule_cnt; i++) {
|
||||
result->add_rule(m_result_rules.get(i));
|
||||
}
|
||||
result->inherit_predicates(source);
|
||||
}
|
||||
reset();
|
||||
return result;
|
||||
}
|
||||
};
|
78
src/muz/rel/dl_mk_similarity_compressor.h
Normal file
78
src/muz/rel/dl_mk_similarity_compressor.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_similarity_compressor.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-10-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_
|
||||
#define _DL_MK_SIMILARITY_COMPRESSOR_H_
|
||||
|
||||
#include<utility>
|
||||
|
||||
#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_ */
|
||||
|
742
src/muz/rel/dl_mk_simple_joins.cpp
Normal file
742
src/muz/rel/dl_mk_simple_joins.cpp
Normal file
|
@ -0,0 +1,742 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_simple_joins.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2010-05-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include<utility>
|
||||
#include<sstream>
|
||||
#include<limits>
|
||||
#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<cost>::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*, app*> app_pair;
|
||||
typedef map<app_pair, pair_info *,
|
||||
pair_hash<obj_ptr_hash<app>, obj_ptr_hash<app> >, default_eq<app_pair> > cost_map;
|
||||
typedef map<rule *, ptr_vector<app>, ptr_hash<rule>, ptr_eq<rule> > 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<app> m_interpreted;
|
||||
rule_pred_map m_rules_content;
|
||||
rule_ref_vector m_introduced_rules;
|
||||
ptr_hashtable<rule, ptr_hash<rule>, ptr_eq<rule> > m_modified_rules;
|
||||
|
||||
ast_ref_vector m_pinned;
|
||||
mutable ptr_vector<sort> 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; i<n; i++) {
|
||||
SASSERT(is_var(t->get_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()<t2->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; i<n; i++) {
|
||||
//We assume that the mk_simple_joins transformer is applied after mk_filter_rules,
|
||||
//so the only literals which appear in pairs are the ones that contain only variables.
|
||||
var * v1 = to_var(t1->get_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()<v2->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]<norm1[v2_idx]) {
|
||||
std::swap(t1, t2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
norm1[v1_idx]=i;
|
||||
norm2[v2_idx]=i;
|
||||
}
|
||||
//if we did not exit the loop prematurely, the two terms are indistinguishable,
|
||||
//so the order should not matter
|
||||
}
|
||||
|
||||
result.resize(max_var_idx+1, static_cast<expr *>(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<app> & rule_content =
|
||||
m_rules_content.insert_if_not_there2(r, ptr_vector<app>())->get_data().m_value;
|
||||
SASSERT(rule_content.empty());
|
||||
|
||||
unsigned pos_tail_size=r->get_positive_tail_size();
|
||||
for(unsigned i=0; i<pos_tail_size; i++) {
|
||||
rule_content.push_back(r->get_tail(i));
|
||||
}
|
||||
for(unsigned i=0; i<pos_tail_size; i++) {
|
||||
app * t1 = r->get_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; j<pos_tail_size; j++) {
|
||||
app * t2 = r->get_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<sort> & domain) {
|
||||
unsigned n=t->get_num_args();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
var * v=to_var(t->get_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<sort> 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<rule, ptr_hash<rule>, default_eq<rule *> > 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<app> & removed_tails,
|
||||
const ptr_vector<app> & added_tails0, const ptr_vector<app> & 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<app> 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; i<rt_sz; i++) {
|
||||
for(unsigned j=i+1; j<rt_sz; j++) {
|
||||
app_pair pair_key = get_key(removed_tails[i], removed_tails[j]);
|
||||
remove_rule_from_pair(pair_key, r, original_len);
|
||||
}
|
||||
}
|
||||
//remove edges between surviving tails and removed tails
|
||||
for(unsigned i=0; i<len; i++) {
|
||||
if(added_tails.contains(rule_content[i])) {
|
||||
continue;
|
||||
}
|
||||
for(unsigned ri=0; ri<rt_sz; ri++) {
|
||||
app_pair pair_key = get_key(rule_content[i], removed_tails[ri]);
|
||||
remove_rule_from_pair(pair_key, r, original_len);
|
||||
}
|
||||
}
|
||||
|
||||
if(len==1) {
|
||||
return;
|
||||
}
|
||||
|
||||
app * head = r->get_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; i<tail_size; i++) {
|
||||
counter.count_vars(m, r->get_tail(i), 1);
|
||||
}
|
||||
for(unsigned i=0; i<len; i++) {
|
||||
counter.count_vars(m, rule_content[i], 1);
|
||||
}
|
||||
|
||||
//add edges that contain added tails
|
||||
while(!added_tails.empty()) {
|
||||
app * a_tail = added_tails.back(); //added tail
|
||||
|
||||
var_idx_set a_tail_vars = rm.collect_vars(a_tail);
|
||||
counter.count_vars(m, a_tail, -1); //temporarily remove a_tail variables from counter
|
||||
|
||||
for(unsigned i=0; i<len; i++) {
|
||||
app * o_tail = rule_content[i]; //other tail
|
||||
if(added_tails.contains(o_tail)) {
|
||||
//this avoids adding edges between new tails twice
|
||||
continue;
|
||||
}
|
||||
|
||||
counter.count_vars(m, o_tail, -1); //temporarily remove o_tail variables from counter
|
||||
var_idx_set scope_vars = rm.collect_vars(o_tail);
|
||||
scope_vars |= a_tail_vars;
|
||||
var_idx_set non_local_vars;
|
||||
counter.collect_positive(non_local_vars);
|
||||
counter.count_vars(m, o_tail, 1); //restore o_tail variables in counter
|
||||
set_intersection(non_local_vars, scope_vars);
|
||||
|
||||
register_pair(o_tail, a_tail, r, non_local_vars);
|
||||
}
|
||||
counter.count_vars(m, a_tail, 1); //restore t1 variables in counter
|
||||
added_tails.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void apply_binary_rule(rule * r, app_pair pair_key, app * t_new) {
|
||||
app * t1 = pair_key.first;
|
||||
app * t2 = pair_key.second;
|
||||
ptr_vector<app> & 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<app> removed_tails;
|
||||
ptr_vector<app> added_tails;
|
||||
for(unsigned i1=0; i1<len; i1++) {
|
||||
app * rt1 = rule_content[i1];
|
||||
if(rt1->get_decl()!=t1_pred) {
|
||||
continue;
|
||||
}
|
||||
unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0;
|
||||
for(unsigned i2=i2start; i2<len; i2++) {
|
||||
app * rt2 = rule_content[i2];
|
||||
if(i1==i2 || rt2->get_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<cost>(m_context.get_sort_size_estimate(sort));
|
||||
//unsigned sz;
|
||||
//if(!m_context.get_sort_size(sort, sz)) {
|
||||
// sz=UINT_MAX;
|
||||
//}
|
||||
//return static_cast<cost>(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<cost>(rel_size_int);
|
||||
cost curr_size = rel_size;
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
if(!is_var(t->get_arg(i))) {
|
||||
curr_size /= get_domain_size(pred, i);
|
||||
}
|
||||
}
|
||||
return curr_size;
|
||||
}
|
||||
}
|
||||
cost res = 1;
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
if(is_var(t->get_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; i<n; i++) {
|
||||
unsigned arg_index1, arg_index2;
|
||||
vi.get(i, arg_index1, arg_index2);
|
||||
inters_size *= get_domain_size(t1_pred, arg_index1);
|
||||
//joined arguments must have the same domain
|
||||
SASSERT(get_domain_size(t1_pred, arg_index1)==get_domain_size(t2_pred, arg_index2));
|
||||
}
|
||||
cost res = estimate_size(t1)*estimate_size(t2)/(inters_size*inters_size);
|
||||
//cost res = -inters_size;
|
||||
|
||||
/*unsigned t1_strat = get_stratum(t1_pred);
|
||||
SASSERT(t1_strat<=m_head_stratum);
|
||||
if(t1_strat<m_head_stratum) {
|
||||
unsigned t2_strat = get_stratum(t2_pred);
|
||||
SASSERT(t2_strat<=m_head_stratum);
|
||||
if(t2_strat<m_head_stratum) {
|
||||
//the rule of this predicates would depend on predicates
|
||||
//in lower stratum than the head, which is a good thing, since
|
||||
//then the rule code will not need to appear in a loop
|
||||
if(res>0) {
|
||||
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 || c<best_cost) {
|
||||
found = true;
|
||||
best_cost = c;
|
||||
best = key;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
return false;
|
||||
}
|
||||
p=best;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
rule_set * run(rule_set const & source) {
|
||||
|
||||
unsigned num_rules = source.get_num_rules();
|
||||
for (unsigned i = 0; i < num_rules; i++) {
|
||||
register_rule(source.get_rule(i));
|
||||
}
|
||||
|
||||
app_pair selected;
|
||||
while(pick_best_pair(selected)) {
|
||||
join_pair(selected);
|
||||
}
|
||||
|
||||
if(m_modified_rules.empty()) {
|
||||
return 0;
|
||||
}
|
||||
rule_set * result = alloc(rule_set, m_context);
|
||||
rule_pred_map::iterator rcit = m_rules_content.begin();
|
||||
rule_pred_map::iterator rcend = m_rules_content.end();
|
||||
for(; rcit!=rcend; ++rcit) {
|
||||
rule * orig_r = rcit->m_key;
|
||||
ptr_vector<app> 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<app> tail(content);
|
||||
svector<bool> negs(tail.size(), false);
|
||||
unsigned or_len = orig_r->get_tail_size();
|
||||
for(unsigned i=orig_r->get_positive_tail_size(); i<or_len; i++) {
|
||||
tail.push_back(orig_r->get_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);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
63
src/muz/rel/dl_mk_simple_joins.h
Normal file
63
src/muz/rel/dl_mk_simple_joins.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_simple_joins.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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_ */
|
||||
|
1117
src/muz/rel/dl_product_relation.cpp
Normal file
1117
src/muz/rel/dl_product_relation.cpp
Normal file
File diff suppressed because it is too large
Load diff
191
src/muz/rel/dl_product_relation.h
Normal file
191
src/muz/rel/dl_product_relation.h
Normal file
|
@ -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<family_id> 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<unsigned>(x); }
|
||||
};
|
||||
|
||||
rel_spec_store<rel_spec, svector_hash<fid_hash> > 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<const product_relation> & 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<relation_base> 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
|
||||
|
1702
src/muz/rel/dl_relation_manager.cpp
Normal file
1702
src/muz/rel/dl_relation_manager.cpp
Normal file
File diff suppressed because it is too large
Load diff
688
src/muz/rel/dl_relation_manager.h
Normal file
688
src/muz/rel/dl_relation_manager.h
Normal file
|
@ -0,0 +1,688 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_relation_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<func_decl, family_id> decl2kind_map;
|
||||
|
||||
typedef u_map<relation_plugin *> kind2plugin_map;
|
||||
|
||||
typedef map<const table_plugin *, table_relation_plugin *, ptr_hash<const table_plugin>,
|
||||
ptr_eq<const table_plugin> > tp2trp_map;
|
||||
typedef map<const relation_plugin *, finite_product_relation_plugin *, ptr_hash<const relation_plugin>,
|
||||
ptr_eq<const relation_plugin> > rp2fprp_map;
|
||||
|
||||
typedef map<func_decl *, relation_base *, ptr_hash<func_decl>, ptr_eq<func_decl> > relation_map;
|
||||
typedef ptr_vector<table_plugin> table_plugin_vector;
|
||||
typedef ptr_vector<relation_plugin> 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<relation_base *>(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<table_base *>(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 Spec, class Hash, class Eq=vector_eq_proc<Spec> >
|
||||
class rel_spec_store {
|
||||
typedef relation_signature::hash r_hash;
|
||||
typedef relation_signature::eq r_eq;
|
||||
|
||||
typedef map<Spec, unsigned, Hash, Eq > family_id_idx_store;
|
||||
typedef map<relation_signature, family_id_idx_store *, r_hash, r_eq> sig2store;
|
||||
|
||||
typedef u_map<Spec> family_id2spec;
|
||||
typedef map<relation_signature, family_id2spec *, r_hash, r_eq> sig2spec_store;
|
||||
|
||||
relation_plugin & m_parent;
|
||||
svector<family_id> 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_idx<m_allocated_kinds.size());
|
||||
ids.insert(spec, res_idx);
|
||||
|
||||
family_id2spec * idspecs = m_kind_specs.find(sig);
|
||||
idspecs->insert(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_ */
|
||||
|
666
src/muz/rel/dl_sieve_relation.cpp
Normal file
666
src/muz/rel/dl_sieve_relation.cpp
Normal file
|
@ -0,0 +1,666 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_explanations.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-11-08.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#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<n; i++) {
|
||||
if(inner_columns && inner_columns[i]) {
|
||||
unsigned inner_idx = m_inner2sig.size();
|
||||
SASSERT(get_inner().get_signature()[inner_idx]==s[i]);
|
||||
m_sig2inner.push_back(inner_idx);
|
||||
m_inner2sig.push_back(i);
|
||||
}
|
||||
else {
|
||||
m_sig2inner.push_back(UINT_MAX);
|
||||
m_ignored_cols.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
set_kind(p.get_relation_kind(*this, inner_columns));
|
||||
}
|
||||
|
||||
void sieve_relation::add_fact(const relation_fact & f) {
|
||||
relation_fact inner_f = f;
|
||||
project_out_vector_columns(inner_f, m_ignored_cols);
|
||||
get_inner().add_fact(inner_f);
|
||||
}
|
||||
|
||||
bool sieve_relation::contains_fact(const relation_fact & f) const {
|
||||
relation_fact inner_f = f;
|
||||
project_out_vector_columns(inner_f, m_ignored_cols);
|
||||
return get_inner().contains_fact(inner_f);
|
||||
}
|
||||
|
||||
sieve_relation * sieve_relation::clone() const {
|
||||
relation_base * new_inner = get_inner().clone();
|
||||
return get_plugin().mk_from_inner(get_signature(), m_inner_cols.c_ptr(), new_inner);
|
||||
}
|
||||
|
||||
relation_base * sieve_relation::complement(func_decl* p) const {
|
||||
//this is not precisely a complement, because we still treat the ignored collumns as
|
||||
//full, but it should give reasonable results inside the product relation
|
||||
relation_base * new_inner = get_inner().complement(p);
|
||||
return get_plugin().mk_from_inner(get_signature(), m_inner_cols.c_ptr(), new_inner);
|
||||
}
|
||||
|
||||
void sieve_relation::to_formula(expr_ref& fml) const {
|
||||
ast_manager& m = fml.get_manager();
|
||||
expr_ref_vector s(m);
|
||||
expr_ref tmp(m);
|
||||
relation_signature const& sig = get_inner().get_signature();
|
||||
unsigned sz = sig.size();
|
||||
for (unsigned i = sz ; 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<sieve_relation_plugin *>(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<sieve_relation&>(r);
|
||||
}
|
||||
|
||||
sieve_relation const & sieve_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<sieve_relation const&>(r);
|
||||
}
|
||||
|
||||
sieve_relation* sieve_relation_plugin::get(relation_base* r) {
|
||||
return dynamic_cast<sieve_relation*>(r);
|
||||
}
|
||||
|
||||
sieve_relation const* sieve_relation_plugin::get(relation_base const* r) {
|
||||
return dynamic_cast<sieve_relation const*>(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<bool> & inner_columns) {
|
||||
SASSERT(inner_columns.size()==s.size());
|
||||
unsigned n = s.size();
|
||||
relation_signature inner_sig_singleton;
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
inner_sig_singleton.reset();
|
||||
inner_sig_singleton.push_back(s[i]);
|
||||
inner_columns[i] = inner.can_handle_signature(inner_sig_singleton);
|
||||
}
|
||||
#if Z3DEBUG
|
||||
//we assume that if a relation plugin can handle two sets of columns separetely,
|
||||
//it can also handle them in one relation
|
||||
relation_signature inner_sig;
|
||||
collect_inner_signature(s, inner_columns, inner_sig);
|
||||
SASSERT(inner.can_handle_signature(inner_sig));
|
||||
#endif
|
||||
}
|
||||
|
||||
void sieve_relation_plugin::collect_inner_signature(const relation_signature & s,
|
||||
const svector<bool> & inner_columns, relation_signature & inner_sig) {
|
||||
SASSERT(inner_columns.size()==s.size());
|
||||
inner_sig.reset();
|
||||
unsigned n = s.size();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
if(inner_columns[i]) {
|
||||
inner_sig.push_back(s[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sieve_relation_plugin::extract_inner_signature(const relation_signature & s,
|
||||
relation_signature & inner_sig) {
|
||||
UNREACHABLE();
|
||||
#if 0
|
||||
svector<bool> 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<sieve_relation *>(mk_empty(original.get_signature(), original.get_kind()));
|
||||
}
|
||||
|
||||
relation_base * sieve_relation_plugin::mk_empty(const relation_base & original) {
|
||||
return mk_empty(static_cast<const sieve_relation &>(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<bool> 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<bool> 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<bool> 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<bool> 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<bool> m_result_inner_cols;
|
||||
|
||||
scoped_ptr<relation_join_fn> 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<const sieve_relation *>(&r1) : 0;
|
||||
const sieve_relation * sr2 = r2_sieved ? static_cast<const sieve_relation *>(&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<const sieve_relation *>(&r1) : 0;
|
||||
const sieve_relation * sr2 = r2_sieved ? static_cast<const sieve_relation *>(&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<const sieve_relation *>(&r1) : 0;
|
||||
const sieve_relation * sr2 = r2_sieved ? static_cast<const sieve_relation *>(&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; i<col_cnt; i++) {
|
||||
//if at least one end of an equality is not an inner column, we ignore that equality
|
||||
//(which introduces imprecision)
|
||||
if(r1_sieved && !sr1->is_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<bool> m_result_inner_cols;
|
||||
|
||||
scoped_ptr<relation_transformer_fn> 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<const sieve_relation &>(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<const sieve_relation &>(r0);
|
||||
unsigned_vector inner_removed_cols;
|
||||
|
||||
for(unsigned i=0; i<col_cnt; i++) {
|
||||
unsigned col = removed_cols[i];
|
||||
if(r.is_inner_col(col)) {
|
||||
inner_removed_cols.push_back(r.get_inner_col(col));
|
||||
}
|
||||
}
|
||||
|
||||
svector<bool> 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<const sieve_relation &>(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<bool> 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<relation_union_fn> 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<sieve_relation *>(&tgt) : 0;
|
||||
const sieve_relation * ssrc = src_sieved ? static_cast<const sieve_relation *>(&src) : 0;
|
||||
sieve_relation * sdelta = delta_sieved ? static_cast<sieve_relation *>(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<const sieve_relation *>(&tgt) : 0;
|
||||
const sieve_relation * ssrc = src_sieved ? static_cast<const sieve_relation *>(&src) : 0;
|
||||
const sieve_relation * sdelta = delta_sieved ? static_cast<const sieve_relation *>(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<relation_mutator_fn> 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<sieve_relation &>(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<const sieve_relation &>(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<col_cnt; i++) {
|
||||
unsigned col = identical_cols[i];
|
||||
if(r.is_inner_col(col)) {
|
||||
inner_icols.push_back(r.get_inner_col(col));
|
||||
}
|
||||
}
|
||||
|
||||
if(inner_icols.size()<2) {
|
||||
return alloc(identity_relation_mutator_fn);
|
||||
}
|
||||
|
||||
relation_mutator_fn * inner_fun = get_manager().mk_filter_identical_fn(r.get_inner(), inner_icols);
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_fn, inner_fun);
|
||||
}
|
||||
|
||||
relation_mutator_fn * sieve_relation_plugin::mk_filter_equal_fn(const relation_base & r0,
|
||||
const relation_element & value, unsigned col) {
|
||||
if(&r0.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
const sieve_relation & r = static_cast<const sieve_relation &>(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<const sieve_relation &>(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<sz; i++) {
|
||||
if(!cond_vars.contains(i)) {
|
||||
continue;
|
||||
}
|
||||
if(!r.is_inner_col(i)) {
|
||||
//If the condition involves columns which do not belong to the inner relation,
|
||||
//we do nothing (which introduces imprecision).
|
||||
//Maybe we might try to do some quantifier elimination...
|
||||
return alloc(identity_relation_mutator_fn);
|
||||
}
|
||||
subst_vect[subst_ofs-i] = m.mk_var(r.m_sig2inner[i], sig[i]);
|
||||
}
|
||||
expr_ref inner_cond(m);
|
||||
get_context().get_var_subst()(condition, subst_vect.size(), subst_vect.c_ptr(), inner_cond);
|
||||
|
||||
relation_mutator_fn * inner_fun = get_manager().mk_filter_interpreted_fn(r.get_inner(), to_app(inner_cond));
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_fn, inner_fun);
|
||||
}
|
||||
|
||||
class sieve_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn {
|
||||
scoped_ptr<relation_intersection_filter_fn> 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<sieve_relation *>(&r) : 0;
|
||||
const sieve_relation * sneg = neg_sieved ? static_cast<const sieve_relation *>(&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<const sieve_relation *>(&r) : 0;
|
||||
const sieve_relation * sneg = neg_sieved ? static_cast<const sieve_relation *>(&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; i<col_cnt; i++) {
|
||||
//if at least one end of an equality is not an inner column, we ignore that equality
|
||||
//(which introduces imprecision)
|
||||
bool r_col_inner = r_sieved && !sr->is_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);
|
||||
}
|
||||
|
||||
|
||||
};
|
198
src/muz/rel/dl_sieve_relation.h
Normal file
198
src/muz/rel/dl_sieve_relation.h
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_sieve_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<bool> 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<bool_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<rel_spec, rel_spec::hash, default_eq<rel_spec> > 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<bool> & inner_columns);
|
||||
void extract_inner_signature(const relation_signature & s, relation_signature & inner_sig);
|
||||
void collect_inner_signature(const relation_signature & s, const svector<bool> & 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<bool> & 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<bool> 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<bool> 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<relation_base> 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<sieve_relation_plugin &>(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_ */
|
||||
|
1246
src/muz/rel/dl_sparse_table.cpp
Normal file
1246
src/muz/rel/dl_sparse_table.cpp
Normal file
File diff suppressed because it is too large
Load diff
480
src/muz/rel/dl_sparse_table.h
Normal file
480
src/muz/rel/dl_sparse_table.h
Normal file
|
@ -0,0 +1,480 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _DL_SPARSE_TABLE_H_
|
||||
#define _DL_SPARSE_TABLE_H_
|
||||
|
||||
#include<iostream>
|
||||
#include<list>
|
||||
#include<utility>
|
||||
|
||||
#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<sparse_table> sp_table_vector;
|
||||
typedef map<table_signature, sp_table_vector *,
|
||||
table_signature::hash, table_signature::eq > 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<char> 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<store_offset, offset_hash_proc, offset_eq_proc> 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<after_last; i+=m_entry_size) {
|
||||
m_data_indexer.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
entry_storage & operator=(const entry_storage & o) {
|
||||
m_data_indexer.reset();
|
||||
m_entry_size = o.m_entry_size;
|
||||
m_unique_part_size = o.m_unique_part_size;
|
||||
m_data_size = o.m_data_size;
|
||||
m_data = o.m_data;
|
||||
m_reserve = o.m_reserve;
|
||||
store_offset after_last=after_last_offset();
|
||||
for(store_offset i=0; i<after_last; i+=m_entry_size) {
|
||||
m_data_indexer.insert(i);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
resize_data(0);
|
||||
m_data_indexer.reset();
|
||||
m_reserve = NO_RESERVE;
|
||||
}
|
||||
|
||||
unsigned entry_size() const { return m_entry_size; }
|
||||
unsigned get_size_estimate_bytes() const;
|
||||
char * get(store_offset ofs) { return m_data.begin()+ofs; }
|
||||
const char * get(store_offset ofs) const
|
||||
{ return const_cast<entry_storage *>(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<uint64>(1)<<length)-1 ),
|
||||
m_write_mask( ~(m_mask<<m_small_offset) ),
|
||||
m_offset(offset),
|
||||
m_length(length) {
|
||||
SASSERT(length<=64);
|
||||
SASSERT(length+m_small_offset<=64);
|
||||
}
|
||||
table_element get(const char * rec) const {
|
||||
const uint64 * ptr = reinterpret_cast<const uint64*>(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<uint64*>(rec+m_big_offset);
|
||||
*ptr&=m_write_mask;
|
||||
*ptr|=val<<m_small_offset;
|
||||
}
|
||||
unsigned const next_ofs() const { return m_offset+m_length; }
|
||||
};
|
||||
class column_layout : public svector<column_info> {
|
||||
|
||||
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<unsigned> key_spec; //sequence of columns in a key
|
||||
typedef svector<table_element> key_value; //values of key columns
|
||||
typedef map<key_spec, key_indexer*, svector_hash_proc<unsigned_hash>,
|
||||
vector_eq_proc<key_spec> > 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<sparse_table_plugin &>(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_ */
|
773
src/muz/rel/dl_table.cpp
Normal file
773
src/muz/rel/dl_table.cpp
Normal file
|
@ -0,0 +1,773 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<const hashtable_table &>(t1);
|
||||
const hashtable_table & ht2 = static_cast<const hashtable_table &>(t2);
|
||||
|
||||
hashtable_table_plugin & plugin = ht1.get_plugin();
|
||||
|
||||
hashtable_table * res = static_cast<hashtable_table *>(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; i<m_joined_col_cnt; i++) {
|
||||
if(row1[m_cols1[i]]!=row2[m_cols2[i]]) {
|
||||
match=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
acc.reset();
|
||||
acc.append(row1);
|
||||
acc.append(row2);
|
||||
res->m_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<unsigned>(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<unsigned>(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]<get_signature()[i]);
|
||||
result += ((unsigned)f[i]) << m_shift[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void bitvector_table::offset2fact(unsigned offset, table_fact& f) const {
|
||||
SASSERT(m_num_cols == f.size());
|
||||
for (unsigned i = 0; i < m_num_cols; ++i) {
|
||||
f[i] = m_mask[i] & (offset >> 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<equivalence_table const&>(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<unsigned>(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<equivalence_table const&>(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<equivalence_table &>(tgt0);
|
||||
if (m_plugin.is_equivalence_table(src)) {
|
||||
mk_union1(tgt, static_cast<equivalence_table const&>(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<const equivalence_table&>(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<const equivalence_table&>(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<table_fact> 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<unsigned>(_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<unsigned>(_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<unsigned>(get_signature()[0]);
|
||||
}
|
||||
|
||||
unsigned equivalence_table::get_size_estimate_bytes() const {
|
||||
if (is_sparse()) return m_sparse->get_size_estimate_bytes();
|
||||
return static_cast<unsigned>(get_signature()[0]);
|
||||
}
|
||||
|
||||
bool equivalence_table::knows_exact_size() const {
|
||||
return (!is_sparse() || m_sparse->knows_exact_size());
|
||||
}
|
||||
|
||||
};
|
||||
|
265
src/muz/rel/dl_table.h
Normal file
265
src/muz/rel/dl_table.h
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_TABLE_H_
|
||||
#define _DL_TABLE_H_
|
||||
|
||||
#include<iostream>
|
||||
#include<list>
|
||||
#include<utility>
|
||||
|
||||
#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<table_fact, svector_hash_proc<table_element_hash>,
|
||||
vector_eq_proc<table_fact> > 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<hashtable_table_plugin &>(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<unsigned>(f[0]); }
|
||||
unsigned second(table_fact const& f) const { return static_cast<unsigned>(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_ */
|
||||
|
193
src/muz/rel/dl_table_plugin.h
Normal file
193
src/muz/rel/dl_table_plugin.h
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table_plugin.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<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<class Traits>
|
||||
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<app> fact;
|
||||
typedef relation_kind kind;
|
||||
};
|
||||
|
||||
typedef tr_infrastructure<relation_traits> 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_traits> table_infrastructure;
|
||||
|
||||
typedef table_infrastructure::plugin_object table_plugin_base;
|
||||
|
||||
class table_base1 : public table_infrastructure::base_ancestor {
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_TABLE_PLUGIN_H_ */
|
||||
|
490
src/muz/rel/dl_table_relation.cpp
Normal file
490
src/muz/rel/dl_table_relation.cpp
Normal file
|
@ -0,0 +1,490 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table_relation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include<string>
|
||||
#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<table_join_fn> 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<table_relation_plugin &>(t1.get_plugin());
|
||||
|
||||
const table_relation & tr1 = static_cast<const table_relation &>(t1);
|
||||
const table_relation & tr2 = static_cast<const table_relation &>(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<const table_relation &>(r1);
|
||||
const table_relation & tr2 = static_cast<const table_relation &>(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<const unsigned *>(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<const table_relation &>(r1);
|
||||
const table_relation & tr2 = static_cast<const table_relation &>(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<table_transformer_fn> 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<table_relation_plugin &>(t.get_plugin());
|
||||
|
||||
const table_relation & tr = static_cast<const table_relation &>(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<const table_relation &>(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<const table_relation &>(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<const table_relation &>(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<const table_relation &>(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<const table_relation &>(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<table_union_fn> 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<table_relation &>(tgt);
|
||||
const table_relation & tr_src = static_cast<const table_relation &>(src);
|
||||
table_relation * tr_delta = static_cast<table_relation *>(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<const table_relation &>(tgt);
|
||||
const table_relation & tr_src = static_cast<const table_relation &>(src);
|
||||
const table_relation * tr_delta = static_cast<const table_relation *>(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<table_mutator_fn> 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<table_relation &>(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<const table_relation &>(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<const table_relation &>(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<const table_relation &>(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<const table_relation &>(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<table_intersection_filter_fn> 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<table_relation &>(r);
|
||||
const table_relation & tr_src = static_cast<const table_relation &>(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<const table_relation &>(r);
|
||||
const table_relation & tr_neg = static_cast<const table_relation &>(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<const table_relation &>(r);
|
||||
const table_relation & tr_neg = static_cast<const table_relation &>(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<arity;i++) {
|
||||
if(i!=0) {
|
||||
out << ',';
|
||||
}
|
||||
|
||||
table_element sym_num = fact[i];
|
||||
|
||||
relation_sort sort = pred.get_domain(i);
|
||||
|
||||
out << ctx.get_argument_name(&pred, i) << '=';
|
||||
ctx.print_constant_name(sort, sym_num, out);
|
||||
out << '(' << sym_num << ')';
|
||||
}
|
||||
out << ")\n";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
133
src/muz/rel/dl_table_relation.h
Normal file
133
src/muz/rel/dl_table_relation.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
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<table_base> 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<table_relation_plugin &>(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_ */
|
||||
|
407
src/muz/rel/dl_vector_relation.h
Normal file
407
src/muz/rel/dl_vector_relation.h
Normal file
|
@ -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<unsigned, unsigned> u_pair;
|
||||
|
||||
template<typename T>
|
||||
class vector_relation_helper {
|
||||
public:
|
||||
static void mk_project_t(T& t, unsigned_vector const& renaming) {}
|
||||
};
|
||||
|
||||
template<typename T, typename Helper = vector_relation_helper<T> >
|
||||
class vector_relation : public relation_base {
|
||||
protected:
|
||||
T m_default;
|
||||
vector<T>* 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<T>)),
|
||||
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<vector_relation&>(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<u_pair, unsigned, pair_hash<unsigned_hash, unsigned_hash>, default_eq<u_pair> > 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<T>* elems = alloc(vector<T>);
|
||||
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
|
||||
|
790
src/muz/rel/karr_relation.cpp
Normal file
790
src/muz/rel/karr_relation.cpp
Normal file
|
@ -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<rational> 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<rational> 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<rational> 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<rational> 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<rational> 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<rational> 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<rational> const& r1, vector<rational> 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<rational> & 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<rational>& 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<rational> 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<karr_relation&>(r);
|
||||
}
|
||||
|
||||
karr_relation const & karr_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<karr_relation const&>(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<karr_relation*>(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<karr_relation*>(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<karr_relation*>(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<rational> 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<rational> 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<rational> 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<rational> 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<rational> 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;
|
||||
}
|
||||
};
|
88
src/muz/rel/karr_relation.h
Normal file
88
src/muz/rel/karr_relation.h
Normal file
|
@ -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_ */
|
||||
|
Loading…
Reference in a new issue