mirror of
https://github.com/Z3Prover/z3
synced 2025-12-05 19:42:23 +00:00
merged with unstable
This commit is contained in:
commit
3a0947b3ba
413 changed files with 31618 additions and 17204 deletions
12
src/muz/README
Normal file
12
src/muz/README
Normal 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
|
||||
297
src/muz/base/dl_boogie_proof.cpp
Normal file
297
src/muz/base/dl_boogie_proof.cpp
Normal 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";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
87
src/muz/base/dl_boogie_proof.h
Normal file
87
src/muz/base/dl_boogie_proof.h
Normal 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
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
575
src/muz/base/dl_context.h
Normal 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
160
src/muz/base/dl_costs.cpp
Normal 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
115
src/muz/base/dl_costs.h
Normal 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_ */
|
||||
|
||||
83
src/muz/base/dl_engine_base.h
Normal file
83
src/muz/base/dl_engine_base.h
Normal 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
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
331
src/muz/base/dl_rule.h
Normal 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_ */
|
||||
|
||||
773
src/muz/base/dl_rule_set.cpp
Normal file
773
src/muz/base/dl_rule_set.cpp
Normal 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
282
src/muz/base/dl_rule_set.h
Normal 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_ */
|
||||
|
||||
84
src/muz/base/dl_rule_subsumption_index.cpp
Normal file
84
src/muz/base/dl_rule_subsumption_index.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
65
src/muz/base/dl_rule_subsumption_index.h
Normal file
65
src/muz/base/dl_rule_subsumption_index.h
Normal 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_ */
|
||||
|
||||
159
src/muz/base/dl_rule_transformer.cpp
Normal file
159
src/muz/base/dl_rule_transformer.cpp
Normal 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
117
src/muz/base/dl_rule_transformer.h
Normal file
117
src/muz/base/dl_rule_transformer.h
Normal 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
667
src/muz/base/dl_util.cpp
Normal 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
734
src/muz/base/dl_util.h
Normal 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_ */
|
||||
|
||||
81
src/muz/base/fixedpoint_params.pyg
Normal file
81
src/muz/base/fixedpoint_params.pyg
Normal 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
501
src/muz/base/hnf.cpp
Normal 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
49
src/muz/base/hnf.h
Normal 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_ */
|
||||
612
src/muz/base/proof_utils.cpp
Normal file
612
src/muz/base/proof_utils.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
48
src/muz/base/proof_utils.h
Normal file
48
src/muz/base/proof_utils.h
Normal 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
|
||||
1544
src/muz/bmc/dl_bmc_engine.cpp
Normal file
1544
src/muz/bmc/dl_bmc_engine.cpp
Normal file
File diff suppressed because it is too large
Load diff
82
src/muz/bmc/dl_bmc_engine.h
Normal file
82
src/muz/bmc/dl_bmc_engine.h
Normal 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
242
src/muz/clp/clp_context.cpp
Normal 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
46
src/muz/clp/clp_context.h
Normal 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
|
||||
489
src/muz/duality/duality_dl_interface.cpp
Normal file
489
src/muz/duality/duality_dl_interface.cpp
Normal 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> ¶ms = 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());
|
||||
}
|
||||
}
|
||||
80
src/muz/duality/duality_dl_interface.h
Normal file
80
src/muz/duality/duality_dl_interface.h
Normal 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
|
||||
1508
src/muz/fp/datalog_parser.cpp
Normal file
1508
src/muz/fp/datalog_parser.cpp
Normal file
File diff suppressed because it is too large
Load diff
48
src/muz/fp/datalog_parser.h
Normal file
48
src/muz/fp/datalog_parser.h
Normal 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
479
src/muz/fp/dl_cmds.cpp
Normal 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
37
src/muz/fp/dl_cmds.h
Normal 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
|
||||
54
src/muz/fp/dl_register_engine.cpp
Normal file
54
src/muz/fp/dl_register_engine.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/muz/fp/dl_register_engine.h
Normal file
36
src/muz/fp/dl_register_engine.h
Normal 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
422
src/muz/fp/horn_tactic.cpp
Normal 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
35
src/muz/fp/horn_tactic.h
Normal 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
176
src/muz/pdr/pdr_closure.cpp
Normal 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
67
src/muz/pdr/pdr_closure.h
Normal 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
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
430
src/muz/pdr/pdr_context.h
Normal 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
|
||||
226
src/muz/pdr/pdr_dl_interface.cpp
Normal file
226
src/muz/pdr/pdr_dl_interface.cpp
Normal 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();
|
||||
}
|
||||
82
src/muz/pdr/pdr_dl_interface.h
Normal file
82
src/muz/pdr/pdr_dl_interface.h
Normal 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
|
||||
1015
src/muz/pdr/pdr_farkas_learner.cpp
Normal file
1015
src/muz/pdr/pdr_farkas_learner.cpp
Normal file
File diff suppressed because it is too large
Load diff
128
src/muz/pdr/pdr_farkas_learner.h
Normal file
128
src/muz/pdr/pdr_farkas_learner.h
Normal 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
|
||||
779
src/muz/pdr/pdr_generalizers.cpp
Normal file
779
src/muz/pdr/pdr_generalizers.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
110
src/muz/pdr/pdr_generalizers.h
Normal file
110
src/muz/pdr/pdr_generalizers.h
Normal 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
327
src/muz/pdr/pdr_manager.cpp
Normal 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
306
src/muz/pdr/pdr_manager.h
Normal 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
|
||||
461
src/muz/pdr/pdr_prop_solver.cpp
Normal file
461
src/muz/pdr/pdr_prop_solver.cpp
Normal 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() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
141
src/muz/pdr/pdr_prop_solver.h
Normal file
141
src/muz/pdr/pdr_prop_solver.h
Normal 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
|
||||
127
src/muz/pdr/pdr_reachable_cache.cpp
Normal file
127
src/muz/pdr/pdr_reachable_cache.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
66
src/muz/pdr/pdr_reachable_cache.h
Normal file
66
src/muz/pdr/pdr_reachable_cache.h
Normal 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
|
||||
167
src/muz/pdr/pdr_smt_context_manager.cpp
Normal file
167
src/muz/pdr/pdr_smt_context_manager.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
110
src/muz/pdr/pdr_smt_context_manager.h
Normal file
110
src/muz/pdr/pdr_smt_context_manager.h
Normal 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
600
src/muz/pdr/pdr_sym_mux.cpp
Normal 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
249
src/muz/pdr/pdr_sym_mux.h
Normal 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
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
167
src/muz/pdr/pdr_util.h
Normal 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
|
||||
328
src/muz/rel/aig_exporter.cpp
Normal file
328
src/muz/rel/aig_exporter.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
68
src/muz/rel/aig_exporter.h
Normal file
68
src/muz/rel/aig_exporter.h
Normal 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
490
src/muz/rel/dl_base.cpp
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_base.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include"ast_pp.h"
|
||||
#include"union_find.h"
|
||||
#include"vector.h"
|
||||
#include"dl_context.h"
|
||||
#include"dl_base.h"
|
||||
#include"bool_rewriter.h"
|
||||
#include"dl_relation_manager.h"
|
||||
#include<sstream>
|
||||
|
||||
|
||||
namespace datalog {
|
||||
|
||||
void universal_delete(relation_base* ptr) {
|
||||
ptr->deallocate();
|
||||
}
|
||||
|
||||
void universal_delete(table_base* ptr) {
|
||||
ptr->deallocate();
|
||||
}
|
||||
|
||||
void dealloc_ptr_vector_content(ptr_vector<relation_base> & v) {
|
||||
ptr_vector<relation_base>::iterator it = v.begin();
|
||||
ptr_vector<relation_base>::iterator end = v.end();
|
||||
for(; it!=end; ++it) {
|
||||
(*it)->deallocate();
|
||||
}
|
||||
}
|
||||
|
||||
void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig,
|
||||
expr_ref_vector & renaming_arg) {
|
||||
ast_manager & m = renaming_arg.get_manager();
|
||||
unsigned sz = map.size();
|
||||
unsigned ofs = sz-1;
|
||||
renaming_arg.resize(sz, static_cast<expr *>(0));
|
||||
for(unsigned i=0; i<sz; i++) {
|
||||
if(map[i]!=UINT_MAX) {
|
||||
renaming_arg.set(ofs-i, m.mk_var(map[i], orig_sig[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
context & get_context_from_rel_manager(const relation_manager & rm) {
|
||||
return rm.get_context();
|
||||
}
|
||||
|
||||
ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm) {
|
||||
return rm.get_context().get_manager();
|
||||
}
|
||||
|
||||
#if DL_LEAK_HUNTING
|
||||
void leak_guard_check(const symbol & s) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void relation_signature::output(ast_manager & m, std::ostream & out) const {
|
||||
unsigned sz=size();
|
||||
out<<"(";
|
||||
for(unsigned i=0; i<sz; i++) {
|
||||
if(i) { out<<","; }
|
||||
out << mk_pp((*this)[i], m);
|
||||
}
|
||||
out<<")";
|
||||
}
|
||||
|
||||
|
||||
relation_fact::relation_fact(context & ctx) : app_ref_vector(ctx.get_manager()) {}
|
||||
|
||||
void relation_base::reset() {
|
||||
ast_manager & m = get_plugin().get_ast_manager();
|
||||
app_ref bottom_ref(m.mk_false(), m);
|
||||
scoped_ptr<relation_mutator_fn> reset_fn =
|
||||
get_manager().mk_filter_interpreted_fn(static_cast<relation_base &>(*this), bottom_ref);
|
||||
if(!reset_fn) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
}
|
||||
(*reset_fn)(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void table_signature::from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2, table_signature & result) {
|
||||
result.reset();
|
||||
|
||||
unsigned s1sz=s1.size();
|
||||
unsigned s2sz=s2.size();
|
||||
unsigned s1first_func=s1sz-s1.functional_columns();
|
||||
unsigned s2first_func=s2sz-s2.functional_columns();
|
||||
for(unsigned i=0; i<s1first_func; i++) {
|
||||
result.push_back(s1[i]);
|
||||
}
|
||||
for(unsigned i=0; i<s2first_func; i++) {
|
||||
result.push_back(s2[i]);
|
||||
}
|
||||
for(unsigned i=s1first_func; i<s1sz; i++) {
|
||||
result.push_back(s1[i]);
|
||||
}
|
||||
for(unsigned i=s2first_func; i<s2sz; i++) {
|
||||
result.push_back(s2[i]);
|
||||
}
|
||||
result.set_functional_columns(s1.functional_columns()+s2.functional_columns());
|
||||
}
|
||||
|
||||
void table_signature::from_project(const table_signature & src, unsigned col_cnt,
|
||||
const unsigned * removed_cols, table_signature & result) {
|
||||
signature_base::from_project(src, col_cnt, removed_cols, result);
|
||||
|
||||
unsigned func_cnt = src.functional_columns();
|
||||
|
||||
if(removed_cols==0) {
|
||||
result.set_functional_columns(func_cnt);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned first_src_fun = src.first_functional();
|
||||
if(removed_cols[0]<first_src_fun) {
|
||||
//if we remove at least one non-functional column, all the columns in the result are non-functional
|
||||
result.set_functional_columns(0);
|
||||
}
|
||||
else {
|
||||
//all columns we are removing are functional
|
||||
SASSERT(func_cnt>=col_cnt);
|
||||
result.set_functional_columns(func_cnt-col_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
void table_signature::from_project_with_reduce(const table_signature & src, unsigned col_cnt,
|
||||
const unsigned * removed_cols, table_signature & result) {
|
||||
signature_base::from_project(src, col_cnt, removed_cols, result);
|
||||
|
||||
unsigned remaining_fun = src.functional_columns();
|
||||
unsigned first_src_fun = src.first_functional();
|
||||
for(int i=col_cnt-1; i>=0; i--) {
|
||||
if(removed_cols[i]<first_src_fun) {
|
||||
break;
|
||||
}
|
||||
remaining_fun--;
|
||||
}
|
||||
result.set_functional_columns(remaining_fun);
|
||||
}
|
||||
|
||||
void table_signature::from_join_project(const table_signature & s1, const table_signature & s2,
|
||||
unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols, table_signature & result) {
|
||||
table_signature aux;
|
||||
from_join(s1, s2, joined_col_cnt, cols1, cols2, aux);
|
||||
|
||||
//after the join the column order is
|
||||
//(non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2)
|
||||
|
||||
if(s1.functional_columns()==0 && s2.functional_columns()==0) {
|
||||
from_project(aux, removed_col_cnt, removed_cols, result);
|
||||
SASSERT(result.functional_columns()==0);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned join_sig_sz = s1.size()+s2.size();
|
||||
unsigned s1_first_func = s1.first_functional();
|
||||
unsigned s2_first_func = s2.first_functional();
|
||||
unsigned second_ofs = s1_first_func;
|
||||
unsigned first_func_ofs = second_ofs + s2_first_func;
|
||||
unsigned second_func_ofs = second_ofs + s1.functional_columns();
|
||||
|
||||
svector<unsigned> remaining_in_equivalence_class;
|
||||
remaining_in_equivalence_class.resize(join_sig_sz, 0);
|
||||
bool merging_rows_can_happen = false;
|
||||
|
||||
union_find_default_ctx uf_ctx;
|
||||
union_find<> uf(uf_ctx); //the numbers in uf correspond to column indexes after the join
|
||||
for(unsigned i=0; i<join_sig_sz; i++) {
|
||||
unsigned v = uf.mk_var();
|
||||
SASSERT(v==i);
|
||||
}
|
||||
|
||||
for(unsigned i=0; i<joined_col_cnt; i++) {
|
||||
unsigned idx1 = (s1_first_func>cols1[i]) ? cols1[i] : (first_func_ofs+cols1[i]-s1_first_func);
|
||||
unsigned idx2 = (s2_first_func>cols2[i]) ? (second_ofs+cols2[i]) : (second_func_ofs+cols2[i]-s2_first_func);
|
||||
uf.merge(idx1, idx2);
|
||||
}
|
||||
for(unsigned i=0; i<first_func_ofs; i++) { //we only count the non-functional columns
|
||||
remaining_in_equivalence_class[uf.find(i)]++;
|
||||
}
|
||||
|
||||
for(unsigned i=0; i<removed_col_cnt; i++) {
|
||||
unsigned rc = removed_cols[i];
|
||||
if(rc>=first_func_ofs) {
|
||||
//removing functional columns won't make us merge rows
|
||||
continue;
|
||||
}
|
||||
unsigned eq_class_idx = uf.find(rc);
|
||||
if(remaining_in_equivalence_class[eq_class_idx]>1) {
|
||||
remaining_in_equivalence_class[eq_class_idx]--;
|
||||
}
|
||||
else {
|
||||
merging_rows_can_happen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(merging_rows_can_happen) {
|
||||
//this one marks all columns as non-functional
|
||||
from_project(aux, removed_col_cnt, removed_cols, result);
|
||||
SASSERT(result.functional_columns()==0);
|
||||
}
|
||||
else {
|
||||
//this one preserves columns to be functional
|
||||
from_project_with_reduce(aux, removed_col_cnt, removed_cols, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// table_base
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
//here we give generic implementation of table operations using iterators
|
||||
|
||||
bool table_base::empty() const {
|
||||
return begin()==end();
|
||||
}
|
||||
|
||||
void table_base::remove_facts(unsigned fact_cnt, const table_fact * facts) {
|
||||
for(unsigned i=0; i<fact_cnt; i++) {
|
||||
remove_fact(facts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void table_base::remove_facts(unsigned fact_cnt, const table_element * facts) {
|
||||
for(unsigned i=0; i<fact_cnt; i++) {
|
||||
remove_fact(facts + i*get_signature().size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void table_base::reset() {
|
||||
vector<table_fact> to_remove;
|
||||
table_base::iterator it = begin();
|
||||
table_base::iterator iend = end();
|
||||
table_fact row;
|
||||
for(; it!=iend; ++it) {
|
||||
it->get_fact(row);
|
||||
to_remove.push_back(row);
|
||||
}
|
||||
remove_facts(to_remove.size(), to_remove.c_ptr());
|
||||
}
|
||||
|
||||
bool table_base::contains_fact(const table_fact & f) const {
|
||||
iterator it = begin();
|
||||
iterator iend = end();
|
||||
|
||||
table_fact row;
|
||||
|
||||
for(; it!=iend; ++it) {
|
||||
it->get_fact(row);
|
||||
if(vectors_equal(row, f)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool table_base::fetch_fact(table_fact & f) const {
|
||||
if(get_signature().functional_columns()==0) {
|
||||
return contains_fact(f);
|
||||
}
|
||||
else {
|
||||
unsigned sig_sz = get_signature().size();
|
||||
unsigned non_func_cnt = sig_sz-get_signature().functional_columns();
|
||||
table_base::iterator it = begin();
|
||||
table_base::iterator iend = end();
|
||||
table_fact row;
|
||||
for(; it!=iend; ++it) {
|
||||
it->get_fact(row);
|
||||
bool differs = false;
|
||||
for(unsigned i=0; i<non_func_cnt; i++) {
|
||||
if(row[i]!=f[i]) {
|
||||
differs = true;
|
||||
}
|
||||
}
|
||||
if(differs) {
|
||||
continue;
|
||||
}
|
||||
for(unsigned i=non_func_cnt; i<sig_sz; i++) {
|
||||
f[i]=row[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool table_base::suggest_fact(table_fact & f) {
|
||||
if(get_signature().functional_columns()==0) {
|
||||
if(contains_fact(f)) {
|
||||
return false;
|
||||
}
|
||||
add_new_fact(f);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if(fetch_fact(f)) {
|
||||
return false;
|
||||
}
|
||||
add_new_fact(f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void table_base::ensure_fact(const table_fact & f) {
|
||||
if(get_signature().functional_columns()==0) {
|
||||
add_fact(f);
|
||||
}
|
||||
else {
|
||||
remove_fact(f);
|
||||
add_fact(f);
|
||||
}
|
||||
}
|
||||
|
||||
table_base * table_base::clone() const {
|
||||
table_base * res = get_plugin().mk_empty(get_signature());
|
||||
|
||||
iterator it = begin();
|
||||
iterator iend = end();
|
||||
|
||||
table_fact row;
|
||||
|
||||
for(; it!=iend; ++it) {
|
||||
it->get_fact(row);
|
||||
res->add_new_fact(row);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Default method for complementation.
|
||||
|
||||
It assumes that the compiler creates only tables with
|
||||
at most one column (0 or 1 columns).
|
||||
Complementation of tables with more than one columns
|
||||
is transformed into a cross product of complements and/or
|
||||
difference.
|
||||
|
||||
*/
|
||||
table_base * table_base::complement(func_decl* p, const table_element * func_columns) const {
|
||||
const table_signature & sig = get_signature();
|
||||
SASSERT(sig.functional_columns()==0 || func_columns!=0);
|
||||
SASSERT(sig.first_functional() <= 1);
|
||||
|
||||
table_base * res = get_plugin().mk_empty(sig);
|
||||
|
||||
table_fact fact;
|
||||
fact.resize(sig.first_functional());
|
||||
fact.append(sig.functional_columns(), func_columns);
|
||||
|
||||
if (sig.first_functional() == 0) {
|
||||
if (empty()) {
|
||||
res->add_fact(fact);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
VERIFY(sig.first_functional() == 1);
|
||||
|
||||
uint64 upper_bound = get_signature()[0];
|
||||
bool empty_table = empty();
|
||||
|
||||
if (upper_bound > (1 << 18)) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "creating large table of size " << upper_bound;
|
||||
if (p) buffer << " for relation " << p->get_name();
|
||||
warning_msg(buffer.str().c_str());
|
||||
}
|
||||
|
||||
for(table_element i = 0; i < upper_bound; i++) {
|
||||
fact[0] = i;
|
||||
if(empty_table || !contains_fact(fact)) {
|
||||
res->add_fact(fact);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void table_base::display(std::ostream & out) const {
|
||||
out << "table with signature ";
|
||||
print_container(get_signature(), out);
|
||||
out << ":\n";
|
||||
|
||||
iterator it = begin();
|
||||
iterator iend = end();
|
||||
for(; it!=iend; ++it) {
|
||||
const row_interface & r = *it;
|
||||
r.display(out);
|
||||
}
|
||||
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
|
||||
class table_base::row_interface::fact_row_iterator : public table_base::row_iterator_core {
|
||||
const row_interface & m_parent;
|
||||
unsigned m_index;
|
||||
protected:
|
||||
virtual bool is_finished() const { return m_index==m_parent.size(); }
|
||||
public:
|
||||
fact_row_iterator(const row_interface & row, bool finished)
|
||||
: m_parent(row), m_index(finished ? row.size() : 0) {}
|
||||
|
||||
virtual table_element operator*() {
|
||||
SASSERT(!is_finished());
|
||||
return m_parent[m_index];
|
||||
}
|
||||
|
||||
virtual void operator++() {
|
||||
m_index++;
|
||||
SASSERT(m_index<=m_parent.size());
|
||||
}
|
||||
};
|
||||
|
||||
table_base::row_iterator table_base::row_interface::begin() const {
|
||||
return row_iterator(alloc(fact_row_iterator, *this, false));
|
||||
}
|
||||
table_base::row_iterator table_base::row_interface::end() const {
|
||||
return row_iterator(alloc(fact_row_iterator, *this, true));
|
||||
}
|
||||
|
||||
void table_base::row_interface::get_fact(table_fact & result) const {
|
||||
result.reset();
|
||||
unsigned n=size();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
result.push_back((*this)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void table_base::row_interface::display(std::ostream & out) const {
|
||||
table_fact fact;
|
||||
get_fact(fact);
|
||||
print_container(fact, out);
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
void table_base::to_formula(relation_signature const& sig, expr_ref& fml) const {
|
||||
// iterate over rows and build disjunction
|
||||
ast_manager & m = fml.get_manager();
|
||||
expr_ref_vector disjs(m);
|
||||
expr_ref_vector conjs(m);
|
||||
dl_decl_util util(m);
|
||||
bool_rewriter brw(m);
|
||||
table_fact fact;
|
||||
iterator it = begin();
|
||||
iterator iend = end();
|
||||
for(; it != iend; ++it) {
|
||||
const row_interface & r = *it;
|
||||
r.get_fact(fact);
|
||||
conjs.reset();
|
||||
for (unsigned i = 0; i < fact.size(); ++i) {
|
||||
conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i])));
|
||||
}
|
||||
brw.mk_and(conjs.size(), conjs.c_ptr(), fml);
|
||||
disjs.push_back(fml);
|
||||
}
|
||||
brw.mk_or(disjs.size(), disjs.c_ptr(), fml);
|
||||
}
|
||||
|
||||
}
|
||||
1275
src/muz/rel/dl_base.h
Normal file
1275
src/muz/rel/dl_base.h
Normal file
File diff suppressed because it is too large
Load diff
707
src/muz/rel/dl_bound_relation.cpp
Normal file
707
src/muz/rel/dl_bound_relation.cpp
Normal file
|
|
@ -0,0 +1,707 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_bound_relation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic (strict upper) bound relation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-2-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "dl_bound_relation.h"
|
||||
#include "debug.h"
|
||||
#include "ast_pp.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
bound_relation_plugin::bound_relation_plugin(relation_manager& m):
|
||||
relation_plugin(bound_relation_plugin::get_name(), m),
|
||||
m_arith(get_ast_manager()),
|
||||
m_bsimp(get_ast_manager()) {
|
||||
}
|
||||
|
||||
bool bound_relation_plugin::can_handle_signature(const relation_signature & sig) {
|
||||
for (unsigned i = 0; i < sig.size(); ++i) {
|
||||
if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bound_relation& bound_relation_plugin::get(relation_base& r) {
|
||||
return dynamic_cast<bound_relation&>(r);
|
||||
}
|
||||
|
||||
bound_relation const & bound_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<bound_relation const&>(r);
|
||||
}
|
||||
|
||||
bound_relation* bound_relation_plugin::get(relation_base* r) {
|
||||
return dynamic_cast<bound_relation*>(r);
|
||||
}
|
||||
|
||||
bool bound_relation_plugin::is_interval_relation(relation_base const& r) {
|
||||
return symbol("interval_relation") == r.get_plugin().get_name();
|
||||
}
|
||||
|
||||
interval_relation& bound_relation_plugin::get_interval_relation(relation_base& r) {
|
||||
SASSERT(is_interval_relation(r));
|
||||
return dynamic_cast<interval_relation&>(r);
|
||||
}
|
||||
|
||||
interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) {
|
||||
SASSERT(is_interval_relation(r));
|
||||
return dynamic_cast<interval_relation const&>(r);
|
||||
}
|
||||
|
||||
relation_base * bound_relation_plugin::mk_empty(const relation_signature & s) {
|
||||
return alloc(bound_relation, *this, s, true);
|
||||
}
|
||||
|
||||
relation_base * bound_relation_plugin::mk_full(func_decl* p, const relation_signature & s) {
|
||||
return alloc(bound_relation, *this, s, false);
|
||||
}
|
||||
|
||||
class bound_relation_plugin::join_fn : public convenient_relation_join_fn {
|
||||
public:
|
||||
join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2)
|
||||
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) {
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) {
|
||||
bound_relation const& r1 = get(_r1);
|
||||
bound_relation const& r2 = get(_r2);
|
||||
bound_relation_plugin& p = r1.get_plugin();
|
||||
bound_relation* result = dynamic_cast<bound_relation*>(p.mk_full(0, get_result_signature()));
|
||||
result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if (!check_kind(r1) || !check_kind(r2)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2);
|
||||
}
|
||||
|
||||
|
||||
class bound_relation_plugin::project_fn : public convenient_relation_project_fn {
|
||||
public:
|
||||
project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
|
||||
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) {
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & _r) {
|
||||
bound_relation const& r = get(_r);
|
||||
bound_relation_plugin& p = r.get_plugin();
|
||||
bound_relation* result = get(p.mk_full(0, get_result_signature()));
|
||||
result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * bound_relation_plugin::mk_project_fn(const relation_base & r,
|
||||
unsigned col_cnt, const unsigned * removed_cols) {
|
||||
return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
class bound_relation_plugin::rename_fn : public convenient_relation_rename_fn {
|
||||
public:
|
||||
rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
|
||||
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & _r) {
|
||||
bound_relation const& r = get(_r);
|
||||
bound_relation_plugin& p = r.get_plugin();
|
||||
bound_relation* result = get(p.mk_full(0, get_result_signature()));
|
||||
result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * bound_relation_plugin::mk_rename_fn(const relation_base & r,
|
||||
unsigned cycle_len, const unsigned * permutation_cycle) {
|
||||
if(check_kind(r)) {
|
||||
return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
class bound_relation_plugin::union_fn : public relation_union_fn {
|
||||
bool m_is_widen;
|
||||
public:
|
||||
union_fn(bool is_widen) :
|
||||
m_is_widen(is_widen) {
|
||||
}
|
||||
virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) {
|
||||
TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n"););
|
||||
get(_r).mk_union(get(_src), get(_delta), m_is_widen);
|
||||
}
|
||||
};
|
||||
|
||||
class bound_relation_plugin::union_fn_i : public relation_union_fn {
|
||||
bool m_is_widen;
|
||||
public:
|
||||
union_fn_i(bool is_widen) :
|
||||
m_is_widen(is_widen) {
|
||||
}
|
||||
virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) {
|
||||
TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n"););
|
||||
get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen);
|
||||
TRACE("bound_relation", _r.display(tout << "dst':\n"););
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
relation_union_fn * bound_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) {
|
||||
return alloc(union_fn_i, false);
|
||||
}
|
||||
if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) {
|
||||
return alloc(union_fn, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
relation_union_fn * bound_relation_plugin::mk_widen_fn(
|
||||
const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) {
|
||||
return alloc(union_fn_i, true);
|
||||
}
|
||||
if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) {
|
||||
return alloc(union_fn, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn {
|
||||
unsigned_vector m_cols;
|
||||
public:
|
||||
filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols)
|
||||
: m_cols(col_cnt, identical_cols) {}
|
||||
|
||||
virtual void operator()(relation_base & r) {
|
||||
for (unsigned i = 1; i < m_cols.size(); ++i) {
|
||||
get(r).equate(m_cols[0], m_cols[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * bound_relation_plugin::mk_filter_identical_fn(
|
||||
const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) {
|
||||
if(check_kind(t)) {
|
||||
return alloc(filter_identical_fn, col_cnt, identical_cols);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn {
|
||||
public:
|
||||
filter_equal_fn(relation_element const& value, unsigned col) {}
|
||||
|
||||
virtual void operator()(relation_base & r) { }
|
||||
};
|
||||
|
||||
relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r,
|
||||
const relation_element & value, unsigned col) {
|
||||
if (check_kind(r)) {
|
||||
return alloc(filter_equal_fn, value, col);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
|
||||
enum kind_t { NOT_APPLICABLE, EQ_VAR, EQ_SUB, LT_VAR, LE_VAR, K_FALSE };
|
||||
app_ref m_cond;
|
||||
app_ref m_lt;
|
||||
arith_util m_arith;
|
||||
interval_relation* m_interval;
|
||||
unsigned_vector m_vars;
|
||||
kind_t m_kind;
|
||||
|
||||
unsigned get_var(expr* a) {
|
||||
SASSERT(is_var(a));
|
||||
return to_var(a)->get_idx();
|
||||
}
|
||||
|
||||
// x = z - y
|
||||
void mk_sub_eq(expr* x, expr* z, expr* y) {
|
||||
SASSERT(is_var(x));
|
||||
SASSERT(is_var(z));
|
||||
SASSERT(is_var(y));
|
||||
m_vars.push_back(get_var(x));
|
||||
m_vars.push_back(get_var(z));
|
||||
m_vars.push_back(get_var(y));
|
||||
m_kind = EQ_SUB;
|
||||
}
|
||||
|
||||
void mk_lt(expr* l, expr* r) {
|
||||
SASSERT(is_var(l));
|
||||
SASSERT(is_var(r));
|
||||
m_vars.push_back(get_var(l));
|
||||
m_vars.push_back(get_var(r));
|
||||
m_lt = m_arith.mk_lt(l, r);
|
||||
m_kind = LT_VAR;
|
||||
}
|
||||
|
||||
|
||||
void mk_le(expr* l, expr* r) {
|
||||
SASSERT(is_var(l));
|
||||
SASSERT(is_var(r));
|
||||
m_vars.push_back(get_var(l));
|
||||
m_vars.push_back(get_var(r));
|
||||
m_kind = LE_VAR;
|
||||
}
|
||||
|
||||
void mk_eq(expr* l, expr* r) {
|
||||
m_vars.push_back(get_var(l));
|
||||
m_vars.push_back(get_var(r));
|
||||
m_kind = EQ_VAR;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
filter_interpreted_fn(ast_manager& m, app* cond) :
|
||||
m_cond(cond, m),
|
||||
m_lt(m), m_arith(m), m_interval(0), m_kind(NOT_APPLICABLE) {
|
||||
expr* l, *r, *r1, *r2, *c2;
|
||||
rational n1;
|
||||
if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) &&
|
||||
is_var(l) && is_var(r)) {
|
||||
mk_lt(l, r);
|
||||
}
|
||||
else if (m.is_not(cond, c2) &&
|
||||
(m_arith.is_ge(c2, l, r) || m_arith.is_le(c2, r, l)) &&
|
||||
is_var(l) && is_var(r)) {
|
||||
mk_lt(l, r);
|
||||
}
|
||||
else if ((m_arith.is_le(cond, l, r) || m_arith.is_ge(cond, r, l)) &&
|
||||
is_var(l) && is_var(r)) {
|
||||
mk_le(l, r);
|
||||
}
|
||||
else if (m.is_not(cond, c2) &&
|
||||
(m_arith.is_gt(c2, l, r) || m_arith.is_lt(c2, r, l)) &&
|
||||
is_var(l) && is_var(r)) {
|
||||
mk_le(l, r);
|
||||
}
|
||||
else if (m.is_false(cond)) {
|
||||
m_kind = K_FALSE;
|
||||
}
|
||||
else if (m.is_eq(cond, l, r) && is_var(l) && is_var(r)) {
|
||||
mk_eq(l, r);
|
||||
}
|
||||
else if (m.is_eq(cond, l, r) &&
|
||||
m_arith.is_sub(r, r1, r2) &&
|
||||
is_var(l) && is_var(r1) && is_var(r2)) {
|
||||
mk_sub_eq(l, r1, r2);
|
||||
}
|
||||
else if (m.is_eq(cond, l, r) &&
|
||||
m_arith.is_sub(l, r1, r2) &&
|
||||
is_var(r) && is_var(r1) && is_var(r2)) {
|
||||
mk_sub_eq(r, r1, r2);
|
||||
}
|
||||
else if (m.is_eq(cond, l, r) &&
|
||||
m_arith.is_add(r, r1, r2) &&
|
||||
m_arith.is_numeral(r1, n1) &&
|
||||
n1.is_pos() && is_var(l) && is_var(r2)) {
|
||||
mk_lt(r2, l);
|
||||
}
|
||||
else if (m.is_eq(cond, l, r) &&
|
||||
m_arith.is_add(r, r1, r2) &&
|
||||
m_arith.is_numeral(r2, n1) &&
|
||||
n1.is_pos() && is_var(l) && is_var(r1)) {
|
||||
mk_lt(r1, l);
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// x = z - y
|
||||
// x = y
|
||||
// x < y
|
||||
// x <= y
|
||||
// x < y + z
|
||||
//
|
||||
|
||||
void operator()(relation_base& t) {
|
||||
TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout););
|
||||
bound_relation& r = get(t);
|
||||
switch(m_kind) {
|
||||
case K_FALSE:
|
||||
r.set_empty();
|
||||
break;
|
||||
case NOT_APPLICABLE:
|
||||
break;
|
||||
case EQ_VAR:
|
||||
r.equate(m_vars[0], m_vars[1]);
|
||||
break;
|
||||
case EQ_SUB:
|
||||
// TBD
|
||||
break;
|
||||
case LT_VAR:
|
||||
r.mk_lt(m_vars[0], m_vars[1]);
|
||||
break;
|
||||
case LE_VAR:
|
||||
r.mk_le(m_vars[0], m_vars[1]);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
TRACE("dl", t.display(tout << "result\n"););
|
||||
}
|
||||
|
||||
bool supports_attachment(relation_base& t) {
|
||||
return is_interval_relation(t);
|
||||
}
|
||||
|
||||
void attach(relation_base& t) {
|
||||
SASSERT(is_interval_relation(t));
|
||||
interval_relation& r = get_interval_relation(t);
|
||||
m_interval = &r;
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * bound_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) {
|
||||
return alloc(filter_interpreted_fn, t.get_plugin().get_ast_manager(), condition);
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// bound_relation
|
||||
|
||||
void bound_relation_helper::mk_project_t(uint_set2& t, unsigned_vector const& renaming) {
|
||||
if (t.lt.empty() && t.le.empty()) {
|
||||
return;
|
||||
}
|
||||
uint_set::iterator it = t.lt.begin(), end = t.lt.end();
|
||||
unsigned_vector ltv, lev;
|
||||
for (; it != end; ++it) {
|
||||
ltv.push_back(renaming[*it]);
|
||||
}
|
||||
it = t.le.begin(), end = t.le.end();
|
||||
for (; it != end; ++it) {
|
||||
lev.push_back(renaming[*it]);
|
||||
}
|
||||
TRACE("dl",
|
||||
tout << "project: ";
|
||||
for (unsigned i = 0; i < renaming.size(); ++i)
|
||||
if (renaming[i] == UINT_MAX) tout << i << " ";
|
||||
tout << ": ";
|
||||
it = t.lt.begin(); end = t.lt.end();
|
||||
for (; it != end; ++it) tout << *it << " ";
|
||||
tout << " le ";
|
||||
it = t.le.begin(); end = t.le.end();
|
||||
for (; it != end; ++it) tout << *it << " ";
|
||||
tout << " => ";
|
||||
for (unsigned i = 0; i < ltv.size(); ++i) tout << ltv[i] << " ";
|
||||
tout << " le ";
|
||||
for (unsigned i = 0; i < lev.size(); ++i) tout << lev[i] << " ";
|
||||
tout << "\n";);
|
||||
t.lt.reset();
|
||||
for (unsigned i = 0; i < ltv.size(); ++i) {
|
||||
t.lt.insert(ltv[i]);
|
||||
}
|
||||
t.le.reset();
|
||||
for (unsigned i = 0; i < lev.size(); ++i) {
|
||||
t.le.insert(lev[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bound_relation::bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty):
|
||||
vector_relation<uint_set2, bound_relation_helper>(p, s, is_empty, uint_set2())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
uint_set2 bound_relation::mk_intersect(uint_set2 const& t1, uint_set2 const& t2, bool& is_empty) const {
|
||||
is_empty = false;
|
||||
uint_set2 r(t1);
|
||||
r.lt |= t2.lt;
|
||||
r.le |= t2.le;
|
||||
return r;
|
||||
}
|
||||
|
||||
uint_set2 bound_relation::mk_widen(uint_set2 const& t1, uint_set2 const& t2) const {
|
||||
return mk_unite(t1, t2);
|
||||
}
|
||||
|
||||
uint_set2 bound_relation::mk_unite(uint_set2 const& t1, uint_set2 const& t2) const {
|
||||
uint_set2 s1(t1);
|
||||
s1.lt &= t2.lt;
|
||||
s1.le &= t2.le;
|
||||
return s1;
|
||||
}
|
||||
|
||||
uint_set2 bound_relation::mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, uint_set2 const& t) const {
|
||||
unsigned sz = old_eqs.get_num_vars();
|
||||
SASSERT(sz == new_eqs.get_num_vars());
|
||||
uint_set2 result;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (t.lt.contains(i)) {
|
||||
unsigned j = i;
|
||||
do {
|
||||
result.lt.insert(new_eqs.find(j));
|
||||
j = old_eqs.next(j);
|
||||
}
|
||||
while (j != i);
|
||||
}
|
||||
if (t.le.contains(i)) {
|
||||
unsigned j = i;
|
||||
do {
|
||||
result.le.insert(new_eqs.find(j));
|
||||
j = old_eqs.next(j);
|
||||
}
|
||||
while (j != i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bound_relation::is_subset_of(uint_set2 const& t1, uint_set2 const& t2) const {
|
||||
uint_set2 s1, s2;
|
||||
normalize(t1, s1);
|
||||
normalize(t2, s2);
|
||||
return s1.lt.subset_of(s2.lt) && s1.le.subset_of(s2.le);
|
||||
}
|
||||
|
||||
void bound_relation::mk_rename_elem(uint_set2& t, unsigned col_cnt, unsigned const* cycle) {
|
||||
// [ 0 -> 2 -> 3 -> 0]
|
||||
if (col_cnt == 0) return;
|
||||
unsigned col1, col2;
|
||||
col1 = find(cycle[0]);
|
||||
col2 = find(cycle[col_cnt-1]);
|
||||
bool has_col2_lt = t.lt.contains(col2);
|
||||
t.lt.remove(col2);
|
||||
bool has_col2_le = t.le.contains(col2);
|
||||
t.le.remove(col2);
|
||||
for (unsigned i = 0; i + 1 < col_cnt; ++i) {
|
||||
col1 = find(cycle[i]);
|
||||
col2 = find(cycle[i+1]);
|
||||
if (t.lt.contains(col1)) {
|
||||
t.lt.remove(col1);
|
||||
t.lt.insert(col2);
|
||||
}
|
||||
if (t.le.contains(col1)) {
|
||||
t.le.remove(col1);
|
||||
t.le.insert(col2);
|
||||
}
|
||||
}
|
||||
if (has_col2_lt) {
|
||||
col1 = find(cycle[0]);
|
||||
t.lt.insert(col1);
|
||||
}
|
||||
if (has_col2_le) {
|
||||
col1 = find(cycle[0]);
|
||||
t.le.insert(col1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool bound_relation::is_full(uint_set2 const& t) const {
|
||||
return t.lt.empty() && t.le.empty();
|
||||
}
|
||||
|
||||
bool bound_relation::is_empty(unsigned index, uint_set2 const& t) const {
|
||||
return t.lt.contains(find(index)) || t.le.contains(find(index));
|
||||
}
|
||||
|
||||
void bound_relation::normalize(uint_set const& src, uint_set& dst) const {
|
||||
uint_set::iterator it = src.begin(), end = src.end();
|
||||
for (; it != end; ++it) {
|
||||
dst.insert(find(*it));
|
||||
}
|
||||
}
|
||||
void bound_relation::normalize(uint_set2 const& src, uint_set2& dst) const {
|
||||
normalize(src.lt, dst.lt);
|
||||
normalize(src.le, dst.le);
|
||||
}
|
||||
|
||||
|
||||
void bound_relation::mk_lt(unsigned i) {
|
||||
uint_set2& dst = (*this)[i];
|
||||
while (!m_todo.empty()) {
|
||||
unsigned j = m_todo.back().first;
|
||||
bool strict = m_todo.back().second;
|
||||
if (i == j && strict) {
|
||||
m_todo.reset();
|
||||
m_empty = true;
|
||||
return;
|
||||
}
|
||||
m_todo.pop_back();
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
uint_set2& src = (*m_elems)[j];
|
||||
uint_set::iterator it = src.lt.begin(), end = src.lt.end();
|
||||
for(; it != end; ++it) {
|
||||
m_todo.push_back(std::make_pair(*it, true));
|
||||
}
|
||||
it = src.le.begin(), end = src.le.end();
|
||||
for(; it != end; ++it) {
|
||||
m_todo.push_back(std::make_pair(*it, strict));
|
||||
}
|
||||
if (strict) {
|
||||
dst.lt.insert(j);
|
||||
}
|
||||
else {
|
||||
dst.le.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_relation::mk_lt(unsigned i, unsigned j) {
|
||||
m_todo.reset();
|
||||
i = find(i);
|
||||
m_todo.push_back(std::make_pair(find(j), true));
|
||||
mk_lt(i);
|
||||
}
|
||||
|
||||
void bound_relation::mk_le(unsigned i, unsigned j) {
|
||||
m_todo.reset();
|
||||
i = find(i);
|
||||
m_todo.push_back(std::make_pair(find(j), false));
|
||||
mk_lt(i);
|
||||
}
|
||||
|
||||
bool bound_relation::is_lt(unsigned i, unsigned j) const {
|
||||
return (*this)[i].lt.contains(find(j));
|
||||
}
|
||||
|
||||
void bound_relation::add_fact(const relation_fact & f) {
|
||||
bound_relation r(get_plugin(), get_signature(), false);
|
||||
for (unsigned i = 0; i < f.size(); ++i) {
|
||||
scoped_ptr<relation_mutator_fn> fe = get_plugin().mk_filter_equal_fn(r, f[i], i);
|
||||
(*fe)(r);
|
||||
}
|
||||
mk_union(r, 0, false);
|
||||
}
|
||||
|
||||
bool bound_relation::contains_fact(const relation_fact & f) const {
|
||||
if (empty()) {
|
||||
return false;
|
||||
}
|
||||
// this is a very rough approximation.
|
||||
return true;
|
||||
}
|
||||
|
||||
bound_relation * bound_relation::clone() const {
|
||||
bound_relation* result = 0;
|
||||
if (empty()) {
|
||||
result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature()));
|
||||
}
|
||||
else {
|
||||
result = bound_relation_plugin::get(get_plugin().mk_full(0, get_signature()));
|
||||
result->copy(*this);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void bound_relation::mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen) {
|
||||
unsigned size = get_signature().size();
|
||||
for (unsigned i = 0; i < size; ++i) {
|
||||
if (find(i) != i) {
|
||||
continue;
|
||||
}
|
||||
uint_set2& s = (*this)[i];
|
||||
ext_numeral const& lo = src[i].sup();
|
||||
if (lo.is_infinite()) {
|
||||
s.lt.reset();
|
||||
s.le.reset();
|
||||
continue;
|
||||
}
|
||||
uint_set::iterator it = s.lt.begin(), end = s.lt.end();
|
||||
for(; it != end; ++it) {
|
||||
ext_numeral const& hi = src[*it].inf();
|
||||
if (hi.is_infinite() || lo.to_rational() >= hi.to_rational()) {
|
||||
s.lt.remove(*it);
|
||||
}
|
||||
}
|
||||
it = s.le.begin(), end = s.le.end();
|
||||
for(; it != end; ++it) {
|
||||
ext_numeral const& hi = src[*it].inf();
|
||||
if (hi.is_infinite() || lo.to_rational() > hi.to_rational()) {
|
||||
s.le.remove(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bound_relation * bound_relation::complement(func_decl* p) const {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bound_relation::to_formula(expr_ref& fml) const {
|
||||
ast_manager& m = get_plugin().get_ast_manager();
|
||||
arith_util& arith = get_plugin().m_arith;
|
||||
basic_simplifier_plugin& bsimp = get_plugin().m_bsimp;
|
||||
expr_ref_vector conjs(m);
|
||||
relation_signature const& sig = get_signature();
|
||||
for (unsigned i = 0; i < sig.size(); ++i) {
|
||||
if (i != find(i)) {
|
||||
conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)])));
|
||||
continue;
|
||||
}
|
||||
uint_set2 const& upper = (*this)[i];
|
||||
uint_set::iterator it = upper.lt.begin(), end = upper.lt.end();
|
||||
for (; it != end; ++it) {
|
||||
conjs.push_back(arith.mk_lt(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it])));
|
||||
}
|
||||
it = upper.le.begin(), end = upper.le.end();
|
||||
for (; it != end; ++it) {
|
||||
conjs.push_back(arith.mk_le(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it])));
|
||||
}
|
||||
}
|
||||
bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml);
|
||||
}
|
||||
|
||||
|
||||
void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const {
|
||||
uint_set::iterator it = src.lt.begin(), end = src.lt.end();
|
||||
out << "#" << i;
|
||||
if (!src.lt.empty()) {
|
||||
out << " < ";
|
||||
for(; it != end; ++it) {
|
||||
out << *it << " ";
|
||||
}
|
||||
}
|
||||
if (!src.le.empty()) {
|
||||
it = src.le.begin(), end = src.le.end();
|
||||
out << " <= ";
|
||||
for(; it != end; ++it) {
|
||||
out << *it << " ";
|
||||
}
|
||||
}
|
||||
if (src.lt.empty() && src.le.empty()) {
|
||||
out << " < oo";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
bound_relation_plugin& bound_relation::get_plugin() const {
|
||||
return dynamic_cast<bound_relation_plugin&>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
178
src/muz/rel/dl_bound_relation.h
Normal file
178
src/muz/rel/dl_bound_relation.h
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_bound_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic (strict upper) bound relation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-2-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_BOUND_RELATION_H_
|
||||
#define _DL_BOUND_RELATION_H_
|
||||
|
||||
#include "dl_context.h"
|
||||
#include "dl_relation_manager.h"
|
||||
#include "dl_base.h"
|
||||
#include "uint_set.h"
|
||||
#include "dl_vector_relation.h"
|
||||
#include "dl_interval_relation.h"
|
||||
#include "arith_decl_plugin.h"
|
||||
#include "basic_simplifier_plugin.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class bound_relation;
|
||||
|
||||
class bound_relation_plugin : public relation_plugin {
|
||||
friend class bound_relation;
|
||||
class join_fn;
|
||||
class project_fn;
|
||||
class rename_fn;
|
||||
class union_fn;
|
||||
class union_fn_i;
|
||||
class filter_equal_fn;
|
||||
class filter_identical_fn;
|
||||
class filter_interpreted_fn;
|
||||
class filter_intersection_fn;
|
||||
arith_util m_arith;
|
||||
basic_simplifier_plugin m_bsimp;
|
||||
public:
|
||||
bound_relation_plugin(relation_manager& m);
|
||||
virtual bool can_handle_signature(const relation_signature & s);
|
||||
static symbol get_name() { return symbol("bound_relation"); }
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
|
||||
unsigned col);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
|
||||
virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2,
|
||||
unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; }
|
||||
|
||||
|
||||
#if 0
|
||||
virtual intersection_filter_fn * mk_filter_by_intersection_fn(
|
||||
const relation_base & t,
|
||||
const relation_base & src, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * src_cols) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bound_relation* get(relation_base* r);
|
||||
private:
|
||||
static bound_relation& get(relation_base& r);
|
||||
static bound_relation const & get(relation_base const& r);
|
||||
|
||||
|
||||
static bool is_interval_relation(relation_base const& r);
|
||||
static interval_relation& get_interval_relation(relation_base& r);
|
||||
static interval_relation const& get_interval_relation(relation_base const& r);
|
||||
};
|
||||
|
||||
struct uint_set2 {
|
||||
uint_set lt;
|
||||
uint_set le;
|
||||
uint_set2(uint_set2 const& other):lt(other.lt), le(other.le) {}
|
||||
uint_set2() {}
|
||||
bool operator==(const uint_set2& other) const {
|
||||
return other.lt == lt && other.le == le;
|
||||
}
|
||||
bool operator!=(const uint_set2& other) const {
|
||||
return other.lt != lt || other.le != le;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & target, const uint_set2 & s) {
|
||||
return target << s.lt << " " << s.le;
|
||||
}
|
||||
|
||||
|
||||
class bound_relation_helper {
|
||||
public:
|
||||
static void mk_project_t(uint_set2& t, unsigned_vector const& renaming);
|
||||
};
|
||||
|
||||
class bound_relation : public vector_relation<uint_set2, bound_relation_helper> {
|
||||
friend class bound_relation_plugin;
|
||||
svector<std::pair<unsigned, bool> > m_todo;
|
||||
|
||||
public:
|
||||
bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty);
|
||||
bound_relation& operator=(bound_relation const& other);
|
||||
|
||||
virtual bool empty() const { return m_empty; }
|
||||
virtual void add_fact(const relation_fact & f);
|
||||
virtual bool contains_fact(const relation_fact & f) const;
|
||||
virtual bound_relation * clone() const;
|
||||
virtual bound_relation * complement(func_decl* p) const;
|
||||
virtual void to_formula(expr_ref& fml) const;
|
||||
bound_relation_plugin& get_plugin() const;
|
||||
|
||||
void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen);
|
||||
|
||||
void mk_lt(unsigned i, unsigned j);
|
||||
|
||||
void mk_lt(unsigned i);
|
||||
|
||||
void mk_le(unsigned i, unsigned j);
|
||||
|
||||
bool is_lt(unsigned i, unsigned j) const;
|
||||
|
||||
virtual bool is_precise() const { return false; }
|
||||
|
||||
private:
|
||||
typedef uint_set2 T;
|
||||
virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const;
|
||||
|
||||
virtual T mk_widen(T const& t1, T const& t2) const;
|
||||
|
||||
virtual T mk_unite(T const& t1, T const& t2) const;
|
||||
|
||||
virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const;
|
||||
|
||||
virtual void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle);
|
||||
|
||||
|
||||
virtual bool is_subset_of(T const& t1, T const& t2) const;
|
||||
|
||||
virtual bool is_full(T const& t) const;
|
||||
|
||||
virtual bool is_empty(unsigned idx, T const& t) const;
|
||||
|
||||
virtual void display_index(unsigned idx, T const& t, std::ostream& out) const;
|
||||
|
||||
void normalize(T const& src, T& dst) const;
|
||||
|
||||
void normalize(uint_set const& src, uint_set& dst) const;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
439
src/muz/rel/dl_check_table.cpp
Normal file
439
src/muz/rel/dl_check_table.cpp
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_check_table.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-11-15
|
||||
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "dl_check_table.h"
|
||||
#include "dl_table.h"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
|
||||
bool check_table_plugin::can_handle_signature(table_signature const& s) {
|
||||
return m_tocheck.can_handle_signature(s) && m_checker.can_handle_signature(s);
|
||||
}
|
||||
|
||||
|
||||
check_table & check_table_plugin::get(table_base& r) {
|
||||
return static_cast<check_table&>(r);
|
||||
}
|
||||
|
||||
check_table const & check_table_plugin::get(table_base const& r) {
|
||||
return static_cast<check_table const &>(r);
|
||||
}
|
||||
|
||||
table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; }
|
||||
table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; }
|
||||
table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):0; }
|
||||
table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):0; }
|
||||
table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; }
|
||||
table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; }
|
||||
table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):0; }
|
||||
table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):0; }
|
||||
|
||||
table_base * check_table_plugin::mk_empty(const table_signature & s) {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
table_base* checker = m_checker.mk_empty(s);
|
||||
table_base* tocheck = m_tocheck.mk_empty(s);
|
||||
return alloc(check_table, *this, s, tocheck, checker);
|
||||
}
|
||||
|
||||
class check_table_plugin::join_fn : public table_join_fn {
|
||||
scoped_ptr<table_join_fn> m_tocheck;
|
||||
scoped_ptr<table_join_fn> m_checker;
|
||||
public:
|
||||
join_fn(check_table_plugin& p,
|
||||
const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
m_tocheck = p.get_manager().mk_join_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2);
|
||||
m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2);
|
||||
}
|
||||
|
||||
virtual table_base* operator()(const table_base & t1, const table_base & t2) {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2));
|
||||
table_base* tchecker = (*m_checker)(checker(t1), checker(t2));
|
||||
check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if (!check_kind(t1) || !check_kind(t2)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2);
|
||||
}
|
||||
|
||||
class check_table_plugin::join_project_fn : public table_join_fn {
|
||||
scoped_ptr<table_join_fn> m_tocheck;
|
||||
scoped_ptr<table_join_fn> m_checker;
|
||||
public:
|
||||
join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2,
|
||||
unsigned removed_col_cnt, const unsigned * removed_cols) {
|
||||
m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols);
|
||||
m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
virtual table_base* operator()(const table_base & t1, const table_base & t2) {
|
||||
table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2));
|
||||
table_base* tchecker = (*m_checker)(checker(t1), checker(t2));
|
||||
check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols) {
|
||||
if (!check_kind(t1) || !check_kind(t2)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
class check_table_plugin::union_fn : public table_union_fn {
|
||||
scoped_ptr<table_union_fn> m_tocheck;
|
||||
scoped_ptr<table_union_fn> m_checker;
|
||||
public:
|
||||
union_fn(check_table_plugin& p, table_base const& tgt, const table_base& src, table_base const* delta) {
|
||||
m_tocheck = p.get_manager().mk_union_fn(tocheck(tgt), tocheck(src), tocheck(delta));
|
||||
m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta));
|
||||
}
|
||||
|
||||
virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
(*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta));
|
||||
(*m_checker)(checker(tgt), checker(src), checker(delta));
|
||||
get(tgt).well_formed();
|
||||
if (delta) {
|
||||
get(*delta).well_formed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) {
|
||||
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn, *this, tgt, src, delta);
|
||||
|
||||
}
|
||||
|
||||
class check_table_plugin::project_fn : public table_transformer_fn {
|
||||
scoped_ptr<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> m_tocheck;
|
||||
public:
|
||||
project_fn(check_table_plugin& p, const table_base & t, unsigned col_cnt, const unsigned * removed_cols) {
|
||||
m_checker = p.get_manager().mk_project_fn(checker(t), col_cnt, removed_cols);
|
||||
m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
table_base* operator()(table_base const& src) {
|
||||
table_base* tchecker = (*m_checker)(checker(src));
|
||||
table_base* ttocheck = (*m_tocheck)(tocheck(src));
|
||||
check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) {
|
||||
if (!check_kind(t)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(project_fn, *this, t, col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn {
|
||||
scoped_ptr<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> m_tocheck;
|
||||
public:
|
||||
select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) {
|
||||
m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col);
|
||||
m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col);
|
||||
}
|
||||
|
||||
table_base* operator()(table_base const& src) {
|
||||
table_base* tchecker = (*m_checker)(checker(src));
|
||||
table_base* ttocheck = (*m_tocheck)(tocheck(src));
|
||||
check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t,
|
||||
const table_element & value, unsigned col) {
|
||||
if (!check_kind(t)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(select_equal_and_project_fn, *this, t, value, col);
|
||||
}
|
||||
|
||||
class check_table_plugin::rename_fn : public table_transformer_fn {
|
||||
scoped_ptr<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> m_tocheck;
|
||||
public:
|
||||
rename_fn(check_table_plugin& p, const table_base & t, unsigned cycle_len, unsigned const* cycle) {
|
||||
m_checker = p.get_manager().mk_rename_fn(checker(t), cycle_len, cycle);
|
||||
m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle);
|
||||
}
|
||||
|
||||
table_base* operator()(table_base const& src) {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
table_base* tchecker = (*m_checker)(checker(src));
|
||||
table_base* ttocheck = (*m_tocheck)(tocheck(src));
|
||||
check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) {
|
||||
if (!check_kind(t)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(rename_fn, *this, t, len, cycle);
|
||||
}
|
||||
|
||||
class check_table_plugin::filter_identical_fn : public table_mutator_fn {
|
||||
scoped_ptr<table_mutator_fn> m_checker;
|
||||
scoped_ptr<table_mutator_fn> m_tocheck;
|
||||
public:
|
||||
filter_identical_fn(check_table_plugin& p, const table_base & t,unsigned cnt, unsigned const* cols)
|
||||
{
|
||||
m_checker = p.get_manager().mk_filter_identical_fn(checker(t), cnt, cols);
|
||||
m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols);
|
||||
}
|
||||
|
||||
void operator()(table_base & t) {
|
||||
(*m_checker)(checker(t));
|
||||
(*m_tocheck)(tocheck(t));
|
||||
get(t).well_formed();
|
||||
}
|
||||
};
|
||||
|
||||
table_mutator_fn * check_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols) {
|
||||
if (check_kind(t)) {
|
||||
return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class check_table_plugin::filter_equal_fn : public table_mutator_fn {
|
||||
scoped_ptr<table_mutator_fn> m_checker;
|
||||
scoped_ptr<table_mutator_fn> m_tocheck;
|
||||
public:
|
||||
filter_equal_fn(check_table_plugin& p, const table_base & t, const table_element & v, unsigned col)
|
||||
{
|
||||
m_checker = p.get_manager().mk_filter_equal_fn(checker(t), v, col);
|
||||
m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col);
|
||||
}
|
||||
|
||||
virtual void operator()(table_base& src) {
|
||||
(*m_checker)(checker(src));
|
||||
(*m_tocheck)(tocheck(src));
|
||||
get(src).well_formed();
|
||||
}
|
||||
};
|
||||
|
||||
table_mutator_fn * check_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) {
|
||||
if (check_kind(t)) {
|
||||
return alloc(filter_equal_fn, *this, t, value, col);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class check_table_plugin::filter_interpreted_fn : public table_mutator_fn {
|
||||
scoped_ptr<table_mutator_fn> m_checker;
|
||||
scoped_ptr<table_mutator_fn> m_tocheck;
|
||||
public:
|
||||
filter_interpreted_fn(check_table_plugin& p, const table_base & t, app * condition)
|
||||
{
|
||||
m_checker = p.get_manager().mk_filter_interpreted_fn(checker(t), condition);
|
||||
m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition);
|
||||
}
|
||||
|
||||
virtual void operator()(table_base& src) {
|
||||
(*m_checker)(checker(src));
|
||||
(*m_tocheck)(tocheck(src));
|
||||
get(src).well_formed();
|
||||
}
|
||||
};
|
||||
|
||||
table_mutator_fn * check_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) {
|
||||
if (check_kind(t)) {
|
||||
return alloc(filter_interpreted_fn, *this, t, condition);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn {
|
||||
scoped_ptr<table_transformer_fn> m_checker;
|
||||
scoped_ptr<table_transformer_fn> m_tocheck;
|
||||
public:
|
||||
filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition,
|
||||
unsigned removed_col_cnt, const unsigned * removed_cols)
|
||||
{
|
||||
m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols);
|
||||
m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
table_base* operator()(table_base const& src) {
|
||||
table_base* tchecker = (*m_checker)(checker(src));
|
||||
table_base* ttocheck = (*m_tocheck)(tocheck(src));
|
||||
check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t,
|
||||
app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) {
|
||||
if (check_kind(t)) {
|
||||
return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn {
|
||||
scoped_ptr<table_intersection_filter_fn> m_checker;
|
||||
scoped_ptr<table_intersection_filter_fn> m_tocheck;
|
||||
public:
|
||||
filter_by_negation_fn(
|
||||
check_table_plugin& p,
|
||||
const table_base & t,
|
||||
const table_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols) {
|
||||
m_checker = p.get_manager().mk_filter_by_negation_fn(checker(t), checker(negated_obj), joined_col_cnt, t_cols, negated_cols);
|
||||
m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols);
|
||||
}
|
||||
|
||||
virtual void operator()(table_base& src, table_base const& negated_obj) {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
(*m_checker)(checker(src), checker(negated_obj));
|
||||
(*m_tocheck)(tocheck(src), tocheck(negated_obj));
|
||||
get(src).well_formed();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
table_intersection_filter_fn * check_table_plugin::mk_filter_by_negation_fn(const table_base & t,
|
||||
const table_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols) {
|
||||
if (check_kind(t) && check_kind(negated_obj)) {
|
||||
return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------
|
||||
// check_table
|
||||
|
||||
|
||||
check_table::check_table(check_table_plugin & p, const table_signature & sig):
|
||||
table_base(p, sig) {
|
||||
(well_formed());
|
||||
}
|
||||
|
||||
check_table::check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker):
|
||||
table_base(p, sig),
|
||||
m_checker(checker),
|
||||
m_tocheck(tocheck) {
|
||||
well_formed();
|
||||
}
|
||||
|
||||
check_table::~check_table() {
|
||||
m_tocheck->deallocate();
|
||||
m_checker->deallocate();
|
||||
}
|
||||
|
||||
bool check_table::well_formed() const {
|
||||
get_plugin().m_count++;
|
||||
iterator it = m_tocheck->begin(), end = m_tocheck->end();
|
||||
for (; it != end; ++it) {
|
||||
table_fact fact;
|
||||
it->get_fact(fact);
|
||||
if (!m_checker->contains_fact(fact)) {
|
||||
m_tocheck->display(verbose_stream());
|
||||
m_checker->display(verbose_stream());
|
||||
verbose_stream() << get_plugin().m_count << "\n";
|
||||
UNREACHABLE();
|
||||
fatal_error(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
iterator it2 = m_checker->begin(), end2 = m_checker->end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
table_fact fact;
|
||||
it2->get_fact(fact);
|
||||
if (!m_tocheck->contains_fact(fact)) {
|
||||
m_tocheck->display(verbose_stream());
|
||||
m_checker->display(verbose_stream());
|
||||
verbose_stream() << get_plugin().m_count << "\n";
|
||||
UNREACHABLE();
|
||||
fatal_error(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_table::empty() const {
|
||||
if (m_tocheck->empty() != m_checker->empty()) {
|
||||
m_tocheck->display(verbose_stream());
|
||||
m_checker->display(verbose_stream());
|
||||
verbose_stream() << get_plugin().m_count << "\n";
|
||||
fatal_error(0);
|
||||
}
|
||||
return m_tocheck->empty();
|
||||
}
|
||||
|
||||
|
||||
void check_table::add_fact(const table_fact & f) {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
m_tocheck->add_fact(f);
|
||||
m_checker->add_fact(f);
|
||||
well_formed();
|
||||
}
|
||||
|
||||
void check_table::remove_fact(const table_element* f) {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
m_tocheck->remove_fact(f);
|
||||
m_checker->remove_fact(f);
|
||||
well_formed();
|
||||
}
|
||||
|
||||
bool check_table::contains_fact(const table_fact & f) const {
|
||||
return m_checker->contains_fact(f);
|
||||
}
|
||||
|
||||
table_base * check_table::clone() const {
|
||||
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
|
||||
check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->clone(), m_checker->clone());
|
||||
return result;
|
||||
}
|
||||
|
||||
table_base * check_table::complement(func_decl* p, const table_element * func_columns) const {
|
||||
check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p, func_columns), m_checker->complement(p, func_columns));
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
135
src/muz/rel/dl_check_table.h
Normal file
135
src/muz/rel/dl_check_table.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_check_table.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-11-15
|
||||
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _DL_CHECK_TABLE_H_
|
||||
#define _DL_CHECK_TABLE_H_
|
||||
|
||||
#include "dl_base.h"
|
||||
#include "dl_decl_plugin.h"
|
||||
#include "dl_relation_manager.h"
|
||||
|
||||
namespace datalog {
|
||||
class check_table;
|
||||
|
||||
class check_table_plugin : public table_plugin {
|
||||
friend class check_table;
|
||||
table_plugin& m_checker;
|
||||
table_plugin& m_tocheck;
|
||||
unsigned m_count;
|
||||
protected:
|
||||
class join_fn;
|
||||
class join_project_fn;
|
||||
class union_fn;
|
||||
class transformer_fn;
|
||||
class rename_fn;
|
||||
class project_fn;
|
||||
class select_equal_and_project_fn;
|
||||
class filter_equal_fn;
|
||||
class filter_identical_fn;
|
||||
class filter_interpreted_fn;
|
||||
class filter_interpreted_and_project_fn;
|
||||
class filter_by_negation_fn;
|
||||
|
||||
public:
|
||||
check_table_plugin(relation_manager & manager, symbol const& checker, symbol const& tocheck)
|
||||
: table_plugin(symbol("check"), manager),
|
||||
m_checker(*manager.get_table_plugin(checker)),
|
||||
m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {}
|
||||
|
||||
virtual table_base * mk_empty(const table_signature & s);
|
||||
|
||||
virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src,
|
||||
const table_base * delta);
|
||||
virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t,
|
||||
const table_element & value, unsigned col);
|
||||
virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value,
|
||||
unsigned col);
|
||||
virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition);
|
||||
virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t,
|
||||
app * condition, unsigned removed_col_cnt, const unsigned * removed_cols);
|
||||
virtual table_intersection_filter_fn * mk_filter_by_negation_fn(
|
||||
const table_base & t,
|
||||
const table_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols);
|
||||
|
||||
virtual bool can_handle_signature(table_signature const& s);
|
||||
|
||||
private:
|
||||
static check_table& get(table_base& r);
|
||||
|
||||
static check_table const & get(table_base const& r);
|
||||
|
||||
static table_base& checker(table_base& r);
|
||||
static table_base const& checker(table_base const& r);
|
||||
static table_base* checker(table_base* r);
|
||||
static table_base const* checker(table_base const* r);
|
||||
static table_base& tocheck(table_base& r);
|
||||
static table_base const& tocheck(table_base const& r);
|
||||
static table_base* tocheck(table_base* r);
|
||||
static table_base const* tocheck(table_base const* r);
|
||||
};
|
||||
|
||||
class check_table : public table_base {
|
||||
friend class check_table_plugin;
|
||||
|
||||
table_base* m_checker;
|
||||
table_base* m_tocheck;
|
||||
|
||||
check_table(check_table_plugin & p, const table_signature & sig);
|
||||
check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker);
|
||||
|
||||
virtual ~check_table();
|
||||
|
||||
bool well_formed() const;
|
||||
|
||||
public:
|
||||
|
||||
check_table_plugin & get_plugin() const {
|
||||
return static_cast<check_table_plugin &>(table_base::get_plugin());
|
||||
}
|
||||
|
||||
virtual bool empty() const;
|
||||
virtual void add_fact(const table_fact & f);
|
||||
virtual void remove_fact(const table_element* fact);
|
||||
virtual bool contains_fact(const table_fact & f) const;
|
||||
virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const;
|
||||
virtual table_base * clone() const;
|
||||
|
||||
virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); }
|
||||
virtual iterator end() const { return m_tocheck->end(); }
|
||||
|
||||
virtual unsigned get_size_estimate_rows() const { return m_tocheck->get_size_estimate_rows(); }
|
||||
virtual unsigned get_size_estimate_bytes() const { return m_tocheck->get_size_estimate_bytes(); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_CHECK_TABLE_H_ */
|
||||
1346
src/muz/rel/dl_compiler.cpp
Normal file
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
282
src/muz/rel/dl_compiler.h
Normal 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_ */
|
||||
|
||||
456
src/muz/rel/dl_external_relation.cpp
Normal file
456
src/muz/rel/dl_external_relation.cpp
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_external_relation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-05-10
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "debug.h"
|
||||
#include "ast_pp.h"
|
||||
#include "dl_context.h"
|
||||
#include "dl_external_relation.h"
|
||||
#include "dl_decl_plugin.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
external_relation::external_relation(external_relation_plugin & p, const relation_signature & s, expr* r)
|
||||
: relation_base(p, s),
|
||||
m_rel(r, p.get_ast_manager()),
|
||||
m_select_fn(p.get_ast_manager()),
|
||||
m_store_fn(p.get_ast_manager()),
|
||||
m_is_empty_fn(p.get_ast_manager())
|
||||
{
|
||||
}
|
||||
|
||||
external_relation::~external_relation() {
|
||||
}
|
||||
|
||||
void external_relation::mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const {
|
||||
ast_manager& m = m_rel.get_manager();
|
||||
family_id fid = get_plugin().get_family_id();
|
||||
ptr_vector<expr> args;
|
||||
args.push_back(m_rel);
|
||||
for (unsigned i = 0; i < f.size(); ++i) {
|
||||
args.push_back(f[i]);
|
||||
}
|
||||
if (!fn.get()) {
|
||||
fn = m.mk_func_decl(fid, k, 0, 0, args.size(), args.c_ptr());
|
||||
}
|
||||
if (destructive) {
|
||||
get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr());
|
||||
res = m_rel;
|
||||
}
|
||||
else {
|
||||
get_plugin().reduce(fn, args.size(), args.c_ptr(), res);
|
||||
}
|
||||
}
|
||||
|
||||
bool external_relation::empty() const {
|
||||
ast_manager& m = m_rel.get_manager();
|
||||
expr* r = m_rel.get();
|
||||
expr_ref res(m);
|
||||
if (!m_is_empty_fn.get()) {
|
||||
family_id fid = get_plugin().get_family_id();
|
||||
const_cast<func_decl_ref&>(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, 0, 1, &r);
|
||||
}
|
||||
get_plugin().reduce(m_is_empty_fn, 1, &r, res);
|
||||
return m.is_true(res);
|
||||
}
|
||||
|
||||
void external_relation::add_fact(const relation_fact & f) {
|
||||
mk_accessor(OP_RA_STORE, m_store_fn, f, true, m_rel);
|
||||
}
|
||||
|
||||
bool external_relation::contains_fact(const relation_fact & f) const {
|
||||
ast_manager& m = get_plugin().get_ast_manager();
|
||||
expr_ref res(m);
|
||||
mk_accessor(OP_RA_SELECT, const_cast<func_decl_ref&>(m_select_fn), f, false, res);
|
||||
return !m.is_false(res);
|
||||
}
|
||||
|
||||
external_relation * external_relation::clone() const {
|
||||
ast_manager& m = m_rel.get_manager();
|
||||
family_id fid = get_plugin().get_family_id();
|
||||
expr* rel = m_rel.get();
|
||||
expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m);
|
||||
expr* rel_out = res.get();
|
||||
func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,0, 1, &rel), m);
|
||||
get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out);
|
||||
return alloc(external_relation, get_plugin(), get_signature(), res);
|
||||
}
|
||||
|
||||
external_relation * external_relation::complement(func_decl* p) const {
|
||||
ast_manager& m = m_rel.get_manager();
|
||||
family_id fid = get_plugin().get_family_id();
|
||||
expr_ref res(m);
|
||||
expr* rel = m_rel;
|
||||
func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,0, 1, &rel), m);
|
||||
get_plugin().reduce(fn, 1, &rel, res);
|
||||
return alloc(external_relation, get_plugin(), get_signature(), res);
|
||||
}
|
||||
|
||||
void external_relation::display(std::ostream & out) const {
|
||||
out << mk_pp(m_rel, m_rel.get_manager()) << "\n";
|
||||
}
|
||||
|
||||
void external_relation::display_tuples(func_decl & pred, std::ostream & out) const {
|
||||
display(out);
|
||||
}
|
||||
|
||||
|
||||
external_relation_plugin & external_relation::get_plugin() const {
|
||||
return static_cast<external_relation_plugin &>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// external_relation_plugin
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
|
||||
external_relation_plugin::external_relation_plugin(external_relation_context& ctx, relation_manager & m)
|
||||
: relation_plugin(external_relation_plugin::get_name(), m), m_ext(ctx) {}
|
||||
|
||||
external_relation const & external_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<external_relation const&>(r);
|
||||
}
|
||||
|
||||
external_relation & external_relation_plugin::get(relation_base & r) {
|
||||
return dynamic_cast<external_relation&>(r);
|
||||
}
|
||||
|
||||
relation_base * external_relation_plugin::mk_empty(const relation_signature & s) {
|
||||
ast_manager& m = get_ast_manager();
|
||||
sort* r_sort = get_relation_sort(s);
|
||||
parameter param(r_sort);
|
||||
family_id fid = get_family_id();
|
||||
expr_ref e(m.mk_fresh_const("T", r_sort), m);
|
||||
expr* args[1] = { e.get() };
|
||||
func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)0), m);
|
||||
reduce_assign(empty_decl, 0, 0, 1, args);
|
||||
return alloc(external_relation, *this, s, e);
|
||||
}
|
||||
|
||||
sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) {
|
||||
vector<parameter> sorts;
|
||||
ast_manager& m = get_ast_manager();
|
||||
family_id fid = get_family_id();
|
||||
for (unsigned i = 0; i < sig.size(); ++i) {
|
||||
sorts.push_back(parameter(sig[i]));
|
||||
}
|
||||
return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr());
|
||||
}
|
||||
|
||||
sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) {
|
||||
SASSERT(s->get_num_parameters() > col);
|
||||
SASSERT(s->get_parameter(col).is_ast());
|
||||
SASSERT(is_sort(s->get_parameter(col).get_ast()));
|
||||
return to_sort(s->get_parameter(col).get_ast());
|
||||
}
|
||||
|
||||
family_id external_relation_plugin::get_family_id() {
|
||||
return m_ext.get_family_id();
|
||||
}
|
||||
|
||||
|
||||
void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) {
|
||||
ast_manager& m = get_ast_manager();
|
||||
family_id fid = get_family_id();
|
||||
parameter param(condition);
|
||||
f = m.mk_func_decl(fid, OP_RA_FILTER, 1, ¶m, 1, &s);
|
||||
}
|
||||
|
||||
class external_relation_plugin::join_fn : public convenient_relation_join_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_join_fn;
|
||||
expr* m_args[2];
|
||||
public:
|
||||
join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2)
|
||||
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2),
|
||||
m_plugin(p),
|
||||
m_join_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
vector<parameter> params;
|
||||
for (unsigned i = 0; i < col_cnt; ++i) {
|
||||
params.push_back(parameter(cols1[i]));
|
||||
params.push_back(parameter(cols2[i]));
|
||||
}
|
||||
sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) };
|
||||
m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain);
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) {
|
||||
expr_ref res(m_plugin.get_ast_manager());
|
||||
m_args[0] = get(r1).get_relation();
|
||||
m_args[1] = get(r2).get_relation();
|
||||
m_plugin.reduce(m_join_fn, 2, m_args, res);
|
||||
return alloc(external_relation, m_plugin, get_result_signature(), res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if (!check_kind(r1) || !check_kind(r2)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::project_fn : public convenient_relation_project_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_project_fn;
|
||||
public:
|
||||
project_fn(external_relation_plugin& p, sort* relation_sort,
|
||||
const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
|
||||
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols),
|
||||
m_plugin(p),
|
||||
m_project_fn(p.get_ast_manager()) {
|
||||
vector<parameter> params;
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
for (unsigned i = 0; i < removed_col_cnt; ++i) {
|
||||
params.push_back(parameter(removed_cols[i]));
|
||||
}
|
||||
m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort);
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r) {
|
||||
expr_ref res(m_plugin.get_ast_manager());
|
||||
expr* rel = get(r).get_relation();
|
||||
m_plugin.reduce(m_project_fn, 1, &rel, res);
|
||||
return alloc(external_relation, m_plugin, get_result_signature(), to_app(res));
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r,
|
||||
unsigned col_cnt, const unsigned * removed_cols) {
|
||||
return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::rename_fn : public convenient_relation_rename_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_rename_fn;
|
||||
expr* m_args[2];
|
||||
public:
|
||||
rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
|
||||
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle),
|
||||
m_plugin(p),
|
||||
m_rename_fn(p.get_ast_manager()) {
|
||||
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
vector<parameter> params;
|
||||
for (unsigned i = 0; i < cycle_len; ++i) {
|
||||
SASSERT(cycle[i] < orig_sig.size());
|
||||
params.push_back(parameter(cycle[i]));
|
||||
}
|
||||
m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort);
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r) {
|
||||
expr* rel = get(r).get_relation();
|
||||
expr_ref res(m_plugin.get_ast_manager());
|
||||
m_args[0] = rel;
|
||||
m_plugin.reduce(m_rename_fn, 1, &rel, res);
|
||||
return alloc(external_relation, m_plugin, get_result_signature(), res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r,
|
||||
unsigned cycle_len, const unsigned * permutation_cycle) {
|
||||
if(!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::union_fn : public relation_union_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_union_fn;
|
||||
expr* m_args[2];
|
||||
expr* m_outs[2];
|
||||
|
||||
public:
|
||||
union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort):
|
||||
m_plugin(p),
|
||||
m_union_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
sort* domain[2] = { relation_sort, relation_sort };
|
||||
m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain);
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) {
|
||||
ast_manager& m = m_plugin.get_ast_manager();
|
||||
expr_ref_vector res(m);
|
||||
m_args[0] = get(r).get_relation();
|
||||
m_args[1] = get(src).get_relation();
|
||||
m_outs[0] = m_args[0];
|
||||
unsigned num_out = 1;
|
||||
if (delta) {
|
||||
m_outs[1] = get(*delta).get_relation();
|
||||
++num_out;
|
||||
}
|
||||
m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs);
|
||||
}
|
||||
};
|
||||
|
||||
relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort());
|
||||
}
|
||||
|
||||
relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort());
|
||||
}
|
||||
|
||||
class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
app_ref m_condition;
|
||||
func_decl_ref m_filter_fn;
|
||||
public:
|
||||
filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition)
|
||||
: m_plugin(p),
|
||||
m_condition(condition, p.get_ast_manager()),
|
||||
m_filter_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
p.mk_filter_fn(relation_sort, condition, m_filter_fn);
|
||||
SASSERT(m.is_bool(condition));
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & r) {
|
||||
SASSERT(m_plugin.check_kind(r));
|
||||
expr* arg = get(r).get_relation();
|
||||
m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg);
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) {
|
||||
if(!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition);
|
||||
}
|
||||
|
||||
relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r,
|
||||
const relation_element & value, unsigned col) {
|
||||
if(!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
ast_manager& m = get_ast_manager();
|
||||
app_ref condition(m);
|
||||
expr_ref var(m);
|
||||
sort* relation_sort = get(r).get_sort();
|
||||
sort* column_sort = get_column_sort(col, relation_sort);
|
||||
var = m.mk_var(col, column_sort);
|
||||
condition = m.mk_eq(var, value);
|
||||
return mk_filter_interpreted_fn(r, condition);
|
||||
}
|
||||
|
||||
class external_relation_plugin::filter_identical_fn : public relation_mutator_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref_vector m_filter_fn;
|
||||
public:
|
||||
filter_identical_fn(external_relation_plugin& p, sort* relation_sort,
|
||||
unsigned col_cnt, const unsigned * identical_cols)
|
||||
: m_plugin(p), m_filter_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
func_decl_ref fn(m);
|
||||
app_ref eq(m);
|
||||
if (col_cnt <= 1) {
|
||||
return;
|
||||
}
|
||||
unsigned col = identical_cols[0];
|
||||
sort* s = p.get_column_sort(col, relation_sort);
|
||||
var* v0 = m.mk_var(col, s);
|
||||
for (unsigned i = 1; i < col_cnt; ++i) {
|
||||
col = identical_cols[i];
|
||||
s = p.get_column_sort(col, relation_sort);
|
||||
eq = m.mk_eq(v0, m.mk_var(col, s));
|
||||
p.mk_filter_fn(relation_sort, eq.get(), fn);
|
||||
m_filter_fn.push_back(fn);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & r) {
|
||||
expr* r0 = get(r).get_relation();
|
||||
for (unsigned i = 0; i < m_filter_fn.size(); ++i) {
|
||||
m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r,
|
||||
unsigned col_cnt, const unsigned * identical_cols) {
|
||||
if (!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_negated_filter_fn;
|
||||
expr* m_args[2];
|
||||
public:
|
||||
negation_filter_fn(external_relation_plugin& p,
|
||||
const relation_base & tgt, const relation_base & neg_t,
|
||||
unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) :
|
||||
convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols),
|
||||
m_plugin(p),
|
||||
m_negated_filter_fn(p.get_ast_manager())
|
||||
{
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
vector<parameter> params;
|
||||
for (unsigned i = 0; i < joined_col_cnt; ++i) {
|
||||
params.push_back(parameter(t_cols[i]));
|
||||
params.push_back(parameter(negated_cols[i]));
|
||||
}
|
||||
sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() };
|
||||
m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain);
|
||||
}
|
||||
|
||||
void operator()(relation_base & t, const relation_base & negated_obj) {
|
||||
m_args[0] = get(t).get_relation();
|
||||
m_args[1] = get(negated_obj).get_relation();
|
||||
m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args);
|
||||
}
|
||||
};
|
||||
|
||||
relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t,
|
||||
const relation_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols) {
|
||||
if (!check_kind(t) || !check_kind(negated_obj)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols);
|
||||
}
|
||||
|
||||
};
|
||||
154
src/muz/rel/dl_external_relation.h
Normal file
154
src/muz/rel/dl_external_relation.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_external_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-05-10
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_EXTERNAL_RELATION_H_
|
||||
#define _DL_EXTERNAL_RELATION_H_
|
||||
|
||||
#include "dl_base.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class external_relation;
|
||||
|
||||
class external_relation_context {
|
||||
public:
|
||||
virtual ~external_relation_context() {}
|
||||
|
||||
virtual family_id get_family_id() const = 0;
|
||||
|
||||
// reduce arguments.
|
||||
virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) = 0;
|
||||
|
||||
// overwrite terms passed in outs vector with values computed by function.
|
||||
virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) = 0;
|
||||
};
|
||||
|
||||
class external_relation_plugin : public relation_plugin {
|
||||
|
||||
friend class external_relation;
|
||||
class join_fn;
|
||||
class project_fn;
|
||||
class rename_fn;
|
||||
class union_fn;
|
||||
class filter_identical_fn;
|
||||
class filter_interpreted_fn;
|
||||
class negation_filter_fn;
|
||||
|
||||
external_relation_context& m_ext;
|
||||
|
||||
public:
|
||||
external_relation_plugin(external_relation_context& ctx, relation_manager & m);
|
||||
|
||||
virtual bool can_handle_signature(const relation_signature & s) { return true; }
|
||||
|
||||
static symbol get_name() { return symbol("external_relation"); }
|
||||
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
|
||||
unsigned col);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
|
||||
const relation_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols);
|
||||
|
||||
private:
|
||||
|
||||
static external_relation& get(relation_base& r);
|
||||
static external_relation const & get(relation_base const& r);
|
||||
|
||||
void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) {
|
||||
m_ext.reduce(f, num_args, args, result);
|
||||
}
|
||||
|
||||
void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) {
|
||||
m_ext.reduce_assign(f, num_args, args, num_out, outs);
|
||||
}
|
||||
|
||||
sort* get_relation_sort(relation_signature const& sig);
|
||||
|
||||
sort* get_column_sort(unsigned col, sort* relation_sort);
|
||||
|
||||
void mk_filter_fn(sort* s, app* condition, func_decl_ref& f);
|
||||
|
||||
family_id get_family_id();
|
||||
};
|
||||
|
||||
class external_relation : public relation_base {
|
||||
friend class external_relation_plugin;
|
||||
friend class external_relation_plugin::join_fn;
|
||||
friend class external_relation_plugin::project_fn;
|
||||
friend class external_relation_plugin::rename_fn;
|
||||
friend class external_relation_plugin::union_fn;
|
||||
friend class external_relation_plugin::filter_identical_fn;
|
||||
friend class external_relation_plugin::filter_interpreted_fn;
|
||||
friend class external_relation_plugin::negation_filter_fn;
|
||||
|
||||
expr_ref m_rel;
|
||||
func_decl_ref m_select_fn;
|
||||
func_decl_ref m_store_fn;
|
||||
func_decl_ref m_is_empty_fn;
|
||||
|
||||
unsigned size() const { return get_signature().size(); }
|
||||
|
||||
sort* get_sort() const { return m_rel.get_manager().get_sort(m_rel); }
|
||||
|
||||
void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const;
|
||||
|
||||
external_relation(external_relation_plugin & p, const relation_signature & s, expr* r);
|
||||
virtual ~external_relation();
|
||||
|
||||
public:
|
||||
external_relation_plugin & get_plugin() const;
|
||||
|
||||
virtual bool empty() const;
|
||||
|
||||
virtual void add_fact(const relation_fact & f);
|
||||
|
||||
virtual bool contains_fact(const relation_fact & f) const;
|
||||
|
||||
virtual external_relation * clone() const;
|
||||
|
||||
virtual external_relation * complement(func_decl*) const;
|
||||
|
||||
virtual void display(std::ostream & out) const;
|
||||
|
||||
virtual void display_tuples(func_decl & pred, std::ostream & out) const;
|
||||
|
||||
expr* get_relation() const { return m_rel.get(); }
|
||||
|
||||
virtual void to_formula(expr_ref& fml) const { fml = get_relation(); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
2377
src/muz/rel/dl_finite_product_relation.cpp
Normal file
2377
src/muz/rel/dl_finite_product_relation.cpp
Normal file
File diff suppressed because it is too large
Load diff
366
src/muz/rel/dl_finite_product_relation.h
Normal file
366
src/muz/rel/dl_finite_product_relation.h
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_finite_product_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-24.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_FINITE_PRODUCT_RELATION_H_
|
||||
#define _DL_FINITE_PRODUCT_RELATION_H_
|
||||
|
||||
|
||||
#include "dl_base.h"
|
||||
#include "dl_relation_manager.h"
|
||||
#include "dl_table_relation.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class finite_product_relation;
|
||||
|
||||
void universal_delete(finite_product_relation* ptr);
|
||||
|
||||
|
||||
class finite_product_relation_plugin : public relation_plugin {
|
||||
friend class finite_product_relation;
|
||||
public:
|
||||
struct rel_spec {
|
||||
family_id m_inner_kind; //null_family_id means we don't care about the kind
|
||||
svector<bool> m_table_cols;
|
||||
|
||||
rel_spec() : m_inner_kind(null_family_id) {}
|
||||
rel_spec(const svector<bool>& table_cols)
|
||||
: m_inner_kind(null_family_id), m_table_cols(table_cols) {}
|
||||
|
||||
bool operator==(const rel_spec & o) const {
|
||||
return m_inner_kind==o.m_inner_kind && vectors_equal(m_table_cols, o.m_table_cols);
|
||||
}
|
||||
struct hash {
|
||||
unsigned operator()(const rel_spec & o) const {
|
||||
return o.m_inner_kind^svector_hash<bool_hash>()(o.m_table_cols);
|
||||
}
|
||||
};
|
||||
};
|
||||
private:
|
||||
|
||||
class join_fn;
|
||||
class converting_join_fn;
|
||||
class project_fn;
|
||||
class rename_fn;
|
||||
class union_fn;
|
||||
class inner_singleton_union_fn;
|
||||
class converting_union_fn;
|
||||
class filter_identical_fn;
|
||||
class filter_equal_fn;
|
||||
class filter_interpreted_fn;
|
||||
class negation_filter_fn;
|
||||
class filter_identical_pairs_fn;
|
||||
|
||||
relation_plugin & m_inner_plugin;
|
||||
|
||||
rel_spec_store<rel_spec, rel_spec::hash, default_eq<rel_spec> > m_spec_store;
|
||||
|
||||
static symbol get_name(relation_plugin & inner_plugin);
|
||||
family_id get_relation_kind(finite_product_relation & r, const bool * table_columns);
|
||||
|
||||
static void get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s,
|
||||
svector<bool> & table_columns);
|
||||
void get_all_possible_table_columns(const relation_signature & s, svector<bool> & table_columns) {
|
||||
get_all_possible_table_columns(get_manager(), s, table_columns);
|
||||
}
|
||||
|
||||
void split_signatures(const relation_signature & s, table_signature & table_sig,
|
||||
relation_signature & remaining_sig);
|
||||
void split_signatures(const relation_signature & s, const bool * table_columns,
|
||||
table_signature & table_sig, relation_signature & remaining_sig);
|
||||
public:
|
||||
static finite_product_relation & get(relation_base & r);
|
||||
static const finite_product_relation & get(const relation_base & r);
|
||||
static finite_product_relation * get(relation_base * r);
|
||||
static const finite_product_relation * get(const relation_base * r);
|
||||
|
||||
static finite_product_relation_plugin & get_plugin(relation_manager & rmgr, relation_plugin & inner);
|
||||
|
||||
finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager);
|
||||
|
||||
virtual void initialize(family_id fid);
|
||||
|
||||
relation_plugin & get_inner_plugin() const { return m_inner_plugin; }
|
||||
|
||||
virtual bool can_handle_signature(const relation_signature & s);
|
||||
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
/**
|
||||
\c inner_kind==null_family_id means we don't care about the kind of the inner relation
|
||||
*/
|
||||
finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns,
|
||||
family_id inner_kind=null_family_id);
|
||||
finite_product_relation * mk_empty(const finite_product_relation & original);
|
||||
virtual relation_base * mk_empty(const relation_base & original);
|
||||
virtual relation_base * mk_empty(const relation_signature & s, family_id kind);
|
||||
|
||||
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
|
||||
|
||||
/**
|
||||
\brief Return true if \c r can be converted to \c finite_product_relation_plugin either
|
||||
by \c mk_from_table_relation or by \c mk_from_inner_relation.
|
||||
*/
|
||||
bool can_be_converted(const relation_base & r);
|
||||
|
||||
/**
|
||||
If the conversion cannot be performed, 0 is returned.
|
||||
*/
|
||||
finite_product_relation * mk_from_table_relation(const table_relation & r);
|
||||
finite_product_relation * mk_from_inner_relation(const relation_base & r);
|
||||
|
||||
bool can_convert_to_table_relation(const finite_product_relation & r);
|
||||
table_relation * to_table_relation(const finite_product_relation & r);
|
||||
|
||||
protected:
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
|
||||
unsigned col);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
|
||||
const relation_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols);
|
||||
|
||||
private:
|
||||
/**
|
||||
\brief Create a filter that enforces equality between pairs of table and relation columns
|
||||
|
||||
The column numbers in arrays \c table_cols and \c rel_cols must be local to the table/inner relation.
|
||||
*/
|
||||
relation_mutator_fn * mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt,
|
||||
const unsigned * table_cols, const unsigned * rel_cols);
|
||||
|
||||
/**
|
||||
\brief Create a join-project operation that creates a table according to \c relation_table
|
||||
but with references to relations updated and removed according to the content of \c filtered_table.
|
||||
\c selected_columns contains sorted indexes of data columns in \c relation_table that are also in
|
||||
the \c filtered_table (so that the first column in \c filtered_table corresponds to
|
||||
\c selected_columns[0] -th column in \c relation_table etc...)
|
||||
|
||||
Signature of \c relation_table:
|
||||
(data columns)(functional column with indexes of relation objects)
|
||||
|
||||
Signature of \c filtered_table:
|
||||
(selected data columns)(non-functional column with original relation object indexes)
|
||||
(functional column with indexes of filtered relation objects)
|
||||
|
||||
*/
|
||||
static table_join_fn * mk_assembler_of_filter_result(const table_base & relation_table,
|
||||
const table_base & filtered_table, const unsigned_vector & selected_columns);
|
||||
|
||||
};
|
||||
|
||||
class finite_product_relation : public relation_base {
|
||||
friend class finite_product_relation_plugin;
|
||||
friend class finite_product_relation_plugin::join_fn;
|
||||
friend class finite_product_relation_plugin::project_fn;
|
||||
friend class finite_product_relation_plugin::union_fn;
|
||||
friend class finite_product_relation_plugin::rename_fn;
|
||||
friend class finite_product_relation_plugin::inner_singleton_union_fn;
|
||||
friend class finite_product_relation_plugin::filter_equal_fn;
|
||||
friend class finite_product_relation_plugin::filter_identical_pairs_fn;
|
||||
|
||||
class live_rel_collection_reducer;
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
Size of this sort determines how many different relation objects can we refer to.
|
||||
*/
|
||||
static const table_sort s_rel_idx_sort;
|
||||
|
||||
|
||||
/**
|
||||
\brief The last column in the signature is a functional column with index of the
|
||||
associated inner relation. The other columns correspond to the relation signature
|
||||
according to \c m_table2sig.
|
||||
|
||||
It holds that \c m_table_sig.size()-1==m_table2sig.size()
|
||||
*/
|
||||
|
||||
table_signature m_table_sig;
|
||||
unsigned_vector m_table2sig; // (ordered list)
|
||||
unsigned_vector m_sig2table; //index of corresponding table column or UINT_MAX
|
||||
private:
|
||||
relation_signature m_other_sig;
|
||||
unsigned_vector m_other2sig; // (ordered list)
|
||||
public:
|
||||
unsigned_vector m_sig2other; //index of corresponding other relation column or UINT_MAX
|
||||
private:
|
||||
relation_plugin & m_other_plugin;
|
||||
family_id m_other_kind;
|
||||
|
||||
mutable table_base * m_table;
|
||||
public:
|
||||
mutable relation_vector m_others;
|
||||
private:
|
||||
mutable unsigned_vector m_available_rel_indexes;
|
||||
|
||||
/**
|
||||
\c UINT_MAX means uninitialized.
|
||||
If we can get away with it, we want to have a single full relation to refer to.
|
||||
*/
|
||||
mutable unsigned m_full_rel_idx;
|
||||
|
||||
mutable idx_set m_live_rel_collection_acc;
|
||||
mutable scoped_ptr<table_transformer_fn> m_live_rel_collection_project;
|
||||
|
||||
mutable scoped_ptr<table_intersection_filter_fn> m_empty_rel_removal_filter;
|
||||
|
||||
void recycle_rel_idx(unsigned idx) const;
|
||||
|
||||
// creates a full relation if it does not exist.
|
||||
unsigned get_full_rel_idx();
|
||||
|
||||
|
||||
|
||||
public:
|
||||
relation_base & get_inner_rel(table_element idx)
|
||||
{ SASSERT(idx<UINT_MAX); return get_inner_rel(static_cast<unsigned>(idx)); }
|
||||
relation_base & get_inner_rel(unsigned idx) { SASSERT(m_others[idx]); return *m_others[idx]; }
|
||||
const relation_base & get_inner_rel(unsigned idx) const
|
||||
{ return const_cast<finite_product_relation &>(*this).get_inner_rel(idx); }
|
||||
|
||||
unsigned get_next_rel_idx() const;
|
||||
|
||||
/**
|
||||
The relation takes ownership of the \c inner object.
|
||||
*/
|
||||
void set_inner_rel(table_element idx, relation_base * inner)
|
||||
{ SASSERT(idx<UINT_MAX); return set_inner_rel(static_cast<unsigned>(idx), inner); }
|
||||
/**
|
||||
The relation takes ownership of the \c inner object.
|
||||
*/
|
||||
void set_inner_rel(unsigned idx, relation_base * inner) {
|
||||
SASSERT(!m_others[idx]);
|
||||
SASSERT(inner);
|
||||
m_others[idx] = inner;
|
||||
}
|
||||
table_base & get_table() { return *m_table; }
|
||||
|
||||
table_plugin & get_table_plugin() const { return get_table().get_plugin(); }
|
||||
|
||||
void garbage_collect(bool remove_empty) const;
|
||||
|
||||
/**
|
||||
\brief Initialize an empty relation with table \c table_vals and relations in \c others.
|
||||
|
||||
The relation object takes ownership of relations inside the \c others vector.
|
||||
|
||||
If \c contiguous is true, it can be assumed that there are no zero elements in the \c others array.
|
||||
*/
|
||||
void init(const table_base & table_vals, const relation_vector & others, bool contiguous);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/**
|
||||
\brief Extract the values of table non-functional columns from the relation fact.
|
||||
The value of the functional column which determines index of the inner relation is undefined.
|
||||
*/
|
||||
void extract_table_fact(const relation_fact rf, table_fact & tf) const;
|
||||
/**
|
||||
\brief Extract the values of the inner relation columns from the relation fact.
|
||||
*/
|
||||
void extract_other_fact(const relation_fact rf, relation_fact & of) const;
|
||||
|
||||
relation_base * mk_empty_inner();
|
||||
relation_base * mk_full_inner(func_decl* pred);
|
||||
|
||||
|
||||
void complement_self(func_decl* pred);
|
||||
|
||||
void collect_live_relation_indexes(idx_set & res) const;
|
||||
|
||||
|
||||
/**
|
||||
\brief Try to modify relations in \c rels so that they have the same columns corresponding to the table
|
||||
and the inner relation (so that the union can be perofrmed on theim in a straightforward way).
|
||||
|
||||
Relations in \c rels must all have equal signature.
|
||||
|
||||
Even if the function fails and false is returned, some relations may already be modified. They are
|
||||
in a valid state, but with different specification.
|
||||
*/
|
||||
static bool try_unify_specifications(ptr_vector<finite_product_relation> & rels);
|
||||
|
||||
bool try_modify_specification(const bool * table_cols);
|
||||
|
||||
virtual bool can_swap(const relation_base & r) const
|
||||
{ return &get_plugin()==&r.get_plugin(); }
|
||||
|
||||
/**
|
||||
\brief Swap content of the current relation with the content of \c r.
|
||||
|
||||
Both relations must come from the same plugin and be of the same signature.
|
||||
*/
|
||||
virtual void swap(relation_base & r);
|
||||
|
||||
/**
|
||||
\brief Create a \c finite_product_relation object.
|
||||
*/
|
||||
finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s,
|
||||
const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind);
|
||||
finite_product_relation(const finite_product_relation & r);
|
||||
virtual ~finite_product_relation();
|
||||
public:
|
||||
context & get_context() const;
|
||||
finite_product_relation_plugin & get_plugin() const {
|
||||
return static_cast<finite_product_relation_plugin &>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
bool is_table_column(unsigned col_idx) const { return m_sig2table[col_idx]!=UINT_MAX; }
|
||||
|
||||
const table_base & get_table() const { return *m_table; }
|
||||
|
||||
const relation_base & get_inner_rel(table_element idx) const
|
||||
{ SASSERT(idx<UINT_MAX); return get_inner_rel(static_cast<unsigned>(idx)); }
|
||||
|
||||
/**
|
||||
The function calls garbage_collect, so the internal state may change when it is called.
|
||||
*/
|
||||
virtual bool empty() const;
|
||||
void reset() { m_table->reset(); garbage_collect(false); }
|
||||
|
||||
virtual void add_fact(const relation_fact & f);
|
||||
virtual bool contains_fact(const relation_fact & f) const;
|
||||
|
||||
virtual finite_product_relation * clone() const;
|
||||
virtual finite_product_relation * complement(func_decl* p) const;
|
||||
|
||||
virtual void display(std::ostream & out) const;
|
||||
virtual void display_tuples(func_decl & pred, std::ostream & out) const;
|
||||
|
||||
virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); }
|
||||
virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); }
|
||||
|
||||
virtual void to_formula(expr_ref& fml) const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_FINITE_PRODUCT_RELATION_H_ */
|
||||
|
||||
1119
src/muz/rel/dl_instruction.cpp
Normal file
1119
src/muz/rel/dl_instruction.cpp
Normal file
File diff suppressed because it is too large
Load diff
349
src/muz/rel/dl_instruction.h
Normal file
349
src/muz/rel/dl_instruction.h
Normal 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_ */
|
||||
|
||||
653
src/muz/rel/dl_interval_relation.cpp
Normal file
653
src/muz/rel/dl_interval_relation.cpp
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_interval_relation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic interval reatlion.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-2-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "debug.h"
|
||||
#include "optional.h"
|
||||
#include "ast_pp.h"
|
||||
#include "dl_interval_relation.h"
|
||||
#include "dl_relation_manager.h"
|
||||
#include "bool_rewriter.h"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
// -------------------------
|
||||
// interval_relation_plugin
|
||||
|
||||
interval_relation_plugin::interval_relation_plugin(relation_manager& m):
|
||||
relation_plugin(interval_relation_plugin::get_name(), m),
|
||||
m_empty(m_dep),
|
||||
m_arith(get_ast_manager()) {
|
||||
}
|
||||
|
||||
bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) {
|
||||
for (unsigned i = 0; i < sig.size(); ++i) {
|
||||
if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
relation_base * interval_relation_plugin::mk_empty(const relation_signature & s) {
|
||||
return alloc(interval_relation, *this, s, true);
|
||||
}
|
||||
|
||||
relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) {
|
||||
return alloc(interval_relation, *this, s, false);
|
||||
}
|
||||
|
||||
class interval_relation_plugin::join_fn : public convenient_relation_join_fn {
|
||||
public:
|
||||
join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2)
|
||||
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) {
|
||||
interval_relation const& r1 = get(_r1);
|
||||
interval_relation const& r2 = get(_r2);
|
||||
interval_relation_plugin& p = r1.get_plugin();
|
||||
interval_relation* result = dynamic_cast<interval_relation*>(p.mk_full(0, get_result_signature()));
|
||||
result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if (!check_kind(r1) || !check_kind(r2)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2);
|
||||
}
|
||||
|
||||
|
||||
class interval_relation_plugin::project_fn : public convenient_relation_project_fn {
|
||||
public:
|
||||
project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
|
||||
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) {
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & _r) {
|
||||
interval_relation const& r = get(_r);
|
||||
interval_relation_plugin& p = r.get_plugin();
|
||||
interval_relation* result = dynamic_cast<interval_relation*>(p.mk_full(0, get_result_signature()));
|
||||
result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * interval_relation_plugin::mk_project_fn(const relation_base & r,
|
||||
unsigned col_cnt, const unsigned * removed_cols) {
|
||||
return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn {
|
||||
public:
|
||||
rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
|
||||
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & _r) {
|
||||
interval_relation const& r = get(_r);
|
||||
interval_relation_plugin& p = r.get_plugin();
|
||||
interval_relation* result = dynamic_cast<interval_relation*>(p.mk_full(0, get_result_signature()));
|
||||
result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r,
|
||||
unsigned cycle_len, const unsigned * permutation_cycle) {
|
||||
if(!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle);
|
||||
}
|
||||
|
||||
interval interval_relation_plugin::unite(interval const& src1, interval const& src2) {
|
||||
bool l_open = src1.is_lower_open();
|
||||
bool r_open = src1.is_upper_open();
|
||||
ext_numeral low = src1.inf();
|
||||
ext_numeral high = src1.sup();
|
||||
if (src2.inf() < low || (src2.inf() == low && l_open)) {
|
||||
low = src2.inf();
|
||||
l_open = src2.is_lower_open();
|
||||
}
|
||||
if (src2.sup() > high || (src2.sup() == high && r_open)) {
|
||||
high = src2.sup();
|
||||
r_open = src2.is_upper_open();
|
||||
}
|
||||
return interval(dep(), low, l_open, 0, high, r_open, 0);
|
||||
}
|
||||
|
||||
interval interval_relation_plugin::widen(interval const& src1, interval const& src2) {
|
||||
bool l_open = src1.is_lower_open();
|
||||
bool r_open = src1.is_upper_open();
|
||||
ext_numeral low = src1.inf();
|
||||
ext_numeral high = src1.sup();
|
||||
|
||||
if (src2.inf() < low || (low == src2.inf() && l_open && !src2.is_lower_open())) {
|
||||
low = ext_numeral(false);
|
||||
l_open = true;
|
||||
}
|
||||
if (high < src2.sup() || (src2.sup() == high && !r_open && src2.is_upper_open())) {
|
||||
high = ext_numeral(true);
|
||||
r_open = true;
|
||||
}
|
||||
return interval(dep(), low, l_open, 0, high, r_open, 0);
|
||||
}
|
||||
|
||||
interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) {
|
||||
isempty = false;
|
||||
if (is_empty(0, src1) || is_infinite(src2)) {
|
||||
return src1;
|
||||
}
|
||||
if (is_empty(0, src2) || is_infinite(src1)) {
|
||||
return src2;
|
||||
}
|
||||
bool l_open = src1.is_lower_open();
|
||||
bool r_open = src1.is_upper_open();
|
||||
ext_numeral low = src1.inf();
|
||||
ext_numeral high = src1.sup();
|
||||
if (src2.inf() > low || (src2.inf() == low && !l_open)) {
|
||||
low = src2.inf();
|
||||
l_open = src2.is_lower_open();
|
||||
}
|
||||
if (src2.sup() < high || (src2.sup() == high && !r_open)) {
|
||||
high = src2.sup();
|
||||
r_open = src2.is_upper_open();
|
||||
}
|
||||
if (low > high || (low == high && (l_open || r_open))) {
|
||||
isempty = true;
|
||||
return interval(dep());
|
||||
}
|
||||
else {
|
||||
return interval(dep(), low, l_open, 0, high, r_open, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool interval_relation_plugin::is_infinite(interval const& i) {
|
||||
return i.plus_infinity() && i.minus_infinity();
|
||||
}
|
||||
|
||||
bool interval_relation_plugin::is_empty(unsigned, interval const& i) {
|
||||
return i.sup() < i.inf();
|
||||
}
|
||||
|
||||
class interval_relation_plugin::union_fn : public relation_union_fn {
|
||||
bool m_is_widen;
|
||||
public:
|
||||
union_fn(bool is_widen) :
|
||||
m_is_widen(is_widen) {
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) {
|
||||
|
||||
TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n"););
|
||||
|
||||
interval_relation& r = get(_r);
|
||||
interval_relation const& src = get(_src);
|
||||
if (_delta) {
|
||||
interval_relation& d = get(*_delta);
|
||||
r.mk_union(src, &d, m_is_widen);
|
||||
}
|
||||
else {
|
||||
r.mk_union(src, 0, m_is_widen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn, false);
|
||||
}
|
||||
|
||||
relation_union_fn * interval_relation_plugin::mk_widen_fn(
|
||||
const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn, true);
|
||||
}
|
||||
|
||||
class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn {
|
||||
unsigned_vector m_identical_cols;
|
||||
public:
|
||||
filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols)
|
||||
: m_identical_cols(col_cnt, identical_cols) {}
|
||||
|
||||
virtual void operator()(relation_base & r) {
|
||||
interval_relation & pr = get(r);
|
||||
for (unsigned i = 1; i < m_identical_cols.size(); ++i) {
|
||||
unsigned c1 = m_identical_cols[0];
|
||||
unsigned c2 = m_identical_cols[i];
|
||||
pr.equate(c1, c2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn(
|
||||
const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) {
|
||||
if(!check_kind(t)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_identical_fn, col_cnt, identical_cols);
|
||||
}
|
||||
|
||||
|
||||
class interval_relation_plugin::filter_equal_fn : public relation_mutator_fn {
|
||||
unsigned m_col;
|
||||
rational m_value;
|
||||
public:
|
||||
filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col)
|
||||
: m_col(col) {
|
||||
arith_util arith(m.get_context().get_manager());
|
||||
VERIFY(arith.is_numeral(value, m_value));
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & _r) {
|
||||
interval_relation & r = get(_r);
|
||||
interval_relation_plugin & p = r.get_plugin();
|
||||
r.mk_intersect(m_col, interval(p.dep(), m_value));
|
||||
TRACE("interval_relation", tout << m_value << "\n"; r.display(tout););
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * interval_relation_plugin::mk_filter_equal_fn(const relation_base & r,
|
||||
const relation_element & value, unsigned col) {
|
||||
if(check_kind(r)) {
|
||||
return alloc(filter_equal_fn, get_manager(), value, col);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
class interval_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
|
||||
app_ref m_cond;
|
||||
public:
|
||||
filter_interpreted_fn(interval_relation const& t, app* cond):
|
||||
m_cond(cond, t.get_plugin().get_ast_manager()) {
|
||||
}
|
||||
|
||||
void operator()(relation_base& t) {
|
||||
get(t).filter_interpreted(m_cond);
|
||||
TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout););
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * interval_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) {
|
||||
if (check_kind(t)) {
|
||||
return alloc(filter_interpreted_fn, get(t), condition);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
interval_relation& interval_relation_plugin::get(relation_base& r) {
|
||||
return dynamic_cast<interval_relation&>(r);
|
||||
}
|
||||
|
||||
interval_relation const & interval_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<interval_relation const&>(r);
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
// interval_relation
|
||||
|
||||
interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty):
|
||||
vector_relation<interval>(p, s, is_empty, interval(p.dep()))
|
||||
{
|
||||
TRACE("interval_relation", tout << s.size() << "\n";);
|
||||
}
|
||||
|
||||
void interval_relation::add_fact(const relation_fact & f) {
|
||||
interval_relation r(get_plugin(), get_signature(), false);
|
||||
ast_manager& m = get_plugin().get_ast_manager();
|
||||
for (unsigned i = 0; i < f.size(); ++i) {
|
||||
app_ref eq(m);
|
||||
expr* e = f[i];
|
||||
eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e);
|
||||
r.filter_interpreted(eq.get());
|
||||
}
|
||||
mk_union(r, 0, false);
|
||||
}
|
||||
|
||||
bool interval_relation::contains_fact(const relation_fact & f) const {
|
||||
SASSERT(f.size() == get_signature().size());
|
||||
interval_relation_plugin& p = get_plugin();
|
||||
|
||||
for (unsigned i = 0; i < f.size(); ++i) {
|
||||
if (f[i] != f[find(i)]) {
|
||||
return false;
|
||||
}
|
||||
interval const& iv = (*this)[i];
|
||||
if (p.is_infinite(iv)) {
|
||||
continue;
|
||||
}
|
||||
rational v;
|
||||
if (p.m_arith.is_numeral(f[i], v)) {
|
||||
if (!iv.contains(v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TBD: may or must?
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
interval_relation * interval_relation::clone() const {
|
||||
interval_relation* result = alloc(interval_relation, get_plugin(), get_signature(), empty());
|
||||
result->copy(*this);
|
||||
return result;
|
||||
}
|
||||
|
||||
interval_relation * interval_relation::complement(func_decl*) const {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void interval_relation::to_formula(expr_ref& fml) const {
|
||||
ast_manager& m = get_plugin().get_ast_manager();
|
||||
arith_util& arith = get_plugin().m_arith;
|
||||
expr_ref_vector conjs(m);
|
||||
relation_signature const& sig = get_signature();
|
||||
for (unsigned i = 0; i < sig.size(); ++i) {
|
||||
if (i != find(i)) {
|
||||
conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]),
|
||||
m.mk_var(find(i), sig[find(i)])));
|
||||
continue;
|
||||
}
|
||||
interval const& iv = (*this)[i];
|
||||
sort* ty = sig[i];
|
||||
expr_ref var(m.mk_var(i, ty), m);
|
||||
if (!iv.minus_infinity()) {
|
||||
expr* lo = arith.mk_numeral(iv.get_lower_value(), ty);
|
||||
if (iv.is_lower_open()) {
|
||||
conjs.push_back(arith.mk_lt(lo, var));
|
||||
}
|
||||
else {
|
||||
conjs.push_back(arith.mk_le(lo, var));
|
||||
}
|
||||
}
|
||||
if (!iv.plus_infinity()) {
|
||||
expr* hi = arith.mk_numeral(iv.get_upper_value(), ty);
|
||||
if (iv.is_upper_open()) {
|
||||
conjs.push_back(arith.mk_lt(var, hi));
|
||||
}
|
||||
else {
|
||||
conjs.push_back(arith.mk_le(var, hi));
|
||||
}
|
||||
}
|
||||
}
|
||||
bool_rewriter br(m);
|
||||
br.mk_and(conjs.size(), conjs.c_ptr(), fml);
|
||||
}
|
||||
|
||||
|
||||
void interval_relation::display_index(unsigned i, interval const& j, std::ostream & out) const {
|
||||
out << i << " in " << j << "\n";
|
||||
}
|
||||
|
||||
interval_relation_plugin& interval_relation::get_plugin() const {
|
||||
return static_cast<interval_relation_plugin &>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
void interval_relation::mk_intersect(unsigned idx, interval const& i) {
|
||||
bool isempty;
|
||||
(*this)[idx] = mk_intersect((*this)[idx], i, isempty);
|
||||
if (isempty || is_empty(idx, (*this)[idx])) {
|
||||
set_empty();
|
||||
}
|
||||
}
|
||||
|
||||
void interval_relation::mk_rename_elem(interval& i, unsigned, unsigned const* ) {
|
||||
|
||||
}
|
||||
|
||||
void interval_relation::filter_interpreted(app* cond) {
|
||||
interval_relation_plugin& p = get_plugin();
|
||||
rational k;
|
||||
unsigned x, y;
|
||||
if (p.is_lt(cond, x, k, y)) {
|
||||
// 0 < x - y + k
|
||||
if (x == UINT_MAX) {
|
||||
// y < k
|
||||
mk_intersect(y, interval(p.dep(), k, true, false, 0));
|
||||
return;
|
||||
}
|
||||
if (y == UINT_MAX) {
|
||||
// -k < x
|
||||
mk_intersect(x, interval(p.dep(), -k, true, true, 0));
|
||||
return;
|
||||
}
|
||||
// y < x + k
|
||||
ext_numeral x_hi = (*this)[x].sup();
|
||||
ext_numeral y_lo = (*this)[y].inf();
|
||||
if (!x_hi.is_infinite()) {
|
||||
mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, 0));
|
||||
}
|
||||
if (!y_lo.is_infinite()) {
|
||||
mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, 0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool is_int = false;
|
||||
if (p.is_le(cond, x, k, y, is_int)) {
|
||||
// 0 <= x - y + k
|
||||
if (x == UINT_MAX) {
|
||||
// y <= k
|
||||
mk_intersect(y, interval(p.dep(), k, false, false, 0));
|
||||
return;
|
||||
}
|
||||
if (y == UINT_MAX) {
|
||||
// -k <= x
|
||||
mk_intersect(x, interval(p.dep(), -k, false, true, 0));
|
||||
return;
|
||||
}
|
||||
ext_numeral x_hi = (*this)[x].sup();
|
||||
ext_numeral y_lo = (*this)[y].inf();
|
||||
if (!x_hi.is_infinite()) {
|
||||
mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, 0));
|
||||
}
|
||||
if (!y_lo.is_infinite()) {
|
||||
mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, 0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (p.is_eq(cond, x, k, y)) {
|
||||
// y = x + k
|
||||
if (x == UINT_MAX) {
|
||||
SASSERT(y != UINT_MAX);
|
||||
mk_intersect(y, interval(p.dep(), k));
|
||||
return;
|
||||
}
|
||||
if (y == UINT_MAX) {
|
||||
// x = - k
|
||||
SASSERT(x != UINT_MAX);
|
||||
mk_intersect(x, interval(p.dep(), -k));
|
||||
return;
|
||||
}
|
||||
interval x_i = (*this)[x];
|
||||
interval y_i = (*this)[y];
|
||||
x_i += interval(p.dep(), k);
|
||||
y_i -= interval(p.dep(), k);
|
||||
mk_intersect(x, y_i);
|
||||
mk_intersect(y, x_i);
|
||||
}
|
||||
if (get_plugin().get_ast_manager().is_false(cond)) {
|
||||
set_empty();
|
||||
}
|
||||
}
|
||||
|
||||
bool interval_relation_plugin::is_linear(expr* e, unsigned& neg, unsigned& pos, rational& k, bool is_pos) const {
|
||||
#define SET_VAR(_idx_) \
|
||||
if (is_pos &&pos == UINT_MAX) { \
|
||||
pos = _idx_; \
|
||||
return true; \
|
||||
} \
|
||||
if (!is_pos && neg == UINT_MAX) { \
|
||||
neg = _idx_; \
|
||||
return true; \
|
||||
} \
|
||||
else { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
if (is_var(e)) {
|
||||
SET_VAR(to_var(e)->get_idx());
|
||||
}
|
||||
if (!is_app(e)) {
|
||||
return false;
|
||||
}
|
||||
app* a = to_app(e);
|
||||
|
||||
if (m_arith.is_add(e)) {
|
||||
for (unsigned i = 0; i < a->get_num_args(); ++i) {
|
||||
if (!is_linear(a->get_arg(i), neg, pos, k, is_pos)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (m_arith.is_sub(e)) {
|
||||
SASSERT(a->get_num_args() == 2);
|
||||
return
|
||||
is_linear(a->get_arg(0), neg, pos, k, is_pos) &&
|
||||
is_linear(a->get_arg(1), neg, pos, k, !is_pos);
|
||||
}
|
||||
rational k1;
|
||||
SASSERT(!m_arith.is_mul(e) || a->get_num_args() == 2);
|
||||
if (m_arith.is_mul(e) &&
|
||||
m_arith.is_numeral(a->get_arg(0), k1) &&
|
||||
k1.is_minus_one() &&
|
||||
is_var(a->get_arg(1))) {
|
||||
SET_VAR(to_var(a->get_arg(1))->get_idx());
|
||||
}
|
||||
|
||||
if (m_arith.is_numeral(e, k1)) {
|
||||
if (is_pos) {
|
||||
k += k1;
|
||||
}
|
||||
else {
|
||||
k -= k1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0 <= x - y + k
|
||||
bool interval_relation_plugin::is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const {
|
||||
ast_manager& m = get_ast_manager();
|
||||
k.reset();
|
||||
x = UINT_MAX;
|
||||
y = UINT_MAX;
|
||||
|
||||
if (m_arith.is_le(cond)) {
|
||||
is_int = m_arith.is_int(cond->get_arg(0));
|
||||
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
|
||||
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
|
||||
return (x != UINT_MAX || y != UINT_MAX);
|
||||
}
|
||||
if (m_arith.is_ge(cond)) {
|
||||
is_int = m_arith.is_int(cond->get_arg(0));
|
||||
if (!is_linear(cond->get_arg(0), y, x, k, true)) return false;
|
||||
if (!is_linear(cond->get_arg(1), y, x, k, false)) return false;
|
||||
return (x != UINT_MAX || y != UINT_MAX);
|
||||
}
|
||||
if (m_arith.is_lt(cond) && m_arith.is_int(cond->get_arg(0))) {
|
||||
is_int = true;
|
||||
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
|
||||
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
|
||||
k -= rational::one();
|
||||
return (x != UINT_MAX || y != UINT_MAX);
|
||||
}
|
||||
if (m_arith.is_gt(cond) && m_arith.is_int(cond->get_arg(0))) {
|
||||
is_int = true;
|
||||
if (!is_linear(cond->get_arg(0), y, x, k, true)) return false;
|
||||
if (!is_linear(cond->get_arg(1), y, x, k, false)) return false;
|
||||
k += rational::one();
|
||||
return (x != UINT_MAX || y != UINT_MAX);
|
||||
}
|
||||
if (m.is_not(cond) && is_app(cond->get_arg(0))) {
|
||||
// not (0 <= x - y + k)
|
||||
// <=>
|
||||
// 0 > x - y + k
|
||||
// <=>
|
||||
// 0 <= y - x - k - 1
|
||||
if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) {
|
||||
k.neg();
|
||||
k -= rational::one();
|
||||
std::swap(x, y);
|
||||
return true;
|
||||
}
|
||||
// not (0 < x - y + k)
|
||||
// <=>
|
||||
// 0 >= x - y + k
|
||||
// <=>
|
||||
// 0 <= y - x - k
|
||||
if (is_lt(to_app(cond->get_arg(0)), x, k, y)) {
|
||||
is_int = false;
|
||||
k.neg();
|
||||
std::swap(x, y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0 < x - y + k
|
||||
bool interval_relation_plugin::is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const {
|
||||
k.reset();
|
||||
x = UINT_MAX;
|
||||
y = UINT_MAX;
|
||||
if (m_arith.is_lt(cond) && m_arith.is_real(cond->get_arg(0))) {
|
||||
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
|
||||
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
|
||||
return (x != UINT_MAX || y != UINT_MAX);
|
||||
}
|
||||
if (m_arith.is_gt(cond) && m_arith.is_real(cond->get_arg(0))) {
|
||||
if (!is_linear(cond->get_arg(0), y, x, k, true)) return false;
|
||||
if (!is_linear(cond->get_arg(1), y, x, k, false)) return false;
|
||||
return (x != UINT_MAX || y != UINT_MAX);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0 = x - y + k
|
||||
bool interval_relation_plugin::is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const {
|
||||
ast_manager& m = get_ast_manager();
|
||||
k.reset();
|
||||
x = UINT_MAX;
|
||||
y = UINT_MAX;
|
||||
if (m.is_eq(cond)) {
|
||||
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
|
||||
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
|
||||
return (x != UINT_MAX || y != UINT_MAX);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
142
src/muz/rel/dl_interval_relation.h
Normal file
142
src/muz/rel/dl_interval_relation.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_interval_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic interval reatlion.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-2-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_INTERVAL_RELATION_H_
|
||||
#define _DL_INTERVAL_RELATION_H_
|
||||
|
||||
|
||||
#include "dl_context.h"
|
||||
#include "dl_relation_manager.h"
|
||||
#include "dl_base.h"
|
||||
#include "old_interval.h"
|
||||
#include "dl_vector_relation.h"
|
||||
#include "arith_decl_plugin.h"
|
||||
#include "basic_simplifier_plugin.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class interval_relation;
|
||||
|
||||
class interval_relation_plugin : public relation_plugin {
|
||||
v_dependency_manager m_dep;
|
||||
interval m_empty;
|
||||
arith_util m_arith;
|
||||
|
||||
class join_fn;
|
||||
class project_fn;
|
||||
class rename_fn;
|
||||
class union_fn;
|
||||
class filter_equal_fn;
|
||||
class filter_identical_fn;
|
||||
class filter_interpreted_fn;
|
||||
friend class interval_relation;
|
||||
|
||||
interval unite(interval const& src1, interval const& src2);
|
||||
interval widen(interval const& src1, interval const& src2);
|
||||
interval meet(interval const& src1, interval const& src2, bool& is_empty);
|
||||
|
||||
v_dependency_manager & dep() const { return const_cast<v_dependency_manager&>(m_dep); }
|
||||
|
||||
public:
|
||||
interval_relation_plugin(relation_manager& m);
|
||||
virtual bool can_handle_signature(const relation_signature & s);
|
||||
static symbol get_name() { return symbol("interval_relation"); }
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
|
||||
unsigned col);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
|
||||
static bool is_empty(unsigned idx, interval const& i);
|
||||
static bool is_infinite(interval const& i);
|
||||
|
||||
private:
|
||||
static interval_relation& get(relation_base& r);
|
||||
static interval_relation const & get(relation_base const& r);
|
||||
|
||||
bool is_linear(expr* e, unsigned& pos, unsigned& neg, rational& k, bool is_pos) const;
|
||||
|
||||
// x + k <= y
|
||||
bool is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const;
|
||||
// x + k < y
|
||||
bool is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const;
|
||||
// x + k = y
|
||||
bool is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const;
|
||||
};
|
||||
|
||||
|
||||
class interval_relation : public vector_relation<interval> {
|
||||
friend class interval_relation_plugin;
|
||||
friend class interval_relation_plugin::filter_equal_fn;
|
||||
public:
|
||||
interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty);
|
||||
|
||||
virtual void add_fact(const relation_fact & f);
|
||||
virtual bool contains_fact(const relation_fact & f) const;
|
||||
virtual interval_relation * clone() const;
|
||||
virtual interval_relation * complement(func_decl*) const;
|
||||
virtual void to_formula(expr_ref& fml) const;
|
||||
interval_relation_plugin& get_plugin() const;
|
||||
|
||||
void filter_interpreted(app* cond);
|
||||
virtual bool is_precise() const { return false; }
|
||||
|
||||
private:
|
||||
|
||||
virtual interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const {
|
||||
return get_plugin().meet(t1, t2, is_empty);
|
||||
}
|
||||
|
||||
virtual interval mk_unite(interval const& t1, interval const& t2) const { return get_plugin().unite(t1,t2); }
|
||||
|
||||
virtual interval mk_widen(interval const& t1, interval const& t2) const { return get_plugin().widen(t1,t2); }
|
||||
|
||||
virtual bool is_subset_of(interval const& t1, interval const& t2) const { NOT_IMPLEMENTED_YET(); return false; }
|
||||
|
||||
virtual bool is_full(interval const& t) const {
|
||||
return interval_relation_plugin::is_infinite(t);
|
||||
}
|
||||
|
||||
virtual bool is_empty(unsigned idx, interval const& t) const {
|
||||
return interval_relation_plugin::is_empty(idx, t);
|
||||
}
|
||||
|
||||
virtual void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle);
|
||||
|
||||
virtual void display_index(unsigned idx, interval const & i, std::ostream& out) const;
|
||||
|
||||
void mk_intersect(unsigned idx, interval const& i);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
467
src/muz/rel/dl_lazy_table.cpp
Normal file
467
src/muz/rel/dl_lazy_table.cpp
Normal 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
305
src/muz/rel/dl_lazy_table.h
Normal 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
|
||||
879
src/muz/rel/dl_mk_explanations.cpp
Normal file
879
src/muz/rel/dl_mk_explanations.cpp
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_explanations.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-11-08.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include"ast_pp.h"
|
||||
#include"ast_smt_pp.h"
|
||||
#include"dl_finite_product_relation.h"
|
||||
#include"dl_product_relation.h"
|
||||
#include"dl_sieve_relation.h"
|
||||
#include"dl_mk_explanations.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// explanation_relation_plugin declaration
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class explanation_relation;
|
||||
|
||||
class explanation_relation_plugin : public relation_plugin {
|
||||
friend class explanation_relation;
|
||||
|
||||
class join_fn;
|
||||
class project_fn;
|
||||
class rename_fn;
|
||||
class union_fn;
|
||||
class foreign_union_fn;
|
||||
class assignment_filter_fn;
|
||||
class negation_filter_fn;
|
||||
class intersection_filter_fn;
|
||||
|
||||
bool m_relation_level_explanations;
|
||||
|
||||
func_decl_ref m_union_decl;
|
||||
|
||||
vector<ptr_vector<explanation_relation> > m_pool;
|
||||
|
||||
|
||||
app * mk_union(app * a1, app * a2) {
|
||||
return get_ast_manager().mk_app(m_union_decl, a1, a2);
|
||||
}
|
||||
|
||||
public:
|
||||
static symbol get_name(bool relation_level) {
|
||||
return symbol(relation_level ? "relation_explanation" : "fact_explanation");
|
||||
}
|
||||
|
||||
explanation_relation_plugin(bool relation_level, relation_manager & manager)
|
||||
: relation_plugin(get_name(relation_level), manager),
|
||||
m_relation_level_explanations(relation_level),
|
||||
m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {}
|
||||
|
||||
~explanation_relation_plugin() {
|
||||
for (unsigned i = 0; i < m_pool.size(); ++i) {
|
||||
for (unsigned j = 0; j < m_pool[i].size(); ++j) {
|
||||
dealloc(m_pool[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool can_handle_signature(const relation_signature & s) {
|
||||
unsigned n=s.size();
|
||||
for (unsigned i=0; i<n; i++) {
|
||||
if (!get_context().get_decl_util().is_rule_sort(s[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
|
||||
void recycle(explanation_relation* r);
|
||||
|
||||
protected:
|
||||
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
|
||||
const relation_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols);
|
||||
virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t,
|
||||
const relation_base & src, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * src_cols);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// explanation_relation
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class explanation_relation : public relation_base {
|
||||
friend class explanation_relation_plugin;
|
||||
friend class explanation_relation_plugin::join_fn;
|
||||
friend class explanation_relation_plugin::project_fn;
|
||||
friend class explanation_relation_plugin::rename_fn;
|
||||
friend class explanation_relation_plugin::union_fn;
|
||||
friend class explanation_relation_plugin::foreign_union_fn;
|
||||
friend class explanation_relation_plugin::assignment_filter_fn;
|
||||
friend class explanation_relation_plugin::intersection_filter_fn;
|
||||
|
||||
bool m_empty;
|
||||
/**
|
||||
Valid only if \c !m_empty.
|
||||
|
||||
Zero elements mean undefined.
|
||||
*/
|
||||
relation_fact m_data;
|
||||
|
||||
explanation_relation(explanation_relation_plugin & p, const relation_signature & s)
|
||||
: relation_base(p, s), m_empty(true), m_data(p.get_ast_manager()) {
|
||||
|
||||
DEBUG_CODE(
|
||||
unsigned sz = s.size();
|
||||
for (unsigned i=0;i<sz; i++) {
|
||||
SASSERT( p.get_context().get_decl_util().is_rule_sort(s[i]) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void assign_data(const relation_fact & f) {
|
||||
m_empty = false;
|
||||
|
||||
unsigned n=get_signature().size();
|
||||
SASSERT(f.size()==n);
|
||||
m_data.reset();
|
||||
m_data.append(n, f.c_ptr());
|
||||
}
|
||||
void set_undefined() {
|
||||
m_empty = false;
|
||||
m_data.reset();
|
||||
m_data.resize(get_signature().size());
|
||||
}
|
||||
void unite_with_data(const relation_fact & f) {
|
||||
if (empty()) {
|
||||
assign_data(f);
|
||||
return;
|
||||
}
|
||||
unsigned n=get_signature().size();
|
||||
SASSERT(f.size()==n);
|
||||
for (unsigned i=0; i<n; i++) {
|
||||
SASSERT(!is_undefined(i));
|
||||
m_data[i] = get_plugin().mk_union(m_data[i], f[i]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void deallocate() {
|
||||
get_plugin().recycle(this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explanation_relation_plugin & get_plugin() const {
|
||||
return static_cast<explanation_relation_plugin &>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
virtual void to_formula(expr_ref& fml) const {
|
||||
ast_manager& m = fml.get_manager();
|
||||
fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]);
|
||||
}
|
||||
|
||||
bool is_undefined(unsigned col_idx) const {
|
||||
return m_data[col_idx]==0;
|
||||
}
|
||||
bool no_undefined() const {
|
||||
if (empty()) {
|
||||
return true;
|
||||
}
|
||||
unsigned n = get_signature().size();
|
||||
for (unsigned i=0; i<n; i++) {
|
||||
if (is_undefined(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool empty() const { return m_empty; }
|
||||
|
||||
virtual void reset() {
|
||||
m_empty = true;
|
||||
}
|
||||
|
||||
virtual void add_fact(const relation_fact & f) {
|
||||
SASSERT(empty());
|
||||
assign_data(f);
|
||||
}
|
||||
|
||||
virtual bool contains_fact(const relation_fact & f) const {
|
||||
UNREACHABLE();
|
||||
throw 0;
|
||||
}
|
||||
|
||||
virtual explanation_relation * clone() const {
|
||||
explanation_relation * res = static_cast<explanation_relation *>(get_plugin().mk_empty(get_signature()));
|
||||
res->m_empty = m_empty;
|
||||
SASSERT(res->m_data.empty());
|
||||
res->m_data.append(m_data);
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual relation_base * complement(func_decl* pred) const {
|
||||
explanation_relation * res = static_cast<explanation_relation *>(get_plugin().mk_empty(get_signature()));
|
||||
if (empty()) {
|
||||
res->set_undefined();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void display_explanation(app * expl, std::ostream & out) const {
|
||||
if (expl) {
|
||||
//TODO: some nice explanation output
|
||||
ast_smt_pp pp(get_plugin().get_ast_manager());
|
||||
pp.display_expr_smt2(out, expl);
|
||||
}
|
||||
else {
|
||||
out << "<undefined>";
|
||||
}
|
||||
}
|
||||
|
||||
virtual void display(std::ostream & out) const {
|
||||
if (empty()) {
|
||||
out << "<empty explanation relation>\n";
|
||||
return;
|
||||
}
|
||||
unsigned sz = get_signature().size();
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
if (i!=0) {
|
||||
out << ", ";
|
||||
}
|
||||
display_explanation(m_data[0], out);
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
virtual unsigned get_size_estimate() const { return empty() ? 0 : 1; }
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// explanation_relation_plugin
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
|
||||
relation_base * explanation_relation_plugin::mk_empty(const relation_signature & s) {
|
||||
if (m_pool.size() > s.size() && !m_pool[s.size()].empty()) {
|
||||
explanation_relation* r = m_pool[s.size()].back();
|
||||
m_pool[s.size()].pop_back();
|
||||
r->m_empty = true;
|
||||
r->m_data.reset();
|
||||
return r;
|
||||
}
|
||||
return alloc(explanation_relation, *this, s);
|
||||
}
|
||||
|
||||
void explanation_relation_plugin::recycle(explanation_relation* r) {
|
||||
relation_signature const& sig = r->get_signature();
|
||||
if (m_pool.size() <= sig.size()) {
|
||||
m_pool.resize(sig.size()+1);
|
||||
}
|
||||
m_pool[sig.size()].push_back(r);
|
||||
}
|
||||
|
||||
|
||||
class explanation_relation_plugin::join_fn : public convenient_relation_join_fn {
|
||||
public:
|
||||
join_fn(const relation_signature & sig1, const relation_signature & sig2)
|
||||
: convenient_relation_join_fn(sig1, sig2, 0, 0, 0) {}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) {
|
||||
const explanation_relation & r1 = static_cast<const explanation_relation &>(r1_0);
|
||||
const explanation_relation & r2 = static_cast<const explanation_relation &>(r2_0);
|
||||
explanation_relation_plugin & plugin = r1.get_plugin();
|
||||
|
||||
explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
|
||||
if (!r1.empty() && !r2.empty()) {
|
||||
res->m_empty = false;
|
||||
SASSERT(res->m_data.empty());
|
||||
res->m_data.append(r1.m_data);
|
||||
res->m_data.append(r2.m_data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
if (col_cnt!=0) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, r1.get_signature(), r2.get_signature());
|
||||
}
|
||||
|
||||
|
||||
class explanation_relation_plugin::project_fn : public convenient_relation_project_fn {
|
||||
public:
|
||||
project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols)
|
||||
: convenient_relation_project_fn(sig, col_cnt, removed_cols) {}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r0) {
|
||||
const explanation_relation & r = static_cast<const explanation_relation &>(r0);
|
||||
explanation_relation_plugin & plugin = r.get_plugin();
|
||||
|
||||
explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
|
||||
if (!r.empty()) {
|
||||
relation_fact proj_data = r.m_data;
|
||||
project_out_vector_columns(proj_data, m_removed_cols);
|
||||
res->assign_data(proj_data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt,
|
||||
const unsigned * removed_cols) {
|
||||
if (&r.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
|
||||
class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn {
|
||||
public:
|
||||
rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle)
|
||||
: convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r0) {
|
||||
const explanation_relation & r = static_cast<const explanation_relation &>(r0);
|
||||
explanation_relation_plugin & plugin = r.get_plugin();
|
||||
|
||||
explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
|
||||
if (!r.empty()) {
|
||||
relation_fact permutated_data = r.m_data;
|
||||
permutate_by_cycle(permutated_data, m_cycle);
|
||||
res->assign_data(permutated_data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r,
|
||||
unsigned permutation_cycle_len, const unsigned * permutation_cycle) {
|
||||
return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle);
|
||||
}
|
||||
|
||||
|
||||
class explanation_relation_plugin::union_fn : public relation_union_fn {
|
||||
scoped_ptr<relation_union_fn> m_delta_union_fun;
|
||||
public:
|
||||
virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) {
|
||||
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
|
||||
const explanation_relation & src = static_cast<const explanation_relation &>(src0);
|
||||
explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(delta0) : 0;
|
||||
explanation_relation_plugin & plugin = tgt.get_plugin();
|
||||
|
||||
if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (src.empty()) {
|
||||
return;
|
||||
}
|
||||
if (plugin.m_relation_level_explanations) {
|
||||
tgt.unite_with_data(src.m_data);
|
||||
if (delta) {
|
||||
if (!m_delta_union_fun) {
|
||||
m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src);
|
||||
SASSERT(m_delta_union_fun);
|
||||
}
|
||||
(*m_delta_union_fun)(*delta, src);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tgt.empty()) {
|
||||
tgt.assign_data(src.m_data);
|
||||
if (delta && delta->empty()) {
|
||||
delta->assign_data(src.m_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class explanation_relation_plugin::foreign_union_fn : public relation_union_fn {
|
||||
scoped_ptr<relation_union_fn> m_delta_union_fun;
|
||||
public:
|
||||
virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) {
|
||||
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
|
||||
explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(delta0) : 0;
|
||||
|
||||
if (src.empty()) {
|
||||
return;
|
||||
}
|
||||
tgt.set_undefined();
|
||||
if (delta) {
|
||||
delta->set_undefined();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (!check_kind(tgt) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
if (!check_kind(src)) {
|
||||
//this is to handle the product relation
|
||||
return alloc(foreign_union_fn);
|
||||
}
|
||||
return alloc(union_fn);
|
||||
}
|
||||
|
||||
class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn {
|
||||
ast_manager & m_manager;
|
||||
var_subst & m_subst;
|
||||
unsigned m_col_idx;
|
||||
app_ref m_new_rule;
|
||||
public:
|
||||
assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule)
|
||||
: m_manager(ctx.get_manager()),
|
||||
m_subst(ctx.get_var_subst()),
|
||||
m_col_idx(col_idx),
|
||||
m_new_rule(new_rule) {}
|
||||
|
||||
virtual void operator()(relation_base & r0) {
|
||||
explanation_relation & r = static_cast<explanation_relation &>(r0);
|
||||
|
||||
if (!r.is_undefined(m_col_idx)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
unsigned sz = r.get_signature().size();
|
||||
ptr_vector<expr> subst_arg;
|
||||
subst_arg.resize(sz, 0);
|
||||
unsigned ofs = sz-1;
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
SASSERT(!r.is_undefined(i) || !contains_var(m_new_rule, i));
|
||||
subst_arg[ofs-i] = r.m_data.get(i);
|
||||
}
|
||||
expr_ref res(m_manager);
|
||||
m_subst(m_new_rule, subst_arg.size(), subst_arg.c_ptr(), res);
|
||||
r.m_data[m_col_idx] = to_app(res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * explanation_relation_plugin::mk_filter_interpreted_fn(const relation_base & r,
|
||||
app * cond) {
|
||||
if (&r.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
ast_manager & m = get_ast_manager();
|
||||
if (!m.is_eq(cond)) {
|
||||
return 0;
|
||||
}
|
||||
expr * arg1 = cond->get_arg(0);
|
||||
expr * arg2 = cond->get_arg(1);
|
||||
|
||||
if (is_var(arg2)) {
|
||||
std::swap(arg1, arg2);
|
||||
}
|
||||
|
||||
if (!is_var(arg1) || !is_app(arg2)) {
|
||||
return 0;
|
||||
}
|
||||
var * col_var = to_var(arg1);
|
||||
app * new_rule = to_app(arg2);
|
||||
if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) {
|
||||
return 0;
|
||||
}
|
||||
unsigned col_idx = col_var->get_idx();
|
||||
|
||||
return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager()));
|
||||
}
|
||||
|
||||
|
||||
class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn {
|
||||
public:
|
||||
virtual void operator()(relation_base & r, const relation_base & neg) {
|
||||
if (!neg.empty()) {
|
||||
r.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r,
|
||||
const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols,
|
||||
const unsigned * negated_cols) {
|
||||
if (&r.get_plugin()!=this || &neg.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(negation_filter_fn);
|
||||
}
|
||||
|
||||
class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn {
|
||||
func_decl_ref m_union_decl;
|
||||
public:
|
||||
intersection_filter_fn(explanation_relation_plugin & plugin)
|
||||
: m_union_decl(plugin.m_union_decl) {}
|
||||
|
||||
virtual void operator()(relation_base & tgt0, const relation_base & src0) {
|
||||
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
|
||||
const explanation_relation & src = static_cast<const explanation_relation &>(src0);
|
||||
|
||||
if (src.empty()) {
|
||||
tgt.reset();
|
||||
return;
|
||||
}
|
||||
if (tgt.empty()) {
|
||||
return;
|
||||
}
|
||||
unsigned sz = tgt.get_signature().size();
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
if (src.is_undefined(i)) {
|
||||
continue;
|
||||
}
|
||||
app * curr_src = src.m_data.get(i);
|
||||
if (tgt.is_undefined(i)) {
|
||||
tgt.m_data.set(i, curr_src);
|
||||
continue;
|
||||
}
|
||||
app * curr_tgt = tgt.m_data.get(i);
|
||||
if (curr_tgt->get_decl()==m_union_decl.get()) {
|
||||
if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) {
|
||||
tgt.m_data.set(i, curr_src);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//the intersection is imprecise because we do nothing here, but it is good enough for
|
||||
//the purpose of explanations
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn(
|
||||
const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt,
|
||||
const unsigned * tgt_cols, const unsigned * src_cols) {
|
||||
if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
//this checks the join is one to one on all columns
|
||||
if (tgt.get_signature()!=src.get_signature()
|
||||
|| joined_col_cnt!=tgt.get_signature().size()
|
||||
|| !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) {
|
||||
return 0;
|
||||
}
|
||||
counter ctr;
|
||||
ctr.count(joined_col_cnt, tgt_cols);
|
||||
if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(intersection_filter_fn, *this);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// mk_explanations
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
|
||||
mk_explanations::mk_explanations(context & ctx)
|
||||
: plugin(50000),
|
||||
m_manager(ctx.get_manager()),
|
||||
m_context(ctx),
|
||||
m_decl_util(ctx.get_decl_util()),
|
||||
m_relation_level(ctx.explanations_on_relation_level()),
|
||||
m_pinned(m_manager) {
|
||||
m_e_sort = m_decl_util.mk_rule_sort();
|
||||
m_pinned.push_back(m_e_sort);
|
||||
|
||||
relation_manager & rmgr = ctx.get_rel_context()->get_rmanager();
|
||||
symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level);
|
||||
m_er_plugin = static_cast<explanation_relation_plugin *>(rmgr.get_relation_plugin(er_symbol));
|
||||
if (!m_er_plugin) {
|
||||
m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr);
|
||||
rmgr.register_plugin(m_er_plugin);
|
||||
if (!m_relation_level) {
|
||||
DEBUG_CODE(
|
||||
finite_product_relation_plugin * dummy;
|
||||
SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy));
|
||||
);
|
||||
rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr));
|
||||
}
|
||||
}
|
||||
DEBUG_CODE(
|
||||
if (!m_relation_level) {
|
||||
finite_product_relation_plugin * dummy;
|
||||
SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
func_decl * mk_explanations::get_union_decl(context & ctx) {
|
||||
ast_manager & m = ctx.get_manager();
|
||||
sort_ref s(ctx.get_decl_util().mk_rule_sort(), m);
|
||||
//can it happen that the function name would collide with some other symbol?
|
||||
//if functions can be overloaded by their ranges, it should be fine.
|
||||
return m.mk_func_decl(symbol("e_union"), s, s, s);
|
||||
}
|
||||
|
||||
void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) {
|
||||
SASSERT(m_relation_level);
|
||||
|
||||
relation_manager & rmgr = m_context.get_rel_context()->get_rmanager();
|
||||
unsigned sz = e_decl->get_arity();
|
||||
relation_signature sig;
|
||||
rmgr.from_predicate(e_decl, sig);
|
||||
|
||||
svector<bool> inner_sieve(sz-1, true);
|
||||
inner_sieve.push_back(false);
|
||||
|
||||
svector<bool> expl_sieve(sz-1, false);
|
||||
expl_sieve.push_back(true);
|
||||
|
||||
sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr);
|
||||
|
||||
family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id
|
||||
family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind);
|
||||
family_id expl_kind = m_er_plugin->get_kind();
|
||||
family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind);
|
||||
|
||||
product_relation_plugin::rel_spec product_spec;
|
||||
product_spec.push_back(inner_sieve_kind);
|
||||
product_spec.push_back(expl_sieve_kind);
|
||||
|
||||
family_id pred_kind =
|
||||
product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec);
|
||||
|
||||
rmgr.set_predicate_kind(e_decl, pred_kind);
|
||||
}
|
||||
|
||||
func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) {
|
||||
decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0);
|
||||
if (e->get_data().m_value==0) {
|
||||
relation_signature e_domain;
|
||||
e_domain.append(orig_decl->get_arity(), orig_decl->get_domain());
|
||||
e_domain.push_back(m_e_sort);
|
||||
func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"),
|
||||
e_domain.size(), e_domain.c_ptr(), orig_decl);
|
||||
m_pinned.push_back(new_decl);
|
||||
e->get_data().m_value = new_decl;
|
||||
|
||||
if (m_relation_level) {
|
||||
assign_rel_level_kind(new_decl, orig_decl);
|
||||
}
|
||||
}
|
||||
return e->get_data().m_value;
|
||||
}
|
||||
|
||||
app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) {
|
||||
expr_ref_vector args(m_manager);
|
||||
func_decl * e_decl = get_e_decl(lit->get_decl());
|
||||
args.append(lit->get_num_args(), lit->get_args());
|
||||
args.push_back(m_manager.mk_var(e_var_idx, m_e_sort));
|
||||
return m_manager.mk_app(e_decl, args.c_ptr());
|
||||
}
|
||||
|
||||
symbol mk_explanations::get_rule_symbol(rule * r) {
|
||||
if (r->name() == symbol::null) {
|
||||
std::stringstream sstm;
|
||||
r->display(m_context, sstm);
|
||||
std::string res = sstm.str();
|
||||
res = res.substr(0, res.find_last_not_of('\n')+1);
|
||||
return symbol(res.c_str());
|
||||
}
|
||||
else {
|
||||
return r->name();
|
||||
}
|
||||
}
|
||||
|
||||
rule * mk_explanations::get_e_rule(rule * r) {
|
||||
rule_counter ctr;
|
||||
ctr.count_rule_vars(m_manager, r);
|
||||
unsigned max_var;
|
||||
unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0;
|
||||
unsigned head_var = next_var++;
|
||||
app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager);
|
||||
|
||||
app_ref_vector e_tail(m_manager);
|
||||
svector<bool> neg_flags;
|
||||
unsigned pos_tail_sz = r->get_positive_tail_size();
|
||||
for (unsigned i=0; i<pos_tail_sz; i++) {
|
||||
unsigned e_var = next_var++;
|
||||
e_tail.push_back(get_e_lit(r->get_tail(i), e_var));
|
||||
neg_flags.push_back(false);
|
||||
}
|
||||
unsigned tail_sz = r->get_tail_size();
|
||||
for (unsigned i=pos_tail_sz; i<tail_sz; i++) {
|
||||
e_tail.push_back(r->get_tail(i));
|
||||
neg_flags.push_back(r->is_neg_tail(i));
|
||||
}
|
||||
|
||||
symbol rule_repr = get_rule_symbol(r);
|
||||
|
||||
expr_ref_vector rule_expr_args(m_manager);
|
||||
for (unsigned tail_idx=0; tail_idx<pos_tail_sz; tail_idx++) {
|
||||
app * tail = e_tail.get(tail_idx);
|
||||
if (true || m_relation_level) {
|
||||
//this adds the explanation term of the tail
|
||||
rule_expr_args.push_back(tail->get_arg(tail->get_num_args()-1));
|
||||
}
|
||||
else {
|
||||
//this adds argument values and the explanation term
|
||||
//(values will be substituted for variables at runtime by the finite_product_relation)
|
||||
rule_expr_args.append(tail->get_num_args(), tail->get_args());
|
||||
}
|
||||
}
|
||||
//rule_expr contains rule function with string representation of the rule as symbol and
|
||||
//for each positive uninterpreted tail it contains its argument values and its explanation term
|
||||
expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr());
|
||||
|
||||
app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager);
|
||||
e_tail.push_back(e_record);
|
||||
neg_flags.push_back(false);
|
||||
SASSERT(e_tail.size()==neg_flags.size());
|
||||
|
||||
return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr());
|
||||
}
|
||||
|
||||
void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) {
|
||||
rule_set::iterator rit = src.begin();
|
||||
rule_set::iterator rend = src.end();
|
||||
for (; rit!=rend; ++rit) {
|
||||
rule * e_rule = get_e_rule(*rit);
|
||||
dst.add_rule(e_rule);
|
||||
}
|
||||
|
||||
//add rules that will (for output predicates) copy facts from explained relations back to
|
||||
//the original ones
|
||||
expr_ref_vector lit_args(m_manager);
|
||||
decl_set::iterator pit = src.get_output_predicates().begin();
|
||||
decl_set::iterator pend = src.get_output_predicates().end();
|
||||
for (; pit != pend; ++pit) {
|
||||
func_decl * orig_decl = *pit;
|
||||
|
||||
lit_args.reset();
|
||||
unsigned arity = orig_decl->get_arity();
|
||||
for (unsigned i=0; i<arity; i++) {
|
||||
lit_args.push_back(m_manager.mk_var(i, orig_decl->get_domain(i)));
|
||||
}
|
||||
app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager);
|
||||
app_ref e_lit(get_e_lit(orig_lit, arity), m_manager);
|
||||
app * tail[] = { e_lit.get() };
|
||||
dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig,
|
||||
relation_base & e_rel) {
|
||||
SASSERT(m_e_fact_relation);
|
||||
SASSERT(e_rel.get_plugin().is_product_relation());
|
||||
|
||||
product_relation & prod_rel = static_cast<product_relation &>(e_rel);
|
||||
SASSERT(prod_rel.size()==2);
|
||||
SASSERT(prod_rel[0].get_plugin().is_sieve_relation());
|
||||
SASSERT(prod_rel[1].get_plugin().is_sieve_relation());
|
||||
sieve_relation * srels[] = {
|
||||
static_cast<sieve_relation *>(&prod_rel[0]),
|
||||
static_cast<sieve_relation *>(&prod_rel[1]) };
|
||||
if (&srels[0]->get_inner().get_plugin()==m_er_plugin) {
|
||||
std::swap(srels[0], srels[1]);
|
||||
}
|
||||
SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin());
|
||||
SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin);
|
||||
|
||||
relation_base & new_orig = srels[0]->get_inner();
|
||||
explanation_relation & expl_rel = static_cast<explanation_relation &>(srels[1]->get_inner());
|
||||
|
||||
{
|
||||
scoped_ptr<relation_union_fn> orig_union_fun = rmgr.mk_union_fn(new_orig, orig);
|
||||
SASSERT(orig_union_fun);
|
||||
(*orig_union_fun)(new_orig, orig);
|
||||
}
|
||||
|
||||
{
|
||||
scoped_ptr<relation_union_fn> expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation);
|
||||
SASSERT(expl_union_fun);
|
||||
(*expl_union_fun)(expl_rel, *m_e_fact_relation);
|
||||
}
|
||||
}
|
||||
|
||||
void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) {
|
||||
|
||||
if (!m_e_fact_relation) {
|
||||
relation_signature expl_singleton_sig;
|
||||
expl_singleton_sig.push_back(m_e_sort);
|
||||
|
||||
relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind());
|
||||
relation_fact es_fact(m_manager);
|
||||
es_fact.push_back(m_decl_util.mk_fact(symbol("fact")));
|
||||
expl_singleton->add_fact(es_fact);
|
||||
|
||||
SASSERT(&expl_singleton->get_plugin()==m_er_plugin);
|
||||
m_e_fact_relation = static_cast<explanation_relation *>(expl_singleton);
|
||||
}
|
||||
func_decl_set const& predicates = m_context.get_predicates();
|
||||
decl_set::iterator it = predicates.begin();
|
||||
decl_set::iterator end = predicates.end();
|
||||
for (; it!=end; ++it) {
|
||||
func_decl * orig_decl = *it;
|
||||
func_decl * e_decl = get_e_decl(orig_decl);
|
||||
|
||||
if (!rmgr.try_get_relation(orig_decl) &&
|
||||
!src.contains(orig_decl)) {
|
||||
// there are no facts or rules for this predicate
|
||||
continue;
|
||||
}
|
||||
|
||||
dst.inherit_predicate(src, orig_decl, e_decl);
|
||||
|
||||
relation_base & orig_rel = rmgr.get_relation(orig_decl);
|
||||
relation_base & e_rel = rmgr.get_relation(e_decl);
|
||||
SASSERT(e_rel.empty()); //the e_rel should be a new relation
|
||||
|
||||
if (m_relation_level) {
|
||||
translate_rel_level_relation(rmgr, orig_rel, e_rel);
|
||||
}
|
||||
else {
|
||||
scoped_ptr<relation_join_fn> product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0);
|
||||
SASSERT(product_fun);
|
||||
scoped_rel<relation_base> aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation);
|
||||
scoped_ptr<relation_union_fn> union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel);
|
||||
SASSERT(union_fun);
|
||||
(*union_fun)(e_rel, *aux_extended_rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule_set * mk_explanations::operator()(rule_set const & source) {
|
||||
|
||||
if (source.empty()) {
|
||||
return 0;
|
||||
}
|
||||
if (!m_context.generate_explanations()) {
|
||||
return 0;
|
||||
}
|
||||
rule_set * res = alloc(rule_set, m_context);
|
||||
transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res);
|
||||
transform_rules(source, *res);
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
86
src/muz/rel/dl_mk_explanations.h
Normal file
86
src/muz/rel/dl_mk_explanations.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_explanations.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-11-08.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _DL_MK_EXPLANATIONS_H_
|
||||
#define _DL_MK_EXPLANATIONS_H_
|
||||
|
||||
#include "dl_context.h"
|
||||
#include "dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class explanation_relation;
|
||||
class explanation_relation_plugin;
|
||||
|
||||
class mk_explanations : public rule_transformer::plugin {
|
||||
|
||||
typedef obj_map<func_decl, func_decl *> decl_map;
|
||||
|
||||
ast_manager & m_manager;
|
||||
context & m_context;
|
||||
dl_decl_util & m_decl_util;
|
||||
bool m_relation_level;
|
||||
ast_ref_vector m_pinned;
|
||||
explanation_relation_plugin * m_er_plugin;
|
||||
sort * m_e_sort;
|
||||
scoped_rel<explanation_relation> m_e_fact_relation;
|
||||
|
||||
decl_map m_e_decl_map;
|
||||
|
||||
symbol get_rule_symbol(rule * r);
|
||||
|
||||
app * get_e_lit(app * lit, unsigned e_var_idx);
|
||||
rule * get_e_rule(rule * r);
|
||||
|
||||
/**
|
||||
If \c m_relation_level is true, ensure \c e_decl predicate will be represented by
|
||||
the right relation object. \c orig is the predicate corresponding to \c e_decl without
|
||||
the explanation column.
|
||||
*/
|
||||
void assign_rel_level_kind(func_decl * e_decl, func_decl * orig);
|
||||
void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel);
|
||||
|
||||
void transform_rules(const rule_set & src, rule_set & dst);
|
||||
|
||||
void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst);
|
||||
public:
|
||||
/**
|
||||
If relation_level is true, the explanation will not be stored for each fact,
|
||||
but we will rather store history of the whole relation.
|
||||
*/
|
||||
mk_explanations(context & ctx);
|
||||
|
||||
/**
|
||||
\brief Return explanation predicate that corresponds to \c orig_decl.
|
||||
*/
|
||||
func_decl * get_e_decl(func_decl * orig_decl);
|
||||
|
||||
static func_decl * get_union_decl(context & ctx);
|
||||
func_decl * get_union_decl() const {
|
||||
return get_union_decl(m_context);
|
||||
}
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
|
||||
static expr* get_explanation(relation_base const& r);
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_EXPLANATIONS_H_ */
|
||||
|
||||
155
src/muz/rel/dl_mk_partial_equiv.cpp
Normal file
155
src/muz/rel/dl_mk_partial_equiv.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_partial_equiv.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Rule transformer which identifies predicates that are partial equivalence relations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-05-14
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "dl_mk_partial_equiv.h"
|
||||
#include "dl_relation_manager.h"
|
||||
#include "ast_pp.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) {
|
||||
func_decl* p = r->get_decl();
|
||||
return
|
||||
p->get_arity() == 2 &&
|
||||
p->get_domain(0) == p->get_domain(1) &&
|
||||
r->get_tail_size() == 1 &&
|
||||
r->get_tail(0)->get_decl() == p &&
|
||||
r->get_head()->get_arg(0) == r->get_tail(0)->get_arg(1) &&
|
||||
r->get_head()->get_arg(1) == r->get_tail(0)->get_arg(0) &&
|
||||
is_var(r->get_head()->get_arg(0)) &&
|
||||
is_var(r->get_head()->get_arg(1)) &&
|
||||
r->get_head()->get_arg(0) != r->get_head()->get_arg(1);
|
||||
}
|
||||
|
||||
|
||||
bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) {
|
||||
func_decl* p = r->get_decl();
|
||||
if (p->get_arity() != 2 ||
|
||||
p->get_domain(0) != p->get_domain(1) ||
|
||||
r->get_tail_size() != 2 ||
|
||||
r->get_tail(0)->get_decl() != p ||
|
||||
r->get_tail(1)->get_decl() != p) {
|
||||
return false;
|
||||
}
|
||||
app* h = r->get_head();
|
||||
app* a = r->get_tail(0);
|
||||
app* b = r->get_tail(1);
|
||||
expr* x1 = h->get_arg(0);
|
||||
expr* x2 = h->get_arg(1);
|
||||
expr* a1 = a->get_arg(0);
|
||||
expr* a2 = a->get_arg(1);
|
||||
expr* b1 = b->get_arg(0);
|
||||
expr* b2 = b->get_arg(1);
|
||||
|
||||
if (!(is_var(x1) && is_var(x2) && is_var(a1) && is_var(a2) && is_var(b1) && is_var(b2))) {
|
||||
return false;
|
||||
}
|
||||
if (x1 == x2 || a1 == a2 || b1 == b2) {
|
||||
return false;
|
||||
}
|
||||
if (a2 == b1) {
|
||||
if (x1 == b2 && x2 == a1) {
|
||||
return true;
|
||||
}
|
||||
if (x1 == a1 && x2 == b2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (a1 == b2) {
|
||||
if (x1 == b1 && x2 == a2) {
|
||||
return true;
|
||||
}
|
||||
if (x1 == a2 && x2 == b1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) {
|
||||
// TODO mc
|
||||
|
||||
if (source.get_num_rules() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_context.get_engine() != DATALOG_ENGINE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
relation_manager & rm = m_context.get_rel_context()->get_rmanager();
|
||||
rule_set::decl2rules::iterator it = source.begin_grouped_rules();
|
||||
rule_set::decl2rules::iterator end = source.end_grouped_rules();
|
||||
|
||||
rule_set* res = alloc(rule_set, m_context);
|
||||
|
||||
for (; it != end; ++it) {
|
||||
func_decl* p = it->m_key;
|
||||
rule_vector const& rv = *(it->m_value);
|
||||
bool has_symmetry = false;
|
||||
bool has_transitivity = false;
|
||||
unsigned i_symmetry, i_transitivity;
|
||||
family_id kind = rm.get_requested_predicate_kind(p);
|
||||
for (unsigned i = 0; i < rv.size(); ++i) {
|
||||
|
||||
if (kind != null_family_id) {
|
||||
res->add_rule(rv[i]);
|
||||
}
|
||||
else if (is_symmetry(rv[i])) {
|
||||
i_symmetry = i;
|
||||
has_symmetry = true;
|
||||
}
|
||||
else if (is_transitivity(rv[i])) {
|
||||
i_transitivity = i;
|
||||
has_transitivity = true;
|
||||
}
|
||||
else {
|
||||
res->add_rule(rv[i]);
|
||||
}
|
||||
}
|
||||
if (has_symmetry && !has_transitivity) {
|
||||
res->add_rule(rv[i_symmetry]);
|
||||
}
|
||||
else if (!has_symmetry && has_transitivity) {
|
||||
res->add_rule(rv[i_transitivity]);
|
||||
}
|
||||
else if (has_symmetry && has_transitivity) {
|
||||
TRACE("dl", tout << "updating predicate " << mk_pp(p, m) << " to partial equivalence\n";);
|
||||
SASSERT(kind == null_family_id);
|
||||
rm.set_predicate_kind(p, rm.get_table_plugin(symbol("equivalence"))->get_kind());
|
||||
}
|
||||
}
|
||||
|
||||
if (res->get_num_rules() == source.get_num_rules()) {
|
||||
dealloc(res);
|
||||
return 0;
|
||||
}
|
||||
res->inherit_predicates(source);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
50
src/muz/rel/dl_mk_partial_equiv.h
Normal file
50
src/muz/rel/dl_mk_partial_equiv.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_partial_equiv.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Rule transformer which identifies predicates that are partial equivalence relations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-05-14
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#ifndef _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_
|
||||
#define _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_
|
||||
|
||||
#include "dl_context.h"
|
||||
#include "dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_partial_equivalence_transformer : public rule_transformer::plugin {
|
||||
ast_manager & m;
|
||||
context & m_context;
|
||||
public:
|
||||
mk_partial_equivalence_transformer(context & ctx, unsigned priority=45000)
|
||||
: plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_context(ctx) {}
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
|
||||
private:
|
||||
|
||||
bool is_symmetry(rule const* r);
|
||||
bool is_transitivity(rule const* r);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */
|
||||
|
||||
|
||||
546
src/muz/rel/dl_mk_similarity_compressor.cpp
Normal file
546
src/muz/rel/dl_mk_similarity_compressor.cpp
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_similarity_compressor.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-10-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include<utility>
|
||||
#include<sstream>
|
||||
#include"dl_mk_similarity_compressor.h"
|
||||
#include"dl_relation_manager.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
mk_similarity_compressor::mk_similarity_compressor(context & ctx) :
|
||||
plugin(5000),
|
||||
m_context(ctx),
|
||||
m_manager(ctx.get_manager()),
|
||||
m_threshold_count(ctx.similarity_compressor_threshold()),
|
||||
m_result_rules(ctx.get_rule_manager()),
|
||||
m_modified(false),
|
||||
m_pinned(m_manager) {
|
||||
SASSERT(m_threshold_count>1);
|
||||
}
|
||||
|
||||
void mk_similarity_compressor::reset() {
|
||||
m_rules.reset();
|
||||
m_result_rules.reset();
|
||||
m_pinned.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
Allows to traverse head and positive tails in a single for loop starting from -1
|
||||
*/
|
||||
static app * get_by_tail_index(rule * r, int idx) {
|
||||
if (idx < 0) {
|
||||
return r->get_head();
|
||||
}
|
||||
SASSERT(idx < static_cast<int>(r->get_positive_tail_size()));
|
||||
return r->get_tail(idx);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static int aux_compare(T a, T b) {
|
||||
return (a>b) ? 1 : ( (a==b) ? 0 : -1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static int aux_compare(T* a, T* b);
|
||||
|
||||
static int compare_var_args(app* t1, app* t2) {
|
||||
SASSERT(t1->get_num_args()==t2->get_num_args());
|
||||
int res;
|
||||
unsigned n = t1->get_num_args();
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
expr * a1 = t1->get_arg(i);
|
||||
expr * a2 = t2->get_arg(i);
|
||||
res = aux_compare(is_var(a1), is_var(a2));
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
if (is_var(a1)) {
|
||||
res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx());
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compare_args(app* t1, app* t2, int & skip_countdown) {
|
||||
SASSERT(t1->get_num_args()==t2->get_num_args());
|
||||
int res;
|
||||
unsigned n = t1->get_num_args();
|
||||
for (unsigned i=0; i<n; i++) {
|
||||
if (is_var(t1->get_arg(i))) {
|
||||
SASSERT(t1->get_arg(i) == t2->get_arg(i));
|
||||
continue;
|
||||
}
|
||||
if ((skip_countdown--) == 0) {
|
||||
continue;
|
||||
}
|
||||
res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id());
|
||||
if (res!=0) { return res; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return 0 if r1 and r2 could be similar. If the rough similarity
|
||||
equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1.
|
||||
|
||||
Two rules are in the same rough similarity class if they differ only in constant arguments
|
||||
of positive uninterpreted predicates.
|
||||
*/
|
||||
static int rough_compare(rule * r1, rule * r2) {
|
||||
int res = aux_compare(r1->get_tail_size(), r2->get_tail_size());
|
||||
if (res!=0) { return res; }
|
||||
res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size());
|
||||
if (res!=0) { return res; }
|
||||
res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size());
|
||||
if (res!=0) { return res; }
|
||||
|
||||
int pos_tail_sz = r1->get_positive_tail_size();
|
||||
for (int i=-1; i<pos_tail_sz; i++) {
|
||||
app * t1 = get_by_tail_index(r1, i);
|
||||
app * t2 = get_by_tail_index(r2, i);
|
||||
res = aux_compare(t1->get_decl()->get_id(), t2->get_decl()->get_id());
|
||||
if (res!=0) { return res; }
|
||||
res = compare_var_args(t1, t2);
|
||||
if (res!=0) { return res; }
|
||||
}
|
||||
|
||||
unsigned tail_sz = r1->get_tail_size();
|
||||
for (unsigned i=pos_tail_sz; i<tail_sz; i++) {
|
||||
res = aux_compare(r1->get_tail(i)->get_id(), r2->get_tail(i)->get_id());
|
||||
if (res!=0) { return res; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\c r1 and \c r2 must be equal according to the \c rough_compare function for this function
|
||||
to be called.
|
||||
*/
|
||||
static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) {
|
||||
SASSERT(rough_compare(r1, r2)==0);
|
||||
int pos_tail_sz = r1->get_positive_tail_size();
|
||||
for (int i=-1; i<pos_tail_sz; i++) {
|
||||
int res = compare_args(get_by_tail_index(r1, i), get_by_tail_index(r2, i), skipped_arg_index);
|
||||
if (res!=0) { return res; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class const_info {
|
||||
int m_tail_index;
|
||||
unsigned m_arg_index;
|
||||
bool m_has_parent;
|
||||
/** Parent is a constant that appears earlier in the rule and has always the same value
|
||||
as this constant. */
|
||||
unsigned m_parent_index;
|
||||
public:
|
||||
|
||||
const_info(int tail_index, unsigned arg_index)
|
||||
: m_tail_index(tail_index), m_arg_index(arg_index), m_has_parent(false) {}
|
||||
|
||||
int tail_index() const { return m_tail_index; }
|
||||
unsigned arg_index() const { return m_arg_index; }
|
||||
bool has_parent() const { return m_has_parent; }
|
||||
unsigned parent_index() const { SASSERT(has_parent()); return m_parent_index; }
|
||||
|
||||
void set_parent_index(unsigned idx) {
|
||||
SASSERT(!m_has_parent);
|
||||
m_has_parent = true;
|
||||
m_parent_index = idx;
|
||||
}
|
||||
};
|
||||
|
||||
typedef svector<const_info> info_vector;
|
||||
|
||||
static void collect_const_indexes(app * t, int tail_index, info_vector & res) {
|
||||
unsigned n = t->get_num_args();
|
||||
for (unsigned i=0; i<n; i++) {
|
||||
if (is_var(t->get_arg(i))) {
|
||||
continue;
|
||||
}
|
||||
res.push_back(const_info(tail_index, i));
|
||||
}
|
||||
}
|
||||
|
||||
static void collect_const_indexes(rule * r, info_vector & res) {
|
||||
collect_const_indexes(r->get_head(), -1, res);
|
||||
unsigned pos_tail_sz = r->get_positive_tail_size();
|
||||
for (unsigned i=0; i<pos_tail_sz; i++) {
|
||||
collect_const_indexes(r->get_tail(i), i, res);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) {
|
||||
unsigned const_cnt = const_infos.size();
|
||||
tgt.reset();
|
||||
for (unsigned i=0; i<const_cnt; i++) {
|
||||
const_info inf = const_infos[i];
|
||||
if (inf.has_parent()) {
|
||||
continue;
|
||||
}
|
||||
app * pred = get_by_tail_index(r, inf.tail_index());
|
||||
tgt.push_back(to_app(pred->get_arg(inf.arg_index())));
|
||||
SASSERT(tgt.back()->get_num_args()==0);
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
static void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) {
|
||||
unsigned const_cnt = const_infos.size();
|
||||
tgt.reset();
|
||||
for (unsigned i=0; i<const_cnt; i++) {
|
||||
const_info inf = const_infos[i];
|
||||
if (inf.has_parent()) {
|
||||
continue;
|
||||
}
|
||||
app * pred = get_by_tail_index(r, inf.tail_index());
|
||||
tgt.push_back(pred->get_decl()->get_domain(inf.arg_index()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief From the \c tail_indexes and \c arg_indexes remove elements corresponding to constants
|
||||
that are the same in rules \c *first ... \c *(after_last-1).
|
||||
*/
|
||||
static void remove_stable_constants(rule_vector::iterator first, rule_vector::iterator after_last,
|
||||
info_vector & const_infos) {
|
||||
SASSERT(after_last-first>1);
|
||||
unsigned const_cnt = const_infos.size();
|
||||
ptr_vector<app> vals;
|
||||
rule * r = *(first++);
|
||||
collect_orphan_consts(r, const_infos, vals);
|
||||
SASSERT(vals.size()==const_cnt);
|
||||
rule_vector::iterator it = first;
|
||||
for (; it!=after_last; ++it) {
|
||||
for (unsigned i=0; i<const_cnt; i++) {
|
||||
app * pred = get_by_tail_index(*it, const_infos[i].tail_index());
|
||||
app * val = to_app(pred->get_arg(const_infos[i].arg_index()));
|
||||
if (vals[i]!=val) {
|
||||
vals[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned removed_cnt = 0;
|
||||
for (unsigned i=0; i<const_cnt; i++) {
|
||||
if (vals[i]!=0) {
|
||||
removed_cnt++;
|
||||
}
|
||||
else if (removed_cnt!=0) {
|
||||
const_infos[i-removed_cnt] = const_infos[i];
|
||||
}
|
||||
}
|
||||
if (removed_cnt!=0) {
|
||||
const_infos.shrink(const_cnt-removed_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief When function returns, \c parents will contain for each constant the index of the
|
||||
first constant that is equal to it in all the rules. If there is no such, it will contain
|
||||
its own index.
|
||||
*/
|
||||
static void detect_equal_constants(rule_vector::iterator first, rule_vector::iterator after_last,
|
||||
info_vector & const_infos) {
|
||||
SASSERT(first!=after_last);
|
||||
unsigned const_cnt = const_infos.size();
|
||||
ptr_vector<app> vals;
|
||||
ptr_vector<sort> sorts;
|
||||
rule * r = *(first++);
|
||||
collect_orphan_consts(r, const_infos, vals);
|
||||
collect_orphan_sorts(r, const_infos, sorts);
|
||||
SASSERT(vals.size()==const_cnt);
|
||||
vector<unsigned_vector> possible_parents(const_cnt);
|
||||
for (unsigned i=1; i<const_cnt; i++) {
|
||||
for (unsigned j=0; j<i; j++) {
|
||||
if (vals[i]==vals[j] && sorts[i]==sorts[j]) {
|
||||
possible_parents[i].push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
rule_vector::iterator it = first;
|
||||
for (; it!=after_last; ++it) {
|
||||
collect_orphan_consts(*it, const_infos, vals);
|
||||
for (unsigned i=1; i<const_cnt; i++) {
|
||||
unsigned_vector & ppars = possible_parents[i];
|
||||
unsigned j=0;
|
||||
while(j<ppars.size()) {
|
||||
if (vals[i]!=vals[ppars[j]]) {
|
||||
ppars[j] = ppars.back();
|
||||
ppars.pop_back();
|
||||
}
|
||||
else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned i=0; i<const_cnt; i++) {
|
||||
unsigned parent = i;
|
||||
unsigned_vector & ppars = possible_parents[i];
|
||||
unsigned ppars_sz = ppars.size();
|
||||
for (unsigned j=0; j<ppars_sz; j++) {
|
||||
if (ppars[j]<parent) {
|
||||
parent = ppars[j];
|
||||
}
|
||||
}
|
||||
if (parent!=i) {
|
||||
const_infos[i].set_parent_index(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned get_constant_count(rule * r) {
|
||||
unsigned res = r->get_head()->get_num_args() - count_variable_arguments(r->get_head());
|
||||
unsigned pos_tail_sz = r->get_positive_tail_size();
|
||||
for (unsigned i=0; i<pos_tail_sz; i++) {
|
||||
res+= r->get_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool initial_comparator(rule * r1, rule * r2) {
|
||||
int res = rough_compare(r1, r2);
|
||||
if (res!=0) { return res>0; }
|
||||
return total_compare(r1, r2)>0;
|
||||
}
|
||||
|
||||
class arg_ignoring_comparator {
|
||||
unsigned m_ignored_index;
|
||||
public:
|
||||
arg_ignoring_comparator(unsigned ignored_index) : m_ignored_index(ignored_index) {}
|
||||
bool operator()(rule * r1, rule * r2) const {
|
||||
return total_compare(r1, r2, m_ignored_index)>0;
|
||||
}
|
||||
bool eq(rule * r1, rule * r2) const {
|
||||
return total_compare(r1, r2, m_ignored_index)==0;
|
||||
}
|
||||
};
|
||||
|
||||
void mk_similarity_compressor::merge_class(rule_vector::iterator first,
|
||||
rule_vector::iterator after_last) {
|
||||
SASSERT(after_last-first>1);
|
||||
info_vector const_infos;
|
||||
rule * r = *first; //an arbitrary representative of the class
|
||||
collect_const_indexes(r, const_infos);
|
||||
remove_stable_constants(first, after_last, const_infos);
|
||||
|
||||
unsigned const_cnt = const_infos.size();
|
||||
SASSERT(const_cnt>0);
|
||||
|
||||
detect_equal_constants(first, after_last, const_infos);
|
||||
|
||||
|
||||
//The aux relation contains column for each constant which does not have an earlier constant
|
||||
//that it is equal to (i.e. only has no parent)
|
||||
ptr_vector<sort> aux_domain;
|
||||
collect_orphan_sorts(r, const_infos, aux_domain);
|
||||
|
||||
func_decl* head_pred = r->get_decl();
|
||||
symbol const& name_prefix = head_pred->get_name();
|
||||
std::string name_suffix = "sc_" + to_string(const_cnt);
|
||||
func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()),
|
||||
aux_domain.size(), aux_domain.c_ptr(), head_pred);
|
||||
m_pinned.push_back(aux_pred);
|
||||
|
||||
relation_fact val_fact(m_manager, const_cnt);
|
||||
rule_vector::iterator it = first;
|
||||
for (; it!=after_last; ++it) {
|
||||
collect_orphan_consts(*it, const_infos, val_fact);
|
||||
m_context.add_fact(aux_pred, val_fact);
|
||||
}
|
||||
m_context.get_rel_context()->get_rmanager().mark_saturated(aux_pred);
|
||||
|
||||
app * new_head = r->get_head();
|
||||
ptr_vector<app> new_tail;
|
||||
svector<bool> new_negs;
|
||||
unsigned tail_sz = r->get_tail_size();
|
||||
for (unsigned i=0; i<tail_sz; i++) {
|
||||
new_tail.push_back(r->get_tail(i));
|
||||
new_negs.push_back(r->is_neg_tail(i));
|
||||
}
|
||||
|
||||
rule_counter ctr;
|
||||
ctr.count_rule_vars(m_manager, r);
|
||||
unsigned max_var_idx, new_var_idx_base;
|
||||
if (ctr.get_max_positive(max_var_idx)) {
|
||||
new_var_idx_base = max_var_idx+1;
|
||||
}
|
||||
else {
|
||||
new_var_idx_base = 0;
|
||||
}
|
||||
|
||||
ptr_vector<var> const_vars; //variables at indexes of their corresponding constants
|
||||
expr_ref_vector aux_vars(m_manager); //variables as arguments for the auxiliary predicate
|
||||
|
||||
unsigned aux_column_index = 0;
|
||||
|
||||
for (unsigned i=0; i<const_cnt; ) {
|
||||
int tail_idx = const_infos[i].tail_index();
|
||||
app * & mod_tail = (tail_idx==-1) ? new_head : new_tail[tail_idx];
|
||||
ptr_vector<expr> mod_args(mod_tail->get_num_args(), mod_tail->get_args());
|
||||
|
||||
for (; i<const_cnt && const_infos[i].tail_index()==tail_idx; i++) { //here the outer loop counter is modified
|
||||
const_info & inf = const_infos[i];
|
||||
var * mod_var;
|
||||
if (!inf.has_parent()) {
|
||||
mod_var = m_manager.mk_var(new_var_idx_base+aux_column_index,
|
||||
aux_domain[aux_column_index]);
|
||||
aux_column_index++;
|
||||
aux_vars.push_back(mod_var);
|
||||
}
|
||||
else {
|
||||
mod_var = const_vars[inf.parent_index()];
|
||||
}
|
||||
const_vars.push_back(mod_var);
|
||||
mod_args[inf.arg_index()] = mod_var;
|
||||
}
|
||||
|
||||
app * upd_tail = m_manager.mk_app(mod_tail->get_decl(), mod_args.c_ptr());
|
||||
m_pinned.push_back(upd_tail);
|
||||
mod_tail = upd_tail;
|
||||
}
|
||||
|
||||
app_ref aux_tail(m_manager.mk_app(aux_pred, aux_vars.c_ptr()), m_manager);
|
||||
new_tail.push_back(aux_tail);
|
||||
new_negs.push_back(false);
|
||||
|
||||
rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(),
|
||||
new_negs.c_ptr());
|
||||
m_result_rules.push_back(new_rule);
|
||||
|
||||
//TODO: allow for a rule to have multiple parent objects
|
||||
new_rule->set_accounting_parent_object(m_context, r);
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
void mk_similarity_compressor::process_class(rule_set const& source, rule_vector::iterator first,
|
||||
rule_vector::iterator after_last) {
|
||||
SASSERT(first!=after_last);
|
||||
//remove duplicates
|
||||
{
|
||||
rule_vector::iterator it = first;
|
||||
rule_vector::iterator prev = it;
|
||||
++it;
|
||||
while(it!=after_last) {
|
||||
if (it!=after_last && total_compare(*prev, *it)==0) {
|
||||
--after_last;
|
||||
std::swap(*it, *after_last);
|
||||
m_modified = true;
|
||||
}
|
||||
else {
|
||||
prev = it;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
SASSERT(first!=after_last);
|
||||
|
||||
unsigned const_cnt = get_constant_count(*first);
|
||||
#if 0
|
||||
for (unsigned ignored_index=0; ignored_index<const_cnt; ignored_index++) {
|
||||
arg_ignoring_comparator comparator(ignored_index);
|
||||
std::sort(first, after_last, comparator);
|
||||
|
||||
rule_vector::iterator it = first;
|
||||
rule_vector::iterator grp_begin = it;
|
||||
unsigned grp_size=0;
|
||||
while(it!=after_last) {
|
||||
rule_vector::iterator prev = it;
|
||||
++it;
|
||||
grp_size++;
|
||||
if (it==after_last || !comparator.eq(*prev, *it)) {
|
||||
if (grp_size>m_threshold_count) {
|
||||
merge_class(grp_begin, it);
|
||||
//group was processed, so we remove it from the class
|
||||
if (it==after_last) {
|
||||
after_last=grp_begin;
|
||||
it=after_last;
|
||||
}
|
||||
else {
|
||||
while(it!=grp_begin) {
|
||||
std::swap(*--it, *--after_last);
|
||||
}
|
||||
}
|
||||
}
|
||||
grp_begin = it;
|
||||
grp_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//TODO: compress also rules with pairs (or tuples) of equal constants
|
||||
|
||||
#if 1
|
||||
if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) {
|
||||
unsigned rule_cnt = static_cast<unsigned>(after_last-first);
|
||||
if (rule_cnt>m_threshold_count) {
|
||||
merge_class(first, after_last);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//put rules which weren't merged into result
|
||||
rule_vector::iterator it = first;
|
||||
for (; it!=after_last; ++it) {
|
||||
m_result_rules.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
rule_set * mk_similarity_compressor::operator()(rule_set const & source) {
|
||||
// TODO mc
|
||||
m_modified = false;
|
||||
unsigned init_rule_cnt = source.get_num_rules();
|
||||
SASSERT(m_rules.empty());
|
||||
for (unsigned i=0; i<init_rule_cnt; i++) {
|
||||
m_rules.push_back(source.get_rule(i));
|
||||
}
|
||||
|
||||
std::sort(m_rules.begin(), m_rules.end(), initial_comparator);
|
||||
|
||||
rule_vector::iterator it = m_rules.begin();
|
||||
rule_vector::iterator end = m_rules.end();
|
||||
rule_vector::iterator cl_begin = it;
|
||||
while(it!=end) {
|
||||
rule_vector::iterator prev = it;
|
||||
++it;
|
||||
if (it==end || rough_compare(*prev, *it)!=0) {
|
||||
process_class(source, cl_begin, it);
|
||||
cl_begin = it;
|
||||
}
|
||||
}
|
||||
|
||||
rule_set * result = static_cast<rule_set *>(0);
|
||||
if (m_modified) {
|
||||
result = alloc(rule_set, m_context);
|
||||
unsigned fin_rule_cnt = m_result_rules.size();
|
||||
for (unsigned i=0; i<fin_rule_cnt; i++) {
|
||||
result->add_rule(m_result_rules.get(i));
|
||||
}
|
||||
result->inherit_predicates(source);
|
||||
}
|
||||
reset();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
78
src/muz/rel/dl_mk_similarity_compressor.h
Normal file
78
src/muz/rel/dl_mk_similarity_compressor.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_similarity_compressor.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-10-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_
|
||||
#define _DL_MK_SIMILARITY_COMPRESSOR_H_
|
||||
|
||||
#include<utility>
|
||||
|
||||
#include"map.h"
|
||||
#include"obj_pair_hashtable.h"
|
||||
|
||||
#include"dl_context.h"
|
||||
#include"dl_rule_set.h"
|
||||
#include"dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
/**
|
||||
\brief Functor for merging groups of similar rules.
|
||||
|
||||
A rule sequence
|
||||
|
||||
P("1",x):-Q(x).
|
||||
...
|
||||
P("N",x):-Q(x).
|
||||
|
||||
will be replaced by
|
||||
|
||||
P(y,x):-Q(x), Aux(y).
|
||||
|
||||
and a set of facts
|
||||
|
||||
Aux("1").
|
||||
...
|
||||
Aux("N").
|
||||
|
||||
Similar transformation is performed when the varying constant appears in the positive tail.
|
||||
*/
|
||||
class mk_similarity_compressor : public rule_transformer::plugin {
|
||||
|
||||
context & m_context;
|
||||
ast_manager & m_manager;
|
||||
/** number of similar rules necessary for a group to be introduced */
|
||||
unsigned m_threshold_count;
|
||||
rule_vector m_rules;
|
||||
rule_ref_vector m_result_rules;
|
||||
bool m_modified;
|
||||
ast_ref_vector m_pinned;
|
||||
|
||||
void merge_class(rule_vector::iterator first, rule_vector::iterator after_last);
|
||||
void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last);
|
||||
|
||||
void reset();
|
||||
public:
|
||||
mk_similarity_compressor(context & ctx);
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_SIMILARITY_COMPRESSOR_H_ */
|
||||
|
||||
742
src/muz/rel/dl_mk_simple_joins.cpp
Normal file
742
src/muz/rel/dl_mk_simple_joins.cpp
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_simple_joins.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2010-05-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include<utility>
|
||||
#include<sstream>
|
||||
#include<limits>
|
||||
#include"dl_mk_simple_joins.h"
|
||||
#include"dl_relation_manager.h"
|
||||
#include"ast_pp.h"
|
||||
#include"trace.h"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
|
||||
mk_simple_joins::mk_simple_joins(context & ctx):
|
||||
plugin(1000),
|
||||
m_context(ctx),
|
||||
rm(ctx.get_rule_manager()) {
|
||||
}
|
||||
|
||||
class join_planner {
|
||||
typedef float cost;
|
||||
|
||||
class pair_info {
|
||||
cost m_total_cost;
|
||||
/**
|
||||
\brief Number of rules longer than two that contain this pair.
|
||||
|
||||
This number is being updated by \c add_rule and \remove rule. Even though between
|
||||
adding a rule and removing it, the length of a rule can decrease without this pair
|
||||
being notified about it, it will surely see the decrease from length 3 to 2 which
|
||||
the threshold for rule being counted in this counter.
|
||||
*/
|
||||
unsigned m_consumers;
|
||||
bool m_stratified;
|
||||
unsigned m_src_stratum;
|
||||
public:
|
||||
var_idx_set m_all_nonlocal_vars;
|
||||
rule_vector m_rules;
|
||||
|
||||
pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {}
|
||||
|
||||
bool can_be_joined() const {
|
||||
return m_consumers>0;
|
||||
}
|
||||
|
||||
cost get_cost() const {
|
||||
/*if(m_instantiated) {
|
||||
return std::numeric_limits<cost>::min();
|
||||
}*/
|
||||
SASSERT(m_consumers>0);
|
||||
cost amortized = m_total_cost/m_consumers;
|
||||
if(m_stratified) {
|
||||
return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f);
|
||||
}
|
||||
else {
|
||||
return amortized;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Add rule \c r among rules interested in current predicate pair.
|
||||
|
||||
The \c pl.m_rule_content entry of the rule has to be properly filled in
|
||||
by the time of a call to this function
|
||||
*/
|
||||
void add_rule(join_planner & pl, app * t1, app * t2, rule * r,
|
||||
const var_idx_set & non_local_vars_normalized) {
|
||||
if(m_rules.empty()) {
|
||||
m_total_cost = pl.compute_cost(t1, t2);
|
||||
m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl()));
|
||||
}
|
||||
m_rules.push_back(r);
|
||||
if(pl.m_rules_content.find_core(r)->get_data().m_value.size()>2) {
|
||||
m_consumers++;
|
||||
}
|
||||
if(m_stratified) {
|
||||
unsigned head_stratum = pl.get_stratum(r->get_decl());
|
||||
SASSERT(head_stratum>=m_src_stratum);
|
||||
if(head_stratum==m_src_stratum) {
|
||||
m_stratified = false;
|
||||
}
|
||||
}
|
||||
idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized);
|
||||
}
|
||||
/**
|
||||
\brief Remove rule from the pair record. Return true if no rules remain
|
||||
in the pair, and so it should be removed.
|
||||
*/
|
||||
bool remove_rule(rule * r, unsigned original_length) {
|
||||
TRUSTME( remove_from_vector(m_rules, r) );
|
||||
if(original_length>2) {
|
||||
SASSERT(m_consumers>0);
|
||||
m_consumers--;
|
||||
}
|
||||
SASSERT(!m_rules.empty() || m_consumers==0);
|
||||
return m_rules.empty();
|
||||
}
|
||||
private:
|
||||
pair_info & operator=(const pair_info &); //to avoid the implicit one
|
||||
};
|
||||
typedef std::pair<app*, app*> app_pair;
|
||||
typedef map<app_pair, pair_info *,
|
||||
pair_hash<obj_ptr_hash<app>, obj_ptr_hash<app> >, default_eq<app_pair> > cost_map;
|
||||
typedef map<rule *, ptr_vector<app>, ptr_hash<rule>, ptr_eq<rule> > rule_pred_map;
|
||||
|
||||
context & m_context;
|
||||
ast_manager & m;
|
||||
rule_manager & rm;
|
||||
var_subst & m_var_subst;
|
||||
rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels
|
||||
|
||||
cost_map m_costs;
|
||||
ptr_vector<app> m_interpreted;
|
||||
rule_pred_map m_rules_content;
|
||||
rule_ref_vector m_introduced_rules;
|
||||
ptr_hashtable<rule, ptr_hash<rule>, ptr_eq<rule> > m_modified_rules;
|
||||
|
||||
ast_ref_vector m_pinned;
|
||||
mutable ptr_vector<sort> m_vars;
|
||||
|
||||
public:
|
||||
join_planner(context & ctx, rule_set & rs_aux_copy)
|
||||
: m_context(ctx), m(ctx.get_manager()),
|
||||
rm(ctx.get_rule_manager()),
|
||||
m_var_subst(ctx.get_var_subst()),
|
||||
m_rs_aux_copy(rs_aux_copy),
|
||||
m_introduced_rules(ctx.get_rule_manager()),
|
||||
m_pinned(ctx.get_manager())
|
||||
{
|
||||
}
|
||||
|
||||
~join_planner()
|
||||
{
|
||||
cost_map::iterator it = m_costs.begin();
|
||||
cost_map::iterator end = m_costs.end();
|
||||
for (; it != end; ++it) {
|
||||
dealloc(it->m_value);
|
||||
}
|
||||
m_costs.reset();
|
||||
}
|
||||
private:
|
||||
|
||||
void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const {
|
||||
SASSERT(result.size()>0);
|
||||
unsigned res_ofs = result.size()-1;
|
||||
unsigned n=t->get_num_args();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
SASSERT(is_var(t->get_arg(i)));
|
||||
var * v = to_var(t->get_arg(i));
|
||||
unsigned var_idx = v->get_idx();
|
||||
if(result[res_ofs-var_idx]==0) {
|
||||
result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort());
|
||||
next_var++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const {
|
||||
SASSERT(result.empty());
|
||||
if(t1->get_num_args()==0 && t2->get_num_args()==0) {
|
||||
return; //nothing to normalize
|
||||
}
|
||||
SASSERT(!t1->is_ground() || !t2->is_ground());
|
||||
|
||||
unsigned max_var_idx = 0;
|
||||
{
|
||||
var_idx_set& orig_var_set = rm.collect_vars(t1, t2);
|
||||
var_idx_set::iterator ovit = orig_var_set.begin();
|
||||
var_idx_set::iterator ovend = orig_var_set.end();
|
||||
for(; ovit!=ovend; ++ovit) {
|
||||
unsigned var_idx = *ovit;
|
||||
if(var_idx>max_var_idx) {
|
||||
max_var_idx = var_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(t1->get_decl()!=t2->get_decl()) {
|
||||
if(t1->get_decl()->get_id()<t2->get_decl()->get_id()) {
|
||||
std::swap(t1, t2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int_vector norm1(max_var_idx+1, -1);
|
||||
int_vector norm2(max_var_idx+1, -1);
|
||||
unsigned n=t1->get_num_args();
|
||||
SASSERT(n==t2->get_num_args());
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
//We assume that the mk_simple_joins transformer is applied after mk_filter_rules,
|
||||
//so the only literals which appear in pairs are the ones that contain only variables.
|
||||
var * v1 = to_var(t1->get_arg(i));
|
||||
var * v2 = to_var(t2->get_arg(i));
|
||||
if(v1->get_sort()!=v2->get_sort()) {
|
||||
//different sorts mean we can distinguish the two terms
|
||||
if(v1->get_sort()->get_id()<v2->get_sort()->get_id()) {
|
||||
std::swap(t1, t2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
unsigned v1_idx = v1->get_idx();
|
||||
unsigned v2_idx = v2->get_idx();
|
||||
//since the rules already went through the mk_filter_rules transformer,
|
||||
//variables must be linear
|
||||
SASSERT(norm1[v1_idx]==-1);
|
||||
SASSERT(norm2[v2_idx]==-1);
|
||||
|
||||
if(norm2[v1_idx]!=norm1[v2_idx]) {
|
||||
//now we can distinguish the two terms
|
||||
if(norm2[v1_idx]<norm1[v2_idx]) {
|
||||
std::swap(t1, t2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
norm1[v1_idx]=i;
|
||||
norm2[v2_idx]=i;
|
||||
}
|
||||
//if we did not exit the loop prematurely, the two terms are indistinguishable,
|
||||
//so the order should not matter
|
||||
}
|
||||
|
||||
result.resize(max_var_idx+1, static_cast<expr *>(0));
|
||||
unsigned next_var = 0;
|
||||
get_normalizer(t1, next_var, result);
|
||||
get_normalizer(t2, next_var, result);
|
||||
}
|
||||
|
||||
app_pair get_key(app * t1, app * t2) {
|
||||
expr_ref_vector norm_subst(m);
|
||||
get_normalizer(t1, t2, norm_subst);
|
||||
expr_ref t1n_ref(m);
|
||||
expr_ref t2n_ref(m);
|
||||
m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr(), t1n_ref);
|
||||
m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr(), t2n_ref);
|
||||
app * t1n = to_app(t1n_ref);
|
||||
app * t2n = to_app(t2n_ref);
|
||||
if(t1n>t2n) {
|
||||
std::swap(t1n, t2n);
|
||||
}
|
||||
m_pinned.push_back(t1n);
|
||||
m_pinned.push_back(t2n);
|
||||
|
||||
/*
|
||||
IF_VERBOSE(0,
|
||||
print_renaming(norm_subst, verbose_stream());
|
||||
display_predicate(m_context, t1, verbose_stream());
|
||||
display_predicate(m_context, t2, verbose_stream());
|
||||
display_predicate(m_context, t1n, verbose_stream());
|
||||
display_predicate(m_context, t2n, verbose_stream()););
|
||||
*/
|
||||
return app_pair(t1n, t2n);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Add rule \c r among rules interested in predicate pair \c t1, \c t2.
|
||||
|
||||
The \c m_rule_content entry of the rule \c r has to be properly filled in
|
||||
by the time of a call to this function
|
||||
*/
|
||||
void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) {
|
||||
TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n";
|
||||
r->display(m_context, tout); tout << "\n";);
|
||||
SASSERT(t1!=t2);
|
||||
cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0);
|
||||
pair_info * & ptr_inf = e->get_data().m_value;
|
||||
if(ptr_inf==0) {
|
||||
ptr_inf = alloc(pair_info);
|
||||
}
|
||||
pair_info & inf = *ptr_inf;
|
||||
|
||||
expr_ref_vector normalizer(m);
|
||||
get_normalizer(t1, t2, normalizer);
|
||||
unsigned norm_ofs = normalizer.size()-1;
|
||||
var_idx_set normalized_vars;
|
||||
var_idx_set::iterator vit = non_local_vars.begin();
|
||||
var_idx_set::iterator vend = non_local_vars.end();
|
||||
for(; vit!=vend; ++vit) {
|
||||
unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx();
|
||||
normalized_vars.insert(norm_var);
|
||||
}
|
||||
|
||||
inf.add_rule(*this, t1, t2, r, normalized_vars);
|
||||
}
|
||||
|
||||
pair_info & get_pair(app_pair key) const {
|
||||
cost_map::entry * e = m_costs.find_core(key);
|
||||
SASSERT(e);
|
||||
return *e->get_data().m_value;
|
||||
}
|
||||
|
||||
void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) {
|
||||
pair_info * ptr = &get_pair(key);
|
||||
if(ptr->remove_rule(r, original_len)) {
|
||||
SASSERT(ptr->m_rules.empty());
|
||||
m_costs.remove(key);
|
||||
dealloc(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void register_rule(rule * r) {
|
||||
rule_counter counter;
|
||||
counter.count_rule_vars(m, r, 1);
|
||||
|
||||
ptr_vector<app> & rule_content =
|
||||
m_rules_content.insert_if_not_there2(r, ptr_vector<app>())->get_data().m_value;
|
||||
SASSERT(rule_content.empty());
|
||||
|
||||
unsigned pos_tail_size=r->get_positive_tail_size();
|
||||
for(unsigned i=0; i<pos_tail_size; i++) {
|
||||
rule_content.push_back(r->get_tail(i));
|
||||
}
|
||||
for(unsigned i=0; i<pos_tail_size; i++) {
|
||||
app * t1 = r->get_tail(i);
|
||||
var_idx_set t1_vars = rm.collect_vars(t1);
|
||||
counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter
|
||||
for(unsigned j=i+1; j<pos_tail_size; j++) {
|
||||
app * t2 = r->get_tail(j);
|
||||
counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter
|
||||
var_idx_set scope_vars = rm.collect_vars(t2);
|
||||
scope_vars |= t1_vars;
|
||||
var_idx_set non_local_vars;
|
||||
counter.collect_positive(non_local_vars);
|
||||
counter.count_vars(m, t2, 1); //restore t2 variables in counter
|
||||
set_intersection(non_local_vars, scope_vars);
|
||||
register_pair(t1, t2, r, non_local_vars);
|
||||
}
|
||||
counter.count_vars(m, t1, 1); //restore t1 variables in counter
|
||||
}
|
||||
}
|
||||
|
||||
bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args,
|
||||
ptr_vector<sort> & domain) {
|
||||
unsigned n=t->get_num_args();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
var * v=to_var(t->get_arg(i));
|
||||
if(v->get_idx()==var_idx) {
|
||||
args.push_back(v);
|
||||
domain.push_back(m.get_sort(v));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void join_pair(app_pair pair_key) {
|
||||
app * t1 = pair_key.first;
|
||||
app * t2 = pair_key.second;
|
||||
pair_info & inf = get_pair(pair_key);
|
||||
SASSERT(!inf.m_rules.empty());
|
||||
var_idx_set & output_vars = inf.m_all_nonlocal_vars;
|
||||
expr_ref_vector args(m);
|
||||
ptr_vector<sort> domain;
|
||||
|
||||
unsigned arity = output_vars.num_elems();
|
||||
idx_set::iterator ovit=output_vars.begin();
|
||||
idx_set::iterator ovend=output_vars.end();
|
||||
//TODO: improve quadratic complexity
|
||||
for(;ovit!=ovend;++ovit) {
|
||||
unsigned var_idx=*ovit;
|
||||
|
||||
bool found=extract_argument_info(var_idx, t1, args, domain);
|
||||
if(!found) {
|
||||
found=extract_argument_info(var_idx, t2, args, domain);
|
||||
}
|
||||
SASSERT(found);
|
||||
}
|
||||
|
||||
SASSERT(args.size()==arity);
|
||||
SASSERT(domain.size()==arity);
|
||||
|
||||
rule * one_parent = inf.m_rules.back();
|
||||
|
||||
func_decl* parent_head = one_parent->get_decl();
|
||||
const char * one_parent_name = parent_head->get_name().bare_str();
|
||||
std::string parent_name;
|
||||
if(inf.m_rules.size()>1) {
|
||||
parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1);
|
||||
}
|
||||
else {
|
||||
parent_name = one_parent_name;
|
||||
}
|
||||
|
||||
func_decl * decl = m_context.mk_fresh_head_predicate(
|
||||
symbol(parent_name.c_str()), symbol("split"),
|
||||
arity, domain.c_ptr(), parent_head);
|
||||
|
||||
app_ref head(m.mk_app(decl, arity, args.c_ptr()), m);
|
||||
|
||||
app * tail[] = {t1, t2};
|
||||
|
||||
rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, 0);
|
||||
|
||||
//TODO: update accounting so that it can handle multiple parents
|
||||
new_rule->set_accounting_parent_object(m_context, one_parent);
|
||||
|
||||
m_introduced_rules.push_back(new_rule);
|
||||
|
||||
//here we copy the inf.m_rules vector because inf.m_rules will get changed
|
||||
//in the iteration. Also we use hashtable instead of vector because we do
|
||||
//not want to process one rule twice.
|
||||
typedef ptr_hashtable<rule, ptr_hash<rule>, default_eq<rule *> > rule_hashtable;
|
||||
rule_hashtable relevant_rules;
|
||||
insert_into_set(relevant_rules, inf.m_rules);
|
||||
rule_hashtable::iterator rit = relevant_rules.begin();
|
||||
rule_hashtable::iterator rend = relevant_rules.end();
|
||||
for(; rit!=rend; ++rit) {
|
||||
apply_binary_rule(*rit, pair_key, head);
|
||||
}
|
||||
|
||||
// SASSERT(!m_costs.contains(pair_key));
|
||||
}
|
||||
|
||||
void replace_edges(rule * r, const ptr_vector<app> & removed_tails,
|
||||
const ptr_vector<app> & added_tails0, const ptr_vector<app> & rule_content) {
|
||||
SASSERT(removed_tails.size()>=added_tails0.size());
|
||||
unsigned len = rule_content.size();
|
||||
unsigned original_len = len+removed_tails.size()-added_tails0.size();
|
||||
ptr_vector<app> added_tails(added_tails0); //we need a copy since we'll be modifying it
|
||||
|
||||
unsigned rt_sz = removed_tails.size();
|
||||
//remove edges between removed tails
|
||||
for(unsigned i=0; i<rt_sz; i++) {
|
||||
for(unsigned j=i+1; j<rt_sz; j++) {
|
||||
app_pair pair_key = get_key(removed_tails[i], removed_tails[j]);
|
||||
remove_rule_from_pair(pair_key, r, original_len);
|
||||
}
|
||||
}
|
||||
//remove edges between surviving tails and removed tails
|
||||
for(unsigned i=0; i<len; i++) {
|
||||
if(added_tails.contains(rule_content[i])) {
|
||||
continue;
|
||||
}
|
||||
for(unsigned ri=0; ri<rt_sz; ri++) {
|
||||
app_pair pair_key = get_key(rule_content[i], removed_tails[ri]);
|
||||
remove_rule_from_pair(pair_key, r, original_len);
|
||||
}
|
||||
}
|
||||
|
||||
if(len==1) {
|
||||
return;
|
||||
}
|
||||
|
||||
app * head = r->get_head();
|
||||
|
||||
var_counter counter;
|
||||
counter.count_vars(m, head, 1);
|
||||
|
||||
unsigned tail_size=r->get_tail_size();
|
||||
unsigned pos_tail_size=r->get_positive_tail_size();
|
||||
|
||||
for(unsigned i=pos_tail_size; i<tail_size; i++) {
|
||||
counter.count_vars(m, r->get_tail(i), 1);
|
||||
}
|
||||
for(unsigned i=0; i<len; i++) {
|
||||
counter.count_vars(m, rule_content[i], 1);
|
||||
}
|
||||
|
||||
//add edges that contain added tails
|
||||
while(!added_tails.empty()) {
|
||||
app * a_tail = added_tails.back(); //added tail
|
||||
|
||||
var_idx_set a_tail_vars = rm.collect_vars(a_tail);
|
||||
counter.count_vars(m, a_tail, -1); //temporarily remove a_tail variables from counter
|
||||
|
||||
for(unsigned i=0; i<len; i++) {
|
||||
app * o_tail = rule_content[i]; //other tail
|
||||
if(added_tails.contains(o_tail)) {
|
||||
//this avoids adding edges between new tails twice
|
||||
continue;
|
||||
}
|
||||
|
||||
counter.count_vars(m, o_tail, -1); //temporarily remove o_tail variables from counter
|
||||
var_idx_set scope_vars = rm.collect_vars(o_tail);
|
||||
scope_vars |= a_tail_vars;
|
||||
var_idx_set non_local_vars;
|
||||
counter.collect_positive(non_local_vars);
|
||||
counter.count_vars(m, o_tail, 1); //restore o_tail variables in counter
|
||||
set_intersection(non_local_vars, scope_vars);
|
||||
|
||||
register_pair(o_tail, a_tail, r, non_local_vars);
|
||||
}
|
||||
counter.count_vars(m, a_tail, 1); //restore t1 variables in counter
|
||||
added_tails.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void apply_binary_rule(rule * r, app_pair pair_key, app * t_new) {
|
||||
app * t1 = pair_key.first;
|
||||
app * t2 = pair_key.second;
|
||||
ptr_vector<app> & rule_content = m_rules_content.find_core(r)->get_data().m_value;
|
||||
unsigned len = rule_content.size();
|
||||
if(len==1) {
|
||||
return;
|
||||
}
|
||||
|
||||
func_decl * t1_pred = t1->get_decl();
|
||||
func_decl * t2_pred = t2->get_decl();
|
||||
ptr_vector<app> removed_tails;
|
||||
ptr_vector<app> added_tails;
|
||||
for(unsigned i1=0; i1<len; i1++) {
|
||||
app * rt1 = rule_content[i1];
|
||||
if(rt1->get_decl()!=t1_pred) {
|
||||
continue;
|
||||
}
|
||||
unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0;
|
||||
for(unsigned i2=i2start; i2<len; i2++) {
|
||||
app * rt2 = rule_content[i2];
|
||||
if(i1==i2 || rt2->get_decl()!=t2_pred) {
|
||||
continue;
|
||||
}
|
||||
if(get_key(rt1, rt2)!=pair_key) {
|
||||
continue;
|
||||
}
|
||||
expr_ref_vector normalizer(m);
|
||||
get_normalizer(rt1, rt2, normalizer);
|
||||
expr_ref_vector denormalizer(m);
|
||||
reverse_renaming(m, normalizer, denormalizer);
|
||||
expr_ref new_transf(m);
|
||||
m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr(), new_transf);
|
||||
app * new_lit = to_app(new_transf);
|
||||
|
||||
m_pinned.push_back(new_lit);
|
||||
rule_content[i1]=new_lit;
|
||||
rule_content[i2]=rule_content.back();
|
||||
rule_content.pop_back();
|
||||
len--; //here the bound of both loops changes!!!
|
||||
removed_tails.push_back(rt1);
|
||||
removed_tails.push_back(rt2);
|
||||
added_tails.push_back(new_lit);
|
||||
//this exits the inner loop, the outer one continues in case there will
|
||||
//be other matches
|
||||
break;
|
||||
}
|
||||
}
|
||||
SASSERT(!removed_tails.empty());
|
||||
SASSERT(!added_tails.empty());
|
||||
m_modified_rules.insert(r);
|
||||
replace_edges(r, removed_tails, added_tails, rule_content);
|
||||
}
|
||||
|
||||
cost get_domain_size(func_decl * pred, unsigned arg_index) const {
|
||||
relation_sort sort = pred->get_domain(arg_index);
|
||||
return static_cast<cost>(m_context.get_sort_size_estimate(sort));
|
||||
//unsigned sz;
|
||||
//if(!m_context.get_sort_size(sort, sz)) {
|
||||
// sz=UINT_MAX;
|
||||
//}
|
||||
//return static_cast<cost>(sz);
|
||||
}
|
||||
|
||||
unsigned get_stratum(func_decl * pred) const {
|
||||
return m_rs_aux_copy.get_predicate_strat(pred);
|
||||
}
|
||||
|
||||
cost estimate_size(app * t) const {
|
||||
func_decl * pred = t->get_decl();
|
||||
unsigned n=pred->get_arity();
|
||||
rel_context_base* rel = m_context.get_rel_context();
|
||||
if (!rel) {
|
||||
return cost(1);
|
||||
}
|
||||
relation_manager& rm = rel->get_rmanager();
|
||||
if( (m_context.saturation_was_run() && rm.try_get_relation(pred))
|
||||
|| rm.is_saturated(pred)) {
|
||||
SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist
|
||||
unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows();
|
||||
if(rel_size_int!=0) {
|
||||
cost rel_size = static_cast<cost>(rel_size_int);
|
||||
cost curr_size = rel_size;
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
if(!is_var(t->get_arg(i))) {
|
||||
curr_size /= get_domain_size(pred, i);
|
||||
}
|
||||
}
|
||||
return curr_size;
|
||||
}
|
||||
}
|
||||
cost res = 1;
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
if(is_var(t->get_arg(i))) {
|
||||
res *= get_domain_size(pred, i);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
cost compute_cost(app * t1, app * t2) const {
|
||||
func_decl * t1_pred = t1->get_decl();
|
||||
func_decl * t2_pred = t2->get_decl();
|
||||
cost inters_size = 1;
|
||||
variable_intersection vi(m_context.get_manager());
|
||||
vi.populate(t1, t2);
|
||||
unsigned n = vi.size();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
unsigned arg_index1, arg_index2;
|
||||
vi.get(i, arg_index1, arg_index2);
|
||||
inters_size *= get_domain_size(t1_pred, arg_index1);
|
||||
//joined arguments must have the same domain
|
||||
SASSERT(get_domain_size(t1_pred, arg_index1)==get_domain_size(t2_pred, arg_index2));
|
||||
}
|
||||
cost res = estimate_size(t1)*estimate_size(t2)/(inters_size*inters_size);
|
||||
//cost res = -inters_size;
|
||||
|
||||
/*unsigned t1_strat = get_stratum(t1_pred);
|
||||
SASSERT(t1_strat<=m_head_stratum);
|
||||
if(t1_strat<m_head_stratum) {
|
||||
unsigned t2_strat = get_stratum(t2_pred);
|
||||
SASSERT(t2_strat<=m_head_stratum);
|
||||
if(t2_strat<m_head_stratum) {
|
||||
//the rule of this predicates would depend on predicates
|
||||
//in lower stratum than the head, which is a good thing, since
|
||||
//then the rule code will not need to appear in a loop
|
||||
if(res>0) {
|
||||
res /= 2;
|
||||
}
|
||||
else {
|
||||
res *= 2;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
TRACE("report_costs",
|
||||
display_predicate(m_context, t1, tout);
|
||||
display_predicate(m_context, t2, tout);
|
||||
tout << res << "\n";);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool pick_best_pair(app_pair & p) {
|
||||
app_pair best;
|
||||
bool found = false;
|
||||
cost best_cost;
|
||||
|
||||
cost_map::iterator it = m_costs.begin();
|
||||
cost_map::iterator end = m_costs.end();
|
||||
for(; it!=end; ++it) {
|
||||
app_pair key = it->m_key;
|
||||
pair_info & inf = *it->m_value;
|
||||
if(!inf.can_be_joined()) {
|
||||
continue;
|
||||
}
|
||||
cost c = inf.get_cost();
|
||||
if(!found || c<best_cost) {
|
||||
found = true;
|
||||
best_cost = c;
|
||||
best = key;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
return false;
|
||||
}
|
||||
p=best;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
rule_set * run(rule_set const & source) {
|
||||
|
||||
unsigned num_rules = source.get_num_rules();
|
||||
for (unsigned i = 0; i < num_rules; i++) {
|
||||
register_rule(source.get_rule(i));
|
||||
}
|
||||
|
||||
app_pair selected;
|
||||
while(pick_best_pair(selected)) {
|
||||
join_pair(selected);
|
||||
}
|
||||
|
||||
if(m_modified_rules.empty()) {
|
||||
return 0;
|
||||
}
|
||||
rule_set * result = alloc(rule_set, m_context);
|
||||
rule_pred_map::iterator rcit = m_rules_content.begin();
|
||||
rule_pred_map::iterator rcend = m_rules_content.end();
|
||||
for(; rcit!=rcend; ++rcit) {
|
||||
rule * orig_r = rcit->m_key;
|
||||
ptr_vector<app> content = rcit->m_value;
|
||||
SASSERT(content.size()<=2);
|
||||
if(content.size()==orig_r->get_positive_tail_size()) {
|
||||
//rule did not change
|
||||
result->add_rule(orig_r);
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr_vector<app> tail(content);
|
||||
svector<bool> negs(tail.size(), false);
|
||||
unsigned or_len = orig_r->get_tail_size();
|
||||
for(unsigned i=orig_r->get_positive_tail_size(); i<or_len; i++) {
|
||||
tail.push_back(orig_r->get_tail(i));
|
||||
negs.push_back(orig_r->is_neg_tail(i));
|
||||
}
|
||||
|
||||
rule * new_rule = m_context.get_rule_manager().mk(orig_r->get_head(), tail.size(), tail.c_ptr(),
|
||||
negs.c_ptr());
|
||||
|
||||
new_rule->set_accounting_parent_object(m_context, orig_r);
|
||||
m_context.get_rule_manager().mk_rule_rewrite_proof(*orig_r, *new_rule);
|
||||
result->add_rule(new_rule);
|
||||
}
|
||||
while (!m_introduced_rules.empty()) {
|
||||
result->add_rule(m_introduced_rules.back());
|
||||
m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back());
|
||||
m_introduced_rules.pop_back();
|
||||
}
|
||||
result->inherit_predicates(source);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
rule_set * mk_simple_joins::operator()(rule_set const & source) {
|
||||
rule_set rs_aux_copy(m_context);
|
||||
rs_aux_copy.replace_rules(source);
|
||||
if(!rs_aux_copy.is_closed()) {
|
||||
rs_aux_copy.close();
|
||||
}
|
||||
|
||||
join_planner planner(m_context, rs_aux_copy);
|
||||
|
||||
return planner.run(source);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
63
src/muz/rel/dl_mk_simple_joins.h
Normal file
63
src/muz/rel/dl_mk_simple_joins.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_simple_joins.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2010-05-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_SIMPLE_JOINS_H_
|
||||
#define _DL_MK_SIMPLE_JOINS_H_
|
||||
|
||||
#include"map.h"
|
||||
#include"obj_pair_hashtable.h"
|
||||
|
||||
#include"dl_context.h"
|
||||
#include"dl_rule_set.h"
|
||||
#include"dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
/**
|
||||
\brief Functor for creating rules that contain simple joins.
|
||||
A simple join is the join of two tables.
|
||||
|
||||
After applying this transformation, every rule has at most one join.
|
||||
So, the rules will have the form
|
||||
|
||||
HEAD :- TAIL.
|
||||
HEAD :- TAIL_1, TAIL_2.
|
||||
|
||||
We also assume a rule may contain interpreted expressions that work as filtering conditions.
|
||||
So, we may also have:
|
||||
|
||||
HEAD :- TAIL, C_1, ..., C_n.
|
||||
HEAD :- TAIL_1, TAIL_2, C_1, ..., C_n.
|
||||
|
||||
Where the C_i's are interpreted expressions.
|
||||
|
||||
We say that a rule containing C_i's is a rule with a "big tail".
|
||||
*/
|
||||
class mk_simple_joins : public rule_transformer::plugin {
|
||||
context & m_context;
|
||||
rule_manager & rm;
|
||||
public:
|
||||
mk_simple_joins(context & ctx);
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_SIMPLE_JOINS_H_ */
|
||||
|
||||
1117
src/muz/rel/dl_product_relation.cpp
Normal file
1117
src/muz/rel/dl_product_relation.cpp
Normal file
File diff suppressed because it is too large
Load diff
191
src/muz/rel/dl_product_relation.h
Normal file
191
src/muz/rel/dl_product_relation.h
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/*++
|
||||
Copyright (c) 2010 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_product_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A Relation relation combinator.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2010-4-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_PRODUCT_RELATION_H_
|
||||
#define _DL_PRODUCT_RELATION_H_
|
||||
|
||||
|
||||
#include "dl_context.h"
|
||||
#include "dl_relation_manager.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class product_relation;
|
||||
|
||||
class product_relation_plugin : public relation_plugin {
|
||||
friend class product_relation;
|
||||
public:
|
||||
typedef svector<family_id> rel_spec;
|
||||
private:
|
||||
class join_fn;
|
||||
class transform_fn;
|
||||
class mutator_fn;
|
||||
class aligned_union_fn;
|
||||
class unaligned_union_fn;
|
||||
class single_non_transparent_src_union_fn;
|
||||
class filter_equal_fn;
|
||||
class filter_identical_fn;
|
||||
class filter_interpreted_fn;
|
||||
struct fid_hash {
|
||||
typedef family_id data;
|
||||
unsigned operator()(data x) const { return static_cast<unsigned>(x); }
|
||||
};
|
||||
|
||||
rel_spec_store<rel_spec, svector_hash<fid_hash> > m_spec_store;
|
||||
|
||||
family_id get_relation_kind(const product_relation & r);
|
||||
|
||||
bool is_product_relation(relation_base * r) { return r->get_plugin().is_product_relation(); }
|
||||
|
||||
public:
|
||||
static product_relation_plugin& get_plugin(relation_manager & rmgr);
|
||||
|
||||
product_relation_plugin(relation_manager& m);
|
||||
|
||||
virtual void initialize(family_id fid);
|
||||
|
||||
virtual bool can_handle_signature(const relation_signature & s);
|
||||
virtual bool can_handle_signature(const relation_signature & s, family_id kind);
|
||||
|
||||
static symbol get_name() { return symbol("product_relation"); }
|
||||
|
||||
family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec);
|
||||
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
virtual relation_base * mk_empty(const relation_signature & s, family_id kind);
|
||||
|
||||
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
|
||||
virtual relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind);
|
||||
|
||||
protected:
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
|
||||
unsigned col);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
|
||||
static bool is_product_relation(relation_base const& r);
|
||||
|
||||
private:
|
||||
static product_relation& get(relation_base& r);
|
||||
static product_relation const & get(relation_base const& r);
|
||||
static product_relation* get(relation_base* r);
|
||||
static product_relation const* get(relation_base const* r);
|
||||
|
||||
relation_union_fn * mk_union_w_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta, bool is_widen);
|
||||
|
||||
bool are_aligned(const product_relation& r1, const product_relation& r2);
|
||||
static void get_common_spec(const ptr_vector<const product_relation> & rels, rel_spec & res);
|
||||
};
|
||||
|
||||
|
||||
class product_relation : public relation_base {
|
||||
friend class product_relation_plugin;
|
||||
|
||||
friend class product_relation_plugin::join_fn;
|
||||
friend class product_relation_plugin::transform_fn;
|
||||
friend class product_relation_plugin::mutator_fn;
|
||||
friend class product_relation_plugin::aligned_union_fn;
|
||||
friend class product_relation_plugin::unaligned_union_fn;
|
||||
friend class product_relation_plugin::single_non_transparent_src_union_fn;
|
||||
friend class product_relation_plugin::filter_equal_fn;
|
||||
friend class product_relation_plugin::filter_identical_fn;
|
||||
friend class product_relation_plugin::filter_interpreted_fn;
|
||||
|
||||
|
||||
|
||||
typedef product_relation_plugin::rel_spec rel_spec;
|
||||
|
||||
/**
|
||||
If m_relations is empty, value of this determines whether the relation is empty or full.
|
||||
*/
|
||||
bool m_default_empty;
|
||||
|
||||
/**
|
||||
There must not be two relations of the same kind
|
||||
*/
|
||||
ptr_vector<relation_base> m_relations;
|
||||
|
||||
/**
|
||||
Array of kinds of inner relations.
|
||||
|
||||
If two product relations have equal signature and specification, their
|
||||
m_relations arrays contain corresponding relations at the same indexes.
|
||||
|
||||
The value returned by get_kind() depends uniquely on the specification.
|
||||
*/
|
||||
rel_spec m_spec;
|
||||
|
||||
/**
|
||||
\brief Ensure the kind assigned to this relation reflects the types of inner relations.
|
||||
*/
|
||||
void ensure_correct_kind();
|
||||
/**
|
||||
The current specification must be a subset of the new one.
|
||||
*/
|
||||
void convert_spec(const rel_spec & spec);
|
||||
public:
|
||||
product_relation(product_relation_plugin& p, relation_signature const& s);
|
||||
product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations);
|
||||
|
||||
~product_relation();
|
||||
|
||||
virtual bool empty() const;
|
||||
virtual void add_fact(const relation_fact & f);
|
||||
virtual bool contains_fact(const relation_fact & f) const;
|
||||
virtual product_relation * clone() const;
|
||||
virtual product_relation * complement(func_decl* p) const;
|
||||
virtual void display(std::ostream & out) const;
|
||||
virtual void to_formula(expr_ref& fml) const;
|
||||
product_relation_plugin& get_plugin() const;
|
||||
|
||||
unsigned size() const { return m_relations.size(); }
|
||||
relation_base& operator[](unsigned i) const { return *m_relations[i]; }
|
||||
|
||||
/**
|
||||
If all relations except one are sieve_relations with no inner columns,
|
||||
return true and into \c idx assign index of that relation. Otherwise return
|
||||
false.
|
||||
*/
|
||||
bool try_get_single_non_transparent(unsigned & idx) const;
|
||||
|
||||
virtual bool is_precise() const {
|
||||
for (unsigned i = 0; i < m_relations.size(); ++i) {
|
||||
if (!m_relations[i]->is_precise()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
1721
src/muz/rel/dl_relation_manager.cpp
Normal file
1721
src/muz/rel/dl_relation_manager.cpp
Normal file
File diff suppressed because it is too large
Load diff
701
src/muz/rel/dl_relation_manager.h
Normal file
701
src/muz/rel/dl_relation_manager.h
Normal 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_ */
|
||||
|
||||
666
src/muz/rel/dl_sieve_relation.cpp
Normal file
666
src/muz/rel/dl_sieve_relation.cpp
Normal file
|
|
@ -0,0 +1,666 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_explanations.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-11-08.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include"ast_pp.h"
|
||||
#include"dl_sieve_relation.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// sieve_relation
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
sieve_relation::sieve_relation(sieve_relation_plugin & p, const relation_signature & s,
|
||||
const bool * inner_columns, relation_base * inner)
|
||||
: relation_base(p, s), m_inner_cols(s.size(), inner_columns), m_inner(inner) {
|
||||
unsigned n = s.size();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
if(inner_columns && inner_columns[i]) {
|
||||
unsigned inner_idx = m_inner2sig.size();
|
||||
SASSERT(get_inner().get_signature()[inner_idx]==s[i]);
|
||||
m_sig2inner.push_back(inner_idx);
|
||||
m_inner2sig.push_back(i);
|
||||
}
|
||||
else {
|
||||
m_sig2inner.push_back(UINT_MAX);
|
||||
m_ignored_cols.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
set_kind(p.get_relation_kind(*this, inner_columns));
|
||||
}
|
||||
|
||||
void sieve_relation::add_fact(const relation_fact & f) {
|
||||
relation_fact inner_f = f;
|
||||
project_out_vector_columns(inner_f, m_ignored_cols);
|
||||
get_inner().add_fact(inner_f);
|
||||
}
|
||||
|
||||
bool sieve_relation::contains_fact(const relation_fact & f) const {
|
||||
relation_fact inner_f = f;
|
||||
project_out_vector_columns(inner_f, m_ignored_cols);
|
||||
return get_inner().contains_fact(inner_f);
|
||||
}
|
||||
|
||||
sieve_relation * sieve_relation::clone() const {
|
||||
relation_base * new_inner = get_inner().clone();
|
||||
return get_plugin().mk_from_inner(get_signature(), m_inner_cols.c_ptr(), new_inner);
|
||||
}
|
||||
|
||||
relation_base * sieve_relation::complement(func_decl* p) const {
|
||||
//this is not precisely a complement, because we still treat the ignored collumns as
|
||||
//full, but it should give reasonable results inside the product relation
|
||||
relation_base * new_inner = get_inner().complement(p);
|
||||
return get_plugin().mk_from_inner(get_signature(), m_inner_cols.c_ptr(), new_inner);
|
||||
}
|
||||
|
||||
void sieve_relation::to_formula(expr_ref& fml) const {
|
||||
ast_manager& m = fml.get_manager();
|
||||
expr_ref_vector s(m);
|
||||
expr_ref tmp(m);
|
||||
relation_signature const& sig = get_inner().get_signature();
|
||||
unsigned sz = sig.size();
|
||||
for (unsigned i = sz ; i > 0; ) {
|
||||
--i;
|
||||
unsigned idx = m_inner2sig[i];
|
||||
s.push_back(m.mk_var(idx, sig[i]));
|
||||
}
|
||||
get_inner().to_formula(tmp);
|
||||
get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr(), fml);
|
||||
}
|
||||
|
||||
|
||||
void sieve_relation::display(std::ostream & out) const {
|
||||
out << "Sieve relation ";
|
||||
print_container(m_inner_cols, out);
|
||||
out <<"\n";
|
||||
get_inner().display(out);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// sieve_relation_plugin
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
sieve_relation_plugin & sieve_relation_plugin::get_plugin(relation_manager & rmgr) {
|
||||
sieve_relation_plugin * res = static_cast<sieve_relation_plugin *>(rmgr.get_relation_plugin(get_name()));
|
||||
if(!res) {
|
||||
res = alloc(sieve_relation_plugin, rmgr);
|
||||
rmgr.register_plugin(res);
|
||||
}
|
||||
return *res;
|
||||
}
|
||||
|
||||
sieve_relation& sieve_relation_plugin::get(relation_base& r) {
|
||||
return dynamic_cast<sieve_relation&>(r);
|
||||
}
|
||||
|
||||
sieve_relation const & sieve_relation_plugin::get(relation_base const& r) {
|
||||
return dynamic_cast<sieve_relation const&>(r);
|
||||
}
|
||||
|
||||
sieve_relation* sieve_relation_plugin::get(relation_base* r) {
|
||||
return dynamic_cast<sieve_relation*>(r);
|
||||
}
|
||||
|
||||
sieve_relation const* sieve_relation_plugin::get(relation_base const* r) {
|
||||
return dynamic_cast<sieve_relation const*>(r);
|
||||
}
|
||||
|
||||
sieve_relation_plugin::sieve_relation_plugin(relation_manager & manager)
|
||||
: relation_plugin(get_name(), manager, ST_SIEVE_RELATION),
|
||||
m_spec_store(*this) {}
|
||||
|
||||
void sieve_relation_plugin::initialize(family_id fid) {
|
||||
relation_plugin::initialize(fid);
|
||||
m_spec_store.add_available_kind(get_kind());
|
||||
}
|
||||
|
||||
family_id sieve_relation_plugin::get_relation_kind(const relation_signature & sig,
|
||||
const bool * inner_columns, family_id inner_kind) {
|
||||
rel_spec spec(sig.size(), inner_columns, inner_kind);
|
||||
return m_spec_store.get_relation_kind(sig, spec);
|
||||
}
|
||||
|
||||
family_id sieve_relation_plugin::get_relation_kind(sieve_relation & r, const bool * inner_columns) {
|
||||
const relation_signature & sig = r.get_signature();
|
||||
return get_relation_kind(sig, inner_columns, r.get_inner().get_kind());
|
||||
}
|
||||
|
||||
void sieve_relation_plugin::extract_inner_columns(const relation_signature & s, relation_plugin & inner,
|
||||
svector<bool> & inner_columns) {
|
||||
SASSERT(inner_columns.size()==s.size());
|
||||
unsigned n = s.size();
|
||||
relation_signature inner_sig_singleton;
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
inner_sig_singleton.reset();
|
||||
inner_sig_singleton.push_back(s[i]);
|
||||
inner_columns[i] = inner.can_handle_signature(inner_sig_singleton);
|
||||
}
|
||||
#if Z3DEBUG
|
||||
//we assume that if a relation plugin can handle two sets of columns separetely,
|
||||
//it can also handle them in one relation
|
||||
relation_signature inner_sig;
|
||||
collect_inner_signature(s, inner_columns, inner_sig);
|
||||
SASSERT(inner.can_handle_signature(inner_sig));
|
||||
#endif
|
||||
}
|
||||
|
||||
void sieve_relation_plugin::collect_inner_signature(const relation_signature & s,
|
||||
const svector<bool> & inner_columns, relation_signature & inner_sig) {
|
||||
SASSERT(inner_columns.size()==s.size());
|
||||
inner_sig.reset();
|
||||
unsigned n = s.size();
|
||||
for(unsigned i=0; i<n; i++) {
|
||||
if(inner_columns[i]) {
|
||||
inner_sig.push_back(s[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sieve_relation_plugin::extract_inner_signature(const relation_signature & s,
|
||||
relation_signature & inner_sig) {
|
||||
UNREACHABLE();
|
||||
#if 0
|
||||
svector<bool> inner_cols(s.size());
|
||||
extract_inner_columns(s, inner_cols.c_ptr());
|
||||
collect_inner_signature(s, inner_cols, inner_sig);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sieve_relation_plugin::can_handle_signature(const relation_signature & s) {
|
||||
//we do not want this plugin to handle anything by default
|
||||
return false;
|
||||
#if 0
|
||||
relation_signature inner_sig;
|
||||
extract_inner_signature(s, inner_sig);
|
||||
SASSERT(inner_sig.size()<=s.size());
|
||||
return !inner_sig.empty() && inner_sig.size()!=s.size();
|
||||
#endif
|
||||
}
|
||||
|
||||
sieve_relation * sieve_relation_plugin::mk_from_inner(const relation_signature & s, const bool * inner_columns,
|
||||
relation_base * inner_rel) {
|
||||
SASSERT(!inner_rel->get_plugin().is_sieve_relation()); //it does not make sense to make a sieve of a sieve
|
||||
return alloc(sieve_relation, *this, s, inner_columns, inner_rel);
|
||||
}
|
||||
|
||||
sieve_relation * sieve_relation_plugin::mk_empty(const sieve_relation & original) {
|
||||
return static_cast<sieve_relation *>(mk_empty(original.get_signature(), original.get_kind()));
|
||||
}
|
||||
|
||||
relation_base * sieve_relation_plugin::mk_empty(const relation_base & original) {
|
||||
return mk_empty(static_cast<const sieve_relation &>(original));
|
||||
}
|
||||
|
||||
relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s, family_id kind) {
|
||||
rel_spec spec;
|
||||
m_spec_store.get_relation_spec(s, kind, spec);
|
||||
relation_signature inner_sig;
|
||||
collect_inner_signature(s, spec.m_inner_cols, inner_sig);
|
||||
relation_base * inner = get_manager().mk_empty_relation(inner_sig, spec.m_inner_kind);
|
||||
return mk_from_inner(s, spec.m_inner_cols.c_ptr(), inner);
|
||||
}
|
||||
|
||||
|
||||
relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
#if 0
|
||||
svector<bool> inner_cols(s.size());
|
||||
extract_inner_columns(s, inner_cols.c_ptr());
|
||||
return mk_empty(s, inner_cols.c_ptr());
|
||||
#endif
|
||||
}
|
||||
|
||||
sieve_relation * sieve_relation_plugin::mk_empty(const relation_signature & s, relation_plugin & inner_plugin) {
|
||||
SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve
|
||||
svector<bool> inner_cols(s.size());
|
||||
extract_inner_columns(s, inner_plugin, inner_cols);
|
||||
relation_signature inner_sig;
|
||||
collect_inner_signature(s, inner_cols, inner_sig);
|
||||
relation_base * inner_rel = inner_plugin.mk_empty(inner_sig);
|
||||
return mk_from_inner(s, inner_cols, inner_rel);
|
||||
}
|
||||
|
||||
relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) {
|
||||
relation_signature empty_sig;
|
||||
relation_plugin& plugin = get_manager().get_appropriate_plugin(s);
|
||||
relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id);
|
||||
svector<bool> inner_cols;
|
||||
inner_cols.resize(s.size(), false);
|
||||
return mk_from_inner(s, inner_cols, inner);
|
||||
}
|
||||
|
||||
sieve_relation * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin) {
|
||||
SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve
|
||||
svector<bool> inner_cols(s.size());
|
||||
extract_inner_columns(s, inner_plugin, inner_cols);
|
||||
relation_signature inner_sig;
|
||||
collect_inner_signature(s, inner_cols, inner_sig);
|
||||
relation_base * inner_rel = inner_plugin.mk_full(p, inner_sig, null_family_id);
|
||||
return mk_from_inner(s, inner_cols, inner_rel);
|
||||
}
|
||||
|
||||
class sieve_relation_plugin::join_fn : public convenient_relation_join_fn {
|
||||
sieve_relation_plugin & m_plugin;
|
||||
unsigned_vector m_inner_cols_1;
|
||||
unsigned_vector m_inner_cols_2;
|
||||
svector<bool> m_result_inner_cols;
|
||||
|
||||
scoped_ptr<relation_join_fn> m_inner_join_fun;
|
||||
public:
|
||||
join_fn(sieve_relation_plugin & p, const relation_base & r1, const relation_base & r2, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2, relation_join_fn * inner_join_fun)
|
||||
: convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2),
|
||||
m_plugin(p),
|
||||
m_inner_join_fun(inner_join_fun) {
|
||||
bool r1_sieved = r1.get_plugin().is_sieve_relation();
|
||||
bool r2_sieved = r2.get_plugin().is_sieve_relation();
|
||||
const sieve_relation * sr1 = r1_sieved ? static_cast<const sieve_relation *>(&r1) : 0;
|
||||
const sieve_relation * sr2 = r2_sieved ? static_cast<const sieve_relation *>(&r2) : 0;
|
||||
if(r1_sieved) {
|
||||
m_result_inner_cols.append(sr1->m_inner_cols);
|
||||
}
|
||||
else {
|
||||
m_result_inner_cols.resize(r1.get_signature().size(), true);
|
||||
}
|
||||
if(r2_sieved) {
|
||||
m_result_inner_cols.append(sr2->m_inner_cols);
|
||||
}
|
||||
else {
|
||||
m_result_inner_cols.resize(m_result_inner_cols.size() + r2.get_signature().size(), true);
|
||||
}
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) {
|
||||
bool r1_sieved = r1.get_plugin().is_sieve_relation();
|
||||
bool r2_sieved = r2.get_plugin().is_sieve_relation();
|
||||
SASSERT(r1_sieved || r2_sieved);
|
||||
const sieve_relation * sr1 = r1_sieved ? static_cast<const sieve_relation *>(&r1) : 0;
|
||||
const sieve_relation * sr2 = r2_sieved ? static_cast<const sieve_relation *>(&r2) : 0;
|
||||
const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1;
|
||||
const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2;
|
||||
|
||||
relation_base * inner_res = (*m_inner_join_fun)(inner1, inner2);
|
||||
|
||||
return m_plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_join_fn * sieve_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if( &r1.get_plugin()!=this && &r2.get_plugin()!=this ) {
|
||||
//we create just operations that involve the current plugin
|
||||
return 0;
|
||||
}
|
||||
bool r1_sieved = r1.get_plugin().is_sieve_relation();
|
||||
bool r2_sieved = r2.get_plugin().is_sieve_relation();
|
||||
const sieve_relation * sr1 = r1_sieved ? static_cast<const sieve_relation *>(&r1) : 0;
|
||||
const sieve_relation * sr2 = r2_sieved ? static_cast<const sieve_relation *>(&r2) : 0;
|
||||
const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1;
|
||||
const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2;
|
||||
|
||||
unsigned_vector inner_cols1;
|
||||
unsigned_vector inner_cols2;
|
||||
|
||||
for(unsigned i=0; i<col_cnt; i++) {
|
||||
//if at least one end of an equality is not an inner column, we ignore that equality
|
||||
//(which introduces imprecision)
|
||||
if(r1_sieved && !sr1->is_inner_col(cols1[i])) {
|
||||
continue;
|
||||
}
|
||||
if(r2_sieved && !sr2->is_inner_col(cols2[i])) {
|
||||
continue;
|
||||
}
|
||||
inner_cols1.push_back( r1_sieved ? sr1->get_inner_col(cols1[i]) : cols1[i] );
|
||||
inner_cols2.push_back( r2_sieved ? sr2->get_inner_col(cols2[i]) : cols2[i] );
|
||||
}
|
||||
|
||||
relation_join_fn * inner_join_fun = get_manager().mk_join_fn(inner1, inner2, inner_cols1, inner_cols2, false);
|
||||
if(!inner_join_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2, inner_join_fun);
|
||||
}
|
||||
|
||||
|
||||
class sieve_relation_plugin::transformer_fn : public convenient_relation_transformer_fn {
|
||||
svector<bool> m_result_inner_cols;
|
||||
|
||||
scoped_ptr<relation_transformer_fn> m_inner_fun;
|
||||
public:
|
||||
transformer_fn(relation_transformer_fn * inner_fun, const relation_signature & result_sig,
|
||||
const bool * result_inner_cols)
|
||||
: m_result_inner_cols(result_sig.size(), result_inner_cols), m_inner_fun(inner_fun) {
|
||||
get_result_signature() = result_sig;
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r0) {
|
||||
SASSERT(r0.get_plugin().is_sieve_relation());
|
||||
const sieve_relation & r = static_cast<const sieve_relation &>(r0);
|
||||
sieve_relation_plugin & plugin = r.get_plugin();
|
||||
|
||||
relation_base * inner_res = (*m_inner_fun)(r.get_inner());
|
||||
|
||||
return plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * sieve_relation_plugin::mk_project_fn(const relation_base & r0, unsigned col_cnt,
|
||||
const unsigned * removed_cols) {
|
||||
if(&r0.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
const sieve_relation & r = static_cast<const sieve_relation &>(r0);
|
||||
unsigned_vector inner_removed_cols;
|
||||
|
||||
for(unsigned i=0; i<col_cnt; i++) {
|
||||
unsigned col = removed_cols[i];
|
||||
if(r.is_inner_col(col)) {
|
||||
inner_removed_cols.push_back(r.get_inner_col(col));
|
||||
}
|
||||
}
|
||||
|
||||
svector<bool> result_inner_cols = r.m_inner_cols;
|
||||
project_out_vector_columns(result_inner_cols, col_cnt, removed_cols);
|
||||
|
||||
relation_signature result_sig;
|
||||
relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, result_sig);
|
||||
|
||||
relation_transformer_fn * inner_fun;
|
||||
if(inner_removed_cols.empty()) {
|
||||
inner_fun = alloc(identity_relation_transformer_fn);
|
||||
}
|
||||
else {
|
||||
inner_fun = get_manager().mk_project_fn(r.get_inner(), inner_removed_cols);
|
||||
}
|
||||
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr());
|
||||
}
|
||||
|
||||
relation_transformer_fn * sieve_relation_plugin::mk_rename_fn(const relation_base & r0,
|
||||
unsigned cycle_len, const unsigned * permutation_cycle) {
|
||||
if(&r0.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
const sieve_relation & r = static_cast<const sieve_relation &>(r0);
|
||||
|
||||
unsigned sig_sz = r.get_signature().size();
|
||||
unsigned_vector permutation;
|
||||
add_sequence(0, sig_sz, permutation);
|
||||
permutate_by_cycle(permutation, cycle_len, permutation_cycle);
|
||||
|
||||
bool inner_identity;
|
||||
unsigned_vector inner_permutation;
|
||||
collect_sub_permutation(permutation, r.m_sig2inner, inner_permutation, inner_identity);
|
||||
|
||||
svector<bool> result_inner_cols = r.m_inner_cols;
|
||||
permutate_by_cycle(result_inner_cols, cycle_len, permutation_cycle);
|
||||
|
||||
relation_signature result_sig;
|
||||
relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, result_sig);
|
||||
|
||||
relation_transformer_fn * inner_fun =
|
||||
get_manager().mk_permutation_rename_fn(r.get_inner(), inner_permutation);
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr());
|
||||
}
|
||||
|
||||
|
||||
class sieve_relation_plugin::union_fn : public relation_union_fn {
|
||||
scoped_ptr<relation_union_fn> m_union_fun;
|
||||
public:
|
||||
union_fn(relation_union_fn * union_fun) : m_union_fun(union_fun) {}
|
||||
|
||||
virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) {
|
||||
bool tgt_sieved = tgt.get_plugin().is_sieve_relation();
|
||||
bool src_sieved = src.get_plugin().is_sieve_relation();
|
||||
bool delta_sieved = delta && delta->get_plugin().is_sieve_relation();
|
||||
sieve_relation * stgt = tgt_sieved ? static_cast<sieve_relation *>(&tgt) : 0;
|
||||
const sieve_relation * ssrc = src_sieved ? static_cast<const sieve_relation *>(&src) : 0;
|
||||
sieve_relation * sdelta = delta_sieved ? static_cast<sieve_relation *>(delta) : 0;
|
||||
relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt;
|
||||
const relation_base & isrc = src_sieved ? ssrc->get_inner() : src;
|
||||
relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta;
|
||||
|
||||
(*m_union_fun)(itgt, isrc, idelta);
|
||||
}
|
||||
};
|
||||
|
||||
relation_union_fn * sieve_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if(&tgt.get_plugin()!=this && &src.get_plugin()!=this && (delta && &delta->get_plugin()!=this)) {
|
||||
//we create the operation only if it involves this plugin
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool tgt_sieved = tgt.get_plugin().is_sieve_relation();
|
||||
bool src_sieved = src.get_plugin().is_sieve_relation();
|
||||
bool delta_sieved = delta && delta->get_plugin().is_sieve_relation();
|
||||
const sieve_relation * stgt = tgt_sieved ? static_cast<const sieve_relation *>(&tgt) : 0;
|
||||
const sieve_relation * ssrc = src_sieved ? static_cast<const sieve_relation *>(&src) : 0;
|
||||
const sieve_relation * sdelta = delta_sieved ? static_cast<const sieve_relation *>(delta) : 0;
|
||||
const relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt;
|
||||
const relation_base & isrc = src_sieved ? ssrc->get_inner() : src;
|
||||
const relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta;
|
||||
|
||||
//Now we require that the sieved and inner columns must match on all relations.
|
||||
//We may want to allow for some cases of misalignment even though it could introcude imprecision
|
||||
if( tgt_sieved && src_sieved && (!delta || delta_sieved) ) {
|
||||
if( !vectors_equal(stgt->m_inner_cols, ssrc->m_inner_cols)
|
||||
|| (delta && !vectors_equal(stgt->m_inner_cols, sdelta->m_inner_cols)) ) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( (stgt && !stgt->no_sieved_columns())
|
||||
|| (ssrc && !ssrc->no_sieved_columns())
|
||||
|| (sdelta && !sdelta->no_sieved_columns()) ) {
|
||||
//We have an unsieved relation and then some relation with some sieved columns,
|
||||
//which means there is an misalignment.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta);
|
||||
if(!union_fun) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return alloc(union_fn, union_fun);
|
||||
}
|
||||
|
||||
|
||||
class sieve_relation_plugin::filter_fn : public relation_mutator_fn {
|
||||
scoped_ptr<relation_mutator_fn> m_inner_fun;
|
||||
public:
|
||||
filter_fn(relation_mutator_fn * inner_fun)
|
||||
: m_inner_fun(inner_fun) {}
|
||||
|
||||
virtual void operator()(relation_base & r0) {
|
||||
SASSERT(r0.get_plugin().is_sieve_relation());
|
||||
sieve_relation & r = static_cast<sieve_relation &>(r0);
|
||||
|
||||
(*m_inner_fun)(r.get_inner());
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * sieve_relation_plugin::mk_filter_identical_fn(const relation_base & r0,
|
||||
unsigned col_cnt, const unsigned * identical_cols) {
|
||||
if(&r0.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
const sieve_relation & r = static_cast<const sieve_relation &>(r0);
|
||||
unsigned_vector inner_icols;
|
||||
|
||||
//we ignore the columns which do not belong to the inner relation (which introduces imprecision)
|
||||
for(unsigned i=0; i<col_cnt; i++) {
|
||||
unsigned col = identical_cols[i];
|
||||
if(r.is_inner_col(col)) {
|
||||
inner_icols.push_back(r.get_inner_col(col));
|
||||
}
|
||||
}
|
||||
|
||||
if(inner_icols.size()<2) {
|
||||
return alloc(identity_relation_mutator_fn);
|
||||
}
|
||||
|
||||
relation_mutator_fn * inner_fun = get_manager().mk_filter_identical_fn(r.get_inner(), inner_icols);
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_fn, inner_fun);
|
||||
}
|
||||
|
||||
relation_mutator_fn * sieve_relation_plugin::mk_filter_equal_fn(const relation_base & r0,
|
||||
const relation_element & value, unsigned col) {
|
||||
if(&r0.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
const sieve_relation & r = static_cast<const sieve_relation &>(r0);
|
||||
if(!r.is_inner_col(col)) {
|
||||
//if the column which do not belong to the inner relation, we do nothing (which introduces imprecision)
|
||||
return alloc(identity_relation_mutator_fn);
|
||||
}
|
||||
unsigned inner_col = r.get_inner_col(col);
|
||||
|
||||
relation_mutator_fn * inner_fun = get_manager().mk_filter_equal_fn(r.get_inner(), value, inner_col);
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_fn, inner_fun);
|
||||
}
|
||||
|
||||
relation_mutator_fn * sieve_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb,
|
||||
app * condition) {
|
||||
if(&rb.get_plugin()!=this) {
|
||||
return 0;
|
||||
}
|
||||
ast_manager & m = get_ast_manager();
|
||||
const sieve_relation & r = static_cast<const sieve_relation &>(rb);
|
||||
const relation_signature sig = r.get_signature();
|
||||
unsigned sz = sig.size();
|
||||
|
||||
var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition);
|
||||
expr_ref_vector subst_vect(m);
|
||||
subst_vect.resize(sz);
|
||||
unsigned subst_ofs = sz-1;
|
||||
for(unsigned i=0; i<sz; i++) {
|
||||
if(!cond_vars.contains(i)) {
|
||||
continue;
|
||||
}
|
||||
if(!r.is_inner_col(i)) {
|
||||
//If the condition involves columns which do not belong to the inner relation,
|
||||
//we do nothing (which introduces imprecision).
|
||||
//Maybe we might try to do some quantifier elimination...
|
||||
return alloc(identity_relation_mutator_fn);
|
||||
}
|
||||
subst_vect[subst_ofs-i] = m.mk_var(r.m_sig2inner[i], sig[i]);
|
||||
}
|
||||
expr_ref inner_cond(m);
|
||||
get_context().get_var_subst()(condition, subst_vect.size(), subst_vect.c_ptr(), inner_cond);
|
||||
|
||||
relation_mutator_fn * inner_fun = get_manager().mk_filter_interpreted_fn(r.get_inner(), to_app(inner_cond));
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_fn, inner_fun);
|
||||
}
|
||||
|
||||
class sieve_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn {
|
||||
scoped_ptr<relation_intersection_filter_fn> m_inner_fun;
|
||||
public:
|
||||
negation_filter_fn(relation_intersection_filter_fn * inner_fun)
|
||||
: m_inner_fun(inner_fun) {}
|
||||
|
||||
virtual void operator()(relation_base & r, const relation_base & neg) {
|
||||
bool r_sieved = r.get_plugin().is_sieve_relation();
|
||||
bool neg_sieved = neg.get_plugin().is_sieve_relation();
|
||||
SASSERT(r_sieved || neg_sieved);
|
||||
sieve_relation * sr = r_sieved ? static_cast<sieve_relation *>(&r) : 0;
|
||||
const sieve_relation * sneg = neg_sieved ? static_cast<const sieve_relation *>(&neg) : 0;
|
||||
relation_base & inner_r = r_sieved ? sr->get_inner() : r;
|
||||
const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg;
|
||||
|
||||
(*m_inner_fun)(inner_r, inner_neg);
|
||||
}
|
||||
};
|
||||
|
||||
relation_intersection_filter_fn * sieve_relation_plugin::mk_filter_by_negation_fn(const relation_base & r,
|
||||
const relation_base & neg, unsigned col_cnt, const unsigned * r_cols,
|
||||
const unsigned * neg_cols) {
|
||||
if(&r.get_plugin()!=this && &neg.get_plugin()!=this) {
|
||||
//we create just operations that involve the current plugin
|
||||
return 0;
|
||||
}
|
||||
bool r_sieved = r.get_plugin().is_sieve_relation();
|
||||
bool neg_sieved = neg.get_plugin().is_sieve_relation();
|
||||
SASSERT(r_sieved || neg_sieved);
|
||||
const sieve_relation * sr = r_sieved ? static_cast<const sieve_relation *>(&r) : 0;
|
||||
const sieve_relation * sneg = neg_sieved ? static_cast<const sieve_relation *>(&neg) : 0;
|
||||
const relation_base & inner_r = r_sieved ? sr->get_inner() : r;
|
||||
const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg;
|
||||
|
||||
unsigned_vector ir_cols;
|
||||
unsigned_vector ineg_cols;
|
||||
|
||||
for(unsigned i=0; i<col_cnt; i++) {
|
||||
//if at least one end of an equality is not an inner column, we ignore that equality
|
||||
//(which introduces imprecision)
|
||||
bool r_col_inner = r_sieved && !sr->is_inner_col(r_cols[i]);
|
||||
bool neg_col_inner = neg_sieved && !sneg->is_inner_col(neg_cols[i]);
|
||||
if(r_col_inner && neg_col_inner) {
|
||||
ir_cols.push_back( r_sieved ? sr->get_inner_col(i) : i );
|
||||
ineg_cols.push_back( neg_sieved ? sneg->get_inner_col(i) : i );
|
||||
}
|
||||
else if(!r_col_inner && neg_col_inner) {
|
||||
//Sieved (i.e. full) column in r is matched on an inner column in neg.
|
||||
//If we assume the column in neg is not full, no rows from the inner relation of
|
||||
//r would be removed. So in this case we perform no operation at cost of a little
|
||||
//impresicion.
|
||||
return alloc(identity_relation_intersection_filter_fn);
|
||||
}
|
||||
else {
|
||||
//Inner or sieved column in r must match a sieved column in neg.
|
||||
//Since sieved columns are full, this is always true so we can skip the equality.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
relation_intersection_filter_fn * inner_fun =
|
||||
get_manager().mk_filter_by_negation_fn(inner_r, inner_neg, ir_cols, ineg_cols);
|
||||
if(!inner_fun) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(negation_filter_fn, inner_fun);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
198
src/muz/rel/dl_sieve_relation.h
Normal file
198
src/muz/rel/dl_sieve_relation.h
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_sieve_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-11-11.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _DL_SIEVE_RELATION_H_
|
||||
#define _DL_SIEVE_RELATION_H_
|
||||
|
||||
#include "dl_context.h"
|
||||
#include "dl_relation_manager.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class sieve_relation;
|
||||
|
||||
class sieve_relation_plugin : public relation_plugin {
|
||||
friend class sieve_relation;
|
||||
public:
|
||||
struct rel_spec {
|
||||
svector<bool> m_inner_cols;
|
||||
family_id m_inner_kind;
|
||||
|
||||
/**
|
||||
Create uninitialized rel_spec.
|
||||
*/
|
||||
rel_spec() {}
|
||||
/**
|
||||
\c inner_kind==null_family_id means we will not specify a relation kind when requesting
|
||||
the relation object from the relation_manager.
|
||||
|
||||
\c inner_kind==null_family_id cannot hold in a specification of existing relation object.
|
||||
*/
|
||||
rel_spec(unsigned sig_sz, const bool * inner_cols, family_id inner_kind=null_family_id)
|
||||
: m_inner_cols(sig_sz, inner_cols), m_inner_kind(inner_kind) {}
|
||||
|
||||
bool operator==(const rel_spec & o) const {
|
||||
return m_inner_kind==o.m_inner_kind && vectors_equal(m_inner_cols, o.m_inner_cols);
|
||||
}
|
||||
|
||||
struct hash {
|
||||
unsigned operator()(const rel_spec & s) const {
|
||||
return svector_hash<bool_hash>()(s.m_inner_cols)^s.m_inner_kind;
|
||||
}
|
||||
};
|
||||
};
|
||||
private:
|
||||
|
||||
class join_fn;
|
||||
class transformer_fn;
|
||||
class union_fn;
|
||||
class filter_fn;
|
||||
class negation_filter_fn;
|
||||
|
||||
rel_spec_store<rel_spec, rel_spec::hash, default_eq<rel_spec> > m_spec_store;
|
||||
|
||||
family_id get_relation_kind(sieve_relation & r, const bool * inner_columns);
|
||||
|
||||
void extract_inner_columns(const relation_signature & s, relation_plugin & inner,
|
||||
svector<bool> & inner_columns);
|
||||
void extract_inner_signature(const relation_signature & s, relation_signature & inner_sig);
|
||||
void collect_inner_signature(const relation_signature & s, const svector<bool> & inner_columns,
|
||||
relation_signature & inner_sig);
|
||||
public:
|
||||
static symbol get_name() { return symbol("sieve_relation"); }
|
||||
static sieve_relation_plugin& get_plugin(relation_manager & rmgr);
|
||||
|
||||
static sieve_relation& get(relation_base& r);
|
||||
static sieve_relation const & get(relation_base const& r);
|
||||
static sieve_relation* get(relation_base* r);
|
||||
static sieve_relation const* get(relation_base const* r);
|
||||
|
||||
sieve_relation_plugin(relation_manager & manager);
|
||||
|
||||
virtual void initialize(family_id fid);
|
||||
|
||||
family_id get_relation_kind(const relation_signature & sig, const bool * inner_columns,
|
||||
family_id inner_kind);
|
||||
family_id get_relation_kind(const relation_signature & sig, const svector<bool> & inner_columns,
|
||||
family_id inner_kind) {
|
||||
SASSERT(sig.size()==inner_columns.size());
|
||||
return get_relation_kind(sig, inner_columns.c_ptr(), inner_kind);
|
||||
}
|
||||
|
||||
virtual bool can_handle_signature(const relation_signature & s);
|
||||
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
sieve_relation * mk_empty(const sieve_relation & original);
|
||||
virtual relation_base * mk_empty(const relation_base & original);
|
||||
virtual relation_base * mk_empty(const relation_signature & s, family_id kind);
|
||||
sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin);
|
||||
|
||||
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
|
||||
sieve_relation * mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin);
|
||||
|
||||
|
||||
sieve_relation * mk_from_inner(const relation_signature & s, const bool * inner_columns,
|
||||
relation_base * inner_rel);
|
||||
sieve_relation * mk_from_inner(const relation_signature & s, const svector<bool> inner_columns,
|
||||
relation_base * inner_rel) {
|
||||
SASSERT(inner_columns.size()==s.size());
|
||||
return mk_from_inner(s, inner_columns.c_ptr(), inner_rel);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
|
||||
unsigned col);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
|
||||
const relation_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols);
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// sieve_relation
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class sieve_relation : public relation_base {
|
||||
friend class sieve_relation_plugin;
|
||||
friend class sieve_relation_plugin::join_fn;
|
||||
friend class sieve_relation_plugin::transformer_fn;
|
||||
friend class sieve_relation_plugin::union_fn;
|
||||
friend class sieve_relation_plugin::filter_fn;
|
||||
|
||||
svector<bool> m_inner_cols;
|
||||
|
||||
unsigned_vector m_sig2inner;
|
||||
unsigned_vector m_inner2sig;
|
||||
unsigned_vector m_ignored_cols; //in ascending order, so that it can be used in project-like functions
|
||||
|
||||
scoped_rel<relation_base> m_inner;
|
||||
|
||||
|
||||
sieve_relation(sieve_relation_plugin & p, const relation_signature & s,
|
||||
const bool * inner_columns, relation_base * inner);
|
||||
|
||||
public:
|
||||
sieve_relation_plugin & get_plugin() const {
|
||||
return static_cast<sieve_relation_plugin &>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
bool is_inner_col(unsigned idx) const { return m_sig2inner[idx]!=UINT_MAX; }
|
||||
unsigned get_inner_col(unsigned idx) const {
|
||||
SASSERT(is_inner_col(idx));
|
||||
return m_sig2inner[idx];
|
||||
}
|
||||
bool no_sieved_columns() const { return m_ignored_cols.size()==0; }
|
||||
bool no_inner_columns() const { return m_ignored_cols.size()==get_signature().size(); }
|
||||
|
||||
relation_base & get_inner() { return *m_inner; }
|
||||
const relation_base & get_inner() const { return *m_inner; }
|
||||
|
||||
virtual void add_fact(const relation_fact & f);
|
||||
virtual bool contains_fact(const relation_fact & f) const;
|
||||
virtual sieve_relation * clone() const;
|
||||
virtual relation_base * complement(func_decl*p) const;
|
||||
virtual void to_formula(expr_ref& fml) const;
|
||||
|
||||
virtual bool empty() const { return get_inner().empty(); }
|
||||
virtual void reset() { get_inner().reset(); }
|
||||
virtual unsigned get_size_estimate_rows() const { return get_inner().get_size_estimate_rows(); }
|
||||
virtual unsigned get_size_estimate_bytes() const { return get_inner().get_size_estimate_bytes(); }
|
||||
|
||||
virtual void display(std::ostream & out) const;
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_SIEVE_RELATION_H_ */
|
||||
|
||||
1413
src/muz/rel/dl_sparse_table.cpp
Normal file
1413
src/muz/rel/dl_sparse_table.cpp
Normal file
File diff suppressed because it is too large
Load diff
498
src/muz/rel/dl_sparse_table.h
Normal file
498
src/muz/rel/dl_sparse_table.h
Normal 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
773
src/muz/rel/dl_table.cpp
Normal file
|
|
@ -0,0 +1,773 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"dl_context.h"
|
||||
#include"dl_util.h"
|
||||
#include"dl_table.h"
|
||||
#include"dl_relation_manager.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// hashtable_table
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
table_base * hashtable_table_plugin::mk_empty(const table_signature & s) {
|
||||
SASSERT(can_handle_signature(s));
|
||||
return alloc(hashtable_table, *this, s);
|
||||
}
|
||||
|
||||
|
||||
class hashtable_table_plugin::join_fn : public convenient_table_join_fn {
|
||||
unsigned m_joined_col_cnt;
|
||||
public:
|
||||
join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2)
|
||||
: convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2),
|
||||
m_joined_col_cnt(col_cnt) {}
|
||||
|
||||
virtual table_base * operator()(const table_base & t1, const table_base & t2) {
|
||||
|
||||
const hashtable_table & ht1 = static_cast<const hashtable_table &>(t1);
|
||||
const hashtable_table & ht2 = static_cast<const hashtable_table &>(t2);
|
||||
|
||||
hashtable_table_plugin & plugin = ht1.get_plugin();
|
||||
|
||||
hashtable_table * res = static_cast<hashtable_table *>(plugin.mk_empty(get_result_signature()));
|
||||
|
||||
hashtable_table::storage::iterator els1it = ht1.m_data.begin();
|
||||
hashtable_table::storage::iterator els1end = ht1.m_data.end();
|
||||
hashtable_table::storage::iterator els2end = ht2.m_data.end();
|
||||
|
||||
table_fact acc;
|
||||
|
||||
for(; els1it!=els1end; ++els1it) {
|
||||
const table_fact & row1 = *els1it;
|
||||
|
||||
hashtable_table::storage::iterator els2it = ht2.m_data.begin();
|
||||
for(; els2it!=els2end; ++els2it) {
|
||||
const table_fact & row2 = *els2it;
|
||||
|
||||
bool match=true;
|
||||
for(unsigned i=0; i<m_joined_col_cnt; i++) {
|
||||
if(row1[m_cols1[i]]!=row2[m_cols2[i]]) {
|
||||
match=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
acc.reset();
|
||||
acc.append(row1);
|
||||
acc.append(row2);
|
||||
res->m_data.insert(acc);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
table_join_fn * hashtable_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind()) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2);
|
||||
}
|
||||
|
||||
|
||||
class hashtable_table::our_iterator_core : public iterator_core {
|
||||
const hashtable_table & m_parent;
|
||||
storage::iterator m_inner;
|
||||
storage::iterator m_end;
|
||||
|
||||
class our_row : public row_interface {
|
||||
const our_iterator_core & m_parent;
|
||||
public:
|
||||
our_row(const our_iterator_core & parent) : row_interface(parent.m_parent), m_parent(parent) {}
|
||||
|
||||
virtual void get_fact(table_fact & result) const {
|
||||
result = *m_parent.m_inner;
|
||||
}
|
||||
virtual table_element operator[](unsigned col) const {
|
||||
return (*m_parent.m_inner)[col];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
our_row m_row_obj;
|
||||
|
||||
public:
|
||||
our_iterator_core(const hashtable_table & t, bool finished) :
|
||||
m_parent(t), m_inner(finished ? t.m_data.end() : t.m_data.begin()),
|
||||
m_end(t.m_data.end()), m_row_obj(*this) {}
|
||||
|
||||
virtual bool is_finished() const {
|
||||
return m_inner==m_end;
|
||||
}
|
||||
|
||||
virtual row_interface & operator*() {
|
||||
SASSERT(!is_finished());
|
||||
return m_row_obj;
|
||||
}
|
||||
virtual void operator++() {
|
||||
SASSERT(!is_finished());
|
||||
++m_inner;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
table_base::iterator hashtable_table::begin() const {
|
||||
return mk_iterator(alloc(our_iterator_core, *this, false));
|
||||
}
|
||||
|
||||
table_base::iterator hashtable_table::end() const {
|
||||
return mk_iterator(alloc(our_iterator_core, *this, true));
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// bitvector_table
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
bool bitvector_table_plugin::can_handle_signature(const table_signature & sig) {
|
||||
if(sig.functional_columns()!=0) {
|
||||
return false;
|
||||
}
|
||||
unsigned cols = sig.size();
|
||||
unsigned shift = 0;
|
||||
for (unsigned i = 0; i < cols; ++i) {
|
||||
unsigned s = static_cast<unsigned>(sig[i]);
|
||||
if (s != sig[i] || !is_power_of_two(s)) {
|
||||
return false;
|
||||
}
|
||||
unsigned num_bits = 0;
|
||||
unsigned bit_pos = 1;
|
||||
for (num_bits = 1; num_bits < 32; ++num_bits) {
|
||||
if (bit_pos & s) {
|
||||
break;
|
||||
}
|
||||
bit_pos <<= 1;
|
||||
}
|
||||
shift += num_bits;
|
||||
if (shift >= 32) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
table_base * bitvector_table_plugin::mk_empty(const table_signature & s) {
|
||||
SASSERT(can_handle_signature(s));
|
||||
return alloc(bitvector_table, *this, s);
|
||||
}
|
||||
|
||||
class bitvector_table::bv_iterator : public iterator_core {
|
||||
|
||||
bitvector_table const& m_bv;
|
||||
unsigned m_offset;
|
||||
|
||||
class our_row : public caching_row_interface {
|
||||
const bv_iterator& m_parent;
|
||||
public:
|
||||
our_row(const bv_iterator & p) : caching_row_interface(p.m_bv), m_parent(p) {}
|
||||
virtual void get_fact(table_fact& result) const {
|
||||
if (result.size() < size()) {
|
||||
result.resize(size(), 0);
|
||||
}
|
||||
m_parent.m_bv.offset2fact(m_parent.m_offset, result);
|
||||
}
|
||||
};
|
||||
our_row m_row_obj;
|
||||
|
||||
public:
|
||||
bv_iterator(const bitvector_table& bv, bool end):
|
||||
m_bv(bv), m_offset(end?m_bv.m_bv.size():0), m_row_obj(*this)
|
||||
{
|
||||
if (!is_finished() && !m_bv.m_bv.get(m_offset)) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool is_finished() const {
|
||||
return m_offset == m_bv.m_bv.size();
|
||||
}
|
||||
|
||||
virtual row_interface & operator*() {
|
||||
SASSERT(!is_finished());
|
||||
return m_row_obj;
|
||||
}
|
||||
virtual void operator++() {
|
||||
SASSERT(!is_finished());
|
||||
++m_offset;
|
||||
while (!is_finished() && !m_bv.m_bv.get(m_offset)) {
|
||||
++m_offset;
|
||||
}
|
||||
m_row_obj.reset();
|
||||
}
|
||||
};
|
||||
|
||||
bitvector_table::bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig)
|
||||
: table_base(plugin, sig) {
|
||||
SASSERT(plugin.can_handle_signature(sig));
|
||||
|
||||
m_num_cols = sig.size();
|
||||
unsigned shift = 0;
|
||||
for (unsigned i = 0; i < m_num_cols; ++i) {
|
||||
unsigned s = static_cast<unsigned>(sig[i]);
|
||||
if (s != sig[i] || !is_power_of_two(s)) {
|
||||
throw default_exception("bit-vector table is specialized to small domains that are powers of two");
|
||||
}
|
||||
m_shift.push_back(shift);
|
||||
m_mask.push_back(s - 1);
|
||||
unsigned num_bits = 0;
|
||||
unsigned bit_pos = 1;
|
||||
for (num_bits = 1; num_bits < 32; ++num_bits) {
|
||||
if (bit_pos & s) {
|
||||
break;
|
||||
}
|
||||
bit_pos <<= 1;
|
||||
}
|
||||
shift += num_bits;
|
||||
if (shift >= 32) {
|
||||
throw default_exception("bit-vector table is specialized to small domains that are powers of two");
|
||||
}
|
||||
m_bv.reserve(1 << shift);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned bitvector_table::fact2offset(const table_element* f) const {
|
||||
unsigned result = 0;
|
||||
for (unsigned i = 0; i < m_num_cols; ++i) {
|
||||
SASSERT(f[i]<get_signature()[i]);
|
||||
result += ((unsigned)f[i]) << m_shift[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void bitvector_table::offset2fact(unsigned offset, table_fact& f) const {
|
||||
SASSERT(m_num_cols == f.size());
|
||||
for (unsigned i = 0; i < m_num_cols; ++i) {
|
||||
f[i] = m_mask[i] & (offset >> m_shift[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void bitvector_table::add_fact(const table_fact & f) {
|
||||
m_bv.set(fact2offset(f.c_ptr()));
|
||||
}
|
||||
|
||||
void bitvector_table::remove_fact(const table_element* fact) {
|
||||
m_bv.unset(fact2offset(fact));
|
||||
}
|
||||
|
||||
bool bitvector_table::contains_fact(const table_fact & f) const {
|
||||
return m_bv.get(fact2offset(f.c_ptr()));
|
||||
}
|
||||
|
||||
table_base::iterator bitvector_table::begin() const {
|
||||
return mk_iterator(alloc(bv_iterator, *this, false));
|
||||
}
|
||||
|
||||
table_base::iterator bitvector_table::end() const {
|
||||
return mk_iterator(alloc(bv_iterator, *this, true));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// equivalence_table
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
bool equivalence_table_plugin::can_handle_signature(const table_signature & sig) {
|
||||
return sig.functional_columns() == 0 && sig.size() == 2 && sig[0] < UINT_MAX && sig[0] == sig[1];
|
||||
}
|
||||
|
||||
bool equivalence_table_plugin::is_equivalence_table(table_base const& tbl) const {
|
||||
if (tbl.get_kind() != get_kind()) return false;
|
||||
equivalence_table const& t = static_cast<equivalence_table const&>(tbl);
|
||||
return !t.is_sparse();
|
||||
}
|
||||
|
||||
table_base * equivalence_table_plugin::mk_empty(const table_signature & s) {
|
||||
TRACE("dl", for (unsigned i = 0; i < s.size(); ++i) tout << s[i] << " "; tout << "\n";);
|
||||
SASSERT(can_handle_signature(s));
|
||||
return alloc(equivalence_table, *this, s);
|
||||
}
|
||||
|
||||
class equivalence_table_plugin::select_equal_and_project_fn : public table_transformer_fn {
|
||||
unsigned m_val;
|
||||
table_sort m_sort;
|
||||
public:
|
||||
select_equal_and_project_fn(const table_signature & sig, table_element val, unsigned col)
|
||||
: m_val(static_cast<unsigned>(val)),
|
||||
m_sort(sig[0]) {
|
||||
SASSERT(val <= UINT_MAX);
|
||||
SASSERT(col == 0 || col == 1);
|
||||
SASSERT(sig.functional_columns() == 0);
|
||||
SASSERT(sig.size() == 2);
|
||||
SASSERT(sig[0] < UINT_MAX && sig[0] == sig[1]);
|
||||
}
|
||||
|
||||
virtual table_base* operator()(const table_base& tb) {
|
||||
TRACE("dl", tout << "\n";);
|
||||
table_plugin & plugin = tb.get_plugin();
|
||||
table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse"));
|
||||
SASSERT(rp);
|
||||
table_signature sig;
|
||||
sig.push_back(m_sort);
|
||||
table_base* result = rp->mk_empty(sig);
|
||||
equivalence_table const& eq_table = static_cast<equivalence_table const&>(tb);
|
||||
if (eq_table.is_valid(m_val)) {
|
||||
table_fact fact;
|
||||
fact.resize(1);
|
||||
unsigned r = m_val;
|
||||
do {
|
||||
fact[0] = r;
|
||||
result->add_fact(fact);
|
||||
r = eq_table.m_uf.next(r);
|
||||
}
|
||||
while (r != m_val);
|
||||
}
|
||||
TRACE("dl", tb.display(tout << "src:\n"); result->display(tout << "result\n"););
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
table_transformer_fn * equivalence_table_plugin::mk_select_equal_and_project_fn(
|
||||
const table_base & t, const table_element & value, unsigned col) {
|
||||
return alloc(select_equal_and_project_fn, t.get_signature(), value, col);
|
||||
}
|
||||
|
||||
class equivalence_table_plugin::union_fn : public table_union_fn {
|
||||
|
||||
equivalence_table_plugin& m_plugin;
|
||||
|
||||
|
||||
void mk_union1(equivalence_table & tgt, const equivalence_table & src, table_base * delta) {
|
||||
unsigned num_vars = src.m_uf.get_num_vars();
|
||||
table_fact fact;
|
||||
fact.resize(2);
|
||||
for (unsigned i = 0; i < num_vars; ++i) {
|
||||
if (src.is_valid(i) && src.m_uf.find(i) == i) {
|
||||
fact[0] = i;
|
||||
equivalence_table::class_iterator it = src.class_begin(i);
|
||||
equivalence_table::class_iterator end = src.class_end(i);
|
||||
for (; it != end; ++it) {
|
||||
fact[1] = *it;
|
||||
if (!tgt.contains_fact(fact)) {
|
||||
tgt.add_fact(fact);
|
||||
if (delta) {
|
||||
delta->add_fact(fact);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mk_union2(equivalence_table & tgt, const table_base & src, table_base * delta) {
|
||||
table_fact fact;
|
||||
table_base::iterator it = src.begin(), end = src.end();
|
||||
for (; it != end; ++it) {
|
||||
it->get_fact(fact);
|
||||
if (!tgt.contains_fact(fact)) {
|
||||
tgt.add_fact(fact);
|
||||
if (delta) {
|
||||
delta->add_fact(fact);
|
||||
TRACE("dl",
|
||||
tout << "Add: ";
|
||||
for (unsigned i = 0; i < fact.size(); ++i) tout << fact[i] << " ";
|
||||
tout << "\n";);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
union_fn(equivalence_table_plugin& p) : m_plugin(p) {}
|
||||
|
||||
virtual void operator()(table_base & tgt0, const table_base & src, table_base * delta) {
|
||||
TRACE("dl", tout << "union\n";);
|
||||
equivalence_table & tgt = static_cast<equivalence_table &>(tgt0);
|
||||
if (m_plugin.is_equivalence_table(src)) {
|
||||
mk_union1(tgt, static_cast<equivalence_table const&>(src), delta);
|
||||
}
|
||||
else {
|
||||
mk_union2(tgt, src, delta);
|
||||
}
|
||||
TRACE("dl", src.display(tout << "src\n"); tgt.display(tout << "tgt\n");
|
||||
if (delta) delta->display(tout << "delta\n"););
|
||||
}
|
||||
};
|
||||
|
||||
table_union_fn * equivalence_table_plugin::mk_union_fn(
|
||||
const table_base & tgt, const table_base & src, const table_base * delta) {
|
||||
if (!is_equivalence_table(tgt) ||
|
||||
tgt.get_signature() != src.get_signature() ||
|
||||
(delta && delta->get_signature() != tgt.get_signature())) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn,*this);
|
||||
}
|
||||
|
||||
class equivalence_table_plugin::join_project_fn : public convenient_table_join_project_fn {
|
||||
equivalence_table_plugin& m_plugin;
|
||||
public:
|
||||
join_project_fn(
|
||||
equivalence_table_plugin& plugin, const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols)
|
||||
: convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols),
|
||||
m_plugin(plugin) {
|
||||
m_removed_cols.push_back(UINT_MAX);
|
||||
}
|
||||
|
||||
virtual table_base * operator()(const table_base & tb1, const table_base & tb2) {
|
||||
SASSERT(m_cols1.size() == 1);
|
||||
const table_signature & res_sign = get_result_signature();
|
||||
table_plugin * plugin = &tb1.get_plugin();
|
||||
if (!plugin->can_handle_signature(res_sign)) {
|
||||
plugin = &tb2.get_plugin();
|
||||
if (!plugin->can_handle_signature(res_sign)) {
|
||||
plugin = &tb1.get_manager().get_appropriate_plugin(res_sign);
|
||||
}
|
||||
}
|
||||
SASSERT(plugin->can_handle_signature(res_sign));
|
||||
table_base * result = plugin->mk_empty(res_sign);
|
||||
|
||||
if (m_plugin.is_equivalence_table(tb1)) {
|
||||
mk_join(0, m_cols1[0], static_cast<const equivalence_table&>(tb1),
|
||||
2, m_cols2[0], tb2, result);
|
||||
}
|
||||
else if (m_plugin.is_equivalence_table(tb2)) {
|
||||
mk_join(tb1.get_signature().size(), m_cols2[0], static_cast<const equivalence_table&>(tb2),
|
||||
0, m_cols1[0], tb1, result);
|
||||
}
|
||||
else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
TRACE("dl", tb1.display(tout << "tb1\n"); tb2.display(tout << "tb2\n"); result->display(tout << "result\n"););
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
table_base * mk_join(unsigned offs1, unsigned col1, equivalence_table const & t1,
|
||||
unsigned offs2, unsigned col2, table_base const& t2, table_base* res) {
|
||||
table_base::iterator els2it = t2.begin();
|
||||
table_base::iterator els2end = t2.end();
|
||||
|
||||
table_fact acc, proj;
|
||||
acc.resize(t1.get_signature().size() + t2.get_signature().size());
|
||||
|
||||
for(; els2it != els2end; ++els2it) {
|
||||
const table_base::row_interface & row2 = *els2it;
|
||||
table_element const& e2 = row2[col2];
|
||||
equivalence_table::class_iterator it = t1.class_begin(e2);
|
||||
equivalence_table::class_iterator end = t1.class_end(e2);
|
||||
if (it != end) {
|
||||
for (unsigned i = 0; i < row2.size(); ++i) {
|
||||
acc[i+offs2] = row2[i];
|
||||
}
|
||||
}
|
||||
for (; it != end; ++it) {
|
||||
acc[offs1+col1] = e2;
|
||||
acc[offs1+1-col1] = *it;
|
||||
mk_project(acc, proj);
|
||||
TRACE("dl", for (unsigned i = 0; i < proj.size(); ++i) tout << proj[i] << " "; tout << "\n";);
|
||||
res->add_fact(proj);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual void mk_project(table_fact const & f, table_fact & p) const {
|
||||
unsigned sz = f.size();
|
||||
p.reset();
|
||||
for (unsigned i = 0, r = 0; i < sz; ++i) {
|
||||
if (r < m_removed_cols.size() && m_removed_cols[r] == i) {
|
||||
++r;
|
||||
}
|
||||
else {
|
||||
p.push_back(f[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
table_join_fn * equivalence_table_plugin::mk_join_project_fn(
|
||||
const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols) {
|
||||
if (col_cnt != 1) {
|
||||
TRACE("dl", tout << "WARNING: join_project on multiple columns is not implemented\n";);
|
||||
return 0;
|
||||
}
|
||||
if (is_equivalence_table(t1) || is_equivalence_table(t2)) {
|
||||
return alloc(join_project_fn, *this, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2,
|
||||
removed_col_cnt, removed_cols);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class equivalence_table::eq_iterator : public iterator_core {
|
||||
|
||||
equivalence_table const& m_eq;
|
||||
unsigned m_last;
|
||||
unsigned m_current;
|
||||
unsigned m_next;
|
||||
|
||||
class our_row : public caching_row_interface {
|
||||
const eq_iterator& m_parent;
|
||||
public:
|
||||
our_row(const eq_iterator & p) : caching_row_interface(p.m_eq), m_parent(p) {}
|
||||
|
||||
virtual void get_fact(table_fact& result) const {
|
||||
if (result.size() < size()) {
|
||||
result.resize(size(), 0);
|
||||
}
|
||||
result[0] = m_parent.m_current;
|
||||
result[1] = m_parent.m_next;
|
||||
}
|
||||
|
||||
virtual table_element operator[](unsigned col) const {
|
||||
if (col == 0) return m_parent.m_current;
|
||||
if (col == 1) return m_parent.m_next;
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
our_row m_row_obj;
|
||||
|
||||
public:
|
||||
eq_iterator(const equivalence_table& eq, bool end):
|
||||
m_eq(eq),
|
||||
m_last(eq.m_uf.get_num_vars()),
|
||||
m_current(end?m_last:0),
|
||||
m_next(0),
|
||||
m_row_obj(*this)
|
||||
{
|
||||
while (m_current < m_last && !m_eq.is_valid(m_current)) {
|
||||
m_current++;
|
||||
m_next = m_current;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool is_finished() const {
|
||||
return m_current == m_last;
|
||||
}
|
||||
|
||||
virtual row_interface & operator*() {
|
||||
SASSERT(!is_finished());
|
||||
return m_row_obj;
|
||||
}
|
||||
|
||||
virtual void operator++() {
|
||||
SASSERT(!is_finished());
|
||||
m_next = m_eq.m_uf.next(m_next);
|
||||
if (m_next == m_current) {
|
||||
do {
|
||||
m_current++;
|
||||
m_next = m_current;
|
||||
}
|
||||
while (m_current < m_last && !m_eq.is_valid(m_current));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
equivalence_table::equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig)
|
||||
: table_base(plugin, sig), m_uf(m_ctx), m_sparse(0) {
|
||||
SASSERT(plugin.can_handle_signature(sig));
|
||||
}
|
||||
|
||||
equivalence_table::~equivalence_table() {
|
||||
if (is_sparse()) {
|
||||
m_sparse->deallocate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void equivalence_table::add_fact(const table_fact & f) {
|
||||
if (is_sparse()) {
|
||||
add_fact_sparse(f);
|
||||
}
|
||||
else {
|
||||
TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";);
|
||||
while (first(f) >= m_uf.get_num_vars()) m_uf.mk_var();
|
||||
while (second(f) >= m_uf.get_num_vars()) m_uf.mk_var();
|
||||
m_uf.merge(first(f), second(f));
|
||||
m_valid.reserve(m_uf.get_num_vars());
|
||||
m_valid.set(first(f));
|
||||
m_valid.set(second(f));
|
||||
}
|
||||
}
|
||||
|
||||
void equivalence_table::remove_fact(const table_element* fact) {
|
||||
mk_sparse();
|
||||
m_sparse->remove_fact(fact);
|
||||
}
|
||||
|
||||
void equivalence_table::mk_sparse() {
|
||||
if (m_sparse) return;
|
||||
|
||||
TRACE("dl",tout << "\n";);
|
||||
table_plugin & plugin = get_plugin();
|
||||
table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse"));
|
||||
SASSERT(rp);
|
||||
table_base* result = rp->mk_empty(get_signature());
|
||||
table_base::iterator it = begin(), e = end();
|
||||
table_fact fact;
|
||||
for (; it != e; ++it) {
|
||||
it->get_fact(fact);
|
||||
result->add_fact(fact);
|
||||
}
|
||||
m_sparse = result;
|
||||
}
|
||||
|
||||
void equivalence_table::add_fact_sparse(table_fact const& f) {
|
||||
table_base::iterator it = m_sparse->begin(), end = m_sparse->end();
|
||||
vector<table_fact> to_add;
|
||||
to_add.push_back(f);
|
||||
table_fact f1(f);
|
||||
|
||||
f1[0] = f[1];
|
||||
f1[1] = f[0];
|
||||
to_add.push_back(f1);
|
||||
|
||||
f1[0] = f[1];
|
||||
f1[1] = f[1];
|
||||
to_add.push_back(f1);
|
||||
|
||||
f1[0] = f[0];
|
||||
f1[1] = f[0];
|
||||
to_add.push_back(f1);
|
||||
|
||||
for (; it != end; ++it) {
|
||||
if ((*it)[0] == f[0]) {
|
||||
f1[0] = f[1];
|
||||
f1[1] = (*it)[1];
|
||||
to_add.push_back(f1);
|
||||
std::swap(f1[0],f1[1]);
|
||||
to_add.push_back(f1);
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < to_add.size(); ++i) {
|
||||
m_sparse->add_fact(to_add[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool equivalence_table::contains_fact(const table_fact & f) const {
|
||||
TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";);
|
||||
if (is_sparse()) {
|
||||
return m_sparse->contains_fact(f);
|
||||
}
|
||||
return
|
||||
is_valid(first(f)) &&
|
||||
is_valid(second(f)) &&
|
||||
m_uf.find(first(f)) == m_uf.find(second(f));
|
||||
}
|
||||
|
||||
table_base* equivalence_table::clone() const {
|
||||
if (is_sparse()) {
|
||||
return m_sparse->clone();
|
||||
}
|
||||
TRACE("dl",tout << "\n";);
|
||||
table_plugin & plugin = get_plugin();
|
||||
table_base* result = plugin.mk_empty(get_signature());
|
||||
table_fact fact;
|
||||
fact.resize(2);
|
||||
for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) {
|
||||
if (m_valid.get(i) && m_uf.find(i) == i) {
|
||||
unsigned n = m_uf.next(i);
|
||||
fact[0] = i;
|
||||
while (n != i) {
|
||||
fact[1] = n;
|
||||
result->add_fact(fact);
|
||||
n = m_uf.next(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
table_base::iterator equivalence_table::begin() const {
|
||||
if (is_sparse()) return m_sparse->begin();
|
||||
return mk_iterator(alloc(eq_iterator, *this, false));
|
||||
}
|
||||
|
||||
table_base::iterator equivalence_table::end() const {
|
||||
if (is_sparse()) return m_sparse->end();
|
||||
return mk_iterator(alloc(eq_iterator, *this, true));
|
||||
}
|
||||
|
||||
equivalence_table::class_iterator equivalence_table::class_begin(table_element const& _e) const {
|
||||
SASSERT(!is_sparse());
|
||||
unsigned e = static_cast<unsigned>(_e);
|
||||
return class_iterator(*this, e, !is_valid(e));
|
||||
}
|
||||
|
||||
equivalence_table::class_iterator equivalence_table::class_end(table_element const& _e) const {
|
||||
SASSERT(!is_sparse());
|
||||
unsigned e = static_cast<unsigned>(_e);
|
||||
return class_iterator(*this, e, true);
|
||||
}
|
||||
|
||||
void equivalence_table::display(std::ostream& out) const {
|
||||
if (is_sparse()) {
|
||||
m_sparse->display(out);
|
||||
return;
|
||||
}
|
||||
for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) {
|
||||
if (is_valid(i) && m_uf.find(i) == i) {
|
||||
unsigned j = i, last = i;
|
||||
do {
|
||||
out << "<" << i << " " << j << ">\n";
|
||||
j = m_uf.next(j);
|
||||
}
|
||||
while (last != j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned equivalence_table::get_size_estimate_rows() const {
|
||||
if (is_sparse()) return m_sparse->get_size_estimate_rows();
|
||||
return static_cast<unsigned>(get_signature()[0]);
|
||||
}
|
||||
|
||||
unsigned equivalence_table::get_size_estimate_bytes() const {
|
||||
if (is_sparse()) return m_sparse->get_size_estimate_bytes();
|
||||
return static_cast<unsigned>(get_signature()[0]);
|
||||
}
|
||||
|
||||
bool equivalence_table::knows_exact_size() const {
|
||||
return (!is_sparse() || m_sparse->knows_exact_size());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
265
src/muz/rel/dl_table.h
Normal file
265
src/muz/rel/dl_table.h
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_TABLE_H_
|
||||
#define _DL_TABLE_H_
|
||||
|
||||
#include<iostream>
|
||||
#include<list>
|
||||
#include<utility>
|
||||
|
||||
#include "ast.h"
|
||||
#include "bit_vector.h"
|
||||
#include "buffer.h"
|
||||
#include "hashtable.h"
|
||||
#include "map.h"
|
||||
#include "ref_vector.h"
|
||||
#include "vector.h"
|
||||
#include "union_find.h"
|
||||
#include "dl_base.h"
|
||||
#include "dl_util.h"
|
||||
#include "bit_vector.h"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class context;
|
||||
class variable_intersection;
|
||||
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// hashtable_table
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class hashtable_table;
|
||||
|
||||
class hashtable_table_plugin : public table_plugin {
|
||||
friend class hashtable_table;
|
||||
protected:
|
||||
class join_fn;
|
||||
public:
|
||||
typedef hashtable_table table;
|
||||
|
||||
hashtable_table_plugin(relation_manager & manager)
|
||||
: table_plugin(symbol("hashtable"), manager) {}
|
||||
|
||||
virtual table_base * mk_empty(const table_signature & s);
|
||||
|
||||
virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
};
|
||||
|
||||
class hashtable_table : public table_base {
|
||||
friend class hashtable_table_plugin;
|
||||
friend class hashtable_table_plugin::join_fn;
|
||||
|
||||
class our_iterator_core;
|
||||
|
||||
typedef hashtable<table_fact, svector_hash_proc<table_element_hash>,
|
||||
vector_eq_proc<table_fact> > storage;
|
||||
|
||||
storage m_data;
|
||||
|
||||
hashtable_table(hashtable_table_plugin & plugin, const table_signature & sig)
|
||||
: table_base(plugin, sig) {}
|
||||
public:
|
||||
hashtable_table_plugin & get_plugin() const
|
||||
{ return static_cast<hashtable_table_plugin &>(table_base::get_plugin()); }
|
||||
|
||||
virtual void add_fact(const table_fact & f) {
|
||||
m_data.insert(f);
|
||||
}
|
||||
virtual void remove_fact(const table_element* fact) {
|
||||
table_fact f(get_signature().size(), fact);
|
||||
m_data.remove(f);
|
||||
}
|
||||
virtual bool contains_fact(const table_fact & f) const {
|
||||
return m_data.contains(f);
|
||||
}
|
||||
|
||||
virtual iterator begin() const;
|
||||
virtual iterator end() const;
|
||||
|
||||
virtual unsigned get_size_estimate_rows() const { return m_data.size(); }
|
||||
virtual unsigned get_size_estimate_bytes() const { return m_data.size()*get_signature().size()*8; }
|
||||
virtual bool knows_exact_size() const { return true; }
|
||||
};
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// bitvector_table
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class bitvector_table;
|
||||
|
||||
class bitvector_table_plugin : public table_plugin {
|
||||
public:
|
||||
typedef bitvector_table table;
|
||||
|
||||
bitvector_table_plugin(relation_manager & manager)
|
||||
: table_plugin(symbol("bitvector"), manager) {}
|
||||
|
||||
virtual bool can_handle_signature(const table_signature & s);
|
||||
|
||||
virtual table_base * mk_empty(const table_signature & s);
|
||||
};
|
||||
|
||||
class bitvector_table : public table_base {
|
||||
friend class bitvector_table_plugin;
|
||||
|
||||
class bv_iterator;
|
||||
bit_vector m_bv;
|
||||
unsigned m_num_cols;
|
||||
unsigned_vector m_shift;
|
||||
unsigned_vector m_mask;
|
||||
|
||||
unsigned fact2offset(const table_element* f) const;
|
||||
void offset2fact(unsigned offset, table_fact& f) const;
|
||||
|
||||
bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig);
|
||||
public:
|
||||
virtual void add_fact(const table_fact & f);
|
||||
virtual void remove_fact(const table_element* fact);
|
||||
virtual bool contains_fact(const table_fact & f) const;
|
||||
virtual iterator begin() const;
|
||||
virtual iterator end() const;
|
||||
};
|
||||
|
||||
// -------------------------------------------
|
||||
// Equivalence table.
|
||||
// Really: partial equivalence relation table.
|
||||
// -------------------------------------------
|
||||
|
||||
class equivalence_table;
|
||||
|
||||
class equivalence_table_plugin : public table_plugin {
|
||||
class union_fn;
|
||||
class select_equal_and_project_fn;
|
||||
class join_project_fn;
|
||||
|
||||
bool is_equivalence_table(table_base const& tbl) const;
|
||||
|
||||
public:
|
||||
typedef equivalence_table table;
|
||||
|
||||
equivalence_table_plugin(relation_manager & manager)
|
||||
: table_plugin(symbol("equivalence"), manager) {}
|
||||
|
||||
virtual bool can_handle_signature(const table_signature & s);
|
||||
|
||||
virtual table_base * mk_empty(const table_signature & s);
|
||||
|
||||
protected:
|
||||
virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src,
|
||||
const table_base * delta);
|
||||
virtual table_transformer_fn * mk_select_equal_and_project_fn(
|
||||
const table_base & t,
|
||||
const table_element & value, unsigned col);
|
||||
virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
|
||||
|
||||
#if 0
|
||||
virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
const table_element & value, unsigned col);
|
||||
virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t,
|
||||
const table_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols);
|
||||
#endif
|
||||
};
|
||||
|
||||
class equivalence_table : public table_base {
|
||||
friend class equivalence_table_plugin;
|
||||
|
||||
class eq_iterator;
|
||||
union_find_default_ctx m_ctx;
|
||||
bit_vector m_valid;
|
||||
union_find<> m_uf;
|
||||
table_base* m_sparse;
|
||||
|
||||
equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig);
|
||||
virtual ~equivalence_table();
|
||||
|
||||
unsigned first(table_fact const& f) const { return static_cast<unsigned>(f[0]); }
|
||||
unsigned second(table_fact const& f) const { return static_cast<unsigned>(f[1]); }
|
||||
|
||||
bool is_valid(unsigned entry) const { return entry < m_valid.size() && m_valid.get(entry); }
|
||||
bool is_sparse() const { return m_sparse != 0; }
|
||||
|
||||
// iterator over equivalence class of 'n'.
|
||||
class class_iterator {
|
||||
equivalence_table const& m_parent;
|
||||
unsigned m_current;
|
||||
unsigned m_last;
|
||||
bool m_end;
|
||||
public:
|
||||
class_iterator(equivalence_table const& s, unsigned n, bool end):
|
||||
m_parent(s), m_current(n), m_last(n), m_end(end) {}
|
||||
|
||||
unsigned operator*() { return m_current; }
|
||||
|
||||
class_iterator& operator++() {
|
||||
m_current = m_parent.m_uf.next(m_current);
|
||||
m_end = (m_current == m_last);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const class_iterator & it) const {
|
||||
return
|
||||
(m_end && it.m_end) ||
|
||||
(!m_end && !it.m_end && m_current == it.m_current);
|
||||
}
|
||||
bool operator!=(const class_iterator & it) const { return !operator==(it); }
|
||||
|
||||
};
|
||||
class_iterator class_begin(table_element const& e) const;
|
||||
class_iterator class_end(table_element const& e) const;
|
||||
|
||||
void add_fact_sparse(table_fact const& f);
|
||||
void mk_sparse();
|
||||
|
||||
|
||||
public:
|
||||
virtual void add_fact(const table_fact & f);
|
||||
virtual void remove_fact(const table_element* fact);
|
||||
virtual bool contains_fact(const table_fact & f) const;
|
||||
virtual table_base* clone() const;
|
||||
virtual iterator begin() const;
|
||||
virtual iterator end() const;
|
||||
virtual unsigned get_size_estimate_rows() const;
|
||||
virtual unsigned get_size_estimate_bytes() const;
|
||||
virtual bool knows_exact_size() const;
|
||||
virtual void display(std::ostream & out) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_TABLE_H_ */
|
||||
|
||||
193
src/muz/rel/dl_table_plugin.h
Normal file
193
src/muz/rel/dl_table_plugin.h
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table_plugin.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-23.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_TABLE_PLUGIN_H_
|
||||
#define _DL_TABLE_PLUGIN_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"map.h"
|
||||
#include"vector.h"
|
||||
|
||||
#include"dl_table_ops.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
/**
|
||||
Termplate class containing common infrastructure for relations and tables
|
||||
*/
|
||||
template<class Traits>
|
||||
struct tr_infrastructure {
|
||||
|
||||
typedef typename Traits::base_object base_object;
|
||||
typedef typename Traits::signature signature;
|
||||
typedef typename Traits::element element;
|
||||
typedef typename Traits::fact fact;
|
||||
typedef typename Traits::kind kind;
|
||||
|
||||
class base_fn {
|
||||
public:
|
||||
virtual ~base_fn() {}
|
||||
};
|
||||
|
||||
class join_fn : public base_fn {
|
||||
public:
|
||||
virtual base_object * operator()(const base_object & t1, const base_object & t2);
|
||||
};
|
||||
|
||||
class transformer_fn : public base_fn {
|
||||
public:
|
||||
virtual base_object * operator()(const base_object & t);
|
||||
};
|
||||
|
||||
class union_fn : public base_fn {
|
||||
public:
|
||||
virtual void operator()(base_object & tgt, const base_object & src, base_object * delta);
|
||||
};
|
||||
|
||||
class mutator_fn : public base_fn {
|
||||
public:
|
||||
virtual void operator()(base_object & t);
|
||||
};
|
||||
|
||||
class negation_filter_fn : public base_fn {
|
||||
public:
|
||||
virtual void operator()(base_object & t, const base_object & negated_obj);
|
||||
};
|
||||
|
||||
class plugin_object {
|
||||
const kind m_kind;
|
||||
protected:
|
||||
plugin_object(kind k) : m_kind(k) {}
|
||||
public:
|
||||
kind get_kind();
|
||||
|
||||
virtual base_object * mk_empty(const signature & s) = 0;
|
||||
|
||||
virtual join_fn * mk_join_fn(const table_base & t1, const table_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
}
|
||||
|
||||
virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols) = 0
|
||||
|
||||
virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle) = 0;
|
||||
|
||||
virtual union_fn * mk_union_fn(base_object & tgt, const base_object & src, base_object * delta) = 0;
|
||||
|
||||
virtual mutator_fn * mk_filter_identical_fn(base_object & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols) = 0;
|
||||
|
||||
virtual mutator_fn * mk_filter_equal_fn(base_object & t, const element & value,
|
||||
unsigned col) = 0;
|
||||
|
||||
virtual mutator_fn * mk_filter_interpreted_fn(base_object & t, app * condition) = 0;
|
||||
|
||||
virtual negation_filter_fn * mk_filter_interpreted_fn(base_object & t,
|
||||
const base_object & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols) = 0;
|
||||
|
||||
};
|
||||
|
||||
class base_ancestor {
|
||||
const kind m_kind;
|
||||
protected:
|
||||
relation_manager & m_manager;
|
||||
signature m_signature;
|
||||
|
||||
base_ancestor(kind k, relation_manager & m, const signature & s)
|
||||
: m_kind(k), m_manager(m), m_signature(s) {}
|
||||
public:
|
||||
virtual ~base_ancestor() {}
|
||||
|
||||
kind get_kind() const { return m_kind; }
|
||||
relation_manager & get_manager() const { return m_manager; }
|
||||
const signature & get_signature() const { return m_signature; }
|
||||
|
||||
virtual bool empty() const = 0;
|
||||
virtual void add_fact(const fact & f) = 0;
|
||||
virtual bool contains_fact(const fact & f) const = 0;
|
||||
|
||||
/**
|
||||
\brief Return table that contains the same data as the current one.
|
||||
*/
|
||||
virtual base_object * clone() const;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// relation_base
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class relation_base1;
|
||||
|
||||
enum relation_kind {
|
||||
RK_UNKNOWN,
|
||||
RK_TABLE
|
||||
};
|
||||
|
||||
struct relation_traits {
|
||||
typedef relation_base1 base_object;
|
||||
typedef relation_signature signature;
|
||||
typedef app * element;
|
||||
typedef ptr_vector<app> fact;
|
||||
typedef relation_kind kind;
|
||||
};
|
||||
|
||||
typedef tr_infrastructure<relation_traits> relation_infrastructure;
|
||||
|
||||
typedef relation_infrastructure::plugin_object relation_plugin_base;
|
||||
|
||||
class relation_base1 : public relation_infrastructure::base_ancestor {
|
||||
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// table_base
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class table_base1;
|
||||
|
||||
struct table_traits {
|
||||
typedef table_base1 base_object;
|
||||
typedef table_signature signature;
|
||||
typedef unsigned element;
|
||||
typedef unsigned_vector fact;
|
||||
typedef table_kind kind;
|
||||
};
|
||||
|
||||
typedef tr_infrastructure<table_traits> table_infrastructure;
|
||||
|
||||
typedef table_infrastructure::plugin_object table_plugin_base;
|
||||
|
||||
class table_base1 : public table_infrastructure::base_ancestor {
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_TABLE_PLUGIN_H_ */
|
||||
|
||||
490
src/muz/rel/dl_table_relation.cpp
Normal file
490
src/muz/rel/dl_table_relation.cpp
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table_relation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include<string>
|
||||
#include"dl_context.h"
|
||||
#include"dl_relation_manager.h"
|
||||
#include"dl_table_relation.h"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// table_relation_plugin
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
symbol table_relation_plugin::create_plugin_name(const table_plugin &p) {
|
||||
std::string name = std::string("tr_") + p.get_name().bare_str();
|
||||
return symbol(name.c_str());
|
||||
}
|
||||
|
||||
bool table_relation_plugin::can_handle_signature(const relation_signature & s) {
|
||||
table_signature tsig;
|
||||
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";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
133
src/muz/rel/dl_table_relation.h
Normal file
133
src/muz/rel/dl_table_relation.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_table_relation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-24.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_TABLE_RELATION_H_
|
||||
#define _DL_TABLE_RELATION_H_
|
||||
|
||||
|
||||
#include "dl_base.h"
|
||||
#include "dl_util.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class table_relation;
|
||||
|
||||
class table_relation_plugin : public relation_plugin {
|
||||
friend class table_relation;
|
||||
|
||||
class tr_join_project_fn;
|
||||
class tr_transformer_fn;
|
||||
class universal_target_union_fn;
|
||||
class tr_union_fn;
|
||||
class tr_mutator_fn;
|
||||
class tr_intersection_filter_fn;
|
||||
|
||||
table_plugin & m_table_plugin;
|
||||
|
||||
static symbol create_plugin_name(const table_plugin & p);
|
||||
public:
|
||||
table_relation_plugin(table_plugin & tp, relation_manager & manager)
|
||||
: relation_plugin(create_plugin_name(tp), manager, ST_TABLE_RELATION), m_table_plugin(tp) {}
|
||||
|
||||
table_plugin & get_table_plugin() { return m_table_plugin; }
|
||||
|
||||
virtual bool can_handle_signature(const relation_signature & s);
|
||||
|
||||
virtual relation_base * mk_empty(const relation_signature & s);
|
||||
virtual relation_base * mk_full(const relation_signature & s, func_decl* p, family_id kind);
|
||||
relation_base * mk_from_table(const relation_signature & s, table_base * t);
|
||||
|
||||
protected:
|
||||
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
|
||||
virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2,
|
||||
unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * removed_cols);
|
||||
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
|
||||
const unsigned * permutation_cycle);
|
||||
virtual relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t,
|
||||
const unsigned * permutation);
|
||||
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta);
|
||||
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
|
||||
const unsigned * identical_cols);
|
||||
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
|
||||
unsigned col);
|
||||
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
|
||||
virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t,
|
||||
app * condition, unsigned removed_col_cnt, const unsigned * removed_cols);
|
||||
virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t,
|
||||
const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols);
|
||||
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
|
||||
const relation_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols);
|
||||
virtual relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t,
|
||||
const relation_element & value, unsigned col);
|
||||
};
|
||||
|
||||
class table_relation : public relation_base {
|
||||
friend class table_relation_plugin;
|
||||
friend class table_relation_plugin::tr_join_project_fn;
|
||||
friend class table_relation_plugin::tr_transformer_fn;
|
||||
|
||||
scoped_rel<table_base> m_table;
|
||||
|
||||
/**
|
||||
\brief Create a \c table_relation object.
|
||||
|
||||
The newly created object takes ownership of the \c table object.
|
||||
*/
|
||||
table_relation(table_relation_plugin & p, const relation_signature & s, table_base * table)
|
||||
: relation_base(p, s), m_table(table) {
|
||||
SASSERT(s.size()==table->get_signature().size());
|
||||
}
|
||||
public:
|
||||
|
||||
table_relation_plugin & get_plugin() const {
|
||||
return static_cast<table_relation_plugin &>(relation_base::get_plugin());
|
||||
}
|
||||
|
||||
table_base & get_table() { return *m_table; }
|
||||
const table_base & get_table() const { return *m_table; }
|
||||
|
||||
virtual bool empty() const { return m_table->empty(); }
|
||||
|
||||
void add_table_fact(const table_fact & f);
|
||||
|
||||
virtual void add_fact(const relation_fact & f);
|
||||
virtual bool contains_fact(const relation_fact & f) const;
|
||||
virtual relation_base * clone() const;
|
||||
virtual relation_base * complement(func_decl* p) const;
|
||||
virtual void to_formula(expr_ref& fml) const { get_table().to_formula(get_signature(), fml); }
|
||||
|
||||
virtual void display(std::ostream & out) const {
|
||||
get_table().display(out);
|
||||
}
|
||||
virtual void display_tuples(func_decl & pred, std::ostream & out) const;
|
||||
|
||||
virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); }
|
||||
virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); }
|
||||
virtual bool knows_exact_size() const { return m_table->knows_exact_size(); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_TABLE_RELATION_H_ */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue