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.
 
 
 
 
 
 

284 lines
8.9 KiB

//==============================================================================
//
// Copyright (c) 2002-
// Authors:
// * Dave Parker <d.a.parker@cs.bham.ac.uk> (University of Birmingham/Oxford)
//
//------------------------------------------------------------------------------
//
// This file is part of PRISM.
//
// PRISM is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// PRISM 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 PRISM; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//==============================================================================
package explicit;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import parser.State;
import prism.PrismComponent;
import prism.PrismException;
import prism.PrismNotSupportedException;
/**
* Class to perform bisimulation minimisation for explicit-state models.
*/
public class Bisimulation extends PrismComponent
{
// Local storage of partition info
protected int numStates;
protected int[] partition;
protected int numBlocks;
protected MDPSimple mdp;
/**
* Construct a new Bisimulation object.
*/
public Bisimulation(PrismComponent parent) throws PrismException
{
super(parent);
}
/**
* Perform bisimulation minimisation on a model.
* @param model The model
* @param propNames Names of the propositions in {@code propBSs}
* @param propBSs Propositions (satisfying sets of states) to be preserved by bisimulation.
*/
public Model minimise(Model model, List<String> propNames, List<BitSet> propBSs) throws PrismException
{
switch (model.getModelType()) {
case DTMC:
return minimiseDTMC((DTMC) model, propNames, propBSs);
case CTMC:
return minimiseCTMC((CTMC) model, propNames, propBSs);
default:
throw new PrismNotSupportedException("Bisimulation minimisation not yet supported for " + model.getModelType() + "s");
}
}
/**
* Perform bisimulation minimisation on a DTMC.
* @param dtmc The DTMC
* @param propNames Names of the propositions in {@code propBSs}
* @param propBSs Propositions (satisfying sets of states) to be preserved by bisimulation.
*/
private DTMC minimiseDTMC(DTMC dtmc, List<String> propNames, List<BitSet> propBSs)
{
// Create initial partition based on propositions
initialisePartitionInfo(dtmc, propBSs);
//printPartition(dtmc);
// Iterative splitting
boolean changed = true;
while (changed)
changed = splitDTMC(dtmc);
mainLog.println("Minimisation: " + numStates + " to " + numBlocks + " States");
//printPartition(dtmc);
// Build reduced model
DTMCSimple dtmcNew = new DTMCSimple(numBlocks);
for (int i = 0; i < numBlocks; i++) {
for (Map.Entry<Integer, Double> e : mdp.getChoice(i, 0)) {
dtmcNew.setProbability((Integer) mdp.getAction(i, 0), e.getKey(), e.getValue());
}
}
attachStatesAndLabels(dtmc, dtmcNew, propNames, propBSs);
return dtmcNew;
}
/**
* Perform bisimulation minimisation on a CTMC.
* @param ctmc The CTMC
* @param propNames Names of the propositions in {@code propBSs}
* @param propBSs Propositions (satisfying sets of states) to be preserved by bisimulation.
*/
private CTMC minimiseCTMC(CTMC ctmc, List<String> propNames, List<BitSet> propBSs)
{
// Create initial partition based on propositions
initialisePartitionInfo(ctmc, propBSs);
//printPartition(ctmc);
// Iterative splitting
boolean changed = true;
while (changed)
changed = splitDTMC(ctmc);
mainLog.println("Minimisation: " + numStates + " to " + numBlocks + " States");
//printPartition(ctmc);
// Build reduced model
CTMCSimple ctmcNew = new CTMCSimple(numBlocks);
for (int i = 0; i < numBlocks; i++) {
for (Map.Entry<Integer, Double> e : mdp.getChoice(i, 0)) {
ctmcNew.setProbability((Integer) mdp.getAction(i, 0), e.getKey(), e.getValue());
}
}
attachStatesAndLabels(ctmc, ctmcNew, propNames, propBSs);
return ctmcNew;
}
/**
* Construct the initial partition based on a set of proposition bitsets.
* Store info in {@code numStates}, {@code numBlocks} and {@code partition}.
*/
private void initialisePartitionInfo(Model model, List<BitSet> propBSs)
{
BitSet bs1, bs0;
numStates = model.getNumStates();
partition = new int[numStates];
// Compute all non-empty combinations of propositions
List<BitSet> all = new ArrayList<BitSet>();
bs1 = (BitSet) propBSs.get(0).clone();
bs0 = (BitSet) bs1.clone();
bs0.flip(0, numStates);
all.add(bs1);
all.add(bs0);
int n = propBSs.size();
for (int i = 1; i < n; i++) {
BitSet bs = propBSs.get(i);
int m = all.size();
for (int j = 0; j < m; j++) {
bs1 = all.get(j);
bs0 = (BitSet) bs1.clone();
bs0.andNot(bs);
bs1.and(bs);
if (bs1.isEmpty()) {
all.set(j, bs0);
} else {
if (!bs0.isEmpty())
all.add(bs0);
}
}
}
// Construct initial partition
numBlocks = all.size();
for (int j = 0; j < numBlocks; j++) {
BitSet bs = all.get(j);
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
partition[i] = j;
}
}
}
/**
* Perform a split of the current partition, if possible, updating {@code numBlocks} and {@code partition}.
* @return whether or not the partition was split
*/
private boolean splitDTMC(DTMC dtmc)
{
int s, a, i, numBlocksNew, numChoicesOld;
Distribution distrNew;
int partitionNew[];
partitionNew = new int[numStates];
numBlocksNew = 0;
// Compute the signature for each state (i.e. the distribution for outgoing
// transitions, lifted to the current partition)
// For convenience, we just store them as an MDP, with action label equal to the index of the block
mdp = new MDPSimple(numBlocks);
for (s = 0; s < numStates; s++) {
// Build lifted distribution
Iterator<Map.Entry<Integer, Double>> iter = dtmc.getTransitionsIterator(s);
distrNew = new Distribution();
while (iter.hasNext()) {
Map.Entry<Integer, Double> e = iter.next();
distrNew.add(partition[e.getKey()], e.getValue());
}
// Store in MDP, update new partition
a = partition[s];
numChoicesOld = mdp.getNumChoices(a);
i = mdp.addChoice(a, distrNew);
if (i == numChoicesOld)
mdp.setAction(a, i, numBlocksNew++);
partitionNew[s] = (Integer) mdp.getAction(a, i);
}
// Debug info
//mainLog.println("New partition: " + java.util.Arrays.toString(partitionNew));
//mainLog.println("Signatures MDP: " + mdp.infoString());
//mainLog.println("Signatures MDP: " + mdp);
//try { mdp.exportToDotFile("mdp.dot"); } catch (PrismException e) {}
// Update info
boolean changed = numBlocks != numBlocksNew;
partition = partitionNew;
numBlocks = numBlocksNew;
return changed;
}
/**
* Display the current partition, showing the states in each block.
*/
@SuppressWarnings("unused")
private void printPartition(Model model)
{
for (int i = 0; i < numBlocks; i++) {
mainLog.print(i + ":");
for (int j = 0; j < numStates; j++)
if (partition[j] == i)
if (model.getStatesList() != null)
mainLog.print(" " + model.getStatesList().get(j));
else
mainLog.print(" " + j);
mainLog.println();
}
}
/**
* Attach a list of states to the minimised model by adding a representative state
* from the original model.
* Also attach information about the propositions (used for bisimulation minimisation)
* to the minimised model, in the form of labels (stored as BitSets).
* @param model The original model
* @param modelNew The minimised model
* @param propNames The names of the propositions
* @param propBSs Satisfying states (of the minimised model) for the propositions
*/
private void attachStatesAndLabels(Model model, ModelExplicit modelNew, List<String> propNames, List<BitSet> propBSs)
{
// Attach states
if (model.getStatesList() != null) {
List<State> statesList = model.getStatesList();
List<State> statesListNew = new ArrayList<State>(numBlocks);
for (int i = 0; i < numBlocks; i++) {
statesListNew.add(null);
}
for (int i = 0; i < numStates; i++) {
if (statesListNew.get(partition[i]) == null)
statesListNew.set(partition[i], statesList.get(i));
}
modelNew.setStatesList(statesListNew);
}
// Build/attach new labels
int numProps = propBSs.size();
for (int i = 0; i < numProps; i++) {
String propName = propNames.get(i);
BitSet propBS = propBSs.get(i);
BitSet propBSnew = new BitSet();
for (int j = propBS.nextSetBit(0); j >= 0; j = propBS.nextSetBit(j + 1))
propBSnew.set(partition[j]);
modelNew.addLabel(propName, propBSnew);
}
}
}