mirror of
https://github.com/Z3Prover/z3
synced 2025-07-18 02:16:40 +00:00
Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
3f9edad676
commit
e9eab22e5c
1186 changed files with 381859 additions and 0 deletions
367
lib/dl_mk_subsumption_checker.cpp
Normal file
367
lib/dl_mk_subsumption_checker.cpp
Normal file
|
@ -0,0 +1,367 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_subsumption_checker.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Rule transformer which checks for subsumption
|
||||
(currently just for subsumption with total relations)
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-10-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include"ast_pp.h"
|
||||
|
||||
#include "rewriter.h"
|
||||
#include "rewriter_def.h"
|
||||
|
||||
|
||||
#include"dl_mk_subsumption_checker.h"
|
||||
#include"dl_table_relation.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// mk_subsumption_checker
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
|
||||
bool mk_subsumption_checker::is_total_rule(const rule * r) {
|
||||
if(r->get_tail_size()!=0) { return false; }
|
||||
|
||||
unsigned pt_len = r->get_positive_tail_size();
|
||||
if(pt_len!=r->get_uninterpreted_tail_size()) {
|
||||
//we dont' expect rules with negative tails to be total
|
||||
return false;
|
||||
}
|
||||
|
||||
for(unsigned i=0; i<pt_len; i++) {
|
||||
func_decl * tail_pred = r->get_tail(i)->get_decl();
|
||||
if(!m_total_relations.contains(tail_pred)) {
|
||||
//this rule has a non-total predicate in the tail
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned t_len = r->get_positive_tail_size();
|
||||
for(unsigned i=pt_len; i<t_len; i++) {
|
||||
SASSERT(!r->is_neg_tail(i)); //we assume interpreted tail not to be negated
|
||||
if(!m.is_true(r->get_tail(i))) {
|
||||
//this rule has an interpreted tail which is not constant true
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var_idx_set head_vars;
|
||||
app * head = r->get_head();
|
||||
unsigned arity = head->get_num_args();
|
||||
for(unsigned i=0; i<arity; i++) {
|
||||
expr * arg = head->get_arg(i);
|
||||
if(!is_var(arg)) { return false; }
|
||||
unsigned idx = to_var(arg)->get_idx();
|
||||
if(head_vars.contains(idx)) { return false; }
|
||||
head_vars.insert(idx);
|
||||
}
|
||||
SASSERT(head_vars.num_elems()==arity);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mk_subsumption_checker::on_discovered_total_relation(func_decl * pred, rule * r) {
|
||||
//this should be rule marking a new relation as total
|
||||
SASSERT(!m_total_relations.contains(pred));
|
||||
SASSERT(!r || pred==r->get_head()->get_decl());
|
||||
SASSERT(!r || is_total_rule(r));
|
||||
|
||||
m_total_relations.insert(pred);
|
||||
m_total_relation_defining_rules.insert(pred, r);
|
||||
m_have_new_total_rule = true;
|
||||
if(r) {
|
||||
m_ref_holder.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
void mk_subsumption_checker::scan_for_total_rules(const rule_set & rules) {
|
||||
bool new_discovered;
|
||||
//we cycle through the rules until we keep discovering new total relations
|
||||
//(discovering a total relation migh reveal other total relations)
|
||||
do {
|
||||
new_discovered = false;
|
||||
rule_set::iterator rend = rules.end();
|
||||
for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) {
|
||||
rule * r = *rit;
|
||||
func_decl * head_pred = r->get_head()->get_decl();
|
||||
if(is_total_rule(r) && !m_total_relations.contains(head_pred)) {
|
||||
on_discovered_total_relation(head_pred, r);
|
||||
new_discovered = true;
|
||||
}
|
||||
}
|
||||
} while(new_discovered);
|
||||
}
|
||||
|
||||
|
||||
bool mk_subsumption_checker::transform_rule(rule * r,
|
||||
rule_subsumption_index& subs_index, rule_ref & res)
|
||||
{
|
||||
unsigned u_len = r->get_uninterpreted_tail_size();
|
||||
unsigned len = r->get_tail_size();
|
||||
if(u_len==0) {
|
||||
res = r;
|
||||
return true;
|
||||
}
|
||||
app_ref head(r->get_head(), m);
|
||||
|
||||
app_ref_vector tail(m);
|
||||
svector<bool> tail_neg;
|
||||
|
||||
for(unsigned i=0; i<u_len; i++) {
|
||||
app * tail_atom = r->get_tail(i);
|
||||
bool neg = r->is_neg_tail(i);
|
||||
if(m_total_relations.contains(tail_atom->get_decl())
|
||||
|| subs_index.is_subsumed(tail_atom)) {
|
||||
if(neg) {
|
||||
//rule contains negated total relation, this means that it is unsatisfiable
|
||||
//and can be removed
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
//we remove total relations from the tail
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!neg && head.get()==tail_atom) {
|
||||
//rule contains its head positively in the tail, therefore
|
||||
//it will never add any new facts to the relation, so it
|
||||
//can be removed
|
||||
return false;
|
||||
}
|
||||
tail.push_back(tail_atom);
|
||||
tail_neg.push_back(neg);
|
||||
}
|
||||
|
||||
if(tail.size()==u_len) {
|
||||
res = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
//we just copy the interpreted part of the tail
|
||||
for(unsigned i=u_len; i<len; i++) {
|
||||
tail.push_back(r->get_tail(i));
|
||||
tail_neg.push_back(r->is_neg_tail(i));
|
||||
}
|
||||
|
||||
SASSERT(tail.size()==tail_neg.size());
|
||||
res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr());
|
||||
res->set_accounting_parent_object(m_context, r);
|
||||
m_context.get_rule_manager().fix_unbound_vars(res, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rule_size_comparator(rule * r1, rule * r2) {
|
||||
return r1->get_tail_size() < r2->get_tail_size();
|
||||
}
|
||||
|
||||
bool mk_subsumption_checker::transform_rules(const rule_set & orig, rule_set & tgt) {
|
||||
|
||||
bool modified = false;
|
||||
|
||||
func_decl_set total_relations_with_included_rules;
|
||||
|
||||
rule_subsumption_index subs_index(m_context);
|
||||
|
||||
rule_ref_vector orig_rules(m_context.get_rule_manager());
|
||||
orig_rules.append(orig.get_num_rules(), orig.begin());
|
||||
|
||||
rule * * rbegin = orig_rules.c_ptr();
|
||||
rule * * rend = rbegin + orig_rules.size();
|
||||
|
||||
//before traversing we sort rules so that the shortest are in the beginning.
|
||||
//this will help make subsumption checks more efficient
|
||||
std::sort(rbegin, rend, rule_size_comparator);
|
||||
|
||||
for(rule_set::iterator rit = rbegin; rit!=rend; ++rit) {
|
||||
|
||||
rule * r = *rit;
|
||||
func_decl * head_pred = r->get_head()->get_decl();
|
||||
|
||||
if(m_total_relations.contains(head_pred)) {
|
||||
if(!m_context.is_output_predicate(head_pred) ||
|
||||
total_relations_with_included_rules.contains(head_pred)) {
|
||||
//We just skip definitions of total non-output relations as
|
||||
//we'll eliminate them from the problem.
|
||||
//We also skip rules of total output relations for which we have
|
||||
//already output the rule which implies their totality.
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
rule * defining_rule;
|
||||
TRUSTME(m_total_relation_defining_rules.find(head_pred, defining_rule));
|
||||
if(defining_rule) {
|
||||
rule_ref totality_rule(m_context.get_rule_manager());
|
||||
TRUSTME(transform_rule(defining_rule, subs_index, totality_rule));
|
||||
if(defining_rule!=totality_rule) {
|
||||
modified = true;
|
||||
}
|
||||
tgt.add_rule(totality_rule);
|
||||
SASSERT(totality_rule->get_head()->get_decl()==head_pred);
|
||||
}
|
||||
else {
|
||||
modified = true;
|
||||
}
|
||||
total_relations_with_included_rules.insert(head_pred);
|
||||
continue;
|
||||
}
|
||||
|
||||
rule_ref new_rule(m_context.get_rule_manager());
|
||||
if(!transform_rule(r, subs_index, new_rule)) {
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
if(m_new_total_relation_discovery_during_transformation && is_total_rule(new_rule)) {
|
||||
on_discovered_total_relation(head_pred, new_rule.get());
|
||||
}
|
||||
if(subs_index.is_subsumed(new_rule)) {
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
if(new_rule.get()!=r) {
|
||||
modified = true;
|
||||
}
|
||||
tgt.add_rule(new_rule);
|
||||
subs_index.add(new_rule);
|
||||
}
|
||||
TRACE("dl",
|
||||
tout << "original set size: "<<orig.get_num_rules()<<"\n"
|
||||
<< "reduced set size: "<<tgt.get_num_rules()<<"\n"; );
|
||||
return modified;
|
||||
}
|
||||
|
||||
void mk_subsumption_checker::scan_for_relations_total_due_to_facts() {
|
||||
relation_manager& rm = m_context.get_rmanager();
|
||||
|
||||
decl_set candidate_preds;
|
||||
m_context.collect_predicates(candidate_preds);
|
||||
|
||||
decl_set::iterator end = candidate_preds.end();
|
||||
for(decl_set::iterator it = candidate_preds.begin(); it!=end; ++it) {
|
||||
func_decl * pred = *it;
|
||||
|
||||
if(m_total_relations.contains(pred)) { continue; } //already total
|
||||
|
||||
relation_base * rel = rm.try_get_relation(pred);
|
||||
|
||||
if(!rel || !rel->knows_exact_size()) { continue; }
|
||||
|
||||
unsigned arity = pred->get_arity();
|
||||
if(arity>30) { continue; }
|
||||
|
||||
//for now we only check booleans domains
|
||||
for(unsigned i=0; i<arity; i++) {
|
||||
if(!m.is_bool(pred->get_domain(i))) {
|
||||
goto next_pred;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
unsigned total_size = 1<<arity;
|
||||
//by calling rel.knows_exact_size() we got assured that the estimate is exact
|
||||
unsigned rel_sz = rel->get_size_estimate_rows();
|
||||
|
||||
obj_hashtable<app> * head_store;
|
||||
if(m_ground_unconditional_rule_heads.find(pred, head_store)) {
|
||||
//Some relations may receive facts by ground unconditioned rules.
|
||||
//We scanned for those earlier, so now we check whether we cannot get a
|
||||
//better estimate of relation size from these.
|
||||
|
||||
unsigned gnd_rule_cnt = head_store->size();
|
||||
if(gnd_rule_cnt>rel_sz) {
|
||||
rel_sz = gnd_rule_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(total_size>=rel_sz);
|
||||
if(total_size==rel_sz) {
|
||||
on_discovered_total_relation(pred, 0);
|
||||
}
|
||||
}
|
||||
next_pred:;
|
||||
}
|
||||
}
|
||||
|
||||
void mk_subsumption_checker::collect_ground_unconditional_rule_heads(const rule_set & rules)
|
||||
{
|
||||
rule_set::iterator rend = rules.end();
|
||||
for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) {
|
||||
rule * r = *rit;
|
||||
func_decl * pred = r->get_head()->get_decl();
|
||||
|
||||
if(r->get_tail_size()!=0) { continue; }
|
||||
|
||||
|
||||
app * head = r->get_head();
|
||||
unsigned arity = pred->get_arity();
|
||||
for(unsigned i=0; i<arity; i++) {
|
||||
expr * arg = head->get_arg(i);
|
||||
if(!is_app(arg)) {
|
||||
goto next_rule;
|
||||
}
|
||||
}
|
||||
|
||||
if(!m_ground_unconditional_rule_heads.contains(pred)) {
|
||||
m_ground_unconditional_rule_heads.insert(pred, alloc(obj_hashtable<app>));
|
||||
}
|
||||
obj_hashtable<app> * head_store;
|
||||
m_ground_unconditional_rule_heads.find(pred, head_store);
|
||||
head_store->insert(head);
|
||||
|
||||
next_rule:;
|
||||
}
|
||||
}
|
||||
|
||||
rule_set * mk_subsumption_checker::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
|
||||
// TODO mc, pc
|
||||
|
||||
m_have_new_total_rule = false;
|
||||
collect_ground_unconditional_rule_heads(source);
|
||||
scan_for_relations_total_due_to_facts();
|
||||
scan_for_total_rules(source);
|
||||
|
||||
m_have_new_total_rule = false;
|
||||
rule_set * res = alloc(rule_set, m_context);
|
||||
bool modified = transform_rules(source, *res);
|
||||
|
||||
if(!m_have_new_total_rule && !modified) {
|
||||
dealloc(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//During the construction of the new set we may discover new total relations
|
||||
//(by quantifier elimination on the uninterpreted tails).
|
||||
SASSERT(m_new_total_relation_discovery_during_transformation || !m_have_new_total_rule);
|
||||
while(m_have_new_total_rule) {
|
||||
m_have_new_total_rule = false;
|
||||
|
||||
rule_set * old = res;
|
||||
res = alloc(rule_set, m_context);
|
||||
transform_rules(*old, *res);
|
||||
dealloc(old);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue