You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
620 lines
16 KiB
620 lines
16 KiB
/*
|
|
* This file is part of the program ltl2dstar (http://www.ltl2dstar.de/).
|
|
* Copyright (C) 2005-2007 Joachim Klein <j.klein@ltl2dstar.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
|
|
#ifndef SAFRASALGORITHM_H
|
|
#define SAFRASALGORITHM_H
|
|
|
|
/** @file
|
|
* Provides an implementation of the transition function of Safra's algorithm.
|
|
*/
|
|
|
|
#include "SafraTree.hpp"
|
|
#include "SafraTreeTemplate.hpp"
|
|
#include "NBAAnalysis.hpp"
|
|
#include "common/BitSet.hpp"
|
|
#include "common/BitSetIterator.hpp"
|
|
#include "GraphAlgorithms.hpp"
|
|
#include "Configuration.hpp"
|
|
#include <vector>
|
|
|
|
|
|
namespace SafrasAlgorithmInternal {
|
|
|
|
/**
|
|
* Safra tree visitor that resets the final flag on the Safra tree node.
|
|
*/
|
|
class STVisitor_reset_final_flag {
|
|
public:
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
node->setFinalFlag(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Safra tree visitor that creates a new child node if
|
|
* the label of the node and the set of final states in the
|
|
* NBA intersect.
|
|
*/
|
|
class STVisitor_check_finalset {
|
|
public:
|
|
/**
|
|
* Constructor.
|
|
* @param final_states the states that are accepting (final) in the NBA
|
|
* @param tree_template the tree template to keep track of new nodes
|
|
*/
|
|
STVisitor_check_finalset(const BitSet& final_states,
|
|
SafraTreeTemplate& tree_template) :
|
|
_final_states(final_states),
|
|
_tree_template(tree_template) {}
|
|
|
|
/** */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
if (_final_states.intersects(node->getLabeling())) {
|
|
BitSet q_and_f(_final_states);
|
|
q_and_f.Intersect(node->getLabeling());
|
|
|
|
SafraTreeNode *new_child=tree.newNode();
|
|
node->addAsYoungestChild(new_child);
|
|
|
|
_tree_template.setRenameable(new_child->getID());
|
|
|
|
new_child->getLabeling()=q_and_f;
|
|
}
|
|
}
|
|
private:
|
|
const BitSet& _final_states;
|
|
SafraTreeTemplate& _tree_template;
|
|
};
|
|
|
|
/**
|
|
* Safra tree visitor that performs the powerset construction
|
|
* on the label of the Safra tree node.
|
|
*/
|
|
template <typename NBA_t>
|
|
class STVisitor_powerset {
|
|
public:
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
STVisitor_powerset(NBA_t& nba, APElement elem)
|
|
: _nba(nba), _elem(elem) {}
|
|
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
BitSet new_labeling;
|
|
BitSet& old_labeling=node->getLabeling();
|
|
for (int i=old_labeling.nextSetBit(0);
|
|
i!=-1;
|
|
i=old_labeling.nextSetBit(i+1)) {
|
|
new_labeling.Union(*(_nba[i]->getEdge(_elem)));
|
|
}
|
|
|
|
node->getLabeling()=new_labeling;
|
|
}
|
|
|
|
private:
|
|
NBA_t& _nba;
|
|
APElement _elem;
|
|
};
|
|
|
|
/**
|
|
* A Safra tree visitor that subtracts (minus operator) a BitSet from
|
|
* the label of the tree node.
|
|
*/
|
|
class STVisitor_subtract_labeling {
|
|
public:
|
|
STVisitor_subtract_labeling(BitSet &bitset) : _bitset(bitset) {}
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
node->getLabeling().Minus(_bitset);
|
|
}
|
|
private:
|
|
BitSet &_bitset;
|
|
};
|
|
|
|
/**
|
|
* A Safra tree visitor that removes all
|
|
* children of the node.
|
|
*/
|
|
class STVisitor_remove_subtree {
|
|
public:
|
|
STVisitor_remove_subtree(SafraTreeTemplate& tree_template) :
|
|
_tree_template(tree_template) {}
|
|
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
unsigned int id=node->getID();
|
|
if (_tree_template.isRenameable(id)) {
|
|
// this node was created recently, so we only delete it from
|
|
// the renameableNames, but don't mark it in restrictedNames
|
|
_tree_template.setRenameable(id, false);
|
|
} else {
|
|
_tree_template.setRestricted(id);
|
|
}
|
|
|
|
tree.remove(node);
|
|
}
|
|
|
|
private:
|
|
SafraTreeTemplate& _tree_template;
|
|
};
|
|
|
|
|
|
/**
|
|
* A Safra tree visitor that modifies the
|
|
* children so that all children have
|
|
* disjoint labels
|
|
*/
|
|
class STVisitor_check_children_horizontal {
|
|
public:
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
if (node->getChildCount()<=1) {
|
|
return;
|
|
}
|
|
|
|
BitSet already_seen;
|
|
bool first=true;
|
|
for (SafraTreeNode::child_iterator it=node->children_begin();
|
|
it!=node->children_end();
|
|
++it) {
|
|
SafraTreeNode *cur_child=*it;
|
|
if (first) {
|
|
already_seen=cur_child->getLabeling();
|
|
first=false;
|
|
} else {
|
|
BitSet& current=cur_child->getLabeling();
|
|
|
|
BitSet intersection=already_seen; // make copy
|
|
if (intersection.intersects(current)) {
|
|
// There are some labels, which occur in older brothers,
|
|
// remove them from current node and its children
|
|
STVisitor_subtract_labeling stv_sub(intersection);
|
|
tree.walkSubTreePostOrder(stv_sub, cur_child);
|
|
}
|
|
|
|
already_seen.Union(current);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A Safra tree visitor that ensures that
|
|
* the union of the labels of the children
|
|
* are a proper subset of the label of the
|
|
* parents. Otherwise, the children are
|
|
* removed and the final flag is set on
|
|
* the tree node.
|
|
*/
|
|
class STVisitor_check_children_vertical {
|
|
public:
|
|
STVisitor_check_children_vertical(SafraTreeTemplate& tree_template) :
|
|
_tree_template(tree_template) {}
|
|
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
if (node->getChildCount()==0) {return;}
|
|
|
|
BitSet labeling_union;
|
|
for (SafraTreeNode::child_iterator it=node->children_begin();
|
|
it!=node->children_end();
|
|
++it) {
|
|
labeling_union.Union((*it)->getLabeling());
|
|
}
|
|
|
|
if (labeling_union == node->getLabeling()) {
|
|
// The union of the labelings of the children is exactly the
|
|
// same as the labeling of the parent ->
|
|
// remove children
|
|
STVisitor_remove_subtree stv_remove(_tree_template);
|
|
tree.walkChildrenPostOrder(stv_remove, node);
|
|
|
|
node->setFinalFlag(true);
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
SafraTreeTemplate& _tree_template;
|
|
};
|
|
|
|
|
|
/**
|
|
* Safra tree visitor that attempts
|
|
* to reorder the independant children
|
|
* into a canonical order.
|
|
* Two children are independet if
|
|
* their is no state that is reachable by
|
|
* states in both labels.
|
|
*/
|
|
class STVisitor_reorder_children {
|
|
public:
|
|
/**
|
|
* Constructor
|
|
* nba_reachability A vector of BitSets (state index -> BitSet) of states
|
|
* in the NBA that are reachable from a state.
|
|
* N the maximum number of nodes in the Safra tree
|
|
*/
|
|
STVisitor_reorder_children(std::vector<BitSet>& nba_reachability,
|
|
unsigned int N) :
|
|
_nba_reachability(nba_reachability), _n(N) {
|
|
_node_order=new unsigned int[N];
|
|
_node_reachability=new BitSet[N];
|
|
}
|
|
|
|
/** Destructor */
|
|
~STVisitor_reorder_children() {
|
|
delete[] _node_order;
|
|
delete[] _node_reachability;
|
|
}
|
|
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
if (node->getChildCount()<=1) {return;}
|
|
|
|
unsigned int i=0;
|
|
for (SafraTreeNode::child_iterator it=
|
|
node->children_begin();
|
|
it!=node->children_end();
|
|
++it) {
|
|
BitSet& reachable_this=_node_reachability[(*it)->getID()];
|
|
reachable_this.clear();
|
|
_node_order[(*it)->getID()]=i++;
|
|
|
|
BitSet& label_this=(*it)->getLabeling();
|
|
for (BitSetIterator label_it(label_this);
|
|
label_it!=BitSetIterator::end(label_this);
|
|
++label_it) {
|
|
reachable_this.Union(_nba_reachability[*label_it]);
|
|
}
|
|
|
|
// std::cerr << "reachability_this: "<<reachable_this << std::endl;
|
|
}
|
|
|
|
|
|
// reorder...
|
|
// std::cerr << "Sorting!" << std::endl;
|
|
|
|
// Bubble sort, ough!
|
|
bool finished=false;
|
|
while (!finished) {
|
|
finished=true;
|
|
|
|
for (SafraTreeNode *a=node->getOldestChild();
|
|
a!=0 && a->getYoungerBrother()!=0;
|
|
a=a->getYoungerBrother()) {
|
|
|
|
SafraTreeNode* b=a->getYoungerBrother();
|
|
|
|
BitSet &reach_a=_node_reachability[a->getID()];
|
|
BitSet &reach_b=_node_reachability[b->getID()];
|
|
|
|
if (reach_a.intersects(reach_b)) {
|
|
// a and b are not independant...
|
|
// --> keep relative order...
|
|
assert(_node_order[a->getID()] < _node_order[b->getID()]);
|
|
} else {
|
|
// a and b are independant...
|
|
if (! (a->getLabeling() <
|
|
b->getLabeling())) {
|
|
// swap
|
|
node->swapChildren(a,b);
|
|
a=b;
|
|
finished=false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::vector<BitSet>& _nba_reachability;
|
|
BitSet* _node_reachability;
|
|
unsigned int* _node_order;
|
|
unsigned int _n;
|
|
};
|
|
|
|
|
|
/**
|
|
* A Safra tree visitor that removes tree nodes
|
|
* with empty labels.
|
|
*/
|
|
class STVisitor_remove_empty {
|
|
public:
|
|
STVisitor_remove_empty(SafraTreeTemplate& tree_template) :
|
|
_tree_template(tree_template) {}
|
|
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
if (node->getLabeling().isEmpty()) {
|
|
unsigned int id=node->getID();
|
|
if (_tree_template.isRenameable(id)) {
|
|
// this node was created recently, so we only delete it in
|
|
// renameableNames, but don't mark it in restrictedNodes
|
|
_tree_template.setRenameable(id, false);
|
|
} else {
|
|
_tree_template.setRestricted(id);
|
|
}
|
|
|
|
tree.remove(node);
|
|
}
|
|
}
|
|
|
|
private:
|
|
SafraTreeTemplate& _tree_template;
|
|
};
|
|
|
|
|
|
/**
|
|
* A Safra tree visitor that checks if all
|
|
* the successor states in the NBA of the label
|
|
* are accepting. If this is the case, all
|
|
* children are removed, and the final flag is set.
|
|
*/
|
|
class STVisitor_check_for_succ_final {
|
|
private:
|
|
bool _success;
|
|
BitSet const& _nba_states_with_all_succ_final;
|
|
SafraTreeTemplate& _tree_template;
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
* @param nba_states_with_all_succ_final A BitSet with the indizes of the
|
|
* NBA states that only have accepting (final)
|
|
* successors.
|
|
* @param tree_template SafraTreeTemplate to keep track of removed nodes
|
|
*/
|
|
STVisitor_check_for_succ_final(const BitSet& nba_states_with_all_succ_final, SafraTreeTemplate& tree_template)
|
|
: _success(false),
|
|
_nba_states_with_all_succ_final(nba_states_with_all_succ_final),
|
|
_tree_template(tree_template) {}
|
|
|
|
/** Returns true if the condition was triggered. */
|
|
bool wasSuccessful() {return _success;}
|
|
|
|
/** Node visitor */
|
|
void visit(SafraTree& tree, SafraTreeNode *node) {
|
|
|
|
bool all_final=true;
|
|
for (BitSetIterator it=BitSetIterator(node->getLabeling());
|
|
it!=BitSetIterator::end(node->getLabeling());
|
|
++it) {
|
|
if (!_nba_states_with_all_succ_final.get(*it)) {
|
|
all_final=false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (all_final) {
|
|
// remove all children of node & set final flag
|
|
STVisitor_remove_subtree stv_remove(_tree_template);
|
|
tree.walkChildrenPostOrder(stv_remove, node);
|
|
|
|
node->setFinalFlag();
|
|
|
|
_success=true;
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* A class which calculates the transition function of Safra's algorithm.
|
|
* You have to provide at least the final states of the NBA, the other
|
|
* values have to be set, if the corresponding option is set.
|
|
*/
|
|
template <typename NBA_t>
|
|
class SafrasAlgorithm {
|
|
public:
|
|
typedef NBAAnalysis<NBA_t> NBAAnalysis_t;
|
|
|
|
SafrasAlgorithm(NBA_t& nba, const Options_Safra &options);
|
|
~SafrasAlgorithm();
|
|
|
|
|
|
typedef SafraTreeTemplate_ptr result_t;
|
|
typedef SafraTree_ptr state_t;
|
|
|
|
result_t delta(state_t tree, APElement elem) {
|
|
return process(*tree, elem);
|
|
}
|
|
|
|
state_t getStartState() {
|
|
SafraTree_ptr start(new SafraTree(_NODES));
|
|
if (_nba.getStartState()!=0) {
|
|
start->getRootNode()->getLabeling().set(_nba.getStartState()->getName());
|
|
}
|
|
|
|
return start;
|
|
}
|
|
|
|
void prepareAcceptance(RabinAcceptance& acceptance) {
|
|
acceptance.newAcceptancePairs(_NODES);
|
|
}
|
|
|
|
SafraTreeTemplate_ptr process(const SafraTree& tree,
|
|
APElement element);
|
|
|
|
bool checkEmpty() {
|
|
if (_nba.size()==0 ||
|
|
_nba.getStartState()==0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
Options_Safra _options;
|
|
NBAAnalysis_t _nba_analysis;
|
|
NBA_t& _nba;
|
|
unsigned int _NODES;
|
|
|
|
std::vector<BitSet*> _next;
|
|
|
|
/** Cacheing the STVisitor_reorder_children, as it's initialization is complex. */
|
|
SafrasAlgorithmInternal::STVisitor_reorder_children* stv_reorder;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Constructor
|
|
* @param nba The NBA
|
|
* @param options The options for Safra's algorithm
|
|
*/
|
|
template <typename NBA_t>
|
|
SafrasAlgorithm<NBA_t>::SafrasAlgorithm(NBA_t& nba, const Options_Safra &options):
|
|
_options(options),
|
|
_nba_analysis(nba),
|
|
_nba(nba),
|
|
_NODES(2*nba.getStateCount()),
|
|
stv_reorder(0)
|
|
{
|
|
_next.resize(nba.getStateCount());
|
|
}
|
|
|
|
/** Destructor */
|
|
template <typename NBA_t>
|
|
SafrasAlgorithm<NBA_t>::~SafrasAlgorithm() {
|
|
delete stv_reorder;
|
|
}
|
|
|
|
/**
|
|
* Get the next Safra tree using the
|
|
* transition function as described by Safra.
|
|
* @param tree the original tree
|
|
* @param elem the edge label
|
|
* @return a SafraTreeTemplate containing the new tree and
|
|
* bookkeeping which states were created/deleted.
|
|
*/
|
|
template <typename NBA_t>
|
|
SafraTreeTemplate_ptr
|
|
SafrasAlgorithm<NBA_t>::process(SafraTree const& tree,
|
|
APElement elem) {
|
|
// Make copy of original tree
|
|
SafraTree_ptr cur(new SafraTree(tree));
|
|
|
|
SafraTreeTemplate_ptr tree_template(new SafraTreeTemplate(cur));
|
|
|
|
SafrasAlgorithmInternal::STVisitor_reset_final_flag stv_reset_flag;
|
|
cur->walkTreePostOrder(stv_reset_flag);
|
|
|
|
#ifdef NBA2DRA_POWERSET_FIRST
|
|
SafrasAlgorithmInternal::STVisitor_powerset<NBA_t> stv_powerset(_nba, elem);
|
|
cur->walkTreePostOrder(stv_powerset);
|
|
|
|
SafrasAlgorithmInternal::STVisitor_check_finalset stv_final(_nba_analysis.getFinalStates(),
|
|
*tree_template);
|
|
cur->walkTreePostOrder(stv_final);
|
|
#else
|
|
SafrasAlgorithmInternal::STVisitor_check_finalset stv_final(_nba_analysis.getFinalStates(),
|
|
*tree_template);
|
|
cur->walkTreePostOrder(stv_final);
|
|
|
|
SafrasAlgorithmInternal::STVisitor_powerset<NBA_t> stv_powerset(_nba, elem);
|
|
cur->walkTreePostOrder(stv_powerset);
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Optimization: ACCEPTING_TRUE_LOOPS
|
|
*/
|
|
if (_options.opt_accloop) {
|
|
if (cur->getRootNode() != 0) {
|
|
SafraTreeNode *root=cur->getRootNode();
|
|
|
|
if (_nba_analysis.getStatesWithAcceptingTrueLoops().intersects(root->getLabeling())) {
|
|
// True Loop
|
|
SafrasAlgorithmInternal::STVisitor_remove_subtree stv_remove(*tree_template);
|
|
cur->walkChildrenPostOrder(stv_remove, root);
|
|
|
|
root->getLabeling().clear();
|
|
|
|
unsigned int canonical_true_loop=
|
|
_nba_analysis.getStatesWithAcceptingTrueLoops().nextSetBit(0);
|
|
root->getLabeling().set(canonical_true_loop);
|
|
root->setFinalFlag(true);
|
|
|
|
return tree_template;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SafrasAlgorithmInternal::STVisitor_check_children_horizontal stv_horizontal;
|
|
cur->walkTreePostOrder(stv_horizontal);
|
|
|
|
SafrasAlgorithmInternal::STVisitor_remove_empty stv_empty(*tree_template);
|
|
cur->walkTreePostOrder(stv_empty);
|
|
|
|
SafrasAlgorithmInternal::STVisitor_check_children_vertical stv_vertical(*tree_template);
|
|
cur->walkTreePostOrder(stv_vertical);
|
|
|
|
|
|
/*
|
|
* Optimization: REORDER
|
|
*/
|
|
if (_options.opt_reorder) {
|
|
if (stv_reorder==0) {
|
|
stv_reorder=new SafrasAlgorithmInternal::STVisitor_reorder_children(_nba_analysis.getReachability(), cur->getNodeMax());
|
|
}
|
|
|
|
cur->walkTreePostOrder(*stv_reorder);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Optimization: ALL SUCCESSORS ARE ACCEPTING
|
|
*/
|
|
if (_options.opt_accsucc) {
|
|
SafrasAlgorithmInternal::STVisitor_check_for_succ_final
|
|
stv_succ(_nba_analysis.getStatesWithAllSuccAccepting(),
|
|
*tree_template);
|
|
|
|
cur->walkTreePostOrder(stv_succ);
|
|
if (stv_succ.wasSuccessful()) {
|
|
#ifdef NBA2DRA_MERGE
|
|
if (stv_succ.wasMerged()) {
|
|
SafrasAlgorithmInternal::STVisitor_remove_empty stv_empty;
|
|
cur->walkTreePostOrder(stv_empty);
|
|
|
|
SafrasAlgorithmInternal::STVisitor_check_children_vertical stv_vertical;
|
|
cur->walkTreePostOrder(stv_vertical);
|
|
}
|
|
#endif // NBA2DRA_MERGE
|
|
}
|
|
}
|
|
|
|
return tree_template;
|
|
}
|
|
|
|
|
|
|
|
#endif
|