3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 09:05:31 +00:00

merged with unstable

This commit is contained in:
Ken McMillan 2013-10-18 17:26:41 -07:00
commit 3a0947b3ba
413 changed files with 31618 additions and 17204 deletions

12
src/muz/README Normal file
View file

@ -0,0 +1,12 @@
muZ: routines related to solving satisfiability of Horn clauses and
solving Datalog programs.
- base - contains base routines and the main context for
maintaining fixedpoint solvers
- transforms - common rule transformations
- rel - relational algebra based Datalog engine
- pdr - PDR based Horn clause solver
- clp - Dart/Symbolic execution-based solver
- tab - Tabulation based solver
- bmc - Bounded model checking based solver
- fp - main exported routines

View file

@ -0,0 +1,297 @@
/**
Example from Boogie:
(derivation
(step s!4 (main_loop_LoopHead true true)
rule!3 (subst
(= assertsPassed@@1 true)
(= assertsPassed@2@@0 true)
(= main_loop_LoopHead_assertsPassed true)
)
(labels @950 +895 +668 +670 +893 +899 )
(ref true ))
(step s!3 (main true false)
rule!1 (subst
(= assertsPassed true)
(= assertsPassed@0 true)
(= assertsPassed@2 false)
(= main_assertsPassed false)
)
(labels @839 +763 +547 +546 +761 +544 +545 +si_fcall_805 +681 +768 )
(ref s!4 ))
(step s!2 (main_SeqInstr true false)
rule!2 (subst
(= assertsPassed@@0 true)
(= assertsPassed@0@@0 false)
(= main_SeqInstr_assertsPassed false)
)
(labels @890 +851 +572 +si_fcall_883 +853 )
(ref s!3 ))
(step s!1 (@Fail!0)
rule!4 (subst
(= assertsPassed@@2 true)
(= main_SeqInstr_assertsPassed@@0 false)
)
(labels )
(ref s!2 ))
)
(model
"tickleBool -> {
true -> true
false -> true
else -> true
}
")
*/
#include "dl_boogie_proof.h"
#include "model_pp.h"
#include "proof_utils.h"
#include "ast_pp.h"
#include "qe_util.h"
namespace datalog {
/**
\brief push hyper-resolution steps upwards such that every use of
hyper-resolution uses a premise that is not derived from hyper-resolution.
perform the following rewrite:
hr(hr(p1,p2,p3,..),p4,p5) => hr(p1,hr(p2,p4),p3,..,p5)
*/
void mk_input_resolution(proof_ref& pr) {
ast_manager& m = pr.get_manager();
proof_ref pr1(m);
proof_ref_vector premises1(m), premises2(m), premises(m);
expr_ref conclusion1(m), conclusion2(m), conclusion(m);
svector<std::pair<unsigned, unsigned> > positions1, positions2, positions;
vector<expr_ref_vector> substs1, substs2, substs;
if (m.is_hyper_resolve(pr, premises1, conclusion1, positions1, substs1) &&
m.is_hyper_resolve(premises1[0].get(), premises, conclusion2, positions, substs2)) {
for (unsigned i = 1; i < premises1.size(); ++i) {
pr1 = premises1[i].get();
mk_input_resolution(pr1);
premises1[i] = pr1;
}
for (unsigned i = 0; i < premises.size(); ++i) {
pr1 = premises[i].get();
mk_input_resolution(pr1);
premises[i] = pr1;
}
unsigned sz = premises.size();
for (unsigned i = 1; i < sz; ++i) {
proof* premise = premises[i].get();
expr_ref_vector literals(m);
expr* l1, *l2;
if (!m.is_implies(premise, l1, l2)) {
continue;
}
qe::flatten_and(l1, literals);
positions2.reset();
premises2.reset();
premises2.push_back(premise);
substs2.reset();
for (unsigned j = 0; j < literals.size(); ++j) {
expr* lit = literals[j].get();
for (unsigned k = 1; k < premises1.size(); ++k) {
if (m.get_fact(premises1[k].get()) == lit) {
premises2.push_back(premises1[k].get());
positions2.push_back(std::make_pair(j+1,0));
substs2.push_back(expr_ref_vector(m));
break;
}
}
}
premises[i] = m.mk_hyper_resolve(premises2.size(), premises2.c_ptr(), l2, positions2, substs2);
}
conclusion = conclusion1;
pr = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs);
}
}
void boogie_proof::set_proof(proof* p) {
std::cout << "set proof\n";
m_proof = p;
proof_utils::push_instantiations_up(m_proof);
mk_input_resolution(m_proof);
std::cout << "proof set\n";
}
void boogie_proof::set_model(model* m) {
m_model = m;
}
void boogie_proof::pp(std::ostream& out) {
if (m_proof) {
pp_proof(out);
}
if (m_model) {
model_pp(out, *m_model);
}
}
void boogie_proof::pp_proof(std::ostream& out) {
vector<step> steps;
ptr_vector<proof> rules;
rules.push_back(m_proof);
steps.push_back(step());
obj_map<proof, unsigned> index;
index.insert(m_proof, 0);
for (unsigned j = 0; j < rules.size(); ++j) {
proof* p = rules[j];
proof_ref_vector premises(m);
expr_ref conclusion(m);
svector<std::pair<unsigned, unsigned> > positions;
vector<expr_ref_vector> substs;
expr* tmp;
steps[j].m_fact = m.get_fact(p);
m.is_implies(steps[j].m_fact, tmp, steps[j].m_fact);
get_subst(p, steps[j].m_subst);
get_labels(p, steps[j].m_labels);
if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) {
for (unsigned i = 1; i < premises.size(); ++i) {
proof* premise = premises[i].get();
unsigned position = 0;
if (!index.find(premise, position)) {
position = rules.size();
rules.push_back(premise);
steps.push_back(step());
index.insert(premise, position);
}
steps[j].m_refs.push_back(position);
}
get_rule_name(premises[0].get(), steps[j].m_rule_name);
}
}
for (unsigned j = steps.size(); j > 0; ) {
--j;
step &s = steps[j];
// TBD
// s.m_labels;
// set references, compensate for reverse ordering.
for (unsigned i = 0; i < s.m_refs.size(); ++i) {
s.m_refs[i] = rules.size()-1-s.m_refs[i];
}
}
steps.reverse();
pp_steps(out, steps);
}
/**
\brief extract the instantiation by searching for the first occurrence of a hyper-resolution
rule that produces an instance.
*/
void boogie_proof::get_subst(proof* p, subst& s) {
ptr_vector<proof> todo;
todo.push_back(p);
ast_mark visited;
std::cout << "get_subst\n" << mk_pp(p, m) << "\n";
while (!todo.empty()) {
proof* p = todo.back();
todo.pop_back();
if (visited.is_marked(p)) {
continue;
}
visited.mark(p, true);
proof_ref_vector premises(m);
expr_ref conclusion(m);
svector<std::pair<unsigned, unsigned> > positions;
vector<expr_ref_vector> substs;
if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) {
expr_ref_vector const& sub = substs[0];
if (!sub.empty()) {
quantifier* q = to_quantifier(m.get_fact(premises[0].get()));
unsigned sz = sub.size();
SASSERT(sz == q->get_num_decls());
for (unsigned i = 0; i < sz; ++i) {
s.push_back(std::make_pair(q->get_decl_name(sz-1-i), sub[i]));
}
return;
}
}
unsigned sz = m.get_num_parents(p);
for (unsigned i = 0; i < sz; ++i) {
todo.push_back(m.get_parent(p, i));
}
}
}
void boogie_proof::get_rule_name(proof* p, symbol& n) {
}
void boogie_proof::get_labels(proof* p, labels& lbls) {
}
void boogie_proof::pp_steps(std::ostream& out, vector<step>& steps) {
out << "(derivation\n";
for (unsigned i = 0; i < steps.size(); ++i) {
pp_step(out, i, steps[i]);
}
out << ")\n";
}
// step :: "(" "step" step-name fact rule-name subst labels premises ")"
void boogie_proof::pp_step(std::ostream& out, unsigned id, step& s) {
out << "(step\n";
out << " s!" << id << " ";
pp_fact(out, s.m_fact);
out << " " << s.m_rule_name << "\n";
pp_subst(out << " ", s.m_subst);
pp_labels(out << " ", s.m_labels);
pp_premises(out << " ", s.m_refs);
out << ")\n";
}
// fact :: "(" predicate theory-term* ")"
void boogie_proof::pp_fact(std::ostream& out, expr* fact) {
out << mk_pp(fact, m) << "\n";
}
// subst :: "(" "subst" assignment* ")"
void boogie_proof::pp_subst(std::ostream& out, subst& s) {
out << "(subst";
for (unsigned i = 0; i < s.size(); ++i) {
pp_assignment(out, s[i].first, s[i].second);
}
out << ")\n";
}
// assignment :: "(" "=" variable theory-term ")"
void boogie_proof::pp_assignment(std::ostream& out, symbol const& v, expr* t) {
out << "\n (= " << v << " " << mk_pp(t, m) << ")";
}
// labels :: "(" "labels" label* ")"
void boogie_proof::pp_labels(std::ostream& out, labels& lbls) {
out << "(labels";
for (unsigned i = 0; i < lbls.size(); ++i) {
out << " " << lbls[i];
}
out << ")\n";
}
// premises "(" "ref" step-name* ")"
void boogie_proof::pp_premises(std::ostream& out, refs& refs) {
out << "(ref";
for (unsigned i = 0; i < refs.size(); ++i) {
out << " s!" << refs[i];
}
out << ")\n";
}
}

View file

@ -0,0 +1,87 @@
/**
output :: derivation model
derivation :: "(" "derivation" step* ")"
step :: "(" "step" step-name fact rule-name subst labels premises ")"
step-name :: identifier
rule-name :: identifier
fact :: "(" predicate theory-term* ")"
subst :: "(" "subst" assignment* ")"
assignment :: "(" "=" variable theory-term ")"
labels :: "(" "labels" label* ")"
premises :: "(" "ref" step-name* ")"
model :: "(" "model" smtlib2-model ")"
In each step the "fact" is derivable by hyper-resolution from the named
premises and the named rule, under the given substitution for the
universally quantified variables in the rule. The premises of each
step must have occurred previously in the step sequence. The last fact
is a nullary placeholder predicate representing satisfaction of the query
(its name is arbitrary).
The labels list consists of all the positively labeled sub-formulas whose
truth is used in the proof, and all the negatively labeled formulas whose
negation is used. A theory-term is a ground term using only interpreted
constants of the background theories.
The smtlib2-model gives an interpretation of the uninterpreted constants
in the background theory under which the derivation is valid. Currently
it is a quoted string in the old z3 model format, for compatibility with
Boogie, however, this should be changed to the new model format (using
define-fun) when Boogie supports this.
*/
#include "ast.h"
#include "model.h"
namespace datalog {
class boogie_proof {
typedef vector<std::pair<symbol,expr*> > subst;
typedef svector<symbol> labels;
typedef unsigned_vector refs;
struct step {
symbol m_rule_name;
expr* m_fact;
subst m_subst;
labels m_labels;
refs m_refs;
};
ast_manager& m;
proof_ref m_proof;
model_ref m_model;
void pp_proof(std::ostream& out);
void pp_steps(std::ostream& out, vector<step>& steps);
void pp_step(std::ostream& out, unsigned i, step& s);
void pp_fact(std::ostream& out, expr* fact);
void pp_subst(std::ostream& out, subst& s);
void pp_assignment(std::ostream& out, symbol const& v, expr* t);
void pp_labels(std::ostream& out, labels& lbls);
void pp_premises(std::ostream& out, refs& refs);
void get_subst(proof* p, subst& sub);
void get_rule_name(proof* p, symbol&);
void get_labels(proof* p, labels&);
public:
boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(0) {}
void set_proof(proof* p);
void set_model(model* m);
void pp(std::ostream& out);
};
}

1334
src/muz/base/dl_context.cpp Normal file

File diff suppressed because it is too large Load diff

575
src/muz/base/dl_context.h Normal file
View file

@ -0,0 +1,575 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_context.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-18.
Revision History:
--*/
#ifndef _DL_CONTEXT_H_
#define _DL_CONTEXT_H_
#ifdef _CYGWIN
#undef min
#undef max
#endif
#include"arith_decl_plugin.h"
#include"map.h"
#include"th_rewriter.h"
#include"str_hashtable.h"
#include"var_subst.h"
#include"dl_costs.h"
#include"dl_decl_plugin.h"
#include"dl_rule_set.h"
#include"lbool.h"
#include"statistics.h"
#include"params.h"
#include"trail.h"
#include"model_converter.h"
#include"model2expr.h"
#include"smt_params.h"
#include"dl_rule_transformer.h"
#include"expr_abstract.h"
#include"expr_functors.h"
#include"dl_engine_base.h"
struct fixedpoint_params;
namespace datalog {
enum execution_result {
OK,
TIMEOUT,
MEMOUT,
INPUT_ERROR,
APPROX,
CANCELED
};
class relation_manager;
typedef sort * relation_sort;
typedef uint64 table_element;
typedef svector<table_element> table_fact;
typedef app * relation_element;
typedef app_ref relation_element_ref;
class relation_fact : public app_ref_vector {
public:
class el_proxy {
friend class relation_fact;
relation_fact & m_parent;
unsigned m_idx;
el_proxy(relation_fact & parent, unsigned idx) : m_parent(parent), m_idx(idx) {}
public:
operator relation_element() const {
return m_parent.get(m_idx);
}
relation_element operator->() const {
return m_parent.get(m_idx);
}
relation_element operator=(const relation_element & val) const {
m_parent.set(m_idx, val);
return m_parent.get(m_idx);
}
relation_element operator=(const el_proxy & val) {
m_parent.set(m_idx, val);
return m_parent.get(m_idx);
}
};
typedef const relation_element * iterator;
relation_fact(ast_manager & m) : app_ref_vector(m) {}
relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); }
relation_fact(context & ctx);
iterator begin() const { return c_ptr(); }
iterator end() const { return c_ptr()+size(); }
relation_element operator[](unsigned i) const { return get(i); }
el_proxy operator[](unsigned i) { return el_proxy(*this, i); }
};
// attempt to modularize context code.
class rel_context_base : public engine_base {
public:
rel_context_base(ast_manager& m, char const* name): engine_base(m, name) {}
virtual ~rel_context_base() {}
virtual relation_manager & get_rmanager() = 0;
virtual const relation_manager & get_rmanager() const = 0;
virtual relation_base & get_relation(func_decl * pred) = 0;
virtual relation_base * try_get_relation(func_decl * pred) const = 0;
virtual bool is_empty_relation(func_decl* pred) const = 0;
virtual expr_ref try_get_formula(func_decl * pred) const = 0;
virtual void display_output_facts(rule_set const& rules, std::ostream & out) const = 0;
virtual void display_facts(std::ostream & out) const = 0;
virtual void display_profile(std::ostream& out) = 0;
virtual void restrict_predicates(func_decl_set const& predicates) = 0;
virtual bool result_contains_fact(relation_fact const& f) = 0;
virtual void add_fact(func_decl* pred, relation_fact const& fact) = 0;
virtual void add_fact(func_decl* pred, table_fact const& fact) = 0;
virtual bool has_facts(func_decl * pred) const = 0;
virtual void store_relation(func_decl * pred, relation_base * rel) = 0;
virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0;
virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
symbol const * relation_names) = 0;
virtual bool output_profile() const = 0;
virtual void collect_non_empty_predicates(func_decl_set& preds) = 0;
virtual void transform_rules() = 0;
virtual bool try_get_size(func_decl* pred, unsigned& rel_sz) const = 0;
virtual lbool saturate() = 0;
};
class context {
public:
typedef unsigned finite_element;
enum sort_kind {
SK_UINT64,
SK_SYMBOL
};
private:
class sort_domain;
class symbol_sort_domain;
class uint64_sort_domain;
class restore_rules;
class contains_pred;
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
typedef map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> sym2decl;
typedef obj_map<const func_decl, svector<symbol> > pred2syms;
typedef obj_map<const sort, sort_domain*> sort_domain_map;
class contains_pred : public i_expr_pred {
context const& ctx;
public:
contains_pred(context& ctx): ctx(ctx) {}
virtual ~contains_pred() {}
virtual bool operator()(expr* e) {
return ctx.is_predicate(e);
}
};
ast_manager & m;
register_engine_base& m_register_engine;
smt_params & m_fparams;
params_ref m_params_ref;
fixedpoint_params* m_params;
dl_decl_util m_decl_util;
th_rewriter m_rewriter;
var_subst m_var_subst;
rule_manager m_rule_manager;
unused_vars_eliminator m_elim_unused_vars;
expr_abstractor m_abstractor;
contains_pred m_contains_p;
check_pred m_check_pred;
rule_transformer m_transf;
trail_stack<context> m_trail;
ast_ref_vector m_pinned;
app_ref_vector m_vars;
svector<symbol> m_names;
sort_domain_map m_sorts;
func_decl_set m_preds;
sym2decl m_preds_by_name;
pred2syms m_argument_var_names;
rule_set m_rule_set;
rule_set m_transformed_rule_set;
unsigned m_rule_fmls_head;
expr_ref_vector m_rule_fmls;
svector<symbol> m_rule_names;
expr_ref_vector m_background;
model_converter_ref m_mc;
proof_converter_ref m_pc;
rel_context_base* m_rel;
scoped_ptr<engine_base> m_engine;
bool m_closed;
bool m_saturation_was_run;
execution_result m_last_status;
expr_ref m_last_answer;
DL_ENGINE m_engine_type;
volatile bool m_cancel;
bool is_fact(app * head) const;
bool has_sort_domain(relation_sort s) const;
sort_domain & get_sort_domain(relation_sort s);
const sort_domain & get_sort_domain(relation_sort s) const;
class engine_type_proc;
public:
context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& p = params_ref());
~context();
void reset();
void push();
void pop();
bool saturation_was_run() const { return m_saturation_was_run; }
void notify_saturation_was_run() { m_saturation_was_run = true; }
void configure_engine();
ast_manager & get_manager() const { return m; }
rule_manager & get_rule_manager() { return m_rule_manager; }
smt_params & get_fparams() const { return m_fparams; }
fixedpoint_params const& get_params() const { return *m_params; }
DL_ENGINE get_engine() { configure_engine(); return m_engine_type; }
register_engine_base& get_register_engine() { return m_register_engine; }
th_rewriter& get_rewriter() { return m_rewriter; }
var_subst & get_var_subst() { return m_var_subst; }
dl_decl_util & get_decl_util() { return m_decl_util; }
bool generate_proof_trace() const;
bool output_profile() const;
bool output_tuples() const;
bool use_map_names() const;
bool fix_unbound_vars() const;
symbol default_table() const;
symbol default_relation() const;
symbol default_table_checker() const;
bool default_table_checked() const;
bool dbg_fpr_nonempty_relation_signature() const;
unsigned dl_profile_milliseconds_threshold() const;
bool all_or_nothing_deltas() const;
bool compile_with_widening() const;
bool unbound_compressor() const;
bool similarity_compressor() const;
unsigned similarity_compressor_threshold() const;
unsigned soft_timeout() const;
unsigned initial_restart_timeout() const;
bool generate_explanations() const;
bool explanations_on_relation_level() const;
bool magic_sets_for_queries() const;
bool eager_emptiness_checking() const;
bool bit_blast() const;
bool karr() const;
bool scale() const;
bool magic() const;
bool quantify_arrays() const;
bool instantiate_quantifiers() const;
void register_finite_sort(sort * s, sort_kind k);
/**
Register uninterpreted constant to be used as an implicitly quantified variable.
The variable gets quantified in the formula passed to rule::mk_rule_from_horn.
*/
void register_variable(func_decl* var);
expr_ref bind_variables(expr* fml, bool is_forall);
/**
Register datalog relation.
If names is true, we associate the predicate with its name, so that it can be
retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced
e.g. by rule transformations do not need to be named.
*/
void register_predicate(func_decl * pred, bool named);
/**
Restrict reltaions to set of predicates.
*/
void restrict_predicates(func_decl_set const& preds);
/**
\brief Retrieve predicates
*/
func_decl_set const& get_predicates() const { return m_preds; }
bool is_predicate(func_decl* pred) const { return m_preds.contains(pred); }
bool is_predicate(expr * e) const { return is_app(e) && is_predicate(to_app(e)->get_decl()); }
/**
\brief If a predicate name has a \c func_decl object assigned, return pointer to it;
otherwise return 0.
Not all \c func_decl object used as relation identifiers need to be assigned to their
names. Generally, the names coming from the parses are registered here.
*/
func_decl * try_get_predicate_decl(symbol const& pred_name) const {
func_decl * res = 0;
m_preds_by_name.find(pred_name, res);
return res;
}
/**
\brief Create a fresh head predicate declaration.
*/
func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix,
unsigned arity, sort * const * domain, func_decl* orig_pred=0);
/**
\brief Return number of a symbol in a DK_SYMBOL kind sort (\see register_sort() )
*/
finite_element get_constant_number(relation_sort sort, symbol s);
/**
\brief Return number of a symbol in a DK_UINT64 kind sort (\see register_sort() )
*/
finite_element get_constant_number(relation_sort srt, uint64 el);
/**
\brief Output name of constant with number \c num in sort \c sort.
*/
void print_constant_name(relation_sort sort, uint64 num, std::ostream & out);
bool try_get_sort_constant_count(relation_sort srt, uint64 & constant_count);
uint64 get_sort_size_estimate(relation_sort srt);
/**
\brief Assign names of variables used in the declaration of a predicate.
These names are used when printing out the relations to make the output conform
to the one of bddbddb.
*/
void set_argument_names(const func_decl * pred, svector<symbol> var_names);
symbol get_argument_name(const func_decl * pred, unsigned arg_index);
void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
symbol const * relation_names);
void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); }
rule_set & get_rules() { flush_add_rules(); return m_rule_set; }
void get_rules_as_formulas(expr_ref_vector& fmls, svector<symbol>& names);
void get_raw_rule_formulas(expr_ref_vector& fmls, svector<symbol>& names);
void add_fact(app * head);
void add_fact(func_decl * pred, const relation_fact & fact);
bool has_facts(func_decl * pred) const;
void add_rule(rule_ref& r);
void assert_expr(expr* e);
expr_ref get_background_assertion();
unsigned get_num_assertions() { return m_background.size(); }
expr* get_assertion(unsigned i) const { return m_background[i]; }
/**
Method exposed from API for adding rules.
*/
void add_rule(expr* rl, symbol const& name);
/**
Update a named rule.
*/
void update_rule(expr* rl, symbol const& name);
/**
Retrieve the maximal number of relevant unfoldings of 'pred'
with respect to the current state.
*/
unsigned get_num_levels(func_decl* pred);
/**
Retrieve the current cover of 'pred' up to 'level' unfoldings.
Return just the delta that is known at 'level'. To
obtain the full set of properties of 'pred' one should query
at 'level+1', 'level+2' etc, and include level=-1.
*/
expr_ref get_cover_delta(int level, func_decl* pred);
/**
Add a property of predicate 'pred' at 'level'.
It gets pushed forward when possible.
*/
void add_cover(int level, func_decl* pred, expr* property);
/**
\brief Check rule subsumption.
*/
bool check_subsumes(rule const& stronger_rule, rule const& weaker_rule);
/**
\brief Check if rule is well-formed according to engine.
*/
void check_rule(rule_ref& r);
/**
\brief Return true if facts to \c pred can be added using the \c add_table_fact() function.
This function should return true if \c pred is represented by a table_relation
and there is no transformation of relation values before they are put into the
table.
*/
void add_table_fact(func_decl * pred, const table_fact & fact);
void add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]);
/**
\brief To be called after all rules are added.
*/
void close();
void ensure_closed();
bool is_closed() { return m_closed; }
/**
\brief Undo the effect of the \c close operation.
*/
void reopen();
void ensure_opened();
model_converter_ref& get_model_converter() { return m_mc; }
void add_model_converter(model_converter* mc) { m_mc = concat(m_mc.get(), mc); }
proof_converter_ref& get_proof_converter() { return m_pc; }
void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); }
void transform_rules(rule_transformer& transf);
void transform_rules(rule_transformer::plugin* plugin);
void replace_rules(rule_set const& rs);
void record_transformed_rules();
void apply_default_transformation();
void collect_params(param_descrs& r);
void updt_params(params_ref const& p);
void display_rules(std::ostream & out) const {
m_rule_set.display(out);
}
void display(std::ostream & out) const;
void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out);
void display_profile(std::ostream& out) const;
// -----------------------------------
//
// basic usage methods
//
// -----------------------------------
void cancel();
bool canceled() const { return m_cancel; }
void cleanup();
void reset_cancel() { cleanup(); }
/**
\brief check if query 'q' is satisfied under asserted rules and background.
If successful, return OK and into \c result assign a relation with all
tuples matching the query. Otherwise return reason for failure and do not modify
\c result.
The numbers of variables in the query body must form a contiguous sequence
starting from zero.
The caller becomes an owner of the relation object returned in \c result. The
relation object, however, should not outlive the datalog context since it is
linked to a relation plugin in the context.
*/
lbool query(expr* q);
/**
\brief retrieve model from inductive invariant that shows query is unsat.
\pre engine == 'pdr' || engine == 'duality' - this option is only supported
for PDR mode and Duality mode.
*/
model_ref get_model();
/**
\brief retrieve proof from derivation of the query.
\pre engine == 'pdr' || engine == 'duality'- this option is only supported
for PDR mode and Duality mode.
*/
proof_ref get_proof();
/**
Query multiple output relations.
*/
lbool rel_query(unsigned num_rels, func_decl * const* rels);
/**
\brief retrieve last proof status.
*/
execution_result get_status();
void set_status(execution_result r) { m_last_status = r; }
/**
\brief retrieve formula corresponding to query that returns l_true.
The formula describes one or more instances of the existential variables
in the query that are derivable.
*/
expr* get_answer_as_formula();
void collect_statistics(statistics& st) const;
void reset_statistics();
/**
\brief Display a certificate for reachability and/or unreachability.
*/
void display_certificate(std::ostream& out);
/**
\brief query result if it contains fact.
*/
bool result_contains_fact(relation_fact const& f);
rel_context_base* get_rel_context() { ensure_engine(); return m_rel; }
private:
/**
Just reset all tables.
*/
void reset_tables();
void flush_add_rules();
void ensure_engine();
void check_quantifier_free(rule_ref& r);
void check_uninterpreted_free(rule_ref& r);
void check_existential_tail(rule_ref& r);
void check_positive_predicates(rule_ref& r);
// auxilary functions for SMT2 pretty-printer.
void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out);
//undefined and private copy constructor and operator=
context(const context&);
context& operator=(const context&);
};
};
#endif /* _DL_CONTEXT_H_ */

160
src/muz/base/dl_costs.cpp Normal file
View file

@ -0,0 +1,160 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_costs.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-20.
Revision History:
--*/
#include "debug.h"
#include "stopwatch.h"
#include "dl_context.h"
#include "dl_rule.h"
#include "dl_costs.h"
namespace datalog {
// -----------------------------------
//
// costs
//
// -----------------------------------
costs::costs() : milliseconds(0), instructions(0) {}
bool costs::empty() const {
return !milliseconds && !instructions;
}
void costs::reset() {
milliseconds = 0;
instructions = 0;
}
costs costs::operator-(const costs & o) const {
costs res(*this);
SASSERT(milliseconds>o.milliseconds);
res.milliseconds-=o.milliseconds;
SASSERT(instructions>o.instructions);
res.instructions-=o.instructions;
return res;
}
void costs::operator+=(const costs & o) {
milliseconds+=o.milliseconds;
instructions+=o.instructions;
}
bool costs::passes_thresholds(context & ctx) const {
return milliseconds >= ctx.dl_profile_milliseconds_threshold();
}
void costs::output(std::ostream & out) const {
out << "instr: " << instructions << " time: " << milliseconds << "ms";
}
// -----------------------------------
//
// accounted_object
//
// -----------------------------------
accounted_object::~accounted_object() {
if(m_parent_object) {
SASSERT(m_context);
m_context->get_rule_manager().dec_ref(m_parent_object);
}
}
void accounted_object::set_accounting_parent_object(context & ctx, rule * parent) {
if(m_parent_object) {
SASSERT(m_context);
SASSERT(m_context==&ctx);
m_context->get_rule_manager().dec_ref(m_parent_object);
}
m_context = &ctx;
m_parent_object = parent;
m_context->get_rule_manager().inc_ref(m_parent_object);
}
void accounted_object::process_costs() {
costs delta = get_current_costs();
if(delta.empty()) {
return;
}
get_current_costs().reset();
accounted_object * obj = this;
do {
obj->m_processed_cost+=delta;
obj=obj->m_parent_object;
} while(obj);
}
void accounted_object::get_total_cost(costs & result) const {
result.reset();
result+=m_current_cost;
result+=m_processed_cost;
}
bool accounted_object::passes_output_thresholds(context & ctx) const {
costs c;
get_total_cost(c);
return c.passes_thresholds(ctx);
}
void accounted_object::output_profile(std::ostream & out) const {
costs c;
get_total_cost(c);
c.output(out);
}
// -----------------------------------
//
// cost_recorder
//
// -----------------------------------
cost_recorder::cost_recorder() : m_obj(0) {
m_stopwatch = alloc(stopwatch);
m_stopwatch->start();
}
cost_recorder::~cost_recorder() {
if(m_obj) {
finish();
}
dealloc(m_stopwatch);
}
void cost_recorder::start(accounted_object * obj) {
uint64 curr_time = static_cast<uint64>(m_stopwatch->get_current_seconds()*1000);
if(m_obj) {
costs::time_type time_delta = static_cast<costs::time_type>(curr_time-m_last_time);
costs & c = m_obj->get_current_costs();
c.instructions++;
c.milliseconds+=time_delta;
m_obj->m_being_recorded = false;
}
m_running = obj!=0;
m_obj = obj;
m_last_time = curr_time;
if(obj) {
m_obj->m_being_recorded = true;
}
}
};

115
src/muz/base/dl_costs.h Normal file
View file

@ -0,0 +1,115 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_costs.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-20.
Revision History:
--*/
#ifndef _DL_COSTS_H_
#define _DL_COSTS_H_
#include<iosfwd>
#include "ast.h"
class stopwatch;
namespace datalog {
class context;
class rule;
class cost_recorder;
struct costs {
typedef unsigned time_type;
time_type milliseconds;
unsigned instructions;
costs();
bool empty() const;
void reset();
costs operator-(const costs & o) const;
void operator+=(const costs & o);
bool passes_thresholds(context & ctx) const;
void output(std::ostream & out) const;
};
class accounted_object {
friend class cost_recorder;
context * m_context;
rule * m_parent_object;
costs m_current_cost;
costs m_processed_cost;
bool m_being_recorded;
protected:
accounted_object() : m_context(0), m_parent_object(0), m_being_recorded(false) {}
~accounted_object();
public:
void set_accounting_parent_object(context & ctx, rule * parent);
rule * get_parent_object() const { return m_parent_object; }
costs & get_current_costs() { return m_current_cost; }
const costs & get_current_costs() const { return m_current_cost; }
const costs & get_processed_costs() const { return m_processed_cost; }
void get_total_cost(costs & result) const;
bool being_recorded() const { return m_being_recorded; }
void process_costs();
bool passes_output_thresholds(context & ctx) const;
void output_profile(std::ostream & out) const;
private:
//private and undefined copy constructor and operator= to avoid the default ones
accounted_object(const accounted_object &);
accounted_object& operator=(const accounted_object &);
};
class cost_recorder {
accounted_object * m_obj;
//it's a pointer to avoid everything depending on the stopwatch.h
//(and transitively then on windows.h) header file
stopwatch * m_stopwatch;
bool m_running;
uint64 m_last_time;
public:
cost_recorder();
~cost_recorder();
/**
\brief Start recording costs for the next object.
If the recording of the previous object did not finish, it will be finished here.
Also, it will be done more efficiently than if the \c finish() function was called
before separately.
*/
void start(accounted_object *);
void finish() { start(0); }
};
};
#endif /* _DL_COSTS_H_ */

View file

@ -0,0 +1,83 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
dl_engine_base.h
Abstract:
Base class for Datalog engines.
Author:
Nikolaj Bjorner (nbjorner) 2013-08-28
Revision History:
--*/
#ifndef _DL_ENGINE_BASE_H_
#define _DL_ENGINE_BASE_H_
#include "model.h"
namespace datalog {
enum DL_ENGINE {
DATALOG_ENGINE,
PDR_ENGINE,
QPDR_ENGINE,
BMC_ENGINE,
QBMC_ENGINE,
TAB_ENGINE,
CLP_ENGINE,
LAST_ENGINE,
DUALITY_ENGINE
};
class engine_base {
ast_manager& m;
std::string m_name;
public:
engine_base(ast_manager& m, char const* name): m(m), m_name(name) {}
virtual ~engine_base() {}
virtual expr_ref get_answer() = 0;
virtual lbool query(expr* q) = 0;
virtual lbool query(unsigned num_rels, func_decl*const* rels) { return l_undef; }
virtual void reset_statistics() {}
virtual void display_profile(std::ostream& out) const {}
virtual void collect_statistics(statistics& st) const {}
virtual unsigned get_num_levels(func_decl* pred) {
throw default_exception(std::string("get_num_levels is not supported for ") + m_name);
}
virtual expr_ref get_cover_delta(int level, func_decl* pred) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void add_cover(int level, func_decl* pred, expr* property) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void display_certificate(std::ostream& out) const {
throw default_exception(std::string("certificates are not supported for ") + m_name);
}
virtual model_ref get_model() {
return model_ref(alloc(model, m));
}
virtual proof_ref get_proof() {
return proof_ref(m.mk_asserted(m.mk_true()), m);
}
virtual void updt_params() {}
virtual void cancel() {}
virtual void cleanup() {}
};
class context;
class register_engine_base {
public:
virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0;
virtual void set_context(context* ctx) = 0;
};
}
#endif

1142
src/muz/base/dl_rule.cpp Normal file

File diff suppressed because it is too large Load diff

331
src/muz/base/dl_rule.h Normal file
View file

@ -0,0 +1,331 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-17.
Revision History:
--*/
#ifndef _DL_RULE_H_
#define _DL_RULE_H_
#include"ast.h"
#include"dl_costs.h"
#include"dl_util.h"
#include"used_vars.h"
#include"proof_converter.h"
#include"model_converter.h"
#include"ast_counter.h"
#include"rewriter.h"
#include"hnf.h"
#include"qe_lite.h"
namespace datalog {
class rule;
class rule_manager;
class rule_set;
class table;
class context;
typedef obj_ref<rule, rule_manager> rule_ref;
typedef ref_vector<rule, rule_manager> rule_ref_vector;
typedef ptr_vector<rule> rule_vector;
/**
\brief Manager for the \c rule class
\remark \c rule_manager objects are interchangable as long as they
contain the same \c ast_manager object.
*/
class rule_manager
{
class remove_label_cfg : public default_rewriter_cfg {
family_id m_label_fid;
public:
remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {}
virtual ~remove_label_cfg();
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result,
proof_ref & result_pr);
};
ast_manager& m;
context& m_ctx;
rule_counter m_counter;
used_vars m_used;
ptr_vector<sort> m_vars;
var_idx_set m_var_idx;
ptr_vector<expr> m_todo;
ast_mark m_mark;
app_ref_vector m_body;
app_ref m_head;
expr_ref_vector m_args;
svector<bool> m_neg;
hnf m_hnf;
qe_lite m_qe;
remove_label_cfg m_cfg;
rewriter_tpl<remove_label_cfg> m_rwr;
// only the context can create a rule_manager
friend class context;
explicit rule_manager(context& ctx);
/**
\brief Move functions from predicate tails into the interpreted tail by introducing new variables.
*/
void hoist_compound_predicates(unsigned num_bound, app_ref& head, app_ref_vector& body);
void hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body);
void flatten_body(app_ref_vector& body);
void remove_labels(expr_ref& fml, proof_ref& pr);
app_ref ensure_app(expr* e);
void check_app(expr* e);
bool contains_predicate(expr* fml) const;
void bind_variables(expr* fml, bool is_forall, expr_ref& result);
void mk_negations(app_ref_vector& body, svector<bool>& is_negated);
void mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name);
void mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name);
static expr_ref mk_implies(app_ref_vector const& body, expr* head);
unsigned extract_horn(expr* fml, app_ref_vector& body, app_ref& head);
/**
\brief Perform cheap quantifier elimination to reduce the number of variables in the interpreted tail.
*/
void reduce_unbound_vars(rule_ref& r);
void reset_collect_vars();
var_idx_set& finalize_collect_vars();
public:
ast_manager& get_manager() const { return m; }
void inc_ref(rule * r);
void dec_ref(rule * r);
used_vars& reset_used() { m_used.reset(); return m_used; }
var_idx_set& collect_vars(expr * pred);
var_idx_set& collect_vars(expr * e1, expr* e2);
var_idx_set& collect_rule_vars(rule * r);
var_idx_set& collect_rule_vars_ex(rule * r, app* t);
var_idx_set& collect_tail_vars(rule * r);
void accumulate_vars(expr* pred);
ptr_vector<sort>& get_var_sorts() { return m_vars; }
var_idx_set& get_var_idx() { return m_var_idx; }
/**
\brief Create a Datalog rule from a Horn formula.
The formula is of the form (forall (...) (forall (...) (=> (and ...) head)))
*/
void mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name = symbol::null);
/**
\brief Create a Datalog query from an expression.
The formula is of the form (exists (...) (exists (...) (and ...))
*/
func_decl* mk_query(expr* query, rule_set& rules);
/**
\brief Create a Datalog rule head :- tail[0], ..., tail[n-1].
Return 0 if it is not a valid rule.
\remark A tail may contain negation. tail[i] is assumed to be negated if is_neg != 0 && is_neg[i] == true
*/
rule * mk(app * head, unsigned n, app * const * tail, bool const * is_neg = 0,
symbol const& name = symbol::null, bool normalize = true);
/**
\brief Create a rule with the same tail as \c source and with a specified head.
*/
rule * mk(rule const * source, app * new_head, symbol const& name = symbol::null);
/**
\brief Create a copy of the given rule.
*/
rule * mk(rule const * source, symbol const& name = symbol::null);
/** make sure there are not non-quantified variables that occur only in interpreted predicates */
void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination);
/**
\brief add proof that new rule is obtained by rewriting old rule.
*/
void mk_rule_rewrite_proof(rule& old_rule, rule& new_rule);
/**
\brief tag rule as asserted.
*/
void mk_rule_asserted_proof(rule& r);
/**
\brief apply substitution to variables of rule.
*/
void substitute(rule_ref& r, unsigned sz, expr*const* es);
/**
\brief Check that head :- tail[0], ..., tail[n-1]
is a valid Datalog rule.
*/
void check_valid_rule(app * head, unsigned n, app * const * tail) const;
/**
\brief Check that \c head may occur as a Datalog rule head.
*/
void check_valid_head(expr * head) const;
/**
\brief Return true if \c head may occur as a fact.
*/
bool is_fact(app * head) const;
static bool is_forall(ast_manager& m, expr* e, quantifier*& q);
rule_counter& get_counter() { return m_counter; }
};
class rule : public accounted_object {
friend class rule_manager;
app * m_head;
proof* m_proof;
unsigned m_tail_size:20;
// unsigned m_reserve:12;
unsigned m_ref_cnt;
unsigned m_positive_cnt;
unsigned m_uninterp_cnt;
symbol m_name;
/**
The following field is an array of tagged pointers.
- Tag 0: the atom is not negated
- Tag 1: the atom is negated.
The order of tail formulas is the following:
uninterpreted positive,
uninterpreted negative,
interpreted.
The negated flag is never set for interpreted tails.
*/
app * m_tail[0];
static unsigned get_obj_size(unsigned n) { return sizeof(rule) + n * sizeof(app *); }
rule() : m_ref_cnt(0) {}
~rule() {}
void deallocate(ast_manager & m);
void get_used_vars(used_vars& uv) const;
public:
proof * get_proof() const { return m_proof; }
void set_proof(ast_manager& m, proof* p);
app * get_head() const { return m_head; }
func_decl* get_decl() const { return get_head()->get_decl(); }
unsigned get_tail_size() const { return m_tail_size; }
/**
\brief Return number of positive uninterpreted predicates in the tail.
These predicates are the first in the tail.
*/
unsigned get_positive_tail_size() const { return m_positive_cnt; }
unsigned get_uninterpreted_tail_size() const { return m_uninterp_cnt; }
/**
\brief Return i-th tail atom. The first \c get_uninterpreted_tail_size()
atoms are uninterpreted and the first \c get_positive_tail_size() are
uninterpreted and non-negated.
*/
app * get_tail(unsigned i) const { SASSERT(i < m_tail_size); return UNTAG(app *, m_tail[i]); }
func_decl* get_decl(unsigned i) const { SASSERT(i < get_uninterpreted_tail_size()); return get_tail(i)->get_decl(); }
bool is_neg_tail(unsigned i) const { SASSERT(i < m_tail_size); return GET_TAG(m_tail[i]) == 1; }
/**
Check whether predicate p is in the interpreted tail.
If only_positive is true, only the positive predicate tail atoms are checked.
*/
bool is_in_tail(const func_decl * p, bool only_positive=false) const;
bool has_uninterpreted_non_predicates(func_decl*& f) const;
void has_quantifiers(bool& existential, bool& universal) const;
bool has_quantifiers() const;
bool has_negation() const;
/**
\brief Store in d the (direct) dependencies of the given rule.
*/
void norm_vars(rule_manager & rm);
void get_vars(ptr_vector<sort>& sorts) const;
void to_formula(expr_ref& result) const;
void display(context & ctx, std::ostream & out) const;
std::ostream& display_smt2(ast_manager& m, std::ostream & out) const;
symbol const& name() const { return m_name; }
unsigned hash() const;
};
struct rule_eq_proc {
bool operator()(const rule * r1, const rule * r2) const;
};
struct rule_hash_proc {
unsigned operator()(const rule * r) const;
};
};
#endif /* _DL_RULE_H_ */

View file

@ -0,0 +1,773 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule_set.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-17.
Revision History:
--*/
#include<algorithm>
#include<functional>
#include"dl_context.h"
#include"dl_rule_set.h"
#include"ast_pp.h"
namespace datalog {
rule_dependencies::rule_dependencies(context& ctx): m_context(ctx) {
}
rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed):
m_context(o.m_context) {
if (reversed) {
iterator oit = o.begin();
iterator oend = o.end();
for (; oit!=oend; ++oit) {
func_decl * pred = oit->m_key;
item_set & orig_items = *oit->get_value();
ensure_key(pred);
item_set::iterator dit = orig_items.begin();
item_set::iterator dend = orig_items.end();
for (; dit!=dend; ++dit) {
func_decl * master_pred = *dit;
insert(master_pred, pred);
}
}
}
else {
iterator oit = o.begin();
iterator oend = o.end();
for (; oit!=oend; ++oit) {
func_decl * pred = oit->m_key;
item_set & orig_items = *oit->get_value();
m_data.insert(pred, alloc(item_set, orig_items));
}
}
}
rule_dependencies::~rule_dependencies() {
reset();
}
void rule_dependencies::reset() {
reset_dealloc_values(m_data);
}
void rule_dependencies::remove_m_data_entry(func_decl * key) {
item_set * itm_set = m_data.find(key);
dealloc(itm_set);
m_data.remove(key);
}
rule_dependencies::item_set & rule_dependencies::ensure_key(func_decl * pred) {
deps_type::obj_map_entry * e = m_data.insert_if_not_there2(pred, 0);
if (!e->get_data().m_value) {
e->get_data().m_value = alloc(item_set);
}
return *e->get_data().m_value;
}
void rule_dependencies::insert(func_decl * depending, func_decl * master) {
SASSERT(m_data.contains(master)); //see m_data documentation
item_set & s = ensure_key(depending);
s.insert(master);
}
void rule_dependencies::populate(const rule_set & rules) {
SASSERT(m_data.empty());
rule_set::decl2rules::iterator it = rules.m_head2rules.begin();
rule_set::decl2rules::iterator end = rules.m_head2rules.end();
for (; it != end; ++it) {
ptr_vector<rule> * rules = it->m_value;
ptr_vector<rule>::iterator it2 = rules->begin();
ptr_vector<rule>::iterator end2 = rules->end();
for (; it2 != end2; ++it2) {
populate(*it2);
}
}
}
void rule_dependencies::populate(unsigned n, rule * const * rules) {
SASSERT(m_data.empty());
for (unsigned i=0; i<n; i++) {
populate(rules[i]);
}
}
void rule_dependencies::populate(rule const* r) {
TRACE("dl_verbose", tout << r->get_decl()->get_name() << "\n";);
m_visited.reset();
func_decl * d = r->get_decl();
func_decl_set & s = ensure_key(d);
for (unsigned i = 0; i < r->get_tail_size(); ++i) {
m_todo.push_back(r->get_tail(i));
}
while (!m_todo.empty()) {
expr* e = m_todo.back();
m_todo.pop_back();
if (m_visited.is_marked(e)) {
continue;
}
m_visited.mark(e, true);
if (is_app(e)) {
app* a = to_app(e);
d = a->get_decl();
if (m_context.is_predicate(d)) {
// insert d and ensure the invariant
// that every predicate is present as
// a key in m_data
s.insert(d);
ensure_key(d);
}
m_todo.append(a->get_num_args(), a->get_args());
}
else if (is_quantifier(e)) {
m_todo.push_back(to_quantifier(e)->get_expr());
}
}
}
const rule_dependencies::item_set & rule_dependencies::get_deps(func_decl * f) const {
deps_type::obj_map_entry * e = m_data.find_core(f);
if (!e) {
return m_empty_item_set;
}
SASSERT(e->get_data().get_value());
return *e->get_data().get_value();
}
void rule_dependencies::restrict(const item_set & allowed) {
ptr_vector<func_decl> to_remove;
iterator pit = begin();
iterator pend = end();
for (; pit!=pend; ++pit) {
func_decl * pred = pit->m_key;
if (!allowed.contains(pred)) {
to_remove.insert(pred);
continue;
}
item_set& itms = *pit->get_value();
set_intersection(itms, allowed);
}
ptr_vector<func_decl>::iterator rit = to_remove.begin();
ptr_vector<func_decl>::iterator rend = to_remove.end();
for (; rit != rend; ++rit) {
remove_m_data_entry(*rit);
}
}
void rule_dependencies::remove(func_decl * itm) {
remove_m_data_entry(itm);
iterator pit = begin();
iterator pend = end();
for (; pit != pend; ++pit) {
item_set & itms = *pit->get_value();
itms.remove(itm);
}
}
void rule_dependencies::remove(const item_set & to_remove) {
item_set::iterator rit = to_remove.begin();
item_set::iterator rend = to_remove.end();
for (; rit!=rend; ++rit) {
remove_m_data_entry(*rit);
}
iterator pit = begin();
iterator pend = end();
for (; pit!=pend; ++pit) {
item_set * itms = pit->get_value();
set_difference(*itms, to_remove);
}
}
unsigned rule_dependencies::out_degree(func_decl * f) const {
unsigned res = 0;
iterator pit = begin();
iterator pend = end();
for (; pit!=pend; ++pit) {
item_set & itms = *pit->get_value();
if (itms.contains(f)) {
res++;
}
}
return res;
}
bool rule_dependencies::sort_deps(ptr_vector<func_decl> & res) {
typedef obj_map<func_decl, unsigned> deg_map;
unsigned init_len = res.size();
deg_map degs;
unsigned curr_index = init_len;
rule_dependencies reversed(*this, true);
iterator pit = begin();
iterator pend = end();
for (; pit!=pend; ++pit) {
func_decl * pred = pit->m_key;
unsigned deg = in_degree(pred);
if (deg==0) {
res.push_back(pred);
}
else {
degs.insert(pred, deg);
}
}
while (curr_index<res.size()) { //res.size() can change in the loop iteration
func_decl * curr = res[curr_index];
const item_set & children = reversed.get_deps(curr);
item_set::iterator cit = children.begin();
item_set::iterator cend = children.end();
for (; cit!=cend; ++cit) {
func_decl * child = *cit;
deg_map::obj_map_entry * e = degs.find_core(child);
SASSERT(e);
unsigned & child_deg = e->get_data().m_value;
SASSERT(child_deg>0);
child_deg--;
if (child_deg==0) {
res.push_back(child);
}
}
curr_index++;
}
if (res.size() < init_len + m_data.size()) {
res.shrink(init_len);
return false;
}
SASSERT(res.size()==init_len+m_data.size());
return true;
}
void rule_dependencies::display(std::ostream & out ) const
{
iterator pit = begin();
iterator pend = end();
for (; pit != pend; ++pit) {
func_decl * pred = pit->m_key;
const item_set & deps = *pit->m_value;
item_set::iterator dit=deps.begin();
item_set::iterator dend=deps.end();
if (dit == dend) {
out<<pred->get_name()<<" - <none>\n";
}
for (; dit != dend; ++dit) {
func_decl * dep = *dit;
out << pred->get_name() << " -> " << dep->get_name() << "\n";
}
}
}
// -----------------------------------
//
// rule_set
//
// -----------------------------------
rule_set::rule_set(context & ctx)
: m_context(ctx),
m_rule_manager(ctx.get_rule_manager()),
m_rules(m_rule_manager),
m_deps(ctx),
m_stratifier(0),
m_refs(ctx.get_manager()) {
}
rule_set::rule_set(const rule_set & other)
: m_context(other.m_context),
m_rule_manager(other.m_rule_manager),
m_rules(m_rule_manager),
m_deps(other.m_context),
m_stratifier(0),
m_refs(m_context.get_manager()) {
add_rules(other);
if (other.m_stratifier) {
VERIFY(close());
}
}
rule_set::~rule_set() {
reset();
}
void rule_set::reset() {
m_rules.reset();
reset_dealloc_values(m_head2rules);
m_deps.reset();
m_stratifier = 0;
m_output_preds.reset();
m_orig2pred.reset();
m_pred2orig.reset();
m_refs.reset();
}
ast_manager & rule_set::get_manager() const {
return m_context.get_manager();
}
func_decl* rule_set::get_orig(func_decl* pred) const {
func_decl* orig = pred;
m_pred2orig.find(pred, orig);
return orig;
}
func_decl* rule_set::get_pred(func_decl* orig) const {
func_decl* pred = orig;
m_orig2pred.find(orig, pred);
return pred;
}
void rule_set::inherit_predicates(rule_set const& other) {
m_refs.append(other.m_refs);
set_union(m_output_preds, other.m_output_preds);
{
obj_map<func_decl, func_decl*>::iterator it = other.m_orig2pred.begin();
obj_map<func_decl, func_decl*>::iterator end = other.m_orig2pred.end();
for (; it != end; ++it) {
m_orig2pred.insert(it->m_key, it->m_value);
}
}
{
obj_map<func_decl, func_decl*>::iterator it = other.m_pred2orig.begin();
obj_map<func_decl, func_decl*>::iterator end = other.m_pred2orig.end();
for (; it != end; ++it) {
m_pred2orig.insert(it->m_key, it->m_value);
}
}
}
void rule_set::inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred) {
if (other.is_output_predicate(orig)) {
set_output_predicate(pred);
}
orig = other.get_orig(orig);
m_refs.push_back(pred);
m_refs.push_back(orig);
m_orig2pred.insert(orig, pred);
m_pred2orig.insert(pred, orig);
}
void rule_set::add_rule(rule * r) {
TRACE("dl_verbose", r->display(m_context, tout << "add:"););
SASSERT(!is_closed());
m_rules.push_back(r);
app * head = r->get_head();
SASSERT(head != 0);
func_decl * d = head->get_decl();
decl2rules::obj_map_entry* e = m_head2rules.insert_if_not_there2(d, 0);
if (!e->get_data().m_value) e->get_data().m_value = alloc(ptr_vector<rule>);
e->get_data().m_value->push_back(r);
}
void rule_set::del_rule(rule * r) {
TRACE("dl", r->display(m_context, tout << "del:"););
func_decl* d = r->get_decl();
rule_vector* rules = m_head2rules.find(d);
#define DEL_VECTOR(_v) \
for (unsigned i = (_v).size(); i > 0; ) { \
--i; \
if ((_v)[i] == r) { \
(_v)[i] = (_v).back(); \
(_v).pop_back(); \
break; \
} \
} \
DEL_VECTOR(*rules);
DEL_VECTOR(m_rules);
}
void rule_set::ensure_closed() {
if (!is_closed()) {
VERIFY(close());
}
}
bool rule_set::close() {
SASSERT(!is_closed()); //the rule_set is not already closed
m_deps.populate(*this);
m_stratifier = alloc(rule_stratifier, m_deps);
if (!stratified_negation()) {
m_stratifier = 0;
m_deps.reset();
return false;
}
return true;
}
void rule_set::reopen() {
if (is_closed()) {
m_stratifier = 0;
m_deps.reset();
}
}
/**
\brief Return true if the negation is indeed stratified.
*/
bool rule_set::stratified_negation() {
ptr_vector<rule>::const_iterator it = m_rules.c_ptr();
ptr_vector<rule>::const_iterator end = m_rules.c_ptr()+m_rules.size();
for (; it != end; it++) {
rule * r = *it;
app * head = r->get_head();
func_decl * head_decl = head->get_decl();
unsigned n = r->get_uninterpreted_tail_size();
for (unsigned i = r->get_positive_tail_size(); i < n; i++) {
SASSERT(r->is_neg_tail(i));
func_decl * tail_decl = r->get_tail(i)->get_decl();
unsigned neg_strat = get_predicate_strat(tail_decl);
unsigned head_strat = get_predicate_strat(head_decl);
SASSERT(head_strat>=neg_strat); //head strat can never be lower than that of a tail
if (head_strat == neg_strat) {
return false;
}
}
}
return true;
}
void rule_set::replace_rules(const rule_set & src) {
if (this != &src) {
reset();
add_rules(src);
}
}
void rule_set::add_rules(const rule_set & src) {
SASSERT(!is_closed());
unsigned n = src.get_num_rules();
for (unsigned i = 0; i < n; i++) {
add_rule(src.get_rule(i));
}
inherit_predicates(src);
}
const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const {
decl2rules::obj_map_entry * e = m_head2rules.find_core(pred);
if (!e) {
return m_empty_rule_vector;
}
return *e->get_data().m_value;
}
const rule_set::pred_set_vector & rule_set::get_strats() const {
SASSERT(m_stratifier);
return m_stratifier->get_strats();
}
unsigned rule_set::get_predicate_strat(func_decl * pred) const {
SASSERT(m_stratifier);
return m_stratifier->get_predicate_strat(pred);
}
void rule_set::split_founded_rules(func_decl_set& founded, func_decl_set& non_founded) {
founded.reset();
non_founded.reset();
{
decl2rules::iterator it = begin_grouped_rules(), end = end_grouped_rules();
for (; it != end; ++it) {
non_founded.insert(it->m_key);
}
}
bool change = true;
while (change) {
change = false;
func_decl_set::iterator it = non_founded.begin(), end = non_founded.end();
for (; it != end; ++it) {
rule_vector const& rv = get_predicate_rules(*it);
bool found = false;
for (unsigned i = 0; !found && i < rv.size(); ++i) {
rule const& r = *rv[i];
bool is_founded = true;
for (unsigned j = 0; is_founded && j < r.get_uninterpreted_tail_size(); ++j) {
is_founded = founded.contains(r.get_decl(j));
}
if (is_founded) {
founded.insert(*it);
non_founded.remove(*it);
change = true;
found = true;
}
}
}
}
}
void rule_set::display(std::ostream & out) const {
out << "; rule count: " << get_num_rules() << "\n";
out << "; predicate count: " << m_head2rules.size() << "\n";
func_decl_set::iterator pit = m_output_preds.begin();
func_decl_set::iterator pend = m_output_preds.end();
for (; pit != pend; ++pit) {
out << "; output: " << (*pit)->get_name() << '\n';
}
decl2rules::iterator it = m_head2rules.begin();
decl2rules::iterator end = m_head2rules.end();
for (; it != end; ++it) {
ptr_vector<rule> * rules = it->m_value;
ptr_vector<rule>::iterator it2 = rules->begin();
ptr_vector<rule>::iterator end2 = rules->end();
for (; it2 != end2; ++it2) {
rule * r = *it2;
if (!r->passes_output_thresholds(m_context)) {
continue;
}
r->display(m_context, out);
}
}
}
void rule_set::display_deps( std::ostream & out ) const
{
const pred_set_vector & strats = get_strats();
pred_set_vector::const_iterator sit = strats.begin();
pred_set_vector::const_iterator send = strats.end();
for (; sit!=send; ++sit) {
func_decl_set & strat = **sit;
func_decl_set::iterator fit=strat.begin();
func_decl_set::iterator fend=strat.end();
bool non_empty = false;
for (; fit!=fend; ++fit) {
func_decl * first = *fit;
const func_decl_set & deps = m_deps.get_deps(first);
func_decl_set::iterator dit=deps.begin();
func_decl_set::iterator dend=deps.end();
for (; dit!=dend; ++dit) {
non_empty = true;
func_decl * dep = *dit;
out<<first->get_name()<<" -> "<<dep->get_name()<<"\n";
}
}
if (non_empty && sit!=send) {
out << "\n";
}
}
}
// -----------------------------------
//
// rule_stratifier
//
// -----------------------------------
rule_stratifier::~rule_stratifier() {
comp_vector::iterator it = m_strats.begin();
comp_vector::iterator end = m_strats.end();
for (; it!=end; ++it) {
SASSERT(*it);
dealloc(*it);
}
}
unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const {
unsigned num;
if (!m_pred_strat_nums.find(pred, num)) {
//the number of the predicate is not stored, therefore it did not appear
//in the algorithm and therefore it does not depend on anything and nothing
//depends on it. So it is safe to assign zero strate to it, although it is
//not strictly true.
num = 0;
}
return num;
}
void rule_stratifier::traverse(T* el) {
unsigned p_num;
if (m_preorder_nums.find(el, p_num)) {
if (p_num < m_first_preorder) {
//traversed in a previous sweep
return;
}
if (m_component_nums.contains(el)) {
//we already assigned a component for el
return;
}
while (!m_stack_P.empty()) {
unsigned on_stack_num;
VERIFY( m_preorder_nums.find(m_stack_P.back(), on_stack_num) );
if (on_stack_num <= p_num) {
break;
}
m_stack_P.pop_back();
}
}
else {
p_num=m_next_preorder++;
m_preorder_nums.insert(el, p_num);
m_stack_S.push_back(el);
m_stack_P.push_back(el);
const item_set & children = m_deps.get_deps(el);
item_set::iterator cit=children.begin();
item_set::iterator cend=children.end();
for (; cit!=cend; ++cit) {
traverse(*cit);
}
if (el == m_stack_P.back()) {
unsigned comp_num = m_components.size();
item_set * new_comp = alloc(item_set);
m_components.push_back(new_comp);
T* s_el;
do {
s_el=m_stack_S.back();
m_stack_S.pop_back();
new_comp->insert(s_el);
m_component_nums.insert(s_el, comp_num);
} while (s_el!=el);
m_stack_P.pop_back();
}
}
}
void rule_stratifier::process() {
if (m_deps.empty()) {
return;
}
//detect strong components
rule_dependencies::iterator it = m_deps.begin();
rule_dependencies::iterator end = m_deps.end();
for (; it!=end; ++it) {
T * el = it->m_key;
//we take a note of the preorder number with which this sweep started
m_first_preorder = m_next_preorder;
traverse(el);
}
//do topological sorting
//degres of components (number of inter-component edges ending up in the component)
svector<unsigned> in_degrees;
in_degrees.resize(m_components.size());
//init in_degrees
it = m_deps.begin();
end = m_deps.end();
for (; it != end; ++it) {
T * el = it->m_key;
item_set * out_edges = it->m_value;
unsigned el_comp;
VERIFY( m_component_nums.find(el, el_comp) );
item_set::iterator eit = out_edges->begin();
item_set::iterator eend = out_edges->end();
for (; eit!=eend; ++eit) {
T * tgt = *eit;
unsigned tgt_comp = m_component_nums.find(tgt);
if (el_comp != tgt_comp) {
in_degrees[tgt_comp]++;
}
}
}
// We put components whose indegree is zero to m_strats and assign its
// m_components entry to zero.
unsigned comp_cnt = m_components.size();
for (unsigned i = 0; i < comp_cnt; i++) {
if (in_degrees[i] == 0) {
m_strats.push_back(m_components[i]);
m_components[i] = 0;
}
}
SASSERT(!m_strats.empty()); //the component graph is acyclic and non-empty
//we remove edges from components with zero indegre building the topological ordering
unsigned strats_index = 0;
while (strats_index < m_strats.size()) { //m_strats.size() changes inside the loop!
item_set * comp = m_strats[strats_index];
item_set::iterator cit=comp->begin();
item_set::iterator cend=comp->end();
for (; cit!=cend; ++cit) {
T * el = *cit;
const item_set & deps = m_deps.get_deps(el);
item_set::iterator eit=deps.begin();
item_set::iterator eend=deps.end();
for (; eit!=eend; ++eit) {
T * tgt = *eit;
unsigned tgt_comp;
VERIFY( m_component_nums.find(tgt, tgt_comp) );
//m_components[tgt_comp]==0 means the edge is intra-component.
//Otherwise it would go to another component, but it is not possible, since
//as m_components[tgt_comp]==0, its indegree has already reached zero.
if (m_components[tgt_comp]) {
SASSERT(in_degrees[tgt_comp]>0);
in_degrees[tgt_comp]--;
if (in_degrees[tgt_comp]==0) {
m_strats.push_back(m_components[tgt_comp]);
m_components[tgt_comp] = 0;
}
}
traverse(*cit);
}
}
strats_index++;
}
//we have managed to topologicaly order all the components
SASSERT(std::find_if(m_components.begin(), m_components.end(),
std::bind1st(std::not_equal_to<item_set*>(), (item_set*)0)) == m_components.end());
//reverse the strats array, so that the only the later components would depend on earlier ones
std::reverse(m_strats.begin(), m_strats.end());
SASSERT(m_pred_strat_nums.empty());
unsigned strat_cnt = m_strats.size();
for (unsigned strat_index=0; strat_index<strat_cnt; strat_index++) {
item_set * comp = m_strats[strat_index];
item_set::iterator cit=comp->begin();
item_set::iterator cend=comp->end();
for (; cit != cend; ++cit) {
T * el = *cit;
m_pred_strat_nums.insert(el, strat_index);
}
}
//finalize structures that are not needed anymore
m_preorder_nums.finalize();
m_stack_S.finalize();
m_stack_P.finalize();
m_component_nums.finalize();
m_components.finalize();
}
void rule_stratifier::display(std::ostream& out) const {
m_deps.display(out << "dependencies\n");
out << "strata\n";
for (unsigned i = 0; i < m_strats.size(); ++i) {
item_set::iterator it = m_strats[i]->begin();
item_set::iterator end = m_strats[i]->end();
for (; it != end; ++it) {
out << (*it)->get_name() << " ";
}
out << "\n";
}
}
};

282
src/muz/base/dl_rule_set.h Normal file
View file

@ -0,0 +1,282 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule_set.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-17.
Revision History:
--*/
#ifndef _DL_RULE_SET_H_
#define _DL_RULE_SET_H_
#include"obj_hashtable.h"
#include"dl_rule.h"
namespace datalog {
class rule_set;
class rule_dependencies {
public:
typedef obj_hashtable<func_decl> item_set;
typedef obj_map<func_decl, item_set * > deps_type;
typedef deps_type::iterator iterator;
private:
/**
Map (dependent -> set of master objects)
Each master object is also present as a key of the map, even if its master set
is empty.
*/
deps_type m_data;
context & m_context;
ptr_vector<expr> m_todo;
ast_mark m_visited;
//we need to take care with removing to avoid memory leaks
void remove_m_data_entry(func_decl * key);
//sometimes we need to return reference to an empty set,
//so we return reference to this one.
item_set m_empty_item_set;
item_set & ensure_key(func_decl * pred);
void insert(func_decl * depending, func_decl * master);
void populate(rule const* r);
public:
rule_dependencies(context& ctx);
rule_dependencies(const rule_dependencies & o, bool reversed = false);
~rule_dependencies();
void reset();
void populate(const rule_set & rules);
void populate(unsigned n, rule * const * rules);
void restrict(const item_set & allowed);
void remove(func_decl * itm);
void remove(const item_set & to_remove);
bool empty() const { return m_data.empty(); }
const item_set & get_deps(func_decl * f) const;
/**
\brief Number of predicates \c f depends on.
*/
unsigned in_degree(func_decl * f) const { return get_deps(f).size(); }
/**
\brief Number of predicates that depend on \c f.
*/
unsigned out_degree(func_decl * f) const;
/**
\brief If the rependency graph is acyclic, put all elements into \c res
ordered so that elements can depend only on elements that are before them.
If the graph is not acyclic, return false.
*/
bool sort_deps(ptr_vector<func_decl> & res);
iterator begin() const { return m_data.begin(); }
iterator end() const { return m_data.end(); }
void display( std::ostream & out ) const;
};
class rule_stratifier {
public:
typedef func_decl T;
typedef obj_hashtable<T> item_set;
typedef ptr_vector<item_set> comp_vector;
typedef obj_map<T, item_set *> deps_type;
private:
const rule_dependencies & m_deps;
comp_vector m_strats;
obj_map<T, unsigned> m_preorder_nums;
ptr_vector<T> m_stack_S;
ptr_vector<T> m_stack_P;
obj_map<T, unsigned> m_component_nums;
comp_vector m_components;
obj_map<T, unsigned> m_pred_strat_nums;
unsigned m_next_preorder;
unsigned m_first_preorder;
/**
Finds strongly connected components using the Gabow's algorithm.
*/
void traverse(T* el);
/**
Calls \c traverse to identify strognly connected components and then
orders them using topological sorting.
*/
void process();
public:
/**
\remark The \c stratifier object keeps a reference to the \c deps object, so
it must exist for the whole lifetime of the \c stratifier object.
*/
rule_stratifier(const rule_dependencies & deps)
: m_deps(deps), m_next_preorder(0)
{
process();
}
~rule_stratifier();
/**
Return vector of components ordered so that the only dependencies are from
later components to earlier.
*/
const comp_vector & get_strats() const { return m_strats; }
unsigned get_predicate_strat(func_decl * pred) const;
void display( std::ostream & out ) const;
};
/**
\brief Datalog "Program" (aka rule set).
*/
class rule_set {
friend class rule_dependencies;
public:
typedef ptr_vector<func_decl_set> pred_set_vector;
typedef obj_map<func_decl, rule_vector*> decl2rules;
private:
typedef obj_map<func_decl, func_decl_set*> decl2deps;
context & m_context;
rule_manager & m_rule_manager;
rule_ref_vector m_rules; //!< all rules
decl2rules m_head2rules; //!< mapping from head symbol to rules.
rule_dependencies m_deps; //!< dependencies
scoped_ptr<rule_stratifier> m_stratifier; //!< contains stratifier object iff the rule_set is closed
func_decl_set m_output_preds; //!< output predicates
obj_map<func_decl,func_decl*> m_orig2pred;
obj_map<func_decl,func_decl*> m_pred2orig;
func_decl_ref_vector m_refs;
//sometimes we need to return reference to an empty rule_vector,
//so we return reference to this one.
rule_vector m_empty_rule_vector;
void compute_deps();
void compute_tc_deps();
bool stratified_negation();
public:
rule_set(context & ctx);
rule_set(const rule_set & rs);
~rule_set();
ast_manager & get_manager() const;
rule_manager & get_rule_manager() const { return const_cast<rule_manager&>(m_rule_manager); }
context& get_context() const { return m_context; }
void inherit_predicates(rule_set const& other);
void inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred);
func_decl* get_orig(func_decl* pred) const;
func_decl* get_pred(func_decl* orig) const;
/**
\brief Add rule \c r to the rule set.
*/
void add_rule(rule * r);
/**
\brief Remove rule \c r from the rule set.
*/
void del_rule(rule * r);
/**
\brief Add all rules from a different rule_set.
*/
void add_rules(const rule_set& src);
void replace_rules(const rule_set& other);
/**
\brief This method should be invoked after all rules are added to the rule set.
It will check if the negation is indeed stratified.
Return true if succeeded.
\remark If new rules are added, the rule_set will be "reopen".
*/
bool close();
void ensure_closed();
/**
\brief Undo the effect of the \c close() operation.
*/
void reopen();
bool is_closed() const { return m_stratifier != 0; }
unsigned get_num_rules() const { return m_rules.size(); }
bool empty() const { return m_rules.size() == 0; }
rule * get_rule(unsigned i) const { return m_rules[i]; }
rule * last() const { return m_rules[m_rules.size()-1]; }
rule_ref_vector const& get_rules() const { return m_rules; }
const rule_vector & get_predicate_rules(func_decl * pred) const;
bool contains(func_decl* pred) const { return m_head2rules.contains(pred); }
const rule_stratifier & get_stratifier() const {
SASSERT(m_stratifier);
return *m_stratifier;
}
const pred_set_vector & get_strats() const;
unsigned get_predicate_strat(func_decl * pred) const;
const rule_dependencies & get_dependencies() const { SASSERT(is_closed()); return m_deps; }
// split predicats into founded and non-founded.
void split_founded_rules(func_decl_set& founded, func_decl_set& non_founded);
void reset();
void set_output_predicate(func_decl * pred) { m_refs.push_back(pred); m_output_preds.insert(pred); }
bool is_output_predicate(func_decl * pred) const { return m_output_preds.contains(pred); }
const func_decl_set & get_output_predicates() const { return m_output_preds; }
func_decl* get_output_predicate() const { SASSERT(m_output_preds.size() == 1); return *m_output_preds.begin(); }
void display(std::ostream & out) const;
/**
\brief Output rule dependencies.
The rule set must be closed before calling this function.
*/
void display_deps(std::ostream & out) const;
typedef rule * const * iterator;
iterator begin() const { return m_rules.c_ptr(); }
iterator end() const { return m_rules.c_ptr()+m_rules.size(); }
decl2rules::iterator begin_grouped_rules() const { return m_head2rules.begin(); }
decl2rules::iterator end_grouped_rules() const { return m_head2rules.end(); }
};
inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; }
};
#endif /* _DL_RULE_SET_H_ */

View file

@ -0,0 +1,84 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule_subsumption_index.cpp
Abstract:
Subsumption index for rules.
Currently an underapproximation (fails to identify some subsumptions).
Author:
Krystof Hoder (t-khoder) 2011-10-10.
Revision History:
--*/
#include <sstream>
#include "ast_pp.h"
#include "dl_rule_subsumption_index.h"
namespace datalog {
// -----------------------------------
//
// rule_subsumption_index
//
// -----------------------------------
void rule_subsumption_index::handle_unconditioned_rule(rule * r) {
SASSERT(r->get_tail_size()==0);
app * head = r->get_head();
func_decl * pred = head->get_decl();
app_set * head_set;
if(!m_unconditioned_heads.find(pred, head_set)) {
head_set = alloc(app_set);
m_unconditioned_heads.insert(pred, head_set);
}
head_set->insert(head);
}
void rule_subsumption_index::add(rule * r) {
m_ref_holder.push_back(r);
if(r->get_tail_size()==0) {
handle_unconditioned_rule(r);
}
m_rule_set.insert(r);
}
bool rule_subsumption_index::is_subsumed(app * query) {
func_decl * pred = query->get_decl();
app_set * head_set;
if(m_unconditioned_heads.find(pred, head_set)) {
if(head_set->contains(query)) {
return true;
}
}
return false;
}
bool rule_subsumption_index::is_subsumed(rule * r) {
app * head = r->get_head();
if(is_subsumed(head)) {
return true;
}
if(m_rule_set.contains(r)) {
return true;
}
return false;
}
};

View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule_subsumption_index.h
Abstract:
Subsumption index for rules.
Currently an underapproximation (fails to identify some subsumptions).
Author:
Krystof Hoder (t-khoder) 2011-10-10.
Revision History:
--*/
#ifndef _DL_RULE_SUBSUMPTION_INDEX_H_
#define _DL_RULE_SUBSUMPTION_INDEX_H_
#include "dl_context.h"
namespace datalog {
class rule_subsumption_index {
//private and undefined copy constroctor
rule_subsumption_index(rule_subsumption_index const&);
//private and undefined operator=
rule_subsumption_index& operator=(rule_subsumption_index const&);
typedef obj_hashtable<app> app_set;
ast_manager & m;
context & m_context;
rule_ref_vector m_ref_holder;
obj_map<func_decl, app_set *> m_unconditioned_heads;
hashtable<rule *, rule_hash_proc, rule_eq_proc> m_rule_set;
void handle_unconditioned_rule(rule * r);
public:
rule_subsumption_index(context & ctx) :
m(ctx.get_manager()),
m_context(ctx),
m_ref_holder(ctx.get_rule_manager()) {}
~rule_subsumption_index() {
reset_dealloc_values(m_unconditioned_heads);
}
void add(rule * r);
bool is_subsumed(rule * r);
bool is_subsumed(app * query);
};
};
#endif /* _DL_RULE_SUBSUMPTION_INDEX_H_ */

View file

@ -0,0 +1,159 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule_transformer.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-20.
Revision History:
--*/
#include <algorithm>
#include<typeinfo>
#include"dl_context.h"
#include"dl_rule_transformer.h"
#include"stopwatch.h"
namespace datalog {
rule_transformer::rule_transformer(context & ctx)
: m_context(ctx), m_rule_manager(m_context.get_rule_manager()), m_dirty(false) {
}
rule_transformer::~rule_transformer() {
reset();
}
void rule_transformer::reset() {
plugin_vector::iterator it = m_plugins.begin();
plugin_vector::iterator end = m_plugins.end();
for(; it!=end; ++it) {
dealloc(*it);
}
m_plugins.reset();
m_dirty = false;
}
void rule_transformer::cancel() {
plugin_vector::iterator it = m_plugins.begin();
plugin_vector::iterator end = m_plugins.end();
for(; it!=end; ++it) {
(*it)->cancel();
}
}
struct rule_transformer::plugin_comparator {
bool operator()(rule_transformer::plugin * p1, rule_transformer::plugin * p2) {
return p1->get_priority() > p2->get_priority();
}
};
void rule_transformer::ensure_ordered() {
if (m_dirty) {
std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator());
m_dirty = false;
}
}
void rule_transformer::register_plugin(plugin * p) {
m_plugins.push_back(p);
p->attach(*this);
m_dirty=true;
}
bool rule_transformer::operator()(rule_set & rules) {
ensure_ordered();
bool modified = false;
TRACE("dl_rule_transf",
tout<<"init:\n";
rules.display(tout);
);
rule_set* new_rules = alloc(rule_set, rules);
plugin_vector::iterator it = m_plugins.begin();
plugin_vector::iterator end = m_plugins.end();
for(; it!=end && !m_context.canceled(); ++it) {
plugin & p = **it;
IF_VERBOSE(1, verbose_stream() << "(transform " << typeid(p).name() << "...";);
stopwatch sw;
sw.start();
rule_set * new_rules1 = p(*new_rules);
sw.stop();
double sec = sw.get_seconds();
if (sec < 0.001) sec = 0.0;
if (!new_rules1) {
IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";);
continue;
}
if (p.can_destratify_negation() &&
!new_rules1->is_closed() &&
!new_rules1->close()) {
warning_msg("a rule transformation skipped "
"because it destratified negation");
dealloc(new_rules1);
IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";);
continue;
}
modified = true;
dealloc(new_rules);
new_rules = new_rules1;
new_rules->ensure_closed();
IF_VERBOSE(1, verbose_stream() << new_rules->get_num_rules() << " rules " << sec << "s)\n";);
TRACE("dl_rule_transf",
tout << typeid(p).name()<<":\n";
new_rules->display(tout);
);
}
if (modified) {
rules.replace_rules(*new_rules);
}
dealloc(new_rules);
return modified;
}
//------------------------------
//
//rule_transformer::plugin
//
//------------------------------
void rule_transformer::plugin::remove_duplicate_tails(app_ref_vector& tail, svector<bool>& tail_neg)
{
//one set for positive and one for negative
obj_hashtable<app> tail_apps[2];
unsigned i=0;
while(i<tail.size()) {
unsigned set_idx = tail_neg[i] ? 1 : 0;
if (tail_apps[set_idx].contains(tail[i].get())) {
if (i!=tail.size()-1) {
tail[i] = tail.back();
tail_neg[i] = tail_neg.back();
}
tail.pop_back();
tail_neg.pop_back();
}
else {
tail_apps[set_idx].insert(tail[i].get());
i++;
}
}
}
};

View file

@ -0,0 +1,117 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule_transformer.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-20.
Revision History:
--*/
#ifndef _DL_RULE_TRANSFORMER_H_
#define _DL_RULE_TRANSFORMER_H_
#include"map.h"
#include"vector.h"
#include"dl_rule.h"
#include"dl_rule_set.h"
namespace datalog {
class context;
class rule_transformer {
public:
class plugin;
private:
friend class plugin;
typedef svector<plugin*> plugin_vector;
struct plugin_comparator;
context & m_context;
rule_manager & m_rule_manager;
bool m_dirty;
svector<plugin*> m_plugins;
void ensure_ordered();
public:
rule_transformer(context & ctx);
~rule_transformer();
/**
\brief Reset all registered transformers.
*/
void reset();
void cancel();
/**
\brief Add a plugin for rule transformation.
The rule_transformer object takes ownership of the plugin object.
*/
void register_plugin(plugin * p);
/**
\brief Transform the rule set using the registered transformation plugins. If the rule
set has changed, return true; otherwise return false.
*/
bool operator()(rule_set & rules);
};
class rule_transformer::plugin {
friend class rule_transformer;
unsigned m_priority;
bool m_can_destratify_negation;
rule_transformer * m_transformer;
void attach(rule_transformer & transformer) { m_transformer = &transformer; }
protected:
/**
\brief Create a plugin object for rule_transformer.
The priority argument determines in what order will rules be applied to the rules
(higher priority plugins will be applied first).
*/
plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority),
m_can_destratify_negation(can_destratify_negation), m_transformer(0) {}
public:
virtual ~plugin() {}
unsigned get_priority() { return m_priority; }
bool can_destratify_negation() const { return m_can_destratify_negation; }
virtual void cancel() {}
/**
\brief Return \c rule_set object with containing transformed rules or 0 if no
transformation was done.
The caller takes ownership of the returned \c rule_set object.
*/
virtual rule_set * operator()(rule_set const & source) = 0;
/**
Removes duplicate tails.
*/
static void remove_duplicate_tails(app_ref_vector& tail, svector<bool>& tail_neg);
};
};
#endif /* _DL_RULE_TRANSFORMER_H_ */

667
src/muz/base/dl_util.cpp Normal file
View file

@ -0,0 +1,667 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_util.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-20.
Revision History:
--*/
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WINDOWS
#include <windows.h>
#endif
#include"ast_pp.h"
#include"bool_rewriter.h"
#include"for_each_expr.h"
#include"scoped_proof.h"
#include"dl_context.h"
#include"dl_rule.h"
#include"dl_util.h"
#include"stopwatch.h"
namespace datalog {
verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(0) {
IF_VERBOSE(m_lvl,
(verbose_stream() << msg << "...").flush();
m_sw = alloc(stopwatch);
m_sw->start(););
}
verbose_action::~verbose_action() {
double sec = 0.0;
if (m_sw) m_sw->stop();
sec = m_sw?m_sw->get_seconds():0.0;
if (sec < 0.001) sec = 0.0;
IF_VERBOSE(m_lvl,
(verbose_stream() << sec << "s\n").flush();
);
dealloc(m_sw);
}
bool contains_var(expr * trm, unsigned var_idx) {
ptr_vector<sort> vars;
::get_free_vars(trm, vars);
return var_idx < vars.size() && vars[var_idx] != 0;
}
unsigned count_variable_arguments(app * pred)
{
SASSERT(is_uninterp(pred));
unsigned res = 0;
unsigned n = pred->get_num_args();
for (unsigned i = 0; i < n; i++) {
expr * arg = pred->get_arg(i);
if (is_var(arg)) {
res++;
}
}
return res;
}
void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var,
sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) {
expr_ref_buffer new_args(m);
unsigned n = pred->get_num_args();
for (unsigned i = 0; i < n; i++) {
expr * arg = pred->get_arg(i);
if (m.is_value(arg)) {
new_args.push_back(arg);
}
else {
SASSERT(is_var(arg));
int vidx = to_var(arg)->get_idx();
var * new_var = 0;
if (!varidx2var.find(vidx, new_var)) {
new_var = m.mk_var(next_idx, to_var(arg)->get_sort());
next_idx++;
varidx2var.insert(vidx, new_var);
if (non_local_vars.contains(vidx)) {
// other predicates used this variable... so it should be in the domain of the filter
new_rule_domain.push_back(to_var(arg)->get_sort());
new_rule_args.push_back(new_var);
}
}
SASSERT(new_var != 0);
new_args.push_back(new_var);
}
}
new_pred = m.mk_app(pred->get_decl(), new_args.size(), new_args.c_ptr());
}
void apply_subst(expr_ref_vector& tgt, expr_ref_vector const& sub) {
ast_manager& m = tgt.get_manager();
var_subst vs(m, false);
expr_ref tmp(m);
for (unsigned i = 0; i < tgt.size(); ++i) {
if (tgt[i].get()) {
vs(tgt[i].get(), sub.size(), sub.c_ptr(), tmp);
tgt[i] = tmp;
}
else {
tgt[i] = sub[i];
}
}
for (unsigned i = tgt.size(); i < sub.size(); ++i) {
tgt.push_back(sub[i]);
}
}
void output_predicate(context & ctx, app * f, std::ostream & out)
{
func_decl * pred_decl = f->get_decl();
unsigned arity = f->get_num_args();
out << pred_decl->get_name() << '(';
for (unsigned i = 0; i < arity; i++) {
expr * arg = f->get_arg(i);
if (i != 0) {
out << ',';
}
if (is_var(arg)) {
out << "#" << to_var(arg)->get_idx();
}
else {
out << mk_pp(arg, ctx.get_manager());
}
}
out << ")";
}
void display_predicate(context & ctx, app * f, std::ostream & out)
{
output_predicate(ctx, f, out);
out << "\n";
}
void display_fact(context & ctx, app * f, std::ostream & out)
{
func_decl * pred_decl = f->get_decl();
unsigned arity = f->get_num_args();
out << "\t(";
for(unsigned i = 0; i < arity; i++) {
if (i != 0) {
out << ',';
}
expr * arg = f->get_arg(i);
uint64 sym_num;
SASSERT(is_app(arg));
VERIFY( ctx.get_decl_util().is_numeral_ext(to_app(arg), sym_num) );
relation_sort sort = pred_decl->get_domain(i);
out << ctx.get_argument_name(pred_decl, i) << '=';
ctx.print_constant_name(sort, sym_num, out);
out << '(' << sym_num << ')';
}
out << ")\n";
}
void idx_set_union(idx_set & tgt, const idx_set & src) {
idx_set::iterator vit = src.begin();
idx_set::iterator vend = src.end();
for(;vit!=vend;++vit) {
tgt.insert(*vit);
}
}
bool variable_intersection::values_match(const expr * v1, const expr * v2)
{
//return !m_manager.are_distinct(v1, v2);
return v1==v2;
}
bool variable_intersection::args_match(const app * f1, const app * f2)
{
unsigned n=size();
for (unsigned i = 0; i < n; i++) {
unsigned f1_index, f2_index;
get(i, f1_index, f2_index);
if (!values_match(f1->get_arg(f1_index),f2->get_arg(f2_index))) {
return false;
}
}
return true;
}
bool variable_intersection::args_self_match(const app * f)
{
if(!args_match(f,f)) {
return false;
}
unsigned n = m_const_indexes.size();
for(unsigned i=0; i<n; i++) {
unsigned f_index = m_const_indexes[i];
if(!values_match(f->get_arg(f_index), m_consts[i].get())) {
return false;
}
}
return true;
}
void variable_intersection::populate_self(const app * a)
{
SASSERT(is_uninterp(a));
//TODO: optimize quadratic complexity
//TODO: optimize number of checks when variable occurs multiple times
unsigned arity = a->get_num_args();
for(unsigned i1=0; i1<arity; i1++) {
expr * e1=a->get_arg(i1);
if(is_var(e1)) {
var* v1=to_var(e1);
for(unsigned i2=i1+1; i2<arity; i2++) {
expr * e2=a->get_arg(i2);
if(!is_var(e2)) {
continue;
}
var* v2=to_var(e2);
if(v1->get_idx()==v2->get_idx()) {
add_pair(i1, i2);
}
}
}
else {
SASSERT(is_app(e1));
app * c1 = to_app(e1);
SASSERT(c1->get_num_args()==0); //c1 must be a constant
m_const_indexes.push_back(i1);
m_consts.push_back(c1);
SASSERT(m_const_indexes.size()==m_consts.size());
}
}
}
void rule_counter::count_rule_vars(ast_manager & m, const rule * r, int coef) {
reset();
count_vars(m, r->get_head(), 1);
unsigned n = r->get_tail_size();
for (unsigned i = 0; i < n; i++) {
count_vars(m, r->get_tail(i), coef);
}
}
unsigned rule_counter::get_max_rule_var(const rule & r) {
m_todo.push_back(r.get_head());
m_scopes.push_back(0);
unsigned n = r.get_tail_size();
bool has_var = false;
for (unsigned i = 0; i < n; i++) {
m_todo.push_back(r.get_tail(i));
m_scopes.push_back(0);
}
return get_max_var(has_var);
}
void del_rule(horn_subsume_model_converter* mc, rule& r) {
if (mc) {
ast_manager& m = mc->get_manager();
expr_ref_vector body(m);
for (unsigned i = 0; i < r.get_tail_size(); ++i) {
if (r.is_neg_tail(i)) {
body.push_back(m.mk_not(r.get_tail(i)));
}
else {
body.push_back(r.get_tail(i));
}
}
TRACE("dl_dr",
tout << r.get_decl()->get_name() << "\n";
for (unsigned i = 0; i < body.size(); ++i) {
tout << mk_pp(body[i].get(), m) << "\n";
});
mc->insert(r.get_head(), body.size(), body.c_ptr());
}
}
void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) {
if (!pc) return;
ast_manager& m = s1.get_manager();
expr_ref fml1(m), fml2(m), fml3(m);
r1.to_formula(fml1);
r2.to_formula(fml2);
res.to_formula(fml3);
vector<expr_ref_vector> substs;
svector<std::pair<unsigned, unsigned> > positions;
substs.push_back(s1);
substs.push_back(s2);
scoped_proof _sc(m);
proof_ref pr(m);
proof_ref_vector premises(m);
premises.push_back(m.mk_asserted(fml1));
premises.push_back(m.mk_asserted(fml2));
positions.push_back(std::make_pair(idx+1, 0));
TRACE("dl",
tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n";
for (unsigned i = 0; i < s1.size(); ++i) {
tout << mk_pp(s1[i], m) << " ";
}
tout << "\n";
tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n";
for (unsigned i = 0; i < s2.size(); ++i) {
tout << mk_pp(s2[i], m) << " ";
}
tout << "\n";
);
pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml3, positions, substs);
pc->insert(pr);
}
void resolve_rule(rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) {
if (!r1.get_proof()) {
return;
}
SASSERT(r2.get_proof());
ast_manager& m = s1.get_manager();
expr_ref fml(m);
res.to_formula(fml);
vector<expr_ref_vector> substs;
svector<std::pair<unsigned, unsigned> > positions;
substs.push_back(s1);
substs.push_back(s2);
scoped_proof _sc(m);
proof_ref pr(m);
proof_ref_vector premises(m);
premises.push_back(r1.get_proof());
premises.push_back(r2.get_proof());
positions.push_back(std::make_pair(idx+1, 0));
TRACE("dl",
tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n";
for (unsigned i = 0; i < s1.size(); ++i) {
tout << mk_pp(s1[i], m) << " ";
}
tout << "\n";
tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n";
for (unsigned i = 0; i < s2.size(); ++i) {
tout << mk_pp(s2[i], m) << " ";
}
tout << "\n";
);
pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs);
res.set_proof(m, pr);
}
class skip_model_converter : public model_converter {
public:
skip_model_converter() {}
virtual model_converter * translate(ast_translation & translator) {
return alloc(skip_model_converter);
}
};
model_converter* mk_skip_model_converter() { return alloc(skip_model_converter); }
class skip_proof_converter : public proof_converter {
virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) {
SASSERT(num_source == 1);
result = source[0];
}
virtual proof_converter * translate(ast_translation & translator) {
return alloc(skip_proof_converter);
}
};
proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); }
void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt) {
SASSERT(tgt.empty());
unsigned src_sz = src.size();
unsigned src_ofs = src_sz-1;
unsigned max_var_idx = 0;
for(unsigned i=0; i<src_sz; i++) {
if(!src[i]) {
continue;
}
SASSERT(is_var(src[i]));
unsigned var_idx = to_var(src[i])->get_idx();
if(var_idx>max_var_idx) {
max_var_idx=var_idx;
}
}
unsigned tgt_sz = max_var_idx+1;
unsigned tgt_ofs = tgt_sz-1;
tgt.resize(tgt_sz, 0);
for(unsigned i=0; i<src_sz; i++) {
expr * e = src[src_ofs-i];
if(!e) {
continue;
}
var * v = to_var(e);
unsigned var_idx = v->get_idx();
tgt[tgt_ofs-var_idx] = m.mk_var(i, v->get_sort());
}
}
void print_renaming(const expr_ref_vector & cont, std::ostream & out) {
unsigned len = cont.size();
out << "(";
for(int i=len-1; i>=0; i--) {
out << (len-1-i) <<"->";
if(cont.get(i)==0) {
out << "{none}";
}
else {
out << to_var(cont.get(i))->get_idx();
}
if(i!=0) { out << ","; }
}
out << ")\n";
}
void cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle) {
try_remove_cycle_from_permutation(permutation, cycle);
DEBUG_CODE(
//here we assert that there is at most one cycle in the permutation
unsigned_vector aux;
SASSERT(!try_remove_cycle_from_permutation(permutation, aux));
);
}
bool try_remove_cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle) {
SASSERT(cycle.empty());
DEBUG_CODE(
counter ctr;
ctr.count(permutation);
SASSERT(permutation.empty() || ctr.get_max_positive()==permutation.size()-1);
SASSERT(permutation.empty() || ctr.get_positive_count()==permutation.size());
);
unsigned sz = permutation.size();
for(unsigned i=0; i<sz; i++) {
if(i==permutation[i]) {
continue;
}
unsigned prev_i = i;
for(;;) {
cycle.push_back(prev_i);
unsigned next_i = permutation[prev_i];
permutation[prev_i] = prev_i;
if(next_i==i) {
break;
}
prev_i = next_i;
}
return true;
}
return false;
}
void collect_sub_permutation(const unsigned_vector & permutation, const unsigned_vector & translation,
unsigned_vector & res, bool & identity) {
SASSERT(res.empty());
identity = true;
unsigned sz = permutation.size();
for(unsigned new_i=0; new_i<sz; new_i++) {
unsigned idx = permutation[new_i];
bool is_selected = translation[idx]!=UINT_MAX;
if(is_selected) {
unsigned sel_idx = translation[idx];
if(!res.empty() && sel_idx!=res.back()+1) {
identity = false;
}
res.push_back(sel_idx);
}
}
}
void collect_and_transform(const unsigned_vector & src, const unsigned_vector & translation,
unsigned_vector & res) {
unsigned n = src.size();
for(unsigned i=0; i<n; i++) {
unsigned translated = translation[src[i]];
if(translated!=UINT_MAX) {
res.push_back(translated);
}
}
}
void transform_set(const unsigned_vector & map, const idx_set & src, idx_set & result) {
idx_set::iterator it = src.begin();
idx_set::iterator end = src.end();
for(; it!=end; ++it) {
result.insert(map[*it]);
}
}
void add_sequence(unsigned start, unsigned count, unsigned_vector & v) {
unsigned after_last = start+count;
for(unsigned i=start; i<after_last; i++) {
v.push_back(i);
}
}
// -----------------------------------
//
// misc helper functions (not datalog related)
//
// -----------------------------------
void get_file_names(std::string directory, std::string extension, bool traverse_subdirs,
string_vector & res) {
if(directory[directory.size()-1]!='\\' && directory[directory.size()-1]!='/') {
#ifdef _WINDOWS
directory+='\\';
#else
directory+='/';
#endif
}
#ifdef _WINDOWS
WIN32_FIND_DATAA findFileData;
HANDLE hFind;
std::string filePattern = directory+"*."+extension;
hFind = FindFirstFileA(filePattern.c_str(), &findFileData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
char const* name = findFileData.cFileName;
size_t len = strlen(name);
if (len > extension.size() && extension == std::string(name+len-extension.size())) {
res.push_back(directory+std::string(name));
}
} while(FindNextFileA(hFind, &findFileData));
FindClose(hFind);
}
if(traverse_subdirs) {
std::string subdirPattern = directory+"*.*";
hFind = FindFirstFileA(subdirPattern.c_str(), &findFileData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if(findFileData.cFileName[0]=='.') {
continue;
}
get_file_names(directory+findFileData.cFileName, extension, traverse_subdirs, res);
} while(FindNextFileA(hFind, &findFileData));
FindClose(hFind);
}
}
#else
NOT_IMPLEMENTED_YET();
#endif
}
bool file_exists(std::string name) {
struct stat st;
if(stat(name.c_str(),&st) == 0) {
return true;
}
return false;
}
bool is_directory(std::string name) {
if(!file_exists(name)) {
return false;
}
struct stat status;
stat(name.c_str(), &status);
return (status.st_mode&S_IFDIR)!=0;
}
std::string get_file_name_without_extension(std::string name) {
size_t slash_index = name.find_last_of("\\/");
size_t dot_index = name.rfind(".");
size_t ofs = (slash_index==std::string::npos) ? 0 : slash_index+1;
size_t count = (dot_index!=std::string::npos && dot_index>ofs) ?
(dot_index-ofs) : std::string::npos;
return name.substr(ofs, count);
}
bool string_to_uint64(const char * s, uint64 & res) {
#if _WINDOWS
int converted = sscanf_s(s, "%I64u", &res);
#else
int converted = sscanf(s, "%llu", &res);
#endif
if(converted==0) {
return false;
}
SASSERT(converted==1);
return true;
}
bool read_uint64(const char * & s, uint64 & res) {
static const uint64 max_but_one_digit = ULLONG_MAX/10;
static const uint64 max_but_one_digit_safe = (ULLONG_MAX-9)/10;
if(*s<'0' || *s>'9') {
return false;
}
res=*s-'0';
s++;
while(*s>='0' && *s<='9') {
if(res>max_but_one_digit_safe) {
if(res>max_but_one_digit) {
return false; //overflow
}
res*=10;
char digit = *s-'0';
if(static_cast<int>(ULLONG_MAX-res)-digit<0) {
return false; //overflow
}
res+=digit;
}
else {
res*=10;
res+=*s-'0';
s++;
}
}
return true;
}
std::string to_string(uint64 num) {
std::stringstream stm;
stm<<num;
return stm.str();
}
};

734
src/muz/base/dl_util.h Normal file
View file

@ -0,0 +1,734 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_util.h
Abstract:
Datalog utility function and structures.
Author:
Leonardo de Moura (leonardo) 2010-05-20.
Revision History:
--*/
#ifndef _DL_UTIL_H_
#define _DL_UTIL_H_
#include"ast.h"
#include"hashtable.h"
#include"obj_hashtable.h"
#include"uint_set.h"
#include"horn_subsume_model_converter.h"
#include"replace_proof_converter.h"
#include"substitution.h"
#include"ast_counter.h"
#include"statistics.h"
#include"lbool.h"
#include"qe_util.h"
namespace datalog {
class context;
class rule;
class relation_base;
class relation_manager;
class table_base;
class pentagon_relation;
class relation_fact;
class relation_signature;
class verbose_action {
unsigned m_lvl;
class stopwatch* m_sw;
public:
verbose_action(char const* msg, unsigned lvl = 1);
~verbose_action();
};
enum PDR_CACHE_MODE {
NO_CACHE,
HASH_CACHE,
CONSTRAINT_CACHE,
LAST_CACHE_MODE
};
struct std_string_hash_proc {
unsigned operator()(const std::string & s) const
{ return string_hash(s.c_str(), static_cast<unsigned>(s.length()), 17); }
};
// typedef int_hashtable<int_hash, default_eq<int> > idx_set;
typedef uint_set idx_set;
typedef idx_set var_idx_set;
typedef u_map<var *> varidx2var_map;
typedef obj_hashtable<func_decl> func_decl_set; //!< Rule dependencies.
typedef vector<std::string> string_vector;
bool contains_var(expr * trm, unsigned var_idx);
/**
\brief Return number of arguments of \c pred that are variables
*/
unsigned count_variable_arguments(app * pred);
template<typename T>
void copy_nonvariables(app * src, T& tgt)
{
unsigned n = src->get_num_args();
for (unsigned i = 0; i < n; i++) {
expr * arg = src->get_arg(i);
if (!is_var(arg)) {
tgt[i]=arg;
}
}
}
/**
\brief Auxiliary function used to create a tail based on \c pred for a new rule.
The variables in \c pred are re-assigned using \c next_idx and \c varidx2var.
A variable is considered non-local to the rule if it is in the set \c non_local_vars.
Non-local variables are coppied to new_rule_args, and their sorts to \c new_rule_domain.
The new predicate is stored in \c new_pred.
*/
void mk_new_rule_tail(ast_manager & m, app * pred,
var_idx_set const & non_local_vars,
unsigned & next_idx, varidx2var_map & varidx2var,
sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args,
app_ref & new_pred);
/**
\brief Simpler version of the previous function. Initializes next_idx with 0, and
an empty varid2var
*/
inline void mk_new_rule_tail(ast_manager & m, app * pred,
var_idx_set const & non_local_vars,
sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args,
app_ref & new_pred) {
unsigned next_idx = 0;
varidx2var_map varidx2var;
mk_new_rule_tail(m, pred, non_local_vars, next_idx, varidx2var, new_rule_domain, new_rule_args, new_pred);
}
/**
\brief Print a predicate \c f to the stream \c out.
*/
void display_predicate(context & ctx, app * f, std::ostream & out);
/**
\brief Like \c display_predicate, just without the final '\n' character.
*/
void output_predicate(context & ctx, app * f, std::ostream & out);
/**
\brief Print a fact \c f to the stream \c out in a format conforming to Bddbddb.
*/
void display_fact(context & ctx, app * f, std::ostream & out);
class variable_intersection
{
bool values_match(const expr * v1, const expr * v2);
unsigned_vector m_args1;
unsigned_vector m_args2;
unsigned_vector m_const_indexes;
app_ref_vector m_consts;
static unsigned expr_cont_get_size(app * a) { return a->get_num_args(); }
static expr * expr_cont_get(app * a, unsigned i) { return a->get_arg(i); }
static unsigned expr_cont_get_size(const ptr_vector<expr> & v) { return v.size(); }
static unsigned expr_cont_get_size(const expr_ref_vector & v) { return v.size(); }
static expr * expr_cont_get(const ptr_vector<expr> & v, unsigned i) { return v[i]; }
static expr * expr_cont_get(const expr_ref_vector & v, unsigned i) { return v[i]; }
public:
variable_intersection(ast_manager & m) : m_consts(m) {}
unsigned size() const {
return m_args1.size();
}
const unsigned * get_cols1() const {
return m_args1.c_ptr();
}
const unsigned * get_cols2() const {
return m_args2.c_ptr();
}
bool empty() const {
return size()==0;
}
void get(unsigned i, unsigned & index1, unsigned & index2) const {
index1=m_args1[i];
index2=m_args2[i];
}
void reset() {
m_args1.reset();
m_args2.reset();
m_const_indexes.reset();
m_consts.reset();
}
bool args_match(const app * f1, const app * f2);
bool args_self_match(const app * f);
/**
\brief Fill arguments of \c f1 into corresponding positions in
\c tgt using its \c operator[].
*/
template<typename T>
void fill_into_second(const app * f1, T & tgt) const {
unsigned n=size();
for(unsigned i=0; i<n; i++) {
unsigned f1_index, tgt_index;
get(i, f1_index, tgt_index);
tgt[tgt_index]=f1->get_arg(f1_index);
}
}
void add_pair(unsigned idx1, unsigned idx2) {
m_args1.push_back(idx1);
m_args2.push_back(idx2);
}
/**
Find pairs of indexes of arguments of \c a1 and \c a2 that correspond to the same
variable. Here we do not detect the constant arguments in \c a1 and \c a2.
*/
template<typename T1, typename T2>
void populate(const T1 & a1, const T2 & a2)
{
//TODO: optimize quadratic complexity
//TODO: optimize number of checks when variable occurs multiple times
unsigned a1num = expr_cont_get_size(a1);
unsigned a2num = expr_cont_get_size(a2);
for(unsigned i1=0; i1<a1num; i1++) {
expr * e1=expr_cont_get(a1,i1);
if(!is_var(e1)) {
continue;
}
var* v1=to_var(e1);
for(unsigned i2=0; i2<a2num; i2++) {
expr * e2=expr_cont_get(a2,i2);
if(!is_var(e2)) {
continue;
}
var* v2=to_var(e2);
if(v1->get_idx()==v2->get_idx()) {
add_pair(i1, i2);
}
}
}
}
/**
Find pairs of indexes of arguments of \c a that correspond to the same variable
and indexes that correspond to a constant.
*/
void populate_self(const app * a);
};
template<class T>
void project_out_vector_columns(T & container, unsigned removed_col_cnt, const unsigned * removed_cols) {
if(removed_col_cnt==0) {
return;
}
unsigned n = container.size();
unsigned ofs = 1;
unsigned r_i = 1;
for(unsigned i=removed_cols[0]+1; i<n; i++) {
if(r_i!=removed_col_cnt && removed_cols[r_i]==i) {
r_i++;
ofs++;
continue;
}
container[i-ofs] = container[i];
}
if (r_i != removed_col_cnt) {
for (unsigned i = 0; i < removed_col_cnt; ++i) {
std::cout << removed_cols[i] << " ";
}
std::cout << " container size: " << n << "\n";
}
SASSERT(r_i==removed_col_cnt);
container.resize(n-removed_col_cnt);
}
template<class T, class M>
void project_out_vector_columns(ref_vector<T,M> & container, unsigned removed_col_cnt,
const unsigned * removed_cols) {
if(removed_col_cnt==0) {
return;
}
unsigned n = container.size();
unsigned ofs = 1;
int r_i = 1;
for(unsigned i=removed_cols[0]+1; i<n; i++) {
if(r_i!=removed_col_cnt && removed_cols[r_i]==i) {
r_i++;
ofs++;
continue;
}
container.set(i-ofs, container.get(i));
}
SASSERT(r_i==removed_col_cnt);
container.resize(n-removed_col_cnt);
}
template<class T>
void project_out_vector_columns(T & container, const unsigned_vector removed_cols) {
project_out_vector_columns(container, removed_cols.size(), removed_cols.c_ptr());
}
/**
\brief Take a single cycle permutation and store it in the form of a cycle.
The function modifies the \c permutation vector
*/
void cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle);
/**
\brief If \c permutation is an identity, return false. Otherwise remove one cycle from the
permutation, store it in the form of a cycle in \c cycle and return true.
Using this function one can retrieve all cycles in a permutation.
\c cycle must be empty before calling the function.
*/
bool try_remove_cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle);
void collect_sub_permutation(const unsigned_vector & permutation, const unsigned_vector & translation,
unsigned_vector & res, bool & identity);
template<class T>
void permutate_by_cycle(T & container, unsigned cycle_len, const unsigned * permutation_cycle) {
if(cycle_len<2) {
return;
}
typename T::data aux = container[permutation_cycle[0]];
for(unsigned i=1; i<cycle_len; i++) {
container[permutation_cycle[i-1]]=container[permutation_cycle[i]];
}
container[permutation_cycle[cycle_len-1]]=aux;
}
template<class T, class M>
void permutate_by_cycle(ref_vector<T,M> & container, unsigned cycle_len, const unsigned * permutation_cycle) {
if(cycle_len<2) {
return;
}
T * aux = container.get(permutation_cycle[0]);
for(unsigned i=1; i<cycle_len; i++) {
container.set(permutation_cycle[i-1], container.get(permutation_cycle[i]));
}
container.set(permutation_cycle[cycle_len-1], aux);
}
template<class T>
void permutate_by_cycle(T & container, const unsigned_vector permutation_cycle) {
permutate_by_cycle(container, permutation_cycle.size(), permutation_cycle.c_ptr());
}
class rule_counter : public var_counter {
public:
rule_counter(bool stay_non_negative = true): var_counter(stay_non_negative) {}
void count_rule_vars(ast_manager & m, const rule * r, int coef = 1);
unsigned get_max_rule_var(const rule& r);
};
void del_rule(horn_subsume_model_converter* mc, rule& r);
void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res);
void resolve_rule(rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res);
model_converter* mk_skip_model_converter();
proof_converter* mk_skip_proof_converter();
void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt);
void print_renaming(const expr_ref_vector & cont, std::ostream & out);
/**
\brief Update tgt with effect of applying substitution from 'sub' to it.
tgt is extended by variables that are substituted by 'sub'.
We use the convention that the entry at index 'i' corresponds to variable
with de-Bruijn index 'i'.
*/
void apply_subst(expr_ref_vector& tgt, expr_ref_vector const& sub);
// -----------------------------------
//
// container functions
//
// -----------------------------------
template<class Set1, class Set2>
void set_intersection(Set1 & tgt, const Set2 & src) {
svector<typename Set1::data> to_remove;
typename Set1::iterator vit = tgt.begin();
typename Set1::iterator vend = tgt.end();
for(;vit!=vend;++vit) {
typename Set1::data itm=*vit;
if(!src.contains(itm)) {
to_remove.push_back(itm);
}
}
while(!to_remove.empty()) {
tgt.remove(to_remove.back());
to_remove.pop_back();
}
}
template<class Set>
void set_difference(Set & tgt, const Set & to_remove) {
typename Set::iterator vit = to_remove.begin();
typename Set::iterator vend = to_remove.end();
for(;vit!=vend;++vit) {
typename Set::data itm=*vit;
tgt.remove(itm);
}
}
template<class Set1, class Set2>
void set_union(Set1 & tgt, const Set2 & to_add) {
typename Set2::iterator vit = to_add.begin();
typename Set2::iterator vend = to_add.end();
for(;vit!=vend;++vit) {
typename Set1::data itm=*vit;
tgt.insert(itm);
}
}
void idx_set_union(idx_set & tgt, const idx_set & src);
template<class T>
void unite_disjoint_maps(T & tgt, const T & src) {
typename T::iterator it = src.begin();
typename T::iterator end = src.end();
for(; it!=end; ++it) {
SASSERT(!tgt.contains(it->m_key));
tgt.insert(it->m_key, it->m_value);
}
}
template<class T, class U>
void collect_map_range(T & acc, const U & map) {
typename U::iterator it = map.begin();
typename U::iterator end = map.end();
for(; it!=end; ++it) {
acc.push_back(it->m_value);
}
}
template<class T>
void print_container(const T & begin, const T & end, std::ostream & out) {
T it = begin;
out << "(";
bool first = true;
for(; it!=end; ++it) {
if(first) { first = false; } else { out << ","; }
out << (*it);
}
out << ")";
}
template<class T>
void print_container(const T & cont, std::ostream & out) {
print_container(cont.begin(), cont.end(), out);
}
template<class T, class M>
void print_container(const ref_vector<T,M> & cont, std::ostream & out) {
print_container(cont.c_ptr(), cont.c_ptr() + cont.size(), out);
}
template<class T>
void print_map(const T & cont, std::ostream & out) {
typename T::iterator it = cont.begin();
typename T::iterator end = cont.end();
out << "(";
bool first = true;
for(; it!=end; ++it) {
if(first) { first = false; } else { out << ","; }
out << it->m_key << "->" << it->m_value;
}
out << ")";
}
template<class It, class V>
unsigned find_index(const It & begin, const It & end, const V & val) {
unsigned idx = 0;
It it = begin;
for(; it!=end; it++, idx++) {
if(*it==val) {
return idx;
}
}
return UINT_MAX;
}
template<class T, class U>
bool containers_equal(const T & begin1, const T & end1, const U & begin2, const U & end2) {
T it1 = begin1;
U it2 = begin2;
for(; it1!=end1 && it2!=end2; ++it1, ++it2) {
if(*it1!=*it2) {
return false;
}
}
return it1==end1 && it2==end2;
}
template<class T, class U>
bool vectors_equal(const T & c1, const U & c2) {
if(c1.size()!=c2.size()) {
return false;
}
typename T::data * it1 = c1.c_ptr();
typename T::data * end1 = c1.c_ptr()+c1.size();
typename U::data * it2 = c2.c_ptr();
for(; it1!=end1; ++it1, ++it2) {
if(*it1!=*it2) {
return false;
}
}
return true;
}
template<class T>
struct default_obj_chash {
unsigned operator()(T const& cont, unsigned i) const {
return cont[i]->hash();
}
};
template<class T>
unsigned obj_vector_hash(const T & cont) {
return get_composite_hash(cont, cont.size(),default_kind_hash_proc<T>(), default_obj_chash<T>());
}
template<class T>
struct obj_vector_hash_proc {
unsigned operator()(const T & cont) const {
return obj_vector_hash(cont);
}
};
template<class T>
struct svector_hash_proc {
unsigned operator()(const svector<typename T::data> & cont) const {
return svector_hash<T>()(cont);
}
};
template<class T>
struct vector_eq_proc {
bool operator()(const T & c1, const T & c2) const { return vectors_equal(c1, c2); }
};
template<class T>
void dealloc_ptr_vector_content(ptr_vector<T> & v) {
typename ptr_vector<T>::iterator it = v.begin();
typename ptr_vector<T>::iterator end = v.end();
for(; it!=end; ++it) {
dealloc(*it);
}
}
/**
\brief Add elements from an iterable object \c src into the vector \c vector.
*/
template<class VectType, class U>
void push_into_vector(VectType & vector, const U & src) {
typename U::iterator it = src.begin();
typename U::iterator end = src.end();
for(; it!=end; ++it) {
vector.push_back(*it);
}
}
template<class VectType, class U, class M>
void push_into_vector(VectType & vector, const ref_vector<U,M> & src) {
U * const * it = src.begin();
U * const * end = src.end();
for(; it!=end; ++it) {
vector.push_back(*it);
}
}
template<class SetType, class U>
void insert_into_set(SetType & tgt, const U & src) {
typename U::const_iterator it = src.begin();
typename U::const_iterator end = src.end();
for(; it!=end; ++it) {
tgt.insert(*it);
}
}
/**
\brief Remove the first occurence of \c el from \c v and return \c true. If
\c el is not present in \c v, return \c false. The order of elements in \c v
is not preserved.
*/
template<class T>
bool remove_from_vector(T & v, const typename T::data & el) {
unsigned sz = v.size();
for(unsigned i=0; i<sz; i++) {
if(v[i]==el) {
std::swap(v[i], v.back());
v.pop_back();
return true;
}
}
return false;
}
/**
\brief Reset and deallocate the values stored in a mapping of the form obj_map<Key, Value*>
*/
template<typename Key, typename Value, typename Hash, typename Eq>
void reset_dealloc_values(map<Key, Value*, Hash, Eq> & m) {
typename map<Key, Value*, Hash, Eq>::iterator it = m.begin();
typename map<Key, Value*, Hash, Eq>::iterator end = m.end();
for (; it != end; ++it) {
dealloc(it->m_value);
}
m.reset();
}
template<class T>
struct aux__index_comparator {
T* m_keys;
aux__index_comparator(T* keys) : m_keys(keys) {}
bool operator()(unsigned a, unsigned b) {
return m_keys[a]<m_keys[b];
}
};
template<class T, class U>
void sort_two_arrays(unsigned len, T* keys, U* vals) {
if(len<2) {
return;
}
if(len==2) {
if(keys[0]>keys[1]) {
std::swap(keys[0], keys[1]);
std::swap(vals[0], vals[1]);
}
return;
}
unsigned_vector numbers;
for(unsigned i=0; i<len; i++) {
numbers.push_back(i);
}
aux__index_comparator<T> cmp(keys);
std::sort(numbers.begin(), numbers.end(), cmp);
for(unsigned i=0; i<len; i++) {
unsigned prev_i = i;
for(;;) {
unsigned src_i = numbers[prev_i];
numbers[prev_i]=prev_i;
if(src_i==i) {
break;
}
std::swap(keys[prev_i], keys[src_i]);
std::swap(vals[prev_i], vals[src_i]);
prev_i = src_i;
}
}
}
/**
\brief Consider \c translation as a map from indexes to values. Iterate through \c src and store
transformed values of elements into \c res unless they are equal to \c UINT_MAX.
*/
void collect_and_transform(const unsigned_vector & src, const unsigned_vector & translation,
unsigned_vector & res);
/**
\brief Insert into \c res values of \c src transformed by \c map (understood as a function
from its indexes to the values stored in it).
*/
void transform_set(const unsigned_vector & map, const idx_set & src, idx_set & result);
void add_sequence(unsigned start, unsigned count, unsigned_vector & v);
template<class Container>
void add_sequence_without_set(unsigned start, unsigned count, const Container & complement, unsigned_vector & v) {
unsigned after_last = start+count;
for(unsigned i=start; i<after_last; i++) {
if(!complement.contains(i)) {
v.push_back(i);
}
}
}
// -----------------------------------
//
// filesystem functions
//
// -----------------------------------
void get_file_names(std::string directory, std::string extension, bool traverse_subdirs,
string_vector & res);
bool file_exists(std::string name);
bool is_directory(std::string name);
std::string get_file_name_without_extension(std::string name);
// -----------------------------------
//
// misc
//
// -----------------------------------
template<class T>
void universal_delete(T* ptr) {
dealloc(ptr);
}
/**
\brief If it is possible to convert the beginning of \c s to uint64,
store the result of conversion and return true; otherwise return false.
*/
bool string_to_uint64(const char * s, uint64 & res);
std::string to_string(uint64 num);
/**
\brief Read the sequence of decimal digits starting at \c s and interpret it as
uint64. If successful, \c res will contain the read number and \c s will point
to the first non-digit character, and true is returned. If the first character
is not a digit, no parameter is modified and false is returned. If the uint64
overflows, \c points to the character which caused the overflow and false is
returned.
*/
bool read_uint64(const char * & s, uint64 & res);
};
#endif /* _DL_UTIL_H_ */

View file

@ -0,0 +1,81 @@
def_module_params('fixedpoint',
description='fixedpoint parameters',
export=True,
params=(('timeout', UINT, UINT_MAX, 'set timeout'),
('engine', SYMBOL, 'auto-config', 'Select: auto-config, datalog, pdr, bmc'),
('default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'),
('default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'),
('generate_explanations', BOOL, False, '(DATALOG) produce explanations for produced facts when using the datalog engine'),
('use_map_names', BOOL, True, "(DATALOG) use names from map files when displaying tuples"),
('bit_blast', BOOL, False, '(PDR) bit-blast bit-vectors'),
('explanations_on_relation_level', BOOL, False, '(DATALOG) if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect)'),
('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"),
('magic', BOOL, False, "perform symbolic magic set transformation"),
('scale', BOOL, False, "add scaling variable to linear real arithemtic clauses"),
('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"),
('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"),
('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"),
('all_or_nothing_deltas', BOOL, False, "(DATALOG) compile rules so that it is enough for the delta relation in union and widening operations to determine only whether the updated relation was modified or not"),
('compile_with_widening', BOOL, False, "(DATALOG) widening will be used to compile recursive rules"),
('eager_emptiness_checking', BOOL, True, "(DATALOG) emptiness of affected relations will be checked after each instruction, so that we may ommit unnecessary instructions"),
('default_table_checked', BOOL, False, "if true, the detault table will be default_table inside a wrapper that checks that its results are the same as of default_table_checker table"),
('default_table_checker', SYMBOL, 'null', "see default_table_checked"),
('initial_restart_timeout', UINT, 0, "length of saturation run before the first restart (in ms), zero means no restarts"),
('output_profile', BOOL, False, "determines whether profile informations should be output when outputting Datalog rules or instructions"),
('inline_linear', BOOL, True, "try linear inlining method"),
('inline_eager', BOOL, True, "try eager inlining of rules"),
('inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"),
('fix_unbound_vars', BOOL, False, "fix unbound variables in tail"),
('output_tuples', BOOL, True, "determines whether tuples for output predicates should be output"),
('print_with_fixedpoint_extensions', BOOL, True, "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, when printing rules"),
('print_low_level_smt2', BOOL, False, "use (faster) low-level SMT2 printer (the printer is scalable but the result may not be as readable)"),
('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules (instead of attempting to use original names)"),
('bfs_model_search', BOOL, True, "PDR: use BFS strategy for expanding model search"),
('use_farkas', BOOL, True, "PDR: use lemma generator based on Farkas (for linear real arithmetic)"),
('generate_proof_trace', BOOL, False, "PDR: trace for 'sat' answer as proof object"),
('flexible_trace', BOOL, False, "PDR: allow PDR generate long counter-examples "
"by extending candidate trace within search area"),
('unfold_rules', UINT, 0, "PDR: unfold rules statically using iterative squarring"),
('use_model_generalizer', BOOL, False, "PDR: use model for backwards propagation (instead of symbolic simulation)"),
('validate_result', BOOL, False, "PDR validate result (by proof checking or model checking)"),
('simplify_formulas_pre', BOOL, False, "PDR: simplify derived formulas before inductive propagation"),
('simplify_formulas_post', BOOL, False, "PDR: simplify derived formulas after inductive propagation"),
('slice', BOOL, True, "PDR: simplify clause set using slicing"),
('karr', BOOL, False, "Add linear invariants to clauses using Karr's method"),
('quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"),
('instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"),
('coalesce_rules', BOOL, False, "BMC: coalesce rules"),
('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"),
('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"),
('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"),
('use_convex_closure_generalizer', BOOL, False, "PDR: generalize using convex closures of lemmas"),
('use_convex_interior_generalizer', BOOL, False, "PDR: generalize using convex interiors of lemmas"),
('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"),
('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when "
"checking for reachability (not only during cube weakening)"),
('max_num_contexts', UINT, 500, "PDR: maximal number of contexts to create"),
('try_minimize_core', BOOL, False, "PDR: try to reduce core size (before inductive minimization)"),
('profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold will not be printed when printed the instruction/rule list"),
('dbg_fpr_nonempty_relation_signature', BOOL, False,
"if true, finite_product_relation will attempt to avoid creating inner relation with empty signature "
"by putting in half of the table columns, if it would have been empty otherwise"),
('print_answer', BOOL, False, 'print answer instance(s) to query'),
('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'),
('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a format understood by Boogie'),
('print_statistics', BOOL, False, 'print statistics'),
('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'),
('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'),
('full_expand', BOOL, False, 'DUALITY: Fully expand derivation trees'),
('no_conj', BOOL, False, 'DUALITY: No forced covering (conjectures)'),
('feasible_edges', BOOL, True, 'DUALITY: Don\'t expand definitley infeasible edges'),
('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'),
('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'),
('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'),
('profile', BOOL, False, 'DUALITY: profile run time'),
('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'),
))

501
src/muz/base/hnf.cpp Normal file
View file

@ -0,0 +1,501 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
hnf.cpp
Abstract:
Horn normal form conversion.
Author:
Nikolaj Bjorner (nbjorner) 3-20-2013
Notes:
Convert formula
(forall x f(x))
into conjunction
(f1 xy) (f2 xy) (f3 xy)
such that
(forall x f(x)) ~ /\ (forall xy (f_i xy))
modulo definitions that are introduced.
Convert proof with
asserted (forall xy (f' xy))
To:
(forall xy (f' xy)) by mp~ 1, 2
1. asserted/def-intro (forall xy (f xy))
2. (forall xy (f xy)) ~ (forall xy (f' xy)) by trans, 3, 4
3. (forall xy (f xy)) ~ (forall xy (f1 xy)) by pull quantifiers (rewrite)
4. (forall xy (f1 xy)) ~ (forall xy (f' xy)) by oeq_quant_intro 5
5. f1 xy ~ f' xy by sub-proof.
--*/
#include"hnf.h"
#include"warning.h"
#include"used_vars.h"
#include"well_sorted.h"
#include"var_subst.h"
#include"name_exprs.h"
#include"act_cache.h"
#include"cooperate.h"
#include"ast_pp.h"
#include"quant_hoist.h"
#include"dl_util.h"
#include"for_each_ast.h"
#include"for_each_expr.h"
class hnf::imp {
ast_manager& m;
bool m_produce_proofs;
volatile bool m_cancel;
expr_ref_vector m_todo;
proof_ref_vector m_proofs;
expr_ref_vector m_refs;
symbol m_name;
svector<symbol> m_names;
ptr_vector<sort> m_sorts;
quantifier_hoister m_qh;
obj_map<expr, app*> m_memoize_disj;
obj_map<expr, proof*> m_memoize_proof;
func_decl_ref_vector m_fresh_predicates;
expr_ref_vector m_body;
proof_ref_vector m_defs;
public:
imp(ast_manager & m):
m(m),
m_produce_proofs(false),
m_cancel(false),
m_todo(m),
m_proofs(m),
m_refs(m),
m_name("P"),
m_qh(m),
m_fresh_predicates(m),
m_body(m),
m_defs(m) {
}
void operator()(expr * n,
proof* p,
expr_ref_vector& result,
proof_ref_vector& ps) {
expr_ref fml(m);
proof_ref pr(m);
m_todo.reset();
m_proofs.reset();
m_refs.reset();
m_memoize_disj.reset();
m_memoize_proof.reset();
m_fresh_predicates.reset();
m_todo.push_back(n);
m_proofs.push_back(p);
m_produce_proofs = p != 0;
while (!m_todo.empty() && !m_cancel) {
fml = m_todo.back();
pr = m_proofs.back();
m_todo.pop_back();
m_proofs.pop_back();
mk_horn(fml, pr);
if (fml) {
result.push_back(fml);
ps.push_back(pr);
}
}
TRACE("hnf",
tout << mk_pp(n, m) << "\n==>\n";
for (unsigned i = 0; i < result.size(); ++i) {
tout << mk_pp(result[i].get(), m) << "\n";
});
}
void set_cancel(bool f) {
m_cancel = f;
}
void set_name(symbol const& n) {
if (n == symbol::null) {
m_name = symbol("P");
}
else {
m_name = n;
}
}
func_decl_ref_vector const& get_fresh_predicates() {
return m_fresh_predicates;
}
void reset() {
m_cancel = false;
m_todo.reset();
m_proofs.reset();
m_refs.reset();
m_memoize_disj.reset();
m_memoize_proof.reset();
m_fresh_predicates.reset();
}
ast_manager& get_manager() { return m; }
private:
bool produce_proofs() const {
return m_produce_proofs;
}
bool is_predicate(expr* p) const {
return is_app(p) && is_predicate(to_app(p)->get_decl());
}
bool is_predicate(func_decl* f) const {
return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id;
}
class contains_predicate_proc {
imp const& m;
public:
struct found {};
contains_predicate_proc(imp const& m): m(m) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app* n) {
if (m.is_predicate(n)) throw found();
}
};
bool contains_predicate(expr* fml) const {
contains_predicate_proc proc(*this);
try {
quick_for_each_expr(proc, fml);
}
catch (contains_predicate_proc::found) {
return true;
}
return false;
}
void mk_horn(expr_ref& fml, proof_ref& premise) {
SASSERT(!premise || fml == m.get_fact(premise));
expr* e1, *e2;
expr_ref fml0(m), fml1(m), fml2(m), head(m);
proof_ref p(m);
fml0 = fml;
m_names.reset();
m_sorts.reset();
m_body.reset();
m_defs.reset();
m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names);
if (premise){
fml1 = bind_variables(fml0);
if (!m_sorts.empty()) {
proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1));
premise = mk_modus_ponens(premise, p1);
fml = fml1;
}
}
head = fml0;
while (m.is_implies(head, e1, e2)) {
m_body.push_back(e1);
head = e2;
}
qe::flatten_and(m_body);
if (premise) {
p = m.mk_rewrite(fml0, mk_implies(m_body, head));
}
//
// Case:
// A \/ B -> C
// =>
// A -> C
// B -> C
//
if (m_body.size() == 1 && m.is_or(m_body[0].get()) && contains_predicate(m_body[0].get())) {
app* _or = to_app(m_body[0].get());
unsigned sz = _or->get_num_args();
expr* const* args = _or->get_args();
for (unsigned i = 0; i < sz; ++i) {
m_todo.push_back(bind_variables(m.mk_implies(args[i], head)));
m_proofs.push_back(0);
}
if (premise) {
expr_ref f1 = bind_variables(mk_implies(m_body, head));
expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz);
proof_ref p2(m), p3(m);
p2 = m.mk_def_axiom(m.mk_iff(f1, f2));
p3 = mk_quant_intro(fml, f1, p);
p2 = mk_transitivity(p3, p2);
p2 = mk_modus_ponens(premise, p2);
for (unsigned i = 0; i < sz; ++i) {
m_proofs[m_proofs.size()-sz+i] = m.mk_and_elim(p2, i);
}
}
fml = 0;
return;
}
eliminate_disjunctions(m_body, m_defs);
p = mk_congruence(p, m_body, head, m_defs);
eliminate_quantifier_body(m_body, m_defs);
p = mk_congruence(p, m_body, head, m_defs);
fml2 = mk_implies(m_body, head);
fml = bind_variables(fml2);
if (premise) {
SASSERT(p);
p = mk_quant_intro(fml1, fml, p);
premise = mk_modus_ponens(premise, p);
}
}
proof* mk_quant_intro(expr* e1, expr* e2, proof* p) {
if (m_sorts.empty()) {
return p;
}
quantifier* q1 = to_quantifier(e1);
quantifier* q2 = to_quantifier(e2);
if (m.is_iff(m.get_fact(p))) {
return m.mk_quant_intro(q1, q2, p);
}
if (m.is_oeq(m.get_fact(p))) {
return m.mk_oeq_quant_intro(q1, q2, p);
}
UNREACHABLE();
return p;
}
void eliminate_disjunctions(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) {
expr* b = body.get();
expr* e1;
bool negate_args = false;
bool is_disj = false;
unsigned num_disj = 0;
expr* const* disjs = 0;
if (!contains_predicate(b)) {
return;
}
TRACE("hnf", tout << mk_pp(b, m) << "\n";);
if (m.is_or(b)) {
is_disj = true;
negate_args = false;
num_disj = to_app(b)->get_num_args();
disjs = to_app(b)->get_args();
}
if (m.is_not(b, e1) && m.is_and(e1)) {
is_disj = true;
negate_args = true;
num_disj = to_app(e1)->get_num_args();
disjs = to_app(e1)->get_args();
}
if (is_disj) {
app* old_head = 0;
if (m_memoize_disj.find(b, old_head)) {
body = old_head;
}
else {
app_ref head = mk_fresh_head(b);
proof_ref_vector defs(m);
for (unsigned i = 0; i < num_disj; ++i) {
expr* e = disjs[i];
if (negate_args) {
e = m.mk_not(e);
}
m_todo.push_back(bind_variables(m.mk_implies(e, head)));
m_proofs.push_back(0);
if (produce_proofs()) {
defs.push_back(m.mk_def_intro(m_todo.back()));
m_proofs[m_proofs.size()-1] = defs.back();
}
}
if (produce_proofs()) {
proof* p = m.mk_apply_defs(body.get(), head, defs.size(), defs.c_ptr());
m_refs.push_back(p);
m_memoize_proof.insert(b, p);
}
m_memoize_disj.insert(b, head);
m_refs.push_back(b);
m_refs.push_back(head);
// update the body to be the newly introduced head relation
body = head;
}
if (produce_proofs()) {
proofs.push_back(m_memoize_proof.find(b));
}
}
}
app_ref mk_fresh_head(expr* e) {
ptr_vector<sort> sorts0, sorts1;
get_free_vars(e, sorts0);
expr_ref_vector args(m);
for (unsigned i = 0; i < sorts0.size(); ++i) {
if (sorts0[i]) {
args.push_back(m.mk_var(i, sorts0[i]));
sorts1.push_back(sorts0[i]);
}
}
func_decl_ref f(m);
f = m.mk_fresh_func_decl(m_name.str().c_str(), "", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort());
m_fresh_predicates.push_back(f);
return app_ref(m.mk_app(f, args.size(), args.c_ptr()), m);
}
void eliminate_disjunctions(expr_ref_vector& body, proof_ref_vector& proofs) {
for (unsigned i = 0; i < body.size(); ++i) {
expr_ref_vector::element_ref r = body[i];
eliminate_disjunctions(r, proofs);
}
}
void eliminate_quantifier_body(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) {
if (is_forall(body.get()) && contains_predicate(body.get())) {
quantifier* q = to_quantifier(body.get());
expr* e = q->get_expr();
if (!is_predicate(e)) {
app_ref head = mk_fresh_head(e);
m_todo.push_back(bind_variables(m.mk_implies(e, head)));
m_proofs.push_back(0);
body = m.update_quantifier(q, head);
if (produce_proofs()) {
proof* def_intro = m.mk_def_intro(m_todo.back());
proof* def_proof = m.mk_apply_def(e, head, def_intro);
proofs.push_back(m.mk_nnf_neg(q, body.get(), 1, &def_proof));
m_proofs[m_proofs.size()-1] = def_intro;
}
}
}
}
void eliminate_quantifier_body(expr_ref_vector& body, proof_ref_vector& proofs) {
for (unsigned i = 0; i < body.size(); ++i) {
expr_ref_vector::element_ref r = body[i];
eliminate_quantifier_body(r, proofs);
}
}
app_ref mk_implies(expr_ref_vector const& body, expr* head) {
switch (body.size()) {
case 0:
return app_ref(to_app(head), m);
case 1:
return app_ref(m.mk_implies(body[0], head), m);
default:
return app_ref(m.mk_implies(m.mk_and(body.size(), body.c_ptr()), head), m);
}
}
proof_ref mk_congruence(proof* p1, expr_ref_vector const& body, expr* head, proof_ref_vector& defs) {
if (defs.empty()) {
return proof_ref(p1, m);
}
else {
SASSERT(p1);
proof_ref p2(m), p3(m);
app_ref fml = mk_implies(body, head);
expr* fact = m.get_fact(p1);
if (m.is_iff(fact)) {
p1 = m.mk_iff_oeq(p1);
fact = m.get_fact(p1);
}
VERIFY (m.is_oeq(fact) || m.is_eq(fact));
app* e2 = to_app(to_app(fact)->get_arg(1));
p2 = m.mk_oeq_congruence(e2, fml, defs.size(), defs.c_ptr());
p3 = mk_transitivity(p1, p2);
defs.reset();
return proof_ref(p3, m);
}
}
proof_ref mk_modus_ponens(proof* premise, proof* eq) {
proof_ref result(m);
result = m.mk_modus_ponens(premise, eq);
if (m.get_fact(premise) == m.get_fact(result)) {
result = premise;
}
return result;
}
proof* mk_transitivity(proof* p1, proof* p2) {
if (p1) {
app* f = to_app(m.get_fact(p1));
if (f->get_arg(0) == f->get_arg(1)) {
return p2;
}
}
if (p2) {
app* f = to_app(m.get_fact(p2));
if (f->get_arg(0) == f->get_arg(1)) {
return p1;
}
}
return m.mk_transitivity(p1, p2);
}
expr_ref bind_variables(expr* e) {
SASSERT(m_sorts.size() == m_names.size());
if (m_sorts.empty()) {
return expr_ref(e, m);
}
return expr_ref(m.mk_forall(m_sorts.size(), m_sorts.c_ptr(), m_names.c_ptr(), e), m);
}
};
hnf::hnf(ast_manager & m) {
m_imp = alloc(imp, m);
}
hnf::~hnf() {
dealloc(m_imp);
}
void hnf::operator()(expr * n, proof* p, expr_ref_vector & rs, proof_ref_vector& ps) {
m_imp->operator()(n, p, rs, ps);
TRACE("hnf",
ast_manager& m = rs.get_manager();
tout << mk_ismt2_pp(n, m) << "\nHNF result:\n";
for (unsigned i = 0; i < rs.size(); ++i) {
tout << mk_pp(rs[i].get(), m) << "\n";
}
);
}
void hnf::set_cancel(bool f) {
m_imp->set_cancel(f);
}
void hnf::set_name(symbol const& n) {
m_imp->set_name(n);
}
void hnf::reset() {
m_imp->reset();
}
func_decl_ref_vector const& hnf::get_fresh_predicates() {
return m_imp->get_fresh_predicates();
}

49
src/muz/base/hnf.h Normal file
View file

@ -0,0 +1,49 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
hnf.h
Abstract:
Horn normal form convertion.
Author:
Notes:
Very similar to NNF.
--*/
#ifndef _HNF_H_
#define _HNF_H_
#include"ast.h"
#include"params.h"
#include"defined_names.h"
#include"proof_converter.h"
class hnf {
class imp;
imp * m_imp;
public:
hnf(ast_manager & m);
~hnf();
void operator()(expr * n, // [IN] expression that should be put into Horn NF
proof* p, // [IN] proof of n
expr_ref_vector & rs, // [OUT] resultant (conjunction) of expressions
proof_ref_vector& ps // [OUT] proofs of rs
);
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
void set_cancel(bool f);
void set_name(symbol const& name);
void reset();
func_decl_ref_vector const& get_fresh_predicates();
};
#endif /* _HNF_H_ */

View file

@ -0,0 +1,612 @@
#include "dl_util.h"
#include "proof_utils.h"
#include "ast_smt2_pp.h"
#include "var_subst.h"
class reduce_hypotheses {
typedef obj_hashtable<expr> expr_set;
ast_manager& m;
expr_ref_vector m_refs;
obj_map<proof,proof*> m_cache;
obj_map<expr, proof*> m_units;
ptr_vector<expr> m_units_trail;
unsigned_vector m_limits;
obj_map<proof, expr_set*> m_hypmap;
ptr_vector<expr_set> m_hyprefs;
ptr_vector<expr> m_literals;
void reset() {
m_refs.reset();
m_cache.reset();
m_units.reset();
m_units_trail.reset();
m_limits.reset();
std::for_each(m_hyprefs.begin(), m_hyprefs.end(), delete_proc<expr_set>());
m_hypmap.reset();
m_hyprefs.reset();
m_literals.reset();
}
void push() {
m_limits.push_back(m_units_trail.size());
}
void pop() {
unsigned sz = m_limits.back();
while (m_units_trail.size() > sz) {
m_units.remove(m_units_trail.back());
m_units_trail.pop_back();
}
m_limits.pop_back();
}
void get_literals(expr* clause) {
m_literals.reset();
if (m.is_or(clause)) {
m_literals.append(to_app(clause)->get_num_args(), to_app(clause)->get_args());
}
else {
m_literals.push_back(clause);
}
}
void add_hypotheses(proof* p) {
expr_set* hyps = 0;
bool inherited = false;
if (p->get_decl_kind() == PR_HYPOTHESIS) {
hyps = alloc(expr_set);
hyps->insert(m.get_fact(p));
m_hyprefs.push_back(hyps);
}
else {
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
expr_set* hyps1 = m_hypmap.find(m.get_parent(p, i));
if (hyps1) {
if (!hyps) {
hyps = hyps1;
inherited = true;
continue;
}
if (inherited) {
hyps = alloc(expr_set,*hyps);
m_hyprefs.push_back(hyps);
inherited = false;
}
datalog::set_union(*hyps, *hyps1);
}
}
}
m_hypmap.insert(p, hyps);
}
expr_ref complement_lit(expr* e) {
expr* e1;
if (m.is_not(e, e1)) {
return expr_ref(e1, m);
}
else {
return expr_ref(m.mk_not(e), m);
}
}
bool in_hypotheses(expr* e, expr_set* hyps) {
if (!hyps) {
return false;
}
expr_ref not_e = complement_lit(e);
return hyps->contains(not_e);
}
bool contains_hypothesis(proof* p) {
ptr_vector<proof> todo;
ast_mark visit;
todo.push_back(p);
while (!todo.empty()) {
p = todo.back();
todo.pop_back();
if (visit.is_marked(p)) {
continue;
}
visit.mark(p, true);
if (PR_HYPOTHESIS == p->get_decl_kind()) {
return true;
}
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
todo.push_back(m.get_parent(p, i));
}
}
return false;
}
bool is_closed(proof* p) {
expr_set* hyps = m_hypmap.find(p);
return !hyps || hyps->empty();
}
public:
reduce_hypotheses(ast_manager& m): m(m), m_refs(m) {}
void operator()(proof_ref& pr) {
proof_ref tmp(m);
tmp = pr;
elim(pr);
reset();
CTRACE("proof_utils", contains_hypothesis(pr),
tout << "Contains hypothesis:\n";
tout << mk_ismt2_pp(tmp, m) << "\n====>\n";
tout << mk_ismt2_pp(pr, m) << "\n";);
}
void elim(proof_ref& p) {
proof_ref tmp(m);
proof* result = p.get();
if (m_cache.find(p, result)) {
p = result;
return;
}
switch(p->get_decl_kind()) {
case PR_HYPOTHESIS:
if (!m_units.find(m.get_fact(p), result)) {
result = p.get();
}
add_hypotheses(result);
break;
case PR_LEMMA: {
SASSERT(m.get_num_parents(p) == 1);
tmp = m.get_parent(p, 0);
elim(tmp);
get_literals(m.get_fact(p));
expr_set* hyps = m_hypmap.find(tmp);
expr_set* new_hyps = 0;
if (hyps) {
new_hyps = alloc(expr_set, *hyps);
}
for (unsigned i = 0; i < m_literals.size(); ++i) {
expr* e = m_literals[i];
if (!in_hypotheses(e, hyps)) {
m_literals[i] = m_literals.back();
m_literals.pop_back();
--i;
}
else {
SASSERT(new_hyps);
expr_ref not_e = complement_lit(e);
SASSERT(new_hyps->contains(not_e));
new_hyps->remove(not_e);
}
}
if (m_literals.empty()) {
result = tmp;
}
else {
expr_ref clause(m);
if (m_literals.size() == 1) {
clause = m_literals[0];
}
else {
clause = m.mk_or(m_literals.size(), m_literals.c_ptr());
}
tmp = m.mk_lemma(tmp, clause);
m_refs.push_back(tmp);
result = tmp;
}
if (new_hyps && new_hyps->empty()) {
dealloc(new_hyps);
new_hyps = 0;
}
m_hypmap.insert(result, new_hyps);
m_hyprefs.push_back(new_hyps);
TRACE("proof_utils",
tout << "New lemma: " << mk_pp(m.get_fact(p), m)
<< "\n==>\n"
<< mk_pp(m.get_fact(result), m) << "\n";
if (hyps) {
expr_set::iterator it = hyps->begin();
expr_set::iterator end = hyps->end();
for (; it != end; ++it) {
tout << "Hypothesis: " << mk_pp(*it, m) << "\n";
}
});
break;
}
case PR_UNIT_RESOLUTION: {
proof_ref_vector parents(m);
parents.push_back(m.get_parent(p, 0));
push();
bool found_false = false;
for (unsigned i = 1; i < m.get_num_parents(p); ++i) {
tmp = m.get_parent(p, i);
elim(tmp);
if (m.is_false(m.get_fact(tmp))) {
result = tmp;
found_false = true;
break;
}
SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i)));
parents.push_back(tmp);
if (is_closed(tmp) && !m_units.contains(m.get_fact(tmp))) {
m_units.insert(m.get_fact(tmp), tmp);
m_units_trail.push_back(m.get_fact(tmp));
}
}
if (found_false) {
pop();
break;
}
tmp = m.get_parent(p, 0);
expr* old_clause = m.get_fact(tmp);
elim(tmp);
parents[0] = tmp;
expr* clause = m.get_fact(tmp);
if (m.is_false(clause)) {
m_refs.push_back(tmp);
result = tmp;
pop();
break;
}
//
// case where clause is a literal in the old clause.
//
if (is_literal_in_clause(clause, old_clause)) {
bool found = false;
for (unsigned i = 1; !found && i < parents.size(); ++i) {
if (m.is_complement(clause, m.get_fact(parents[i].get()))) {
parents[1] = parents[i].get();
parents.resize(2);
result = m.mk_unit_resolution(parents.size(), parents.c_ptr());
m_refs.push_back(result);
add_hypotheses(result);
found = true;
}
}
if (!found) {
result = parents[0].get();
}
pop();
break;
}
//
// case where new clause is a subset of old clause.
// the literals in clause should be a subset of literals in old_clause.
//
get_literals(clause);
for (unsigned i = 1; i < parents.size(); ++i) {
bool found = false;
for (unsigned j = 0; j < m_literals.size(); ++j) {
if (m.is_complement(m_literals[j], m.get_fact(parents[i].get()))) {
found = true;
break;
}
}
if (!found) {
// literal was removed as hypothesis.
parents[i] = parents.back();
parents.pop_back();
--i;
}
}
if (parents.size() == 1) {
result = parents[0].get();
}
else {
result = m.mk_unit_resolution(parents.size(), parents.c_ptr());
m_refs.push_back(result);
add_hypotheses(result);
}
pop();
break;
}
default: {
ptr_buffer<expr> args;
bool change = false;
bool found_false = false;
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
tmp = m.get_parent(p, i);
elim(tmp);
if (m.is_false(m.get_fact(tmp))) {
result = tmp;
found_false = true;
break;
}
// SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i)));
change = change || (tmp != m.get_parent(p, i));
args.push_back(tmp);
}
if (found_false) {
break;
}
if (m.has_fact(p)) {
args.push_back(m.get_fact(p));
}
if (change) {
tmp = m.mk_app(p->get_decl(), args.size(), args.c_ptr());
m_refs.push_back(tmp);
}
else {
tmp = p;
}
result = tmp;
add_hypotheses(result);
break;
}
}
SASSERT(m_hypmap.contains(result));
m_cache.insert(p, result);
p = result;
}
bool is_literal_in_clause(expr* fml, expr* clause) {
if (!m.is_or(clause)) {
return false;
}
app* cl = to_app(clause);
for (unsigned i = 0; i < cl->get_num_args(); ++i) {
if (cl->get_arg(i) == fml) {
return true;
}
}
return false;
}
};
void proof_utils::reduce_hypotheses(proof_ref& pr) {
ast_manager& m = pr.get_manager();
class reduce_hypotheses reduce(m);
reduce(pr);
CTRACE("proof_utils", !is_closed(m, pr), tout << mk_pp(pr, m) << "\n";);
}
class proof_is_closed {
ast_manager& m;
ptr_vector<expr> m_literals;
ast_mark m_visit;
void reset() {
m_literals.reset();
m_visit.reset();
}
bool check(proof* p) {
// really just a partial check because nodes may be visited
// already under a different lemma scope.
if (m_visit.is_marked(p)) {
return true;
}
bool result = false;
m_visit.mark(p, true);
switch(p->get_decl_kind()) {
case PR_LEMMA: {
unsigned sz = m_literals.size();
expr* cls = m.get_fact(p);
m_literals.push_back(cls);
if (m.is_or(cls)) {
m_literals.append(to_app(cls)->get_num_args(), to_app(cls)->get_args());
}
SASSERT(m.get_num_parents(p) == 1);
result = check(m.get_parent(p, 0));
m_literals.resize(sz);
break;
}
case PR_HYPOTHESIS: {
expr* fact = m.get_fact(p);
for (unsigned i = 0; i < m_literals.size(); ++i) {
if (m.is_complement(m_literals[i], fact)) {
result = true;
break;
}
}
break;
}
default:
result = true;
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
if (!check(m.get_parent(p, i))) {
result = false;
break;
}
}
break;
}
return result;
}
public:
proof_is_closed(ast_manager& m): m(m) {}
bool operator()(proof *p) {
bool ok = check(p);
reset();
return ok;
}
};
bool proof_utils::is_closed(ast_manager& m, proof* p) {
proof_is_closed checker(m);
return checker(p);
}
static void permute_unit_resolution(expr_ref_vector& refs, obj_map<proof,proof*>& cache, proof_ref& pr) {
ast_manager& m = pr.get_manager();
proof* pr2 = 0;
proof_ref_vector parents(m);
proof_ref prNew(pr);
if (cache.find(pr, pr2)) {
pr = pr2;
return;
}
for (unsigned i = 0; i < m.get_num_parents(pr); ++i) {
prNew = m.get_parent(pr, i);
permute_unit_resolution(refs, cache, prNew);
parents.push_back(prNew);
}
prNew = pr;
if (pr->get_decl_kind() == PR_UNIT_RESOLUTION &&
parents[0]->get_decl_kind() == PR_TH_LEMMA) {
/*
Unit resolution:
T1: (or l_1 ... l_n l_1' ... l_m')
T2: (not l_1)
...
T(n+1): (not l_n)
[unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m')
Th lemma:
T1: (not l_1)
...
Tn: (not l_n)
[th-lemma T1 ... Tn]: (or l_{n+1} ... l_m)
Such that (or l_1 .. l_n l_{n+1} .. l_m) is a theory axiom.
Implement conversion:
T1 |- not l_1 ... Tn |- not l_n
------------------------------- TH_LEMMA
(or k_1 .. k_m j_1 ... j_m) S1 |- not k_1 ... Sm |- not k_m
-------------------------------------------------------------- UNIT_RESOLUTION
(or j_1 .. j_m)
|->
T1 |- not l_1 ... Tn |- not l_n S1 |- not k_1 ... Sm |- not k_m
---------------------------------------------------------------- TH_LEMMA
(or j_1 .. j_m)
*/
proof_ref_vector premises(m);
proof* thLemma = parents[0].get();
for (unsigned i = 0; i < m.get_num_parents(thLemma); ++i) {
premises.push_back(m.get_parent(thLemma, i));
}
for (unsigned i = 1; i < parents.size(); ++i) {
premises.push_back(parents[i].get());
}
parameter const* params = thLemma->get_decl()->get_parameters();
unsigned num_params = thLemma->get_decl()->get_num_parameters();
SASSERT(params[0].is_symbol());
family_id tid = m.mk_family_id(params[0].get_symbol());
SASSERT(tid != null_family_id);
prNew = m.mk_th_lemma(tid, m.get_fact(pr),
premises.size(), premises.c_ptr(), num_params-1, params+1);
}
else {
ptr_vector<expr> args;
for (unsigned i = 0; i < parents.size(); ++i) {
args.push_back(parents[i].get());
}
if (m.has_fact(pr)) {
args.push_back(m.get_fact(pr));
}
prNew = m.mk_app(pr->get_decl(), args.size(), args.c_ptr());
}
cache.insert(pr, prNew);
refs.push_back(prNew);
pr = prNew;
}
// permute unit resolution over Theory lemmas to track premises.
void proof_utils::permute_unit_resolution(proof_ref& pr) {
expr_ref_vector refs(pr.get_manager());
obj_map<proof,proof*> cache;
::permute_unit_resolution(refs, cache, pr);
}
class push_instantiations_up_cl {
ast_manager& m;
public:
push_instantiations_up_cl(ast_manager& m): m(m) {}
void operator()(proof_ref& p) {
expr_ref_vector s0(m);
p = push(p, s0);
}
private:
proof* push(proof* p, expr_ref_vector const& sub) {
proof_ref_vector premises(m);
expr_ref conclusion(m);
svector<std::pair<unsigned, unsigned> > positions;
vector<expr_ref_vector> substs;
if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) {
for (unsigned i = 0; i < premises.size(); ++i) {
compose(substs[i], sub);
premises[i] = push(premises[i].get(), substs[i]);
substs[i].reset();
}
instantiate(sub, conclusion);
return
m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion,
positions,
substs);
}
if (sub.empty()) {
return p;
}
if (m.is_modus_ponens(p)) {
SASSERT(m.get_num_parents(p) == 2);
proof* p0 = m.get_parent(p, 0);
proof* p1 = m.get_parent(p, 1);
if (m.get_fact(p0) == m.get_fact(p)) {
return push(p0, sub);
}
expr* e1, *e2;
if (m.is_rewrite(p1, e1, e2) &&
is_quantifier(e1) && is_quantifier(e2) &&
to_quantifier(e1)->get_num_decls() == to_quantifier(e2)->get_num_decls()) {
expr_ref r1(e1,m), r2(e2,m);
instantiate(sub, r1);
instantiate(sub, r2);
p1 = m.mk_rewrite(r1, r2);
return m.mk_modus_ponens(push(p0, sub), p1);
}
}
premises.push_back(p);
substs.push_back(sub);
conclusion = m.get_fact(p);
instantiate(sub, conclusion);
return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs);
}
void compose(expr_ref_vector& sub, expr_ref_vector const& s0) {
for (unsigned i = 0; i < sub.size(); ++i) {
expr_ref e(m);
var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr(), e);
sub[i] = e;
}
}
void instantiate(expr_ref_vector const& sub, expr_ref& fml) {
if (sub.empty()) {
return;
}
if (!is_forall(fml)) {
return;
}
quantifier* q = to_quantifier(fml);
if (q->get_num_decls() != sub.size()) {
TRACE("proof_utils", tout << "quantifier has different number of variables than substitution";
tout << mk_pp(q, m) << "\n";
tout << sub.size() << "\n";);
return;
}
var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr(), fml);
}
};
void proof_utils::push_instantiations_up(proof_ref& pr) {
push_instantiations_up_cl push(pr.get_manager());
push(pr);
}

View file

@ -0,0 +1,48 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
proof_utils.h
Abstract:
Utilities for transforming proofs.
Author:
Nikolaj Bjorner (nbjorner) 2012-10-12.
Revision History:
--*/
#ifndef _PROOF_UTILS_H_
#define _PROOF_UTILS_H_
class proof_utils {
public:
/**
\brief reduce the set of hypotheses used in the proof.
*/
static void reduce_hypotheses(proof_ref& pr);
/**
\brief Check that a proof does not contain open hypotheses.
*/
static bool is_closed(ast_manager& m, proof* p);
/**
\brief Permute unit resolution rule with th-lemma
*/
static void permute_unit_resolution(proof_ref& pr);
/**
\brief Push instantiations created in hyper-resolutions up to leaves.
This produces a "ground" proof where leaves are annotated by instantiations.
*/
static void push_instantiations_up(proof_ref& pr);
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,82 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_bmc_engine.h
Abstract:
BMC engine for fixedpoint solver.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-20
Revision History:
--*/
#ifndef _DL_BMC_ENGINE_H_
#define _DL_BMC_ENGINE_H_
#include "params.h"
#include "statistics.h"
#include "smt_kernel.h"
#include "bv_decl_plugin.h"
#include "smt_params.h"
namespace datalog {
class context;
class bmc : public engine_base {
context& m_ctx;
ast_manager& m;
smt_params m_fparams;
smt::kernel m_solver;
rule_set m_rules;
func_decl_ref m_query_pred;
expr_ref m_answer;
volatile bool m_cancel;
void checkpoint();
class nonlinear_dt;
class nonlinear;
class qlinear;
class linear;
bool is_linear() const;
void assert_expr(expr* e);
public:
bmc(context& ctx);
~bmc();
lbool query(expr* query);
void cancel();
void cleanup();
void display_certificate(std::ostream& out) const;
void collect_statistics(statistics& st) const;
void reset_statistics();
expr_ref get_answer();
// direct access to (new) non-linear compiler.
void compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level);
expr_ref compile_query(func_decl* query_pred, unsigned level);
};
};
#endif

242
src/muz/clp/clp_context.cpp Normal file
View file

@ -0,0 +1,242 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
clp_context.cpp
Abstract:
Bounded CLP (symbolic simulation using Z3) context.
Author:
Nikolaj Bjorner (nbjorner) 2013-04-26
Revision History:
--*/
#include "clp_context.h"
#include "dl_context.h"
#include "unifier.h"
#include "var_subst.h"
#include "substitution.h"
#include "smt_kernel.h"
#include "dl_transforms.h"
namespace datalog {
class clp::imp {
struct stats {
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
unsigned m_num_unfold;
unsigned m_num_no_unfold;
unsigned m_num_subsumed;
};
context& m_ctx;
ast_manager& m;
rule_manager& rm;
smt_params m_fparams;
smt::kernel m_solver;
var_subst m_var_subst;
expr_ref_vector m_ground;
app_ref_vector m_goals;
volatile bool m_cancel;
stats m_stats;
public:
imp(context& ctx):
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager()),
m_solver(m, m_fparams), // TBD: can be replaced by efficient BV solver.
m_var_subst(m, false),
m_ground(m),
m_goals(m),
m_cancel(false)
{
// m_fparams.m_relevancy_lvl = 0;
m_fparams.m_mbqi = false;
m_fparams.m_soft_timeout = 1000;
}
~imp() {}
lbool query(expr* query) {
m_ctx.ensure_opened();
m_solver.reset();
m_goals.reset();
rm.mk_query(query, m_ctx.get_rules());
apply_default_transformation(m_ctx);
func_decl* head_decl = m_ctx.get_rules().get_output_predicate();
rule_set& rules = m_ctx.get_rules();
rule_vector const& rv = rules.get_predicate_rules(head_decl);
if (rv.empty()) {
return l_false;
}
expr_ref head(rv[0]->get_head(), m);
ground(head);
m_goals.push_back(to_app(head));
return search(20, 0);
}
void cancel() {
m_cancel = true;
m_solver.cancel();
}
void cleanup() {
m_cancel = false;
m_goals.reset();
m_solver.reset_cancel();
}
void reset_statistics() {
m_stats.reset();
}
void collect_statistics(statistics& st) const {
//st.update("tab.num_unfold", m_stats.m_num_unfold);
//st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold);
//st.update("tab.num_subsumed", m_stats.m_num_subsumed);
}
void display_certificate(std::ostream& out) const {
expr_ref ans = get_answer();
out << mk_pp(ans, m) << "\n";
}
expr_ref get_answer() const {
return expr_ref(m.mk_true(), m);
}
private:
void reset_ground() {
m_ground.reset();
}
void ground(expr_ref& e) {
ptr_vector<sort> sorts;
get_free_vars(e, sorts);
if (m_ground.size() < sorts.size()) {
m_ground.resize(sorts.size());
}
for (unsigned i = 0; i < sorts.size(); ++i) {
if (sorts[i] && !m_ground[i].get()) {
m_ground[i] = m.mk_fresh_const("c",sorts[i]);
}
}
m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e);
}
static bool rule_sort_fn(const rule *r1, const rule *r2) {
return r1->get_uninterpreted_tail_size() < r2->get_uninterpreted_tail_size();
}
lbool search(unsigned depth, unsigned index) {
if (index == m_goals.size()) {
return l_true;
}
if (depth == 0) {
return l_undef;
}
IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";);
unsigned num_goals = m_goals.size();
app* head = m_goals[index].get();
rule_vector rules(m_ctx.get_rules().get_predicate_rules(head->get_decl()));
std::stable_sort(rules.begin(), rules.end(), rule_sort_fn);
lbool status = l_false;
for (unsigned i = 0; i < rules.size(); ++i) {
rule* r = rules[i];
m_solver.push();
reset_ground();
expr_ref tmp(m);
tmp = r->get_head();
IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(tmp, m) << "\n";);
ground(tmp);
for (unsigned j = 0; j < head->get_num_args(); ++j) {
expr_ref eq(m);
eq = m.mk_eq(head->get_arg(j), to_app(tmp)->get_arg(j));
m_solver.assert_expr(eq);
}
for (unsigned j = r->get_uninterpreted_tail_size(); j < r->get_tail_size(); ++j) {
tmp = r->get_tail(j);
ground(tmp);
m_solver.assert_expr(tmp);
}
lbool is_sat = m_solver.check();
switch (is_sat) {
case l_false:
break;
case l_true:
if (depth == 1 && (index+1 > m_goals.size() || r->get_uninterpreted_tail_size() > 0)) {
status = l_undef;
break;
}
for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) {
tmp = r->get_tail(j);
ground(tmp);
m_goals.push_back(to_app(tmp));
}
switch(search(depth-1, index+1)) {
case l_undef:
status = l_undef;
// fallthrough
case l_false:
m_goals.resize(num_goals);
break;
case l_true:
return l_true;
}
break;
case l_undef:
status = l_undef;
throw default_exception("undef");
}
m_solver.pop(1);
}
return status;
}
proof_ref get_proof() const {
return proof_ref(0, m);
}
};
clp::clp(context& ctx):
engine_base(ctx.get_manager(), "clp"),
m_imp(alloc(imp, ctx)) {
}
clp::~clp() {
dealloc(m_imp);
}
lbool clp::query(expr* query) {
return m_imp->query(query);
}
void clp::cancel() {
m_imp->cancel();
}
void clp::cleanup() {
m_imp->cleanup();
}
void clp::reset_statistics() {
m_imp->reset_statistics();
}
void clp::collect_statistics(statistics& st) const {
m_imp->collect_statistics(st);
}
void clp::display_certificate(std::ostream& out) const {
m_imp->display_certificate(out);
}
expr_ref clp::get_answer() {
return m_imp->get_answer();
}
};

46
src/muz/clp/clp_context.h Normal file
View file

@ -0,0 +1,46 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
clp_context.h
Abstract:
Bounded CLP (symbolic simulation using Z3) context.
Author:
Nikolaj Bjorner (nbjorner) 2013-04-26
Revision History:
--*/
#ifndef _CLP_CONTEXT_H_
#define _CLP_CONTEXT_H_
#include "ast.h"
#include "lbool.h"
#include "statistics.h"
#include "dl_engine_base.h"
namespace datalog {
class context;
class clp : public datalog::engine_base {
class imp;
imp* m_imp;
public:
clp(context& ctx);
~clp();
virtual lbool query(expr* query);
virtual void cancel();
virtual void cleanup();
virtual void reset_statistics();
virtual void collect_statistics(statistics& st) const;
virtual void display_certificate(std::ostream& out) const;
virtual expr_ref get_answer();
};
};
#endif

View file

@ -0,0 +1,489 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
duality_dl_interface.cpp
Abstract:
SMT2 interface for Duality
Author:
Krystof Hoder (t-khoder) 2011-9-22.
Modified by Ken McMIllan (kenmcmil) 2013-4-18.
Revision History:
--*/
#include "dl_context.h"
#include "dl_mk_coi_filter.h"
#include "dl_mk_interp_tail_simplifier.h"
#include "dl_mk_subsumption_checker.h"
#include "dl_mk_rule_inliner.h"
#include "dl_rule.h"
#include "dl_rule_transformer.h"
#include "smt2parser.h"
#include "duality_dl_interface.h"
#include "dl_rule_set.h"
#include "dl_mk_slice.h"
#include "dl_mk_unfold.h"
#include "dl_mk_coalesce.h"
#include "expr_abstract.h"
#include "model_smt2_pp.h"
#include "model_v2_pp.h"
#include "fixedpoint_params.hpp"
// template class symbol_table<family_id>;
#include "duality.h"
#include "duality_profiling.h"
// using namespace Duality;
namespace Duality {
enum DualityStatus {StatusModel, StatusRefutation, StatusUnknown, StatusNull};
class duality_data {
public:
context ctx;
RPFP::LogicSolver *ls;
RPFP *rpfp;
DualityStatus status;
std::vector<expr> clauses;
std::vector<std::vector<RPFP::label_struct> > clause_labels;
hash_map<RPFP::Edge *,int> map; // edges to clauses
Solver::Counterexample cex;
duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) {
ls = 0;
rpfp = 0;
status = StatusNull;
}
~duality_data(){
if(rpfp)
dealloc(rpfp);
if(ls)
dealloc(ls);
if(cex.tree)
delete cex.tree;
}
};
dl_interface::dl_interface(datalog::context& dl_ctx) :
engine_base(dl_ctx.get_manager(), "duality"),
m_ctx(dl_ctx)
{
_d = 0;
}
dl_interface::~dl_interface() {
if(_d)
dealloc(_d);
}
//
// Check if the new rules are weaker so that we can
// re-use existing context.
//
#if 0
void dl_interface::check_reset() {
// TODO
datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules();
datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules();
bool is_subsumed = !old_rules.empty();
for (unsigned i = 0; is_subsumed && i < new_rules.size(); ++i) {
is_subsumed = false;
for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) {
if (m_ctx.check_subsumes(*old_rules[j], *new_rules[i])) {
is_subsumed = true;
}
}
if (!is_subsumed) {
TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule "););
m_context->reset();
}
}
m_old_rules.reset();
m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr());
}
#endif
lbool dl_interface::query(::expr * query) {
// we restore the initial state in the datalog context
m_ctx.ensure_opened();
// if there is old data, get the cex and dispose (later)
Solver::Counterexample old_cex;
duality_data *old_data = _d;
if(old_data)
old_cex = old_data->cex;
// make a new problem and solver
_d = alloc(duality_data,m_ctx.get_manager());
_d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx);
_d->rpfp = alloc(RPFP,_d->ls);
expr_ref_vector rules(m_ctx.get_manager());
svector< ::symbol> names;
// m_ctx.get_rules_as_formulas(rules, names);
m_ctx.get_raw_rule_formulas(rules, names);
// get all the rules as clauses
std::vector<expr> &clauses = _d->clauses;
clauses.clear();
for (unsigned i = 0; i < rules.size(); ++i) {
expr e(_d->ctx,rules[i].get());
clauses.push_back(e);
}
// turn the query into a clause
expr q(_d->ctx,m_ctx.bind_variables(query,false));
std::vector<sort> b_sorts;
std::vector<symbol> b_names;
if (q.is_quantifier() && !q.is_quantifier_forall()) {
int bound = q.get_quantifier_num_bound();
for(int j = 0; j < bound; j++){
b_sorts.push_back(q.get_quantifier_bound_sort(j));
b_names.push_back(q.get_quantifier_bound_name(j));
}
q = q.arg(0);
}
expr qc = implies(q,_d->ctx.bool_val(false));
qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc);
clauses.push_back(qc);
// get the background axioms
unsigned num_asserts = m_ctx.get_num_assertions();
for (unsigned i = 0; i < num_asserts; ++i) {
expr e(_d->ctx,m_ctx.get_assertion(i));
_d->rpfp->AssertAxiom(e);
}
// creates 1-1 map between clauses and rpfp edges
_d->rpfp->FromClauses(clauses);
// populate the edge-to-clause map
for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i)
_d->map[_d->rpfp->edges[i]] = i;
// create a solver object
Solver *rs = Solver::Create("duality", _d->rpfp);
rs->LearnFrom(old_cex); // new solver gets hints from old cex
// set its options
IF_VERBOSE(1, rs->SetOption("report","1"););
rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0");
rs->SetOption("no_conj",m_ctx.get_params().no_conj() ? "1" : "0");
rs->SetOption("feasible_edges",m_ctx.get_params().feasible_edges() ? "1" : "0");
rs->SetOption("use_underapprox",m_ctx.get_params().use_underapprox() ? "1" : "0");
rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0");
unsigned rb = m_ctx.get_params().recursion_bound();
if(rb != UINT_MAX){
std::ostringstream os; os << rb;
rs->SetOption("recursion_bound", os.str());
}
// Solve!
bool ans;
try {
ans = rs->Solve();
}
catch (Duality::solver::cancel_exception &exn){
throw default_exception("duality canceled");
}
// profile!
if(m_ctx.get_params().profile())
print_profile(std::cout);
// save the result and counterexample if there is one
_d->status = ans ? StatusModel : StatusRefutation;
_d->cex = rs->GetCounterexample();
if(old_data){
old_data->cex.tree = 0; // we own it now
dealloc(old_data);
}
dealloc(rs);
// true means the RPFP problem is SAT, so the query is UNSAT
return ans ? l_false : l_true;
}
expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) {
SASSERT(false);
return expr_ref(m_ctx.get_manager());
}
void dl_interface::add_cover(int level, ::func_decl* pred, ::expr* property) {
SASSERT(false);
}
unsigned dl_interface::get_num_levels(::func_decl* pred) {
SASSERT(false);
return 0;
}
void dl_interface::collect_statistics(::statistics& st) const {
}
void dl_interface::reset_statistics() {
}
static hash_set<func_decl> *local_func_decls;
static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexample &cex) {
context &ctx = d->dd()->ctx;
RPFP::Node &node = *cex.root;
RPFP::Edge &edge = *node.Outgoing;
// first, prove the children (that are actually used)
for(unsigned i = 0; i < edge.Children.size(); i++){
if(!cex.tree->Empty(edge.Children[i])){
Solver::Counterexample foo = cex;
foo.root = edge.Children[i];
print_proof(d,out,foo);
}
}
// print the label and the proved fact
out << "(step s!" << node.number;
out << " (" << node.Name.name();
for(unsigned i = 0; i < edge.F.IndParams.size(); i++)
out << " " << cex.tree->Eval(&edge,edge.F.IndParams[i]);
out << ")\n";
// print the rule number
out << " rule!" << node.Outgoing->map->number;
// print the substitution
out << " (subst\n";
RPFP::Edge *orig_edge = edge.map;
int orig_clause = d->dd()->map[orig_edge];
expr &t = d->dd()->clauses[orig_clause];
if (t.is_quantifier() && t.is_quantifier_forall()) {
int bound = t.get_quantifier_num_bound();
std::vector<sort> sorts;
std::vector<symbol> names;
hash_map<int,expr> subst;
for(int j = 0; j < bound; j++){
sort the_sort = t.get_quantifier_bound_sort(j);
symbol name = t.get_quantifier_bound_name(j);
expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort));
out << " (= " << skolem << " " << cex.tree->Eval(&edge,skolem) << ")\n";
expr local_skolem = cex.tree->Localize(&edge,skolem);
(*local_func_decls).insert(local_skolem.decl());
}
}
out << " )\n";
out << " (labels";
std::vector<symbol> labels;
cex.tree->GetLabels(&edge,labels);
for(unsigned j = 0; j < labels.size(); j++){
out << " " << labels[j];
}
out << " )\n";
// reference the proofs of all the children, in syntactic order
// "true" means the child is not needed
out << " (ref ";
for(unsigned i = 0; i < edge.Children.size(); i++){
if(!cex.tree->Empty(edge.Children[i]))
out << " s!" << edge.Children[i]->number;
else
out << " true";
}
out << " )";
out << ")\n";
}
void dl_interface::display_certificate(std::ostream& out) const {
((dl_interface *)this)->display_certificate_non_const(out);
}
void dl_interface::display_certificate_non_const(std::ostream& out) {
if(_d->status == StatusModel){
ast_manager &m = m_ctx.get_manager();
model_ref md = get_model();
model_smt2_pp(out, m, *md.get(), 0);
}
else if(_d->status == StatusRefutation){
out << "(derivation\n";
// negation of the query is the last clause -- prove it
hash_set<func_decl> locals;
local_func_decls = &locals;
print_proof(this,out,_d->cex);
out << ")\n";
out << "(model \n\"";
::model mod(m_ctx.get_manager());
model orig_model = _d->cex.tree->dualModel;
for(unsigned i = 0; i < orig_model.num_consts(); i++){
func_decl cnst = orig_model.get_const_decl(i);
if(locals.find(cnst) == locals.end()){
expr thing = orig_model.get_const_interp(cnst);
mod.register_decl(to_func_decl(cnst.raw()),to_expr(thing.raw()));
}
}
for(unsigned i = 0; i < orig_model.num_funcs(); i++){
func_decl cnst = orig_model.get_func_decl(i);
if(locals.find(cnst) == locals.end()){
func_interp thing = orig_model.get_func_interp(cnst);
::func_interp *thing_raw = thing;
mod.register_decl(to_func_decl(cnst.raw()),thing_raw->copy());
}
}
model_v2_pp(out,mod);
out << "\")\n";
}
}
expr_ref dl_interface::get_answer() {
SASSERT(false);
return expr_ref(m_ctx.get_manager());
}
void dl_interface::cancel() {
#if 0
if(_d && _d->ls)
_d->ls->cancel();
#else
// HACK: duality can't cancel at all times, we just exit here
std::cout << "(error \"duality canceled\")\nunknown\n";
abort();
#endif
}
void dl_interface::cleanup() {
}
void dl_interface::updt_params() {
}
model_ref dl_interface::get_model() {
ast_manager &m = m_ctx.get_manager();
model_ref md(alloc(::model, m));
std::vector<RPFP::Node *> &nodes = _d->rpfp->nodes;
expr_ref_vector conjs(m);
for (unsigned i = 0; i < nodes.size(); ++i) {
RPFP::Node *node = nodes[i];
func_decl &pred = node->Name;
expr_ref prop(m);
prop = to_expr(node->Annotation.Formula);
std::vector<expr> &params = node->Annotation.IndParams;
expr_ref q(m);
expr_ref_vector sig_vars(m);
for (unsigned j = 0; j < params.size(); ++j)
sig_vars.push_back(params[params.size()-j-1]); // TODO: why backwards?
expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q);
if (params.empty()) {
md->register_decl(pred, q);
}
else {
::func_interp* fi = alloc(::func_interp, m, params.size());
fi->set_else(q);
md->register_decl(pred, fi);
}
}
return md;
}
static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) {
context &ctx = d->dd()->ctx;
ast_manager &mgr = ctx.m();
RPFP::Node &node = *cex.root;
RPFP::Edge &edge = *node.Outgoing;
RPFP::Edge *orig_edge = edge.map;
// first, prove the children (that are actually used)
proof_ref_vector prems(mgr);
::vector<expr_ref_vector> substs;
int orig_clause = d->dd()->map[orig_edge];
expr &t = d->dd()->clauses[orig_clause];
prems.push_back(mgr.mk_asserted(ctx.uncook(t)));
substs.push_back(expr_ref_vector(mgr));
if (t.is_quantifier() && t.is_quantifier_forall()) {
int bound = t.get_quantifier_num_bound();
std::vector<sort> sorts;
std::vector<symbol> names;
hash_map<int,expr> subst;
for(int j = 0; j < bound; j++){
sort the_sort = t.get_quantifier_bound_sort(j);
symbol name = t.get_quantifier_bound_name(j);
expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort));
expr val = cex.tree->Eval(&edge,skolem);
expr_ref thing(ctx.uncook(val),mgr);
substs[0].push_back(thing);
expr local_skolem = cex.tree->Localize(&edge,skolem);
(*local_func_decls).insert(local_skolem.decl());
}
}
svector<std::pair<unsigned, unsigned> > pos;
for(unsigned i = 0; i < edge.Children.size(); i++){
if(!cex.tree->Empty(edge.Children[i])){
pos.push_back(std::pair<unsigned,unsigned>(i+1,0));
Solver::Counterexample foo = cex;
foo.root = edge.Children[i];
proof_ref prem = extract_proof(d,foo);
prems.push_back(prem);
substs.push_back(expr_ref_vector(mgr));
}
}
func_decl f = node.Name;
std::vector<expr> args;
for(unsigned i = 0; i < edge.F.IndParams.size(); i++)
args.push_back(cex.tree->Eval(&edge,edge.F.IndParams[i]));
expr conc = f(args);
::vector<proof *> pprems;
for(unsigned i = 0; i < prems.size(); i++)
pprems.push_back(prems[i].get());
proof_ref res(mgr.mk_hyper_resolve(pprems.size(),&pprems[0], ctx.uncook(conc), pos, substs),mgr);
return res;
}
proof_ref dl_interface::get_proof() {
if(_d->status == StatusRefutation){
hash_set<func_decl> locals;
local_func_decls = &locals;
return extract_proof(this,_d->cex);
}
else
return proof_ref(m_ctx.get_manager());
}
}

View file

@ -0,0 +1,80 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
duality_dl_interface.h
Abstract:
SMT2 interface for Duality
Author:
Krystof Hoder (t-khoder) 2011-9-22.
Modified by Ken McMIllan (kenmcmil) 2013-4-18.
Revision History:
--*/
#ifndef _DUALITY_DL_INTERFACE_H_
#define _DUALITY_DL_INTERFACE_H_
#include "lbool.h"
#include "dl_rule.h"
#include "dl_rule_set.h"
#include "dl_engine_base.h"
#include "statistics.h"
namespace datalog {
class context;
}
namespace Duality {
class duality_data;
class dl_interface : public datalog::engine_base {
duality_data *_d;
datalog::context &m_ctx;
public:
dl_interface(datalog::context& ctx);
~dl_interface();
lbool query(expr* query);
void cancel();
void cleanup();
void display_certificate(std::ostream& out) const;
void collect_statistics(statistics& st) const;
void reset_statistics();
expr_ref get_answer();
unsigned get_num_levels(func_decl* pred);
expr_ref get_cover_delta(int level, func_decl* pred);
void add_cover(int level, func_decl* pred, expr* property);
void updt_params();
model_ref get_model();
proof_ref get_proof();
duality_data *dd(){return _d;}
private:
void display_certificate_non_const(std::ostream& out);
};
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
datalog_parser.h
Abstract:
Parser for Datalogish files
Author:
Nikolaj Bjorner (nbjorner) 2010-5-17
Revision History:
--*/
#ifndef _DATALOG_PARSER_H_
#define _DATALOG_PARSER_H_
#include "ast.h"
#include "dl_context.h"
namespace datalog {
class parser {
public:
static parser * create(context& ctx, ast_manager & ast_manager);
virtual ~parser() {}
virtual bool parse_file(char const * path) = 0;
virtual bool parse_string(char const * string) = 0;
};
class wpa_parser {
public:
static wpa_parser * create(context& ctx, ast_manager & ast_manager);
virtual ~wpa_parser() {}
virtual bool parse_directory(char const * path) = 0;
};
};
#endif

479
src/muz/fp/dl_cmds.cpp Normal file
View file

@ -0,0 +1,479 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
dl_cmds.cpp
Abstract:
Datalog commands for SMT2 front-end.
Author:
Leonardo (leonardo) 2011-03-28
Notes:
--*/
#include"cmd_context.h"
#include"dl_cmds.h"
#include"dl_external_relation.h"
#include"dl_context.h"
#include"dl_register_engine.h"
#include"dl_decl_plugin.h"
#include"dl_instruction.h"
#include"dl_compiler.h"
#include"dl_rule.h"
#include"ast_pp.h"
#include"parametric_cmd.h"
#include"cancel_eh.h"
#include"scoped_ctrl_c.h"
#include"scoped_timer.h"
#include"trail.h"
#include"fixedpoint_params.hpp"
#include<iomanip>
struct dl_context {
smt_params m_fparams;
params_ref m_params_ref;
fixedpoint_params m_params;
cmd_context & m_cmd;
datalog::register_engine m_register_engine;
dl_collected_cmds* m_collected_cmds;
unsigned m_ref_count;
datalog::dl_decl_plugin* m_decl_plugin;
scoped_ptr<datalog::context> m_context;
trail_stack<dl_context> m_trail;
fixedpoint_params const& get_params() {
init();
return m_context->get_params();
}
dl_context(cmd_context & ctx, dl_collected_cmds* collected_cmds):
m_params(m_params_ref),
m_cmd(ctx),
m_collected_cmds(collected_cmds),
m_ref_count(0),
m_decl_plugin(0),
m_trail(*this) {}
void inc_ref() {
++m_ref_count;
}
void dec_ref() {
--m_ref_count;
if (0 == m_ref_count) {
dealloc(this);
}
}
void init() {
ast_manager& m = m_cmd.m();
if (!m_context) {
m_context = alloc(datalog::context, m, m_register_engine, m_fparams, m_params_ref);
}
if (!m_decl_plugin) {
symbol name("datalog_relation");
if (m.has_plugin(name)) {
m_decl_plugin = static_cast<datalog::dl_decl_plugin*>(m_cmd.m().get_plugin(m.mk_family_id(name)));
}
else {
m_decl_plugin = alloc(datalog::dl_decl_plugin);
m.register_plugin(symbol("datalog_relation"), m_decl_plugin);
}
}
}
void reset() {
m_context = 0;
}
void register_predicate(func_decl* pred, unsigned num_kinds, symbol const* kinds) {
if (m_collected_cmds) {
m_collected_cmds->m_rels.push_back(pred);
m_trail.push(push_back_vector<dl_context, func_decl_ref_vector>(m_collected_cmds->m_rels));
}
dlctx().register_predicate(pred, false);
dlctx().set_predicate_representation(pred, num_kinds, kinds);
}
void add_rule(expr * rule, symbol const& name) {
init();
if (m_collected_cmds) {
expr_ref rl = m_context->bind_variables(rule, true);
m_collected_cmds->m_rules.push_back(rl);
m_collected_cmds->m_names.push_back(name);
m_trail.push(push_back_vector<dl_context, expr_ref_vector>(m_collected_cmds->m_rules));
m_trail.push(push_back_vector<dl_context, svector<symbol> >(m_collected_cmds->m_names));
}
else {
m_context->add_rule(rule, name);
}
}
bool collect_query(expr* q) {
if (m_collected_cmds) {
expr_ref qr = m_context->bind_variables(q, false);
m_collected_cmds->m_queries.push_back(qr);
m_trail.push(push_back_vector<dl_context, expr_ref_vector>(m_collected_cmds->m_queries));
return true;
}
else {
return false;
}
}
void push() {
m_trail.push_scope();
dlctx().push();
}
void pop() {
m_trail.pop_scope(1);
dlctx().pop();
}
datalog::context & dlctx() {
init();
return *m_context;
}
};
/**
\brief rule command. It is also the owner of dl_context object.
*/
class dl_rule_cmd : public cmd {
ref<dl_context> m_dl_ctx;
mutable unsigned m_arg_idx;
expr* m_t;
symbol m_name;
public:
dl_rule_cmd(dl_context * dl_ctx):
cmd("rule"),
m_dl_ctx(dl_ctx),
m_arg_idx(0),
m_t(0) {}
virtual char const * get_usage() const { return "(forall (q) (=> (and body) head)) :optional-name"; }
virtual char const * get_descr(cmd_context & ctx) const { return "add a Horn rule."; }
virtual unsigned get_arity() const { return VAR_ARITY; }
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
switch(m_arg_idx) {
case 0: return CPK_EXPR;
case 1: return CPK_SYMBOL;
default: return CPK_SYMBOL;
}
}
virtual void set_next_arg(cmd_context & ctx, expr * t) {
m_t = t;
m_arg_idx++;
}
virtual void set_next_arg(cmd_context & ctx, symbol const & s) {
m_name = s;
}
virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); }
virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; }
virtual void finalize(cmd_context & ctx) {
}
virtual void execute(cmd_context & ctx) {
m_dl_ctx->add_rule(m_t, m_name);
}
};
class dl_query_cmd : public parametric_cmd {
ref<dl_context> m_dl_ctx;
expr* m_target;
public:
dl_query_cmd(dl_context * dl_ctx):
parametric_cmd("query"),
m_dl_ctx(dl_ctx),
m_target(0) {
}
virtual char const * get_usage() const { return "(exists (q) (and body))"; }
virtual char const * get_main_descr() const {
return "pose a query based on the Horn rules.";
}
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
if (m_target == 0) return CPK_EXPR;
return parametric_cmd::next_arg_kind(ctx);
}
virtual void set_next_arg(cmd_context & ctx, expr * t) {
m_target = t;
}
virtual void prepare(cmd_context & ctx) {
parametric_cmd::prepare(ctx);
m_target = 0;
}
virtual void execute(cmd_context& ctx) {
if (m_target == 0) {
throw cmd_exception("invalid query command, argument expected");
}
if (m_dl_ctx->collect_query(m_target)) {
return;
}
datalog::context& dlctx = m_dl_ctx->dlctx();
set_background(ctx);
dlctx.updt_params(m_params);
unsigned timeout = m_dl_ctx->get_params().timeout();
cancel_eh<datalog::context> eh(dlctx);
bool query_exn = false;
lbool status = l_undef;
{
scoped_ctrl_c ctrlc(eh);
scoped_timer timer(timeout, &eh);
cmd_context::scoped_watch sw(ctx);
try {
status = dlctx.query(m_target);
}
catch (z3_error & ex) {
ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl;
throw ex;
}
catch (z3_exception& ex) {
ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl;
query_exn = true;
}
}
switch (status) {
case l_false:
ctx.regular_stream() << "unsat\n";
print_certificate(ctx);
break;
case l_true:
ctx.regular_stream() << "sat\n";
print_answer(ctx);
print_certificate(ctx);
break;
case l_undef:
ctx.regular_stream() << "unknown\n";
switch(dlctx.get_status()) {
case datalog::INPUT_ERROR:
ctx.regular_stream() << "input error\n";
break;
case datalog::MEMOUT:
ctx.regular_stream() << "memory bounds exceeded\n";
break;
case datalog::TIMEOUT:
ctx.regular_stream() << "timeout\n";
break;
case datalog::APPROX:
ctx.regular_stream() << "approximated relations\n";
break;
case datalog::OK:
SASSERT(query_exn);
break;
case datalog::CANCELED:
ctx.regular_stream() << "canceled\n";
dlctx.display_profile(ctx.regular_stream());
break;
default:
UNREACHABLE();
break;
}
break;
}
dlctx.cleanup();
print_statistics(ctx);
m_target = 0;
}
virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) {
m_dl_ctx->dlctx().collect_params(p);
}
private:
void set_background(cmd_context& ctx) {
datalog::context& dlctx = m_dl_ctx->dlctx();
ptr_vector<expr>::const_iterator it = ctx.begin_assertions();
ptr_vector<expr>::const_iterator end = ctx.end_assertions();
for (; it != end; ++it) {
dlctx.assert_expr(*it);
}
}
void print_answer(cmd_context& ctx) {
if (m_dl_ctx->get_params().print_answer()) {
datalog::context& dlctx = m_dl_ctx->dlctx();
ast_manager& m = ctx.m();
expr_ref query_result(dlctx.get_answer_as_formula(), m);
sbuffer<symbol> var_names;
unsigned num_decls = 0;
if (is_quantifier(m_target)) {
num_decls = to_quantifier(m_target)->get_num_decls();
}
ctx.display(ctx.regular_stream(), query_result, 0, num_decls, "X", var_names);
ctx.regular_stream() << std::endl;
}
}
void print_statistics(cmd_context& ctx) {
if (m_dl_ctx->get_params().print_statistics()) {
statistics st;
datalog::context& dlctx = m_dl_ctx->dlctx();
unsigned long long max_mem = memory::get_max_used_memory();
unsigned long long mem = memory::get_allocation_size();
dlctx.collect_statistics(st);
st.update("time", ctx.get_seconds());
st.update("memory", static_cast<double>(mem)/static_cast<double>(1024*1024));
st.update("max-memory", static_cast<double>(max_mem)/static_cast<double>(1024*1024));
st.display_smt2(ctx.regular_stream());
}
}
void print_certificate(cmd_context& ctx) {
if (m_dl_ctx->get_params().print_certificate()) {
datalog::context& dlctx = m_dl_ctx->dlctx();
dlctx.display_certificate(ctx.regular_stream());
ctx.regular_stream() << "\n";
}
}
};
class dl_declare_rel_cmd : public cmd {
ref<dl_context> m_dl_ctx;
unsigned m_arg_idx;
mutable unsigned m_query_arg_idx;
symbol m_rel_name;
scoped_ptr<sort_ref_vector> m_domain;
svector<symbol> m_kinds;
void ensure_domain(cmd_context& ctx) {
if (!m_domain) m_domain = alloc(sort_ref_vector, ctx.m());
}
public:
dl_declare_rel_cmd(dl_context * dl_ctx):
cmd("declare-rel"),
m_dl_ctx(dl_ctx),
m_domain(0) {}
virtual char const * get_usage() const { return "<symbol> (<arg1 sort> ...) <representation>*"; }
virtual char const * get_descr(cmd_context & ctx) const { return "declare new relation"; }
virtual unsigned get_arity() const { return VAR_ARITY; }
virtual void prepare(cmd_context & ctx) {
m_arg_idx = 0;
m_query_arg_idx = 0;
m_domain = 0;
m_kinds.reset();
}
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
switch(m_query_arg_idx++) {
case 0: return CPK_SYMBOL; // relation name
case 1: return CPK_SORT_LIST; // arguments
default: return CPK_SYMBOL; // optional representation specification
}
}
virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) {
ensure_domain(ctx);
m_domain->append(num, slist);
m_arg_idx++;
}
virtual void set_next_arg(cmd_context & ctx, symbol const & s) {
if(m_arg_idx==0) {
m_rel_name = s;
}
else {
SASSERT(m_arg_idx>1);
m_kinds.push_back(s);
}
m_arg_idx++;
}
virtual void execute(cmd_context & ctx) {
if(m_arg_idx<2) {
throw cmd_exception("at least 2 arguments expected");
}
ensure_domain(ctx);
ast_manager& m = ctx.m();
func_decl_ref pred(
m.mk_func_decl(m_rel_name, m_domain->size(), m_domain->c_ptr(), m.mk_bool_sort()), m);
ctx.insert(pred);
m_dl_ctx->register_predicate(pred, m_kinds.size(), m_kinds.c_ptr());
m_domain = 0;
}
};
class dl_declare_var_cmd : public cmd {
unsigned m_arg_idx;
symbol m_var_name;
sort* m_var_sort;
ref<dl_context> m_dl_ctx;
public:
dl_declare_var_cmd(dl_context* dl_ctx):
cmd("declare-var"),
m_arg_idx(0),
m_dl_ctx(dl_ctx)
{}
virtual char const * get_usage() const { return "<symbol> <sort>"; }
virtual char const * get_descr(cmd_context & ctx) const { return "declare constant as variable"; }
virtual unsigned get_arity() const { return 2; }
virtual void prepare(cmd_context & ctx) {
m_arg_idx = 0;
}
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
SASSERT(m_arg_idx <= 1);
if (m_arg_idx == 0) {
return CPK_SYMBOL;
}
return CPK_SORT;
}
virtual void set_next_arg(cmd_context & ctx, sort* s) {
m_var_sort = s;
++m_arg_idx;
}
virtual void set_next_arg(cmd_context & ctx, symbol const & s) {
m_var_name = s;
++m_arg_idx;
}
virtual void execute(cmd_context & ctx) {
ast_manager& m = ctx.m();
func_decl_ref var(m.mk_func_decl(m_var_name, 0, static_cast<sort*const*>(0), m_var_sort), m);
ctx.insert(var);
m_dl_ctx->dlctx().register_variable(var);
}
};
static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_cmds) {
dl_context * dl_ctx = alloc(dl_context, ctx, collected_cmds);
ctx.insert(alloc(dl_rule_cmd, dl_ctx));
ctx.insert(alloc(dl_query_cmd, dl_ctx));
ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx));
ctx.insert(alloc(dl_declare_var_cmd, dl_ctx));
// #ifndef _EXTERNAL_RELEASE
// TODO: we need these!
#if 0
ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple.
ctx.insert(alloc(dl_pop_cmd, dl_ctx));
#endif
// #endif
}
void install_dl_cmds(cmd_context & ctx) {
install_dl_cmds_aux(ctx, 0);
}
void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context & ctx) {
install_dl_cmds_aux(ctx, &collected_cmds);
}

37
src/muz/fp/dl_cmds.h Normal file
View file

@ -0,0 +1,37 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
dl_cmds.h
Abstract:
Datalog commands for SMT2 front-end.
Author:
Nikolaj Bjorner (nbjorner) 2012-11-17
Notes:
--*/
#ifndef _DL_CMDS_H_
#define _DL_CMDS_H_
#include "ast.h"
class cmd_context;
struct dl_collected_cmds {
expr_ref_vector m_rules;
svector<symbol> m_names;
expr_ref_vector m_queries;
func_decl_ref_vector m_rels;
dl_collected_cmds(ast_manager& m) : m_rules(m), m_queries(m), m_rels(m) {}
};
void install_dl_cmds(cmd_context & ctx);
void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context& ctx);
#endif

View file

@ -0,0 +1,54 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
dl_register_engine.cpp
Abstract:
Class for creating Datalog engines.
Author:
Nikolaj Bjorner (nbjorner) 2013-08-28
Revision History:
--*/
#include "dl_register_engine.h"
#include "dl_bmc_engine.h"
#include "clp_context.h"
#include "tab_context.h"
#include "rel_context.h"
#include "pdr_dl_interface.h"
#include "duality_dl_interface.h"
namespace datalog {
register_engine::register_engine(): m_ctx(0) {}
engine_base* register_engine::mk_engine(DL_ENGINE engine_type) {
switch(engine_type) {
case PDR_ENGINE:
case QPDR_ENGINE:
return alloc(pdr::dl_interface, *m_ctx);
case DATALOG_ENGINE:
return alloc(rel_context, *m_ctx);
case BMC_ENGINE:
case QBMC_ENGINE:
return alloc(bmc, *m_ctx);
case TAB_ENGINE:
return alloc(tab, *m_ctx);
case CLP_ENGINE:
return alloc(clp, *m_ctx);
case DUALITY_ENGINE:
return alloc(Duality::dl_interface, *m_ctx);
case LAST_ENGINE:
UNREACHABLE();
return 0;
}
UNREACHABLE();
return 0;
}
}

View file

@ -0,0 +1,36 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
dl_register_engine.h
Abstract:
Class for creating Datalog engines.
Author:
Nikolaj Bjorner (nbjorner) 2013-08-28
Revision History:
--*/
#ifndef _DL_REGISTER_ENGINE_H_
#define _DL_REGISTER_ENGINE_H_
#include "dl_context.h"
namespace datalog {
class register_engine : public register_engine_base {
context* m_ctx;
public:
register_engine();
engine_base* mk_engine(DL_ENGINE engine_type);
void set_context(context* ctx) { m_ctx = ctx; }
};
}
#endif

422
src/muz/fp/horn_tactic.cpp Normal file
View file

@ -0,0 +1,422 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
horn_tactic.h
Abstract:
HORN as a tactic to solve Horn clauses.
Author:
Nikolaj Bjorner (nbjorner) 2012-11-16.
Revision History:
--*/
#include"tactical.h"
#include"model_converter.h"
#include"proof_converter.h"
#include"horn_tactic.h"
#include"dl_context.h"
#include"dl_register_engine.h"
#include"expr_replacer.h"
#include"dl_rule_transformer.h"
#include"dl_mk_slice.h"
#include"filter_model_converter.h"
#include"dl_transforms.h"
#include"fixedpoint_params.hpp"
class horn_tactic : public tactic {
struct imp {
ast_manager& m;
bool m_is_simplify;
datalog::register_engine m_register_engine;
datalog::context m_ctx;
smt_params m_fparams;
imp(bool t, ast_manager & m, params_ref const & p):
m(m),
m_is_simplify(t),
m_ctx(m, m_register_engine, m_fparams) {
updt_params(p);
}
void updt_params(params_ref const & p) {
m_ctx.updt_params(p);
}
void collect_param_descrs(param_descrs & r) {
m_ctx.collect_params(r);
}
void reset_statistics() {
m_ctx.reset_statistics();
}
void collect_statistics(statistics & st) const {
m_ctx.collect_statistics(st);
}
void set_cancel(bool f) {
if (f) {
m_ctx.cancel();
}
}
void normalize(expr_ref& f) {
bool is_positive = true;
expr* e = 0;
while (true) {
if (is_forall(f) && is_positive) {
f = to_quantifier(f)->get_expr();
}
else if (is_exists(f) && !is_positive) {
f = to_quantifier(f)->get_expr();
}
else if (m.is_not(f, e)) {
is_positive = !is_positive;
f = e;
}
else {
break;
}
}
if (!is_positive) {
f = m.mk_not(f);
}
}
bool is_predicate(expr* a) {
SASSERT(m.is_bool(a));
return is_app(a) && to_app(a)->get_decl()->get_family_id() == null_family_id;
}
void register_predicate(expr* a) {
SASSERT(is_predicate(a));
m_ctx.register_predicate(to_app(a)->get_decl(), false);
}
void check_predicate(ast_mark& mark, expr* a) {
ptr_vector<expr> todo;
todo.push_back(a);
while (!todo.empty()) {
a = todo.back();
todo.pop_back();
if (mark.is_marked(a)) {
continue;
}
mark.mark(a, true);
if (is_quantifier(a)) {
a = to_quantifier(a)->get_expr();
todo.push_back(a);
}
else if (m.is_not(a) || m.is_and(a) || m.is_or(a) || m.is_implies(a)) {
todo.append(to_app(a)->get_num_args(), to_app(a)->get_args());
}
else if (m.is_ite(a)) {
todo.push_back(to_app(a)->get_arg(1));
todo.push_back(to_app(a)->get_arg(2));
}
else if (is_predicate(a)) {
register_predicate(a);
}
}
}
enum formula_kind { IS_RULE, IS_QUERY, IS_NONE };
bool is_implication(expr* f) {
expr* e1;
while (is_forall(f)) {
f = to_quantifier(f)->get_expr();
}
while (m.is_implies(f, e1, f)) ;
return is_predicate(f);
}
formula_kind get_formula_kind(expr_ref& f) {
expr_ref tmp(f);
normalize(tmp);
ast_mark mark;
expr_ref_vector args(m), body(m);
expr_ref head(m);
expr* a = 0, *a1 = 0;
qe::flatten_or(tmp, args);
for (unsigned i = 0; i < args.size(); ++i) {
a = args[i].get();
check_predicate(mark, a);
if (m.is_not(a, a1)) {
body.push_back(a1);
}
else if (is_predicate(a)) {
if (head) {
return IS_NONE;
}
head = a;
}
else {
body.push_back(m.mk_not(a));
}
}
if (head) {
if (!is_implication(f)) {
f = m.mk_and(body.size(), body.c_ptr());
f = m.mk_implies(f, head);
}
return IS_RULE;
}
else {
f = m.mk_and(body.size(), body.c_ptr());
return IS_QUERY;
}
}
expr_ref mk_rule(expr* body, expr* head) {
return expr_ref(m.mk_implies(body, head), m);
}
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
SASSERT(g->is_well_sorted());
mc = 0; pc = 0; core = 0;
tactic_report report("horn", *g);
bool produce_proofs = g->proofs_enabled();
if (produce_proofs) {
if (!m_ctx.get_params().generate_proof_trace()) {
params_ref params = m_ctx.get_params().p;
params.set_bool("generate_proof_trace", true);
updt_params(params);
}
}
unsigned sz = g->size();
expr_ref q(m), f(m);
expr_ref_vector queries(m);
std::stringstream msg;
m_ctx.reset();
m_ctx.ensure_opened();
for (unsigned i = 0; i < sz; i++) {
f = g->form(i);
formula_kind k = get_formula_kind(f);
switch(k) {
case IS_RULE:
m_ctx.add_rule(f, symbol::null);
break;
case IS_QUERY:
queries.push_back(f);
break;
default:
msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n";
TRACE("horn", tout << msg.str(););
throw tactic_exception(msg.str().c_str());
}
}
if (queries.size() != 1 || m_is_simplify) {
q = m.mk_fresh_const("query", m.mk_bool_sort());
register_predicate(q);
for (unsigned i = 0; i < queries.size(); ++i) {
f = mk_rule(queries[i].get(), q);
m_ctx.add_rule(f, symbol::null);
}
queries.reset();
queries.push_back(q);
filter_model_converter* mc1 = alloc(filter_model_converter, m);
mc1->insert(to_app(q)->get_decl());
mc = mc1;
}
SASSERT(queries.size() == 1);
q = queries[0].get();
if (m_is_simplify) {
simplify(q, g, result, mc, pc);
}
else {
verify(q, g, result, mc, pc);
}
}
void verify(expr* q,
goal_ref const& g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc) {
lbool is_reachable = l_undef;
try {
is_reachable = m_ctx.query(q);
}
catch (default_exception& ex) {
IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";);
throw ex;
}
g->inc_depth();
bool produce_models = g->models_enabled();
bool produce_proofs = g->proofs_enabled();
result.push_back(g.get());
switch (is_reachable) {
case l_true: {
// goal is unsat
if (produce_proofs) {
proof_ref proof = m_ctx.get_proof();
pc = proof2proof_converter(m, proof);
g->assert_expr(m.mk_false(), proof, 0);
}
else {
g->assert_expr(m.mk_false());
}
break;
}
case l_false: {
// goal is sat
g->reset();
if (produce_models) {
model_ref md = m_ctx.get_model();
model_converter_ref mc2 = model2model_converter(&*md);
if (mc) {
mc = concat(mc.get(), mc2.get());
}
else {
mc = mc2;
}
}
break;
}
case l_undef:
// subgoal is unchanged.
break;
}
TRACE("horn", g->display(tout););
SASSERT(g->is_well_sorted());
}
void simplify(expr* q,
goal_ref const& g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc) {
expr_ref fml(m);
func_decl* query_pred = to_app(q)->get_decl();
m_ctx.set_output_predicate(query_pred);
m_ctx.get_rules(); // flush adding rules.
apply_default_transformation(m_ctx);
if (m_ctx.get_params().slice()) {
datalog::rule_transformer transformer(m_ctx);
datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx);
transformer.register_plugin(slice);
m_ctx.transform_rules(transformer);
}
expr_substitution sub(m);
sub.insert(q, m.mk_false());
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
rep->set_substitution(&sub);
g->inc_depth();
g->reset();
result.push_back(g.get());
datalog::rule_set const& rules = m_ctx.get_rules();
datalog::rule_set::iterator it = rules.begin(), end = rules.end();
for (; it != end; ++it) {
datalog::rule* r = *it;
r->to_formula(fml);
(*rep)(fml);
g->assert_expr(fml);
}
}
};
bool m_is_simplify;
params_ref m_params;
statistics m_stats;
imp * m_imp;
public:
horn_tactic(bool t, ast_manager & m, params_ref const & p):
m_is_simplify(t),
m_params(p) {
m_imp = alloc(imp, t, m, p);
}
virtual tactic * translate(ast_manager & m) {
return alloc(horn_tactic, m_is_simplify, m, m_params);
}
virtual ~horn_tactic() {
dealloc(m_imp);
}
virtual void updt_params(params_ref const & p) {
m_params = p;
m_imp->updt_params(p);
}
virtual void collect_param_descrs(param_descrs & r) {
m_imp->collect_param_descrs(r);
}
virtual void operator()(goal_ref const & in,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
(*m_imp)(in, result, mc, pc, core);
}
virtual void collect_statistics(statistics & st) const {
m_imp->collect_statistics(st);
st.copy(m_stats);
}
virtual void reset_statistics() {
m_stats.reset();
m_imp->reset_statistics();
}
virtual void cleanup() {
ast_manager & m = m_imp->m;
imp * d = m_imp;
d->collect_statistics(m_stats);
#pragma omp critical (tactic_cancel)
{
m_imp = 0;
}
dealloc(d);
d = alloc(imp, m_is_simplify, m, m_params);
#pragma omp critical (tactic_cancel)
{
m_imp = d;
}
}
protected:
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(horn_tactic, false, m, p));
}
tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(horn_tactic, true, m, p));
}

35
src/muz/fp/horn_tactic.h Normal file
View file

@ -0,0 +1,35 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
horn_tactic.h
Abstract:
PDR as a tactic to solve Horn clauses.
Author:
Nikolaj Bjorner (nbjorner) 2012-11-16.
Revision History:
--*/
#ifndef _HORN_TACTIC_H_
#define _HORN_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_horn_tactic(ast_manager & m, params_ref const & p = params_ref());
/*
ADD_TACTIC("horn", "apply tactic for horn clauses.", "mk_horn_tactic(m, p)")
*/
tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p = params_ref());
/*
ADD_TACTIC("horn-simplify", "simplify horn clauses.", "mk_horn_simplify_tactic(m, p)")
*/
#endif

176
src/muz/pdr/pdr_closure.cpp Normal file
View file

@ -0,0 +1,176 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pdr_closure.cpp
Abstract:
Utility functions for computing closures.
Author:
Nikolaj Bjorner (nbjorner) 2013-9-1.
Revision History:
--*/
#include "pdr_closure.h"
#include "pdr_context.h"
#include "expr_safe_replace.h"
namespace pdr {
expr_ref scaler::operator()(expr* e, expr* k, obj_map<func_decl, expr*>* translate) {
m_cache[0].reset();
m_cache[1].reset();
m_translate = translate;
m_k = k;
return scale(e, false);
}
expr_ref scaler::scale(expr* e, bool is_mul) {
expr* r;
if (m_cache[is_mul].find(e, r)) {
return expr_ref(r, m);
}
if (!is_app(e)) {
return expr_ref(e, m);
}
app* ap = to_app(e);
if (m_translate && m_translate->find(ap->get_decl(), r)) {
return expr_ref(r, m);
}
if (!is_mul && a.is_numeral(e)) {
return expr_ref(a.mk_mul(m_k, e), m);
}
expr_ref_vector args(m);
bool is_mul_rec = is_mul || a.is_mul(e);
for (unsigned i = 0; i < ap->get_num_args(); ++i) {
args.push_back(scale(ap->get_arg(i), is_mul_rec));
}
expr_ref result(m);
result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr());
m_cache[is_mul].insert(e, result);
return result;
}
expr_ref scaler::undo_k(expr* e, expr* k) {
expr_safe_replace sub(m);
th_rewriter rw(m);
expr_ref result(e, m);
sub.insert(k, a.mk_numeral(rational(1), false));
sub(result);
rw(result);
return result;
}
closure::closure(pred_transformer& p, bool is_closure):
m(p.get_manager()), m_pt(p), a(m),
m_is_closure(is_closure), m_sigma(m), m_trail(m) {}
void closure::add_variables(unsigned num_vars, expr_ref_vector& fmls) {
manager& pm = m_pt.get_pdr_manager();
SASSERT(num_vars > 0);
while (m_vars.size() < num_vars) {
m_vars.resize(m_vars.size()+1);
m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real()));
}
unsigned sz = m_pt.sig_size();
for (unsigned i = 0; i < sz; ++i) {
expr* var;
ptr_vector<expr> vars;
func_decl* fn0 = m_pt.sig(i);
func_decl* fn1 = pm.o2n(fn0, 0);
sort* srt = fn0->get_range();
if (a.is_int_real(srt)) {
for (unsigned j = 0; j < num_vars; ++j) {
if (!m_vars[j].find(fn1, var)) {
var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt);
m_trail.push_back(var);
m_vars[j].insert(fn1, var);
}
vars.push_back(var);
}
fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr())));
}
}
if (m_is_closure) {
for (unsigned i = 0; i < num_vars; ++i) {
fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real())));
}
}
else {
// is interior:
for (unsigned i = 0; i < num_vars; ++i) {
fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real())));
}
}
fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr())));
}
expr_ref closure::close_fml(expr* e) {
expr* e0, *e1, *e2;
expr_ref result(m);
if (a.is_lt(e, e1, e2)) {
result = a.mk_le(e1, e2);
}
else if (a.is_gt(e, e1, e2)) {
result = a.mk_ge(e1, e2);
}
else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) {
result = a.mk_le(e1, e2);
}
else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) {
result = a.mk_ge(e1, e2);
}
else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) ||
(m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) {
result = e;
}
else {
IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";);
result = m.mk_true();
}
return result;
}
expr_ref closure::close_conjunction(expr* fml) {
expr_ref_vector fmls(m);
qe::flatten_and(fml, fmls);
for (unsigned i = 0; i < fmls.size(); ++i) {
fmls[i] = close_fml(fmls[i].get());
}
return qe::mk_and(fmls);
}
expr_ref closure::relax(unsigned i, expr* fml) {
scaler sc(m);
expr_ref result = sc(fml, m_sigma[i].get(), &m_vars[i]);
return close_conjunction(result);
}
expr_ref closure::operator()(expr_ref_vector const& As) {
if (As.empty()) {
return expr_ref(m.mk_false(), m);
}
if (As.size() == 1) {
return expr_ref(As[0], m);
}
expr_ref_vector fmls(m);
expr_ref B(m);
add_variables(As.size(), fmls);
for (unsigned i = 0; i < As.size(); ++i) {
fmls.push_back(relax(i, As[i]));
}
B = qe::mk_and(fmls);
return B;
}
}

67
src/muz/pdr/pdr_closure.h Normal file
View file

@ -0,0 +1,67 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pdr_closure.h
Abstract:
Utility functions for computing closures.
Author:
Nikolaj Bjorner (nbjorner) 2013-9-1.
Revision History:
--*/
#ifndef _PDR_CLOSURE_H_
#define _PDR_CLOSURE_H_
#include "arith_decl_plugin.h"
namespace pdr {
// Arithmetic scaling functor.
// Variables are replaced using
// m_translate. Constants are replaced by
// multiplication with a variable 'k' (scale factor).
class scaler {
ast_manager& m;
arith_util a;
obj_map<expr, expr*> m_cache[2];
expr* m_k;
obj_map<func_decl, expr*>* m_translate;
public:
scaler(ast_manager& m): m(m), a(m), m_translate(0) {}
expr_ref operator()(expr* e, expr* k, obj_map<func_decl, expr*>* translate = 0);
expr_ref undo_k(expr* e, expr* k);
private:
expr_ref scale(expr* e, bool is_mul);
};
class pred_transformer;
class closure {
ast_manager& m;
pred_transformer& m_pt;
arith_util a;
bool m_is_closure;
expr_ref_vector m_sigma;
expr_ref_vector m_trail;
vector<obj_map<func_decl, expr*> > m_vars;
expr_ref relax(unsigned i, expr* fml);
expr_ref close_conjunction(expr* fml);
expr_ref close_fml(expr* fml);
void add_variables(unsigned num_vars, expr_ref_vector& fmls);
public:
closure(pred_transformer& pt, bool is_closure);
expr_ref operator()(expr_ref_vector const& As);
};
}
#endif

2187
src/muz/pdr/pdr_context.cpp Normal file

File diff suppressed because it is too large Load diff

430
src/muz/pdr/pdr_context.h Normal file
View file

@ -0,0 +1,430 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_context.h
Abstract:
PDR for datalog
Author:
Nikolaj Bjorner (nbjorner) 2011-11-20.
Revision History:
--*/
#ifndef _PDR_CONTEXT_H_
#define _PDR_CONTEXT_H_
#ifdef _CYGWIN
#undef min
#undef max
#endif
#include <deque>
#include "pdr_manager.h"
#include "pdr_prop_solver.h"
#include "pdr_reachable_cache.h"
#include "fixedpoint_params.hpp"
namespace datalog {
class rule_set;
class context;
};
namespace pdr {
class pred_transformer;
class model_node;
class context;
typedef obj_map<datalog::rule const, app_ref_vector*> rule2inst;
typedef obj_map<func_decl, pred_transformer*> decl2rel;
//
// Predicate transformer state.
// A predicate transformer corresponds to the
// set of rules that have the same head predicates.
//
class pred_transformer {
struct stats {
unsigned m_num_propagations;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
typedef obj_map<datalog::rule const, expr*> rule2expr;
typedef obj_map<datalog::rule const, ptr_vector<app> > rule2apps;
manager& pm; // pdr-manager
ast_manager& m; // manager
context& ctx;
func_decl_ref m_head; // predicate
func_decl_ref_vector m_sig; // signature
ptr_vector<pred_transformer> m_use; // places where 'this' is referenced.
ptr_vector<datalog::rule> m_rules; // rules used to derive transformer
prop_solver m_solver; // solver context
vector<expr_ref_vector> m_levels; // level formulas
expr_ref_vector m_invariants; // properties that are invariant.
obj_map<expr, unsigned> m_prop2level; // map property to level where it occurs.
obj_map<expr, datalog::rule const*> m_tag2rule; // map tag predicate to rule.
rule2expr m_rule2tag; // map rule to predicate tag.
rule2inst m_rule2inst; // map rules to instantiations of indices
rule2expr m_rule2transition; // map rules to transition
rule2apps m_rule2vars; // map rule to auxiliary variables
expr_ref m_transition; // transition relation.
expr_ref m_initial_state; // initial state.
reachable_cache m_reachable;
ptr_vector<func_decl> m_predicates;
stats m_stats;
void init_sig();
void ensure_level(unsigned level);
bool add_property1(expr * lemma, unsigned lvl); // add property 'p' to state at level lvl.
void add_child_property(pred_transformer& child, expr* lemma, unsigned lvl);
void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result);
// Initialization
void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition);
void init_rule(decl2rel const& pts, datalog::rule const& rule, expr_ref& init,
ptr_vector<datalog::rule const>& rules, expr_ref_vector& transition);
void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx);
void simplify_formulas(tactic& tac, expr_ref_vector& fmls);
// Debugging
bool check_filled(app_ref_vector const& v) const;
void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r);
public:
pred_transformer(context& ctx, manager& pm, func_decl* head);
~pred_transformer();
void add_rule(datalog::rule* r) { m_rules.push_back(r); }
void add_use(pred_transformer* pt) { if (!m_use.contains(pt)) m_use.insert(pt); }
void initialize(decl2rel const& pts);
func_decl* head() const { return m_head; }
ptr_vector<datalog::rule> const& rules() const { return m_rules; }
func_decl* sig(unsigned i) { init_sig(); return m_sig[i].get(); } // signature
func_decl* const* sig() { init_sig(); return m_sig.c_ptr(); }
unsigned sig_size() { init_sig(); return m_sig.size(); }
expr* transition() const { return m_transition; }
expr* initial_state() const { return m_initial_state; }
expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); }
unsigned get_num_levels() { return m_levels.size(); }
expr_ref get_cover_delta(func_decl* p_orig, int level);
void add_cover(unsigned level, expr* property);
std::ostream& display(std::ostream& strm) const;
void collect_statistics(statistics& st) const;
void reset_statistics();
bool is_reachable(expr* state);
void remove_predecessors(expr_ref_vector& literals);
void find_predecessors(datalog::rule const& r, ptr_vector<func_decl>& predicates) const;
datalog::rule const& find_rule(model_core const& model) const;
expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); }
ptr_vector<app>& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); }
bool propagate_to_next_level(unsigned level);
void propagate_to_infinity(unsigned level);
void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level.
lbool is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level);
bool is_invariant(unsigned level, expr* co_state, bool inductive, bool& assumes_level, expr_ref_vector* core = 0);
bool check_inductive(unsigned level, expr_ref_vector& state, bool& assumes_level);
expr_ref get_formulas(unsigned level, bool add_axioms);
void simplify_formulas();
expr_ref get_propagation_formula(decl2rel const& pts, unsigned level);
manager& get_pdr_manager() const { return pm; }
ast_manager& get_manager() const { return m; }
void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r);
void close(expr* e);
app_ref_vector& get_inst(datalog::rule const* r) { return *m_rule2inst.find(r);}
void inherit_properties(pred_transformer& other);
void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector<app>& aux_vars);
prop_solver& get_solver() { return m_solver; }
prop_solver const& get_solver() const { return m_solver; }
void set_use_farkas(bool f) { get_solver().set_use_farkas(f); }
bool get_use_farkas() const { return get_solver().get_use_farkas(); }
class scoped_farkas {
bool m_old;
pred_transformer& m_p;
public:
scoped_farkas(pred_transformer& p, bool v): m_old(p.get_use_farkas()), m_p(p) {
p.set_use_farkas(v);
}
~scoped_farkas() { m_p.set_use_farkas(m_old); }
};
};
// structure for counter-example search.
class model_node {
model_node* m_parent;
pred_transformer& m_pt;
expr_ref m_state;
model_ref m_model;
ptr_vector<model_node> m_children;
unsigned m_level;
unsigned m_orig_level;
unsigned m_depth;
bool m_closed;
datalog::rule const* m_rule;
public:
model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level):
m_parent(parent), m_pt(pt), m_state(state), m_model(0),
m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(0) {
if (m_parent) {
m_parent->m_children.push_back(this);
SASSERT(m_parent->m_level == level+1);
SASSERT(m_parent->m_level > 0);
m_depth = m_parent->m_depth+1;
}
}
void set_model(model_ref& m) { m_model = m; }
unsigned level() const { return m_level; }
unsigned orig_level() const { return m_orig_level; }
unsigned depth() const { return m_depth; }
void increase_level() { ++m_level; }
expr* state() const { return m_state; }
ptr_vector<model_node> const& children() { return m_children; }
pred_transformer& pt() const { return m_pt; }
model_node* parent() const { return m_parent; }
model* get_model_ptr() const { return m_model.get(); }
model const& get_model() const { return *m_model; }
unsigned index() const;
bool is_closed() const { return m_closed; }
bool is_open() const { return !is_closed(); }
bool is_1closed() {
if (is_closed()) return true;
if (m_children.empty()) return false;
for (unsigned i = 0; i < m_children.size(); ++i) {
if (m_children[i]->is_open()) return false;
}
return true;
}
void set_closed();
void set_pre_closed() { m_closed = true; }
void reset() { m_children.reset(); }
void set_rule(datalog::rule const* r) { m_rule = r; }
datalog::rule* get_rule();
void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding);
std::ostream& display(std::ostream& out, unsigned indent);
};
class model_search {
bool m_bfs;
model_node* m_root;
std::deque<model_node*> m_leaves;
vector<obj_map<expr, unsigned> > m_cache;
obj_map<expr, unsigned>& cache(model_node const& n);
void erase_children(model_node& n);
void erase_leaf(model_node& n);
void remove_node(model_node& n);
void enqueue_leaf(model_node& n); // add leaf to priority queue.
void update_models();
public:
model_search(bool bfs): m_bfs(bfs), m_root(0) {}
~model_search();
void reset();
model_node* next();
bool is_repeated(model_node& n) const;
void add_leaf(model_node& n); // add fresh node.
void set_leaf(model_node& n); // Set node as leaf, remove children.
void set_root(model_node* n);
model_node& get_root() const { return *m_root; }
std::ostream& display(std::ostream& out) const;
expr_ref get_trace(context const& ctx);
proof_ref get_proof_trace(context const& ctx);
void backtrack_level(bool uses_level, model_node& n);
};
struct model_exception { };
struct inductive_exception {};
// 'state' is unsatisfiable at 'level' with 'core'.
// Minimize or weaken core.
class core_generalizer {
protected:
context& m_ctx;
public:
typedef vector<std::pair<expr_ref_vector,bool> > cores;
core_generalizer(context& ctx): m_ctx(ctx) {}
virtual ~core_generalizer() {}
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) = 0;
virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
new_cores.push_back(std::make_pair(core, uses_level));
if (!core.empty()) {
(*this)(n, new_cores.back().first, new_cores.back().second);
}
}
virtual void collect_statistics(statistics& st) const {}
virtual void reset_statistics() {}
};
class context {
struct stats {
unsigned m_num_nodes;
unsigned m_max_depth;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
smt_params& m_fparams;
fixedpoint_params const& m_params;
ast_manager& m;
datalog::context* m_context;
manager m_pm;
decl2rel m_rels; // Map from relation predicate to fp-operator.
func_decl_ref m_query_pred;
pred_transformer* m_query;
mutable model_search m_search;
lbool m_last_result;
unsigned m_inductive_lvl;
unsigned m_expanded_lvl;
ptr_vector<core_generalizer> m_core_generalizers;
stats m_stats;
volatile bool m_cancel;
model_converter_ref m_mc;
proof_converter_ref m_pc;
// Functions used by search.
void solve_impl();
bool check_reachability(unsigned level);
void propagate(unsigned max_prop_lvl);
void close_node(model_node& n);
void check_pre_closed(model_node& n);
void expand_node(model_node& n);
lbool expand_state(model_node& n, expr_ref_vector& cube, bool& uses_level);
void create_children(model_node& n);
expr_ref mk_sat_answer() const;
expr_ref mk_unsat_answer() const;
// Generate inductive property
void get_level_property(unsigned lvl, expr_ref_vector& res, vector<relation_info> & rs) const;
// Initialization
class classifier_proc;
void init_core_generalizers(datalog::rule_set& rules);
bool check_invariant(unsigned lvl);
bool check_invariant(unsigned lvl, func_decl* fn);
void checkpoint();
void init_rules(datalog::rule_set& rules, decl2rel& transformers);
void simplify_formulas();
void reset_core_generalizers();
void validate();
public:
/**
Initial values of predicates are stored in corresponding relations in dctx.
We check whether there is some reachable state of the relation checked_relation.
*/
context(
smt_params& fparams,
fixedpoint_params const& params,
ast_manager& m);
~context();
smt_params& get_fparams() const { return m_fparams; }
fixedpoint_params const& get_params() const { return m_params; }
ast_manager& get_manager() const { return m; }
manager& get_pdr_manager() { return m_pm; }
decl2rel const& get_pred_transformers() const { return m_rels; }
pred_transformer& get_pred_transformer(func_decl* p) const { return *m_rels.find(p); }
datalog::context& get_context() const { SASSERT(m_context); return *m_context; }
expr_ref get_answer();
bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; }
bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; }
void collect_statistics(statistics& st) const;
void reset_statistics();
std::ostream& display(std::ostream& strm) const;
void display_certificate(std::ostream& strm) const;
lbool solve();
void cancel();
void cleanup();
void reset();
void set_query(func_decl* q) { m_query_pred = q; }
void set_unsat() { m_last_result = l_false; }
void set_model_converter(model_converter_ref& mc) { m_mc = mc; }
model_converter_ref get_model_converter() { return m_mc; }
void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; }
void update_rules(datalog::rule_set& rules);
void set_axioms(expr* axioms) { m_pm.set_background(axioms); }
unsigned get_num_levels(func_decl* p);
expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p);
void add_cover(int level, func_decl* pred, expr* property);
model_ref get_model();
proof_ref get_proof() const;
model_node& get_root() const { return m_search.get_root(); }
};
};
#endif

View file

@ -0,0 +1,226 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_dl.cpp
Abstract:
SMT2 interface for the datalog PDR
Author:
Krystof Hoder (t-khoder) 2011-9-22.
Revision History:
--*/
#include "dl_context.h"
#include "dl_mk_coi_filter.h"
#include "dl_mk_interp_tail_simplifier.h"
#include "dl_mk_subsumption_checker.h"
#include "dl_mk_rule_inliner.h"
#include "dl_rule.h"
#include "dl_rule_transformer.h"
#include "smt2parser.h"
#include "pdr_context.h"
#include "pdr_dl_interface.h"
#include "dl_rule_set.h"
#include "dl_mk_slice.h"
#include "dl_mk_unfold.h"
#include "dl_mk_coalesce.h"
#include "dl_transforms.h"
#include "scoped_proof.h"
#include "model_smt2_pp.h"
using namespace pdr;
dl_interface::dl_interface(datalog::context& ctx) :
engine_base(ctx.get_manager(), "pdr"),
m_ctx(ctx),
m_pdr_rules(ctx),
m_old_rules(ctx),
m_context(0),
m_refs(ctx.get_manager()) {
m_context = alloc(pdr::context, ctx.get_fparams(), ctx.get_params(), ctx.get_manager());
}
dl_interface::~dl_interface() {
dealloc(m_context);
}
//
// Check if the new rules are weaker so that we can
// re-use existing context.
//
void dl_interface::check_reset() {
datalog::rule_set const& new_rules = m_ctx.get_rules();
datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules();
bool is_subsumed = !old_rules.empty();
for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) {
is_subsumed = false;
for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) {
if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) {
is_subsumed = true;
}
}
if (!is_subsumed) {
TRACE("pdr", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule "););
m_context->reset();
}
}
m_old_rules.replace_rules(new_rules);
}
lbool dl_interface::query(expr * query) {
//we restore the initial state in the datalog context
m_ctx.ensure_opened();
m_refs.reset();
m_pred2slice.reset();
ast_manager& m = m_ctx.get_manager();
datalog::rule_manager& rm = m_ctx.get_rule_manager();
datalog::rule_set old_rules(m_ctx.get_rules());
func_decl_ref query_pred(m);
rm.mk_query(query, m_ctx.get_rules());
expr_ref bg_assertion = m_ctx.get_background_assertion();
check_reset();
TRACE("pdr",
if (!m.is_true(bg_assertion)) {
tout << "axioms:\n";
tout << mk_pp(bg_assertion, m) << "\n";
}
tout << "query: " << mk_pp(query, m) << "\n";
tout << "rules:\n";
m_ctx.display_rules(tout);
);
apply_default_transformation(m_ctx);
if (m_ctx.get_params().slice()) {
datalog::rule_transformer transformer(m_ctx);
datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx);
transformer.register_plugin(slice);
m_ctx.transform_rules(transformer);
// track sliced predicates.
obj_map<func_decl, func_decl*> const& preds = slice->get_predicates();
obj_map<func_decl, func_decl*>::iterator it = preds.begin();
obj_map<func_decl, func_decl*>::iterator end = preds.end();
for (; it != end; ++it) {
m_pred2slice.insert(it->m_key, it->m_value);
m_refs.push_back(it->m_key);
m_refs.push_back(it->m_value);
}
}
if (m_ctx.get_params().unfold_rules() > 0) {
unsigned num_unfolds = m_ctx.get_params().unfold_rules();
datalog::rule_transformer transf1(m_ctx), transf2(m_ctx);
transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx));
transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx));
if (m_ctx.get_params().coalesce_rules()) {
m_ctx.transform_rules(transf1);
}
while (num_unfolds > 0) {
m_ctx.transform_rules(transf2);
--num_unfolds;
}
}
if (m_ctx.get_rules().get_output_predicates().empty()) {
m_context->set_unsat();
return l_false;
}
query_pred = m_ctx.get_rules().get_output_predicate();
IF_VERBOSE(2, m_ctx.display_rules(verbose_stream()););
m_pdr_rules.replace_rules(m_ctx.get_rules());
m_pdr_rules.close();
m_ctx.record_transformed_rules();
m_ctx.reopen();
m_ctx.replace_rules(old_rules);
scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode.
m_context->set_proof_converter(m_ctx.get_proof_converter());
m_context->set_model_converter(m_ctx.get_model_converter());
m_context->set_query(query_pred);
m_context->set_axioms(bg_assertion);
m_context->update_rules(m_pdr_rules);
if (m_pdr_rules.get_rules().empty()) {
m_context->set_unsat();
IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(),0););
return l_false;
}
return m_context->solve();
}
expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) {
func_decl* pred = pred_orig;
m_pred2slice.find(pred_orig, pred);
SASSERT(pred);
return m_context->get_cover_delta(level, pred_orig, pred);
}
void dl_interface::add_cover(int level, func_decl* pred, expr* property) {
if (m_ctx.get_params().slice()) {
throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers");
}
m_context->add_cover(level, pred, property);
}
unsigned dl_interface::get_num_levels(func_decl* pred) {
m_pred2slice.find(pred, pred);
SASSERT(pred);
return m_context->get_num_levels(pred);
}
void dl_interface::collect_statistics(statistics& st) const {
m_context->collect_statistics(st);
}
void dl_interface::reset_statistics() {
m_context->reset_statistics();
}
void dl_interface::display_certificate(std::ostream& out) const {
m_context->display_certificate(out);
}
expr_ref dl_interface::get_answer() {
return m_context->get_answer();
}
void dl_interface::cancel() {
m_context->cancel();
}
void dl_interface::cleanup() {
m_context->cleanup();
}
void dl_interface::updt_params() {
dealloc(m_context);
m_context = alloc(pdr::context, m_ctx.get_fparams(), m_ctx.get_params(), m_ctx.get_manager());
}
model_ref dl_interface::get_model() {
return m_context->get_model();
}
proof_ref dl_interface::get_proof() {
return m_context->get_proof();
}

View file

@ -0,0 +1,82 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_dl_interface.h
Abstract:
SMT2 interface for the datalog PDR
Author:
Krystof Hoder (t-khoder) 2011-9-22.
Revision History:
--*/
#ifndef _PDR_DL_INTERFACE_H_
#define _PDR_DL_INTERFACE_H_
#include "lbool.h"
#include "dl_rule.h"
#include "dl_rule_set.h"
#include "dl_util.h"
#include "dl_engine_base.h"
#include "statistics.h"
namespace datalog {
class context;
}
namespace pdr {
class context;
class dl_interface : public datalog::engine_base {
datalog::context& m_ctx;
datalog::rule_set m_pdr_rules;
datalog::rule_set m_old_rules;
context* m_context;
obj_map<func_decl, func_decl*> m_pred2slice;
ast_ref_vector m_refs;
void check_reset();
public:
dl_interface(datalog::context& ctx);
~dl_interface();
virtual lbool query(expr* query);
virtual void cancel();
virtual void cleanup();
virtual void display_certificate(std::ostream& out) const;
virtual void collect_statistics(statistics& st) const;
virtual void reset_statistics();
virtual expr_ref get_answer();
virtual unsigned get_num_levels(func_decl* pred);
virtual expr_ref get_cover_delta(int level, func_decl* pred);
virtual void add_cover(int level, func_decl* pred, expr* property);
virtual void updt_params();
virtual model_ref get_model();
virtual proof_ref get_proof();
};
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,128 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_farkas_learner.h
Abstract:
SMT2 interface for the datalog PDR
Author:
Krystof Hoder (t-khoder) 2011-11-1.
Revision History:
--*/
#ifndef _PDR_FARKAS_LEARNER_H_
#define _PDR_FARKAS_LEARNER_H_
#include "arith_decl_plugin.h"
#include "ast_translation.h"
#include "bv_decl_plugin.h"
#include "smt_kernel.h"
#include "bool_rewriter.h"
#include "pdr_util.h"
#include "smt_params.h"
#include "tactic.h"
namespace pdr {
class farkas_learner {
class farkas_collector;
class constant_replacer_cfg;
class equality_expander_cfg;
class constr;
typedef obj_hashtable<expr> expr_set;
smt_params m_proof_params;
ast_manager m_pr;
scoped_ptr<smt::kernel> m_ctx;
constr* m_constr;
//
// true: produce a combined constraint by applying Farkas coefficients.
// false: produce a conjunction of the negated literals from the theory lemmas.
//
bool m_combine_farkas_coefficients;
static smt_params get_proof_params(smt_params& orig_params);
//
// all ast objects passed to private functions have m_proof_mgs as their ast_manager
//
ast_translation p2o; /** Translate expression from inner ast_manager to outer one */
ast_translation o2p; /** Translate expression from outer ast_manager to inner one */
/** All ast opbjects here are in the m_proof_mgs */
void get_lemma_guesses_internal(proof * p, expr* A, expr * B, expr_ref_vector& lemmas);
bool farkas2lemma(proof * fstep, expr* A, expr * B, expr_ref& res);
void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res);
bool try_ensure_lemma_in_language(expr_ref& lemma, expr* A, const func_decl_set& lang);
bool is_farkas_lemma(ast_manager& m, expr* e);
void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable<expr>& lemma_set, expr_ref_vector& lemmas);
bool is_pure_expr(func_decl_set const& symbs, expr* e) const;
static void test();
public:
farkas_learner(smt_params& params, ast_manager& m);
~farkas_learner();
/**
All ast objects have the ast_manager which was passed as
an argument to the constructor (i.e. m_outer_mgr)
B is a conjunction of literals.
A && B is unsat, equivalently A => ~B is valid
Find a weakened B' such that
A && B' is unsat and B' uses vocabulary (and constants) in common with A.
return lemmas to weaken B.
*/
bool get_lemma_guesses(expr * A, expr * B, expr_ref_vector& lemmas);
/**
Traverse a proof and retrieve lemmas using the vocabulary from bs.
*/
void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas);
/**
Traverse a proof and retrieve consequences of A that are used to establish ~B.
The assumption is that:
A => \/ ~consequences[i] and \/ ~consequences[i] => ~B
e.g., the second implication can be rewritten as:
B => /\ consequences[i]
*/
void get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences);
/**
\brief Simplify lemmas using subsumption.
*/
void simplify_lemmas(expr_ref_vector& lemmas);
void collect_statistics(statistics& st) const;
};
}
#endif

View file

@ -0,0 +1,779 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_generalizers.cpp
Abstract:
Generalizers of satisfiable states and unsat cores.
Author:
Nikolaj Bjorner (nbjorner) 2011-11-20.
Revision History:
--*/
#include "pdr_context.h"
#include "pdr_farkas_learner.h"
#include "pdr_generalizers.h"
#include "expr_abstract.h"
#include "var_subst.h"
#include "expr_safe_replace.h"
#include "model_smt2_pp.h"
namespace pdr {
// ------------------------
// core_bool_inductive_generalizer
// main propositional induction generalizer.
// drop literals one by one from the core and check if the core is still inductive.
//
void core_bool_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
if (core.size() <= 1) {
return;
}
ast_manager& m = core.get_manager();
TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; });
unsigned num_failures = 0, i = 0, old_core_size = core.size();
ptr_vector<expr> processed;
while (i < core.size() && 1 < core.size() && (!m_failure_limit || num_failures <= m_failure_limit)) {
expr_ref lit(m);
lit = core[i].get();
core[i] = m.mk_true();
if (n.pt().check_inductive(n.level(), core, uses_level)) {
num_failures = 0;
for (i = 0; i < core.size() && processed.contains(core[i].get()); ++i);
}
else {
core[i] = lit;
processed.push_back(lit);
++num_failures;
++i;
}
}
IF_VERBOSE(2, verbose_stream() << "old size: " << old_core_size << " new size: " << core.size() << "\n";);
TRACE("pdr", tout << "old size: " << old_core_size << " new size: " << core.size() << "\n";);
}
void core_multi_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
UNREACHABLE();
}
/**
\brief Find minimal cores.
Apply a simple heuristic: find a minimal core, then find minimal cores that exclude at least one
literal from each of the literals in the minimal cores.
*/
void core_multi_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
ast_manager& m = core.get_manager();
expr_ref_vector old_core(m), core0(core);
bool uses_level1 = uses_level;
m_gen(n, core0, uses_level1);
new_cores.push_back(std::make_pair(core0, uses_level1));
obj_hashtable<expr> core_exprs, core1_exprs;
datalog::set_union(core_exprs, core0);
for (unsigned i = 0; i < old_core.size(); ++i) {
expr* lit = old_core[i].get();
if (core_exprs.contains(lit)) {
expr_ref_vector core1(old_core);
core1[i] = core1.back();
core1.pop_back();
uses_level1 = uses_level;
m_gen(n, core1, uses_level1);
SASSERT(core1.size() <= old_core.size());
if (core1.size() < old_core.size()) {
new_cores.push_back(std::make_pair(core1, uses_level1));
core1_exprs.reset();
datalog::set_union(core1_exprs, core1);
datalog::set_intersection(core_exprs, core1_exprs);
}
}
}
}
// ------------------------
// core_farkas_generalizer
//
// for each disjunct of core:
// weaken predecessor.
//
core_farkas_generalizer::core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p):
core_generalizer(ctx),
m_farkas_learner(p, m)
{}
void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
ast_manager& m = n.pt().get_manager();
if (core.empty()) return;
expr_ref A(m), B(qe::mk_and(core)), C(m);
expr_ref_vector Bs(m);
qe::flatten_or(B, Bs);
A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level());
bool change = false;
for (unsigned i = 0; i < Bs.size(); ++i) {
expr_ref_vector lemmas(m);
C = Bs[i].get();
if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) {
TRACE("pdr",
tout << "Old core:\n" << mk_pp(B, m) << "\n";
tout << "New core:\n" << mk_pp(qe::mk_and(lemmas), m) << "\n";);
Bs[i] = qe::mk_and(lemmas);
change = true;
}
}
if (change) {
C = qe::mk_or(Bs);
TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";);
core.reset();
qe::flatten_and(C, core);
uses_level = true;
}
}
void core_farkas_generalizer::collect_statistics(statistics& st) const {
m_farkas_learner.collect_statistics(st);
}
core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure):
core_generalizer(ctx),
m(ctx.get_manager()),
m_is_closure(is_closure) {
}
void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
// method3(n, core, uses_level, new_cores);
method1(n, core, uses_level, new_cores);
}
void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
UNREACHABLE();
}
// use the entire region as starting point for generalization.
//
// Constraints:
// add_variables: y = y1 + y2
// core: Ay <= b -> conv1: A*y1 <= b*sigma1
// sigma1 > 0
// sigma2 > 0
// 1 = sigma1 + sigma2
// A'y <= b' -> conv2: A'*y2 <= b'*sigma2
//
// If Constraints & Transition(y0, y) is unsat, then
// update with new core.
//
void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
expr_ref_vector conv2(m), fmls(m), fml1_2(m);
bool change = false;
if (core.empty()) {
new_cores.push_back(std::make_pair(core, uses_level));
return;
}
closure cl(n.pt(), m_is_closure);
expr_ref fml1 = qe::mk_and(core);
expr_ref fml2 = n.pt().get_formulas(n.level(), false);
fml1_2.push_back(fml1);
fml1_2.push_back(0);
qe::flatten_and(fml2, fmls);
for (unsigned i = 0; i < fmls.size(); ++i) {
fml2 = m.mk_not(fmls[i].get());
fml1_2[1] = fml2;
expr_ref state = cl(fml1_2);
TRACE("pdr",
tout << "Check states:\n" << mk_pp(state, m) << "\n";
tout << "Old states:\n" << mk_pp(fml2, m) << "\n";
);
model_node nd(0, state, n.pt(), n.level());
pred_transformer::scoped_farkas sf(n.pt(), true);
bool uses_level1 = uses_level;
if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) {
new_cores.push_back(std::make_pair(conv2, uses_level1));
change = true;
expr_ref state1 = qe::mk_and(conv2);
TRACE("pdr",
tout << mk_pp(state, m) << "\n";
tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";);
IF_VERBOSE(0,
verbose_stream() << mk_pp(state, m) << "\n";
verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";);
}
}
if (!m_is_closure || !change) {
new_cores.push_back(std::make_pair(core, uses_level));
}
}
/*
Extract the lemmas from the transition relation that were used to establish unsatisfiability.
Take convex closures of conbinations of these lemmas.
*/
void core_convex_hull_generalizer::method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
TRACE("dl", tout << "method: generalize consequences of F(R)\n";
for (unsigned i = 0; i < core.size(); ++i) {
tout << "B:" << mk_pp(core[i], m) << "\n";
});
bool uses_level1;
expr_ref_vector core1(m);
core1.append(core);
expr_ref_vector consequences(m);
{
n.pt().get_solver().set_consequences(&consequences);
pred_transformer::scoped_farkas sf (n.pt(), true);
VERIFY(l_false == n.pt().is_reachable(n, &core1, uses_level1));
n.pt().get_solver().set_consequences(0);
}
IF_VERBOSE(0,
verbose_stream() << "Consequences: " << consequences.size() << "\n";
for (unsigned i = 0; i < consequences.size(); ++i) {
verbose_stream() << mk_pp(consequences[i].get(), m) << "\n";
}
verbose_stream() << "core: " << core1.size() << "\n";
for (unsigned i = 0; i < core1.size(); ++i) {
verbose_stream() << mk_pp(core1[i].get(), m) << "\n";
});
expr_ref tmp(m);
// Check that F(R) => \/ consequences
{
expr_ref_vector cstate(m);
for (unsigned i = 0; i < consequences.size(); ++i) {
cstate.push_back(m.mk_not(consequences[i].get()));
}
tmp = m.mk_and(cstate.size(), cstate.c_ptr());
model_node nd(0, tmp, n.pt(), n.level());
pred_transformer::scoped_farkas sf (n.pt(), false);
VERIFY(l_false == n.pt().is_reachable(nd, &core1, uses_level1));
}
// Create disjunction.
tmp = m.mk_and(core.size(), core.c_ptr());
// Check that \/ consequences => not (core)
if (!is_unsat(consequences, tmp)) {
IF_VERBOSE(0, verbose_stream() << "Consequences don't contradict the core\n";);
return;
}
IF_VERBOSE(0, verbose_stream() << "Consequences contradict core\n";);
if (!strengthen_consequences(n, consequences, tmp)) {
return;
}
IF_VERBOSE(0, verbose_stream() << "consequences strengthened\n";);
// Use the resulting formula to find Farkas lemmas from core.
}
bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) {
expr_ref A(m), tmp(m), convA(m);
unsigned sz = As.size();
closure cl(n.pt(), m_is_closure);
for (unsigned i = 0; i < As.size(); ++i) {
expr_ref_vector Hs(m);
Hs.push_back(As[i].get());
for (unsigned j = i + 1; j < As.size(); ++j) {
Hs.push_back(As[j].get());
bool unsat = false;
A = cl(Hs);
tmp = As[i].get();
As[i] = A;
unsat = is_unsat(As, B);
As[i] = tmp;
if (unsat) {
IF_VERBOSE(0, verbose_stream() << "New convex: " << mk_pp(convA, m) << "\n";);
convA = A;
As[j] = As.back();
As.pop_back();
--j;
}
else {
Hs.pop_back();
}
}
if (Hs.size() > 1) {
As[i] = convA;
}
}
return sz > As.size();
}
bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) {
smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p);
expr_ref disj(m);
disj = m.mk_or(As.size(), As.c_ptr());
ctx.assert_expr(disj);
ctx.assert_expr(B);
std::cout << "Checking\n" << mk_pp(disj, m) << "\n" << mk_pp(B, m) << "\n";
return l_false == ctx.check();
}
// ---------------------------------
// core_arith_inductive_generalizer
// NB. this is trying out some ideas for generalization in
// an ad hoc specialized way. arith_inductive_generalizer should
// not be used by default. It is a place-holder for a general purpose
// extrapolator of a lattice basis.
core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx):
core_generalizer(ctx),
m(ctx.get_manager()),
a(m),
m_refs(m) {}
void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
if (core.size() <= 1) {
return;
}
reset();
expr_ref e(m), t1(m), t2(m), t3(m);
rational r;
TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; });
svector<eq> eqs;
get_eqs(core, eqs);
if (eqs.empty()) {
return;
}
expr_ref_vector new_core(m);
new_core.append(core);
for (unsigned eq = 0; eq < eqs.size(); ++eq) {
rational r = eqs[eq].m_value;
expr* x = eqs[eq].m_term;
unsigned k = eqs[eq].m_i;
unsigned l = eqs[eq].m_j;
new_core[l] = m.mk_true();
new_core[k] = m.mk_true();
for (unsigned i = 0; i < new_core.size(); ++i) {
if (substitute_alias(r, x, new_core[i].get(), e)) {
new_core[i] = e;
}
}
if (abs(r) >= rational(2) && a.is_int(x)) {
new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(rational(2), true)), a.mk_numeral(rational(0), true));
new_core[l] = a.mk_le(x, a.mk_numeral(rational(0), true));
}
}
bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level);
IF_VERBOSE(1,
verbose_stream() << (inductive?"":"non") << "inductive\n";
verbose_stream() << "old\n";
for (unsigned j = 0; j < core.size(); ++j) {
verbose_stream() << mk_pp(core[j].get(), m) << "\n";
}
verbose_stream() << "new\n";
for (unsigned j = 0; j < new_core.size(); ++j) {
verbose_stream() << mk_pp(new_core[j].get(), m) << "\n";
});
if (inductive) {
core.reset();
core.append(new_core);
}
}
void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) {
if (r.is_neg()) {
expr_ref e(m);
e = a.mk_uminus(x);
m_refs.push_back(e);
x = e;
is_lower = !is_lower;
}
vector<term_loc_t> bound;
bound.push_back(std::make_pair(x, i));
if (is_lower) {
m_lb.insert(abs(r), bound);
}
else {
m_ub.insert(abs(r), bound);
}
}
void core_arith_inductive_generalizer::reset() {
m_refs.reset();
m_lb.reset();
m_ub.reset();
}
void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector<eq>& eqs) {
expr* e1, *x, *y;
expr_ref e(m);
rational r;
for (unsigned i = 0; i < core.size(); ++i) {
e = core[i];
if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
// not (<= x r) <=> x >= r + 1
insert_bound(true, x, r + rational(1), i);
}
else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
// not (>= x r) <=> x <= r - 1
insert_bound(false, x, r - rational(1), i);
}
else if (a.is_le(e, x, y) && a.is_numeral(y, r)) {
insert_bound(false, x, r, i);
}
else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) {
insert_bound(true, x, r, i);
}
}
bounds_t::iterator it = m_lb.begin(), end = m_lb.end();
for (; it != end; ++it) {
rational r = it->m_key;
vector<term_loc_t> & terms1 = it->m_value;
vector<term_loc_t> terms2;
if (r >= rational(2) && m_ub.find(r, terms2)) {
for (unsigned i = 0; i < terms1.size(); ++i) {
bool done = false;
for (unsigned j = 0; !done && j < terms2.size(); ++j) {
expr* t1 = terms1[i].first;
expr* t2 = terms2[j].first;
if (t1 == t2) {
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
done = true;
}
else {
e = m.mk_eq(t1, t2);
th_rewriter rw(m);
rw(e);
if (m.is_true(e)) {
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
done = true;
}
}
}
}
}
}
}
bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) {
rational r2;
expr* y, *z, *e1;
if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) {
result = m.mk_not(result);
return true;
}
if (a.is_le(e, y, z) && a.is_numeral(z, r2)) {
if (r == r2) {
result = a.mk_le(y, x);
return true;
}
if (r == r2 + rational(1)) {
result = a.mk_lt(y, x);
return true;
}
if (r == r2 - rational(1)) {
result = a.mk_le(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x))));
return true;
}
}
if (a.is_ge(e, y, z) && a.is_numeral(z, r2)) {
if (r == r2) {
result = a.mk_ge(y, x);
return true;
}
if (r2 == r + rational(1)) {
result = a.mk_gt(y, x);
return true;
}
if (r2 == r - rational(1)) {
result = a.mk_ge(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x))));
return true;
}
}
return false;
}
//
// < F, phi, i + 1>
// |
// < G, psi, i >
//
// where:
//
// p(x) <- F(x,y,p,q)
// q(x) <- G(x,y)
//
// Hyp:
// Q_k(x) => phi(x) j <= k <= i
// Q_k(x) => R_k(x) j <= k <= i + 1
// Q_k(x) <=> Trans(Q_{k-1}) j < k <= i + 1
// Conclusion:
// Q_{i+1}(x) => phi(x)
//
class core_induction_generalizer::imp {
context& m_ctx;
manager& pm;
ast_manager& m;
//
// Create predicate Q_level
//
func_decl_ref mk_pred(unsigned level, func_decl* f) {
func_decl_ref result(m);
std::ostringstream name;
name << f->get_name() << "_" << level;
symbol sname(name.str().c_str());
result = m.mk_func_decl(sname, f->get_arity(), f->get_domain(), f->get_range());
return result;
}
//
// Create formula exists y . z . F[Q_{level-1}, x, y, z]
//
expr_ref mk_transition_rule(
expr_ref_vector const& reps,
unsigned level,
datalog::rule const& rule)
{
expr_ref_vector conj(m), sub(m);
expr_ref result(m);
ptr_vector<sort> sorts;
svector<symbol> names;
unsigned ut_size = rule.get_uninterpreted_tail_size();
unsigned t_size = rule.get_tail_size();
if (0 == level && 0 < ut_size) {
result = m.mk_false();
return result;
}
app* atom = rule.get_head();
SASSERT(atom->get_num_args() == reps.size());
for (unsigned i = 0; i < reps.size(); ++i) {
expr* arg = atom->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (idx >= sub.size()) sub.resize(idx+1);
if (sub[idx].get()) {
conj.push_back(m.mk_eq(sub[idx].get(), reps[i]));
}
else {
sub[idx] = reps[i];
}
}
else {
conj.push_back(m.mk_eq(arg, reps[i]));
}
}
for (unsigned i = 0; 0 < level && i < ut_size; i++) {
app* atom = rule.get_tail(i);
func_decl* head = atom->get_decl();
func_decl_ref fn = mk_pred(level-1, head);
conj.push_back(m.mk_app(fn, atom->get_num_args(), atom->get_args()));
}
for (unsigned i = ut_size; i < t_size; i++) {
conj.push_back(rule.get_tail(i));
}
result = qe::mk_and(conj);
if (!sub.empty()) {
expr_ref tmp = result;
var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result);
}
get_free_vars(result, sorts);
for (unsigned i = 0; i < sorts.size(); ++i) {
if (!sorts[i]) {
sorts[i] = m.mk_bool_sort();
}
names.push_back(symbol(sorts.size() - i - 1));
}
if (!sorts.empty()) {
sorts.reverse();
result = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), result);
}
return result;
}
expr_ref bind_head(expr_ref_vector const& reps, expr* fml) {
expr_ref result(m);
expr_abstract(m, 0, reps.size(), reps.c_ptr(), fml, result);
ptr_vector<sort> sorts;
svector<symbol> names;
unsigned sz = reps.size();
for (unsigned i = 0; i < sz; ++i) {
sorts.push_back(m.get_sort(reps[sz-i-1]));
names.push_back(symbol(sz-i-1));
}
if (sz > 0) {
result = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), result);
}
return result;
}
expr_ref_vector mk_reps(pred_transformer& pt) {
expr_ref_vector reps(m);
expr_ref rep(m);
for (unsigned i = 0; i < pt.head()->get_arity(); ++i) {
rep = m.mk_const(pm.o2n(pt.sig(i), 0));
reps.push_back(rep);
}
return reps;
}
//
// extract transition axiom:
//
// forall x . p_lvl(x) <=> exists y z . F[p_{lvl-1}(y), q_{lvl-1}(z), x]
//
expr_ref mk_transition_axiom(pred_transformer& pt, unsigned level) {
expr_ref fml(m.mk_false(), m), tr(m);
expr_ref_vector reps = mk_reps(pt);
ptr_vector<datalog::rule> const& rules = pt.rules();
for (unsigned i = 0; i < rules.size(); ++i) {
tr = mk_transition_rule(reps, level, *rules[i]);
fml = (i == 0)?tr.get():m.mk_or(fml, tr);
}
func_decl_ref fn = mk_pred(level, pt.head());
fml = m.mk_iff(m.mk_app(fn, reps.size(), reps.c_ptr()), fml);
fml = bind_head(reps, fml);
return fml;
}
//
// Create implication:
// Q_level(x) => phi(x)
//
expr_ref mk_predicate_property(unsigned level, pred_transformer& pt, expr* phi) {
expr_ref_vector reps = mk_reps(pt);
func_decl_ref fn = mk_pred(level, pt.head());
expr_ref fml(m);
fml = m.mk_implies(m.mk_app(fn, reps.size(), reps.c_ptr()), phi);
fml = bind_head(reps, fml);
return fml;
}
public:
imp(context& ctx): m_ctx(ctx), pm(ctx.get_pdr_manager()), m(ctx.get_manager()) {}
//
// not exists y . F(x,y)
//
expr_ref mk_blocked_transition(pred_transformer& pt, unsigned level) {
SASSERT(level > 0);
expr_ref fml(m.mk_true(), m);
expr_ref_vector reps = mk_reps(pt), fmls(m);
ptr_vector<datalog::rule> const& rules = pt.rules();
for (unsigned i = 0; i < rules.size(); ++i) {
fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i])));
}
fml = qe::mk_and(fmls);
TRACE("pdr", tout << mk_pp(fml, m) << "\n";);
return fml;
}
expr_ref mk_induction_goal(pred_transformer& pt, unsigned level, unsigned depth) {
SASSERT(level >= depth);
expr_ref_vector conjs(m);
ptr_vector<pred_transformer> pts;
unsigned_vector levels;
// negated goal
expr_ref phi = mk_blocked_transition(pt, level);
conjs.push_back(m.mk_not(mk_predicate_property(level, pt, phi)));
pts.push_back(&pt);
levels.push_back(level);
// Add I.H.
for (unsigned lvl = level-depth; lvl < level; ++lvl) {
if (lvl > 0) {
expr_ref psi = mk_blocked_transition(pt, lvl);
conjs.push_back(mk_predicate_property(lvl, pt, psi));
pts.push_back(&pt);
levels.push_back(lvl);
}
}
// Transitions:
for (unsigned qhead = 0; qhead < pts.size(); ++qhead) {
pred_transformer& qt = *pts[qhead];
unsigned lvl = levels[qhead];
// Add transition definition and properties at level.
conjs.push_back(mk_transition_axiom(qt, lvl));
conjs.push_back(mk_predicate_property(lvl, qt, qt.get_formulas(lvl, true)));
// Enqueue additional hypotheses
ptr_vector<datalog::rule> const& rules = qt.rules();
if (lvl + depth < level || lvl == 0) {
continue;
}
for (unsigned i = 0; i < rules.size(); ++i) {
datalog::rule& r = *rules[i];
unsigned ut_size = r.get_uninterpreted_tail_size();
for (unsigned j = 0; j < ut_size; ++j) {
func_decl* f = r.get_tail(j)->get_decl();
pred_transformer* rt = m_ctx.get_pred_transformers().find(f);
bool found = false;
for (unsigned k = 0; !found && k < levels.size(); ++k) {
found = (rt == pts[k] && levels[k] + 1 == lvl);
}
if (!found) {
levels.push_back(lvl-1);
pts.push_back(rt);
}
}
}
}
expr_ref result = qe::mk_and(conjs);
TRACE("pdr", tout << mk_pp(result, m) << "\n";);
return result;
}
};
//
// Instantiate Peano induction schema.
//
void core_induction_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
model_node* p = n.parent();
if (p == 0) {
return;
}
unsigned depth = 2;
imp imp(m_ctx);
ast_manager& m = core.get_manager();
expr_ref goal = imp.mk_induction_goal(p->pt(), p->level(), depth);
smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p);
ctx.assert_expr(goal);
lbool r = ctx.check();
TRACE("pdr", tout << r << "\n";
for (unsigned i = 0; i < core.size(); ++i) {
tout << mk_pp(core[i].get(), m) << "\n";
});
if (r == l_false) {
core.reset();
expr_ref phi = imp.mk_blocked_transition(p->pt(), p->level());
core.push_back(m.mk_not(phi));
uses_level = true;
}
}
};

View file

@ -0,0 +1,110 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_generalizers.h
Abstract:
Generalizer plugins.
Author:
Nikolaj Bjorner (nbjorner) 2011-11-22.
Revision History:
--*/
#ifndef _PDR_GENERALIZERS_H_
#define _PDR_GENERALIZERS_H_
#include "pdr_context.h"
#include "pdr_closure.h"
#include "arith_decl_plugin.h"
namespace pdr {
class core_bool_inductive_generalizer : public core_generalizer {
unsigned m_failure_limit;
public:
core_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : core_generalizer(ctx), m_failure_limit(failure_limit) {}
virtual ~core_bool_inductive_generalizer() {}
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
};
template <typename T>
class r_map : public map<rational, T, rational::hash_proc, rational::eq_proc> {
};
class core_arith_inductive_generalizer : public core_generalizer {
typedef std::pair<expr*, unsigned> term_loc_t;
typedef r_map<vector<term_loc_t> > bounds_t;
ast_manager& m;
arith_util a;
expr_ref_vector m_refs;
bounds_t m_lb;
bounds_t m_ub;
struct eq {
expr* m_term;
rational m_value;
unsigned m_i;
unsigned m_j;
eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {}
};
void reset();
void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i);
void get_eqs(expr_ref_vector const& core, svector<eq>& eqs);
bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result);
public:
core_arith_inductive_generalizer(context& ctx);
virtual ~core_arith_inductive_generalizer() {}
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
};
class core_farkas_generalizer : public core_generalizer {
farkas_learner m_farkas_learner;
public:
core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p);
virtual ~core_farkas_generalizer() {}
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
virtual void collect_statistics(statistics& st) const;
};
class core_convex_hull_generalizer : public core_generalizer {
ast_manager& m;
obj_map<expr, expr*> m_models;
bool m_is_closure;
void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B);
bool is_unsat(expr_ref_vector const& As, expr* B);
public:
core_convex_hull_generalizer(context& ctx, bool is_closure);
virtual ~core_convex_hull_generalizer() {}
virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
};
class core_multi_generalizer : public core_generalizer {
core_bool_inductive_generalizer m_gen;
public:
core_multi_generalizer(context& ctx, unsigned max_failures): core_generalizer(ctx), m_gen(ctx, max_failures) {}
virtual ~core_multi_generalizer() {}
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
};
class core_induction_generalizer : public core_generalizer {
class imp;
public:
core_induction_generalizer(context& ctx): core_generalizer(ctx) {}
virtual ~core_induction_generalizer() {}
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level);
};
};
#endif

327
src/muz/pdr/pdr_manager.cpp Normal file
View file

@ -0,0 +1,327 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_manager.cpp
Abstract:
A manager class for PDR, taking care of creating of AST
objects and conversions between them.
Author:
Krystof Hoder (t-khoder) 2011-8-25.
Revision History:
--*/
#include <sstream>
#include "pdr_manager.h"
#include "ast_smt2_pp.h"
#include "for_each_expr.h"
#include "has_free_vars.h"
#include "expr_replacer.h"
#include "expr_abstract.h"
#include "model2expr.h"
#include "model_smt2_pp.h"
#include "model_converter.h"
namespace pdr {
class collect_decls_proc {
func_decl_set& m_bound_decls;
func_decl_set& m_aux_decls;
public:
collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls):
m_bound_decls(bound_decls),
m_aux_decls(aux_decls) {
}
void operator()(app* a) {
if (a->get_family_id() == null_family_id) {
func_decl* f = a->get_decl();
if (!m_bound_decls.contains(f)) {
m_aux_decls.insert(f);
}
}
}
void operator()(var* v) {}
void operator()(quantifier* q) {}
};
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
expr_ref inductive_property::fixup_clause(expr* fml) const {
expr_ref_vector disjs(m);
qe::flatten_or(fml, disjs);
expr_ref result(m);
bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result);
return result;
}
expr_ref inductive_property::fixup_clauses(expr* fml) const {
expr_ref_vector conjs(m);
expr_ref result(m);
qe::flatten_and(fml, conjs);
for (unsigned i = 0; i < conjs.size(); ++i) {
conjs[i] = fixup_clause(conjs[i].get());
}
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result);
return result;
}
std::string inductive_property::to_string() const {
std::stringstream stm;
model_ref md;
expr_ref result(m);
to_model(md);
model_smt2_pp(stm, m, *md.get(), 0);
return stm.str();
}
void inductive_property::to_model(model_ref& md) const {
md = alloc(model, m);
vector<relation_info> const& rs = m_relation_info;
expr_ref_vector conjs(m);
for (unsigned i = 0; i < rs.size(); ++i) {
relation_info ri(rs[i]);
func_decl * pred = ri.m_pred;
expr_ref prop = fixup_clauses(ri.m_body);
func_decl_ref_vector const& sig = ri.m_vars;
expr_ref q(m);
expr_ref_vector sig_vars(m);
for (unsigned j = 0; j < sig.size(); ++j) {
sig_vars.push_back(m.mk_const(sig[sig.size()-j-1]));
}
expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q);
if (sig.empty()) {
md->register_decl(pred, q);
}
else {
func_interp* fi = alloc(func_interp, m, sig.size());
fi->set_else(q);
md->register_decl(pred, fi);
}
}
TRACE("pdr", model_smt2_pp(tout, m, *md, 0););
apply(const_cast<model_converter_ref&>(m_mc), md, 0);
}
expr_ref inductive_property::to_expr() const {
model_ref md;
expr_ref result(m);
to_model(md);
model2expr(md, result);
return result;
}
void inductive_property::display(ptr_vector<datalog::rule> const& rules, std::ostream& out) const {
func_decl_set bound_decls, aux_decls;
collect_decls_proc collect_decls(bound_decls, aux_decls);
for (unsigned i = 0; i < m_relation_info.size(); ++i) {
bound_decls.insert(m_relation_info[i].m_pred);
func_decl_ref_vector const& sig = m_relation_info[i].m_vars;
for (unsigned j = 0; j < sig.size(); ++j) {
bound_decls.insert(sig[j]);
}
for_each_expr(collect_decls, m_relation_info[i].m_body);
}
for (unsigned i = 0; i < rules.size(); ++i) {
bound_decls.insert(rules[i]->get_decl());
}
for (unsigned i = 0; i < rules.size(); ++i) {
unsigned u_sz = rules[i]->get_uninterpreted_tail_size();
unsigned t_sz = rules[i]->get_tail_size();
for (unsigned j = u_sz; j < t_sz; ++j) {
for_each_expr(collect_decls, rules[i]->get_tail(j));
}
}
smt2_pp_environment_dbg env(m);
func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end();
for (; it != end; ++it) {
func_decl* f = *it;
ast_smt2_pp(out, f, env);
out << "\n";
}
out << to_string() << "\n";
for (unsigned i = 0; i < rules.size(); ++i) {
out << "(push)\n";
out << "(assert (not\n";
rules[i]->display_smt2(m, out);
out << "))\n";
out << "(check-sat)\n";
out << "(pop)\n";
}
}
vector<std::string> manager::get_state_suffixes() {
vector<std::string> res;
res.push_back("_n");
return res;
}
manager::manager(smt_params& fparams, unsigned max_num_contexts, ast_manager& manager) :
m(manager),
m_fparams(fparams),
m_brwr(m),
m_mux(m, get_state_suffixes()),
m_background(m.mk_true(), m),
m_contexts(fparams, max_num_contexts, m),
m_next_unique_num(0)
{
}
void manager::add_new_state(func_decl * s) {
SASSERT(s->get_arity()==0); //we currently don't support non-constant states
decl_vector vect;
SASSERT(o_index(0)==1); //we assume this in the number of retrieved symbols
m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect);
m_o0_preds.push_back(vect[o_index(0)]);
}
func_decl * manager::get_o_pred(func_decl* s, unsigned idx)
{
func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx));
if(res) { return res; }
add_new_state(s);
res = m_mux.try_get_by_prefix(s, o_index(idx));
SASSERT(res);
return res;
}
func_decl * manager::get_n_pred(func_decl* s)
{
func_decl * res = m_mux.try_get_by_prefix(s, n_index());
if(res) { return res; }
add_new_state(s);
res = m_mux.try_get_by_prefix(s, n_index());
SASSERT(res);
return res;
}
void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) {
m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res);
}
void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) {
m_brwr.mk_and(core.size(), core.c_ptr(), res);
}
void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) {
m_brwr.mk_not(cube, res);
}
void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) {
m_brwr.mk_not(lemma, res);
}
expr_ref manager::mk_and(unsigned sz, expr* const* exprs) {
expr_ref result(m);
m_brwr.mk_and(sz, exprs, result);
return result;
}
expr_ref manager::mk_or(unsigned sz, expr* const* exprs) {
expr_ref result(m);
m_brwr.mk_or(sz, exprs, result);
return result;
}
expr_ref manager::mk_not_and(expr_ref_vector const& conjs) {
expr_ref result(m), e(m);
expr_ref_vector es(conjs);
qe::flatten_and(es);
for (unsigned i = 0; i < es.size(); ++i) {
m_brwr.mk_not(es[i].get(), e);
es[i] = e;
}
m_brwr.mk_or(es.size(), es.c_ptr(), result);
return result;
}
void manager::get_or(expr* e, expr_ref_vector& result) {
result.push_back(e);
for (unsigned i = 0; i < result.size(); ) {
e = result[i].get();
if (m.is_or(e)) {
result.append(to_app(e)->get_num_args(), to_app(e)->get_args());
result[i] = result.back();
result.pop_back();
}
else {
++i;
}
}
}
bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value)
{
if(!is_app(atom0)) {
return false;
}
app * atom = to_app(atom0);
expr * arg1;
expr * arg2;
app * candidate_state;
app_ref candidate_value(m);
if(m.is_not(atom, arg1)) {
if(!is_app(arg1)) {
return false;
}
candidate_state = to_app(arg1);
candidate_value = m.mk_false();
}
else if(m.is_eq(atom, arg1, arg2)) {
if(!is_app(arg1) || !is_app(arg2)) {
return false;
}
if(!m_mux.is_muxed(to_app(arg1)->get_decl())) {
std::swap(arg1, arg2);
}
candidate_state = to_app(arg1);
candidate_value = to_app(arg2);
}
else {
candidate_state = atom;
candidate_value = m.mk_true();
}
if(!m_mux.is_muxed(candidate_state->get_decl())) {
return false;
}
state = candidate_state;
value = candidate_value;
return true;
}
bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) {
app_ref dummy_value_holder(m);
app * s;
if(try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) {
state = s->get_decl();
return true;
}
else {
return false;
}
}
bool manager::implication_surely_holds(expr * lhs, expr * rhs, expr * bg) {
smt::kernel sctx(m, get_fparams());
if(bg) {
sctx.assert_expr(bg);
}
sctx.assert_expr(lhs);
expr_ref neg_rhs(m.mk_not(rhs),m);
sctx.assert_expr(neg_rhs);
lbool smt_res = sctx.check();
return smt_res==l_false;
}
};

306
src/muz/pdr/pdr_manager.h Normal file
View file

@ -0,0 +1,306 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_manager.h
Abstract:
A manager class for PDR, taking care of creating of AST
objects and conversions between them.
Author:
Krystof Hoder (t-khoder) 2011-8-25.
Revision History:
--*/
#ifndef _PDR_MANAGER_H_
#define _PDR_MANAGER_H_
#include <utility>
#include <map>
#include "bool_rewriter.h"
#include "expr_replacer.h"
#include "expr_substitution.h"
#include "map.h"
#include "ref_vector.h"
#include "smt_kernel.h"
#include "pdr_util.h"
#include "pdr_sym_mux.h"
#include "pdr_farkas_learner.h"
#include "pdr_smt_context_manager.h"
#include "dl_rule.h"
namespace smt {
class context;
}
namespace pdr {
struct relation_info {
func_decl_ref m_pred;
func_decl_ref_vector m_vars;
expr_ref m_body;
relation_info(ast_manager& m, func_decl* pred, ptr_vector<func_decl> const& vars, expr* b):
m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {}
relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {}
};
class unknown_exception {};
class inductive_property {
ast_manager& m;
model_converter_ref m_mc;
vector<relation_info> m_relation_info;
expr_ref fixup_clauses(expr* property) const;
expr_ref fixup_clause(expr* clause) const;
public:
inductive_property(ast_manager& m, model_converter_ref& mc, vector<relation_info> const& relations):
m(m),
m_mc(mc),
m_relation_info(relations) {}
std::string to_string() const;
expr_ref to_expr() const;
void to_model(model_ref& md) const;
void display(ptr_vector<datalog::rule> const& rules, std::ostream& out) const;
};
class manager
{
ast_manager& m;
smt_params& m_fparams;
mutable bool_rewriter m_brwr;
sym_mux m_mux;
expr_ref m_background;
decl_vector m_o0_preds;
pdr::smt_context_manager m_contexts;
/** whenever we need an unique number, we get this one and increase */
unsigned m_next_unique_num;
static vector<std::string> get_state_suffixes();
unsigned n_index() const { return 0; }
unsigned o_index(unsigned i) const { return i+1; }
void add_new_state(func_decl * s);
public:
manager(smt_params& fparams, unsigned max_num_contexts, ast_manager & manager);
ast_manager& get_manager() const { return m; }
smt_params& get_fparams() const { return m_fparams; }
bool_rewriter& get_brwr() const { return m_brwr; }
expr_ref mk_and(unsigned sz, expr* const* exprs);
expr_ref mk_and(expr_ref_vector const& exprs) {
return mk_and(exprs.size(), exprs.c_ptr());
}
expr_ref mk_and(expr* a, expr* b) {
expr* args[2] = { a, b };
return mk_and(2, args);
}
expr_ref mk_or(unsigned sz, expr* const* exprs);
expr_ref mk_or(expr_ref_vector const& exprs) {
return mk_or(exprs.size(), exprs.c_ptr());
}
expr_ref mk_not_and(expr_ref_vector const& exprs);
void get_or(expr* e, expr_ref_vector& result);
//"o" predicates stand for the old states and "n" for the new states
func_decl * get_o_pred(func_decl * s, unsigned idx);
func_decl * get_n_pred(func_decl * s);
/**
Marks symbol as non-model which means it will not appear in models collected by
get_state_cube_from_model function.
This is to take care of auxiliary symbols introduced by the disjunction relations
to relativize lemmas coming from disjuncts.
*/
void mark_as_non_model(func_decl * p) {
m_mux.mark_as_non_model(p);
}
func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); }
func_decl * const * end_o0_preds() const { return m_o0_preds.end(); }
bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); }
func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); }
bool is_o(func_decl * p, unsigned idx) const {
return m_mux.has_index(p, o_index(idx));
}
bool is_o(expr* e, unsigned idx) const {
return is_app(e) && is_o(to_app(e)->get_decl(), idx);
}
bool is_o(func_decl * p) const {
unsigned idx;
return m_mux.try_get_index(p, idx) && idx!=n_index();
}
bool is_o(expr* e) const {
return is_app(e) && is_o(to_app(e)->get_decl());
}
bool is_n(func_decl * p) const {
return m_mux.has_index(p, n_index());
}
bool is_n(expr* e) const {
return is_app(e) && is_n(to_app(e)->get_decl());
}
/** true if p should not appead in models propagates into child relations */
bool is_non_model_sym(func_decl * p) const
{ return m_mux.is_non_model_sym(p); }
/** true if f doesn't contain any n predicates */
bool is_o_formula(expr * f) const {
return !m_mux.contains(f, n_index());
}
/** true if f contains only o state preds of index o_idx */
bool is_o_formula(expr * f, unsigned o_idx) const {
return m_mux.is_homogenous_formula(f, o_index(o_idx));
}
/** true if f doesn't contain any o predicates */
bool is_n_formula(expr * f) const {
return m_mux.is_homogenous_formula(f, n_index());
}
func_decl * o2n(func_decl * p, unsigned o_idx) const {
return m_mux.conv(p, o_index(o_idx), n_index());
}
func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const {
return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx));
}
func_decl * n2o(func_decl * p, unsigned o_idx) const {
return m_mux.conv(p, n_index(), o_index(o_idx));
}
void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) const
{ m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); }
void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) const
{ m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); }
void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const
{ m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); }
void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous=true) const
{ m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); }
/**
Return true if all state symbols which e contains are of one kind (either "n" or one of "o").
*/
bool is_homogenous_formula(expr * e) const {
return m_mux.is_homogenous_formula(e);
}
/**
Collect indices used in expression.
*/
void collect_indices(expr* e, unsigned_vector& indices) const {
m_mux.collect_indices(e, indices);
}
/**
Collect used variables of each index.
*/
void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const {
m_mux.collect_variables(e, vars);
}
/**
Return true iff both s1 and s2 are either "n" or "o" of the same index.
If one (or both) of them are not state symbol, return false.
*/
bool have_different_state_kinds(func_decl * s1, func_decl * s2) const {
unsigned i1, i2;
return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1!=i2;
}
/**
Increase indexes of state symbols in formula by dist.
The 'N' index becomes 'O' index with number dist-1.
*/
void formula_shift(expr * src, expr_ref & tgt, unsigned dist) const {
SASSERT(n_index()==0);
SASSERT(o_index(0)==1);
m_mux.shift_formula(src, dist, tgt);
}
void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res);
void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res);
void mk_cube_into_lemma(expr * cube, expr_ref & res);
void mk_lemma_into_cube(expr * lemma, expr_ref & res);
/**
Remove from vec all atoms that do not have an "o" state.
The order of elements in vec may change.
An assumption is that atoms having "o" state of given index
do not have "o" states of other indexes or "n" states.
*/
void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const
{ m_mux.filter_idx(vec, o_index(o_idx)); }
void filter_n_atoms(expr_ref_vector& vec) const
{ m_mux.filter_idx(vec, n_index()); }
/**
Partition literals into o_lits and others.
*/
void partition_o_atoms(expr_ref_vector const& lits,
expr_ref_vector& o_lits,
expr_ref_vector& other,
unsigned o_idx) const {
m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx));
}
void filter_out_non_model_atoms(expr_ref_vector& vec) const
{ m_mux.filter_non_model_lits(vec); }
bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value);
bool try_get_state_decl_from_atom(expr * atom, func_decl *& state);
std::string pp_model(const model_core & mdl) const
{ return m_mux.pp_model(mdl); }
void set_background(expr* b) { m_background = b; }
expr* get_background() const { return m_background; }
/**
Return true if we can show that lhs => rhs. The function can have false negatives
(i.e. when smt::context returns unknown), but no false positives.
bg is background knowledge and can be null
*/
bool implication_surely_holds(expr * lhs, expr * rhs, expr * bg=0);
unsigned get_unique_num() { return m_next_unique_num++; }
pdr::smt_context* mk_fresh() { return m_contexts.mk_fresh(); }
void collect_statistics(statistics& st) const { m_contexts.collect_statistics(st); }
void reset_statistics() { m_contexts.reset_statistics(); }
};
}
#endif

View file

@ -0,0 +1,461 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
prop_solver.cpp
Abstract:
SMT solver abstraction for PDR.
Author:
Krystof Hoder (t-khoder) 2011-8-17.
Revision History:
--*/
#include <sstream>
#include "model.h"
#include "pdr_util.h"
#include "pdr_prop_solver.h"
#include "ast_smt2_pp.h"
#include "dl_util.h"
#include "model_pp.h"
#include "smt_params.h"
#include "datatype_decl_plugin.h"
#include "bv_decl_plugin.h"
#include "pdr_farkas_learner.h"
#include "ast_smt2_pp.h"
#include "expr_replacer.h"
#include "fixedpoint_params.hpp"
//
// Auxiliary structure to introduce propositional names for assumptions that are not
// propositional. It is to work with the smt::context's restriction
// that assumptions be propositional literals.
//
namespace pdr {
class prop_solver::safe_assumptions {
prop_solver& s;
ast_manager& m;
expr_ref_vector m_atoms;
expr_ref_vector m_assumptions;
obj_map<app,expr *> m_proxies2expr;
obj_map<expr, app*> m_expr2proxies;
unsigned m_num_proxies;
app * mk_proxy(expr* literal) {
app* res;
SASSERT(!is_var(literal)); //it doesn't make sense to introduce names to variables
if (m_expr2proxies.find(literal, res)) {
return res;
}
SASSERT(s.m_proxies.size() >= m_num_proxies);
if (m_num_proxies == s.m_proxies.size()) {
std::stringstream name;
name << "pdr_proxy_" << s.m_proxies.size();
res = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort());
s.m_proxies.push_back(res);
s.m_aux_symbols.insert(res->get_decl());
}
else {
res = s.m_proxies[m_num_proxies].get();
}
++m_num_proxies;
m_expr2proxies.insert(literal, res);
m_proxies2expr.insert(res, literal);
expr_ref implies(m.mk_or(m.mk_not(res), literal), m);
s.m_ctx->assert_expr(implies);
m_assumptions.push_back(implies);
TRACE("pdr_verbose", tout << "name asserted " << mk_pp(implies, m) << "\n";);
return res;
}
void mk_safe(expr_ref_vector& conjs) {
qe::flatten_and(conjs);
expand_literals(conjs);
for (unsigned i = 0; i < conjs.size(); ++i) {
expr * lit = conjs[i].get();
expr * lit_core = lit;
m.is_not(lit, lit_core);
SASSERT(!m.is_true(lit));
if (!is_uninterp(lit_core) || to_app(lit_core)->get_num_args() != 0) {
conjs[i] = mk_proxy(lit);
}
}
m_assumptions.append(conjs);
}
expr* apply_accessor(
ptr_vector<func_decl> const& acc,
unsigned j,
func_decl* f,
expr* c) {
if (is_app(c) && to_app(c)->get_decl() == f) {
return to_app(c)->get_arg(j);
}
else {
return m.mk_app(acc[j], c);
}
}
void expand_literals(expr_ref_vector& conjs) {
arith_util arith(m);
datatype_util dt(m);
bv_util bv(m);
expr* e1, *e2, *c, *val;
rational r;
unsigned bv_size;
TRACE("pdr",
tout << "begin expand\n";
for (unsigned i = 0; i < conjs.size(); ++i) {
tout << mk_pp(conjs[i].get(), m) << "\n";
});
for (unsigned i = 0; i < conjs.size(); ++i) {
expr* e = conjs[i].get();
if (m.is_eq(e, e1, e2) && arith.is_int_real(e1)) {
conjs[i] = arith.mk_le(e1,e2);
if (i+1 == conjs.size()) {
conjs.push_back(arith.mk_ge(e1, e2));
}
else {
conjs.push_back(conjs[i+1].get());
conjs[i+1] = arith.mk_ge(e1, e2);
}
++i;
}
else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) ||
(m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){
func_decl* f = to_app(val)->get_decl();
func_decl* r = dt.get_constructor_recognizer(f);
conjs[i] = m.mk_app(r, c);
ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
for (unsigned j = 0; j < acc.size(); ++j) {
conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j)));
}
}
else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) ||
(m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) {
rational two(2);
for (unsigned j = 0; j < bv_size; ++j) {
parameter p(j);
//expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c);
expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c));
if ((r % two).is_zero()) {
e = m.mk_not(e);
}
r = div(r, two);
if (j == 0) {
conjs[i] = e;
}
else {
conjs.push_back(e);
}
}
}
}
TRACE("pdr",
tout << "end expand\n";
for (unsigned i = 0; i < conjs.size(); ++i) {
tout << mk_pp(conjs[i].get(), m) << "\n";
});
}
public:
safe_assumptions(prop_solver& s, expr_ref_vector const& assumptions):
s(s), m(s.m), m_atoms(assumptions), m_assumptions(m), m_num_proxies(0) {
mk_safe(m_atoms);
}
~safe_assumptions() {
}
expr_ref_vector const& atoms() const { return m_atoms; }
unsigned assumptions_size() const { return m_assumptions.size(); }
expr* assumptions(unsigned i) const { return m_assumptions[i]; }
void undo_proxies(expr_ref_vector& es) {
expr_ref e(m);
expr* r;
for (unsigned i = 0; i < es.size(); ++i) {
e = es[i].get();
if (is_app(e) && m_proxies2expr.find(to_app(e), r)) {
es[i] = r;
}
}
}
void elim_proxies(expr_ref_vector& es) {
expr_substitution sub(m, false, m.proofs_enabled());
proof_ref pr(m);
if (m.proofs_enabled()) {
pr = m.mk_asserted(m.mk_true());
}
obj_map<app,expr*>::iterator it = m_proxies2expr.begin(), end = m_proxies2expr.end();
for (; it != end; ++it) {
sub.insert(it->m_key, m.mk_true(), pr);
}
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
rep->set_substitution(&sub);
replace_proxies(*rep, es);
}
private:
void replace_proxies(expr_replacer& rep, expr_ref_vector& es) {
expr_ref e(m);
for (unsigned i = 0; i < es.size(); ++i) {
e = es[i].get();
rep(e);
es[i] = e;
if (m.is_true(e)) {
es[i] = es.back();
es.pop_back();
--i;
}
}
}
};
prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& name) :
m_fparams(pm.get_fparams()),
m(pm.get_manager()),
m_pm(pm),
m_name(name),
m_try_minimize_core(p.try_minimize_core()),
m_ctx(pm.mk_fresh()),
m_pos_level_atoms(m),
m_neg_level_atoms(m),
m_proxies(m),
m_core(0),
m_model(0),
m_consequences(0),
m_subset_based_core(false),
m_use_farkas(false),
m_in_level(false),
m_current_level(0)
{
m_ctx->assert_expr(m_pm.get_background());
}
void prop_solver::add_level() {
unsigned idx = level_cnt();
std::stringstream name;
name << m_name << "#level_" << idx;
func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, 0,m.mk_bool_sort());
m_aux_symbols.insert(lev_pred);
m_level_preds.push_back(lev_pred);
app_ref pos_la(m.mk_const(lev_pred), m);
app_ref neg_la(m.mk_not(pos_la.get()), m);
m_pos_level_atoms.push_back(pos_la);
m_neg_level_atoms.push_back(neg_la);
m_level_atoms_set.insert(pos_la.get());
m_level_atoms_set.insert(neg_la.get());
}
void prop_solver::ensure_level(unsigned lvl) {
while (lvl>=level_cnt()) {
add_level();
}
}
unsigned prop_solver::level_cnt() const {
return m_level_preds.size();
}
void prop_solver::push_level_atoms(unsigned level, expr_ref_vector& tgt) const {
unsigned lev_cnt = level_cnt();
for (unsigned i=0; i<lev_cnt; i++) {
bool active = i>=level;
app * lev_atom = active ? m_neg_level_atoms[i] : m_pos_level_atoms[i];
tgt.push_back(lev_atom);
}
}
void prop_solver::add_formula(expr * form) {
SASSERT(!m_in_level);
m_ctx->assert_expr(form);
IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";);
TRACE("pdr", tout << "add_formula: " << mk_pp(form, m) << "\n";);
}
void prop_solver::add_level_formula(expr * form, unsigned level) {
ensure_level(level);
app * lev_atom = m_pos_level_atoms[level].get();
app_ref lform(m.mk_or(form, lev_atom), m);
add_formula(lform.get());
}
lbool prop_solver::check_safe_assumptions(
safe_assumptions& safe,
const expr_ref_vector& atoms)
{
flet<bool> _model(m_fparams.m_model, m_model != 0);
expr_ref_vector expr_atoms(m);
expr_atoms.append(atoms.size(), atoms.c_ptr());
if (m_in_level) {
push_level_atoms(m_current_level, expr_atoms);
}
lbool result = m_ctx->check(expr_atoms);
TRACE("pdr",
tout << mk_pp(m_pm.mk_and(expr_atoms), m) << "\n";
tout << result << "\n";);
if (result == l_true && m_model) {
m_ctx->get_model(*m_model);
TRACE("pdr_verbose", model_pp(tout, **m_model); );
}
if (result == l_false) {
unsigned core_size = m_ctx->get_unsat_core_size();
m_assumes_level = false;
for (unsigned i = 0; i < core_size; ++i) {
if (m_level_atoms_set.contains(m_ctx->get_unsat_core_expr(i))) {
m_assumes_level = true;
break;
}
}
}
if (result == l_false &&
m_core &&
m.proofs_enabled() &&
m_use_farkas &&
!m_subset_based_core) {
extract_theory_core(safe);
}
else if (result == l_false && m_core) {
extract_subset_core(safe);
SASSERT(expr_atoms.size() >= m_core->size());
}
m_core = 0;
m_model = 0;
m_subset_based_core = false;
return result;
}
void prop_solver::extract_subset_core(safe_assumptions& safe) {
unsigned core_size = m_ctx->get_unsat_core_size();
m_core->reset();
for (unsigned i = 0; i < core_size; ++i) {
expr * core_expr = m_ctx->get_unsat_core_expr(i);
SASSERT(is_app(core_expr));
if (m_level_atoms_set.contains(core_expr)) {
continue;
}
if (m_ctx->is_aux_predicate(core_expr)) {
continue;
}
m_core->push_back(to_app(core_expr));
}
safe.undo_proxies(*m_core);
TRACE("pdr",
tout << "core_exprs: ";
for (unsigned i = 0; i < core_size; ++i) {
tout << mk_pp(m_ctx->get_unsat_core_expr(i), m) << " ";
}
tout << "\n";
tout << "core: " << mk_pp(m_pm.mk_and(*m_core), m) << "\n";
);
}
void prop_solver::extract_theory_core(safe_assumptions& safe) {
proof_ref pr(m);
pr = m_ctx->get_proof();
IF_VERBOSE(21, verbose_stream() << mk_ismt2_pp(pr, m) << "\n";);
farkas_learner fl(m_fparams, m);
expr_ref_vector lemmas(m);
obj_hashtable<expr> bs;
for (unsigned i = 0; i < safe.assumptions_size(); ++i) {
bs.insert(safe.assumptions(i));
}
fl.get_lemmas(pr, bs, lemmas);
safe.elim_proxies(lemmas);
fl.simplify_lemmas(lemmas); // redundant?
bool outside_of_logic =
(m_fparams.m_arith_mode == AS_DIFF_LOGIC &&
!is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) ||
(m_fparams.m_arith_mode == AS_UTVPI &&
!is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr()));
if (outside_of_logic) {
IF_VERBOSE(2,
verbose_stream() << "not diff\n";
for (unsigned i = 0; i < lemmas.size(); ++i) {
verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n";
});
extract_subset_core(safe);
}
else {
IF_VERBOSE(2,
verbose_stream() << "Lemmas\n";
for (unsigned i = 0; i < lemmas.size(); ++i) {
verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n";
});
m_core->reset();
m_core->append(lemmas);
if (m_consequences) {
fl.get_consequences(pr, bs, *m_consequences);
}
}
}
lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) {
return check_assumptions_and_formula(atoms, m.mk_true());
}
lbool prop_solver::check_conjunction_as_assumptions(expr * conj) {
expr_ref_vector asmp(m);
asmp.push_back(conj);
return check_assumptions(asmp);
}
lbool prop_solver::check_assumptions_and_formula(const expr_ref_vector & atoms, expr * form)
{
pdr::smt_context::scoped _scoped(*m_ctx);
safe_assumptions safe(*this, atoms);
m_ctx->assert_expr(form);
CTRACE("pdr", !m.is_true(form), tout << "check with formula: " << mk_pp(form, m) << "\n";);
lbool res = check_safe_assumptions(safe, safe.atoms());
//
// we don't have to undo model naming, as from the model
// we extract the values for state variables directly
//
return res;
}
void prop_solver::collect_statistics(statistics& st) const {
}
void prop_solver::reset_statistics() {
}
}

View file

@ -0,0 +1,141 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
prop_solver.h
Abstract:
SAT solver abstraction for PDR.
Author:
Krystof Hoder (t-khoder) 2011-8-17.
Revision History:
--*/
#ifndef _PROP_SOLVER_H_
#define _PROP_SOLVER_H_
#include <map>
#include <string>
#include <utility>
#include "ast.h"
#include "obj_hashtable.h"
#include "smt_kernel.h"
#include "util.h"
#include "vector.h"
#include "pdr_manager.h"
#include "pdr_smt_context_manager.h"
struct fixedpoint_params;
namespace pdr {
class prop_solver {
private:
smt_params& m_fparams;
ast_manager& m;
manager& m_pm;
symbol m_name;
bool m_try_minimize_core;
scoped_ptr<pdr::smt_context> m_ctx;
decl_vector m_level_preds;
app_ref_vector m_pos_level_atoms; // atoms used to identify level
app_ref_vector m_neg_level_atoms; //
obj_hashtable<expr> m_level_atoms_set;
app_ref_vector m_proxies; // predicates for assumptions
expr_ref_vector* m_core;
model_ref* m_model;
expr_ref_vector* m_consequences;
bool m_subset_based_core;
bool m_assumes_level;
bool m_use_farkas;
func_decl_set m_aux_symbols;
bool m_in_level;
unsigned m_current_level; // set when m_in_level
/** Add level atoms activating certain level into a vector */
void push_level_atoms(unsigned level, expr_ref_vector & tgt) const;
void ensure_level(unsigned lvl);
class safe_assumptions;
void extract_theory_core(safe_assumptions& assumptions);
void extract_subset_core(safe_assumptions& assumptions);
lbool check_safe_assumptions(
safe_assumptions& assumptions,
expr_ref_vector const& atoms);
public:
prop_solver(pdr::manager& pm, fixedpoint_params const& p, symbol const& name);
/** return true is s is a symbol introduced by prop_solver */
bool is_aux_symbol(func_decl * s) const {
return
m_aux_symbols.contains(s) ||
m_ctx->is_aux_predicate(s);
}
void set_core(expr_ref_vector* core) { m_core = core; }
void set_model(model_ref* mdl) { m_model = mdl; }
void set_subset_based_core(bool f) { m_subset_based_core = f; }
void set_consequences(expr_ref_vector* consequences) { m_consequences = consequences; }
bool assumes_level() const { return m_assumes_level; }
void add_level();
unsigned level_cnt() const;
class scoped_level {
bool& m_lev;
public:
scoped_level(prop_solver& ps, unsigned lvl):m_lev(ps.m_in_level) {
SASSERT(!m_lev); m_lev = true; ps.m_current_level = lvl;
}
~scoped_level() { m_lev = false; }
};
void set_use_farkas(bool f) { m_use_farkas = f; }
bool get_use_farkas() const { return m_use_farkas; }
void add_formula(expr * form);
void add_level_formula(expr * form, unsigned level);
/**
* Return true iff conjunction of atoms is consistent with the current state of
* the solver.
*
* If the conjunction of atoms is inconsistent with the solver state and core is non-zero,
* core will contain an unsatisfiable core of atoms.
*
* If the conjunction of atoms is consistent with the solver state and o_model is non-zero,
* o_model will contain the "o" literals true in the assignment.
*/
lbool check_assumptions(const expr_ref_vector & atoms);
lbool check_conjunction_as_assumptions(expr * conj);
/**
* Like check_assumptions, except it also asserts an extra formula
*/
lbool check_assumptions_and_formula(
const expr_ref_vector & atoms,
expr * form);
void collect_statistics(statistics& st) const;
void reset_statistics();
};
}
#endif

View file

@ -0,0 +1,127 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
reachable_cache.cpp
Abstract:
Object for caching of reachable states.
Author:
Krystof Hoder (t-khoder) 2011-9-14.
Revision History:
--*/
#include "pdr_reachable_cache.h"
namespace pdr {
reachable_cache::reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm)
: m(pm.get_manager()),
m_pm(pm),
m_ctx(0),
m_ref_holder(m),
m_disj_connector(m),
m_cache_mode(cm) {
if (m_cache_mode == datalog::CONSTRAINT_CACHE) {
m_ctx = pm.mk_fresh();
m_ctx->assert_expr(m_pm.get_background());
}
}
void reachable_cache::add_disjuncted_formula(expr * f) {
app_ref new_connector(m.mk_fresh_const("disj_conn", m.mk_bool_sort()), m);
app_ref neg_new_connector(m.mk_not(new_connector), m);
app_ref extended_form(m);
if(m_disj_connector) {
extended_form = m.mk_or(m_disj_connector, neg_new_connector, f);
}
else {
extended_form = m.mk_or(neg_new_connector, f);
}
if (m_ctx) {
m_ctx->assert_expr(extended_form);
}
m_disj_connector = new_connector;
}
void reachable_cache::add_reachable(expr * cube) {
switch (m_cache_mode) {
case datalog::NO_CACHE:
break;
case datalog::HASH_CACHE:
m_stats.m_inserts++;
m_cache.insert(cube);
m_ref_holder.push_back(cube);
break;
case datalog::CONSTRAINT_CACHE:
m_stats.m_inserts++;
TRACE("pdr", tout << mk_pp(cube, m) << "\n";);
add_disjuncted_formula(cube);
break;
default:
UNREACHABLE();
}
}
bool reachable_cache::is_reachable(expr * cube) {
bool found = false;
switch (m_cache_mode) {
case datalog::NO_CACHE:
return false;
case datalog::HASH_CACHE:
found = m_cache.contains(cube);
break;
case datalog::CONSTRAINT_CACHE: {
if(!m_disj_connector) {
found = false;
break;
}
expr * connector = m_disj_connector.get();
expr_ref_vector assms(m);
assms.push_back(connector);
m_ctx->push();
m_ctx->assert_expr(cube);
lbool res = m_ctx->check(assms);
m_ctx->pop();
TRACE("pdr", tout << "is_reachable: " << res << " " << mk_pp(cube, m) << "\n";);
found = res == l_true;
break;
}
default:
UNREACHABLE();
break;
}
if (found) m_stats.m_hits++; m_stats.m_miss++;
return found;
}
void reachable_cache::collect_statistics(statistics& st) const {
st.update("cache inserts", m_stats.m_inserts);
st.update("cache miss", m_stats.m_miss);
st.update("cache hits", m_stats.m_hits);
}
void reachable_cache::reset_statistics() {
m_stats.reset();
}
}

View file

@ -0,0 +1,66 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
reachable_cache.h
Abstract:
Object for caching of reachable states.
Author:
Krystof Hoder (t-khoder) 2011-9-14.
Revision History:
--*/
#ifndef _REACHABLE_CACHE_H_
#define _REACHABLE_CACHE_H_
#include "ast.h"
#include "ref_vector.h"
#include "pdr_manager.h"
#include "pdr_smt_context_manager.h"
namespace pdr {
class reachable_cache {
struct stats {
unsigned m_hits;
unsigned m_miss;
unsigned m_inserts;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
ast_manager & m;
manager & m_pm;
scoped_ptr<smt_context> m_ctx;
ast_ref_vector m_ref_holder;
app_ref m_disj_connector;
obj_hashtable<expr> m_cache;
stats m_stats;
datalog::PDR_CACHE_MODE m_cache_mode;
void add_disjuncted_formula(expr * f);
public:
reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm);
void add_init(app * f) { add_disjuncted_formula(f); }
/** add cube whose all models are reachable */
void add_reachable(expr * cube);
/** return true if there is a model of cube which is reachable */
bool is_reachable(expr * cube);
void collect_statistics(statistics& st) const;
void reset_statistics();
};
}
#endif

View file

@ -0,0 +1,167 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_smt_context_manager.cpp
Abstract:
Manager of smt contexts
Author:
Nikolaj Bjorner (nbjorner) 2011-11-26.
Revision History:
--*/
#include "pdr_smt_context_manager.h"
#include "has_free_vars.h"
#include "ast_pp.h"
#include "ast_smt_pp.h"
#include <sstream>
#include "smt_params.h"
namespace pdr {
smt_context::smt_context(smt_context_manager& p, ast_manager& m, app* pred):
m_pred(pred, m),
m_parent(p),
m_in_delay_scope(false),
m_pushed(false)
{}
bool smt_context::is_aux_predicate(func_decl* p) {
return m_parent.is_aux_predicate(p);
}
smt_context::scoped::scoped(smt_context& ctx): m_ctx(ctx) {
SASSERT(!m_ctx.m_in_delay_scope);
SASSERT(!m_ctx.m_pushed);
m_ctx.m_in_delay_scope = true;
}
smt_context::scoped::~scoped() {
SASSERT(m_ctx.m_in_delay_scope);
if (m_ctx.m_pushed) {
m_ctx.pop();
m_ctx.m_pushed = false;
}
m_ctx.m_in_delay_scope = false;
}
_smt_context::_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred):
smt_context(p, ctx.m(), pred),
m_context(ctx)
{}
void _smt_context::assert_expr(expr* e) {
ast_manager& m = m_context.m();
if (m.is_true(e)) {
return;
}
CTRACE("pdr", has_free_vars(e), tout << mk_pp(e, m) << "\n";);
SASSERT(!has_free_vars(e));
if (m_in_delay_scope && !m_pushed) {
m_context.push();
m_pushed = true;
}
expr_ref fml(m);
fml = m_pushed?e:m.mk_implies(m_pred, e);
m_context.assert_expr(fml);
}
lbool _smt_context::check(expr_ref_vector& assumptions) {
ast_manager& m = m_pred.get_manager();
if (!m.is_true(m_pred)) {
assumptions.push_back(m_pred);
}
TRACE("pdr_check",
{
ast_smt_pp pp(m);
for (unsigned i = 0; i < m_context.size(); ++i) {
pp.add_assumption(m_context.get_formulas()[i]);
}
for (unsigned i = 0; i < assumptions.size(); ++i) {
pp.add_assumption(assumptions[i].get());
}
static unsigned lemma_id = 0;
std::ostringstream strm;
strm << "pdr-lemma-" << lemma_id << ".smt2";
std::ofstream out(strm.str().c_str());
pp.display_smt2(out, m.mk_true());
out.close();
lemma_id++;
tout << "pdr_check: " << strm.str() << "\n";
});
lbool result = m_context.check(assumptions.size(), assumptions.c_ptr());
if (!m.is_true(m_pred)) {
assumptions.pop_back();
}
return result;
}
void _smt_context::get_model(model_ref& model) {
m_context.get_model(model);
}
proof* _smt_context::get_proof() {
return m_context.get_proof();
}
smt_context_manager::smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m):
m_fparams(fp),
m(m),
m_max_num_contexts(max_num_contexts),
m_num_contexts(0),
m_predicate_list(m) {
}
smt_context_manager::~smt_context_manager() {
TRACE("pdr",tout << "\n";);
std::for_each(m_contexts.begin(), m_contexts.end(), delete_proc<smt::kernel>());
}
smt_context* smt_context_manager::mk_fresh() {
++m_num_contexts;
app_ref pred(m);
smt::kernel * ctx = 0;
if (m_max_num_contexts == 0) {
m_contexts.push_back(alloc(smt::kernel, m, m_fparams));
pred = m.mk_true();
ctx = m_contexts[m_num_contexts-1];
}
else {
if (m_contexts.size() < m_max_num_contexts) {
m_contexts.push_back(alloc(smt::kernel, m, m_fparams));
}
std::stringstream name;
name << "#context" << m_num_contexts;
pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort());
m_predicate_list.push_back(pred);
m_predicate_set.insert(pred->get_decl());
ctx = m_contexts[(m_num_contexts-1)%m_max_num_contexts];
}
return alloc(_smt_context, *ctx, *this, pred);
}
void smt_context_manager::collect_statistics(statistics& st) const {
for (unsigned i = 0; i < m_contexts.size(); ++i) {
m_contexts[i]->collect_statistics(st);
}
}
void smt_context_manager::reset_statistics() {
for (unsigned i = 0; i < m_contexts.size(); ++i) {
m_contexts[i]->reset_statistics();
}
}
};

View file

@ -0,0 +1,110 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_smt_context_manager.h
Abstract:
Manager of smt contexts
Author:
Nikolaj Bjorner (nbjorner) 2011-11-26.
Revision History:
--*/
#ifndef _PDR_SMT_CONTEXT_MANAGER_H_
#define _PDR_SMT_CONTEXT_MANAGER_H_
#include "smt_kernel.h"
#include "sat_solver.h"
#include "func_decl_dependencies.h"
#include "dl_util.h"
namespace pdr {
class smt_context_manager;
class smt_context {
protected:
app_ref m_pred;
smt_context_manager& m_parent;
bool m_in_delay_scope;
bool m_pushed;
public:
smt_context(smt_context_manager& p, ast_manager& m, app* pred);
virtual ~smt_context() {}
virtual void assert_expr(expr* e) = 0;
virtual lbool check(expr_ref_vector& assumptions) = 0;
virtual void get_model(model_ref& model) = 0;
virtual proof* get_proof() = 0;
virtual unsigned get_unsat_core_size() = 0;
virtual expr* get_unsat_core_expr(unsigned i) = 0;
virtual void push() = 0;
virtual void pop() = 0;
bool is_aux_predicate(func_decl* p);
bool is_aux_predicate(expr* p) { return is_app(p) && is_aux_predicate(to_app(p)->get_decl()); }
class scoped {
smt_context& m_ctx;
public:
scoped(smt_context& ctx);
~scoped();
};
};
class _smt_context : public smt_context {
smt::kernel & m_context;
public:
_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred);
virtual ~_smt_context() {}
virtual void assert_expr(expr* e);
virtual lbool check(expr_ref_vector& assumptions);
virtual void get_model(model_ref& model);
virtual proof* get_proof();
virtual void push() { m_context.push(); }
virtual void pop() { m_context.pop(1); }
virtual unsigned get_unsat_core_size() { return m_context.get_unsat_core_size(); }
virtual expr* get_unsat_core_expr(unsigned i) { return m_context.get_unsat_core_expr(i); }
};
// TBD:
class sat_context : public smt_context {
sat::solver m_solver;
public:
sat_context(smt::kernel & ctx, smt_context_manager& p, app* pred);
virtual ~sat_context() {}
virtual void assert_expr(expr* e);
virtual lbool check(expr_ref_vector& assumptions);
virtual void get_model(model_ref& model);
virtual proof* get_proof();
virtual void pop() { m_solver.pop(1); }
virtual void push() { m_solver.push(); }
// TBD: add unsat core extraction with sat::solver.
virtual unsigned get_unsat_core_size();
virtual expr* get_unsat_core_expr(unsigned i);
};
class smt_context_manager {
smt_params& m_fparams;
ast_manager& m;
unsigned m_max_num_contexts;
ptr_vector<smt::kernel> m_contexts;
unsigned m_num_contexts;
app_ref_vector m_predicate_list;
func_decl_set m_predicate_set;
public:
smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m);
~smt_context_manager();
smt_context* mk_fresh();
void collect_statistics(statistics& st) const;
void reset_statistics();
bool is_aux_predicate(func_decl* p) const { return m_predicate_set.contains(p); }
};
};
#endif

600
src/muz/pdr/pdr_sym_mux.cpp Normal file
View file

@ -0,0 +1,600 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sym_mux.cpp
Abstract:
A symbol multiplexer that helps with having multiple versions of each of a set of symbols.
Author:
Krystof Hoder (t-khoder) 2011-9-8.
Revision History:
--*/
#include <sstream>
#include "ast_pp.h"
#include "for_each_expr.h"
#include "model.h"
#include "rewriter.h"
#include "rewriter_def.h"
#include "pdr_util.h"
#include "pdr_sym_mux.h"
using namespace pdr;
sym_mux::sym_mux(ast_manager & m, const vector<std::string> & suffixes)
: m(m), m_ref_holder(m), m_next_sym_suffix_idx(0), m_suffixes(suffixes)
{
unsigned suf_sz = m_suffixes.size();
for(unsigned i = 0; i < suf_sz; ++i) {
symbol suff_sym = symbol(m_suffixes[i].c_str());
m_used_suffixes.insert(suff_sym);
}
}
std::string sym_mux::get_suffix(unsigned i) const {
while(m_suffixes.size() <= i) {
std::string new_suffix;
symbol new_syffix_sym;
do {
std::stringstream stm;
stm<<'_'<<m_next_sym_suffix_idx;
m_next_sym_suffix_idx++;
new_suffix = stm.str();
new_syffix_sym = symbol(new_suffix.c_str());
}
while (m_used_suffixes.contains(new_syffix_sym));
m_used_suffixes.insert(new_syffix_sym);
m_suffixes.push_back(new_suffix);
}
return m_suffixes[i];
}
void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range,
unsigned tuple_length, decl_vector & tuple)
{
SASSERT(tuple_length>0);
while(tuple.size()<tuple_length) {
tuple.push_back(0);
}
SASSERT(tuple.size()==tuple_length);
std::string pre = prefix->get_name().str();
for(unsigned i=0; i<tuple_length; i++) {
if (tuple[i] != 0) {
SASSERT(tuple[i]->get_arity()==arity);
SASSERT(tuple[i]->get_range()==range);
//domain should match as well, but we won't bother checking an array equality
}
else {
std::string name = pre+get_suffix(i);
tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range);
}
m_ref_holder.push_back(tuple[i]);
m_sym2idx.insert(tuple[i], i);
m_sym2prim.insert(tuple[i], tuple[0]);
}
m_prim2all.insert(tuple[0], tuple);
m_prefix2prim.insert(prefix, tuple[0]);
m_prim2prefix.insert(tuple[0], prefix);
m_prim_preds.push_back(tuple[0]);
m_ref_holder.push_back(prefix);
}
void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const {
SASSERT(m_prim2all.contains(prim));
decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value;
SASSERT(tuple[0]==prim);
if(sz <= tuple.size()) { return; }
func_decl * prefix;
TRUSTME(m_prim2prefix.find(prim, prefix));
std::string prefix_name = prefix->get_name().bare_str();
for(unsigned i=tuple.size(); i<sz; ++i) {
std::string name = prefix_name+get_suffix(i);
func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(),
prefix->get_domain(), prefix->get_range());
tuple.push_back(new_sym);
m_ref_holder.push_back(new_sym);
m_sym2idx.insert(new_sym, i);
m_sym2prim.insert(new_sym, prim);
}
}
func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const
{
if(src_idx==tgt_idx) { return sym; }
func_decl * prim = (src_idx==0) ? sym : get_primary(sym);
if(tgt_idx>src_idx) {
ensure_tuple_size(prim, tgt_idx+1);
}
decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value;
SASSERT(sym_vect[src_idx]==sym);
return sym_vect[tgt_idx];
}
func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx,
unsigned arity, sort * const * domain, sort * range)
{
func_decl * prim = try_get_primary_by_prefix(prefix);
if(prim) {
SASSERT(prim->get_arity()==arity);
SASSERT(prim->get_range()==range);
//domain should match as well, but we won't bother checking an array equality
return conv(prim, 0, idx);
}
decl_vector syms;
create_tuple(prefix, arity, domain, range, idx+1, syms);
return syms[idx];
}
bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const
{
if(!is_app(e)) { return false; }
app * a = to_app(e);
if(m.is_not(a) && is_app(a->get_arg(0))) {
a = to_app(a->get_arg(0));
}
return is_muxed(a->get_decl());
}
struct sym_mux::formula_checker
{
formula_checker(const sym_mux & parent, bool all, unsigned idx) :
m_parent(parent), m_all(all), m_idx(idx),
m_found_what_needed(false)
{
}
void operator()(expr * e)
{
if(m_found_what_needed || !is_app(e)) { return; }
func_decl * sym = to_app(e)->get_decl();
unsigned sym_idx;
if(!m_parent.try_get_index(sym, sym_idx)) { return; }
bool have_idx = sym_idx==m_idx;
if( m_all ? (!have_idx) : have_idx ) {
m_found_what_needed = true;
}
}
bool all_have_idx() const
{
SASSERT(m_all); //we were looking for the queried property
return !m_found_what_needed;
}
bool some_with_idx() const
{
SASSERT(!m_all); //we were looking for the queried property
return m_found_what_needed;
}
private:
const sym_mux & m_parent;
bool m_all;
unsigned m_idx;
/**
If we check whether all muxed symbols are of given index, we look for
counter-examples, checking whether form contains a muxed symbol of an index,
we look for symbol of index m_idx.
*/
bool m_found_what_needed;
};
bool sym_mux::contains(expr * e, unsigned idx) const
{
formula_checker chck(*this, false, idx);
for_each_expr(chck, m_visited, e);
m_visited.reset();
return chck.some_with_idx();
}
bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const
{
formula_checker chck(*this, true, idx);
for_each_expr(chck, m_visited, e);
m_visited.reset();
return chck.all_have_idx();
}
bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const
{
expr * const * begin = vect.c_ptr();
expr * const * end = begin + vect.size();
for(expr * const * it = begin; it!=end; it++) {
if(!is_homogenous_formula(*it, idx)) {
return false;
}
}
return true;
}
class sym_mux::index_collector {
sym_mux const& m_parent;
svector<bool> m_indices;
public:
index_collector(sym_mux const& s):
m_parent(s) {}
void operator()(expr * e) {
if (is_app(e)) {
func_decl * sym = to_app(e)->get_decl();
unsigned idx;
if (m_parent.try_get_index(sym, idx)) {
SASSERT(idx > 0);
--idx;
if (m_indices.size() <= idx) {
m_indices.resize(idx+1, false);
}
m_indices[idx] = true;
}
}
}
void extract(unsigned_vector& indices) {
for (unsigned i = 0; i < m_indices.size(); ++i) {
if (m_indices[i]) {
indices.push_back(i);
}
}
}
};
void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const {
indices.reset();
index_collector collector(*this);
for_each_expr(collector, m_visited, e);
m_visited.reset();
collector.extract(indices);
}
class sym_mux::variable_collector {
sym_mux const& m_parent;
vector<ptr_vector<app> >& m_vars;
public:
variable_collector(sym_mux const& s, vector<ptr_vector<app> >& vars):
m_parent(s), m_vars(vars) {}
void operator()(expr * e) {
if (is_app(e)) {
func_decl * sym = to_app(e)->get_decl();
unsigned idx;
if (m_parent.try_get_index(sym, idx)) {
SASSERT(idx > 0);
--idx;
if (m_vars.size() <= idx) {
m_vars.resize(idx+1, ptr_vector<app>());
}
m_vars[idx].push_back(to_app(e));
}
}
}
};
void sym_mux::collect_variables(expr* e, vector<ptr_vector<app> >& vars) const {
vars.reset();
variable_collector collector(*this, vars);
for_each_expr(collector, m_visited, e);
m_visited.reset();
}
class sym_mux::hmg_checker {
const sym_mux & m_parent;
bool m_found_idx;
unsigned m_idx;
bool m_multiple_indexes;
public:
hmg_checker(const sym_mux & parent) :
m_parent(parent), m_found_idx(false), m_multiple_indexes(false)
{
}
void operator()(expr * e)
{
if(m_multiple_indexes || !is_app(e)) { return; }
func_decl * sym = to_app(e)->get_decl();
unsigned sym_idx;
if(!m_parent.try_get_index(sym, sym_idx)) { return; }
if(!m_found_idx) {
m_found_idx = true;
m_idx = sym_idx;
return;
}
if(m_idx==sym_idx) { return; }
m_multiple_indexes = true;
}
bool has_multiple_indexes() const
{
return m_multiple_indexes;
}
};
bool sym_mux::is_homogenous_formula(expr * e) const {
hmg_checker chck(*this);
for_each_expr(chck, m_visited, e);
m_visited.reset();
return !chck.has_multiple_indexes();
}
struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg
{
private:
ast_manager & m;
const sym_mux & m_parent;
unsigned m_from_idx;
unsigned m_to_idx;
bool m_homogenous;
public:
conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous)
: m(parent.get_manager()),
m_parent(parent),
m_from_idx(from_idx),
m_to_idx(to_idx),
m_homogenous(homogenous) {}
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
if(!is_app(s)) { return false; }
app * a = to_app(s);
func_decl * sym = a->get_decl();
if(!m_parent.has_index(sym, m_from_idx)) {
SASSERT(!m_homogenous || !m_parent.is_muxed(sym));
return false;
}
func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx);
t = m.mk_app(tgt, a->get_args());
return true;
}
};
void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) const
{
if(src_idx==tgt_idx) {
res = f;
return;
}
conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous);
rewriter_tpl<conv_rewriter_cfg> rwr(m, false, r_cfg);
rwr(f, res);
}
struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg
{
private:
ast_manager & m;
const sym_mux & m_parent;
int m_shift;
public:
shifting_rewriter_cfg(const sym_mux & parent, int shift)
: m(parent.get_manager()),
m_parent(parent),
m_shift(shift) {}
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
if(!is_app(s)) { return false; }
app * a = to_app(s);
func_decl * sym = a->get_decl();
unsigned idx;
if(!m_parent.try_get_index(sym, idx)) {
return false;
}
SASSERT(static_cast<int>(idx)+m_shift>=0);
func_decl * tgt = m_parent.conv(sym, idx, idx+m_shift);
t = m.mk_app(tgt, a->get_args());
return true;
}
};
void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) const
{
if(dist==0) {
res = f;
return;
}
shifting_rewriter_cfg r_cfg(*this, dist);
rewriter_tpl<shifting_rewriter_cfg> rwr(m, false, r_cfg);
rwr(f, res);
}
void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx,
expr_ref_vector & res) const
{
res.reset();
expr * const * begin = vect.c_ptr();
expr * const * end = begin + vect.size();
for(expr * const * it = begin; it!=end; it++) {
expr_ref converted(m);
conv_formula(*it, src_idx, tgt_idx, converted);
res.push_back(converted);
}
}
void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const {
unsigned i = 0;
while (i < vect.size()) {
expr* e = vect[i].get();
if(contains(e, idx) && is_homogenous_formula(e, idx)) {
i++;
}
else {
//we don't allow mixing states inside vector elements
SASSERT(!contains(e, idx));
vect[i] = vect.back();
vect.pop_back();
}
}
}
void sym_mux::partition_o_idx(
expr_ref_vector const& lits,
expr_ref_vector& o_lits,
expr_ref_vector& other, unsigned idx) const {
for (unsigned i = 0; i < lits.size(); ++i) {
if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) {
o_lits.push_back(lits[i]);
}
else {
other.push_back(lits[i]);
}
}
}
class sym_mux::nonmodel_sym_checker {
const sym_mux & m_parent;
bool m_found;
public:
nonmodel_sym_checker(const sym_mux & parent) :
m_parent(parent), m_found(false)
{
}
void operator()(expr * e)
{
if(m_found || !is_app(e)) { return; }
func_decl * sym = to_app(e)->get_decl();
if(m_parent.is_non_model_sym(sym)) {
m_found = true;
}
}
bool found() const
{
return m_found;
}
};
bool sym_mux::has_nonmodel_symbol(expr * e) const {
nonmodel_sym_checker chck(*this);
for_each_expr(chck, e);
return chck.found();
}
void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const {
unsigned i=0;
while(i<vect.size()) {
if(!has_nonmodel_symbol(vect[i].get())) {
i++;
continue;
}
vect[i] = vect.back();
vect.pop_back();
}
}
class sym_mux::decl_idx_comparator
{
const sym_mux & m_parent;
public:
decl_idx_comparator(const sym_mux & parent)
: m_parent(parent)
{ }
bool operator()(func_decl * sym1, func_decl * sym2)
{
unsigned idx1, idx2;
if(!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; }
if(!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; }
if(idx1!=idx2) { return idx1<idx2; }
return lt(sym1->get_name(), sym2->get_name());
}
};
std::string sym_mux::pp_model(const model_core & mdl) const {
decl_vector consts;
unsigned sz = mdl.get_num_constants();
for (unsigned i = 0; i < sz; i++) {
func_decl * d = mdl.get_constant(i);
consts.push_back(d);
}
std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this));
std::stringstream res;
decl_vector::iterator end = consts.end();
for(decl_vector::iterator it = consts.begin(); it!=end; it++) {
func_decl * d = *it;
std::string name = d->get_name().str();
const char * arrow = " -> ";
res << name << arrow;
unsigned indent = static_cast<unsigned>(name.length() + strlen(arrow));
res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n";
if(it+1!=end) {
unsigned idx1, idx2;
if(!try_get_index(*it, idx1)) { idx1 = UINT_MAX; }
if(!try_get_index(*(it+1), idx2)) { idx2 = UINT_MAX; }
if(idx1!=idx2) { res << "\n"; }
}
}
return res.str();
}
#if 0
class sym_mux::index_renamer_cfg : public default_rewriter_cfg{
const sym_mux & m_parent;
unsigned m_idx;
public:
index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {}
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
if (!is_app(s)) return false;
app * a = to_app(s);
if (a->get_family_id() != null_family_id) {
return false;
}
func_decl * sym = a->get_decl();
unsigned idx;
if(!m_parent.try_get_index(sym, idx)) {
return false;
}
if (m_idx == idx) {
return false;
}
ast_manager& m = m_parent.get_manager();
symbol name = symbol((sym->get_name().str() + "!").c_str());
func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range());
t = m.mk_app(tgt, a->get_num_args(), a->get_args());
return true;
}
};
#endif

249
src/muz/pdr/pdr_sym_mux.h Normal file
View file

@ -0,0 +1,249 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sym_mux.h
Abstract:
A symbol multiplexer that helps with having multiple versions of each of a set of symbols.
Author:
Krystof Hoder (t-khoder) 2011-9-8.
Revision History:
--*/
#ifndef _SYM_MUX_H_
#define _SYM_MUX_H_
#include "ast.h"
#include "map.h"
#include "vector.h"
class model_core;
namespace pdr {
class sym_mux
{
public:
typedef ptr_vector<app> app_vector;
typedef ptr_vector<func_decl> decl_vector;
private:
typedef obj_map<func_decl,unsigned> sym2u;
typedef obj_map<func_decl, decl_vector> sym2dv;
typedef obj_map<func_decl,func_decl *> sym2sym;
typedef obj_map<func_decl, func_decl *> sym2pred;
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbols;
ast_manager & m;
mutable ast_ref_vector m_ref_holder;
mutable expr_mark m_visited;
mutable unsigned m_next_sym_suffix_idx;
mutable symbols m_used_suffixes;
/** Here we have default suffixes for each of the variants */
mutable vector<std::string> m_suffixes;
/**
Primary symbol is the 0-th variant. This member maps from primary symbol
to vector of all its variants (including the primary variant).
*/
sym2dv m_prim2all;
/**
For each symbol contains its variant index
*/
mutable sym2u m_sym2idx;
/**
For each symbol contains its primary variant
*/
mutable sym2sym m_sym2prim;
/**
Maps prefixes passed to the create_tuple to
the primary symbol created from it.
*/
sym2pred m_prefix2prim;
/**
Maps pripary symbols to prefixes that were used to create them.
*/
sym2sym m_prim2prefix;
decl_vector m_prim_preds;
obj_hashtable<func_decl> m_non_model_syms;
struct formula_checker;
struct conv_rewriter_cfg;
struct shifting_rewriter_cfg;
class decl_idx_comparator;
class hmg_checker;
class nonmodel_sym_checker;
class index_renamer_cfg;
class index_collector;
class variable_collector;
std::string get_suffix(unsigned i) const;
void ensure_tuple_size(func_decl * prim, unsigned sz) const;
expr_ref isolate_o_idx(expr* e, unsigned idx) const;
public:
sym_mux(ast_manager & m, const vector<std::string> & suffixes);
ast_manager & get_manager() const { return m; }
bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); }
bool try_get_index(func_decl * sym, unsigned & idx) const {
return m_sym2idx.find(sym,idx);
}
bool has_index(func_decl * sym, unsigned idx) const {
unsigned actual_idx;
return try_get_index(sym, actual_idx) && idx==actual_idx;
}
/** Return primary symbol. sym must be muxed. */
func_decl * get_primary(func_decl * sym) const {
func_decl * prim;
TRUSTME(m_sym2prim.find(sym, prim));
return prim;
}
/**
Return primary symbol created from prefix, or 0 if the prefix was never used.
*/
func_decl * try_get_primary_by_prefix(func_decl* prefix) const {
func_decl * res;
if(!m_prefix2prim.find(prefix, res)) {
return 0;
}
return res;
}
/**
Return symbol created from prefix, or 0 if the prefix was never used.
*/
func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) const {
func_decl * prim = try_get_primary_by_prefix(prefix);
if(!prim) {
return 0;
}
return conv(prim, 0, idx);
}
/**
Marks symbol as non-model which means it will not appear in models collected by
get_muxed_cube_from_model function.
This is to take care of auxiliary symbols introduced by the disjunction relations
to relativize lemmas coming from disjuncts.
*/
void mark_as_non_model(func_decl * sym) {
SASSERT(is_muxed(sym));
m_non_model_syms.insert(get_primary(sym));
}
func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx,
unsigned arity, sort * const * domain, sort * range);
bool is_muxed_lit(expr * e, unsigned idx) const;
bool is_non_model_sym(func_decl * s) const {
return is_muxed(s) && m_non_model_syms.contains(get_primary(s));
}
/**
Create a multiplexed tuple of propositional constants.
Symbols may be suplied in the tuple vector,
those beyond the size of the array and those with corresponding positions
assigned to zero will be created using prefix.
Tuple length must be at least one.
*/
void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range,
unsigned tuple_length, decl_vector & tuple);
/**
Return true if the only multiplexed symbols which e contains are of index idx.
*/
bool is_homogenous_formula(expr * e, unsigned idx) const;
bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const;
/**
Return true if all multiplexed symbols which e contains are of one index.
*/
bool is_homogenous_formula(expr * e) const;
/**
Return true if expression e contains a muxed symbol of index idx.
*/
bool contains(expr * e, unsigned idx) const;
/**
Collect indices used in expression.
*/
void collect_indices(expr* e, unsigned_vector& indices) const;
/**
Collect used variables of each index.
*/
void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const;
/**
Convert symbol sym which has to be of src_idx variant into variant tgt_idx.
*/
func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const;
/**
Convert src_idx symbols in formula f variant into tgt_idx.
If homogenous is true, formula cannot contain symbols of other variants.
*/
void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous=true) const;
void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx,
expr_ref_vector & res) const;
/**
Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift
symbol index to a negative value.
*/
void shift_formula(expr * f, int dist, expr_ref & res) const;
/**
Remove from vect literals (atoms or negations of atoms) of symbols
that contain multiplexed symbols with indexes other than idx.
Each of the literals can contain only symbols multiplexed with one index
(this trivially holds if the literals are propositional).
Order of elements in vect may be modified by this function
*/
void filter_idx(expr_ref_vector & vect, unsigned idx) const;
/**
Partition literals into o_literals and others.
*/
void partition_o_idx(expr_ref_vector const& lits,
expr_ref_vector& o_lits,
expr_ref_vector& other, unsigned idx) const;
bool has_nonmodel_symbol(expr * e) const;
void filter_non_model_lits(expr_ref_vector & vect) const;
func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); }
func_decl * const * end_prim_preds() const { return m_prim_preds.end(); }
void get_muxed_cube_from_model(const model_core & model, expr_ref_vector & res) const;
std::string pp_model(const model_core & mdl) const;
};
}
#endif

1435
src/muz/pdr/pdr_util.cpp Normal file

File diff suppressed because it is too large Load diff

167
src/muz/pdr/pdr_util.h Normal file
View file

@ -0,0 +1,167 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
pdr_util.h
Abstract:
Utility functions for PDR.
Author:
Krystof Hoder (t-khoder) 2011-8-19.
Revision History:
--*/
#ifndef _PDR_UTIL_H_
#define _PDR_UTIL_H_
#include "ast.h"
#include "ast_pp.h"
#include "obj_hashtable.h"
#include "ref_vector.h"
#include "simplifier.h"
#include "trace.h"
#include "vector.h"
#include "arith_decl_plugin.h"
#include "array_decl_plugin.h"
#include "bv_decl_plugin.h"
class model;
class model_core;
namespace pdr {
/**
* Return the ceiling of base 2 logarithm of a number,
* or zero if the nmber is zero.
*/
unsigned ceil_log2(unsigned u);
typedef ptr_vector<app> app_vector;
typedef ptr_vector<func_decl> decl_vector;
typedef obj_hashtable<func_decl> func_decl_set;
std::string pp_cube(const ptr_vector<expr>& model, ast_manager& manager);
std::string pp_cube(const expr_ref_vector& model, ast_manager& manager);
std::string pp_cube(const ptr_vector<app>& model, ast_manager& manager);
std::string pp_cube(const app_ref_vector& model, ast_manager& manager);
std::string pp_cube(unsigned sz, app * const * lits, ast_manager& manager);
std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& manager);
class model_evaluator {
ast_manager& m;
arith_util m_arith;
array_util m_array;
obj_map<expr,rational> m_numbers;
expr_ref_vector m_refs;
obj_map<expr, expr*> m_values;
model_ref m_model;
//00 -- non-visited
//01 -- X
//10 -- false
//11 -- true
expr_mark m1;
expr_mark m2;
expr_mark m_visited;
void reset();
void setup_model(model_ref& model);
void assign_value(expr* e, expr* v);
void collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect);
void process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect);
expr_ref_vector prune_by_cone_of_influence(ptr_vector<expr> const & formulas);
void eval_arith(app* e);
void eval_basic(app* e);
void eval_eq(app* e, expr* arg1, expr* arg2);
void eval_array_eq(app* e, expr* arg1, expr* arg2);
void inherit_value(expr* e, expr* v);
inline bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); }
inline void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); }
inline bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); }
inline bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); }
inline bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); }
inline void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); }
inline void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); }
inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } }
inline rational const& get_number(expr* x) const { return m_numbers.find(x); }
inline void set_number(expr* x, rational const& v) {
set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v);
}
inline expr* get_value(expr* x) { return m_values.find(x); }
inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); }
bool check_model(ptr_vector<expr> const & formulas);
bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case);
void eval_exprs(expr_ref_vector& es);
public:
model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {}
/**
\brief extract equalities from model that suffice to satisfy formula.
\pre model satisfies formulas
*/
expr_ref_vector minimize_model(ptr_vector<expr> const & formulas, model_ref& mdl);
/**
\brief extract literals from formulas that satisfy formulas.
\pre model satisfies formulas
*/
expr_ref_vector minimize_literals(ptr_vector<expr> const & formulas, model_ref& mdl);
/**
for_each_expr visitor.
*/
void operator()(expr* e) {}
expr_ref eval(model_ref& mdl, expr* e);
expr_ref eval(model_ref& mdl, func_decl* d);
};
/**
\brief replace variables that are used in many disequalities by
an equality using the model.
Assumption: the model satisfies the conjunctions.
*/
void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml);
/**
\brief hoist non-boolean if expressions.
*/
void hoist_non_bool_if(expr_ref& fml);
/**
\brief normalize coefficients in polynomials so that least coefficient is 1.
*/
void normalize_arithmetic(expr_ref& t);
/**
\brief determine if formulas belong to difference logic or UTVPI fragment.
*/
bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls);
bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls);
}
#endif

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

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

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

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

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

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

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,467 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
dl_lazy_table.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2013-09-04
Revision History:
--*/
#include "dl_lazy_table.h"
#include "dl_relation_manager.h"
#include <sstream>
namespace datalog {
// ------------------
// lazy_table_plugin:
symbol lazy_table_plugin::mk_name(table_plugin& p) {
std::ostringstream strm;
strm << "lazy_" << p.get_name();
return symbol(strm.str().c_str());
}
table_base * lazy_table_plugin::mk_empty(const table_signature & s) {
return alloc(lazy_table, alloc(lazy_table_base, *this, m_plugin.mk_empty(s)));
}
lazy_table const& lazy_table_plugin::get(table_base const& tb) { return dynamic_cast<lazy_table const&>(tb); }
lazy_table& lazy_table_plugin::get(table_base& tb) { return dynamic_cast<lazy_table&>(tb); }
lazy_table const* lazy_table_plugin::get(table_base const* tb) { return dynamic_cast<lazy_table const*>(tb); }
lazy_table* lazy_table_plugin::get(table_base* tb) { return dynamic_cast<lazy_table*>(tb); }
// --------------------------
// lazy_table_plugin::join_fn
class lazy_table_plugin::join_fn : public convenient_table_join_fn {
public:
join_fn(table_signature const& s1, table_signature const& s2, unsigned col_cnt,
unsigned const* cols1, unsigned const* cols2):
convenient_table_join_fn(s1, s2, col_cnt, cols1, cols2) {}
virtual table_base* operator()(const table_base& _t1, const table_base& _t2) {
lazy_table const& t1 = get(_t1);
lazy_table const& t2 = get(_t2);
lazy_table_ref* tr = alloc(lazy_table_join, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), t1, t2, get_result_signature());
return alloc(lazy_table, tr);
}
};
table_join_fn * lazy_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 alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2);
}
else {
return 0;
}
}
// ------------------------
// lazy_table_plugin::union
class lazy_table_plugin::union_fn : public table_union_fn {
public:
void operator()(table_base & _tgt, const table_base & _src,
table_base * _delta) {
lazy_table& tgt = get(_tgt);
lazy_table const& src = get(_src);
lazy_table* delta = get(_delta);
table_base const* t_src = src.eval();
table_base * t_tgt = tgt.eval();
table_base * t_delta = delta?delta->eval():0;
verbose_action _t("union");
table_union_fn* m = tgt.get_lplugin().get_manager().mk_union_fn(*t_tgt, *t_src, t_delta);
SASSERT(m);
(*m)(*t_tgt, *t_src, t_delta);
dealloc(m);
}
};
table_union_fn* lazy_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 alloc(union_fn);
}
else {
return 0;
}
}
// --------------------------
// lazy_table_plugin::project
class lazy_table_plugin::project_fn : public convenient_table_project_fn {
public:
project_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols):
convenient_table_project_fn(orig_sig, cnt, cols)
{}
virtual table_base* operator()(table_base const& _t) {
lazy_table const& t = get(_t);
return alloc(lazy_table, alloc(lazy_table_project, m_removed_cols.size(), m_removed_cols.c_ptr(), t, get_result_signature()));
}
};
table_transformer_fn * lazy_table_plugin::mk_project_fn(
const table_base & t, unsigned col_cnt,
const unsigned * removed_cols) {
if (check_kind(t)) {
return alloc(project_fn, t.get_signature(), col_cnt, removed_cols);
}
else {
return 0;
}
}
// -------------------------
// lazy_table_plugin::rename
class lazy_table_plugin::rename_fn : public convenient_table_rename_fn {
public:
rename_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols):
convenient_table_rename_fn(orig_sig, cnt, cols)
{}
virtual table_base* operator()(table_base const& _t) {
lazy_table const& t = get(_t);
return alloc(lazy_table, alloc(lazy_table_rename, m_cycle.size(), m_cycle.c_ptr(), t, get_result_signature()));
}
};
table_transformer_fn * lazy_table_plugin::mk_rename_fn(
const table_base & t, unsigned col_cnt,
const unsigned * removed_cols) {
if (check_kind(t)) {
return alloc(rename_fn, t.get_signature(), col_cnt, removed_cols);
}
else {
return 0;
}
}
// -----------------------------------
// lazy_table_plugin::filter_identical
class lazy_table_plugin::filter_identical_fn : public table_mutator_fn {
unsigned_vector m_cols;
public:
filter_identical_fn(unsigned cnt, unsigned const* cols): m_cols(cnt, cols) {}
virtual void operator()(table_base& _t) {
lazy_table& t = get(_t);
t.set(alloc(lazy_table_filter_identical, m_cols.size(), m_cols.c_ptr(), t));
}
};
table_mutator_fn * lazy_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, col_cnt, identical_cols);
}
else {
return 0;
}
}
// -------------------------------------
// lazy_table_plugin::filter_interpreted
class lazy_table_plugin::filter_interpreted_fn : public table_mutator_fn {
app_ref m_condition;
public:
filter_interpreted_fn(app_ref& p): m_condition(p) {}
virtual void operator()(table_base& _t) {
lazy_table& t = get(_t);
t.set(alloc(lazy_table_filter_interpreted, t, m_condition));
}
};
table_mutator_fn * lazy_table_plugin::mk_filter_interpreted_fn(
const table_base & t, app* condition) {
if (check_kind(t)) {
app_ref cond(condition, get_ast_manager());
return alloc(filter_interpreted_fn, cond);
}
else {
return 0;
}
}
// -------------------------------------
// lazy_table_plugin::filter_by_negation
class lazy_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn {
unsigned_vector m_cols1;
unsigned_vector m_cols2;
public:
filter_by_negation_fn(unsigned cnt, unsigned const* cols1, unsigned const* cols2):
m_cols1(cnt, cols1), m_cols2(cnt, cols2) {}
virtual void operator()(table_base & _t, const table_base & _intersected_obj) {
lazy_table& t = get(_t);
lazy_table const& it = get(_intersected_obj);
t.set(alloc(lazy_table_filter_by_negation, t, it, m_cols1, m_cols2));
}
};
table_intersection_filter_fn * lazy_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, joined_col_cnt, t_cols, negated_cols);
}
else {
return 0;
}
}
// -------------------------------
// lazy_table_plugin::filter_equal
class lazy_table_plugin::filter_equal_fn : public table_mutator_fn {
table_element m_value;
unsigned m_col;
public:
filter_equal_fn(const table_element & value, unsigned col):
m_value(value),
m_col(col)
{ }
virtual void operator()(table_base& _t) {
lazy_table& t = get(_t);
t.set(alloc(lazy_table_filter_equal, m_col, m_value, t));
}
};
table_mutator_fn * lazy_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, value, col);
}
else {
return 0;
}
}
table_plugin* lazy_table_plugin::mk_sparse(relation_manager& rm) {
table_plugin* sp = rm.get_table_plugin(symbol("sparse"));
SASSERT(sp);
if (sp) {
return alloc(lazy_table_plugin, *sp);
}
else {
return 0;
}
}
// ----------
// lazy_table
table_base * lazy_table::clone() const {
table_base* t = eval();
verbose_action _t("clone");
return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t->clone()));
}
table_base * lazy_table::complement(func_decl* p, const table_element * func_columns) const {
table_base* t = eval()->complement(p, func_columns);
return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t));
}
bool lazy_table::empty() const {
return m_ref->eval()->empty();
}
bool lazy_table::contains_fact(const table_fact & f) const {
return m_ref->eval()->contains_fact(f);
}
void lazy_table::remove_fact(table_element const* fact) {
m_ref->eval()->remove_fact(fact);
}
void lazy_table::remove_facts(unsigned fact_cnt, const table_fact * facts) {
m_ref->eval()->remove_facts(fact_cnt, facts);
}
void lazy_table::remove_facts(unsigned fact_cnt, const table_element * facts) {
m_ref->eval()->remove_facts(fact_cnt, facts);
}
void lazy_table::reset() {
m_ref = alloc(lazy_table_base, get_lplugin(), get_lplugin().m_plugin.mk_empty(get_signature()));
}
void lazy_table::add_fact(table_fact const& f) {
m_ref->eval()->add_fact(f);
}
table_base::iterator lazy_table::begin() const {
return eval()->begin();
}
table_base::iterator lazy_table::end() const {
return eval()->end();
}
table_base* lazy_table::eval() const {
return m_ref->eval();
}
// -------------------------
// eval
table_base* lazy_table_join::force() {
SASSERT(!m_table);
table_base* t1 = m_t1->eval();
table_base* t2 = m_t2->eval();
verbose_action _t("join");
table_join_fn* join = rm().mk_join_fn(*t1, *t2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr());
m_table = (*join)(*t1, *t2);
dealloc(join);
return m_table.get();
}
table_base* lazy_table_project::force() {
SASSERT(!m_table);
switch(m_src->kind()) {
case LAZY_TABLE_JOIN: {
lazy_table_join& src = dynamic_cast<lazy_table_join&>(*m_src);
table_base* t1 = src.t1()->eval();
table_base* t2 = src.t2()->eval();
table_join_fn* j_fn = rm().mk_join_project_fn(*t1, *t2, src.cols1(), src.cols2(), m_cols);
if (j_fn) {
verbose_action _t("join_project");
m_table = (*j_fn)(*t1, *t2);
dealloc(j_fn);
}
break;
}
case LAZY_TABLE_FILTER_INTERPRETED: {
lazy_table_filter_interpreted& src = dynamic_cast<lazy_table_filter_interpreted&>(*m_src);
table_transformer_fn* tr = rm().mk_filter_interpreted_and_project_fn(*src.eval(), src.condition(), m_cols.size(), m_cols.c_ptr());
if (tr) {
verbose_action _t("filter_interpreted_project");
m_table = (*tr)(*src.eval());
dealloc(tr);
}
break;
}
case LAZY_TABLE_FILTER_EQUAL: {
lazy_table_filter_equal& src = dynamic_cast<lazy_table_filter_equal&>(*m_src);
table_base* t = src.eval();
table_transformer_fn* tr = rm().mk_select_equal_and_project_fn(*t, src.value(), src.col());
if (tr) {
verbose_action _t("select_equal_project");
m_table = (*tr)(*t);
dealloc(tr);
}
break;
}
default:
break;
}
if (m_table) {
return m_table.get();
}
table_base* src = m_src->eval();
verbose_action _t("project");
table_transformer_fn* project = rm().mk_project_fn(*src, m_cols.size(), m_cols.c_ptr());
SASSERT(project);
m_table = (*project)(*src);
dealloc(project);
return m_table.get();
}
table_base* lazy_table_rename::force() {
SASSERT(!m_table);
table_base* src = m_src->eval();
verbose_action _t("rename");
table_transformer_fn* rename = rm().mk_rename_fn(*src, m_cols.size(), m_cols.c_ptr());
m_table = (*rename)(*src);
dealloc(rename);
return m_table.get();
}
table_base* lazy_table_filter_identical::force() {
SASSERT(!m_table);
m_table = m_src->eval();
m_src->release_table();
m_src = 0;
verbose_action _t("filter_identical");
table_mutator_fn* m = rm().mk_filter_identical_fn(*m_table, m_cols.size(), m_cols.c_ptr());
SASSERT(m);
(*m)(*m_table);
dealloc(m);
return m_table.get();
}
table_base* lazy_table_filter_equal::force() {
SASSERT(!m_table);
m_table = m_src->eval();
m_src->release_table();
m_src = 0;
verbose_action _t("filter_equal");
table_mutator_fn* m = rm().mk_filter_equal_fn(*m_table, m_value, m_col);
SASSERT(m);
(*m)(*m_table);
dealloc(m);
return m_table.get();
}
table_base* lazy_table_filter_interpreted::force() {
SASSERT(!m_table);
m_table = m_src->eval();
m_src->release_table();
m_src = 0;
verbose_action _t("filter_interpreted");
table_mutator_fn* m = rm().mk_filter_interpreted_fn(*m_table, m_condition);
SASSERT(m);
(*m)(*m_table);
dealloc(m);
return m_table.get();
}
table_base* lazy_table_filter_by_negation::force() {
SASSERT(!m_table);
m_table = m_tgt->eval();
m_tgt->release_table();
m_tgt = 0;
switch(m_src->kind()) {
case LAZY_TABLE_JOIN: {
lazy_table_join& src = dynamic_cast<lazy_table_join&>(*m_src);
table_base* t1 = src.t1()->eval();
table_base* t2 = src.t2()->eval();
verbose_action _t("filter_by_negation_join");
table_intersection_join_filter_fn* jn = rm().mk_filter_by_negated_join_fn(*m_table, *t1, *t2, cols1(), cols2(), src.cols1(), src.cols2());
if (jn) {
(*jn)(*m_table, *t1, *t2);
dealloc(jn);
return m_table.get();
}
break;
}
default:
break;
}
table_base* src = m_src->eval();
verbose_action _t("filter_by_negation");
table_intersection_filter_fn* m = rm().mk_filter_by_negation_fn(*m_table, *src, m_cols1, m_cols2);
SASSERT(m);
(*m)(*m_table, *src);
dealloc(m);
return m_table.get();
}
}

305
src/muz/rel/dl_lazy_table.h Normal file
View file

@ -0,0 +1,305 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
dl_lazy_table.h
Abstract:
Structure for delaying table operations.
Author:
Nikolaj Bjorner (nbjorner) 2013-09-04
Revision History:
--*/
#ifndef _DL_LAZY_TABLE_H_
#define _DL_LAZY_TABLE_H_
#include "dl_base.h"
#include "ref.h"
namespace datalog {
class lazy_table;
class lazy_table_plugin : public table_plugin {
friend class lazy_table;
class join_fn;
class project_fn;
class union_fn;
class rename_fn;
class filter_equal_fn;
class filter_identical_fn;
class filter_interpreted_fn;
class filter_by_negation_fn;
table_plugin& m_plugin;
static symbol mk_name(table_plugin& p);
public:
lazy_table_plugin(table_plugin& p):
table_plugin(mk_name(p), p.get_manager()),
m_plugin(p) {}
virtual bool can_handle_signature(const table_signature & s) {
return m_plugin.can_handle_signature(s);
}
virtual table_base * mk_empty(const table_signature & s);
virtual void set_cancel(bool f) { m_plugin.set_cancel(f); }
static table_plugin* mk_sparse(relation_manager& rm);
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_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_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_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);
static lazy_table const& get(table_base const& tb);
static lazy_table& get(table_base& tb);
static lazy_table const* get(table_base const* tb);
static lazy_table* get(table_base* tb);
};
enum lazy_table_kind {
LAZY_TABLE_BASE,
LAZY_TABLE_JOIN,
LAZY_TABLE_PROJECT,
LAZY_TABLE_RENAME,
LAZY_TABLE_FILTER_IDENTICAL,
LAZY_TABLE_FILTER_EQUAL,
LAZY_TABLE_FILTER_INTERPRETED,
LAZY_TABLE_FILTER_BY_NEGATION
};
class lazy_table_ref {
protected:
lazy_table_plugin& m_plugin;
table_signature m_signature;
unsigned m_ref;
scoped_rel<table_base> m_table;
relation_manager& rm() { return m_plugin.get_manager(); }
virtual table_base* force() = 0;
public:
lazy_table_ref(lazy_table_plugin& p, table_signature const& sig):
m_plugin(p), m_signature(sig), m_ref(0) {}
virtual ~lazy_table_ref() {}
void inc_ref() { ++m_ref; }
void dec_ref() { --m_ref; if (0 == m_ref) dealloc(this); }
void release_table() { m_table.release(); }
virtual lazy_table_kind kind() const = 0;
table_signature const& get_signature() const { return m_signature; }
lazy_table_plugin & get_lplugin() const { return m_plugin; }
table_base* eval() { if (!m_table) { m_table = force(); } SASSERT(m_table); return m_table.get(); }
};
class lazy_table : public table_base {
protected:
mutable ref<lazy_table_ref> m_ref;
public:
lazy_table(lazy_table_ref* t):
table_base(t->get_lplugin(), t->get_signature()),
m_ref(t)
{}
virtual ~lazy_table() {}
lazy_table_plugin& get_lplugin() const {
return dynamic_cast<lazy_table_plugin&>(table_base::get_plugin());
}
virtual table_base * clone() const;
virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const;
virtual bool empty() const;
virtual bool contains_fact(const table_fact & f) const;
virtual void remove_fact(table_element const* fact);
virtual void remove_facts(unsigned fact_cnt, const table_fact * facts);
virtual void remove_facts(unsigned fact_cnt, const table_element * facts);
virtual void reset();
virtual void add_fact(table_fact const& f);
virtual unsigned get_size_estimate_rows() const { return 1; }
virtual unsigned get_size_estimate_bytes() const { return 1; }
virtual bool knows_exact_size() const { return false; }
table_base* eval() const;
virtual table_base::iterator begin() const;
virtual table_base::iterator end() const;
lazy_table_ref* get_ref() const { return m_ref.get(); }
void set(lazy_table_ref* r) { m_ref = r; }
};
class lazy_table_base : public lazy_table_ref {
public:
lazy_table_base(lazy_table_plugin & p, table_base* table)
: lazy_table_ref(p, table->get_signature()) {
m_table = table;
// SASSERT(&p.m_plugin == &table->get_lplugin());
}
virtual ~lazy_table_base() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_BASE; }
virtual table_base* force() { return m_table.get(); }
};
class lazy_table_join : public lazy_table_ref {
unsigned_vector m_cols1;
unsigned_vector m_cols2;
ref<lazy_table_ref> m_t1;
ref<lazy_table_ref> m_t2;
public:
lazy_table_join(unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2,
lazy_table const& t1, lazy_table const& t2, table_signature const& sig)
: lazy_table_ref(t1.get_lplugin(), sig),
m_cols1(col_cnt, cols1),
m_cols2(col_cnt, cols2),
m_t1(t1.get_ref()),
m_t2(t2.get_ref()) { }
virtual ~lazy_table_join() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_JOIN; }
unsigned_vector const& cols1() const { return m_cols1; }
unsigned_vector const& cols2() const { return m_cols2; }
lazy_table_ref* t1() const { return m_t1.get(); }
lazy_table_ref* t2() const { return m_t2.get(); }
virtual table_base* force();
};
class lazy_table_project : public lazy_table_ref {
unsigned_vector m_cols;
ref<lazy_table_ref> m_src;
public:
lazy_table_project(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig)
: lazy_table_ref(src.get_lplugin(), sig),
m_cols(col_cnt, cols),
m_src(src.get_ref()) {}
virtual ~lazy_table_project() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_PROJECT; }
unsigned_vector const& cols() const { return m_cols; }
lazy_table_ref* src() const { return m_src.get(); }
virtual table_base* force();
};
class lazy_table_rename : public lazy_table_ref {
unsigned_vector m_cols;
ref<lazy_table_ref> m_src;
public:
lazy_table_rename(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig)
: lazy_table_ref(src.get_lplugin(), sig),
m_cols(col_cnt, cols),
m_src(src.get_ref()) {}
virtual ~lazy_table_rename() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_RENAME; }
unsigned_vector const& cols() const { return m_cols; }
lazy_table_ref* src() const { return m_src.get(); }
virtual table_base* force();
};
class lazy_table_filter_identical : public lazy_table_ref {
unsigned_vector m_cols;
ref<lazy_table_ref> m_src;
public:
lazy_table_filter_identical(unsigned col_cnt, const unsigned * cols, lazy_table const& src)
: lazy_table_ref(src.get_lplugin(), src.get_signature()), m_cols(col_cnt, cols), m_src(src.get_ref()) {}
virtual ~lazy_table_filter_identical() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_IDENTICAL; }
unsigned_vector const& cols() const { return m_cols; }
lazy_table_ref* src() const { return m_src.get(); }
virtual table_base* force();
};
class lazy_table_filter_equal : public lazy_table_ref {
unsigned m_col;
table_element m_value;
ref<lazy_table_ref> m_src;
public:
lazy_table_filter_equal(unsigned col, table_element value, lazy_table const& src)
: lazy_table_ref(src.get_lplugin(), src.get_signature()),
m_col(col),
m_value(value),
m_src(src.get_ref()) {}
virtual ~lazy_table_filter_equal() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_EQUAL; }
unsigned col() const { return m_col; }
table_element value() const { return m_value; }
lazy_table_ref* src() const { return m_src.get(); }
virtual table_base* force();
};
class lazy_table_filter_interpreted : public lazy_table_ref {
app_ref m_condition;
ref<lazy_table_ref> m_src;
public:
lazy_table_filter_interpreted(lazy_table const& src, app* condition)
: lazy_table_ref(src.get_lplugin(), src.get_signature()),
m_condition(condition, src.get_lplugin().get_ast_manager()), m_src(src.get_ref()) {}
virtual ~lazy_table_filter_interpreted() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_INTERPRETED; }
app* condition() const { return m_condition; }
lazy_table_ref* src() const { return m_src.get(); }
virtual table_base* force();
};
class lazy_table_filter_by_negation : public lazy_table_ref {
ref<lazy_table_ref> m_tgt;
ref<lazy_table_ref> m_src;
unsigned_vector m_cols1;
unsigned_vector m_cols2;
public:
lazy_table_filter_by_negation(lazy_table const& tgt, lazy_table const& src,
unsigned_vector const& c1, unsigned_vector const& c2)
: lazy_table_ref(tgt.get_lplugin(), tgt.get_signature()),
m_tgt(tgt.get_ref()),
m_src(src.get_ref()),
m_cols1(c1),
m_cols2(c2) {}
virtual ~lazy_table_filter_by_negation() {}
virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_BY_NEGATION; }
lazy_table_ref* tgt() const { return m_tgt.get(); }
lazy_table_ref* src() const { return m_src.get(); }
unsigned_vector const& cols1() const { return m_cols1; }
unsigned_vector const& cols2() const { return m_cols2; }
virtual table_base* force();
};
}
#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,701 @@
/*++
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());
}
/**
combined filter by negation with a join.
*/
table_intersection_join_filter_fn* mk_filter_by_negated_join_fn(
const table_base & t,
const table_base & src1,
const table_base & src2,
unsigned_vector const& t_cols,
unsigned_vector const& src_cols,
unsigned_vector const& src1_cols,
unsigned_vector const& src2_cols);
/**
\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,498 @@
/*++
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;
class negated_join_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);
virtual table_intersection_join_filter_fn* mk_filter_by_negated_join_fn(
const table_base & t,
const table_base & src1,
const table_base & src2,
unsigned_vector const& t_cols,
unsigned_vector const& src_cols,
unsigned_vector const& src1_cols,
unsigned_vector const& src2_cols);
static sparse_table const& get(table_base const&);
static sparse_table& get(table_base&);
static sparse_table const* get(table_base const*);
static sparse_table* get(table_base*);
};
class entry_storage {
public:
typedef size_t store_offset;
private:
typedef svector<char, size_t> 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;
size_t 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(size_t sz) {
m_data_size = sz;
if (sz + sizeof(uint64) < sz) {
throw default_exception("overflow resizing data section for sparse table");
}
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;
return
get_manager().relation_signature_to_table(s, tsig) &&
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) {
IF_VERBOSE(1, verbose_stream() << "new type returned\n";);
//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_ */

Some files were not shown because too many files have changed in this diff Show more