3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-13 22:41:15 +00:00

re-organization of muz

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2013-08-28 22:11:33 -07:00
parent 9e61820125
commit e4338f085b
37 changed files with 6 additions and 875 deletions

View file

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

View file

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

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

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,562 @@
/*++
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"
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 { return m_params.generate_proof_trace(); }
bool output_profile() const { return m_params.output_profile(); }
bool fix_unbound_vars() const { return m_params.fix_unbound_vars(); }
symbol default_table() const { return m_params.default_table(); }
symbol default_relation() const { return m_params.default_relation(); } // external_relation_plugin::get_name());
symbol default_table_checker() const { return m_params.default_table_checker(); }
bool default_table_checked() const { return m_params.default_table_checked(); }
bool dbg_fpr_nonempty_relation_signature() const { return m_params.dbg_fpr_nonempty_relation_signature(); }
unsigned dl_profile_milliseconds_threshold() const { return m_params.profile_timeout_milliseconds(); }
bool all_or_nothing_deltas() const { return m_params.all_or_nothing_deltas(); }
bool compile_with_widening() const { return m_params.compile_with_widening(); }
bool unbound_compressor() const { return m_params.unbound_compressor(); }
bool similarity_compressor() const { return m_params.similarity_compressor(); }
unsigned similarity_compressor_threshold() const { return m_params.similarity_compressor_threshold(); }
unsigned soft_timeout() const { return m_fparams.m_soft_timeout; }
unsigned initial_restart_timeout() const { return m_params.initial_restart_timeout(); }
bool generate_explanations() const { return m_params.generate_explanations(); }
bool explanations_on_relation_level() const { return m_params.explanations_on_relation_level(); }
bool magic_sets_for_queries() const { return m_params.magic_sets_for_queries(); }
bool eager_emptiness_checking() const { return m_params.eager_emptiness_checking(); }
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 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' - this option is only supported for PDR mode.
*/
model_ref get_model();
/**
\brief retrieve proof from derivation of the query.
\pre engine == 'pdr' - this option is only supported for PDR mode.
*/
proof_ref get_proof();
/**
Query multiple output relations.
*/
lbool rel_query(unsigned num_rels, func_decl * const* rels);
/**
\brief retrieve last proof status.
*/
execution_result get_status();
void set_status(execution_result r) { m_last_status = r; }
/**
\brief retrieve formula corresponding to query that returns l_true.
The formula describes one or more instances of the existential variables
in the query that are derivable.
*/
expr* get_answer_as_formula();
void collect_statistics(statistics& st) const;
void reset_statistics();
/**
\brief Display a certificate for reachability and/or unreachability.
*/
void display_certificate(std::ostream& out);
/**
\brief query result if it contains fact.
*/
bool result_contains_fact(relation_fact const& f);
rel_context_base* get_rel_context() { ensure_engine(); return m_rel; }
private:
/**
Just reset all tables.
*/
void reset_tables();
void flush_add_rules();
void ensure_engine();
void check_quantifier_free(rule_ref& r);
void check_uninterpreted_free(rule_ref& r);
void check_existential_tail(rule_ref& r);
void check_positive_predicates(rule_ref& r);
// auxilary functions for SMT2 pretty-printer.
void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out);
//undefined and private copy constructor and operator=
context(const context&);
context& operator=(const context&);
};
};
#endif /* _DL_CONTEXT_H_ */

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

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

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

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

View file

@ -0,0 +1,82 @@
/*++
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
};
class engine_base {
ast_manager& m;
std::string m_name;
public:
engine_base(ast_manager& m, char const* name): m(m), m_name(name) {}
virtual ~engine_base() {}
virtual expr_ref get_answer() = 0;
virtual lbool query(expr* q) = 0;
virtual lbool query(unsigned num_rels, func_decl*const* rels) { return l_undef; }
virtual void reset_statistics() {}
virtual void display_profile(std::ostream& out) const {}
virtual void collect_statistics(statistics& st) const {}
virtual unsigned get_num_levels(func_decl* pred) {
throw default_exception(std::string("get_num_levels is not supported for ") + m_name);
}
virtual expr_ref get_cover_delta(int level, func_decl* pred) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void add_cover(int level, func_decl* pred, expr* property) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void display_certificate(std::ostream& out) const {
throw default_exception(std::string("certificates are not supported for ") + m_name);
}
virtual model_ref get_model() {
return model_ref(alloc(model, m));
}
virtual proof_ref get_proof() {
return proof_ref(m.mk_asserted(m.mk_true()), m);
}
virtual void updt_params() {}
virtual void cancel() {}
virtual void cleanup() {}
};
class context;
class register_engine_base {
public:
virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0;
virtual void set_context(context* ctx) = 0;
};
}
#endif

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

File diff suppressed because it is too large Load diff

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

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

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

@ -0,0 +1,646 @@
/*++
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"
namespace datalog {
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();
}
};

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

@ -0,0 +1,751 @@
/*++
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"fixedpoint_params.hpp"
#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;
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);
}
template<typename T>
class scoped_rel {
T* m_t;
public:
scoped_rel(T* t) : m_t(t) {}
~scoped_rel() { if (m_t) { universal_delete(m_t); } }
scoped_rel() : m_t(0) {}
scoped_rel& operator=(T* t) { if (m_t) { universal_delete(m_t); } m_t = t; return *this; }
T* operator->() { return m_t; }
const T* operator->() const { return m_t; }
T& operator*() { return *m_t; }
const T& operator*() const { return *m_t; }
operator bool() const { return m_t!=0; }
T* get() { return m_t; }
/**
\brief Remove object from \c scoped_rel without deleting it.
*/
T* release() {
T* res = m_t;
m_t = 0;
return res;
}
};
/**
\brief If it is possible to convert the beginning of \c s to uint64,
store the result of conversion and return true; otherwise return false.
*/
bool string_to_uint64(const char * s, uint64 & res);
std::string to_string(uint64 num);
/**
\brief Read the sequence of decimal digits starting at \c s and interpret it as
uint64. If successful, \c res will contain the read number and \c s will point
to the first non-digit character, and true is returned. If the first character
is not a digit, no parameter is modified and false is returned. If the uint64
overflows, \c points to the character which caused the overflow and false is
returned.
*/
bool read_uint64(const char * & s, uint64 & res);
};
#endif /* _DL_UTIL_H_ */

View file

@ -0,0 +1,74 @@
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'),
('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'),
))

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

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

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

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

View file

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

View file

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