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:
parent
9e61820125
commit
e4338f085b
37 changed files with 6 additions and 875 deletions
297
src/muz/base/dl_boogie_proof.cpp
Normal file
297
src/muz/base/dl_boogie_proof.cpp
Normal file
|
@ -0,0 +1,297 @@
|
|||
/**
|
||||
|
||||
Example from Boogie:
|
||||
|
||||
(derivation
|
||||
(step s!4 (main_loop_LoopHead true true)
|
||||
rule!3 (subst
|
||||
(= assertsPassed@@1 true)
|
||||
(= assertsPassed@2@@0 true)
|
||||
(= main_loop_LoopHead_assertsPassed true)
|
||||
)
|
||||
(labels @950 +895 +668 +670 +893 +899 )
|
||||
(ref true ))
|
||||
(step s!3 (main true false)
|
||||
rule!1 (subst
|
||||
(= assertsPassed true)
|
||||
(= assertsPassed@0 true)
|
||||
(= assertsPassed@2 false)
|
||||
(= main_assertsPassed false)
|
||||
)
|
||||
(labels @839 +763 +547 +546 +761 +544 +545 +si_fcall_805 +681 +768 )
|
||||
(ref s!4 ))
|
||||
(step s!2 (main_SeqInstr true false)
|
||||
rule!2 (subst
|
||||
(= assertsPassed@@0 true)
|
||||
(= assertsPassed@0@@0 false)
|
||||
(= main_SeqInstr_assertsPassed false)
|
||||
)
|
||||
(labels @890 +851 +572 +si_fcall_883 +853 )
|
||||
(ref s!3 ))
|
||||
(step s!1 (@Fail!0)
|
||||
rule!4 (subst
|
||||
(= assertsPassed@@2 true)
|
||||
(= main_SeqInstr_assertsPassed@@0 false)
|
||||
)
|
||||
(labels )
|
||||
(ref s!2 ))
|
||||
)
|
||||
(model
|
||||
"tickleBool -> {
|
||||
true -> true
|
||||
false -> true
|
||||
else -> true
|
||||
}
|
||||
")
|
||||
*/
|
||||
|
||||
#include "dl_boogie_proof.h"
|
||||
#include "model_pp.h"
|
||||
#include "proof_utils.h"
|
||||
#include "ast_pp.h"
|
||||
#include "qe_util.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
/**
|
||||
\brief push hyper-resolution steps upwards such that every use of
|
||||
hyper-resolution uses a premise that is not derived from hyper-resolution.
|
||||
|
||||
perform the following rewrite:
|
||||
|
||||
hr(hr(p1,p2,p3,..),p4,p5) => hr(p1,hr(p2,p4),p3,..,p5)
|
||||
|
||||
*/
|
||||
|
||||
void mk_input_resolution(proof_ref& pr) {
|
||||
ast_manager& m = pr.get_manager();
|
||||
proof_ref pr1(m);
|
||||
proof_ref_vector premises1(m), premises2(m), premises(m);
|
||||
expr_ref conclusion1(m), conclusion2(m), conclusion(m);
|
||||
svector<std::pair<unsigned, unsigned> > positions1, positions2, positions;
|
||||
vector<expr_ref_vector> substs1, substs2, substs;
|
||||
|
||||
if (m.is_hyper_resolve(pr, premises1, conclusion1, positions1, substs1) &&
|
||||
m.is_hyper_resolve(premises1[0].get(), premises, conclusion2, positions, substs2)) {
|
||||
for (unsigned i = 1; i < premises1.size(); ++i) {
|
||||
pr1 = premises1[i].get();
|
||||
mk_input_resolution(pr1);
|
||||
premises1[i] = pr1;
|
||||
}
|
||||
for (unsigned i = 0; i < premises.size(); ++i) {
|
||||
pr1 = premises[i].get();
|
||||
mk_input_resolution(pr1);
|
||||
premises[i] = pr1;
|
||||
}
|
||||
unsigned sz = premises.size();
|
||||
for (unsigned i = 1; i < sz; ++i) {
|
||||
proof* premise = premises[i].get();
|
||||
expr_ref_vector literals(m);
|
||||
expr* l1, *l2;
|
||||
if (!m.is_implies(premise, l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
qe::flatten_and(l1, literals);
|
||||
positions2.reset();
|
||||
premises2.reset();
|
||||
premises2.push_back(premise);
|
||||
substs2.reset();
|
||||
for (unsigned j = 0; j < literals.size(); ++j) {
|
||||
expr* lit = literals[j].get();
|
||||
for (unsigned k = 1; k < premises1.size(); ++k) {
|
||||
if (m.get_fact(premises1[k].get()) == lit) {
|
||||
premises2.push_back(premises1[k].get());
|
||||
positions2.push_back(std::make_pair(j+1,0));
|
||||
substs2.push_back(expr_ref_vector(m));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
premises[i] = m.mk_hyper_resolve(premises2.size(), premises2.c_ptr(), l2, positions2, substs2);
|
||||
}
|
||||
conclusion = conclusion1;
|
||||
pr = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs);
|
||||
}
|
||||
}
|
||||
|
||||
void boogie_proof::set_proof(proof* p) {
|
||||
std::cout << "set proof\n";
|
||||
m_proof = p;
|
||||
proof_utils::push_instantiations_up(m_proof);
|
||||
mk_input_resolution(m_proof);
|
||||
std::cout << "proof set\n";
|
||||
}
|
||||
|
||||
void boogie_proof::set_model(model* m) {
|
||||
m_model = m;
|
||||
}
|
||||
|
||||
void boogie_proof::pp(std::ostream& out) {
|
||||
if (m_proof) {
|
||||
pp_proof(out);
|
||||
}
|
||||
if (m_model) {
|
||||
model_pp(out, *m_model);
|
||||
}
|
||||
}
|
||||
|
||||
void boogie_proof::pp_proof(std::ostream& out) {
|
||||
vector<step> steps;
|
||||
ptr_vector<proof> rules;
|
||||
rules.push_back(m_proof);
|
||||
steps.push_back(step());
|
||||
obj_map<proof, unsigned> index;
|
||||
index.insert(m_proof, 0);
|
||||
|
||||
for (unsigned j = 0; j < rules.size(); ++j) {
|
||||
proof* p = rules[j];
|
||||
proof_ref_vector premises(m);
|
||||
expr_ref conclusion(m);
|
||||
svector<std::pair<unsigned, unsigned> > positions;
|
||||
vector<expr_ref_vector> substs;
|
||||
|
||||
expr* tmp;
|
||||
steps[j].m_fact = m.get_fact(p);
|
||||
m.is_implies(steps[j].m_fact, tmp, steps[j].m_fact);
|
||||
get_subst(p, steps[j].m_subst);
|
||||
get_labels(p, steps[j].m_labels);
|
||||
|
||||
if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) {
|
||||
for (unsigned i = 1; i < premises.size(); ++i) {
|
||||
proof* premise = premises[i].get();
|
||||
unsigned position = 0;
|
||||
if (!index.find(premise, position)) {
|
||||
position = rules.size();
|
||||
rules.push_back(premise);
|
||||
steps.push_back(step());
|
||||
index.insert(premise, position);
|
||||
}
|
||||
steps[j].m_refs.push_back(position);
|
||||
}
|
||||
get_rule_name(premises[0].get(), steps[j].m_rule_name);
|
||||
}
|
||||
}
|
||||
for (unsigned j = steps.size(); j > 0; ) {
|
||||
--j;
|
||||
step &s = steps[j];
|
||||
|
||||
// TBD
|
||||
// s.m_labels;
|
||||
|
||||
// set references, compensate for reverse ordering.
|
||||
for (unsigned i = 0; i < s.m_refs.size(); ++i) {
|
||||
s.m_refs[i] = rules.size()-1-s.m_refs[i];
|
||||
}
|
||||
}
|
||||
steps.reverse();
|
||||
pp_steps(out, steps);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief extract the instantiation by searching for the first occurrence of a hyper-resolution
|
||||
rule that produces an instance.
|
||||
*/
|
||||
void boogie_proof::get_subst(proof* p, subst& s) {
|
||||
ptr_vector<proof> todo;
|
||||
todo.push_back(p);
|
||||
ast_mark visited;
|
||||
std::cout << "get_subst\n" << mk_pp(p, m) << "\n";
|
||||
while (!todo.empty()) {
|
||||
proof* p = todo.back();
|
||||
todo.pop_back();
|
||||
if (visited.is_marked(p)) {
|
||||
continue;
|
||||
}
|
||||
visited.mark(p, true);
|
||||
proof_ref_vector premises(m);
|
||||
expr_ref conclusion(m);
|
||||
svector<std::pair<unsigned, unsigned> > positions;
|
||||
vector<expr_ref_vector> substs;
|
||||
if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) {
|
||||
expr_ref_vector const& sub = substs[0];
|
||||
if (!sub.empty()) {
|
||||
quantifier* q = to_quantifier(m.get_fact(premises[0].get()));
|
||||
unsigned sz = sub.size();
|
||||
SASSERT(sz == q->get_num_decls());
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
s.push_back(std::make_pair(q->get_decl_name(sz-1-i), sub[i]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
unsigned sz = m.get_num_parents(p);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
todo.push_back(m.get_parent(p, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void boogie_proof::get_rule_name(proof* p, symbol& n) {
|
||||
|
||||
}
|
||||
|
||||
void boogie_proof::get_labels(proof* p, labels& lbls) {
|
||||
|
||||
}
|
||||
|
||||
void boogie_proof::pp_steps(std::ostream& out, vector<step>& steps) {
|
||||
out << "(derivation\n";
|
||||
for (unsigned i = 0; i < steps.size(); ++i) {
|
||||
pp_step(out, i, steps[i]);
|
||||
}
|
||||
out << ")\n";
|
||||
|
||||
}
|
||||
|
||||
// step :: "(" "step" step-name fact rule-name subst labels premises ")"
|
||||
void boogie_proof::pp_step(std::ostream& out, unsigned id, step& s) {
|
||||
out << "(step\n";
|
||||
out << " s!" << id << " ";
|
||||
pp_fact(out, s.m_fact);
|
||||
out << " " << s.m_rule_name << "\n";
|
||||
pp_subst(out << " ", s.m_subst);
|
||||
pp_labels(out << " ", s.m_labels);
|
||||
pp_premises(out << " ", s.m_refs);
|
||||
out << ")\n";
|
||||
}
|
||||
|
||||
// fact :: "(" predicate theory-term* ")"
|
||||
void boogie_proof::pp_fact(std::ostream& out, expr* fact) {
|
||||
out << mk_pp(fact, m) << "\n";
|
||||
}
|
||||
|
||||
// subst :: "(" "subst" assignment* ")"
|
||||
void boogie_proof::pp_subst(std::ostream& out, subst& s) {
|
||||
out << "(subst";
|
||||
for (unsigned i = 0; i < s.size(); ++i) {
|
||||
pp_assignment(out, s[i].first, s[i].second);
|
||||
}
|
||||
out << ")\n";
|
||||
}
|
||||
|
||||
// assignment :: "(" "=" variable theory-term ")"
|
||||
void boogie_proof::pp_assignment(std::ostream& out, symbol const& v, expr* t) {
|
||||
out << "\n (= " << v << " " << mk_pp(t, m) << ")";
|
||||
}
|
||||
|
||||
|
||||
// labels :: "(" "labels" label* ")"
|
||||
void boogie_proof::pp_labels(std::ostream& out, labels& lbls) {
|
||||
out << "(labels";
|
||||
for (unsigned i = 0; i < lbls.size(); ++i) {
|
||||
out << " " << lbls[i];
|
||||
}
|
||||
out << ")\n";
|
||||
}
|
||||
|
||||
// premises "(" "ref" step-name* ")"
|
||||
void boogie_proof::pp_premises(std::ostream& out, refs& refs) {
|
||||
out << "(ref";
|
||||
for (unsigned i = 0; i < refs.size(); ++i) {
|
||||
out << " s!" << refs[i];
|
||||
}
|
||||
out << ")\n";
|
||||
}
|
||||
|
||||
|
||||
}
|
87
src/muz/base/dl_boogie_proof.h
Normal file
87
src/muz/base/dl_boogie_proof.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
|
||||
output :: derivation model
|
||||
|
||||
derivation :: "(" "derivation" step* ")"
|
||||
|
||||
step :: "(" "step" step-name fact rule-name subst labels premises ")"
|
||||
|
||||
step-name :: identifier
|
||||
|
||||
rule-name :: identifier
|
||||
|
||||
fact :: "(" predicate theory-term* ")"
|
||||
|
||||
subst :: "(" "subst" assignment* ")"
|
||||
|
||||
assignment :: "(" "=" variable theory-term ")"
|
||||
|
||||
labels :: "(" "labels" label* ")"
|
||||
|
||||
premises :: "(" "ref" step-name* ")"
|
||||
|
||||
model :: "(" "model" smtlib2-model ")"
|
||||
|
||||
In each step the "fact" is derivable by hyper-resolution from the named
|
||||
premises and the named rule, under the given substitution for the
|
||||
universally quantified variables in the rule. The premises of each
|
||||
step must have occurred previously in the step sequence. The last fact
|
||||
is a nullary placeholder predicate representing satisfaction of the query
|
||||
(its name is arbitrary).
|
||||
|
||||
The labels list consists of all the positively labeled sub-formulas whose
|
||||
truth is used in the proof, and all the negatively labeled formulas whose
|
||||
negation is used. A theory-term is a ground term using only interpreted
|
||||
constants of the background theories.
|
||||
|
||||
The smtlib2-model gives an interpretation of the uninterpreted constants
|
||||
in the background theory under which the derivation is valid. Currently
|
||||
it is a quoted string in the old z3 model format, for compatibility with
|
||||
Boogie, however, this should be changed to the new model format (using
|
||||
define-fun) when Boogie supports this.
|
||||
|
||||
*/
|
||||
|
||||
#include "ast.h"
|
||||
#include "model.h"
|
||||
|
||||
namespace datalog {
|
||||
class boogie_proof {
|
||||
typedef vector<std::pair<symbol,expr*> > subst;
|
||||
typedef svector<symbol> labels;
|
||||
typedef unsigned_vector refs;
|
||||
struct step {
|
||||
symbol m_rule_name;
|
||||
expr* m_fact;
|
||||
subst m_subst;
|
||||
labels m_labels;
|
||||
refs m_refs;
|
||||
};
|
||||
|
||||
ast_manager& m;
|
||||
proof_ref m_proof;
|
||||
model_ref m_model;
|
||||
|
||||
void pp_proof(std::ostream& out);
|
||||
void pp_steps(std::ostream& out, vector<step>& steps);
|
||||
void pp_step(std::ostream& out, unsigned i, step& s);
|
||||
void pp_fact(std::ostream& out, expr* fact);
|
||||
void pp_subst(std::ostream& out, subst& s);
|
||||
void pp_assignment(std::ostream& out, symbol const& v, expr* t);
|
||||
void pp_labels(std::ostream& out, labels& lbls);
|
||||
void pp_premises(std::ostream& out, refs& refs);
|
||||
|
||||
void get_subst(proof* p, subst& sub);
|
||||
void get_rule_name(proof* p, symbol&);
|
||||
void get_labels(proof* p, labels&);
|
||||
|
||||
public:
|
||||
boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(0) {}
|
||||
|
||||
void set_proof(proof* p);
|
||||
|
||||
void set_model(model* m);
|
||||
|
||||
void pp(std::ostream& out);
|
||||
};
|
||||
}
|
1267
src/muz/base/dl_context.cpp
Normal file
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
562
src/muz/base/dl_context.h
Normal 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
160
src/muz/base/dl_costs.cpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_costs.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "debug.h"
|
||||
#include "stopwatch.h"
|
||||
#include "dl_context.h"
|
||||
#include "dl_rule.h"
|
||||
#include "dl_costs.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// costs
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
costs::costs() : milliseconds(0), instructions(0) {}
|
||||
|
||||
bool costs::empty() const {
|
||||
return !milliseconds && !instructions;
|
||||
}
|
||||
|
||||
void costs::reset() {
|
||||
milliseconds = 0;
|
||||
instructions = 0;
|
||||
}
|
||||
|
||||
costs costs::operator-(const costs & o) const {
|
||||
costs res(*this);
|
||||
SASSERT(milliseconds>o.milliseconds);
|
||||
res.milliseconds-=o.milliseconds;
|
||||
SASSERT(instructions>o.instructions);
|
||||
res.instructions-=o.instructions;
|
||||
return res;
|
||||
}
|
||||
|
||||
void costs::operator+=(const costs & o) {
|
||||
milliseconds+=o.milliseconds;
|
||||
instructions+=o.instructions;
|
||||
}
|
||||
|
||||
bool costs::passes_thresholds(context & ctx) const {
|
||||
return milliseconds >= ctx.dl_profile_milliseconds_threshold();
|
||||
}
|
||||
|
||||
void costs::output(std::ostream & out) const {
|
||||
out << "instr: " << instructions << " time: " << milliseconds << "ms";
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// accounted_object
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
accounted_object::~accounted_object() {
|
||||
if(m_parent_object) {
|
||||
SASSERT(m_context);
|
||||
m_context->get_rule_manager().dec_ref(m_parent_object);
|
||||
}
|
||||
}
|
||||
|
||||
void accounted_object::set_accounting_parent_object(context & ctx, rule * parent) {
|
||||
if(m_parent_object) {
|
||||
SASSERT(m_context);
|
||||
SASSERT(m_context==&ctx);
|
||||
m_context->get_rule_manager().dec_ref(m_parent_object);
|
||||
}
|
||||
m_context = &ctx;
|
||||
m_parent_object = parent;
|
||||
m_context->get_rule_manager().inc_ref(m_parent_object);
|
||||
}
|
||||
|
||||
void accounted_object::process_costs() {
|
||||
costs delta = get_current_costs();
|
||||
if(delta.empty()) {
|
||||
return;
|
||||
}
|
||||
get_current_costs().reset();
|
||||
accounted_object * obj = this;
|
||||
do {
|
||||
obj->m_processed_cost+=delta;
|
||||
obj=obj->m_parent_object;
|
||||
} while(obj);
|
||||
}
|
||||
|
||||
void accounted_object::get_total_cost(costs & result) const {
|
||||
result.reset();
|
||||
result+=m_current_cost;
|
||||
result+=m_processed_cost;
|
||||
}
|
||||
|
||||
bool accounted_object::passes_output_thresholds(context & ctx) const {
|
||||
costs c;
|
||||
get_total_cost(c);
|
||||
return c.passes_thresholds(ctx);
|
||||
}
|
||||
|
||||
|
||||
void accounted_object::output_profile(std::ostream & out) const {
|
||||
costs c;
|
||||
get_total_cost(c);
|
||||
c.output(out);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// cost_recorder
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
cost_recorder::cost_recorder() : m_obj(0) {
|
||||
m_stopwatch = alloc(stopwatch);
|
||||
m_stopwatch->start();
|
||||
}
|
||||
|
||||
cost_recorder::~cost_recorder() {
|
||||
if(m_obj) {
|
||||
finish();
|
||||
}
|
||||
dealloc(m_stopwatch);
|
||||
}
|
||||
|
||||
void cost_recorder::start(accounted_object * obj) {
|
||||
uint64 curr_time = static_cast<uint64>(m_stopwatch->get_current_seconds()*1000);
|
||||
if(m_obj) {
|
||||
costs::time_type time_delta = static_cast<costs::time_type>(curr_time-m_last_time);
|
||||
costs & c = m_obj->get_current_costs();
|
||||
c.instructions++;
|
||||
c.milliseconds+=time_delta;
|
||||
m_obj->m_being_recorded = false;
|
||||
}
|
||||
m_running = obj!=0;
|
||||
m_obj = obj;
|
||||
m_last_time = curr_time;
|
||||
if(obj) {
|
||||
m_obj->m_being_recorded = true;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
115
src/muz/base/dl_costs.h
Normal file
115
src/muz/base/dl_costs.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_costs.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _DL_COSTS_H_
|
||||
#define _DL_COSTS_H_
|
||||
|
||||
#include<iosfwd>
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
class stopwatch;
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class context;
|
||||
class rule;
|
||||
|
||||
class cost_recorder;
|
||||
|
||||
struct costs {
|
||||
typedef unsigned time_type;
|
||||
|
||||
time_type milliseconds;
|
||||
unsigned instructions;
|
||||
|
||||
costs();
|
||||
|
||||
bool empty() const;
|
||||
void reset();
|
||||
|
||||
costs operator-(const costs & o) const;
|
||||
void operator+=(const costs & o);
|
||||
|
||||
bool passes_thresholds(context & ctx) const;
|
||||
|
||||
void output(std::ostream & out) const;
|
||||
};
|
||||
|
||||
|
||||
class accounted_object {
|
||||
friend class cost_recorder;
|
||||
|
||||
context * m_context;
|
||||
rule * m_parent_object;
|
||||
|
||||
costs m_current_cost;
|
||||
costs m_processed_cost;
|
||||
bool m_being_recorded;
|
||||
protected:
|
||||
accounted_object() : m_context(0), m_parent_object(0), m_being_recorded(false) {}
|
||||
~accounted_object();
|
||||
public:
|
||||
|
||||
void set_accounting_parent_object(context & ctx, rule * parent);
|
||||
rule * get_parent_object() const { return m_parent_object; }
|
||||
|
||||
costs & get_current_costs() { return m_current_cost; }
|
||||
const costs & get_current_costs() const { return m_current_cost; }
|
||||
const costs & get_processed_costs() const { return m_processed_cost; }
|
||||
void get_total_cost(costs & result) const;
|
||||
bool being_recorded() const { return m_being_recorded; }
|
||||
|
||||
void process_costs();
|
||||
|
||||
bool passes_output_thresholds(context & ctx) const;
|
||||
void output_profile(std::ostream & out) const;
|
||||
|
||||
private:
|
||||
//private and undefined copy constructor and operator= to avoid the default ones
|
||||
accounted_object(const accounted_object &);
|
||||
accounted_object& operator=(const accounted_object &);
|
||||
};
|
||||
|
||||
|
||||
class cost_recorder {
|
||||
accounted_object * m_obj;
|
||||
//it's a pointer to avoid everything depending on the stopwatch.h
|
||||
//(and transitively then on windows.h) header file
|
||||
stopwatch * m_stopwatch;
|
||||
|
||||
bool m_running;
|
||||
uint64 m_last_time;
|
||||
public:
|
||||
cost_recorder();
|
||||
~cost_recorder();
|
||||
/**
|
||||
\brief Start recording costs for the next object.
|
||||
|
||||
If the recording of the previous object did not finish, it will be finished here.
|
||||
Also, it will be done more efficiently than if the \c finish() function was called
|
||||
before separately.
|
||||
*/
|
||||
void start(accounted_object *);
|
||||
void finish() { start(0); }
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* _DL_COSTS_H_ */
|
||||
|
82
src/muz/base/dl_engine_base.h
Normal file
82
src/muz/base/dl_engine_base.h
Normal 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
1142
src/muz/base/dl_rule.cpp
Normal file
File diff suppressed because it is too large
Load diff
331
src/muz/base/dl_rule.h
Normal file
331
src/muz/base/dl_rule.h
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rule.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2010-05-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _DL_RULE_H_
|
||||
#define _DL_RULE_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"dl_costs.h"
|
||||
#include"dl_util.h"
|
||||
#include"used_vars.h"
|
||||
#include"proof_converter.h"
|
||||
#include"model_converter.h"
|
||||
#include"ast_counter.h"
|
||||
#include"rewriter.h"
|
||||
#include"hnf.h"
|
||||
#include"qe_lite.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class rule;
|
||||
class rule_manager;
|
||||
class rule_set;
|
||||
class table;
|
||||
class context;
|
||||
|
||||
typedef obj_ref<rule, rule_manager> rule_ref;
|
||||
typedef ref_vector<rule, rule_manager> rule_ref_vector;
|
||||
typedef ptr_vector<rule> rule_vector;
|
||||
/**
|
||||
\brief Manager for the \c rule class
|
||||
|
||||
\remark \c rule_manager objects are interchangable as long as they
|
||||
contain the same \c ast_manager object.
|
||||
*/
|
||||
class rule_manager
|
||||
{
|
||||
class remove_label_cfg : public default_rewriter_cfg {
|
||||
family_id m_label_fid;
|
||||
public:
|
||||
remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {}
|
||||
virtual ~remove_label_cfg();
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result,
|
||||
proof_ref & result_pr);
|
||||
};
|
||||
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
rule_counter m_counter;
|
||||
used_vars m_used;
|
||||
ptr_vector<sort> m_vars;
|
||||
var_idx_set m_var_idx;
|
||||
ptr_vector<expr> m_todo;
|
||||
ast_mark m_mark;
|
||||
app_ref_vector m_body;
|
||||
app_ref m_head;
|
||||
expr_ref_vector m_args;
|
||||
svector<bool> m_neg;
|
||||
hnf m_hnf;
|
||||
qe_lite m_qe;
|
||||
remove_label_cfg m_cfg;
|
||||
rewriter_tpl<remove_label_cfg> m_rwr;
|
||||
|
||||
|
||||
// only the context can create a rule_manager
|
||||
friend class context;
|
||||
|
||||
explicit rule_manager(context& ctx);
|
||||
|
||||
/**
|
||||
\brief Move functions from predicate tails into the interpreted tail by introducing new variables.
|
||||
*/
|
||||
void hoist_compound_predicates(unsigned num_bound, app_ref& head, app_ref_vector& body);
|
||||
|
||||
void hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body);
|
||||
|
||||
void flatten_body(app_ref_vector& body);
|
||||
|
||||
void remove_labels(expr_ref& fml, proof_ref& pr);
|
||||
|
||||
app_ref ensure_app(expr* e);
|
||||
|
||||
void check_app(expr* e);
|
||||
|
||||
bool contains_predicate(expr* fml) const;
|
||||
|
||||
void bind_variables(expr* fml, bool is_forall, expr_ref& result);
|
||||
|
||||
void mk_negations(app_ref_vector& body, svector<bool>& is_negated);
|
||||
|
||||
void mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name);
|
||||
|
||||
void mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name);
|
||||
|
||||
static expr_ref mk_implies(app_ref_vector const& body, expr* head);
|
||||
|
||||
unsigned extract_horn(expr* fml, app_ref_vector& body, app_ref& head);
|
||||
|
||||
/**
|
||||
\brief Perform cheap quantifier elimination to reduce the number of variables in the interpreted tail.
|
||||
*/
|
||||
void reduce_unbound_vars(rule_ref& r);
|
||||
|
||||
void reset_collect_vars();
|
||||
|
||||
var_idx_set& finalize_collect_vars();
|
||||
|
||||
public:
|
||||
|
||||
ast_manager& get_manager() const { return m; }
|
||||
|
||||
void inc_ref(rule * r);
|
||||
|
||||
void dec_ref(rule * r);
|
||||
|
||||
used_vars& reset_used() { m_used.reset(); return m_used; }
|
||||
|
||||
var_idx_set& collect_vars(expr * pred);
|
||||
|
||||
var_idx_set& collect_vars(expr * e1, expr* e2);
|
||||
|
||||
var_idx_set& collect_rule_vars(rule * r);
|
||||
|
||||
var_idx_set& collect_rule_vars_ex(rule * r, app* t);
|
||||
|
||||
var_idx_set& collect_tail_vars(rule * r);
|
||||
|
||||
void accumulate_vars(expr* pred);
|
||||
|
||||
ptr_vector<sort>& get_var_sorts() { return m_vars; }
|
||||
|
||||
var_idx_set& get_var_idx() { return m_var_idx; }
|
||||
|
||||
/**
|
||||
\brief Create a Datalog rule from a Horn formula.
|
||||
The formula is of the form (forall (...) (forall (...) (=> (and ...) head)))
|
||||
|
||||
*/
|
||||
void mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name = symbol::null);
|
||||
|
||||
/**
|
||||
\brief Create a Datalog query from an expression.
|
||||
The formula is of the form (exists (...) (exists (...) (and ...))
|
||||
*/
|
||||
func_decl* mk_query(expr* query, rule_set& rules);
|
||||
|
||||
/**
|
||||
\brief Create a Datalog rule head :- tail[0], ..., tail[n-1].
|
||||
Return 0 if it is not a valid rule.
|
||||
|
||||
\remark A tail may contain negation. tail[i] is assumed to be negated if is_neg != 0 && is_neg[i] == true
|
||||
*/
|
||||
rule * mk(app * head, unsigned n, app * const * tail, bool const * is_neg = 0,
|
||||
symbol const& name = symbol::null, bool normalize = true);
|
||||
|
||||
/**
|
||||
\brief Create a rule with the same tail as \c source and with a specified head.
|
||||
*/
|
||||
rule * mk(rule const * source, app * new_head, symbol const& name = symbol::null);
|
||||
|
||||
/**
|
||||
\brief Create a copy of the given rule.
|
||||
*/
|
||||
rule * mk(rule const * source, symbol const& name = symbol::null);
|
||||
|
||||
/** make sure there are not non-quantified variables that occur only in interpreted predicates */
|
||||
void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination);
|
||||
|
||||
/**
|
||||
\brief add proof that new rule is obtained by rewriting old rule.
|
||||
*/
|
||||
void mk_rule_rewrite_proof(rule& old_rule, rule& new_rule);
|
||||
|
||||
/**
|
||||
\brief tag rule as asserted.
|
||||
*/
|
||||
void mk_rule_asserted_proof(rule& r);
|
||||
|
||||
/**
|
||||
\brief apply substitution to variables of rule.
|
||||
*/
|
||||
void substitute(rule_ref& r, unsigned sz, expr*const* es);
|
||||
|
||||
/**
|
||||
\brief Check that head :- tail[0], ..., tail[n-1]
|
||||
is a valid Datalog rule.
|
||||
*/
|
||||
void check_valid_rule(app * head, unsigned n, app * const * tail) const;
|
||||
|
||||
/**
|
||||
\brief Check that \c head may occur as a Datalog rule head.
|
||||
*/
|
||||
void check_valid_head(expr * head) const;
|
||||
|
||||
/**
|
||||
\brief Return true if \c head may occur as a fact.
|
||||
*/
|
||||
bool is_fact(app * head) const;
|
||||
|
||||
|
||||
static bool is_forall(ast_manager& m, expr* e, quantifier*& q);
|
||||
|
||||
rule_counter& get_counter() { return m_counter; }
|
||||
|
||||
};
|
||||
|
||||
class rule : public accounted_object {
|
||||
friend class rule_manager;
|
||||
|
||||
app * m_head;
|
||||
proof* m_proof;
|
||||
unsigned m_tail_size:20;
|
||||
// unsigned m_reserve:12;
|
||||
unsigned m_ref_cnt;
|
||||
unsigned m_positive_cnt;
|
||||
unsigned m_uninterp_cnt;
|
||||
symbol m_name;
|
||||
/**
|
||||
The following field is an array of tagged pointers.
|
||||
- Tag 0: the atom is not negated
|
||||
- Tag 1: the atom is negated.
|
||||
|
||||
The order of tail formulas is the following:
|
||||
|
||||
uninterpreted positive,
|
||||
uninterpreted negative,
|
||||
interpreted.
|
||||
|
||||
The negated flag is never set for interpreted tails.
|
||||
*/
|
||||
app * m_tail[0];
|
||||
|
||||
static unsigned get_obj_size(unsigned n) { return sizeof(rule) + n * sizeof(app *); }
|
||||
|
||||
rule() : m_ref_cnt(0) {}
|
||||
~rule() {}
|
||||
|
||||
void deallocate(ast_manager & m);
|
||||
|
||||
void get_used_vars(used_vars& uv) const;
|
||||
|
||||
public:
|
||||
|
||||
proof * get_proof() const { return m_proof; }
|
||||
|
||||
void set_proof(ast_manager& m, proof* p);
|
||||
|
||||
app * get_head() const { return m_head; }
|
||||
|
||||
func_decl* get_decl() const { return get_head()->get_decl(); }
|
||||
|
||||
unsigned get_tail_size() const { return m_tail_size; }
|
||||
|
||||
/**
|
||||
\brief Return number of positive uninterpreted predicates in the tail.
|
||||
|
||||
These predicates are the first in the tail.
|
||||
*/
|
||||
unsigned get_positive_tail_size() const { return m_positive_cnt; }
|
||||
unsigned get_uninterpreted_tail_size() const { return m_uninterp_cnt; }
|
||||
|
||||
/**
|
||||
\brief Return i-th tail atom. The first \c get_uninterpreted_tail_size()
|
||||
atoms are uninterpreted and the first \c get_positive_tail_size() are
|
||||
uninterpreted and non-negated.
|
||||
*/
|
||||
app * get_tail(unsigned i) const { SASSERT(i < m_tail_size); return UNTAG(app *, m_tail[i]); }
|
||||
|
||||
func_decl* get_decl(unsigned i) const { SASSERT(i < get_uninterpreted_tail_size()); return get_tail(i)->get_decl(); }
|
||||
|
||||
bool is_neg_tail(unsigned i) const { SASSERT(i < m_tail_size); return GET_TAG(m_tail[i]) == 1; }
|
||||
|
||||
/**
|
||||
Check whether predicate p is in the interpreted tail.
|
||||
|
||||
If only_positive is true, only the positive predicate tail atoms are checked.
|
||||
*/
|
||||
bool is_in_tail(const func_decl * p, bool only_positive=false) const;
|
||||
|
||||
bool has_uninterpreted_non_predicates(func_decl*& f) const;
|
||||
void has_quantifiers(bool& existential, bool& universal) const;
|
||||
bool has_quantifiers() const;
|
||||
bool has_negation() const;
|
||||
|
||||
/**
|
||||
\brief Store in d the (direct) dependencies of the given rule.
|
||||
*/
|
||||
|
||||
void norm_vars(rule_manager & rm);
|
||||
|
||||
void get_vars(ptr_vector<sort>& sorts) const;
|
||||
|
||||
void to_formula(expr_ref& result) const;
|
||||
|
||||
void display(context & ctx, std::ostream & out) const;
|
||||
|
||||
std::ostream& display_smt2(ast_manager& m, std::ostream & out) const;
|
||||
|
||||
symbol const& name() const { return m_name; }
|
||||
|
||||
unsigned hash() const;
|
||||
|
||||
};
|
||||
|
||||
struct rule_eq_proc {
|
||||
bool operator()(const rule * r1, const rule * r2) const;
|
||||
};
|
||||
struct rule_hash_proc {
|
||||
unsigned operator()(const rule * r) const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_RULE_H_ */
|
||||
|
773
src/muz/base/dl_rule_set.cpp
Normal file
773
src/muz/base/dl_rule_set.cpp
Normal file
|
@ -0,0 +1,773 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rule_set.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2010-05-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include<algorithm>
|
||||
#include<functional>
|
||||
#include"dl_context.h"
|
||||
#include"dl_rule_set.h"
|
||||
#include"ast_pp.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
rule_dependencies::rule_dependencies(context& ctx): m_context(ctx) {
|
||||
}
|
||||
|
||||
rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed):
|
||||
m_context(o.m_context) {
|
||||
if (reversed) {
|
||||
iterator oit = o.begin();
|
||||
iterator oend = o.end();
|
||||
for (; oit!=oend; ++oit) {
|
||||
func_decl * pred = oit->m_key;
|
||||
item_set & orig_items = *oit->get_value();
|
||||
|
||||
ensure_key(pred);
|
||||
item_set::iterator dit = orig_items.begin();
|
||||
item_set::iterator dend = orig_items.end();
|
||||
for (; dit!=dend; ++dit) {
|
||||
func_decl * master_pred = *dit;
|
||||
insert(master_pred, pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
iterator oit = o.begin();
|
||||
iterator oend = o.end();
|
||||
for (; oit!=oend; ++oit) {
|
||||
func_decl * pred = oit->m_key;
|
||||
item_set & orig_items = *oit->get_value();
|
||||
m_data.insert(pred, alloc(item_set, orig_items));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule_dependencies::~rule_dependencies() {
|
||||
reset();
|
||||
}
|
||||
void rule_dependencies::reset() {
|
||||
reset_dealloc_values(m_data);
|
||||
}
|
||||
|
||||
void rule_dependencies::remove_m_data_entry(func_decl * key) {
|
||||
item_set * itm_set = m_data.find(key);
|
||||
dealloc(itm_set);
|
||||
m_data.remove(key);
|
||||
}
|
||||
|
||||
rule_dependencies::item_set & rule_dependencies::ensure_key(func_decl * pred) {
|
||||
deps_type::obj_map_entry * e = m_data.insert_if_not_there2(pred, 0);
|
||||
if (!e->get_data().m_value) {
|
||||
e->get_data().m_value = alloc(item_set);
|
||||
}
|
||||
return *e->get_data().m_value;
|
||||
}
|
||||
|
||||
void rule_dependencies::insert(func_decl * depending, func_decl * master) {
|
||||
SASSERT(m_data.contains(master)); //see m_data documentation
|
||||
item_set & s = ensure_key(depending);
|
||||
s.insert(master);
|
||||
}
|
||||
|
||||
void rule_dependencies::populate(const rule_set & rules) {
|
||||
SASSERT(m_data.empty());
|
||||
rule_set::decl2rules::iterator it = rules.m_head2rules.begin();
|
||||
rule_set::decl2rules::iterator end = rules.m_head2rules.end();
|
||||
for (; it != end; ++it) {
|
||||
ptr_vector<rule> * rules = it->m_value;
|
||||
ptr_vector<rule>::iterator it2 = rules->begin();
|
||||
ptr_vector<rule>::iterator end2 = rules->end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
populate(*it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rule_dependencies::populate(unsigned n, rule * const * rules) {
|
||||
SASSERT(m_data.empty());
|
||||
for (unsigned i=0; i<n; i++) {
|
||||
populate(rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void rule_dependencies::populate(rule const* r) {
|
||||
TRACE("dl_verbose", tout << r->get_decl()->get_name() << "\n";);
|
||||
m_visited.reset();
|
||||
func_decl * d = r->get_decl();
|
||||
func_decl_set & s = ensure_key(d);
|
||||
|
||||
for (unsigned i = 0; i < r->get_tail_size(); ++i) {
|
||||
m_todo.push_back(r->get_tail(i));
|
||||
}
|
||||
while (!m_todo.empty()) {
|
||||
expr* e = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
if (m_visited.is_marked(e)) {
|
||||
continue;
|
||||
}
|
||||
m_visited.mark(e, true);
|
||||
if (is_app(e)) {
|
||||
app* a = to_app(e);
|
||||
d = a->get_decl();
|
||||
if (m_context.is_predicate(d)) {
|
||||
// insert d and ensure the invariant
|
||||
// that every predicate is present as
|
||||
// a key in m_data
|
||||
s.insert(d);
|
||||
ensure_key(d);
|
||||
}
|
||||
m_todo.append(a->get_num_args(), a->get_args());
|
||||
}
|
||||
else if (is_quantifier(e)) {
|
||||
m_todo.push_back(to_quantifier(e)->get_expr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rule_dependencies::item_set & rule_dependencies::get_deps(func_decl * f) const {
|
||||
deps_type::obj_map_entry * e = m_data.find_core(f);
|
||||
if (!e) {
|
||||
return m_empty_item_set;
|
||||
}
|
||||
SASSERT(e->get_data().get_value());
|
||||
return *e->get_data().get_value();
|
||||
}
|
||||
|
||||
void rule_dependencies::restrict(const item_set & allowed) {
|
||||
ptr_vector<func_decl> to_remove;
|
||||
iterator pit = begin();
|
||||
iterator pend = end();
|
||||
for (; pit!=pend; ++pit) {
|
||||
func_decl * pred = pit->m_key;
|
||||
if (!allowed.contains(pred)) {
|
||||
to_remove.insert(pred);
|
||||
continue;
|
||||
}
|
||||
item_set& itms = *pit->get_value();
|
||||
set_intersection(itms, allowed);
|
||||
}
|
||||
ptr_vector<func_decl>::iterator rit = to_remove.begin();
|
||||
ptr_vector<func_decl>::iterator rend = to_remove.end();
|
||||
for (; rit != rend; ++rit) {
|
||||
remove_m_data_entry(*rit);
|
||||
}
|
||||
}
|
||||
|
||||
void rule_dependencies::remove(func_decl * itm) {
|
||||
remove_m_data_entry(itm);
|
||||
iterator pit = begin();
|
||||
iterator pend = end();
|
||||
for (; pit != pend; ++pit) {
|
||||
item_set & itms = *pit->get_value();
|
||||
itms.remove(itm);
|
||||
}
|
||||
}
|
||||
|
||||
void rule_dependencies::remove(const item_set & to_remove) {
|
||||
item_set::iterator rit = to_remove.begin();
|
||||
item_set::iterator rend = to_remove.end();
|
||||
for (; rit!=rend; ++rit) {
|
||||
remove_m_data_entry(*rit);
|
||||
}
|
||||
iterator pit = begin();
|
||||
iterator pend = end();
|
||||
for (; pit!=pend; ++pit) {
|
||||
item_set * itms = pit->get_value();
|
||||
set_difference(*itms, to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned rule_dependencies::out_degree(func_decl * f) const {
|
||||
unsigned res = 0;
|
||||
iterator pit = begin();
|
||||
iterator pend = end();
|
||||
for (; pit!=pend; ++pit) {
|
||||
item_set & itms = *pit->get_value();
|
||||
if (itms.contains(f)) {
|
||||
res++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool rule_dependencies::sort_deps(ptr_vector<func_decl> & res) {
|
||||
typedef obj_map<func_decl, unsigned> deg_map;
|
||||
unsigned init_len = res.size();
|
||||
deg_map degs;
|
||||
unsigned curr_index = init_len;
|
||||
rule_dependencies reversed(*this, true);
|
||||
|
||||
iterator pit = begin();
|
||||
iterator pend = end();
|
||||
for (; pit!=pend; ++pit) {
|
||||
func_decl * pred = pit->m_key;
|
||||
unsigned deg = in_degree(pred);
|
||||
if (deg==0) {
|
||||
res.push_back(pred);
|
||||
}
|
||||
else {
|
||||
degs.insert(pred, deg);
|
||||
}
|
||||
}
|
||||
|
||||
while (curr_index<res.size()) { //res.size() can change in the loop iteration
|
||||
func_decl * curr = res[curr_index];
|
||||
const item_set & children = reversed.get_deps(curr);
|
||||
item_set::iterator cit = children.begin();
|
||||
item_set::iterator cend = children.end();
|
||||
for (; cit!=cend; ++cit) {
|
||||
func_decl * child = *cit;
|
||||
deg_map::obj_map_entry * e = degs.find_core(child);
|
||||
SASSERT(e);
|
||||
unsigned & child_deg = e->get_data().m_value;
|
||||
SASSERT(child_deg>0);
|
||||
child_deg--;
|
||||
if (child_deg==0) {
|
||||
res.push_back(child);
|
||||
}
|
||||
}
|
||||
curr_index++;
|
||||
}
|
||||
if (res.size() < init_len + m_data.size()) {
|
||||
res.shrink(init_len);
|
||||
return false;
|
||||
}
|
||||
SASSERT(res.size()==init_len+m_data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void rule_dependencies::display(std::ostream & out ) const
|
||||
{
|
||||
iterator pit = begin();
|
||||
iterator pend = end();
|
||||
for (; pit != pend; ++pit) {
|
||||
func_decl * pred = pit->m_key;
|
||||
const item_set & deps = *pit->m_value;
|
||||
item_set::iterator dit=deps.begin();
|
||||
item_set::iterator dend=deps.end();
|
||||
if (dit == dend) {
|
||||
out<<pred->get_name()<<" - <none>\n";
|
||||
}
|
||||
for (; dit != dend; ++dit) {
|
||||
func_decl * dep = *dit;
|
||||
out << pred->get_name() << " -> " << dep->get_name() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// rule_set
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
rule_set::rule_set(context & ctx)
|
||||
: m_context(ctx),
|
||||
m_rule_manager(ctx.get_rule_manager()),
|
||||
m_rules(m_rule_manager),
|
||||
m_deps(ctx),
|
||||
m_stratifier(0),
|
||||
m_refs(ctx.get_manager()) {
|
||||
}
|
||||
|
||||
rule_set::rule_set(const rule_set & other)
|
||||
: m_context(other.m_context),
|
||||
m_rule_manager(other.m_rule_manager),
|
||||
m_rules(m_rule_manager),
|
||||
m_deps(other.m_context),
|
||||
m_stratifier(0),
|
||||
m_refs(m_context.get_manager()) {
|
||||
add_rules(other);
|
||||
if (other.m_stratifier) {
|
||||
VERIFY(close());
|
||||
}
|
||||
}
|
||||
|
||||
rule_set::~rule_set() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void rule_set::reset() {
|
||||
m_rules.reset();
|
||||
reset_dealloc_values(m_head2rules);
|
||||
m_deps.reset();
|
||||
m_stratifier = 0;
|
||||
m_output_preds.reset();
|
||||
m_orig2pred.reset();
|
||||
m_pred2orig.reset();
|
||||
m_refs.reset();
|
||||
}
|
||||
|
||||
ast_manager & rule_set::get_manager() const {
|
||||
return m_context.get_manager();
|
||||
}
|
||||
|
||||
func_decl* rule_set::get_orig(func_decl* pred) const {
|
||||
func_decl* orig = pred;
|
||||
m_pred2orig.find(pred, orig);
|
||||
return orig;
|
||||
}
|
||||
|
||||
func_decl* rule_set::get_pred(func_decl* orig) const {
|
||||
func_decl* pred = orig;
|
||||
m_orig2pred.find(orig, pred);
|
||||
return pred;
|
||||
}
|
||||
|
||||
void rule_set::inherit_predicates(rule_set const& other) {
|
||||
m_refs.append(other.m_refs);
|
||||
set_union(m_output_preds, other.m_output_preds);
|
||||
{
|
||||
obj_map<func_decl, func_decl*>::iterator it = other.m_orig2pred.begin();
|
||||
obj_map<func_decl, func_decl*>::iterator end = other.m_orig2pred.end();
|
||||
for (; it != end; ++it) {
|
||||
m_orig2pred.insert(it->m_key, it->m_value);
|
||||
}
|
||||
}
|
||||
{
|
||||
obj_map<func_decl, func_decl*>::iterator it = other.m_pred2orig.begin();
|
||||
obj_map<func_decl, func_decl*>::iterator end = other.m_pred2orig.end();
|
||||
for (; it != end; ++it) {
|
||||
m_pred2orig.insert(it->m_key, it->m_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rule_set::inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred) {
|
||||
if (other.is_output_predicate(orig)) {
|
||||
set_output_predicate(pred);
|
||||
}
|
||||
orig = other.get_orig(orig);
|
||||
m_refs.push_back(pred);
|
||||
m_refs.push_back(orig);
|
||||
m_orig2pred.insert(orig, pred);
|
||||
m_pred2orig.insert(pred, orig);
|
||||
}
|
||||
|
||||
void rule_set::add_rule(rule * r) {
|
||||
TRACE("dl_verbose", r->display(m_context, tout << "add:"););
|
||||
SASSERT(!is_closed());
|
||||
m_rules.push_back(r);
|
||||
app * head = r->get_head();
|
||||
SASSERT(head != 0);
|
||||
func_decl * d = head->get_decl();
|
||||
decl2rules::obj_map_entry* e = m_head2rules.insert_if_not_there2(d, 0);
|
||||
if (!e->get_data().m_value) e->get_data().m_value = alloc(ptr_vector<rule>);
|
||||
e->get_data().m_value->push_back(r);
|
||||
}
|
||||
|
||||
void rule_set::del_rule(rule * r) {
|
||||
TRACE("dl", r->display(m_context, tout << "del:"););
|
||||
func_decl* d = r->get_decl();
|
||||
rule_vector* rules = m_head2rules.find(d);
|
||||
#define DEL_VECTOR(_v) \
|
||||
for (unsigned i = (_v).size(); i > 0; ) { \
|
||||
--i; \
|
||||
if ((_v)[i] == r) { \
|
||||
(_v)[i] = (_v).back(); \
|
||||
(_v).pop_back(); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
|
||||
DEL_VECTOR(*rules);
|
||||
DEL_VECTOR(m_rules);
|
||||
}
|
||||
|
||||
void rule_set::ensure_closed() {
|
||||
if (!is_closed()) {
|
||||
VERIFY(close());
|
||||
}
|
||||
}
|
||||
|
||||
bool rule_set::close() {
|
||||
SASSERT(!is_closed()); //the rule_set is not already closed
|
||||
m_deps.populate(*this);
|
||||
m_stratifier = alloc(rule_stratifier, m_deps);
|
||||
if (!stratified_negation()) {
|
||||
m_stratifier = 0;
|
||||
m_deps.reset();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void rule_set::reopen() {
|
||||
if (is_closed()) {
|
||||
m_stratifier = 0;
|
||||
m_deps.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the negation is indeed stratified.
|
||||
*/
|
||||
bool rule_set::stratified_negation() {
|
||||
ptr_vector<rule>::const_iterator it = m_rules.c_ptr();
|
||||
ptr_vector<rule>::const_iterator end = m_rules.c_ptr()+m_rules.size();
|
||||
for (; it != end; it++) {
|
||||
rule * r = *it;
|
||||
app * head = r->get_head();
|
||||
func_decl * head_decl = head->get_decl();
|
||||
unsigned n = r->get_uninterpreted_tail_size();
|
||||
for (unsigned i = r->get_positive_tail_size(); i < n; i++) {
|
||||
SASSERT(r->is_neg_tail(i));
|
||||
func_decl * tail_decl = r->get_tail(i)->get_decl();
|
||||
unsigned neg_strat = get_predicate_strat(tail_decl);
|
||||
unsigned head_strat = get_predicate_strat(head_decl);
|
||||
|
||||
SASSERT(head_strat>=neg_strat); //head strat can never be lower than that of a tail
|
||||
if (head_strat == neg_strat) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void rule_set::replace_rules(const rule_set & src) {
|
||||
if (this != &src) {
|
||||
reset();
|
||||
add_rules(src);
|
||||
}
|
||||
}
|
||||
|
||||
void rule_set::add_rules(const rule_set & src) {
|
||||
SASSERT(!is_closed());
|
||||
unsigned n = src.get_num_rules();
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
add_rule(src.get_rule(i));
|
||||
}
|
||||
inherit_predicates(src);
|
||||
}
|
||||
|
||||
const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const {
|
||||
decl2rules::obj_map_entry * e = m_head2rules.find_core(pred);
|
||||
if (!e) {
|
||||
return m_empty_rule_vector;
|
||||
}
|
||||
return *e->get_data().m_value;
|
||||
}
|
||||
|
||||
const rule_set::pred_set_vector & rule_set::get_strats() const {
|
||||
SASSERT(m_stratifier);
|
||||
return m_stratifier->get_strats();
|
||||
}
|
||||
|
||||
unsigned rule_set::get_predicate_strat(func_decl * pred) const {
|
||||
SASSERT(m_stratifier);
|
||||
return m_stratifier->get_predicate_strat(pred);
|
||||
}
|
||||
|
||||
void rule_set::split_founded_rules(func_decl_set& founded, func_decl_set& non_founded) {
|
||||
founded.reset();
|
||||
non_founded.reset();
|
||||
{
|
||||
decl2rules::iterator it = begin_grouped_rules(), end = end_grouped_rules();
|
||||
for (; it != end; ++it) {
|
||||
non_founded.insert(it->m_key);
|
||||
}
|
||||
}
|
||||
bool change = true;
|
||||
while (change) {
|
||||
change = false;
|
||||
func_decl_set::iterator it = non_founded.begin(), end = non_founded.end();
|
||||
for (; it != end; ++it) {
|
||||
rule_vector const& rv = get_predicate_rules(*it);
|
||||
bool found = false;
|
||||
for (unsigned i = 0; !found && i < rv.size(); ++i) {
|
||||
rule const& r = *rv[i];
|
||||
bool is_founded = true;
|
||||
for (unsigned j = 0; is_founded && j < r.get_uninterpreted_tail_size(); ++j) {
|
||||
is_founded = founded.contains(r.get_decl(j));
|
||||
}
|
||||
if (is_founded) {
|
||||
founded.insert(*it);
|
||||
non_founded.remove(*it);
|
||||
change = true;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rule_set::display(std::ostream & out) const {
|
||||
out << "; rule count: " << get_num_rules() << "\n";
|
||||
out << "; predicate count: " << m_head2rules.size() << "\n";
|
||||
func_decl_set::iterator pit = m_output_preds.begin();
|
||||
func_decl_set::iterator pend = m_output_preds.end();
|
||||
for (; pit != pend; ++pit) {
|
||||
out << "; output: " << (*pit)->get_name() << '\n';
|
||||
}
|
||||
decl2rules::iterator it = m_head2rules.begin();
|
||||
decl2rules::iterator end = m_head2rules.end();
|
||||
for (; it != end; ++it) {
|
||||
ptr_vector<rule> * rules = it->m_value;
|
||||
ptr_vector<rule>::iterator it2 = rules->begin();
|
||||
ptr_vector<rule>::iterator end2 = rules->end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
rule * r = *it2;
|
||||
if (!r->passes_output_thresholds(m_context)) {
|
||||
continue;
|
||||
}
|
||||
r->display(m_context, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rule_set::display_deps( std::ostream & out ) const
|
||||
{
|
||||
const pred_set_vector & strats = get_strats();
|
||||
pred_set_vector::const_iterator sit = strats.begin();
|
||||
pred_set_vector::const_iterator send = strats.end();
|
||||
for (; sit!=send; ++sit) {
|
||||
func_decl_set & strat = **sit;
|
||||
func_decl_set::iterator fit=strat.begin();
|
||||
func_decl_set::iterator fend=strat.end();
|
||||
bool non_empty = false;
|
||||
for (; fit!=fend; ++fit) {
|
||||
func_decl * first = *fit;
|
||||
const func_decl_set & deps = m_deps.get_deps(first);
|
||||
func_decl_set::iterator dit=deps.begin();
|
||||
func_decl_set::iterator dend=deps.end();
|
||||
for (; dit!=dend; ++dit) {
|
||||
non_empty = true;
|
||||
func_decl * dep = *dit;
|
||||
out<<first->get_name()<<" -> "<<dep->get_name()<<"\n";
|
||||
}
|
||||
}
|
||||
if (non_empty && sit!=send) {
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// rule_stratifier
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
rule_stratifier::~rule_stratifier() {
|
||||
comp_vector::iterator it = m_strats.begin();
|
||||
comp_vector::iterator end = m_strats.end();
|
||||
for (; it!=end; ++it) {
|
||||
SASSERT(*it);
|
||||
dealloc(*it);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const {
|
||||
unsigned num;
|
||||
if (!m_pred_strat_nums.find(pred, num)) {
|
||||
//the number of the predicate is not stored, therefore it did not appear
|
||||
//in the algorithm and therefore it does not depend on anything and nothing
|
||||
//depends on it. So it is safe to assign zero strate to it, although it is
|
||||
//not strictly true.
|
||||
num = 0;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
void rule_stratifier::traverse(T* el) {
|
||||
unsigned p_num;
|
||||
if (m_preorder_nums.find(el, p_num)) {
|
||||
if (p_num < m_first_preorder) {
|
||||
//traversed in a previous sweep
|
||||
return;
|
||||
}
|
||||
if (m_component_nums.contains(el)) {
|
||||
//we already assigned a component for el
|
||||
return;
|
||||
}
|
||||
while (!m_stack_P.empty()) {
|
||||
unsigned on_stack_num;
|
||||
VERIFY( m_preorder_nums.find(m_stack_P.back(), on_stack_num) );
|
||||
if (on_stack_num <= p_num) {
|
||||
break;
|
||||
}
|
||||
m_stack_P.pop_back();
|
||||
}
|
||||
}
|
||||
else {
|
||||
p_num=m_next_preorder++;
|
||||
m_preorder_nums.insert(el, p_num);
|
||||
|
||||
m_stack_S.push_back(el);
|
||||
m_stack_P.push_back(el);
|
||||
|
||||
const item_set & children = m_deps.get_deps(el);
|
||||
item_set::iterator cit=children.begin();
|
||||
item_set::iterator cend=children.end();
|
||||
for (; cit!=cend; ++cit) {
|
||||
traverse(*cit);
|
||||
}
|
||||
|
||||
if (el == m_stack_P.back()) {
|
||||
unsigned comp_num = m_components.size();
|
||||
item_set * new_comp = alloc(item_set);
|
||||
m_components.push_back(new_comp);
|
||||
|
||||
T* s_el;
|
||||
do {
|
||||
s_el=m_stack_S.back();
|
||||
m_stack_S.pop_back();
|
||||
new_comp->insert(s_el);
|
||||
m_component_nums.insert(s_el, comp_num);
|
||||
} while (s_el!=el);
|
||||
m_stack_P.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rule_stratifier::process() {
|
||||
if (m_deps.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//detect strong components
|
||||
rule_dependencies::iterator it = m_deps.begin();
|
||||
rule_dependencies::iterator end = m_deps.end();
|
||||
for (; it!=end; ++it) {
|
||||
T * el = it->m_key;
|
||||
//we take a note of the preorder number with which this sweep started
|
||||
m_first_preorder = m_next_preorder;
|
||||
traverse(el);
|
||||
}
|
||||
|
||||
//do topological sorting
|
||||
|
||||
//degres of components (number of inter-component edges ending up in the component)
|
||||
svector<unsigned> in_degrees;
|
||||
in_degrees.resize(m_components.size());
|
||||
|
||||
//init in_degrees
|
||||
it = m_deps.begin();
|
||||
end = m_deps.end();
|
||||
for (; it != end; ++it) {
|
||||
T * el = it->m_key;
|
||||
item_set * out_edges = it->m_value;
|
||||
|
||||
unsigned el_comp;
|
||||
VERIFY( m_component_nums.find(el, el_comp) );
|
||||
|
||||
item_set::iterator eit = out_edges->begin();
|
||||
item_set::iterator eend = out_edges->end();
|
||||
for (; eit!=eend; ++eit) {
|
||||
T * tgt = *eit;
|
||||
|
||||
unsigned tgt_comp = m_component_nums.find(tgt);
|
||||
|
||||
if (el_comp != tgt_comp) {
|
||||
in_degrees[tgt_comp]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We put components whose indegree is zero to m_strats and assign its
|
||||
// m_components entry to zero.
|
||||
unsigned comp_cnt = m_components.size();
|
||||
for (unsigned i = 0; i < comp_cnt; i++) {
|
||||
if (in_degrees[i] == 0) {
|
||||
m_strats.push_back(m_components[i]);
|
||||
m_components[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(!m_strats.empty()); //the component graph is acyclic and non-empty
|
||||
|
||||
//we remove edges from components with zero indegre building the topological ordering
|
||||
unsigned strats_index = 0;
|
||||
while (strats_index < m_strats.size()) { //m_strats.size() changes inside the loop!
|
||||
item_set * comp = m_strats[strats_index];
|
||||
item_set::iterator cit=comp->begin();
|
||||
item_set::iterator cend=comp->end();
|
||||
for (; cit!=cend; ++cit) {
|
||||
T * el = *cit;
|
||||
const item_set & deps = m_deps.get_deps(el);
|
||||
item_set::iterator eit=deps.begin();
|
||||
item_set::iterator eend=deps.end();
|
||||
for (; eit!=eend; ++eit) {
|
||||
T * tgt = *eit;
|
||||
unsigned tgt_comp;
|
||||
VERIFY( m_component_nums.find(tgt, tgt_comp) );
|
||||
|
||||
//m_components[tgt_comp]==0 means the edge is intra-component.
|
||||
//Otherwise it would go to another component, but it is not possible, since
|
||||
//as m_components[tgt_comp]==0, its indegree has already reached zero.
|
||||
if (m_components[tgt_comp]) {
|
||||
SASSERT(in_degrees[tgt_comp]>0);
|
||||
in_degrees[tgt_comp]--;
|
||||
if (in_degrees[tgt_comp]==0) {
|
||||
m_strats.push_back(m_components[tgt_comp]);
|
||||
m_components[tgt_comp] = 0;
|
||||
}
|
||||
}
|
||||
traverse(*cit);
|
||||
}
|
||||
}
|
||||
strats_index++;
|
||||
}
|
||||
//we have managed to topologicaly order all the components
|
||||
SASSERT(std::find_if(m_components.begin(), m_components.end(),
|
||||
std::bind1st(std::not_equal_to<item_set*>(), (item_set*)0)) == m_components.end());
|
||||
|
||||
//reverse the strats array, so that the only the later components would depend on earlier ones
|
||||
std::reverse(m_strats.begin(), m_strats.end());
|
||||
|
||||
SASSERT(m_pred_strat_nums.empty());
|
||||
unsigned strat_cnt = m_strats.size();
|
||||
for (unsigned strat_index=0; strat_index<strat_cnt; strat_index++) {
|
||||
item_set * comp = m_strats[strat_index];
|
||||
item_set::iterator cit=comp->begin();
|
||||
item_set::iterator cend=comp->end();
|
||||
for (; cit != cend; ++cit) {
|
||||
T * el = *cit;
|
||||
m_pred_strat_nums.insert(el, strat_index);
|
||||
}
|
||||
}
|
||||
|
||||
//finalize structures that are not needed anymore
|
||||
m_preorder_nums.finalize();
|
||||
m_stack_S.finalize();
|
||||
m_stack_P.finalize();
|
||||
m_component_nums.finalize();
|
||||
m_components.finalize();
|
||||
|
||||
}
|
||||
|
||||
void rule_stratifier::display(std::ostream& out) const {
|
||||
m_deps.display(out << "dependencies\n");
|
||||
out << "strata\n";
|
||||
for (unsigned i = 0; i < m_strats.size(); ++i) {
|
||||
item_set::iterator it = m_strats[i]->begin();
|
||||
item_set::iterator end = m_strats[i]->end();
|
||||
for (; it != end; ++it) {
|
||||
out << (*it)->get_name() << " ";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
282
src/muz/base/dl_rule_set.h
Normal file
282
src/muz/base/dl_rule_set.h
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rule_set.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2010-05-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_RULE_SET_H_
|
||||
#define _DL_RULE_SET_H_
|
||||
|
||||
#include"obj_hashtable.h"
|
||||
#include"dl_rule.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class rule_set;
|
||||
|
||||
class rule_dependencies {
|
||||
public:
|
||||
typedef obj_hashtable<func_decl> item_set;
|
||||
typedef obj_map<func_decl, item_set * > deps_type;
|
||||
|
||||
typedef deps_type::iterator iterator;
|
||||
private:
|
||||
/**
|
||||
Map (dependent -> set of master objects)
|
||||
|
||||
Each master object is also present as a key of the map, even if its master set
|
||||
is empty.
|
||||
*/
|
||||
deps_type m_data;
|
||||
context & m_context;
|
||||
ptr_vector<expr> m_todo;
|
||||
ast_mark m_visited;
|
||||
|
||||
|
||||
//we need to take care with removing to avoid memory leaks
|
||||
void remove_m_data_entry(func_decl * key);
|
||||
|
||||
//sometimes we need to return reference to an empty set,
|
||||
//so we return reference to this one.
|
||||
item_set m_empty_item_set;
|
||||
|
||||
item_set & ensure_key(func_decl * pred);
|
||||
void insert(func_decl * depending, func_decl * master);
|
||||
void populate(rule const* r);
|
||||
public:
|
||||
rule_dependencies(context& ctx);
|
||||
rule_dependencies(const rule_dependencies & o, bool reversed = false);
|
||||
~rule_dependencies();
|
||||
void reset();
|
||||
|
||||
void populate(const rule_set & rules);
|
||||
void populate(unsigned n, rule * const * rules);
|
||||
void restrict(const item_set & allowed);
|
||||
void remove(func_decl * itm);
|
||||
void remove(const item_set & to_remove);
|
||||
|
||||
bool empty() const { return m_data.empty(); }
|
||||
const item_set & get_deps(func_decl * f) const;
|
||||
/**
|
||||
\brief Number of predicates \c f depends on.
|
||||
*/
|
||||
unsigned in_degree(func_decl * f) const { return get_deps(f).size(); }
|
||||
/**
|
||||
\brief Number of predicates that depend on \c f.
|
||||
*/
|
||||
unsigned out_degree(func_decl * f) const;
|
||||
|
||||
/**
|
||||
\brief If the rependency graph is acyclic, put all elements into \c res
|
||||
ordered so that elements can depend only on elements that are before them.
|
||||
If the graph is not acyclic, return false.
|
||||
*/
|
||||
bool sort_deps(ptr_vector<func_decl> & res);
|
||||
|
||||
iterator begin() const { return m_data.begin(); }
|
||||
iterator end() const { return m_data.end(); }
|
||||
|
||||
void display( std::ostream & out ) const;
|
||||
};
|
||||
|
||||
class rule_stratifier {
|
||||
public:
|
||||
typedef func_decl T;
|
||||
typedef obj_hashtable<T> item_set;
|
||||
typedef ptr_vector<item_set> comp_vector;
|
||||
typedef obj_map<T, item_set *> deps_type;
|
||||
private:
|
||||
|
||||
const rule_dependencies & m_deps;
|
||||
comp_vector m_strats;
|
||||
|
||||
obj_map<T, unsigned> m_preorder_nums;
|
||||
ptr_vector<T> m_stack_S;
|
||||
ptr_vector<T> m_stack_P;
|
||||
|
||||
obj_map<T, unsigned> m_component_nums;
|
||||
comp_vector m_components;
|
||||
obj_map<T, unsigned> m_pred_strat_nums;
|
||||
|
||||
unsigned m_next_preorder;
|
||||
unsigned m_first_preorder;
|
||||
|
||||
/**
|
||||
Finds strongly connected components using the Gabow's algorithm.
|
||||
*/
|
||||
void traverse(T* el);
|
||||
|
||||
/**
|
||||
Calls \c traverse to identify strognly connected components and then
|
||||
orders them using topological sorting.
|
||||
*/
|
||||
void process();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
\remark The \c stratifier object keeps a reference to the \c deps object, so
|
||||
it must exist for the whole lifetime of the \c stratifier object.
|
||||
*/
|
||||
rule_stratifier(const rule_dependencies & deps)
|
||||
: m_deps(deps), m_next_preorder(0)
|
||||
{
|
||||
process();
|
||||
}
|
||||
|
||||
~rule_stratifier();
|
||||
|
||||
/**
|
||||
Return vector of components ordered so that the only dependencies are from
|
||||
later components to earlier.
|
||||
*/
|
||||
const comp_vector & get_strats() const { return m_strats; }
|
||||
|
||||
unsigned get_predicate_strat(func_decl * pred) const;
|
||||
|
||||
void display( std::ostream & out ) const;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Datalog "Program" (aka rule set).
|
||||
*/
|
||||
class rule_set {
|
||||
friend class rule_dependencies;
|
||||
public:
|
||||
typedef ptr_vector<func_decl_set> pred_set_vector;
|
||||
typedef obj_map<func_decl, rule_vector*> decl2rules;
|
||||
private:
|
||||
typedef obj_map<func_decl, func_decl_set*> decl2deps;
|
||||
|
||||
context & m_context;
|
||||
rule_manager & m_rule_manager;
|
||||
rule_ref_vector m_rules; //!< all rules
|
||||
decl2rules m_head2rules; //!< mapping from head symbol to rules.
|
||||
rule_dependencies m_deps; //!< dependencies
|
||||
scoped_ptr<rule_stratifier> m_stratifier; //!< contains stratifier object iff the rule_set is closed
|
||||
func_decl_set m_output_preds; //!< output predicates
|
||||
obj_map<func_decl,func_decl*> m_orig2pred;
|
||||
obj_map<func_decl,func_decl*> m_pred2orig;
|
||||
func_decl_ref_vector m_refs;
|
||||
|
||||
|
||||
//sometimes we need to return reference to an empty rule_vector,
|
||||
//so we return reference to this one.
|
||||
rule_vector m_empty_rule_vector;
|
||||
|
||||
void compute_deps();
|
||||
void compute_tc_deps();
|
||||
bool stratified_negation();
|
||||
public:
|
||||
rule_set(context & ctx);
|
||||
rule_set(const rule_set & rs);
|
||||
~rule_set();
|
||||
|
||||
ast_manager & get_manager() const;
|
||||
rule_manager & get_rule_manager() const { return const_cast<rule_manager&>(m_rule_manager); }
|
||||
context& get_context() const { return m_context; }
|
||||
|
||||
|
||||
void inherit_predicates(rule_set const& other);
|
||||
void inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred);
|
||||
func_decl* get_orig(func_decl* pred) const;
|
||||
func_decl* get_pred(func_decl* orig) const;
|
||||
|
||||
/**
|
||||
\brief Add rule \c r to the rule set.
|
||||
*/
|
||||
void add_rule(rule * r);
|
||||
|
||||
/**
|
||||
\brief Remove rule \c r from the rule set.
|
||||
*/
|
||||
void del_rule(rule * r);
|
||||
|
||||
/**
|
||||
\brief Add all rules from a different rule_set.
|
||||
*/
|
||||
void add_rules(const rule_set& src);
|
||||
void replace_rules(const rule_set& other);
|
||||
|
||||
/**
|
||||
\brief This method should be invoked after all rules are added to the rule set.
|
||||
It will check if the negation is indeed stratified.
|
||||
Return true if succeeded.
|
||||
|
||||
\remark If new rules are added, the rule_set will be "reopen".
|
||||
*/
|
||||
bool close();
|
||||
void ensure_closed();
|
||||
/**
|
||||
\brief Undo the effect of the \c close() operation.
|
||||
*/
|
||||
void reopen();
|
||||
bool is_closed() const { return m_stratifier != 0; }
|
||||
|
||||
unsigned get_num_rules() const { return m_rules.size(); }
|
||||
bool empty() const { return m_rules.size() == 0; }
|
||||
|
||||
rule * get_rule(unsigned i) const { return m_rules[i]; }
|
||||
rule * last() const { return m_rules[m_rules.size()-1]; }
|
||||
rule_ref_vector const& get_rules() const { return m_rules; }
|
||||
|
||||
const rule_vector & get_predicate_rules(func_decl * pred) const;
|
||||
bool contains(func_decl* pred) const { return m_head2rules.contains(pred); }
|
||||
|
||||
const rule_stratifier & get_stratifier() const {
|
||||
SASSERT(m_stratifier);
|
||||
return *m_stratifier;
|
||||
}
|
||||
const pred_set_vector & get_strats() const;
|
||||
unsigned get_predicate_strat(func_decl * pred) const;
|
||||
const rule_dependencies & get_dependencies() const { SASSERT(is_closed()); return m_deps; }
|
||||
|
||||
// split predicats into founded and non-founded.
|
||||
void split_founded_rules(func_decl_set& founded, func_decl_set& non_founded);
|
||||
|
||||
void reset();
|
||||
|
||||
void set_output_predicate(func_decl * pred) { m_refs.push_back(pred); m_output_preds.insert(pred); }
|
||||
bool is_output_predicate(func_decl * pred) const { return m_output_preds.contains(pred); }
|
||||
const func_decl_set & get_output_predicates() const { return m_output_preds; }
|
||||
func_decl* get_output_predicate() const { SASSERT(m_output_preds.size() == 1); return *m_output_preds.begin(); }
|
||||
|
||||
|
||||
void display(std::ostream & out) const;
|
||||
|
||||
/**
|
||||
\brief Output rule dependencies.
|
||||
|
||||
The rule set must be closed before calling this function.
|
||||
*/
|
||||
void display_deps(std::ostream & out) const;
|
||||
|
||||
typedef rule * const * iterator;
|
||||
iterator begin() const { return m_rules.c_ptr(); }
|
||||
iterator end() const { return m_rules.c_ptr()+m_rules.size(); }
|
||||
|
||||
decl2rules::iterator begin_grouped_rules() const { return m_head2rules.begin(); }
|
||||
decl2rules::iterator end_grouped_rules() const { return m_head2rules.end(); }
|
||||
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; }
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_RULE_SET_H_ */
|
||||
|
84
src/muz/base/dl_rule_subsumption_index.cpp
Normal file
84
src/muz/base/dl_rule_subsumption_index.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rule_subsumption_index.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Subsumption index for rules.
|
||||
Currently an underapproximation (fails to identify some subsumptions).
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-10-10.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include "ast_pp.h"
|
||||
|
||||
#include "dl_rule_subsumption_index.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// rule_subsumption_index
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
|
||||
void rule_subsumption_index::handle_unconditioned_rule(rule * r) {
|
||||
SASSERT(r->get_tail_size()==0);
|
||||
app * head = r->get_head();
|
||||
func_decl * pred = head->get_decl();
|
||||
|
||||
app_set * head_set;
|
||||
if(!m_unconditioned_heads.find(pred, head_set)) {
|
||||
head_set = alloc(app_set);
|
||||
m_unconditioned_heads.insert(pred, head_set);
|
||||
}
|
||||
head_set->insert(head);
|
||||
}
|
||||
|
||||
void rule_subsumption_index::add(rule * r) {
|
||||
m_ref_holder.push_back(r);
|
||||
if(r->get_tail_size()==0) {
|
||||
handle_unconditioned_rule(r);
|
||||
}
|
||||
m_rule_set.insert(r);
|
||||
}
|
||||
|
||||
bool rule_subsumption_index::is_subsumed(app * query) {
|
||||
func_decl * pred = query->get_decl();
|
||||
|
||||
app_set * head_set;
|
||||
if(m_unconditioned_heads.find(pred, head_set)) {
|
||||
if(head_set->contains(query)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rule_subsumption_index::is_subsumed(rule * r) {
|
||||
|
||||
app * head = r->get_head();
|
||||
if(is_subsumed(head)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(m_rule_set.contains(r)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
65
src/muz/base/dl_rule_subsumption_index.h
Normal file
65
src/muz/base/dl_rule_subsumption_index.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rule_subsumption_index.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subsumption index for rules.
|
||||
Currently an underapproximation (fails to identify some subsumptions).
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-10-10.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _DL_RULE_SUBSUMPTION_INDEX_H_
|
||||
#define _DL_RULE_SUBSUMPTION_INDEX_H_
|
||||
|
||||
#include "dl_context.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class rule_subsumption_index {
|
||||
//private and undefined copy constroctor
|
||||
rule_subsumption_index(rule_subsumption_index const&);
|
||||
//private and undefined operator=
|
||||
rule_subsumption_index& operator=(rule_subsumption_index const&);
|
||||
|
||||
typedef obj_hashtable<app> app_set;
|
||||
|
||||
ast_manager & m;
|
||||
context & m_context;
|
||||
|
||||
rule_ref_vector m_ref_holder;
|
||||
|
||||
obj_map<func_decl, app_set *> m_unconditioned_heads;
|
||||
|
||||
hashtable<rule *, rule_hash_proc, rule_eq_proc> m_rule_set;
|
||||
|
||||
void handle_unconditioned_rule(rule * r);
|
||||
|
||||
public:
|
||||
rule_subsumption_index(context & ctx) :
|
||||
m(ctx.get_manager()),
|
||||
m_context(ctx),
|
||||
m_ref_holder(ctx.get_rule_manager()) {}
|
||||
|
||||
~rule_subsumption_index() {
|
||||
reset_dealloc_values(m_unconditioned_heads);
|
||||
}
|
||||
|
||||
void add(rule * r);
|
||||
bool is_subsumed(rule * r);
|
||||
bool is_subsumed(app * query);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_RULE_SUBSUMPTION_INDEX_H_ */
|
||||
|
159
src/muz/base/dl_rule_transformer.cpp
Normal file
159
src/muz/base/dl_rule_transformer.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rule_transformer.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <algorithm>
|
||||
#include<typeinfo>
|
||||
|
||||
#include"dl_context.h"
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"stopwatch.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
rule_transformer::rule_transformer(context & ctx)
|
||||
: m_context(ctx), m_rule_manager(m_context.get_rule_manager()), m_dirty(false) {
|
||||
}
|
||||
|
||||
|
||||
rule_transformer::~rule_transformer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void rule_transformer::reset() {
|
||||
plugin_vector::iterator it = m_plugins.begin();
|
||||
plugin_vector::iterator end = m_plugins.end();
|
||||
for(; it!=end; ++it) {
|
||||
dealloc(*it);
|
||||
}
|
||||
m_plugins.reset();
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
void rule_transformer::cancel() {
|
||||
plugin_vector::iterator it = m_plugins.begin();
|
||||
plugin_vector::iterator end = m_plugins.end();
|
||||
for(; it!=end; ++it) {
|
||||
(*it)->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
struct rule_transformer::plugin_comparator {
|
||||
bool operator()(rule_transformer::plugin * p1, rule_transformer::plugin * p2) {
|
||||
return p1->get_priority() > p2->get_priority();
|
||||
}
|
||||
};
|
||||
|
||||
void rule_transformer::ensure_ordered() {
|
||||
if (m_dirty) {
|
||||
std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator());
|
||||
m_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void rule_transformer::register_plugin(plugin * p) {
|
||||
m_plugins.push_back(p);
|
||||
p->attach(*this);
|
||||
m_dirty=true;
|
||||
}
|
||||
|
||||
bool rule_transformer::operator()(rule_set & rules) {
|
||||
ensure_ordered();
|
||||
|
||||
bool modified = false;
|
||||
|
||||
TRACE("dl_rule_transf",
|
||||
tout<<"init:\n";
|
||||
rules.display(tout);
|
||||
);
|
||||
rule_set* new_rules = alloc(rule_set, rules);
|
||||
plugin_vector::iterator it = m_plugins.begin();
|
||||
plugin_vector::iterator end = m_plugins.end();
|
||||
for(; it!=end && !m_context.canceled(); ++it) {
|
||||
plugin & p = **it;
|
||||
|
||||
|
||||
IF_VERBOSE(1, verbose_stream() << "(transform " << typeid(p).name() << "...";);
|
||||
stopwatch sw;
|
||||
sw.start();
|
||||
rule_set * new_rules1 = p(*new_rules);
|
||||
sw.stop();
|
||||
double sec = sw.get_seconds();
|
||||
if (sec < 0.001) sec = 0.0;
|
||||
if (!new_rules1) {
|
||||
IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";);
|
||||
continue;
|
||||
}
|
||||
if (p.can_destratify_negation() &&
|
||||
!new_rules1->is_closed() &&
|
||||
!new_rules1->close()) {
|
||||
warning_msg("a rule transformation skipped "
|
||||
"because it destratified negation");
|
||||
dealloc(new_rules1);
|
||||
IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";);
|
||||
continue;
|
||||
}
|
||||
modified = true;
|
||||
dealloc(new_rules);
|
||||
new_rules = new_rules1;
|
||||
new_rules->ensure_closed();
|
||||
|
||||
IF_VERBOSE(1, verbose_stream() << new_rules->get_num_rules() << " rules " << sec << "s)\n";);
|
||||
TRACE("dl_rule_transf",
|
||||
tout << typeid(p).name()<<":\n";
|
||||
new_rules->display(tout);
|
||||
);
|
||||
}
|
||||
if (modified) {
|
||||
rules.replace_rules(*new_rules);
|
||||
}
|
||||
dealloc(new_rules);
|
||||
return modified;
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
//
|
||||
//rule_transformer::plugin
|
||||
//
|
||||
//------------------------------
|
||||
|
||||
void rule_transformer::plugin::remove_duplicate_tails(app_ref_vector& tail, svector<bool>& tail_neg)
|
||||
{
|
||||
//one set for positive and one for negative
|
||||
obj_hashtable<app> tail_apps[2];
|
||||
unsigned i=0;
|
||||
while(i<tail.size()) {
|
||||
unsigned set_idx = tail_neg[i] ? 1 : 0;
|
||||
if (tail_apps[set_idx].contains(tail[i].get())) {
|
||||
if (i!=tail.size()-1) {
|
||||
tail[i] = tail.back();
|
||||
tail_neg[i] = tail_neg.back();
|
||||
}
|
||||
tail.pop_back();
|
||||
tail_neg.pop_back();
|
||||
}
|
||||
else {
|
||||
tail_apps[set_idx].insert(tail[i].get());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
117
src/muz/base/dl_rule_transformer.h
Normal file
117
src/muz/base/dl_rule_transformer.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_rule_transformer.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_RULE_TRANSFORMER_H_
|
||||
#define _DL_RULE_TRANSFORMER_H_
|
||||
|
||||
#include"map.h"
|
||||
#include"vector.h"
|
||||
#include"dl_rule.h"
|
||||
#include"dl_rule_set.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class context;
|
||||
|
||||
class rule_transformer {
|
||||
public:
|
||||
class plugin;
|
||||
private:
|
||||
friend class plugin;
|
||||
|
||||
typedef svector<plugin*> plugin_vector;
|
||||
struct plugin_comparator;
|
||||
|
||||
context & m_context;
|
||||
rule_manager & m_rule_manager;
|
||||
bool m_dirty;
|
||||
svector<plugin*> m_plugins;
|
||||
|
||||
void ensure_ordered();
|
||||
public:
|
||||
|
||||
rule_transformer(context & ctx);
|
||||
~rule_transformer();
|
||||
|
||||
/**
|
||||
\brief Reset all registered transformers.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
\brief Add a plugin for rule transformation.
|
||||
|
||||
The rule_transformer object takes ownership of the plugin object.
|
||||
*/
|
||||
void register_plugin(plugin * p);
|
||||
|
||||
/**
|
||||
\brief Transform the rule set using the registered transformation plugins. If the rule
|
||||
set has changed, return true; otherwise return false.
|
||||
*/
|
||||
bool operator()(rule_set & rules);
|
||||
};
|
||||
|
||||
class rule_transformer::plugin {
|
||||
friend class rule_transformer;
|
||||
|
||||
unsigned m_priority;
|
||||
bool m_can_destratify_negation;
|
||||
rule_transformer * m_transformer;
|
||||
|
||||
void attach(rule_transformer & transformer) { m_transformer = &transformer; }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
\brief Create a plugin object for rule_transformer.
|
||||
|
||||
The priority argument determines in what order will rules be applied to the rules
|
||||
(higher priority plugins will be applied first).
|
||||
*/
|
||||
plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority),
|
||||
m_can_destratify_negation(can_destratify_negation), m_transformer(0) {}
|
||||
|
||||
public:
|
||||
virtual ~plugin() {}
|
||||
|
||||
unsigned get_priority() { return m_priority; }
|
||||
bool can_destratify_negation() const { return m_can_destratify_negation; }
|
||||
|
||||
virtual void cancel() {}
|
||||
|
||||
/**
|
||||
\brief Return \c rule_set object with containing transformed rules or 0 if no
|
||||
transformation was done.
|
||||
|
||||
The caller takes ownership of the returned \c rule_set object.
|
||||
*/
|
||||
virtual rule_set * operator()(rule_set const & source) = 0;
|
||||
|
||||
/**
|
||||
Removes duplicate tails.
|
||||
*/
|
||||
static void remove_duplicate_tails(app_ref_vector& tail, svector<bool>& tail_neg);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* _DL_RULE_TRANSFORMER_H_ */
|
||||
|
646
src/muz/base/dl_util.cpp
Normal file
646
src/muz/base/dl_util.cpp
Normal 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
751
src/muz/base/dl_util.h
Normal 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_ */
|
||||
|
74
src/muz/base/fixedpoint_params.pyg
Normal file
74
src/muz/base/fixedpoint_params.pyg
Normal 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
501
src/muz/base/hnf.cpp
Normal file
|
@ -0,0 +1,501 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
hnf.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Horn normal form conversion.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 3-20-2013
|
||||
|
||||
Notes:
|
||||
|
||||
Convert formula
|
||||
|
||||
(forall x f(x))
|
||||
|
||||
into conjunction
|
||||
|
||||
(f1 xy) (f2 xy) (f3 xy)
|
||||
|
||||
such that
|
||||
|
||||
(forall x f(x)) ~ /\ (forall xy (f_i xy))
|
||||
|
||||
modulo definitions that are introduced.
|
||||
|
||||
|
||||
Convert proof with
|
||||
asserted (forall xy (f' xy))
|
||||
|
||||
To:
|
||||
(forall xy (f' xy)) by mp~ 1, 2
|
||||
1. asserted/def-intro (forall xy (f xy))
|
||||
2. (forall xy (f xy)) ~ (forall xy (f' xy)) by trans, 3, 4
|
||||
3. (forall xy (f xy)) ~ (forall xy (f1 xy)) by pull quantifiers (rewrite)
|
||||
4. (forall xy (f1 xy)) ~ (forall xy (f' xy)) by oeq_quant_intro 5
|
||||
5. f1 xy ~ f' xy by sub-proof.
|
||||
|
||||
|
||||
--*/
|
||||
#include"hnf.h"
|
||||
#include"warning.h"
|
||||
#include"used_vars.h"
|
||||
#include"well_sorted.h"
|
||||
#include"var_subst.h"
|
||||
#include"name_exprs.h"
|
||||
#include"act_cache.h"
|
||||
#include"cooperate.h"
|
||||
#include"ast_pp.h"
|
||||
#include"quant_hoist.h"
|
||||
#include"dl_util.h"
|
||||
#include"for_each_ast.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
class hnf::imp {
|
||||
ast_manager& m;
|
||||
bool m_produce_proofs;
|
||||
volatile bool m_cancel;
|
||||
expr_ref_vector m_todo;
|
||||
proof_ref_vector m_proofs;
|
||||
expr_ref_vector m_refs;
|
||||
symbol m_name;
|
||||
svector<symbol> m_names;
|
||||
ptr_vector<sort> m_sorts;
|
||||
quantifier_hoister m_qh;
|
||||
obj_map<expr, app*> m_memoize_disj;
|
||||
obj_map<expr, proof*> m_memoize_proof;
|
||||
func_decl_ref_vector m_fresh_predicates;
|
||||
expr_ref_vector m_body;
|
||||
proof_ref_vector m_defs;
|
||||
|
||||
|
||||
public:
|
||||
imp(ast_manager & m):
|
||||
m(m),
|
||||
m_produce_proofs(false),
|
||||
m_cancel(false),
|
||||
m_todo(m),
|
||||
m_proofs(m),
|
||||
m_refs(m),
|
||||
m_name("P"),
|
||||
m_qh(m),
|
||||
m_fresh_predicates(m),
|
||||
m_body(m),
|
||||
m_defs(m) {
|
||||
}
|
||||
|
||||
void operator()(expr * n,
|
||||
proof* p,
|
||||
expr_ref_vector& result,
|
||||
proof_ref_vector& ps) {
|
||||
expr_ref fml(m);
|
||||
proof_ref pr(m);
|
||||
m_todo.reset();
|
||||
m_proofs.reset();
|
||||
m_refs.reset();
|
||||
m_memoize_disj.reset();
|
||||
m_memoize_proof.reset();
|
||||
m_fresh_predicates.reset();
|
||||
m_todo.push_back(n);
|
||||
m_proofs.push_back(p);
|
||||
m_produce_proofs = p != 0;
|
||||
while (!m_todo.empty() && !m_cancel) {
|
||||
fml = m_todo.back();
|
||||
pr = m_proofs.back();
|
||||
m_todo.pop_back();
|
||||
m_proofs.pop_back();
|
||||
mk_horn(fml, pr);
|
||||
if (fml) {
|
||||
result.push_back(fml);
|
||||
ps.push_back(pr);
|
||||
}
|
||||
}
|
||||
TRACE("hnf",
|
||||
tout << mk_pp(n, m) << "\n==>\n";
|
||||
for (unsigned i = 0; i < result.size(); ++i) {
|
||||
tout << mk_pp(result[i].get(), m) << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
void set_name(symbol const& n) {
|
||||
if (n == symbol::null) {
|
||||
m_name = symbol("P");
|
||||
}
|
||||
else {
|
||||
m_name = n;
|
||||
}
|
||||
}
|
||||
|
||||
func_decl_ref_vector const& get_fresh_predicates() {
|
||||
return m_fresh_predicates;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_cancel = false;
|
||||
m_todo.reset();
|
||||
m_proofs.reset();
|
||||
m_refs.reset();
|
||||
m_memoize_disj.reset();
|
||||
m_memoize_proof.reset();
|
||||
m_fresh_predicates.reset();
|
||||
}
|
||||
|
||||
ast_manager& get_manager() { return m; }
|
||||
|
||||
private:
|
||||
|
||||
bool produce_proofs() const {
|
||||
return m_produce_proofs;
|
||||
}
|
||||
|
||||
bool is_predicate(expr* p) const {
|
||||
return is_app(p) && is_predicate(to_app(p)->get_decl());
|
||||
}
|
||||
|
||||
bool is_predicate(func_decl* f) const {
|
||||
return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id;
|
||||
}
|
||||
|
||||
class contains_predicate_proc {
|
||||
imp const& m;
|
||||
public:
|
||||
struct found {};
|
||||
contains_predicate_proc(imp const& m): m(m) {}
|
||||
void operator()(var * n) {}
|
||||
void operator()(quantifier * n) {}
|
||||
void operator()(app* n) {
|
||||
if (m.is_predicate(n)) throw found();
|
||||
}
|
||||
};
|
||||
|
||||
bool contains_predicate(expr* fml) const {
|
||||
contains_predicate_proc proc(*this);
|
||||
try {
|
||||
quick_for_each_expr(proc, fml);
|
||||
}
|
||||
catch (contains_predicate_proc::found) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void mk_horn(expr_ref& fml, proof_ref& premise) {
|
||||
SASSERT(!premise || fml == m.get_fact(premise));
|
||||
expr* e1, *e2;
|
||||
expr_ref fml0(m), fml1(m), fml2(m), head(m);
|
||||
proof_ref p(m);
|
||||
fml0 = fml;
|
||||
m_names.reset();
|
||||
m_sorts.reset();
|
||||
m_body.reset();
|
||||
m_defs.reset();
|
||||
m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names);
|
||||
if (premise){
|
||||
fml1 = bind_variables(fml0);
|
||||
if (!m_sorts.empty()) {
|
||||
proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1));
|
||||
premise = mk_modus_ponens(premise, p1);
|
||||
fml = fml1;
|
||||
}
|
||||
}
|
||||
head = fml0;
|
||||
while (m.is_implies(head, e1, e2)) {
|
||||
m_body.push_back(e1);
|
||||
head = e2;
|
||||
}
|
||||
qe::flatten_and(m_body);
|
||||
if (premise) {
|
||||
p = m.mk_rewrite(fml0, mk_implies(m_body, head));
|
||||
}
|
||||
|
||||
//
|
||||
// Case:
|
||||
// A \/ B -> C
|
||||
// =>
|
||||
// A -> C
|
||||
// B -> C
|
||||
//
|
||||
if (m_body.size() == 1 && m.is_or(m_body[0].get()) && contains_predicate(m_body[0].get())) {
|
||||
app* _or = to_app(m_body[0].get());
|
||||
unsigned sz = _or->get_num_args();
|
||||
expr* const* args = _or->get_args();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
m_todo.push_back(bind_variables(m.mk_implies(args[i], head)));
|
||||
m_proofs.push_back(0);
|
||||
}
|
||||
|
||||
if (premise) {
|
||||
expr_ref f1 = bind_variables(mk_implies(m_body, head));
|
||||
expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz);
|
||||
proof_ref p2(m), p3(m);
|
||||
p2 = m.mk_def_axiom(m.mk_iff(f1, f2));
|
||||
p3 = mk_quant_intro(fml, f1, p);
|
||||
p2 = mk_transitivity(p3, p2);
|
||||
p2 = mk_modus_ponens(premise, p2);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
m_proofs[m_proofs.size()-sz+i] = m.mk_and_elim(p2, i);
|
||||
}
|
||||
}
|
||||
fml = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
eliminate_disjunctions(m_body, m_defs);
|
||||
p = mk_congruence(p, m_body, head, m_defs);
|
||||
|
||||
eliminate_quantifier_body(m_body, m_defs);
|
||||
p = mk_congruence(p, m_body, head, m_defs);
|
||||
|
||||
fml2 = mk_implies(m_body, head);
|
||||
|
||||
fml = bind_variables(fml2);
|
||||
|
||||
if (premise) {
|
||||
SASSERT(p);
|
||||
p = mk_quant_intro(fml1, fml, p);
|
||||
premise = mk_modus_ponens(premise, p);
|
||||
}
|
||||
}
|
||||
|
||||
proof* mk_quant_intro(expr* e1, expr* e2, proof* p) {
|
||||
if (m_sorts.empty()) {
|
||||
return p;
|
||||
}
|
||||
quantifier* q1 = to_quantifier(e1);
|
||||
quantifier* q2 = to_quantifier(e2);
|
||||
if (m.is_iff(m.get_fact(p))) {
|
||||
return m.mk_quant_intro(q1, q2, p);
|
||||
}
|
||||
if (m.is_oeq(m.get_fact(p))) {
|
||||
return m.mk_oeq_quant_intro(q1, q2, p);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void eliminate_disjunctions(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) {
|
||||
expr* b = body.get();
|
||||
expr* e1;
|
||||
bool negate_args = false;
|
||||
bool is_disj = false;
|
||||
unsigned num_disj = 0;
|
||||
expr* const* disjs = 0;
|
||||
if (!contains_predicate(b)) {
|
||||
return;
|
||||
}
|
||||
TRACE("hnf", tout << mk_pp(b, m) << "\n";);
|
||||
if (m.is_or(b)) {
|
||||
is_disj = true;
|
||||
negate_args = false;
|
||||
num_disj = to_app(b)->get_num_args();
|
||||
disjs = to_app(b)->get_args();
|
||||
}
|
||||
if (m.is_not(b, e1) && m.is_and(e1)) {
|
||||
is_disj = true;
|
||||
negate_args = true;
|
||||
num_disj = to_app(e1)->get_num_args();
|
||||
disjs = to_app(e1)->get_args();
|
||||
}
|
||||
if (is_disj) {
|
||||
app* old_head = 0;
|
||||
if (m_memoize_disj.find(b, old_head)) {
|
||||
body = old_head;
|
||||
}
|
||||
else {
|
||||
app_ref head = mk_fresh_head(b);
|
||||
proof_ref_vector defs(m);
|
||||
for (unsigned i = 0; i < num_disj; ++i) {
|
||||
expr* e = disjs[i];
|
||||
if (negate_args) {
|
||||
e = m.mk_not(e);
|
||||
}
|
||||
m_todo.push_back(bind_variables(m.mk_implies(e, head)));
|
||||
m_proofs.push_back(0);
|
||||
if (produce_proofs()) {
|
||||
defs.push_back(m.mk_def_intro(m_todo.back()));
|
||||
m_proofs[m_proofs.size()-1] = defs.back();
|
||||
}
|
||||
}
|
||||
if (produce_proofs()) {
|
||||
proof* p = m.mk_apply_defs(body.get(), head, defs.size(), defs.c_ptr());
|
||||
m_refs.push_back(p);
|
||||
m_memoize_proof.insert(b, p);
|
||||
}
|
||||
m_memoize_disj.insert(b, head);
|
||||
m_refs.push_back(b);
|
||||
m_refs.push_back(head);
|
||||
// update the body to be the newly introduced head relation
|
||||
body = head;
|
||||
}
|
||||
|
||||
if (produce_proofs()) {
|
||||
proofs.push_back(m_memoize_proof.find(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app_ref mk_fresh_head(expr* e) {
|
||||
ptr_vector<sort> sorts0, sorts1;
|
||||
get_free_vars(e, sorts0);
|
||||
expr_ref_vector args(m);
|
||||
for (unsigned i = 0; i < sorts0.size(); ++i) {
|
||||
if (sorts0[i]) {
|
||||
args.push_back(m.mk_var(i, sorts0[i]));
|
||||
sorts1.push_back(sorts0[i]);
|
||||
}
|
||||
}
|
||||
func_decl_ref f(m);
|
||||
f = m.mk_fresh_func_decl(m_name.str().c_str(), "", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort());
|
||||
m_fresh_predicates.push_back(f);
|
||||
return app_ref(m.mk_app(f, args.size(), args.c_ptr()), m);
|
||||
}
|
||||
|
||||
void eliminate_disjunctions(expr_ref_vector& body, proof_ref_vector& proofs) {
|
||||
for (unsigned i = 0; i < body.size(); ++i) {
|
||||
expr_ref_vector::element_ref r = body[i];
|
||||
eliminate_disjunctions(r, proofs);
|
||||
}
|
||||
}
|
||||
|
||||
void eliminate_quantifier_body(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) {
|
||||
if (is_forall(body.get()) && contains_predicate(body.get())) {
|
||||
quantifier* q = to_quantifier(body.get());
|
||||
expr* e = q->get_expr();
|
||||
if (!is_predicate(e)) {
|
||||
app_ref head = mk_fresh_head(e);
|
||||
m_todo.push_back(bind_variables(m.mk_implies(e, head)));
|
||||
m_proofs.push_back(0);
|
||||
body = m.update_quantifier(q, head);
|
||||
if (produce_proofs()) {
|
||||
proof* def_intro = m.mk_def_intro(m_todo.back());
|
||||
proof* def_proof = m.mk_apply_def(e, head, def_intro);
|
||||
proofs.push_back(m.mk_nnf_neg(q, body.get(), 1, &def_proof));
|
||||
m_proofs[m_proofs.size()-1] = def_intro;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void eliminate_quantifier_body(expr_ref_vector& body, proof_ref_vector& proofs) {
|
||||
for (unsigned i = 0; i < body.size(); ++i) {
|
||||
expr_ref_vector::element_ref r = body[i];
|
||||
eliminate_quantifier_body(r, proofs);
|
||||
}
|
||||
}
|
||||
|
||||
app_ref mk_implies(expr_ref_vector const& body, expr* head) {
|
||||
switch (body.size()) {
|
||||
case 0:
|
||||
return app_ref(to_app(head), m);
|
||||
case 1:
|
||||
return app_ref(m.mk_implies(body[0], head), m);
|
||||
default:
|
||||
return app_ref(m.mk_implies(m.mk_and(body.size(), body.c_ptr()), head), m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
proof_ref mk_congruence(proof* p1, expr_ref_vector const& body, expr* head, proof_ref_vector& defs) {
|
||||
if (defs.empty()) {
|
||||
return proof_ref(p1, m);
|
||||
}
|
||||
else {
|
||||
SASSERT(p1);
|
||||
proof_ref p2(m), p3(m);
|
||||
app_ref fml = mk_implies(body, head);
|
||||
expr* fact = m.get_fact(p1);
|
||||
if (m.is_iff(fact)) {
|
||||
p1 = m.mk_iff_oeq(p1);
|
||||
fact = m.get_fact(p1);
|
||||
}
|
||||
VERIFY (m.is_oeq(fact) || m.is_eq(fact));
|
||||
app* e2 = to_app(to_app(fact)->get_arg(1));
|
||||
p2 = m.mk_oeq_congruence(e2, fml, defs.size(), defs.c_ptr());
|
||||
p3 = mk_transitivity(p1, p2);
|
||||
defs.reset();
|
||||
return proof_ref(p3, m);
|
||||
}
|
||||
}
|
||||
|
||||
proof_ref mk_modus_ponens(proof* premise, proof* eq) {
|
||||
proof_ref result(m);
|
||||
result = m.mk_modus_ponens(premise, eq);
|
||||
if (m.get_fact(premise) == m.get_fact(result)) {
|
||||
result = premise;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
proof* mk_transitivity(proof* p1, proof* p2) {
|
||||
if (p1) {
|
||||
app* f = to_app(m.get_fact(p1));
|
||||
if (f->get_arg(0) == f->get_arg(1)) {
|
||||
return p2;
|
||||
}
|
||||
}
|
||||
if (p2) {
|
||||
app* f = to_app(m.get_fact(p2));
|
||||
if (f->get_arg(0) == f->get_arg(1)) {
|
||||
return p1;
|
||||
}
|
||||
}
|
||||
return m.mk_transitivity(p1, p2);
|
||||
}
|
||||
|
||||
expr_ref bind_variables(expr* e) {
|
||||
SASSERT(m_sorts.size() == m_names.size());
|
||||
if (m_sorts.empty()) {
|
||||
return expr_ref(e, m);
|
||||
}
|
||||
return expr_ref(m.mk_forall(m_sorts.size(), m_sorts.c_ptr(), m_names.c_ptr(), e), m);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
hnf::hnf(ast_manager & m) {
|
||||
m_imp = alloc(imp, m);
|
||||
}
|
||||
|
||||
hnf::~hnf() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
void hnf::operator()(expr * n, proof* p, expr_ref_vector & rs, proof_ref_vector& ps) {
|
||||
m_imp->operator()(n, p, rs, ps);
|
||||
TRACE("hnf",
|
||||
ast_manager& m = rs.get_manager();
|
||||
tout << mk_ismt2_pp(n, m) << "\nHNF result:\n";
|
||||
for (unsigned i = 0; i < rs.size(); ++i) {
|
||||
tout << mk_pp(rs[i].get(), m) << "\n";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void hnf::set_cancel(bool f) {
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
|
||||
void hnf::set_name(symbol const& n) {
|
||||
m_imp->set_name(n);
|
||||
}
|
||||
|
||||
void hnf::reset() {
|
||||
m_imp->reset();
|
||||
}
|
||||
|
||||
func_decl_ref_vector const& hnf::get_fresh_predicates() {
|
||||
return m_imp->get_fresh_predicates();
|
||||
}
|
49
src/muz/base/hnf.h
Normal file
49
src/muz/base/hnf.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
hnf.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Horn normal form convertion.
|
||||
Author:
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
Very similar to NNF.
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _HNF_H_
|
||||
#define _HNF_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"params.h"
|
||||
#include"defined_names.h"
|
||||
#include"proof_converter.h"
|
||||
|
||||
class hnf {
|
||||
class imp;
|
||||
imp * m_imp;
|
||||
public:
|
||||
hnf(ast_manager & m);
|
||||
~hnf();
|
||||
|
||||
void operator()(expr * n, // [IN] expression that should be put into Horn NF
|
||||
proof* p, // [IN] proof of n
|
||||
expr_ref_vector & rs, // [OUT] resultant (conjunction) of expressions
|
||||
proof_ref_vector& ps // [OUT] proofs of rs
|
||||
);
|
||||
|
||||
void cancel() { set_cancel(true); }
|
||||
void reset_cancel() { set_cancel(false); }
|
||||
void set_cancel(bool f);
|
||||
void set_name(symbol const& name);
|
||||
void reset();
|
||||
func_decl_ref_vector const& get_fresh_predicates();
|
||||
};
|
||||
|
||||
#endif /* _HNF_H_ */
|
612
src/muz/base/proof_utils.cpp
Normal file
612
src/muz/base/proof_utils.cpp
Normal file
|
@ -0,0 +1,612 @@
|
|||
#include "dl_util.h"
|
||||
#include "proof_utils.h"
|
||||
#include "ast_smt2_pp.h"
|
||||
#include "var_subst.h"
|
||||
|
||||
class reduce_hypotheses {
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
ast_manager& m;
|
||||
expr_ref_vector m_refs;
|
||||
obj_map<proof,proof*> m_cache;
|
||||
obj_map<expr, proof*> m_units;
|
||||
ptr_vector<expr> m_units_trail;
|
||||
unsigned_vector m_limits;
|
||||
obj_map<proof, expr_set*> m_hypmap;
|
||||
ptr_vector<expr_set> m_hyprefs;
|
||||
ptr_vector<expr> m_literals;
|
||||
|
||||
void reset() {
|
||||
m_refs.reset();
|
||||
m_cache.reset();
|
||||
m_units.reset();
|
||||
m_units_trail.reset();
|
||||
m_limits.reset();
|
||||
std::for_each(m_hyprefs.begin(), m_hyprefs.end(), delete_proc<expr_set>());
|
||||
m_hypmap.reset();
|
||||
m_hyprefs.reset();
|
||||
m_literals.reset();
|
||||
}
|
||||
|
||||
void push() {
|
||||
m_limits.push_back(m_units_trail.size());
|
||||
}
|
||||
|
||||
void pop() {
|
||||
unsigned sz = m_limits.back();
|
||||
while (m_units_trail.size() > sz) {
|
||||
m_units.remove(m_units_trail.back());
|
||||
m_units_trail.pop_back();
|
||||
}
|
||||
m_limits.pop_back();
|
||||
}
|
||||
|
||||
void get_literals(expr* clause) {
|
||||
m_literals.reset();
|
||||
if (m.is_or(clause)) {
|
||||
m_literals.append(to_app(clause)->get_num_args(), to_app(clause)->get_args());
|
||||
}
|
||||
else {
|
||||
m_literals.push_back(clause);
|
||||
}
|
||||
}
|
||||
|
||||
void add_hypotheses(proof* p) {
|
||||
expr_set* hyps = 0;
|
||||
bool inherited = false;
|
||||
if (p->get_decl_kind() == PR_HYPOTHESIS) {
|
||||
hyps = alloc(expr_set);
|
||||
hyps->insert(m.get_fact(p));
|
||||
m_hyprefs.push_back(hyps);
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
expr_set* hyps1 = m_hypmap.find(m.get_parent(p, i));
|
||||
if (hyps1) {
|
||||
if (!hyps) {
|
||||
hyps = hyps1;
|
||||
inherited = true;
|
||||
continue;
|
||||
}
|
||||
if (inherited) {
|
||||
hyps = alloc(expr_set,*hyps);
|
||||
m_hyprefs.push_back(hyps);
|
||||
inherited = false;
|
||||
}
|
||||
datalog::set_union(*hyps, *hyps1);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_hypmap.insert(p, hyps);
|
||||
}
|
||||
|
||||
expr_ref complement_lit(expr* e) {
|
||||
expr* e1;
|
||||
if (m.is_not(e, e1)) {
|
||||
return expr_ref(e1, m);
|
||||
}
|
||||
else {
|
||||
return expr_ref(m.mk_not(e), m);
|
||||
}
|
||||
}
|
||||
|
||||
bool in_hypotheses(expr* e, expr_set* hyps) {
|
||||
if (!hyps) {
|
||||
return false;
|
||||
}
|
||||
expr_ref not_e = complement_lit(e);
|
||||
return hyps->contains(not_e);
|
||||
}
|
||||
|
||||
bool contains_hypothesis(proof* p) {
|
||||
ptr_vector<proof> todo;
|
||||
ast_mark visit;
|
||||
todo.push_back(p);
|
||||
while (!todo.empty()) {
|
||||
p = todo.back();
|
||||
todo.pop_back();
|
||||
if (visit.is_marked(p)) {
|
||||
continue;
|
||||
}
|
||||
visit.mark(p, true);
|
||||
if (PR_HYPOTHESIS == p->get_decl_kind()) {
|
||||
return true;
|
||||
}
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
todo.push_back(m.get_parent(p, i));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_closed(proof* p) {
|
||||
expr_set* hyps = m_hypmap.find(p);
|
||||
return !hyps || hyps->empty();
|
||||
}
|
||||
|
||||
public:
|
||||
reduce_hypotheses(ast_manager& m): m(m), m_refs(m) {}
|
||||
|
||||
void operator()(proof_ref& pr) {
|
||||
proof_ref tmp(m);
|
||||
tmp = pr;
|
||||
elim(pr);
|
||||
reset();
|
||||
CTRACE("proof_utils", contains_hypothesis(pr),
|
||||
tout << "Contains hypothesis:\n";
|
||||
tout << mk_ismt2_pp(tmp, m) << "\n====>\n";
|
||||
tout << mk_ismt2_pp(pr, m) << "\n";);
|
||||
|
||||
}
|
||||
|
||||
void elim(proof_ref& p) {
|
||||
proof_ref tmp(m);
|
||||
proof* result = p.get();
|
||||
if (m_cache.find(p, result)) {
|
||||
p = result;
|
||||
return;
|
||||
}
|
||||
switch(p->get_decl_kind()) {
|
||||
case PR_HYPOTHESIS:
|
||||
if (!m_units.find(m.get_fact(p), result)) {
|
||||
result = p.get();
|
||||
}
|
||||
add_hypotheses(result);
|
||||
break;
|
||||
case PR_LEMMA: {
|
||||
SASSERT(m.get_num_parents(p) == 1);
|
||||
tmp = m.get_parent(p, 0);
|
||||
elim(tmp);
|
||||
get_literals(m.get_fact(p));
|
||||
expr_set* hyps = m_hypmap.find(tmp);
|
||||
expr_set* new_hyps = 0;
|
||||
if (hyps) {
|
||||
new_hyps = alloc(expr_set, *hyps);
|
||||
}
|
||||
for (unsigned i = 0; i < m_literals.size(); ++i) {
|
||||
expr* e = m_literals[i];
|
||||
if (!in_hypotheses(e, hyps)) {
|
||||
m_literals[i] = m_literals.back();
|
||||
m_literals.pop_back();
|
||||
--i;
|
||||
}
|
||||
else {
|
||||
SASSERT(new_hyps);
|
||||
expr_ref not_e = complement_lit(e);
|
||||
SASSERT(new_hyps->contains(not_e));
|
||||
new_hyps->remove(not_e);
|
||||
}
|
||||
}
|
||||
if (m_literals.empty()) {
|
||||
result = tmp;
|
||||
}
|
||||
else {
|
||||
expr_ref clause(m);
|
||||
if (m_literals.size() == 1) {
|
||||
clause = m_literals[0];
|
||||
}
|
||||
else {
|
||||
clause = m.mk_or(m_literals.size(), m_literals.c_ptr());
|
||||
}
|
||||
tmp = m.mk_lemma(tmp, clause);
|
||||
m_refs.push_back(tmp);
|
||||
result = tmp;
|
||||
}
|
||||
if (new_hyps && new_hyps->empty()) {
|
||||
dealloc(new_hyps);
|
||||
new_hyps = 0;
|
||||
}
|
||||
m_hypmap.insert(result, new_hyps);
|
||||
m_hyprefs.push_back(new_hyps);
|
||||
TRACE("proof_utils",
|
||||
tout << "New lemma: " << mk_pp(m.get_fact(p), m)
|
||||
<< "\n==>\n"
|
||||
<< mk_pp(m.get_fact(result), m) << "\n";
|
||||
if (hyps) {
|
||||
expr_set::iterator it = hyps->begin();
|
||||
expr_set::iterator end = hyps->end();
|
||||
for (; it != end; ++it) {
|
||||
tout << "Hypothesis: " << mk_pp(*it, m) << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case PR_UNIT_RESOLUTION: {
|
||||
proof_ref_vector parents(m);
|
||||
parents.push_back(m.get_parent(p, 0));
|
||||
push();
|
||||
bool found_false = false;
|
||||
for (unsigned i = 1; i < m.get_num_parents(p); ++i) {
|
||||
tmp = m.get_parent(p, i);
|
||||
elim(tmp);
|
||||
if (m.is_false(m.get_fact(tmp))) {
|
||||
result = tmp;
|
||||
found_false = true;
|
||||
break;
|
||||
}
|
||||
SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i)));
|
||||
parents.push_back(tmp);
|
||||
if (is_closed(tmp) && !m_units.contains(m.get_fact(tmp))) {
|
||||
m_units.insert(m.get_fact(tmp), tmp);
|
||||
m_units_trail.push_back(m.get_fact(tmp));
|
||||
}
|
||||
}
|
||||
if (found_false) {
|
||||
pop();
|
||||
break;
|
||||
}
|
||||
tmp = m.get_parent(p, 0);
|
||||
expr* old_clause = m.get_fact(tmp);
|
||||
elim(tmp);
|
||||
parents[0] = tmp;
|
||||
expr* clause = m.get_fact(tmp);
|
||||
if (m.is_false(clause)) {
|
||||
m_refs.push_back(tmp);
|
||||
result = tmp;
|
||||
pop();
|
||||
break;
|
||||
}
|
||||
//
|
||||
// case where clause is a literal in the old clause.
|
||||
//
|
||||
if (is_literal_in_clause(clause, old_clause)) {
|
||||
bool found = false;
|
||||
for (unsigned i = 1; !found && i < parents.size(); ++i) {
|
||||
if (m.is_complement(clause, m.get_fact(parents[i].get()))) {
|
||||
parents[1] = parents[i].get();
|
||||
parents.resize(2);
|
||||
result = m.mk_unit_resolution(parents.size(), parents.c_ptr());
|
||||
m_refs.push_back(result);
|
||||
add_hypotheses(result);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
result = parents[0].get();
|
||||
}
|
||||
pop();
|
||||
break;
|
||||
}
|
||||
//
|
||||
// case where new clause is a subset of old clause.
|
||||
// the literals in clause should be a subset of literals in old_clause.
|
||||
//
|
||||
get_literals(clause);
|
||||
for (unsigned i = 1; i < parents.size(); ++i) {
|
||||
bool found = false;
|
||||
for (unsigned j = 0; j < m_literals.size(); ++j) {
|
||||
if (m.is_complement(m_literals[j], m.get_fact(parents[i].get()))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// literal was removed as hypothesis.
|
||||
parents[i] = parents.back();
|
||||
parents.pop_back();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
if (parents.size() == 1) {
|
||||
result = parents[0].get();
|
||||
}
|
||||
else {
|
||||
result = m.mk_unit_resolution(parents.size(), parents.c_ptr());
|
||||
m_refs.push_back(result);
|
||||
add_hypotheses(result);
|
||||
}
|
||||
pop();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ptr_buffer<expr> args;
|
||||
bool change = false;
|
||||
bool found_false = false;
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
tmp = m.get_parent(p, i);
|
||||
elim(tmp);
|
||||
if (m.is_false(m.get_fact(tmp))) {
|
||||
result = tmp;
|
||||
found_false = true;
|
||||
break;
|
||||
}
|
||||
// SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i)));
|
||||
change = change || (tmp != m.get_parent(p, i));
|
||||
args.push_back(tmp);
|
||||
}
|
||||
if (found_false) {
|
||||
break;
|
||||
}
|
||||
if (m.has_fact(p)) {
|
||||
args.push_back(m.get_fact(p));
|
||||
}
|
||||
if (change) {
|
||||
tmp = m.mk_app(p->get_decl(), args.size(), args.c_ptr());
|
||||
m_refs.push_back(tmp);
|
||||
}
|
||||
else {
|
||||
tmp = p;
|
||||
}
|
||||
result = tmp;
|
||||
add_hypotheses(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SASSERT(m_hypmap.contains(result));
|
||||
m_cache.insert(p, result);
|
||||
p = result;
|
||||
}
|
||||
|
||||
bool is_literal_in_clause(expr* fml, expr* clause) {
|
||||
if (!m.is_or(clause)) {
|
||||
return false;
|
||||
}
|
||||
app* cl = to_app(clause);
|
||||
for (unsigned i = 0; i < cl->get_num_args(); ++i) {
|
||||
if (cl->get_arg(i) == fml) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void proof_utils::reduce_hypotheses(proof_ref& pr) {
|
||||
ast_manager& m = pr.get_manager();
|
||||
class reduce_hypotheses reduce(m);
|
||||
reduce(pr);
|
||||
CTRACE("proof_utils", !is_closed(m, pr), tout << mk_pp(pr, m) << "\n";);
|
||||
}
|
||||
|
||||
class proof_is_closed {
|
||||
ast_manager& m;
|
||||
ptr_vector<expr> m_literals;
|
||||
ast_mark m_visit;
|
||||
|
||||
void reset() {
|
||||
m_literals.reset();
|
||||
m_visit.reset();
|
||||
}
|
||||
|
||||
bool check(proof* p) {
|
||||
// really just a partial check because nodes may be visited
|
||||
// already under a different lemma scope.
|
||||
if (m_visit.is_marked(p)) {
|
||||
return true;
|
||||
}
|
||||
bool result = false;
|
||||
m_visit.mark(p, true);
|
||||
switch(p->get_decl_kind()) {
|
||||
case PR_LEMMA: {
|
||||
unsigned sz = m_literals.size();
|
||||
expr* cls = m.get_fact(p);
|
||||
m_literals.push_back(cls);
|
||||
if (m.is_or(cls)) {
|
||||
m_literals.append(to_app(cls)->get_num_args(), to_app(cls)->get_args());
|
||||
}
|
||||
SASSERT(m.get_num_parents(p) == 1);
|
||||
result = check(m.get_parent(p, 0));
|
||||
m_literals.resize(sz);
|
||||
break;
|
||||
}
|
||||
case PR_HYPOTHESIS: {
|
||||
expr* fact = m.get_fact(p);
|
||||
for (unsigned i = 0; i < m_literals.size(); ++i) {
|
||||
if (m.is_complement(m_literals[i], fact)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
result = true;
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
if (!check(m.get_parent(p, i))) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
proof_is_closed(ast_manager& m): m(m) {}
|
||||
|
||||
bool operator()(proof *p) {
|
||||
bool ok = check(p);
|
||||
reset();
|
||||
return ok;
|
||||
}
|
||||
};
|
||||
|
||||
bool proof_utils::is_closed(ast_manager& m, proof* p) {
|
||||
proof_is_closed checker(m);
|
||||
return checker(p);
|
||||
}
|
||||
|
||||
|
||||
static void permute_unit_resolution(expr_ref_vector& refs, obj_map<proof,proof*>& cache, proof_ref& pr) {
|
||||
ast_manager& m = pr.get_manager();
|
||||
proof* pr2 = 0;
|
||||
proof_ref_vector parents(m);
|
||||
proof_ref prNew(pr);
|
||||
if (cache.find(pr, pr2)) {
|
||||
pr = pr2;
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m.get_num_parents(pr); ++i) {
|
||||
prNew = m.get_parent(pr, i);
|
||||
permute_unit_resolution(refs, cache, prNew);
|
||||
parents.push_back(prNew);
|
||||
}
|
||||
|
||||
prNew = pr;
|
||||
if (pr->get_decl_kind() == PR_UNIT_RESOLUTION &&
|
||||
parents[0]->get_decl_kind() == PR_TH_LEMMA) {
|
||||
/*
|
||||
Unit resolution:
|
||||
T1: (or l_1 ... l_n l_1' ... l_m')
|
||||
T2: (not l_1)
|
||||
...
|
||||
T(n+1): (not l_n)
|
||||
[unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m')
|
||||
Th lemma:
|
||||
T1: (not l_1)
|
||||
...
|
||||
Tn: (not l_n)
|
||||
[th-lemma T1 ... Tn]: (or l_{n+1} ... l_m)
|
||||
|
||||
Such that (or l_1 .. l_n l_{n+1} .. l_m) is a theory axiom.
|
||||
|
||||
Implement conversion:
|
||||
|
||||
T1 |- not l_1 ... Tn |- not l_n
|
||||
------------------------------- TH_LEMMA
|
||||
(or k_1 .. k_m j_1 ... j_m) S1 |- not k_1 ... Sm |- not k_m
|
||||
-------------------------------------------------------------- UNIT_RESOLUTION
|
||||
(or j_1 .. j_m)
|
||||
|
||||
|
||||
|->
|
||||
|
||||
T1 |- not l_1 ... Tn |- not l_n S1 |- not k_1 ... Sm |- not k_m
|
||||
---------------------------------------------------------------- TH_LEMMA
|
||||
(or j_1 .. j_m)
|
||||
|
||||
*/
|
||||
proof_ref_vector premises(m);
|
||||
proof* thLemma = parents[0].get();
|
||||
for (unsigned i = 0; i < m.get_num_parents(thLemma); ++i) {
|
||||
premises.push_back(m.get_parent(thLemma, i));
|
||||
}
|
||||
for (unsigned i = 1; i < parents.size(); ++i) {
|
||||
premises.push_back(parents[i].get());
|
||||
}
|
||||
parameter const* params = thLemma->get_decl()->get_parameters();
|
||||
unsigned num_params = thLemma->get_decl()->get_num_parameters();
|
||||
SASSERT(params[0].is_symbol());
|
||||
family_id tid = m.mk_family_id(params[0].get_symbol());
|
||||
SASSERT(tid != null_family_id);
|
||||
prNew = m.mk_th_lemma(tid, m.get_fact(pr),
|
||||
premises.size(), premises.c_ptr(), num_params-1, params+1);
|
||||
}
|
||||
else {
|
||||
ptr_vector<expr> args;
|
||||
for (unsigned i = 0; i < parents.size(); ++i) {
|
||||
args.push_back(parents[i].get());
|
||||
}
|
||||
if (m.has_fact(pr)) {
|
||||
args.push_back(m.get_fact(pr));
|
||||
}
|
||||
prNew = m.mk_app(pr->get_decl(), args.size(), args.c_ptr());
|
||||
}
|
||||
|
||||
cache.insert(pr, prNew);
|
||||
refs.push_back(prNew);
|
||||
pr = prNew;
|
||||
}
|
||||
|
||||
|
||||
// permute unit resolution over Theory lemmas to track premises.
|
||||
void proof_utils::permute_unit_resolution(proof_ref& pr) {
|
||||
expr_ref_vector refs(pr.get_manager());
|
||||
obj_map<proof,proof*> cache;
|
||||
::permute_unit_resolution(refs, cache, pr);
|
||||
}
|
||||
|
||||
class push_instantiations_up_cl {
|
||||
ast_manager& m;
|
||||
public:
|
||||
push_instantiations_up_cl(ast_manager& m): m(m) {}
|
||||
|
||||
void operator()(proof_ref& p) {
|
||||
expr_ref_vector s0(m);
|
||||
p = push(p, s0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
proof* push(proof* p, expr_ref_vector const& sub) {
|
||||
proof_ref_vector premises(m);
|
||||
expr_ref conclusion(m);
|
||||
svector<std::pair<unsigned, unsigned> > positions;
|
||||
vector<expr_ref_vector> substs;
|
||||
|
||||
if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) {
|
||||
for (unsigned i = 0; i < premises.size(); ++i) {
|
||||
compose(substs[i], sub);
|
||||
premises[i] = push(premises[i].get(), substs[i]);
|
||||
substs[i].reset();
|
||||
}
|
||||
instantiate(sub, conclusion);
|
||||
return
|
||||
m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion,
|
||||
positions,
|
||||
substs);
|
||||
}
|
||||
if (sub.empty()) {
|
||||
return p;
|
||||
}
|
||||
if (m.is_modus_ponens(p)) {
|
||||
SASSERT(m.get_num_parents(p) == 2);
|
||||
proof* p0 = m.get_parent(p, 0);
|
||||
proof* p1 = m.get_parent(p, 1);
|
||||
if (m.get_fact(p0) == m.get_fact(p)) {
|
||||
return push(p0, sub);
|
||||
}
|
||||
expr* e1, *e2;
|
||||
if (m.is_rewrite(p1, e1, e2) &&
|
||||
is_quantifier(e1) && is_quantifier(e2) &&
|
||||
to_quantifier(e1)->get_num_decls() == to_quantifier(e2)->get_num_decls()) {
|
||||
expr_ref r1(e1,m), r2(e2,m);
|
||||
instantiate(sub, r1);
|
||||
instantiate(sub, r2);
|
||||
p1 = m.mk_rewrite(r1, r2);
|
||||
return m.mk_modus_ponens(push(p0, sub), p1);
|
||||
}
|
||||
}
|
||||
premises.push_back(p);
|
||||
substs.push_back(sub);
|
||||
conclusion = m.get_fact(p);
|
||||
instantiate(sub, conclusion);
|
||||
return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs);
|
||||
}
|
||||
|
||||
void compose(expr_ref_vector& sub, expr_ref_vector const& s0) {
|
||||
for (unsigned i = 0; i < sub.size(); ++i) {
|
||||
expr_ref e(m);
|
||||
var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr(), e);
|
||||
sub[i] = e;
|
||||
}
|
||||
}
|
||||
|
||||
void instantiate(expr_ref_vector const& sub, expr_ref& fml) {
|
||||
if (sub.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!is_forall(fml)) {
|
||||
return;
|
||||
}
|
||||
quantifier* q = to_quantifier(fml);
|
||||
if (q->get_num_decls() != sub.size()) {
|
||||
TRACE("proof_utils", tout << "quantifier has different number of variables than substitution";
|
||||
tout << mk_pp(q, m) << "\n";
|
||||
tout << sub.size() << "\n";);
|
||||
return;
|
||||
}
|
||||
var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr(), fml);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void proof_utils::push_instantiations_up(proof_ref& pr) {
|
||||
push_instantiations_up_cl push(pr.get_manager());
|
||||
push(pr);
|
||||
}
|
||||
|
||||
|
48
src/muz/base/proof_utils.h
Normal file
48
src/muz/base/proof_utils.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
proof_utils.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utilities for transforming proofs.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-10-12.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _PROOF_UTILS_H_
|
||||
#define _PROOF_UTILS_H_
|
||||
|
||||
class proof_utils {
|
||||
public:
|
||||
/**
|
||||
\brief reduce the set of hypotheses used in the proof.
|
||||
*/
|
||||
static void reduce_hypotheses(proof_ref& pr);
|
||||
|
||||
/**
|
||||
\brief Check that a proof does not contain open hypotheses.
|
||||
*/
|
||||
static bool is_closed(ast_manager& m, proof* p);
|
||||
|
||||
/**
|
||||
\brief Permute unit resolution rule with th-lemma
|
||||
*/
|
||||
static void permute_unit_resolution(proof_ref& pr);
|
||||
|
||||
/**
|
||||
\brief Push instantiations created in hyper-resolutions up to leaves.
|
||||
This produces a "ground" proof where leaves are annotated by instantiations.
|
||||
*/
|
||||
static void push_instantiations_up(proof_ref& pr);
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue