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.
533 lines
16 KiB
533 lines
16 KiB
/*
|
|
* This file is part of a Java port of the program ltl2dstar
|
|
* (http://www.ltl2dstar.de/) for PRISM (http://www.prismmodelchecker.org/)
|
|
* Copyright (C) 2005-2007 Joachim Klein <j.klein@ltl2dstar.de>
|
|
* Copyright (c) 2007 Carlos Bederian
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
package jltl2dstar;
|
|
|
|
/** @file
|
|
* Provides an implementation of the transition function of Safra's algorithm.
|
|
*/
|
|
import java.util.Vector;
|
|
|
|
import jltl2ba.APElement;
|
|
import jltl2ba.MyBitSet;
|
|
import prism.PrismException;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public class SafrasAlgorithm {
|
|
|
|
private Options_Safra _options;
|
|
private NBAAnalysis _nba_analysis;
|
|
private NBA _nba;
|
|
private int _NODES;
|
|
|
|
private Vector<MyBitSet> _next;
|
|
|
|
/** Caching the STVisitor_reorder_children, as it's initialization is complex. */
|
|
private STVReorderChildren stv_reorder;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param nba The NBA
|
|
* @param options The options for Safra's algorithm
|
|
*/
|
|
public SafrasAlgorithm(NBA nba, Options_Safra options) {
|
|
_options = options;
|
|
_nba_analysis = new NBAAnalysis(nba);
|
|
_nba = nba;
|
|
_NODES = 2 * nba.getStateCount();
|
|
stv_reorder = null;
|
|
_next = new Vector<MyBitSet>();
|
|
_next.setSize(nba.getStateCount());
|
|
}
|
|
|
|
|
|
// typedef SafraTreeTemplate_ptr result_t;
|
|
// typedef SafraTree_ptr state_t;
|
|
|
|
public SafraTreeTemplate delta(SafraTree tree, APElement elem) throws PrismException {
|
|
return process(tree, elem);
|
|
}
|
|
|
|
public SafraTree getStartState() {
|
|
SafraTree start = new SafraTree(_NODES);
|
|
if (_nba.getStartState() != null) {
|
|
start.getRootNode().getLabeling().set(_nba.getStartState().getName());
|
|
}
|
|
|
|
return start;
|
|
}
|
|
|
|
public void prepareAcceptance(RabinAcceptance acceptance) {
|
|
acceptance.newAcceptancePairs(_NODES);
|
|
}
|
|
|
|
public boolean checkEmpty() {
|
|
if (_nba.size() == 0 || _nba.getStartState() == null) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public SafraTreeTemplate process(SafraTree tree, APElement elem) {
|
|
// Make copy of original tree
|
|
SafraTree cur = new SafraTree(tree);
|
|
SafraTreeTemplate tree_template = new SafraTreeTemplate(cur);
|
|
// System.out.print("Init: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
STVResetFinalFlag stv_reset_flag = new STVResetFinalFlag();
|
|
cur.walkTreePostOrder(stv_reset_flag);
|
|
// System.out.print("Reset: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
STVCheckFinalSet stv_final = new STVCheckFinalSet(_nba_analysis.getFinalStates(), tree_template);
|
|
cur.walkTreePostOrder(stv_final);
|
|
// System.out.print("Final: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
STVPowerset stv_powerset = new STVPowerset(_nba, elem);
|
|
cur.walkTreePostOrder(stv_powerset);
|
|
// System.out.print("Powerset: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
|
|
/*
|
|
* Optimization: ACCEPTING_TRUE_LOOPS
|
|
*/
|
|
if (_options.opt_accloop) {
|
|
if (cur.getRootNode() != null) {
|
|
SafraTreeNode root = cur.getRootNode();
|
|
|
|
if (_nba_analysis.getStatesWithAcceptingTrueLoops().intersects(root.getLabeling())) {
|
|
// True Loop
|
|
STVRemoveSubtree stv_remove = new STVRemoveSubtree(tree_template);
|
|
cur.walkChildrenPostOrder(stv_remove, root);
|
|
|
|
root.getLabeling().clear();
|
|
|
|
int canonical_true_loop = _nba_analysis.getStatesWithAcceptingTrueLoops().nextSetBit(0);
|
|
root.getLabeling().set(canonical_true_loop);
|
|
root.setFinalFlag(true);
|
|
|
|
// System.out.print("Accloop: "); tree_template.getState().print(System.out); System.out.println();
|
|
return tree_template;
|
|
}
|
|
}
|
|
}
|
|
|
|
STVCheckChildrenHorizontal stv_horizontal = new STVCheckChildrenHorizontal();
|
|
cur.walkTreePostOrder(stv_horizontal);
|
|
// System.out.print("Horizontal: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
STVRemoveEmpty stv_empty = new STVRemoveEmpty(tree_template);
|
|
cur.walkTreePostOrder(stv_empty);
|
|
// System.out.print("Empty: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
STVCheckChildrenVertical stv_vertical = new STVCheckChildrenVertical(tree_template);
|
|
cur.walkTreePostOrder(stv_vertical);
|
|
// System.out.print("Vertical: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
/*
|
|
* Optimization: REORDER
|
|
*/
|
|
if (_options.opt_reorder) {
|
|
if (stv_reorder == null) {
|
|
stv_reorder = new STVReorderChildren(_nba_analysis.getReachability(), cur.getNodeMax());
|
|
}
|
|
|
|
cur.walkTreePostOrder(stv_reorder);
|
|
}
|
|
// System.out.print("Reorder: "); tree_template.getState().print(System.out); System.out.println();
|
|
|
|
/*
|
|
* Optimization: ALL SUCCESSORS ARE ACCEPTING
|
|
*/
|
|
if (_options.opt_accsucc) {
|
|
STVCheckForFinalSucc stv_succ = new STVCheckForFinalSucc(_nba_analysis.getStatesWithAllSuccAccepting(), tree_template);
|
|
|
|
cur.walkTreePostOrder(stv_succ);
|
|
}
|
|
// System.out.print("Accsucc: "); tree_template.getState().print(System.out); System.out.println();
|
|
return tree_template;
|
|
}
|
|
|
|
/** Visitors */
|
|
public interface SafraTreeVisitor {
|
|
public void visit(SafraTree tree, SafraTreeNode node);
|
|
}
|
|
|
|
/**
|
|
* A Safra tree visitor that modifies the
|
|
* children so that all children have
|
|
* disjoint labels
|
|
*/
|
|
public class STVCheckChildrenHorizontal implements SafraTreeVisitor {
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
if (node.getChildCount() <= 1) {
|
|
return;
|
|
}
|
|
|
|
MyBitSet already_seen = new MyBitSet(); // FIXME: although it's not used, hmph warnings
|
|
boolean first = true;
|
|
for (SafraTreeNode cur_child : node) {
|
|
if (first) {
|
|
already_seen = (MyBitSet) cur_child.getLabeling().clone();
|
|
first = false;
|
|
} else {
|
|
MyBitSet current = cur_child.getLabeling();
|
|
|
|
MyBitSet intersection = (MyBitSet) already_seen.clone(); // make copy
|
|
if (intersection.intersects(current)) {
|
|
// There are some labels, which occur in older brothers,
|
|
// remove them from current node and its children
|
|
STVSubstractLabeling stv_sub = new STVSubstractLabeling(intersection);
|
|
tree.walkSubTreePostOrder(stv_sub, cur_child);
|
|
}
|
|
already_seen.or(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.
|
|
*/
|
|
public class STVCheckChildrenVertical implements SafraTreeVisitor {
|
|
|
|
private SafraTreeTemplate _tree_template;
|
|
|
|
public STVCheckChildrenVertical(SafraTreeTemplate tree_template) {
|
|
_tree_template = tree_template;
|
|
}
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
if (node.getChildCount() == 0) {return;}
|
|
|
|
MyBitSet labeling_union = new MyBitSet();
|
|
for (SafraTreeNode child : node) {
|
|
labeling_union.or(child.getLabeling());
|
|
}
|
|
|
|
if (labeling_union.equals(node.getLabeling())) {
|
|
// The union of the labelings of the children is exactly the
|
|
// same as the labeling of the parent ->
|
|
// remove children
|
|
STVRemoveSubtree stv_remove = new STVRemoveSubtree(_tree_template);
|
|
tree.walkChildrenPostOrder(stv_remove, node);
|
|
|
|
node.setFinalFlag(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public class STVCheckFinalSet implements SafraTreeVisitor {
|
|
|
|
private MyBitSet _final_states;
|
|
private SafraTreeTemplate _tree_template;
|
|
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
public STVCheckFinalSet(MyBitSet final_states, SafraTreeTemplate tree_template) {
|
|
|
|
_final_states = final_states;
|
|
_tree_template = tree_template;
|
|
}
|
|
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
if (_final_states.intersects(node.getLabeling())) {
|
|
MyBitSet q_and_f = (MyBitSet) _final_states.clone();
|
|
q_and_f.and(node.getLabeling());
|
|
|
|
SafraTreeNode new_child = tree.newNode();
|
|
node.addAsYoungestChild(new_child);
|
|
|
|
_tree_template.setRenameable(new_child.getID(), true);
|
|
|
|
new_child.setLabeling(q_and_f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public class STVCheckForFinalSucc implements SafraTreeVisitor {
|
|
private boolean _success;
|
|
private MyBitSet _nba_states_with_all_succ_final;
|
|
private SafraTreeTemplate _tree_template;
|
|
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
public STVCheckForFinalSucc(MyBitSet 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. */
|
|
public boolean wasSuccessful() {return _success;}
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
|
|
boolean all_final = true;
|
|
for (int i = node.getLabeling().nextSetBit(0); i >= 0; i = node.getLabeling().nextSetBit(i+1)) {
|
|
if (! _nba_states_with_all_succ_final.get(i)) {
|
|
all_final = false;
|
|
break;
|
|
}
|
|
}
|
|
if (all_final) {
|
|
// remove all children of node & set final flag
|
|
STVRemoveSubtree stv_remove = new STVRemoveSubtree(_tree_template);
|
|
tree.walkChildrenPostOrder(stv_remove, node);
|
|
node.setFinalFlag(true);
|
|
_success=true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Safra tree visitor that performs the powerset construction
|
|
* on the label of the Safra tree node.
|
|
*/
|
|
public class STVPowerset implements SafraTreeVisitor {
|
|
|
|
private NBA _nba;
|
|
private APElement _elem;
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public STVPowerset(NBA nba, APElement elem) {
|
|
_nba = nba;
|
|
_elem = elem;
|
|
}
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
MyBitSet old_labeling = node.getLabeling();
|
|
MyBitSet new_labeling = new MyBitSet(old_labeling.size());
|
|
for (int i = old_labeling.nextSetBit(0); i >= 0; i = old_labeling.nextSetBit(i+1)) {
|
|
new_labeling.or(_nba.get(i).getEdge(_elem));
|
|
}
|
|
node.setLabeling(new_labeling);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A Safra tree visitor that removes tree nodes
|
|
* with empty labels.
|
|
*/
|
|
public class STVRemoveEmpty implements SafraTreeVisitor {
|
|
|
|
private SafraTreeTemplate _tree_template;
|
|
|
|
public STVRemoveEmpty(SafraTreeTemplate tree_template) {
|
|
_tree_template = tree_template;
|
|
}
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
if (node.getLabeling().isEmpty()) {
|
|
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, true);
|
|
}
|
|
|
|
tree.remove(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A Safra tree visitor that removes all
|
|
* children of the node.
|
|
*/
|
|
public class STVRemoveSubtree implements SafraTreeVisitor {
|
|
|
|
private SafraTreeTemplate _tree_template;
|
|
|
|
public STVRemoveSubtree(SafraTreeTemplate tree_template) {
|
|
_tree_template = tree_template;
|
|
}
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
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, true);
|
|
}
|
|
|
|
tree.remove(node);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public class STVReorderChildren implements SafraTreeVisitor {
|
|
|
|
private Vector<MyBitSet> _nba_reachability;
|
|
private MyBitSet[] _node_reachability;
|
|
private int[] _node_order;
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
public STVReorderChildren(Vector<MyBitSet> nba_reachability, int N) {
|
|
_nba_reachability = nba_reachability;
|
|
_node_order = new int[N];
|
|
_node_reachability = new MyBitSet[N];
|
|
for (int i = 0; i < N; i++) {
|
|
_node_reachability[i] = new MyBitSet();
|
|
}
|
|
}
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
if (node.getChildCount() <= 1) {return;}
|
|
|
|
int i = 0;
|
|
for (SafraTreeNode child : node) {
|
|
MyBitSet reachable_this = _node_reachability[child.getID()];
|
|
reachable_this.clear();
|
|
_node_order[child.getID()] = i++;
|
|
|
|
MyBitSet label_this = child.getLabeling();
|
|
for (int setbit = label_this.nextSetBit(0); i >= 0; i = label_this.nextSetBit(i+1)) {
|
|
reachable_this.or(_nba_reachability.get(setbit));
|
|
}
|
|
// std::cerr << "reachability_this: "<<reachable_this << std::endl;
|
|
}
|
|
|
|
// reorder...
|
|
// std::cerr << "Sorting!" << std::endl;
|
|
|
|
// Bubble sort, ough!
|
|
boolean finished = false;
|
|
while (!finished) {
|
|
finished=true;
|
|
|
|
for (SafraTreeNode a = node.getOldestChild();
|
|
a != null && a.getYoungerBrother() != null;
|
|
a = a.getYoungerBrother()) {
|
|
|
|
SafraTreeNode b = a.getYoungerBrother();
|
|
|
|
MyBitSet reach_a = _node_reachability[a.getID()];
|
|
MyBitSet 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().compareTo(b.getLabeling()) >= 0) {
|
|
// swap
|
|
node.swapChildren(a,b);
|
|
a=b;
|
|
finished=false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Safra tree visitor that resets the final flag on the Safra tree node.
|
|
*/
|
|
public class STVResetFinalFlag implements SafraTreeVisitor {
|
|
|
|
/** Node visitor */
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
node.setFinalFlag(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A Safra tree visitor that subtracts (minus operator) a BitSet from
|
|
* the label of the tree node.
|
|
*/
|
|
public class STVSubstractLabeling implements SafraTreeVisitor {
|
|
|
|
private MyBitSet _bitset;
|
|
|
|
public STVSubstractLabeling(MyBitSet bitset) {
|
|
_bitset = bitset;
|
|
}
|
|
|
|
public void visit(SafraTree tree, SafraTreeNode node) {
|
|
node.getLabeling().andNot(_bitset);
|
|
}
|
|
}
|
|
}
|