3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-10-18 05:20:26 +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:20:24 -07:00
parent 4597872be8
commit 0d56499e2d
131 changed files with 994 additions and 20069 deletions

View file

@ -0,0 +1,328 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
aig_exporter.cpp
Abstract:
Export AIG files from horn clauses
--*/
#include "aig_exporter.h"
#include "dl_context.h"
#include <set>
namespace datalog {
aig_exporter::aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts) :
m_rules(rules), m_facts(facts), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()),
m_aigm(m), m_next_decl_id(1), m_next_aig_expr_id(2), m_num_and_gates(0),
m_latch_vars(m), m_latch_varsp(m), m_ruleid_var_set(m), m_ruleid_varp_set(m)
{
std::set<func_decl*> predicates;
for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(),
E = m_rules.end_grouped_rules(); I != E; ++I) {
predicates.insert(I->m_key);
}
for (fact_vector::const_iterator I = facts->begin(), E = facts->end(); I != E; ++I) {
predicates.insert(I->first);
}
// reserve pred id = 0 for initalization purposes
unsigned num_preds = (unsigned)predicates.size() + 1;
// poor's man round-up log2
unsigned preds_bitsize = log2(num_preds);
if ((1U << preds_bitsize) < num_preds)
++preds_bitsize;
SASSERT((1U << preds_bitsize) >= num_preds);
for (unsigned i = 0; i < preds_bitsize; ++i) {
m_ruleid_var_set.push_back(m.mk_fresh_const("rule_id", m.mk_bool_sort()));
m_ruleid_varp_set.push_back(m.mk_fresh_const("rule_id_p", m.mk_bool_sort()));
}
}
void aig_exporter::mk_latch_vars(unsigned n) {
for (unsigned i = m_latch_vars.size(); i <= n; ++i) {
m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort()));
m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort()));
}
SASSERT(m_latch_vars.size() > n);
}
expr* aig_exporter::get_latch_var(unsigned i, const expr_ref_vector& vars) {
mk_latch_vars(i);
return vars.get(i);
}
void aig_exporter::assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs) {
unsigned id = 0;
if (decl && !m_decl_id_map.find(decl, id)) {
id = m_next_decl_id++;
SASSERT(id < (1U << vars.size()));
m_decl_id_map.insert(decl, id);
}
for (unsigned i = 0; i < vars.size(); ++i) {
exprs.push_back((id & (1U << i)) ? vars[i] : m.mk_not(vars[i]));
}
}
void aig_exporter::collect_var_substs(substitution& subst, const app *h,
const expr_ref_vector& vars, expr_ref_vector& eqs) {
for (unsigned i = 0; i < h->get_num_args(); ++i) {
expr *arg = h->get_arg(i);
expr *latchvar = get_latch_var(i, vars);
if (is_var(arg)) {
var *v = to_var(arg);
expr_offset othervar;
if (subst.find(v, 0, othervar)) {
eqs.push_back(m.mk_eq(latchvar, othervar.get_expr()));
} else {
subst.insert(v, 0, expr_offset(latchvar, 0));
}
} else {
eqs.push_back(m.mk_eq(latchvar, arg));
}
}
}
void aig_exporter::operator()(std::ostream& out) {
expr_ref_vector transition_function(m), output_preds(m);
var_ref_vector input_vars(m);
rule_counter& vc = m_rm.get_counter();
expr_ref_vector exprs(m);
substitution subst(m);
for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(),
E = m_rules.end_grouped_rules(); I != E; ++I) {
for (rule_vector::iterator II = I->get_value()->begin(),
EE = I->get_value()->end(); II != EE; ++II) {
rule *r = *II;
unsigned numqs = r->get_positive_tail_size();
if (numqs > 1) {
std::cerr << "non-linear clauses not supported\n";
exit(-1);
}
if (numqs != r->get_uninterpreted_tail_size()) {
std::cerr << "negation of queries not supported\n";
exit(-1);
}
exprs.reset();
assert_pred_id(numqs ? r->get_tail(0)->get_decl() : 0, m_ruleid_var_set, exprs);
assert_pred_id(r->get_head()->get_decl(), m_ruleid_varp_set, exprs);
subst.reset();
subst.reserve(1, vc.get_max_rule_var(*r)+1);
if (numqs)
collect_var_substs(subst, r->get_tail(0), m_latch_vars, exprs);
collect_var_substs(subst, r->get_head(), m_latch_varsp, exprs);
for (unsigned i = numqs; i < r->get_tail_size(); ++i) {
expr_ref e(m);
subst.apply(r->get_tail(i), e);
exprs.push_back(e);
}
transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr()));
}
}
// collect table facts
if (m_facts) {
for (fact_vector::const_iterator I = m_facts->begin(), E = m_facts->end(); I != E; ++I) {
exprs.reset();
assert_pred_id(0, m_ruleid_var_set, exprs);
assert_pred_id(I->first, m_ruleid_varp_set, exprs);
for (unsigned i = 0; i < I->second.size(); ++i) {
exprs.push_back(m.mk_eq(get_latch_var(i, m_latch_varsp), I->second[i]));
}
transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr()));
}
}
expr *tr = m.mk_or(transition_function.size(), transition_function.c_ptr());
aig_ref aig = m_aigm.mk_aig(tr);
expr_ref aig_expr(m);
m_aigm.to_formula(aig, aig_expr);
#if 0
std::cout << mk_pp(tr, m) << "\n\n";
std::cout << mk_pp(aig_expr, m) << "\n\n";
#endif
// make rule_id vars latches
for (unsigned i = 0; i < m_ruleid_var_set.size(); ++i) {
m_latch_vars.push_back(m_ruleid_var_set.get(i));
m_latch_varsp.push_back(m_ruleid_varp_set.get(i));
}
// create vars for latches
for (unsigned i = 0; i < m_latch_vars.size(); ++i) {
mk_var(m_latch_vars.get(i));
mk_input_var(m_latch_varsp.get(i));
}
unsigned tr_id = expr_to_aig(aig_expr);
// create latch next state variables: (ite tr varp var)
unsigned_vector latch_varp_ids;
for (unsigned i = 0; i < m_latch_vars.size(); ++i) {
unsigned in_val = mk_and(tr_id, get_var(m_latch_varsp.get(i)));
unsigned latch_val = mk_and(neg(tr_id), get_var(m_latch_vars.get(i)));
latch_varp_ids.push_back(mk_or(in_val, latch_val));
}
m_latch_varsp.reset();
// create output variable (true iff an output predicate is derivable)
unsigned output_id = 0;
{
expr_ref_vector output(m);
const func_decl_set& preds = m_rules.get_output_predicates();
for (func_decl_set::iterator I = preds.begin(), E = preds.end(); I != E; ++I) {
exprs.reset();
assert_pred_id(*I, m_ruleid_var_set, exprs);
output.push_back(m.mk_and(exprs.size(), exprs.c_ptr()));
}
expr *out = m.mk_or(output.size(), output.c_ptr());
aig = m_aigm.mk_aig(out);
m_aigm.to_formula(aig, aig_expr);
output_id = expr_to_aig(aig_expr);
#if 0
std::cout << "output formula\n";
std::cout << mk_pp(out, m) << "\n\n";
std::cout << mk_pp(aig_expr, m) << "\n\n";
#endif
}
// 1) print header
// aag var_index inputs latches outputs andgates
out << "aag " << (m_next_aig_expr_id-1)/2 << ' ' << m_input_vars.size()
<< ' ' << m_latch_vars.size() << " 1 " << m_num_and_gates << '\n';
// 2) print inputs
for (unsigned i = 0; i < m_input_vars.size(); ++i) {
out << m_input_vars[i] << '\n';
}
// 3) print latches
for (unsigned i = 0; i < m_latch_vars.size(); ++i) {
out << get_var(m_latch_vars.get(i)) << ' ' << latch_varp_ids[i] << '\n';
}
// 4) print outputs (just one for now)
out << output_id << '\n';
// 5) print formula
out << m_buffer.str();
}
unsigned aig_exporter::expr_to_aig(const expr *e) {
unsigned id;
if (m_aig_expr_id_map.find(e, id))
return id;
if (is_uninterp_const(e))
return get_var(e);
switch (e->get_kind()) {
case AST_APP: {
const app *a = to_app(e);
switch (a->get_decl_kind()) {
case OP_OR:
SASSERT(a->get_num_args() > 0);
id = expr_to_aig(a->get_arg(0));
for (unsigned i = 1; i < a->get_num_args(); ++i) {
id = mk_or(id, expr_to_aig(a->get_arg(i)));
}
m_aig_expr_id_map.insert(e, id);
return id;
case OP_NOT:
return neg(expr_to_aig(a->get_arg(0)));
case OP_FALSE:
return 0;
case OP_TRUE:
return 1;
}
break;}
case AST_VAR:
return get_var(e);
default:
UNREACHABLE();
}
UNREACHABLE();
return 0;
}
unsigned aig_exporter::neg(unsigned id) const {
return (id % 2) ? (id-1) : (id+1);
}
unsigned aig_exporter::mk_and(unsigned id1, unsigned id2) {
if (id1 > id2)
std::swap(id1, id2);
std::pair<unsigned,unsigned> key(id1, id2);
and_gates_map::const_iterator I = m_and_gates_map.find(key);
if (I != m_and_gates_map.end())
return I->second;
unsigned id = mk_expr_id();
m_buffer << id << ' ' << id1 << ' ' << id2 << '\n';
m_and_gates_map[key] = id;
++m_num_and_gates;
return id;
}
unsigned aig_exporter::mk_or(unsigned id1, unsigned id2) {
return neg(mk_and(neg(id1), neg(id2)));
}
unsigned aig_exporter::get_var(const expr *e) {
unsigned id;
if (m_aig_expr_id_map.find(e, id))
return id;
return mk_input_var(e);
}
unsigned aig_exporter::mk_var(const expr *e) {
SASSERT(!m_aig_expr_id_map.contains(e));
unsigned id = mk_expr_id();
m_aig_expr_id_map.insert(e, id);
return id;
}
unsigned aig_exporter::mk_input_var(const expr *e) {
SASSERT(!m_aig_expr_id_map.contains(e));
unsigned id = mk_expr_id();
m_input_vars.push_back(id);
if (e)
m_aig_expr_id_map.insert(e, id);
return id;
}
unsigned aig_exporter::mk_expr_id() {
unsigned id = m_next_aig_expr_id;
m_next_aig_expr_id += 2;
return id;
}
}

View file

@ -0,0 +1,68 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
aig_exporter.h
Abstract:
Export AIG files from horn clauses
--*/
#ifndef _AIG_EXPORTER_H_
#define _AIG_EXPORTER_H_
#include "aig.h"
#include "dl_rule_set.h"
#include <map>
#include <sstream>
#include "rel_context.h"
namespace datalog {
class aig_exporter {
public:
aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = 0);
void operator()(std::ostream& out);
private:
typedef obj_map<func_decl, unsigned> decl_id_map;
typedef obj_map<const expr, unsigned> aig_expr_id_map;
typedef std::map<std::pair<unsigned,unsigned>, unsigned> and_gates_map;
const rule_set& m_rules;
const fact_vector *m_facts;
ast_manager& m;
rule_manager& m_rm;
aig_manager m_aigm;
decl_id_map m_decl_id_map;
unsigned m_next_decl_id;
aig_expr_id_map m_aig_expr_id_map;
unsigned m_next_aig_expr_id;
and_gates_map m_and_gates_map;
unsigned m_num_and_gates;
expr_ref_vector m_latch_vars, m_latch_varsp;
expr_ref_vector m_ruleid_var_set, m_ruleid_varp_set;
unsigned_vector m_input_vars;
std::stringstream m_buffer;
void mk_latch_vars(unsigned n);
expr* get_latch_var(unsigned i, const expr_ref_vector& vars);
void assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs);
void collect_var_substs(substitution& subst, const app *h,
const expr_ref_vector& vars, expr_ref_vector& eqs);
unsigned expr_to_aig(const expr *e);
unsigned neg(unsigned id) const;
unsigned mk_and(unsigned id1, unsigned id2);
unsigned mk_or(unsigned id1, unsigned id2);
unsigned get_var(const expr *e);
unsigned mk_var(const expr *e);
unsigned mk_input_var(const expr *e = 0);
unsigned mk_expr_id();
};
}
#endif

1345
src/muz/rel/dl_compiler.cpp Normal file

File diff suppressed because it is too large Load diff

282
src/muz/rel/dl_compiler.h Normal file
View file

@ -0,0 +1,282 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_compiler.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-14.
Revision History:
--*/
#ifndef _DL_COMPILER_H_
#define _DL_COMPILER_H_
#include<iostream>
#include<list>
#include<utility>
#include "ast.h"
#include "hashtable.h"
#include "map.h"
#include "obj_pair_hashtable.h"
#include "ref_vector.h"
#include "vector.h"
#include "dl_base.h"
#include "dl_context.h"
#include "dl_instruction.h"
namespace datalog {
class compiler {
typedef instruction::reg_idx reg_idx;
typedef hashtable<unsigned, u_hash, u_eq> int_set;
typedef u_map<unsigned> int2int;
typedef u_map<unsigned_vector> int2ints;
typedef obj_map<func_decl, reg_idx> pred2idx;
typedef unsigned_vector var_vector;
typedef ptr_vector<func_decl> func_decl_vector;
enum assembling_column_kind {
ACK_BOUND_VAR,
ACK_UNBOUND_VAR,
ACK_CONSTANT
};
/**
\brief instruction for assembling head relation from a joint relation built
from rule evaluation.
ACK_BOUND_VAR(source_column) - encodes that the column contains a variable
bound in the body.
ACK_UNBOUND_VAR(var_index) - encodes that the column contains a variable that
is unbound (by the corresponding rule body),
var_index is the de-Brujin index (var->get_idx())
of the variable associated with the column.
ACK_CONSTANT(constant) - encodes that the column contains the constant.
Examples:
P(x) :- Q(x,y), Q(y,z)
The variables in the body relation are [x, y, y, z] indexed as 0, 1, 2, 3.
The variable x gets the instruction ACK_BOUND_VAR(0)
P(u,x) :- Q(x,y), Q(y,z)
The variable u gets the instruction ACK_UNBOUND_VAR(#0)
P(1, x) :- Q(x,y), Q(y,z)
The instruction for column 0 is ACK_CONSTANT(1)
*/
struct assembling_column_info {
relation_sort domain; // domain of the column
assembling_column_kind kind; // "instruction" tag
unsigned source_column; // for ACK_BOUND_VAR
unsigned var_index; // for ACK_UNBOUND_VAR
relation_element constant; // for ACK_CONSTANT
};
class instruction_observer : public instruction_block::instruction_observer {
compiler & m_parent;
rule * m_current;
public:
instruction_observer(compiler & parent) : m_parent(parent), m_current(0) {}
void start_rule(rule * r) { SASSERT(!m_current); m_current=r; }
void finish_rule() { m_current = 0; }
virtual void notify(instruction * i) {
if(m_current) {
i->set_accounting_parent_object(m_parent.m_context, m_current);
}
}
};
context & m_context;
rule_set const & m_rule_set;
/**
Invariant: the \c m_top_level_code never contains the loop that is being constructed,
so instruction that need to be executed before the loop can be pushed into it.
*/
instruction_block & m_top_level_code;
pred2idx m_pred_regs;
reg_idx m_new_reg;
vector<relation_signature> m_reg_signatures;
obj_pair_map<sort, app, reg_idx> m_constant_registers;
obj_pair_map<sort, decl, reg_idx> m_total_registers;
obj_map<decl, reg_idx> m_empty_tables_registers;
instruction_observer m_instruction_observer;
/**
If true, the union operation on the underlying structure only provides the information
whether the updated relation has changed or not. In this case we do not get anything
from using delta relations at position of input relations in the saturation loop, so we
would not do it.
*/
bool all_or_nothing_deltas() const { return m_context.all_or_nothing_deltas(); }
/**
If true, we compile the saturation loops in a way that allows us to use widening.
*/
bool compile_with_widening() const { return m_context.compile_with_widening(); }
reg_idx get_fresh_register(const relation_signature & sig);
reg_idx get_single_column_register(const relation_sort & s);
/**
\brief Allocate registers for predicates in \c pred and add them into the \c regs map.
\c regs must not already contain any predicate from \c preds.
*/
void get_fresh_registers(const func_decl_set & preds, pred2idx & regs);
void make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result,
instruction_block & acc);
void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars,
const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc);
void make_filter_interpreted_and_project(reg_idx src, app_ref & cond,
const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc);
void make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col,
reg_idx & result, instruction_block & acc);
/**
\brief Create add an union or widen operation and put it into \c acc.
*/
void make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widening, instruction_block & acc);
void make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols,
reg_idx & result, instruction_block & acc);
void make_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle,
reg_idx & result, instruction_block & acc);
void make_clone(reg_idx src, reg_idx & result, instruction_block & acc);
/**
\brief Into \c acc add code that will assemble columns of a relation according to description
in \c acis0. The source for bound variables is the table in register \c src.
If \c src is \c execution_context::void_register, it is assumed to be a full relation
with empty signature.
*/
void make_assembling_code(rule* compiled_rule, func_decl* head_pred, reg_idx src, const svector<assembling_column_info> & acis0,
reg_idx & result, bool & dealloc, instruction_block & acc);
void make_dealloc_non_void(reg_idx r, instruction_block & acc);
void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort & s, const relation_element & val,
reg_idx & result, bool & dealloc, instruction_block & acc);
void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result,
bool & dealloc, instruction_block & acc);
void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result,
instruction_block & acc);
void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr,
bool & dealloc, instruction_block& acc);
void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, instruction_block & acc);
void ensure_predicate_loaded(func_decl * pred, instruction_block & acc);
/**
\brief For rule \c r with two positive uninterpreted predicates put into \c res indexes of
local variables in a table that results from join of the two positive predicates.
Used to get input for the "project" part of join-project.
*/
void get_local_indexes_for_projection(rule * r, unsigned_vector & res);
void get_local_indexes_for_projection(app * t, var_counter & globals, unsigned ofs,
unsigned_vector & res);
/**
\brief Into \c acc add instructions that will add new facts following from the rule into
\c head_reg, and add the facts that were not in \c head_reg before into \c delta_reg.
*/
void compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs,
reg_idx delta_reg, bool use_widening, instruction_block & acc);
void compile_rule_evaluation(rule * r, const pred2idx * input_deltas, reg_idx output_delta,
bool use_widening, instruction_block & acc);
/**
\brief Generate code to evaluate rules corresponding to predicates in \c head_preds.
The rules are evaluated in the order their heads appear in the \c head_preds vector.
*/
void compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds,
const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc);
/**
\brief Generate code to evaluate predicates in a stratum based on their non-recursive rules.
*/
void compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds,
const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc);
void make_inloop_delta_transition(const pred2idx & global_head_deltas,
const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc);
void compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds,
const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas,
const pred2idx & local_deltas, instruction_block & acc);
void compile_dependent_rules(const func_decl_set & head_preds,
const pred2idx * input_deltas, const pred2idx & output_deltas,
bool add_saturation_marks, instruction_block & acc);
void detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds,
func_decl_set & global_deltas);
/**
Return true if there is no dependency inside the \c rules stratum.
The head predicates in stratum must be strongly connected by dependency.
*/
bool is_nonrecursive_stratum(const func_decl_set & preds) const;
/**
input_deltas==0 --> we use the actual content of relations instead of deltas
*/
void compile_nonrecursive_stratum(const func_decl_set & preds,
const pred2idx * input_deltas, const pred2idx & output_deltas,
bool add_saturation_marks, instruction_block & acc);
void compile_strats(const rule_stratifier & stratifier,
const pred2idx * input_deltas, const pred2idx & output_deltas,
bool add_saturation_marks, instruction_block & acc);
bool all_saturated(const func_decl_set & preds) const;
void reset();
explicit compiler(context & ctx, rule_set const & rules, instruction_block & top_level_code)
: m_context(ctx),
m_rule_set(rules),
m_top_level_code(top_level_code),
m_instruction_observer(*this) {}
/**
\brief Compile \c rules in to pseudocode.
Instructions to load data and perform computations put into \c execution_code
*/
void do_compilation(instruction_block & execution_code,
instruction_block & termination_code);
public:
static void compile(context & ctx, rule_set const & rules, instruction_block & execution_code,
instruction_block & termination_code) {
compiler(ctx, rules, execution_code)
.do_compilation(execution_code, termination_code);
}
};
};
#endif /* _DL_COMPILER_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,349 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_instruction.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-14.
Revision History:
--*/
#ifndef _DL_INSTRUCTION_H_
#define _DL_INSTRUCTION_H_
#include<iostream>
#include<string>
#include<utility>
#include "ast.h"
#include "vector.h"
#include "dl_base.h"
#include "dl_costs.h"
#include "dl_context.h"
namespace datalog {
class execution_context;
class instruction_block;
class rel_context;
inline void check_overflow(unsigned i) {
if (i == UINT_MAX) {
throw out_of_memory_error();
}
}
// -----------------------------------
//
// execution_context
//
// -----------------------------------
class execution_context {
public:
typedef relation_base * reg_type;
typedef vector<reg_type> reg_vector;
typedef unsigned reg_idx;
/**
\brief A register number that should never be referenced to. Can stand e.g. for a tail
table in a rule with no tail.
*/
static const reg_idx void_register = UINT_MAX;
private:
typedef u_map<std::string> reg_annotations;
context & m_context;
reg_vector m_registers;
reg_annotations m_reg_annotation;
stopwatch * m_stopwatch;
unsigned m_timelimit_ms; //zero means no limit
/**
\brief If true, after every operation that may result in an empty relation, a check
for emptiness will be performed, and if a relation is empty, it will be deleted
and replaced by zero. This allows us to avoid performing operations that would have
no effect due to relation emptiness, but if the check for emptiness is expensive, its
cost may overcome the gains.
*/
bool m_eager_emptiness_checking;
public:
execution_context(context & context);
~execution_context();
void reset();
rel_context & get_rel_context();
void set_timelimit(unsigned time_in_ms);
void reset_timelimit();
bool should_terminate();
bool eager_emptiness_checking() const { return m_eager_emptiness_checking; }
/**
\brief Return reference to \c i -th register that contains pointer to a relation.
If register contains zero, it should be treated as if it contains an empty relation.
*/
reg_type reg(reg_idx i) const {
if (i >= m_registers.size()) {
return 0;
}
return m_registers[i];
}
/**
\brief Return value of the register and assign zero into it place.
*/
reg_type release_reg(reg_idx i) {
SASSERT(i < m_registers.size());
SASSERT(m_registers[i]);
reg_type res = m_registers[i];
m_registers[i] = 0;
return res;
}
/**
\brief Assign value to a register. If it was non-empty, deallocate its content first.
*/
void set_reg(reg_idx i, reg_type val) {
if (i >= m_registers.size()) {
check_overflow(i);
m_registers.resize(i+1,0);
}
if (m_registers[i]) {
m_registers[i]->deallocate();
}
m_registers[i] = val;
}
void make_empty(reg_idx i) {
if (reg(i)) {
set_reg(i, 0);
}
}
unsigned register_count() const {
return m_registers.size();
}
bool get_register_annotation(reg_idx reg, std::string & res) const {
return m_reg_annotation.find(reg, res);
}
void set_register_annotation(reg_idx reg, std::string str) {
m_reg_annotation.insert(reg, str);
}
void report_big_relations(unsigned threshold, std::ostream & out) const;
};
// -----------------------------------
//
// instruction
//
// -----------------------------------
/**
\brief Base class for instructions used in datalog saturation.
A relation in a register is owned by that register and is not referenced from anywhere else.
Instructions that move context of one register to another leave the source register empty
and deallocate the previous content of the target register.
*/
class instruction : public accounted_object {
typedef u_map<base_relation_fn *> fn_cache;
fn_cache m_fn_cache;
static const int rk_encode_base = 1024;
inline static unsigned encode_kind(family_id k)
{ SASSERT(k<rk_encode_base); return k; }
inline static unsigned encode_kinds(family_id k1, family_id k2)
{ SASSERT(k1<rk_encode_base && k2<rk_encode_base); return (k1+1)*rk_encode_base + k2; }
inline static unsigned encode_kinds(family_id k1, family_id k2, family_id k3) {
SASSERT(k1<rk_encode_base && k2<rk_encode_base && k3<rk_encode_base);
return ((k1+1)*rk_encode_base + k2)*rk_encode_base + k3;
}
protected:
friend class instruction_block;
template<typename T>
bool find_fn(const relation_base & r, T* & result) const
{ return m_fn_cache.find(encode_kind(r.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
template<typename T>
bool find_fn(const relation_base & r1, const relation_base & r2, T*& result) const
{ return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
template<typename T>
bool find_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3, T * & result) const
{ return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
void store_fn(const relation_base & r, base_relation_fn * fn)
{ m_fn_cache.insert(encode_kind(r.get_kind()), fn); }
void store_fn(const relation_base & r1, const relation_base & r2, base_relation_fn * fn)
{ m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind()), fn); }
void store_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3,
base_relation_fn * fn)
{ m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), fn); }
/**
Process not only costs associated with the current instruction, but in case of
block instructions, process also costs associated with its child instructions.
*/
virtual void process_all_costs();
/**
\brief Output one line header of the current instruction.
The newline character at the end should not be printed.
*/
virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const {
out << "<instruction>";
}
/**
\brief If relevant, output the body of the current instruction.
Each line must be prepended by \c indentation and ended by a newline character.
*/
virtual void display_body_impl(rel_context const & ctx, std::ostream & out, std::string indentation) const {}
public:
typedef execution_context::reg_type reg_type;
typedef execution_context::reg_idx reg_idx;
virtual ~instruction();
virtual bool perform(execution_context & ctx) = 0;
virtual void make_annotations(execution_context & ctx) = 0;
void display(rel_context_base const& ctx, std::ostream & out) const {
display_indented(ctx, out, "");
}
void display_indented(rel_context_base const & ctx, std::ostream & out, std::string indentation) const;
static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt);
/**
\brief The store operation moves the relation from a register into the context. The register
is set to zero after the operation.
*/
static instruction * mk_store(ast_manager & m, func_decl * pred, reg_idx src);
static instruction * mk_dealloc(reg_idx reg); //maybe not necessary
static instruction * mk_clone(reg_idx from, reg_idx to);
static instruction * mk_move(reg_idx from, reg_idx to);
/**
\brief Return instruction that performs \c body as long as at least one register
in \c control_regs contains non-empty relation.
The instruction object takes over the ownership of the \c body object.
*/
static instruction * mk_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs,
instruction_block * body);
static instruction * mk_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2, reg_idx result);
static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col);
static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols);
static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition);
static instruction * mk_filter_interpreted_and_project(reg_idx src, app_ref & condition,
unsigned col_cnt, const unsigned * removed_cols, reg_idx result);
static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta);
static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta);
static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols,
reg_idx tgt);
static instruction * mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt,
const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
const unsigned * removed_cols, reg_idx result);
static instruction * mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle,
reg_idx tgt);
static instruction * mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2);
static instruction * mk_select_equal_and_project(ast_manager & m, reg_idx src,
const relation_element & value, unsigned col, reg_idx result);
static instruction * mk_unary_singleton(ast_manager & m, func_decl* pred, const relation_sort & s, const relation_element & val, reg_idx tgt);
static instruction * mk_total(const relation_signature & sig, func_decl* pred, reg_idx tgt);
/**
\brief The mark_saturated instruction marks a relation as saturated, so that after
next restart it does not have to be part of the saturation again.
*/
static instruction * mk_mark_saturated(ast_manager & m, func_decl * pred);
static instruction * mk_assert_signature(const relation_signature & s, reg_idx tgt);
};
// -----------------------------------
//
// instruction_block
//
// -----------------------------------
class instruction_block {
public:
struct instruction_observer {
virtual ~instruction_observer() {}
virtual void notify(instruction * i) {}
};
private:
typedef ptr_vector<instruction> instr_seq_type;
instr_seq_type m_data;
instruction_observer* m_observer;
public:
instruction_block() : m_observer(0) {}
~instruction_block();
void reset();
void push_back(instruction * i) {
m_data.push_back(i);
if(m_observer) {
m_observer->notify(i);
}
}
void set_observer(instruction_observer * o) {
SASSERT(o==0 || m_observer==0);
m_observer = o;
}
/**
\brief Perform instructions in the block. If the run was interrupted before completion,
return false; otherwise return true.
The execution can terminate before completion if the function
\c execution_context::should_terminate() returns true.
*/
bool perform(execution_context & ctx) const;
void process_all_costs();
void make_annotations(execution_context & ctx);
void display(rel_context_base const & ctx, std::ostream & out) const {
display_indented(ctx, out, "");
}
void display_indented(rel_context_base const & ctx, std::ostream & out, std::string indentation) const;
};
};
#endif /* _DL_INSTRUCTION_H_ */

593
src/muz/rel/rel_context.cpp Normal file
View file

@ -0,0 +1,593 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
rel_context.cpp
Abstract:
context for relational datalog engine.
Author:
Nikolaj Bjorner (nbjorner) 2012-12-3.
Revision History:
Extracted from dl_context
--*/
#include"rel_context.h"
#include"dl_context.h"
#include"dl_compiler.h"
#include"dl_instruction.h"
#include"dl_mk_explanations.h"
#include"dl_mk_magic_sets.h"
#include"dl_product_relation.h"
#include"dl_bound_relation.h"
#include"dl_interval_relation.h"
#include"karr_relation.h"
#include"dl_finite_product_relation.h"
#include"dl_sparse_table.h"
#include"dl_table.h"
#include"dl_table_relation.h"
#include"aig_exporter.h"
#include"dl_mk_simple_joins.h"
#include"dl_mk_similarity_compressor.h"
#include"dl_mk_unbound_compressor.h"
#include"dl_mk_subsumption_checker.h"
#include"dl_mk_partial_equiv.h"
#include"dl_mk_coi_filter.h"
#include"dl_mk_filter_rules.h"
#include"dl_mk_rule_inliner.h"
#include"dl_mk_interp_tail_simplifier.h"
#include"dl_mk_bit_blast.h"
namespace datalog {
class rel_context::scoped_query {
context& m_ctx;
rule_set m_rules;
decl_set m_preds;
bool m_was_closed;
public:
scoped_query(context& ctx):
m_ctx(ctx),
m_rules(ctx.get_rules()),
m_preds(ctx.get_predicates()),
m_was_closed(ctx.is_closed())
{
if (m_was_closed) {
ctx.reopen();
}
}
~scoped_query() {
m_ctx.reopen();
m_ctx.restrict_predicates(m_preds);
m_ctx.replace_rules(m_rules);
if (m_was_closed) {
m_ctx.close();
}
}
void reset() {
m_ctx.reopen();
m_ctx.restrict_predicates(m_preds);
m_ctx.replace_rules(m_rules);
m_ctx.close();
}
};
rel_context::rel_context(context& ctx)
: rel_context_base(ctx.get_manager(), "datalog"),
m_context(ctx),
m(ctx.get_manager()),
m_rmanager(ctx),
m_answer(m),
m_last_result_relation(0),
m_ectx(ctx) {
// register plugins for builtin tables
get_rmanager().register_plugin(alloc(sparse_table_plugin, get_rmanager()));
get_rmanager().register_plugin(alloc(hashtable_table_plugin, get_rmanager()));
get_rmanager().register_plugin(alloc(bitvector_table_plugin, get_rmanager()));
get_rmanager().register_plugin(alloc(equivalence_table_plugin, get_rmanager()));
// register plugins for builtin relations
get_rmanager().register_plugin(alloc(bound_relation_plugin, get_rmanager()));
get_rmanager().register_plugin(alloc(interval_relation_plugin, get_rmanager()));
get_rmanager().register_plugin(alloc(karr_relation_plugin, get_rmanager()));
}
rel_context::~rel_context() {
if (m_last_result_relation) {
m_last_result_relation->deallocate();
m_last_result_relation = 0;
}
}
lbool rel_context::saturate() {
scoped_query sq(m_context);
return saturate(sq);
}
lbool rel_context::saturate(scoped_query& sq) {
m_context.ensure_closed();
bool time_limit = m_context.soft_timeout()!=0;
unsigned remaining_time_limit = m_context.soft_timeout();
unsigned restart_time = m_context.initial_restart_timeout();
instruction_block termination_code;
lbool result;
TRACE("dl", m_context.display(tout););
while (true) {
m_ectx.reset();
m_code.reset();
termination_code.reset();
m_context.ensure_closed();
transform_rules();
if (m_context.canceled()) {
result = l_undef;
break;
}
TRACE("dl", m_context.display(tout););
if (m_context.get_params().dump_aig().size()) {
const char *filename = static_cast<const char*>(m_context.get_params().dump_aig().c_ptr());
aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts);
std::ofstream strm(filename, std::ios_base::binary);
aig(strm);
exit(0);
}
compiler::compile(m_context, m_context.get_rules(), m_code, termination_code);
TRACE("dl", m_code.display(*this, tout); );
bool timeout_after_this_round = time_limit && (restart_time==0 || remaining_time_limit<=restart_time);
if (time_limit || restart_time!=0) {
unsigned timeout = time_limit ? (restart_time!=0) ?
std::min(remaining_time_limit, restart_time)
: remaining_time_limit : restart_time;
m_ectx.set_timelimit(timeout);
}
bool early_termination = !m_code.perform(m_ectx);
m_ectx.reset_timelimit();
VERIFY( termination_code.perform(m_ectx) || m_context.canceled());
m_code.process_all_costs();
IF_VERBOSE(10, m_ectx.report_big_relations(1000, verbose_stream()););
if (m_context.canceled()) {
result = l_undef;
break;
}
if (!early_termination) {
m_context.set_status(OK);
result = l_true;
break;
}
if (memory::above_high_watermark()) {
m_context.set_status(MEMOUT);
result = l_undef;
break;
}
if (timeout_after_this_round) {
m_context.set_status(TIMEOUT);
result = l_undef;
break;
}
SASSERT(restart_time != 0);
if (time_limit) {
SASSERT(remaining_time_limit>restart_time);
remaining_time_limit -= restart_time;
}
uint64 new_restart_time = static_cast<uint64>(restart_time)*m_context.initial_restart_timeout();
if (new_restart_time > UINT_MAX) {
restart_time = UINT_MAX;
}
else {
restart_time = static_cast<unsigned>(new_restart_time);
}
sq.reset();
}
m_context.record_transformed_rules();
TRACE("dl", display_profile(tout););
return result;
}
lbool rel_context::query(unsigned num_rels, func_decl * const* rels) {
get_rmanager().reset_saturated_marks();
scoped_query _scoped_query(m_context);
for (unsigned i = 0; i < num_rels; ++i) {
m_context.set_output_predicate(rels[i]);
}
m_context.close();
reset_negated_tables();
lbool res = saturate(_scoped_query);
switch(res) {
case l_true: {
expr_ref_vector ans(m);
expr_ref e(m);
bool some_non_empty = num_rels == 0;
bool is_approx = false;
for (unsigned i = 0; i < num_rels; ++i) {
func_decl* q = m_context.get_rules().get_pred(rels[i]);
relation_base& rel = get_relation(q);
if (!rel.empty()) {
some_non_empty = true;
}
if (!rel.is_precise()) {
is_approx = true;
}
rel.to_formula(e);
ans.push_back(e);
}
SASSERT(!m_last_result_relation);
if (some_non_empty) {
m_answer = m.mk_and(ans.size(), ans.c_ptr());
if (is_approx) {
res = l_undef;
m_context.set_status(APPROX);
}
}
else {
m_answer = m.mk_false();
res = l_false;
}
break;
}
case l_false:
m_answer = m.mk_false();
break;
case l_undef:
break;
}
return res;
}
void rel_context::transform_rules() {
rule_transformer transf(m_context);
transf.register_plugin(alloc(mk_coi_filter, m_context));
transf.register_plugin(alloc(mk_filter_rules, m_context));
transf.register_plugin(alloc(mk_simple_joins, m_context));
if (m_context.unbound_compressor()) {
transf.register_plugin(alloc(mk_unbound_compressor, m_context));
}
if (m_context.similarity_compressor()) {
transf.register_plugin(alloc(mk_similarity_compressor, m_context));
}
transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context));
transf.register_plugin(alloc(mk_rule_inliner, m_context));
transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context));
if (m_context.get_params().bit_blast()) {
transf.register_plugin(alloc(mk_bit_blast, m_context, 22000));
transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context, 21000));
}
m_context.transform_rules(transf);
}
bool rel_context::try_get_size(func_decl* p, unsigned& rel_size) const {
relation_base* rb = try_get_relation(p);
if (rb && rb->knows_exact_size()) {
rel_size = rb->get_size_estimate_rows();
return true;
}
else {
return false;
}
}
lbool rel_context::query(expr* query) {
get_rmanager().reset_saturated_marks();
scoped_query _scoped_query(m_context);
rule_manager& rm = m_context.get_rule_manager();
func_decl_ref query_pred(m);
try {
query_pred = rm.mk_query(query, m_context.get_rules());
}
catch (default_exception& exn) {
m_context.set_status(INPUT_ERROR);
throw exn;
}
m_context.close();
reset_negated_tables();
if (m_context.generate_explanations()) {
m_context.transform_rules(alloc(mk_explanations, m_context));
}
query_pred = m_context.get_rules().get_pred(query_pred);
if (m_context.magic_sets_for_queries()) {
m_context.transform_rules(alloc(mk_magic_sets, m_context, query_pred));
query_pred = m_context.get_rules().get_pred(query_pred);
}
lbool res = saturate(_scoped_query);
query_pred = m_context.get_rules().get_pred(query_pred);
if (res != l_undef) {
m_last_result_relation = get_relation(query_pred).clone();
if (m_last_result_relation->empty()) {
res = l_false;
m_answer = m.mk_false();
}
else {
m_last_result_relation->to_formula(m_answer);
if (!m_last_result_relation->is_precise()) {
m_context.set_status(APPROX);
res = l_undef;
}
}
}
return res;
}
void rel_context::reset_negated_tables() {
rule_set::pred_set_vector const & pred_sets = m_context.get_rules().get_strats();
bool non_empty = false;
for (unsigned i = 1; i < pred_sets.size(); ++i) {
func_decl_set::iterator it = pred_sets[i]->begin(), end = pred_sets[i]->end();
for (; it != end; ++it) {
func_decl* pred = *it;
relation_base & rel = get_relation(pred);
if (!rel.empty()) {
non_empty = true;
break;
}
}
}
if (!non_empty) {
return;
}
// collect predicates that depend on negation.
func_decl_set depends_on_negation;
for (unsigned i = 1; i < pred_sets.size(); ++i) {
bool change = true;
while (change) {
change = false;
func_decl_set::iterator it = pred_sets[i]->begin(), end = pred_sets[i]->end();
for (; it != end; ++it) {
func_decl* pred = *it;
if (depends_on_negation.contains(pred)) {
continue;
}
rule_vector const& rules = m_context.get_rules().get_predicate_rules(pred);
bool inserted = false;
for (unsigned j = 0; !inserted && j < rules.size(); ++j) {
rule* r = rules[j];
unsigned psz = r->get_positive_tail_size();
unsigned tsz = r->get_uninterpreted_tail_size();
if (psz < tsz) {
depends_on_negation.insert(pred);
change = true;
inserted = true;
}
for (unsigned k = 0; !inserted && k < tsz; ++k) {
func_decl* tail_decl = r->get_tail(k)->get_decl();
if (depends_on_negation.contains(tail_decl)) {
depends_on_negation.insert(pred);
change = true;
inserted = true;
}
}
}
}
}
}
func_decl_set::iterator it = depends_on_negation.begin(), end = depends_on_negation.end();
for (; it != end; ++it) {
func_decl* pred = *it;
relation_base & rel = get_relation(pred);
if (!rel.empty()) {
TRACE("dl", tout << "Resetting: " << mk_ismt2_pp(pred, m) << "\n";);
rel.reset();
}
}
}
void rel_context::restrict_predicates(func_decl_set const& predicates) {
get_rmanager().restrict_predicates(predicates);
}
relation_base & rel_context::get_relation(func_decl * pred) { return get_rmanager().get_relation(pred); }
relation_base * rel_context::try_get_relation(func_decl * pred) const { return get_rmanager().try_get_relation(pred); }
expr_ref rel_context::try_get_formula(func_decl* p) const {
expr_ref result(m);
relation_base* rb = try_get_relation(p);
if (rb) {
rb->to_formula(result);
}
return result;
}
bool rel_context::is_empty_relation(func_decl* pred) const {
relation_base* rb = try_get_relation(pred);
return !rb || rb->empty();
}
relation_manager & rel_context::get_rmanager() { return m_rmanager; }
const relation_manager & rel_context::get_rmanager() const { return m_rmanager; }
bool rel_context::output_profile() const { return m_context.output_profile(); }
void rel_context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
symbol const * relation_names) {
TRACE("dl",
tout << pred->get_name() << ": ";
for (unsigned i = 0; i < relation_name_cnt; ++i) {
tout << relation_names[i] << " ";
}
tout << "\n";
);
relation_manager & rmgr = get_rmanager();
family_id target_kind = null_family_id;
switch (relation_name_cnt) {
case 0:
return;
case 1:
target_kind = get_ordinary_relation_plugin(relation_names[0]).get_kind();
break;
default: {
svector<family_id> rel_kinds; // kinds of plugins that are not table plugins
family_id rel_kind; // the aggregate kind of non-table plugins
for (unsigned i = 0; i < relation_name_cnt; i++) {
relation_plugin & p = get_ordinary_relation_plugin(relation_names[i]);
rel_kinds.push_back(p.get_kind());
}
if (rel_kinds.size() == 1) {
rel_kind = rel_kinds[0];
}
else {
relation_signature rel_sig;
rmgr.from_predicate(pred, rel_sig);
product_relation_plugin & prod_plugin = product_relation_plugin::get_plugin(rmgr);
rel_kind = prod_plugin.get_relation_kind(rel_sig, rel_kinds);
}
target_kind = rel_kind;
break;
}
}
SASSERT(target_kind != null_family_id);
get_rmanager().set_predicate_kind(pred, target_kind);
}
void rel_context::set_cancel(bool f) {
get_rmanager().set_cancel(f);
}
relation_plugin & rel_context::get_ordinary_relation_plugin(symbol relation_name) {
relation_plugin * plugin = get_rmanager().get_relation_plugin(relation_name);
if (!plugin) {
std::stringstream sstm;
sstm << "relation plugin " << relation_name << " does not exist";
throw default_exception(sstm.str());
}
if (plugin->is_product_relation()) {
throw default_exception("cannot request product relation directly");
}
if (plugin->is_sieve_relation()) {
throw default_exception("cannot request sieve relation directly");
}
if (plugin->is_finite_product_relation()) {
throw default_exception("cannot request finite product relation directly");
}
return *plugin;
}
bool rel_context::result_contains_fact(relation_fact const& f) {
SASSERT(m_last_result_relation);
return m_last_result_relation->contains_fact(f);
}
void rel_context::reset_tables() {
get_rmanager().reset_saturated_marks();
rule_set::decl2rules::iterator it = m_context.get_rules().begin_grouped_rules();
rule_set::decl2rules::iterator end = m_context.get_rules().end_grouped_rules();
for (; it != end; ++it) {
func_decl* p = it->m_key;
relation_base & rel = get_relation(p);
rel.reset();
}
for (unsigned i = 0; i < m_table_facts.size(); ++i) {
func_decl* pred = m_table_facts[i].first;
relation_fact const& fact = m_table_facts[i].second;
get_relation(pred).add_fact(fact);
}
}
void rel_context::add_fact(func_decl* pred, relation_fact const& fact) {
get_rmanager().reset_saturated_marks();
get_relation(pred).add_fact(fact);
m_table_facts.push_back(std::make_pair(pred, fact));
}
void rel_context::add_fact(func_decl* pred, table_fact const& fact) {
get_rmanager().reset_saturated_marks();
relation_base & rel0 = get_relation(pred);
if (rel0.from_table()) {
table_relation & rel = static_cast<table_relation &>(rel0);
rel.add_table_fact(fact);
// TODO: table facts?
}
else {
relation_fact rfact(m);
for (unsigned i = 0; i < fact.size(); ++i) {
rfact.push_back(m_context.get_decl_util().mk_numeral(fact[i], pred->get_domain()[i]));
}
add_fact(pred, rfact);
}
}
bool rel_context::has_facts(func_decl * pred) const {
relation_base* r = try_get_relation(pred);
return r && !r->empty();
}
void rel_context::store_relation(func_decl * pred, relation_base * rel) {
get_rmanager().store_relation(pred, rel);
}
void rel_context::inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) {
if (orig_pred) {
family_id target_kind = get_rmanager().get_requested_predicate_kind(orig_pred);
if (target_kind != null_family_id) {
get_rmanager().set_predicate_kind(new_pred, target_kind);
}
}
}
void rel_context::display_output_facts(rule_set const& rules, std::ostream & out) const {
get_rmanager().display_output_tables(rules, out);
}
void rel_context::display_facts(std::ostream& out) const {
get_rmanager().display(out);
}
void rel_context::display_profile(std::ostream& out) {
m_code.make_annotations(m_ectx);
m_code.process_all_costs();
out << "\n--------------\n";
out << "Instructions\n";
m_code.display(*this, out);
out << "\n--------------\n";
out << "Big relations\n";
m_ectx.report_big_relations(1000, out);
get_rmanager().display_relation_sizes(out);
}
};

130
src/muz/rel/rel_context.h Normal file
View file

@ -0,0 +1,130 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
rel_context.h
Abstract:
context for relational datalog engine.
Author:
Nikolaj Bjorner (nbjorner) 2012-12-3.
Revision History:
Extracted from dl_context
--*/
#ifndef _REL_CONTEXT_H_
#define _REL_CONTEXT_H_
#include "ast.h"
#include "dl_relation_manager.h"
#include "dl_instruction.h"
#include "dl_engine_base.h"
#include "dl_context.h"
#include "lbool.h"
namespace datalog {
class context;
typedef vector<std::pair<func_decl*,relation_fact> > fact_vector;
class rel_context : public rel_context_base {
context& m_context;
ast_manager& m;
relation_manager m_rmanager;
expr_ref m_answer;
relation_base * m_last_result_relation;
fact_vector m_table_facts;
execution_context m_ectx;
instruction_block m_code;
class scoped_query;
void reset_negated_tables();
relation_plugin & get_ordinary_relation_plugin(symbol relation_name);
void reset_tables();
lbool saturate(scoped_query& sq);
void set_cancel(bool f);
public:
rel_context(context& ctx);
virtual ~rel_context();
virtual relation_manager & get_rmanager();
virtual const relation_manager & get_rmanager() const;
ast_manager& get_manager() const { return m; }
context& get_context() const { return m_context; }
virtual relation_base & get_relation(func_decl * pred);
virtual relation_base * try_get_relation(func_decl * pred) const;
virtual bool is_empty_relation(func_decl* pred) const;
virtual expr_ref try_get_formula(func_decl * pred) const;
virtual expr_ref get_answer() { return m_answer; }
virtual bool output_profile() const;
virtual lbool query(expr* q);
virtual lbool query(unsigned num_rels, func_decl * const* rels);
virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
symbol const * relation_names);
virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred);
virtual void cancel() { set_cancel(true); }
virtual void cleanup() { set_cancel(false);}
/**
\brief Restrict the set of used predicates to \c res.
The function deallocates unsused relations, it does not deal with rules.
*/
virtual void restrict_predicates(func_decl_set const& predicates);
virtual void transform_rules();
virtual bool try_get_size(func_decl* pred, unsigned& rel_size) const;
/**
\brief query result if it contains fact.
*/
virtual bool result_contains_fact(relation_fact const& f);
virtual void collect_non_empty_predicates(func_decl_set& ps) {
return get_rmanager().collect_non_empty_predicates(ps);
}
/** \brief add facts to relation
*/
virtual void add_fact(func_decl* pred, relation_fact const& fact);
virtual void add_fact(func_decl* pred, table_fact const& fact);
/** \brief check if facts were added to relation
*/
virtual bool has_facts(func_decl * pred) const;
/**
\brief Store the relation \c rel under the predicate \c pred. The \c context object
takes over the ownership of the relation object.
*/
virtual void store_relation(func_decl * pred, relation_base * rel);
virtual void display_output_facts(rule_set const& rules, std::ostream & out) const;
virtual void display_facts(std::ostream & out) const;
virtual void display_profile(std::ostream& out);
virtual lbool saturate();
};
};
#endif /* _REL_CONTEXT_H_ */