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.
287 lines
9.0 KiB
287 lines
9.0 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2002-
|
|
// Authors:
|
|
// * Dave Parker <david.parker@comlab.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 simulator;
|
|
|
|
import java.io.File;
|
|
import java.util.ArrayList;
|
|
|
|
import parser.Values;
|
|
import parser.ast.ModulesFile;
|
|
import prism.*;
|
|
|
|
public class GenerateSimulationPath
|
|
{
|
|
// The simulator engine and a log for output
|
|
private SimulatorEngine engine;
|
|
private PrismLog mainLog;
|
|
|
|
// Enums
|
|
private enum PathType {
|
|
SIM_PATH_NUM_STEPS, SIM_PATH_TIME, SIM_PATH_DEADLOCK
|
|
};
|
|
|
|
// Basic info needed for path
|
|
private ModulesFile modulesFile;
|
|
private Values initialState;
|
|
private int maxPathLength;
|
|
private File file;
|
|
|
|
// Path configuration options
|
|
private PathType simPathType = null;
|
|
private int simPathLength = 0;
|
|
private double simPathTime = 0.0;
|
|
private String simPathSep = " ";
|
|
private ArrayList<Integer> simVars = null;
|
|
private boolean simLoopCheck = true;
|
|
private int simPathRepeat = 1;
|
|
|
|
public GenerateSimulationPath(SimulatorEngine engine, PrismLog mainLog)
|
|
{
|
|
this.engine = engine;
|
|
this.mainLog = mainLog;
|
|
}
|
|
|
|
/**
|
|
* Generate and export a random path through a model with the simulator.
|
|
* @param modulesFile: The model
|
|
* @param initialState: Initial state (if null, is selected randomly)
|
|
* @param details: Information about the path to be generated
|
|
* @param file: File to output the path to (stdout if null)
|
|
*/
|
|
public void generateSimulationPath(ModulesFile modulesFile, Values initialState, String details, int maxPathLength,
|
|
File file) throws PrismException
|
|
{
|
|
parseDetails(details);
|
|
this.modulesFile = modulesFile;
|
|
this.initialState = initialState;
|
|
this.maxPathLength = maxPathLength;
|
|
this.file = file;
|
|
generatePath();
|
|
}
|
|
|
|
/**
|
|
* Parse a string specifying how to generate a simulation path.
|
|
*/
|
|
private void parseDetails(String details) throws PrismException
|
|
{
|
|
String s, ss[];
|
|
int i, j, n;
|
|
boolean done;
|
|
|
|
ss = details.split(",");
|
|
n = ss.length;
|
|
for (i = 0; i < n; i++) {
|
|
if (ss[i].indexOf("time=") == 0) {
|
|
// path with upper time limit
|
|
simPathType = PathType.SIM_PATH_TIME;
|
|
try {
|
|
simPathTime = Double.parseDouble(ss[i].substring(5));
|
|
if (simPathTime < 0.0)
|
|
throw new NumberFormatException();
|
|
} catch (NumberFormatException e) {
|
|
throw new PrismException("Invalid path time limit \"" + ss[i] + "\"");
|
|
}
|
|
} else if (ss[i].equals("deadlock")) {
|
|
// path until deadlock
|
|
simPathType = PathType.SIM_PATH_DEADLOCK;
|
|
} else if (ss[i].indexOf("sep=") == 0) {
|
|
// specify column separator to display path
|
|
simPathSep = ss[i].substring(4);
|
|
if (simPathSep.equals("space")) {
|
|
simPathSep = " ";
|
|
continue;
|
|
}
|
|
if (simPathSep.equals("tab")) {
|
|
simPathSep = "\t";
|
|
continue;
|
|
}
|
|
if (simPathSep.equals("comma")) {
|
|
simPathSep = ",";
|
|
continue;
|
|
}
|
|
throw new PrismException("Separator must be one of: \"space\", \"tab\", \"comma\"");
|
|
} else if (ss[i].indexOf("vars=") == 0) {
|
|
// Build list of indices of variables to display
|
|
simVars = new ArrayList<Integer>();
|
|
done = false;
|
|
s = ss[i].substring(5);
|
|
if (s.length() < 1 || s.charAt(0) != '(')
|
|
throw new PrismException("Invalid format for \"vars=(...)\"");
|
|
s = s.substring(1);
|
|
if (s.indexOf(')') > -1) {
|
|
s = s.substring(0, s.length() - 1);
|
|
done = true;
|
|
}
|
|
j = engine.getIndexOfVar(s);
|
|
if (j == -1)
|
|
throw new PrismException("Unknown variable \"" + s + "\" in \"vars=(...)\" list");
|
|
simVars.add(j);
|
|
while (i < n && !done) {
|
|
s = ss[++i];
|
|
if (s.indexOf(')') > -1) {
|
|
s = s.substring(0, s.length() - 1);
|
|
done = true;
|
|
}
|
|
j = engine.getIndexOfVar(s);
|
|
if (j == -1)
|
|
throw new PrismException("Unknown variable \"" + s + "\" in \"vars=(...)\" list");
|
|
simVars.add(j);
|
|
}
|
|
} else if (ss[i].indexOf("loopcheck=") == 0) {
|
|
// switch loop detection on/off (default is on)
|
|
s = ss[i].substring(10);
|
|
if (s.equals("true")) {
|
|
simLoopCheck = true;
|
|
continue;
|
|
}
|
|
if (s.equals("false")) {
|
|
simLoopCheck = false;
|
|
continue;
|
|
}
|
|
throw new PrismException("Value for \"loopcheck\" flag must be \"true\" or \"false\"");
|
|
} else if (ss[i].indexOf("repeat=") == 0) {
|
|
// how many times to repeat path generation until successful (for "deadlock" option)
|
|
try {
|
|
simPathRepeat = Integer.parseInt(ss[i].substring(7));
|
|
if (simPathRepeat < 1)
|
|
throw new NumberFormatException();
|
|
} catch (NumberFormatException e) {
|
|
throw new PrismException("Value for \"repeat\" option must be a positive integer");
|
|
}
|
|
} else {
|
|
// path of fixed number of steps
|
|
simPathType = PathType.SIM_PATH_NUM_STEPS;
|
|
try {
|
|
simPathLength = Integer.parseInt(ss[i]);
|
|
if (simPathLength < 0)
|
|
throw new NumberFormatException();
|
|
} catch (NumberFormatException e) {
|
|
throw new PrismException("Invalid path length \"" + ss[i] + "\"");
|
|
}
|
|
}
|
|
}
|
|
if (simPathType == null)
|
|
throw new PrismException("Invalid path details \"" + details + "\"");
|
|
}
|
|
|
|
/**
|
|
* Generate a random path using the simulator.
|
|
*/
|
|
private void generatePath() throws PrismException
|
|
{
|
|
int i = 0, j = 0;
|
|
boolean done;
|
|
double t = 0.0;
|
|
boolean stochastic = (modulesFile.getModelType() == ModelType.CTMC);
|
|
|
|
// print details
|
|
switch (simPathType) {
|
|
case SIM_PATH_NUM_STEPS:
|
|
mainLog.println("\nGenerating random path of length " + simPathLength + " steps...");
|
|
break;
|
|
case SIM_PATH_TIME:
|
|
mainLog.println("\nGenerating random path with time limit " + simPathTime + "...");
|
|
break;
|
|
case SIM_PATH_DEADLOCK:
|
|
mainLog.println("\nGenerating random path until deadlock state...");
|
|
break;
|
|
}
|
|
|
|
// display warning if attempt to use "repeat=" option and not "deadlock" option
|
|
if (simPathRepeat > 1 && simPathType != PathType.SIM_PATH_DEADLOCK) {
|
|
simPathRepeat = 1;
|
|
mainLog.println("\nWarning: Ignoring \"repeat\" option - it is only valid when looking for deadlocks.");
|
|
}
|
|
|
|
// generate path
|
|
engine.createNewPath(modulesFile);
|
|
for (j = 0; j < simPathRepeat; j++) {
|
|
engine.initialisePath(initialState);
|
|
i = 0;
|
|
t = 0.0;
|
|
done = false;
|
|
while (!done) {
|
|
// generate a single step of path
|
|
engine.automaticChoices(1, simLoopCheck);
|
|
if (stochastic)
|
|
t += engine.getTimeSpentInPathState(i++);
|
|
else
|
|
t = ++i;
|
|
// check for termination (depending on type)
|
|
switch (simPathType) {
|
|
case SIM_PATH_NUM_STEPS:
|
|
if (i >= simPathLength || engine.queryIsDeadlock())
|
|
done = true;
|
|
break;
|
|
case SIM_PATH_TIME:
|
|
if (t >= simPathTime || i >= maxPathLength || engine.queryIsDeadlock())
|
|
done = true;
|
|
break;
|
|
case SIM_PATH_DEADLOCK:
|
|
if (engine.queryIsDeadlock() || i >= maxPathLength)
|
|
done = true;
|
|
break;
|
|
}
|
|
// stop if a loop was found (and loop checking was not disabled)
|
|
if (simLoopCheck && engine.isPathLooping())
|
|
break;
|
|
}
|
|
|
|
// if we are generating multiple paths (to find a deadlock) only stop if deadlock actually found
|
|
if (simPathType == PathType.SIM_PATH_DEADLOCK && engine.queryIsDeadlock())
|
|
break;
|
|
}
|
|
if (j < simPathRepeat)
|
|
j++;
|
|
|
|
// display warning if a deterministic loop was detected (but not in case where multiple paths generated)
|
|
if (simLoopCheck && engine.isPathLooping() && simPathRepeat == 1) {
|
|
mainLog.println("\nWarning: Deterministic loop detected after " + i
|
|
+ " steps (use loopcheck=false option to extend path).");
|
|
}
|
|
|
|
// if we needed multiple paths to find a deadlock, say how many
|
|
if (simPathRepeat > 1 && j > 1)
|
|
mainLog.println("\n" + j + " paths were generated.");
|
|
|
|
// export path
|
|
if (simPathType == PathType.SIM_PATH_DEADLOCK && !engine.queryIsDeadlock()) {
|
|
mainLog.print("\nNo deadlock state found within " + maxPathLength + " steps");
|
|
if (simPathRepeat > 1)
|
|
mainLog.print(" (generated " + simPathRepeat + " paths)");
|
|
mainLog.println(".");
|
|
} else {
|
|
engine.exportPath(file, true, simPathSep, simVars);
|
|
}
|
|
|
|
// warning if stopped early
|
|
if (simPathType == PathType.SIM_PATH_TIME && t < simPathTime) {
|
|
mainLog.println("\nWarning: Path terminated before time " + simPathTime + " because maximum path length ("
|
|
+ maxPathLength + ") reached.");
|
|
}
|
|
}
|
|
}
|