3
0
Fork 0
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:
Nikolaj Bjorner 2013-08-28 21:23:16 -07:00
parent 0d56499e2d
commit c8f9535251
36 changed files with 19354 additions and 0 deletions

490
src/muz/rel/dl_base.cpp Normal file
View 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

File diff suppressed because it is too large Load diff

View 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());
}
};

View 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

View 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;
}
};

View 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_ */

View 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, &param, 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, &param, 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);
}
};

View 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

File diff suppressed because it is too large Load diff

View 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_ */

View 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;
}
};

View 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

View 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;
}
};

View 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_ */

View 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;
}
};

View 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_ */

View 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;
}
};

View 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_ */

View 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);
}
};

View 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_ */

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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_ */

View 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);
}
};

View 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_ */

File diff suppressed because it is too large Load diff

View 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
View 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
View 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_ */

View 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_ */

View 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";
}
}
};

View 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_ */

View 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

View 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;
}
};

View 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_ */