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.
510 lines
16 KiB
510 lines
16 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2002-
|
|
// Authors:
|
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford)
|
|
// * Vojtech Forejt <vojtech.forejt@cs.ox.ac.uk> (University of 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.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.util.ArrayList;
|
|
import java.util.BitSet;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
import parser.Observation;
|
|
import parser.State;
|
|
import parser.Unobservation;
|
|
import parser.Values;
|
|
import parser.VarList;
|
|
import prism.ModelGenerator;
|
|
import prism.ModelType;
|
|
import prism.Prism;
|
|
import prism.PrismComponent;
|
|
import prism.PrismException;
|
|
import prism.PrismLog;
|
|
import prism.PrismNotSupportedException;
|
|
import prism.PrismPrintStreamLog;
|
|
import prism.ProgressDisplay;
|
|
import prism.UndefinedConstants;
|
|
|
|
/**
|
|
* Class to perform explicit-state reachability and model construction.
|
|
* The information about the model to be built is provided via a {@link prism.ModelGenerator} interface.
|
|
* To build a PRISM model, use {@link simulator.ModulesFileModelGenerator}.
|
|
*/
|
|
public class ConstructModel extends PrismComponent
|
|
{
|
|
// Options:
|
|
|
|
/** Find deadlocks during model construction? */
|
|
protected boolean findDeadlocks = true;
|
|
/** Automatically fix deadlocks? */
|
|
protected boolean fixDeadlocks = true;
|
|
/** Sort the reachable states before constructing the model? */
|
|
protected boolean sortStates = true;
|
|
/** Build a sparse representation, if possible?
|
|
* (e.g. MDPSparse rather than MDPSimple data structure) */
|
|
protected boolean buildSparse = true;
|
|
/** Should actions be attached to distributions (and used to distinguish them)? */
|
|
protected boolean distinguishActions = true;
|
|
/** Should labels be processed and attached to the model? */
|
|
protected boolean attachLabels = true;
|
|
|
|
// Details of built model:
|
|
|
|
/** Reachable states */
|
|
protected List<State> statesList;
|
|
|
|
public ConstructModel(PrismComponent parent) throws PrismException
|
|
{
|
|
super(parent);
|
|
}
|
|
|
|
/**
|
|
* Get the list of states associated with the last model construction performed.
|
|
*/
|
|
public List<State> getStatesList()
|
|
{
|
|
return statesList;
|
|
}
|
|
|
|
/**
|
|
* Automatically fix deadlocks, if needed?
|
|
* (by adding self-loops in those states)
|
|
*/
|
|
public void setFixDeadlocks(boolean fixDeadlocks)
|
|
{
|
|
this.fixDeadlocks = fixDeadlocks;
|
|
}
|
|
|
|
/**
|
|
* Sort the reachable states before constructing the model?
|
|
*/
|
|
public void setSortStates(boolean sortStates)
|
|
{
|
|
this.sortStates = sortStates;
|
|
}
|
|
|
|
/**
|
|
* Build a sparse representation, if possible?
|
|
* (e.g. MDPSparse rather than MDPSimple data structure)
|
|
*/
|
|
public void setBuildSparse(boolean buildSparse)
|
|
{
|
|
this.buildSparse = buildSparse;
|
|
}
|
|
|
|
/**
|
|
* Should actions be attached to distributions (and used to distinguish them)?
|
|
*/
|
|
public void setDistinguishActions(boolean distinguishActions)
|
|
{
|
|
this.distinguishActions = distinguishActions;
|
|
}
|
|
|
|
/**
|
|
* Should labels be processed and attached to the model?
|
|
*/
|
|
public void setAttachLabels(boolean attachLabels)
|
|
{
|
|
this.attachLabels = attachLabels;
|
|
}
|
|
|
|
/**
|
|
* Build the set of reachable states for a model and return it.
|
|
* @param modelGen The ModelGenerator interface providing the model
|
|
*/
|
|
public List<State> computeReachableStates(ModelGenerator modelGen) throws PrismException
|
|
{
|
|
constructModel(modelGen, true);
|
|
return getStatesList();
|
|
}
|
|
|
|
/**
|
|
* Construct an explicit-state model and return it.
|
|
* @param modelGen The ModelGenerator interface providing the model
|
|
*/
|
|
public Model constructModel(ModelGenerator modelGen) throws PrismException
|
|
{
|
|
return constructModel(modelGen, false);
|
|
}
|
|
|
|
/**
|
|
* Construct an explicit-state model and return it.
|
|
* If {@code justReach} is true, no model is built and null is returned;
|
|
* the set of reachable states can be obtained with {@link #getStatesList()}.
|
|
* @param modelGen The ModelGenerator interface providing the model
|
|
* @param justReach If true, just build the reachable state set, not the model
|
|
*/
|
|
public Model constructModel(ModelGenerator modelGen, boolean justReach) throws PrismException
|
|
{
|
|
// Model info
|
|
ModelType modelType;
|
|
// State storage
|
|
StateStorage<State> states;
|
|
LinkedList<State> explore;
|
|
State state, stateNew;
|
|
// Explicit model storage
|
|
ModelSimple modelSimple = null;
|
|
DTMCSimple dtmc = null;
|
|
CTMCSimple ctmc = null;
|
|
MDPSimple mdp = null;
|
|
POMDPSimple pomdp = null;
|
|
CTMDPSimple ctmdp = null;
|
|
LTSSimple lts = null;
|
|
ModelExplicit model = null;
|
|
Distribution distr = null;
|
|
// Observation info
|
|
List<String> observableVars = null;
|
|
List<String> unobservableVars = null;
|
|
// Misc
|
|
int i, j, nc, nt, src, dest;
|
|
long timer;
|
|
|
|
// Get model info
|
|
modelType = modelGen.getModelType();
|
|
|
|
// Display a warning if there are unbounded vars
|
|
VarList varList = modelGen.createVarList();
|
|
if (modelGen.containsUnboundedVariables())
|
|
mainLog.printWarning("Model contains one or more unbounded variables: model construction may not terminate");
|
|
|
|
// Starting reachability...
|
|
mainLog.print("\nComputing reachable states...");
|
|
mainLog.flush();
|
|
ProgressDisplay progress = new ProgressDisplay(mainLog);
|
|
progress.start();
|
|
timer = System.currentTimeMillis();
|
|
|
|
// Create model storage
|
|
if (!justReach) {
|
|
// Create a (simple, mutable) model of the appropriate type
|
|
switch (modelType) {
|
|
case DTMC:
|
|
modelSimple = dtmc = new DTMCSimple();
|
|
break;
|
|
case CTMC:
|
|
modelSimple = ctmc = new CTMCSimple();
|
|
break;
|
|
case MDP:
|
|
modelSimple = mdp = new MDPSimple();
|
|
break;
|
|
case POMDP:
|
|
modelSimple = pomdp = new POMDPSimple();
|
|
break;
|
|
case CTMDP:
|
|
modelSimple = ctmdp = new CTMDPSimple();
|
|
break;
|
|
case LTS:
|
|
modelSimple = lts = new LTSSimple();
|
|
break;
|
|
case STPG:
|
|
case SMG:
|
|
case PTA:
|
|
case POPTA:
|
|
throw new PrismNotSupportedException("Model construction not supported for " + modelType + "s");
|
|
}
|
|
// Attach variable info
|
|
((ModelExplicit) modelSimple).setVarList(varList);
|
|
}
|
|
|
|
// For partially observable models,
|
|
// get info needed to determine observability
|
|
if (!justReach && modelType.partiallyObservable()) {
|
|
List<String> allVars = modelGen.getVarNames();
|
|
observableVars = modelGen.getObservableVars();
|
|
for (String obsVar : observableVars) {
|
|
if (!allVars.contains(obsVar)) {
|
|
throw new PrismException("Observable " + obsVar + " is not a variable");
|
|
}
|
|
}
|
|
unobservableVars = new ArrayList<>();
|
|
for (String varName : allVars) {
|
|
if (!observableVars.contains(varName)) {
|
|
unobservableVars.add(varName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialise states storage
|
|
states = new IndexedSet<State>(true);
|
|
explore = new LinkedList<State>();
|
|
// Add initial state(s) to 'explore', 'states' and to the model
|
|
for (State initState : modelGen.getInitialStates()) {
|
|
explore.add(initState);
|
|
states.add(initState);
|
|
if (!justReach) {
|
|
modelSimple.addState();
|
|
modelSimple.addInitialState(modelSimple.getNumStates() - 1);
|
|
}
|
|
}
|
|
// Explore...
|
|
src = -1;
|
|
while (!explore.isEmpty()) {
|
|
// Pick next state to explore
|
|
// (they are stored in order found so know index is src+1)
|
|
state = explore.removeFirst();
|
|
src++;
|
|
// Explore all choices/transitions from this state
|
|
modelGen.exploreState(state);
|
|
// Look at each outgoing choice in turn
|
|
nc = modelGen.getNumChoices();
|
|
for (i = 0; i < nc; i++) {
|
|
// For nondet models, collect transitions in a Distribution
|
|
if (!justReach && modelType.nondeterministic()) {
|
|
distr = new Distribution();
|
|
}
|
|
// Look at each transition in the choice
|
|
nt = modelGen.getNumTransitions(i);
|
|
for (j = 0; j < nt; j++) {
|
|
stateNew = modelGen.computeTransitionTarget(i, j);
|
|
// Is this a new state?
|
|
if (states.add(stateNew)) {
|
|
// If so, add to the explore list
|
|
explore.add(stateNew);
|
|
// And to model
|
|
if (!justReach) {
|
|
modelSimple.addState();
|
|
}
|
|
}
|
|
// Get index of state in state set
|
|
dest = states.getIndexOfLastAdd();
|
|
// Add transitions to model
|
|
if (!justReach) {
|
|
switch (modelType) {
|
|
case DTMC:
|
|
dtmc.addToProbability(src, dest, modelGen.getTransitionProbability(i, j));
|
|
break;
|
|
case CTMC:
|
|
ctmc.addToProbability(src, dest, modelGen.getTransitionProbability(i, j));
|
|
break;
|
|
case MDP:
|
|
case POMDP:
|
|
case CTMDP:
|
|
distr.add(dest, modelGen.getTransitionProbability(i, j));
|
|
break;
|
|
case LTS:
|
|
if (distinguishActions) {
|
|
lts.addActionLabelledTransition(src, dest, modelGen.getChoiceAction(i));
|
|
} else {
|
|
lts.addTransition(src, dest);
|
|
}
|
|
break;
|
|
case STPG:
|
|
case SMG:
|
|
case PTA:
|
|
throw new PrismNotSupportedException("Model construction not supported for " + modelType + "s");
|
|
}
|
|
}
|
|
}
|
|
// For nondet models, add collated transition to model
|
|
if (!justReach) {
|
|
if (modelType == ModelType.MDP) {
|
|
if (distinguishActions) {
|
|
mdp.addActionLabelledChoice(src, distr, modelGen.getChoiceAction(i));
|
|
} else {
|
|
mdp.addChoice(src, distr);
|
|
}
|
|
} else if (modelType == ModelType.POMDP) {
|
|
if (distinguishActions) {
|
|
pomdp.addActionLabelledChoice(src, distr, modelGen.getChoiceAction(i));
|
|
} else {
|
|
pomdp.addChoice(src, distr);
|
|
}
|
|
} else if (modelType == ModelType.CTMDP) {
|
|
if (distinguishActions) {
|
|
ctmdp.addActionLabelledChoice(src, distr, modelGen.getChoiceAction(i));
|
|
} else {
|
|
ctmdp.addChoice(src, distr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Print some progress info occasionally
|
|
progress.updateIfReady(src + 1);
|
|
}
|
|
|
|
// Finish progress display
|
|
progress.update(src + 1);
|
|
progress.end(" states");
|
|
|
|
// Reachability complete
|
|
mainLog.print("Reachable states exploration" + (justReach ? "" : " and model construction"));
|
|
mainLog.println(" done in " + ((System.currentTimeMillis() - timer) / 1000.0) + " secs.");
|
|
//mainLog.println(states);
|
|
|
|
// Find/fix deadlocks (if required)
|
|
if (!justReach && findDeadlocks) {
|
|
modelSimple.findDeadlocks(fixDeadlocks);
|
|
}
|
|
|
|
int permut[] = null;
|
|
|
|
if (sortStates) {
|
|
// Sort states and convert set to list
|
|
mainLog.println("Sorting reachable states list...");
|
|
permut = states.buildSortingPermutation();
|
|
statesList = states.toPermutedArrayList(permut);
|
|
//mainLog.println(permut);
|
|
} else {
|
|
statesList = states.toArrayList();
|
|
}
|
|
states.clear();
|
|
states = null;
|
|
//mainLog.println(statesList);
|
|
|
|
// Construct new explicit-state model (with correct state ordering, if desired)
|
|
if (!justReach) {
|
|
switch (modelType) {
|
|
case DTMC:
|
|
if (buildSparse) {
|
|
model = sortStates ? new DTMCSparse(dtmc, permut) : new DTMCSparse(dtmc);
|
|
} else {
|
|
model = sortStates ? new DTMCSimple(dtmc, permut) : (DTMCSimple) dtmc;
|
|
}
|
|
break;
|
|
case CTMC:
|
|
model = sortStates ? new CTMCSimple(ctmc, permut) : (CTMCSimple) ctmc;
|
|
break;
|
|
case MDP:
|
|
if (buildSparse) {
|
|
model = sortStates ? new MDPSparse(mdp, true, permut) : new MDPSparse(mdp);
|
|
} else {
|
|
model = sortStates ? new MDPSimple(mdp, permut) : mdp;
|
|
}
|
|
break;
|
|
case POMDP:
|
|
model = sortStates ? new POMDPSimple(pomdp, permut) : pomdp;
|
|
break;
|
|
case CTMDP:
|
|
model = sortStates ? new CTMDPSimple(ctmdp, permut) : ctmdp;
|
|
break;
|
|
case LTS:
|
|
model = sortStates ? new LTSSimple(lts, permut) : lts;
|
|
break;
|
|
case STPG:
|
|
case SMG:
|
|
case PTA:
|
|
throw new PrismNotSupportedException("Model construction not supported for " + modelType + "s");
|
|
}
|
|
model.setStatesList(statesList);
|
|
model.setConstantValues(new Values(modelGen.getConstantValues()));
|
|
//mainLog.println("Model: " + model);
|
|
}
|
|
|
|
// For partially observable models, add observation info to states
|
|
// (do it after full model construction, including deadlock fixing,
|
|
// because the number of choices for each observation are checked) this here because we need the choice count)
|
|
if (modelType == ModelType.POMDP) {
|
|
for (int s = 0; s < statesList.size(); s++) {
|
|
setStateObservation((POMDPSimple)model, s, statesList.get(s), observableVars, unobservableVars, modelGen);
|
|
}
|
|
}
|
|
|
|
|
|
// Discard permutation
|
|
permut = null;
|
|
|
|
if (!justReach && attachLabels)
|
|
attachLabels(modelGen, model);
|
|
|
|
return model;
|
|
}
|
|
|
|
private void setStateObservation(POMDPSimple pomdp, int s, State state, List<String> observableVars, List<String> unobservableVars, ModelGenerator modelGen) throws PrismException
|
|
{
|
|
Values values1 = new Values(state, modelGen);
|
|
for (String unobservableVarName : unobservableVars) {
|
|
values1.removeValue(unobservableVarName);
|
|
}
|
|
Observation observ = new Observation(values1, modelGen);
|
|
|
|
Values values2 = new Values(state, modelGen);
|
|
for (String observableVarName : observableVars) {
|
|
values2.removeValue(observableVarName);
|
|
}
|
|
Unobservation unobserv = new Unobservation(values2, modelGen);
|
|
|
|
pomdp.setObservation(s, observ, unobserv);
|
|
}
|
|
|
|
private void attachLabels(ModelGenerator modelGen, ModelExplicit model) throws PrismException
|
|
{
|
|
// Get state info
|
|
List <State> statesList = model.getStatesList();
|
|
int numStates = statesList.size();
|
|
// Create storage for labels
|
|
int numLabels = modelGen.getNumLabels();
|
|
// No need to continue unless this ModelGenerator uses labels
|
|
if (numLabels == 0) return;
|
|
BitSet bitsets[] = new BitSet[numLabels];
|
|
for (int j = 0; j < numLabels; j++) {
|
|
bitsets[j] = new BitSet();
|
|
}
|
|
// Construct bitsets for labels
|
|
for (int i = 0; i < numStates; i++) {
|
|
State state = statesList.get(i);
|
|
modelGen.exploreState(state);
|
|
for (int j = 0; j < numLabels; j++) {
|
|
if (modelGen.isLabelTrue(j)) {
|
|
bitsets[j].set(i);
|
|
}
|
|
}
|
|
}
|
|
// Attach labels/bitsets
|
|
for (int j = 0; j < numLabels; j++) {
|
|
model.addLabel(modelGen.getLabelName(j), bitsets[j]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test method.
|
|
*/
|
|
public static void main(String[] args)
|
|
{
|
|
try {
|
|
// Simple example: parse a PRISM file from a file, construct the model and export to a .tra file
|
|
PrismLog mainLog = new PrismPrintStreamLog(System.out);
|
|
Prism prism = new Prism(mainLog);
|
|
parser.ast.ModulesFile modulesFile = prism.parseModelFile(new File(args[0]));
|
|
UndefinedConstants undefinedConstants = new UndefinedConstants(modulesFile, null);
|
|
if (args.length > 2)
|
|
undefinedConstants.defineUsingConstSwitch(args[2]);
|
|
modulesFile.setUndefinedConstants(undefinedConstants.getMFConstantValues());
|
|
ConstructModel constructModel = new ConstructModel(prism);
|
|
constructModel.setSortStates(true);
|
|
simulator.ModulesFileModelGenerator modelGen = new simulator.ModulesFileModelGenerator(modulesFile, constructModel);
|
|
Model model = constructModel.constructModel(modelGen);
|
|
model.exportToPrismExplicitTra(args[1]);
|
|
} catch (FileNotFoundException e) {
|
|
System.out.println("Error: " + e.getMessage());
|
|
System.exit(1);
|
|
} catch (PrismException e) {
|
|
System.out.println("Error: " + e.getMessage());
|
|
System.exit(1);
|
|
}
|
|
}
|
|
}
|