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.
495 lines
14 KiB
495 lines
14 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 optimizations on complete DRAs, notably quotienting using bisimulation.
|
|
*/
|
|
import java.util.*;
|
|
import java.io.PrintStream;
|
|
|
|
import jltl2ba.APElement;
|
|
import jltl2ba.APElementIterator;
|
|
import jltl2ba.MyBitSet;
|
|
import prism.PrismException;
|
|
|
|
/**
|
|
* Provides optimizations on complete DRAs, notably quotienting using bisimulation.
|
|
*/
|
|
public class DRAOptimizations {
|
|
|
|
/** type of a color */
|
|
// typedef unsigned int color_t;
|
|
/** type of vector state indizes */
|
|
// typedef std::vector<unsigned int> state_vector;
|
|
|
|
/** Helper class, storing a coloring of the states */
|
|
public static class Coloring {
|
|
|
|
/** The number of colors */
|
|
private int _nr_of_colors;
|
|
|
|
/** mapping state_id -> color */
|
|
private Vector<Integer> _coloring;
|
|
|
|
/** Keep detailed information of the equivalence classes? */
|
|
private boolean _detailed;
|
|
|
|
/**
|
|
* mapping from color
|
|
* -> the state ids which are colored alike
|
|
* only used when _detailed=true */
|
|
private Vector<MyBitSet> _color2states;
|
|
|
|
/**
|
|
* mapping from color -> one representative state
|
|
*/
|
|
private Vector<Integer> _color2state;
|
|
|
|
/**
|
|
* Constructor, get initial size of the coloring from DRA.
|
|
* @param dra the DRA
|
|
* @param detailed Keep detailed information on the equivalence classes? (default: false)
|
|
*/
|
|
public Coloring(DRA dra, boolean detailed) {
|
|
_nr_of_colors = 0;
|
|
_detailed = detailed;
|
|
_coloring = new Vector<Integer>(dra.size());
|
|
_coloring.setSize(dra.size());
|
|
_color2state = new Vector<Integer>();
|
|
if (_detailed) {
|
|
_color2states = new Vector<MyBitSet>();
|
|
} else {
|
|
_color2states = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructor, explicitly set initial size of the coloring
|
|
* @param size the initial size
|
|
* @param detailed Keep detailed information on the equivalence classes? (default: false)
|
|
*/
|
|
public Coloring(int size, boolean detailed) {
|
|
_nr_of_colors = 0;
|
|
_detailed = detailed;
|
|
_coloring = new Vector<Integer>(size);
|
|
_coloring.setSize(size);
|
|
_color2state = new Vector<Integer>();
|
|
|
|
if (_detailed) {
|
|
_color2states = new Vector<MyBitSet>();
|
|
} else {
|
|
_color2states = null;
|
|
}
|
|
}
|
|
|
|
/** Reset (clear) coloring. */
|
|
public void reset() {_nr_of_colors = 0;}
|
|
|
|
/** Get the flag 'detailed' */
|
|
public boolean getFlagDetailed() {return _detailed;}
|
|
|
|
/** Returns the size (number of states) of this coloring. */
|
|
public int size() {return _coloring.size();}
|
|
|
|
/**
|
|
* Create a new color
|
|
* @return the newly created color
|
|
*/
|
|
public int newColor() {
|
|
_nr_of_colors++;
|
|
|
|
_color2state.setSize(_nr_of_colors);
|
|
|
|
if (_detailed) {
|
|
_color2states.setSize(_nr_of_colors);
|
|
_color2states.set(_nr_of_colors - 1, new MyBitSet());
|
|
}
|
|
|
|
return _nr_of_colors - 1;
|
|
}
|
|
|
|
/** Return the current (last created) color */
|
|
public int currentColor() {
|
|
assert(_nr_of_colors > 0);
|
|
return _nr_of_colors - 1;
|
|
}
|
|
|
|
/** Return the number of colors */
|
|
public int countColors() {
|
|
return _nr_of_colors;
|
|
}
|
|
|
|
/** Set the color of a state */
|
|
public void setColor(int state, int color) {
|
|
assert(color < _nr_of_colors);
|
|
|
|
_coloring.set(state, new Integer(color));
|
|
_color2state.set(color, new Integer(state));
|
|
|
|
if (_detailed) {
|
|
_color2states.get(color).set(state);
|
|
}
|
|
}
|
|
|
|
/** Get the color for a state */
|
|
public int state2color(int state) {
|
|
return _coloring.get(state);
|
|
}
|
|
|
|
/**
|
|
*Get one representative state for the equivalence class with the
|
|
* specified color.
|
|
*/
|
|
public int color2state(int color) {
|
|
assert(color < _nr_of_colors);
|
|
return _color2state.get(color);
|
|
}
|
|
|
|
/**
|
|
* Get the state indizes (in a BitSet) that have the specified color.
|
|
* Can only be called, when the 'detailed' flag is activated in the
|
|
* constructor.
|
|
*/
|
|
public MyBitSet color2states(int color) {
|
|
assert(color < _nr_of_colors);
|
|
assert(_detailed && _color2states != null);
|
|
return _color2states.get(color);
|
|
}
|
|
|
|
/** Print the coloring */
|
|
public void print(PrintStream out) {
|
|
for (int i = 0; i < this.size(); i++) {
|
|
out.println("color[" + i + "] = " + this.state2color(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Functor, provides a 'less-than' Comparator
|
|
* for the states of the DRA, using the color of
|
|
* the states themself and the colors of the
|
|
* to-states of the edges.
|
|
*/
|
|
public static class ColoredStateComparator implements Comparator<Integer> {
|
|
|
|
/** The coloring */
|
|
private Coloring _coloring;
|
|
/** The DRA */
|
|
private DRA _dra;
|
|
|
|
/** Constructor */
|
|
public ColoredStateComparator(Coloring coloring, DRA dra) {
|
|
_coloring = coloring;
|
|
_dra = dra;
|
|
}
|
|
|
|
/**
|
|
* Compares two states 'less-than' using the
|
|
* coloring, uses the bisimulation
|
|
* equivalence relation to determine
|
|
* equality.
|
|
*/
|
|
public int compare(Integer state_x, Integer state_y) {
|
|
int cx = _coloring.state2color(state_x);
|
|
int cy = _coloring.state2color(state_y);
|
|
|
|
if (cx < cy) {
|
|
return -1;
|
|
} else if (cx > cy) {
|
|
return 1;
|
|
}
|
|
|
|
for (APElementIterator it = new APElementIterator(_dra.getAPSize()); it.hasNext(); ) {
|
|
APElement label = it.next();
|
|
DA_State to_x = _dra.get(state_x).edges().get(label);
|
|
DA_State to_y = _dra.get(state_y).edges().get(label);
|
|
|
|
int ctx = _coloring.state2color(to_x.getName());
|
|
int cty = _coloring.state2color(to_y.getName());
|
|
|
|
if (ctx < cty) {
|
|
return -1;
|
|
} else if (ctx > cty) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// we get here only if x and y are equal with this
|
|
// coloring -> return false
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/** Type of an acceptance signature */
|
|
// public typedef std::pair<BitSet*, BitSet*> acceptance_signature_t;
|
|
public static class AcceptanceSignature {
|
|
public MyBitSet l;
|
|
public MyBitSet u;
|
|
|
|
public AcceptanceSignature() {;}
|
|
}
|
|
|
|
/**
|
|
* A container that stores (caches) the acceptance signatures of
|
|
* all the states in a DRA.
|
|
*/
|
|
public static class AcceptanceSignatureContainer {
|
|
|
|
/** Storage for the acceptance signatures */
|
|
private Vector<AcceptanceSignature> _acceptancesig_vector;
|
|
|
|
/**
|
|
* Constructor, fills the container with the acceptance signatures of the states.
|
|
* @param dra the DRA
|
|
*/
|
|
public AcceptanceSignatureContainer(DRA dra) {
|
|
_acceptancesig_vector = new Vector<AcceptanceSignature>(dra.size());
|
|
|
|
for (int i = 0; i < dra.size(); i++) {
|
|
_acceptancesig_vector.add(new AcceptanceSignature());
|
|
_acceptancesig_vector.get(i).l = (MyBitSet) dra.acceptance().getAcceptance_L_forState(i).clone();
|
|
_acceptancesig_vector.get(i).u = (MyBitSet) dra.acceptance().getAcceptance_U_forState(i).clone();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the acceptance signature for state i.
|
|
* @param i the state index
|
|
*/
|
|
public AcceptanceSignature get(int i) {
|
|
return _acceptancesig_vector.get(i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Functor that compares two DRA states based on their
|
|
* acceptance signature.
|
|
*/
|
|
public static class AcceptanceSignatureComparator implements Comparator<Integer> {
|
|
/** The acceptance signature container */
|
|
private AcceptanceSignatureContainer _container;
|
|
|
|
/** Constructor */
|
|
public AcceptanceSignatureComparator(AcceptanceSignatureContainer container) {
|
|
_container = container;
|
|
}
|
|
|
|
/**
|
|
* Compares (less-than) two DRAState indizes based on their
|
|
* acceptance signature.
|
|
*/
|
|
public int compare(Integer x, Integer y) {
|
|
AcceptanceSignature px = _container.get(x);
|
|
AcceptanceSignature py = _container.get(y);
|
|
|
|
return (px.l.compareTo(py.l) == 0 ? px.u.compareTo(py.u) : px.l.compareTo(py.l));
|
|
}
|
|
|
|
public boolean equals(Integer x, Integer y) {
|
|
return compare(x,y) == 0;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Perform quotienting using bisimulation
|
|
* @param dra the DRA to be optimized
|
|
* @param printColoring print colorings on std::cerr? (default: false)
|
|
* @param detailedStates save detailed information on the interals in the state? (default: false)
|
|
* @param printStats print statistics on std::cerr? (default: false)
|
|
* @return shared_ptr to the quotiented DRA
|
|
*/
|
|
public DRA optimizeBisimulation(DRA dra, boolean printColoring, boolean detailedStates, boolean printStats) throws PrismException {
|
|
if (!dra.isCompact())
|
|
dra.makeCompact();
|
|
|
|
Vector<Integer> states = new Vector<Integer>(dra.size());
|
|
states.setSize(dra.size());
|
|
|
|
for (int i = 0; i < dra.size(); i++) {
|
|
states.set(i, new Integer(i));
|
|
}
|
|
|
|
AcceptanceSignatureContainer accsig_container = new AcceptanceSignatureContainer(dra);
|
|
AcceptanceSignatureComparator accsig_comp = new AcceptanceSignatureComparator(accsig_container);
|
|
|
|
|
|
Coloring tmpcoloring = new Coloring(dra, detailedStates);
|
|
// generate initial coloring by running with the
|
|
// different acceptance signature
|
|
Coloring coloring = generateColoring(states, tmpcoloring, accsig_comp);
|
|
|
|
int old_size = dra.size();
|
|
int initial_partition = coloring.countColors();
|
|
|
|
int oldColors;
|
|
do {
|
|
oldColors = coloring.countColors();
|
|
|
|
ColoredStateComparator cnc = new ColoredStateComparator(coloring, dra);
|
|
|
|
tmpcoloring = generateColoring(states, coloring, cnc);
|
|
coloring = tmpcoloring;
|
|
} while (oldColors != coloring.countColors());
|
|
|
|
if (printColoring) {
|
|
coloring.print(System.err);
|
|
}
|
|
|
|
DRA dra_new = generateDRAfromColoring(dra, coloring, detailedStates);
|
|
|
|
int new_size=dra_new.size();
|
|
|
|
if (printStats) {
|
|
System.err.println("Bisimulation: From (" + old_size + ") To (" + new_size + ") Initial: (" + initial_partition + ")");
|
|
}
|
|
return dra_new;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Generate a new coloring based on the Comparator comp
|
|
* (one iteration of refinement)
|
|
* @param states A vector of the states
|
|
* @param coloring The current coloring
|
|
* @param comp the Comparator
|
|
* @return a pointer to a newly created Coloring, memory ownership
|
|
* passes to the caller
|
|
*/
|
|
private <T extends Comparator<Integer>>Coloring generateColoring(Vector<Integer> states, Coloring coloring, T comp) {
|
|
Integer[] statearray = (Integer[]) states.toArray(new Integer[0]);
|
|
Arrays.sort(statearray, comp);
|
|
Vector<Integer> sortedstates = new Vector<Integer>(Arrays.asList(statearray));
|
|
|
|
Coloring result = new Coloring(coloring.size(), coloring.getFlagDetailed());
|
|
|
|
if (sortedstates.size() == 0) {return result;}
|
|
|
|
// reverse iterators
|
|
ListIterator<Integer> current = sortedstates.listIterator(sortedstates.size());
|
|
ListIterator<Integer> last = sortedstates.listIterator(sortedstates.size());
|
|
|
|
result.setColor(current.previous(), result.newColor());
|
|
|
|
// 0 .. n-3 n-2 n-1
|
|
// ^ last
|
|
// ^ current
|
|
while (current.hasPrevious()) {
|
|
Integer curr = current.previous();
|
|
// because states is sorted and we traverse
|
|
// from the end, either:
|
|
// *current < *last with comp(current,last)==true
|
|
// or *current == *last with !comp(current,last)
|
|
|
|
if (comp.compare(curr, last.previous()) < 0) {
|
|
// -> we have to start a new color
|
|
result.setColor(curr, result.newColor());
|
|
} else {
|
|
// -> more of the same, we stay with this color
|
|
result.setColor(curr, result.currentColor());
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate a new DRA from a coloring
|
|
*/
|
|
private DRA generateDRAfromColoring(DRA oldDRA, Coloring coloring, boolean detailedStates) throws PrismException {
|
|
DRA newDRA = new DRA(oldDRA.getAPSet());
|
|
|
|
newDRA.acceptance().newAcceptancePairs(oldDRA.acceptance().size());
|
|
|
|
for (int color = 0; color < coloring.countColors(); ++color) {
|
|
newDRA.newState();
|
|
}
|
|
|
|
int old_start_state = oldDRA.getStartState().getName();
|
|
int start_state_color = coloring.state2color(old_start_state);
|
|
|
|
newDRA.setStartState(newDRA.get(start_state_color));
|
|
|
|
for (int color = 0; color < coloring.countColors(); ++color) {
|
|
DA_State new_state = newDRA.get(color);
|
|
|
|
int old_state_representative = coloring.color2state(color);
|
|
|
|
DA_State old_state = oldDRA.get(old_state_representative);
|
|
|
|
if (detailedStates) {
|
|
MyBitSet old_states = coloring.color2states(color);
|
|
|
|
// create new description...
|
|
if (old_states.cardinality() == 1) {
|
|
if (old_state.hasDescription()) {
|
|
new_state.setDescription(old_state.getDescription());
|
|
}
|
|
} else {
|
|
String s = "<TABLE BORDER=\"1\" CELLBORDER=\"0\"><TR><TD>{</TD>";
|
|
|
|
boolean first = true;
|
|
for (Integer state : old_states) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
s += "<TD>,</TD>";
|
|
}
|
|
|
|
s += "<TD>";
|
|
if (!oldDRA.get(state).hasDescription()) {
|
|
s += state;
|
|
} else {
|
|
s += oldDRA.get(state).getDescription();
|
|
}
|
|
s += "</TD>";
|
|
}
|
|
s += "<TD>}</TD></TR></TABLE>";
|
|
|
|
new_state.setDescription(s);
|
|
}
|
|
}
|
|
|
|
// Create appropriate acceptance conditions
|
|
int old_state_index = old_state.getName();
|
|
for (int i = 0; i < oldDRA.acceptance().size(); ++i) {
|
|
if (oldDRA.acceptance().isStateInAcceptance_L(i, old_state_index)) {
|
|
new_state.acceptance().addTo_L(i);
|
|
}
|
|
|
|
if (oldDRA.acceptance().isStateInAcceptance_U(i, old_state_index)) {
|
|
new_state.acceptance().addTo_U(i);
|
|
}
|
|
}
|
|
|
|
for (Map.Entry<APElement,DA_State> edge : old_state.edges().entrySet()) {
|
|
|
|
int to_color = coloring.state2color(edge.getValue().getName());
|
|
|
|
new_state.edges().put(edge.getKey(), newDRA.get(to_color));
|
|
}
|
|
}
|
|
|
|
return newDRA;
|
|
}
|
|
}
|