//============================================================================== // // Copyright (c) 2002- // Authors: // * Dave Parker (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 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(); 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."); } } }