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.
683 lines
24 KiB
683 lines
24 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2002-
|
|
// Authors:
|
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// 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 pta;
|
|
|
|
import java.util.*;
|
|
|
|
import explicit.IndexedSet;
|
|
import explicit.StateStorage;
|
|
|
|
import parser.*;
|
|
import parser.ast.*;
|
|
import parser.type.*;
|
|
import parser.visitor.ASTTraverse;
|
|
import prism.*;
|
|
|
|
/**
|
|
* Class that converts a PRISM modelling language description
|
|
* of a PTA into data structures in the pta package.
|
|
*/
|
|
public class Modules2PTA extends PrismComponent
|
|
{
|
|
// Setting(s)
|
|
protected double sumRoundOff;
|
|
|
|
// Model to be converted
|
|
private ModulesFile modulesFile;
|
|
// Constants from model
|
|
private Values constantValues;
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public Modules2PTA(PrismComponent parent, ModulesFile modulesFile) throws PrismException
|
|
{
|
|
super(parent);
|
|
sumRoundOff = settings.getDouble(PrismSettings.PRISM_SUM_ROUND_OFF);
|
|
this.modulesFile = modulesFile;
|
|
constantValues = modulesFile.getConstantValues();
|
|
}
|
|
|
|
/**
|
|
* Main method - translate.
|
|
*/
|
|
public PTA translate() throws PrismLangException
|
|
{
|
|
int i, numModules;
|
|
Module module, moduleNew;
|
|
ArrayList<String> nonClocks;
|
|
ArrayList<String> allNonClocks = new ArrayList<String>();
|
|
ArrayList<ArrayList<State>> pcStates;
|
|
PTA pta, pta2;
|
|
|
|
// Do a few basic checks on the model
|
|
if (modulesFile.getModelType() != ModelType.PTA)
|
|
throw new PrismLangException("Model is not a PTA");
|
|
if (modulesFile.getNumGlobals() > 0)
|
|
throw new PrismLangException("PTA models cannot have global variables");
|
|
if (modulesFile.getInitialStates() != null)
|
|
throw new PrismLangException("PTA models cannot use init...endinit");
|
|
|
|
// Check for inter-module variable references
|
|
modulesFile.accept(new ASTTraverse()
|
|
{
|
|
private Module inModule = null;
|
|
|
|
public void visitPre(Module e) throws PrismLangException
|
|
{
|
|
// Register the fact we are entering a module
|
|
inModule = e;
|
|
}
|
|
|
|
public void visitPost(Module e) throws PrismLangException
|
|
{
|
|
// Register the fact we are leaving a module
|
|
inModule = null;
|
|
}
|
|
|
|
public void visitPost(ExpressionVar e) throws PrismLangException
|
|
{
|
|
// For PTAs, references to variables in modules have to be local
|
|
if (inModule != null) {
|
|
if (!inModule.isLocalVariable(e.getName())) {
|
|
throw new PrismLangException("Modules in a PTA cannot access non-local variables", e);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Clone the model file, replace any constants with values,
|
|
// and simplify any expressions as much as possible.
|
|
modulesFile = (ModulesFile) modulesFile.deepCopy().replaceConstants(constantValues).simplify();
|
|
|
|
// Remove formulas/labels from (cloned) model - these are not translated.
|
|
modulesFile.setFormulaList(new FormulaList());
|
|
modulesFile.setLabelList(new LabelList());
|
|
|
|
// Also remove reward structures - these are not currently used
|
|
modulesFile.clearRewardStructs();
|
|
|
|
// Go through list of modules
|
|
numModules = modulesFile.getNumModules();
|
|
pcStates = new ArrayList<ArrayList<State>>(numModules);
|
|
for (i = 0; i < numModules; i++) {
|
|
// Find non-clock variables in module
|
|
module = modulesFile.getModule(i);
|
|
nonClocks = new ArrayList<String>();
|
|
for (Declaration decl : module.getDeclarations()) {
|
|
if (!(decl.getType() instanceof TypeClock)) {
|
|
nonClocks.add(decl.getName());
|
|
}
|
|
}
|
|
allNonClocks.addAll(nonClocks);
|
|
// Convert module to program counter form
|
|
pcStates.add(new ArrayList<State>());
|
|
module = modulesFile.getModule(i);
|
|
moduleNew = convertModuleToPCForm(module, nonClocks, pcStates.get(i));
|
|
// Replace module in model
|
|
modulesFile.setModule(i, moduleNew);
|
|
}
|
|
// Re-compute all variable related info in model
|
|
modulesFile.recomputeVariableinformation();
|
|
|
|
// Convert each module to a PTA and do parallel composition
|
|
numModules = modulesFile.getNumModules();
|
|
pta = null;
|
|
for (i = 0; i < numModules; i++) {
|
|
module = modulesFile.getModule(i);
|
|
pta2 = translateModule(module, pcStates.get(i));
|
|
//mainLog.println(pta2);
|
|
pta = (pta == null) ? pta2 : new PTAParallel().compose(pta, pta2);
|
|
}
|
|
//mainLog.println(pta);
|
|
|
|
// Pass the list of non-clock variables to the PTA
|
|
pta.setLocationNameVars(allNonClocks);
|
|
|
|
return pta;
|
|
}
|
|
|
|
/**
|
|
* Translate a single module.
|
|
* (Which has been transformed using convertModuleToPCForm)
|
|
*/
|
|
private PTA translateModule(Module module, ArrayList<State> pcStates) throws PrismLangException
|
|
{
|
|
// Clocks and PC variable stuff
|
|
ArrayList<String> clocks;
|
|
String pc;
|
|
int pcMax, pcVal;
|
|
Values pcValues;
|
|
// Model components
|
|
Update up;
|
|
Expression expr, invar;
|
|
List<Expression> exprs;
|
|
int numVars, numUpdates, numElements;
|
|
// PTA stuff
|
|
PTA pta;
|
|
Transition tr;
|
|
Edge edge;
|
|
// Misc
|
|
int i, j, k;
|
|
double prob, probSum;
|
|
|
|
// Determine PC variable and clock variables in module
|
|
pc = module.getDeclaration(0).getName();
|
|
pcMax = ((DeclarationInt) module.getDeclaration(0).getDeclType()).getHigh().evaluateInt();
|
|
numVars = module.getNumDeclarations();
|
|
clocks = new ArrayList<String>();
|
|
for (i = 1; i < numVars; i++) {
|
|
clocks.add(module.getDeclaration(i).getName());
|
|
}
|
|
|
|
// Create new PTA and add a clock for each clock variable
|
|
pta = new PTA();
|
|
for (String clockName : clocks)
|
|
pta.addClock(clockName);
|
|
|
|
// Add locations corresponding to PC
|
|
// (labels for locations come from the State objects generated
|
|
// when converting the module to PC form).
|
|
for (i = 0; i < pcMax + 1; i++) {
|
|
pta.addLocation(pcStates.get(i));
|
|
}
|
|
|
|
// Process invariant to determine guard for each location
|
|
for (i = 0; i < pcMax + 1; i++) {
|
|
invar = module.getInvariant();
|
|
if (invar != null) {
|
|
// Get (copy of) existing invariant for module
|
|
invar = module.getInvariant().deepCopy();
|
|
// Evaluate (partially) invariant for this pc value
|
|
pcValues = new Values();
|
|
pcValues.setValue(pc, i);
|
|
// Evaluate (partially) invariant for this PC value
|
|
invar = (Expression) invar.evaluatePartially(null, pcValues).simplify();
|
|
// The (partial) invariant should now be a conjunction of clock constraints (or true)
|
|
// Split into parts, convert to constraints and add to PTA (unless "true")
|
|
// If expression is not (syntactically) convex, complain
|
|
exprs = ParserUtils.splitConjunction(invar);
|
|
for (Expression ex : exprs) {
|
|
if (!(Expression.isTrue(ex) || Expression.isFalse(ex))) {
|
|
checkIsSimpleClockConstraint(ex);
|
|
}
|
|
}
|
|
for (Expression ex : exprs) {
|
|
if (!Expression.isTrue(ex)) {
|
|
for (Constraint c : exprToConstraint(ex, pta)) {
|
|
pta.addInvariantCondition(i, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// For each command in the module
|
|
for (Command command : module.getCommands()) {
|
|
|
|
// Guard is known to be a conjunction where LHS is of form pc=i
|
|
// Extract value i from this and put in pcVal and pcValues
|
|
expr = ((ExpressionBinaryOp) command.getGuard()).getOperand1();
|
|
pcVal = ((ExpressionBinaryOp) expr).getOperand2().evaluateInt();
|
|
pcValues = new Values();
|
|
pcValues.setValue(pc, pcVal);
|
|
// RHS of guard should be conjunction of clock constraints (or true)
|
|
// Split into parts, convert to constraints and add to new PTA transition
|
|
// If expression is not (syntactically) convex, complain
|
|
tr = pta.addTransition(pcVal, command.getSynch());
|
|
exprs = ParserUtils.splitConjunction(((ExpressionBinaryOp) command.getGuard()).getOperand2());
|
|
for (Expression ex : exprs) {
|
|
if (!(Expression.isTrue(ex) || Expression.isFalse(ex))) {
|
|
checkIsSimpleClockConstraint(ex);
|
|
}
|
|
}
|
|
for (Expression ex2 : exprs) {
|
|
if (!Expression.isTrue(ex2)) {
|
|
for (Constraint c : exprToConstraint(ex2, pta)) {
|
|
tr.addGuardConstraint(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go through all updates
|
|
numUpdates = command.getUpdates().getNumUpdates();
|
|
probSum = 0.0;
|
|
for (j = 0; j < numUpdates; j++) {
|
|
up = command.getUpdates().getUpdate(j);
|
|
// Compute probability
|
|
expr = command.getUpdates().getProbability(j);
|
|
prob = (expr == null) ? 1.0 : expr.evaluateDouble(constantValues, pcValues);
|
|
if (prob < 0)
|
|
throw new PrismLangException("Negative probability (" + prob + ") found in PTA");
|
|
if (prob > 1)
|
|
throw new PrismLangException("Probability greater than 1 (" + prob + ") found in PTA");
|
|
probSum += prob;
|
|
// Create edge (destination is temporarily -1 since not known yet)
|
|
edge = tr.addEdge(prob, -1);
|
|
// Go through elements of update
|
|
numElements = up.getNumElements();
|
|
for (k = 0; k < numElements; k++) {
|
|
// Determine destination location and add to edge
|
|
if (up.getVar(k).equals(pc))
|
|
edge.setDestination(up.getExpression(k).evaluateInt(constantValues, pcValues));
|
|
else {
|
|
if (!clocks.contains(up.getVar(k)))
|
|
throw new PrismLangException("Update to non-clock found", up);
|
|
int val = up.getExpression(k).evaluateInt(constantValues, pcValues);
|
|
edge.addReset(pta.getClockIndex(up.getVar(k)), val);
|
|
}
|
|
}
|
|
// If no destination found, must be a loop
|
|
if (edge.getDestination() == -1)
|
|
edge.setDestination(tr.getSource());
|
|
}
|
|
// Check probabilities sum to one (ish)
|
|
if (!PrismUtils.doublesAreCloseAbs(probSum, 1.0, sumRoundOff)) {
|
|
throw new PrismLangException("Probabilities do not sum to one (" + probSum + ") in PTA");
|
|
}
|
|
}
|
|
|
|
return pta;
|
|
}
|
|
|
|
/**
|
|
* Check whether a PRISM expression (over clock variables) is a "simple" clock constraint, i.e. of the form
|
|
* x~c or x~y where x and y are clocks, c is an integer-valued expression and ~ is one of <, <=, >=, >, =.
|
|
* Throws an explanatory exception if not.
|
|
* @param expr: The expression to be checked.
|
|
*/
|
|
private void checkIsSimpleClockConstraint(Expression expr) throws PrismLangException
|
|
{
|
|
ExpressionBinaryOp exprRelOp;
|
|
Expression expr1, expr2;
|
|
int op, clocks = 0;
|
|
|
|
// Check is rel op
|
|
if (!Expression.isRelOp(expr))
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
// Split into parts
|
|
exprRelOp = (ExpressionBinaryOp) expr;
|
|
op = exprRelOp.getOperator();
|
|
expr1 = exprRelOp.getOperand1();
|
|
expr2 = exprRelOp.getOperand2();
|
|
// Check operator is of allowed type
|
|
if (!ExpressionBinaryOp.isRelOp(op))
|
|
throw new PrismLangException("Can't use operator " + exprRelOp.getOperatorSymbol() + " in clock constraint \"" + expr + "\"", expr);
|
|
if (op == ExpressionBinaryOp.NE)
|
|
throw new PrismLangException("Can't use negation in clock constraint \"" + expr + "\"", expr);
|
|
// LHS
|
|
if (expr1.getType() instanceof TypeClock) {
|
|
if (!(expr1 instanceof ExpressionVar)) {
|
|
throw new PrismLangException("Invalid clock expression \"" + expr1 + "\"", expr1);
|
|
}
|
|
clocks++;
|
|
} else if (expr1.getType() instanceof TypeInt) {
|
|
if (!expr1.isConstant()) {
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
}
|
|
} else {
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
}
|
|
// RHS
|
|
if (expr2.getType() instanceof TypeClock) {
|
|
if (!(expr2 instanceof ExpressionVar)) {
|
|
throw new PrismLangException("Invalid clock expression \"" + expr2 + "\"", expr2);
|
|
}
|
|
clocks++;
|
|
} else if (expr2.getType() instanceof TypeInt) {
|
|
if (!expr2.isConstant()) {
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
}
|
|
} else {
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
}
|
|
// Should be at least one clock
|
|
if (clocks == 0)
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
}
|
|
|
|
/**
|
|
* Convert a PRISM expression representing a (simple) clock constraint into
|
|
* the Constraint data structures used in the pta package.
|
|
* Actually creates a list of constraints (since e.g. x=c maps to multiple constraints)
|
|
* @param expr: The expression to be converted.
|
|
* @param pta: The PTA for which this constraint will be used.
|
|
*/
|
|
private List<Constraint> exprToConstraint(Expression expr, PTA pta) throws PrismLangException
|
|
{
|
|
ExpressionBinaryOp exprRelOp;
|
|
Expression expr1, expr2;
|
|
int x, y, v;
|
|
List<Constraint> res = new ArrayList<Constraint>();
|
|
|
|
// Check is rel op and split into parts
|
|
if (!Expression.isRelOp(expr))
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
exprRelOp = (ExpressionBinaryOp) expr;
|
|
expr1 = exprRelOp.getOperand1();
|
|
expr2 = exprRelOp.getOperand2();
|
|
// 3 cases...
|
|
if (expr1.getType() instanceof TypeClock) {
|
|
// Comparison of two clocks (x ~ y)
|
|
if (expr2.getType() instanceof TypeClock) {
|
|
x = pta.getClockIndex(((ExpressionVar) expr1).getName());
|
|
if (x < 0)
|
|
throw new PrismLangException("Unknown clock \"" + ((ExpressionVar) expr1).getName() + "\"", expr);
|
|
y = pta.getClockIndex(((ExpressionVar) expr2).getName());
|
|
if (y < 0)
|
|
throw new PrismLangException("Unknown clock \"" + ((ExpressionVar) expr2).getName() + "\"", expr);
|
|
switch (exprRelOp.getOperator()) {
|
|
case ExpressionBinaryOp.EQ:
|
|
res.add(Constraint.buildXGeqY(x, y));
|
|
res.add(Constraint.buildXLeqY(x, y));
|
|
break;
|
|
case ExpressionBinaryOp.NE:
|
|
throw new PrismLangException("Can't use negation in clock constraint \"" + expr + "\"", expr);
|
|
case ExpressionBinaryOp.GT:
|
|
res.add(Constraint.buildXGtY(x, y));
|
|
break;
|
|
case ExpressionBinaryOp.GE:
|
|
res.add(Constraint.buildXGeqY(x, y));
|
|
break;
|
|
case ExpressionBinaryOp.LT:
|
|
res.add(Constraint.buildXLtY(x, y));
|
|
break;
|
|
case ExpressionBinaryOp.LE:
|
|
res.add(Constraint.buildXLeqY(x, y));
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
// Comparison of clock and integer (x ~ v)
|
|
else {
|
|
x = pta.getClockIndex(((ExpressionVar) expr1).getName());
|
|
if (x < 0)
|
|
throw new PrismLangException("Unknown clock \"" + ((ExpressionVar) expr1).getName() + "\"", expr);
|
|
v = expr2.evaluateInt(constantValues);
|
|
switch (exprRelOp.getOperator()) {
|
|
case ExpressionBinaryOp.EQ:
|
|
res.add(Constraint.buildGeq(x, v));
|
|
res.add(Constraint.buildLeq(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.NE:
|
|
throw new PrismLangException("Can't use negation in clock constraint \"" + expr + "\"", expr);
|
|
case ExpressionBinaryOp.GT:
|
|
res.add(Constraint.buildGt(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.GE:
|
|
res.add(Constraint.buildGeq(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.LT:
|
|
res.add(Constraint.buildLt(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.LE:
|
|
res.add(Constraint.buildLeq(x, v));
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
// Comparison of integer and clock (v ~ x)
|
|
else if (expr2.getType() instanceof TypeClock) {
|
|
x = pta.getClockIndex(((ExpressionVar) expr2).getName());
|
|
if (x < 0)
|
|
throw new PrismLangException("Unknown clock \"" + ((ExpressionVar) expr2).getName() + "\"", expr);
|
|
v = expr1.evaluateInt(constantValues);
|
|
switch (exprRelOp.getOperator()) {
|
|
case ExpressionBinaryOp.EQ:
|
|
res.add(Constraint.buildGeq(x, v));
|
|
res.add(Constraint.buildLeq(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.NE:
|
|
throw new PrismLangException("Can't use negation in clock constraint \"" + expr + "\"", expr);
|
|
case ExpressionBinaryOp.GT:
|
|
res.add(Constraint.buildLt(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.GE:
|
|
res.add(Constraint.buildLeq(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.LT:
|
|
res.add(Constraint.buildGt(x, v));
|
|
break;
|
|
case ExpressionBinaryOp.LE:
|
|
res.add(Constraint.buildGeq(x, v));
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
throw new PrismLangException("Invalid clock constraint \"" + expr + "\"", expr);
|
|
}
|
|
|
|
/**
|
|
* Modify a module, converting a set of variables into a single 0-indexed program counter (PC) variable.
|
|
* The PC variable is guaranteed to be first in the list of new module variables.
|
|
* Every new guard takes the form of a conjunction with the LHS of the form pc=i,
|
|
* where pc is the PC variable and i is an integer literal.
|
|
* Similarly, the first element of every update is of the form (pc'=i).
|
|
* The original module is not changed; the new one is returned.
|
|
* In addition, a list of State objects, representing the meaning of each PC value is stored in pcStates.
|
|
* The method recomputeVariableinformation() will need to be called on the ModulesFile
|
|
* containing this module afterwards, since this changes the global indices of variables.
|
|
* @param module: The module to convert.
|
|
* @param pcVars: The variables that should be converted to a PC.
|
|
* @param pcStates: An empty ArrayList into which PC value states will be stored.
|
|
*/
|
|
private Module convertModuleToPCForm(Module module, List<String> pcVars, ArrayList<State> pcStates) throws PrismLangException
|
|
{
|
|
// Info about variables in model to be used as program counter
|
|
int pcNumVars;
|
|
State pcInit;
|
|
// info about new program counter var
|
|
String pc;
|
|
// Components of old and new module
|
|
Module moduleNew;
|
|
Declaration decl, declNew;
|
|
Command command, commandNew;
|
|
Expression guard, guardNew;
|
|
Updates updates, updatesNew;
|
|
Update update, updateNew;
|
|
Expression invar, invarNew;
|
|
Expression exprPc;
|
|
int numUpdates, numCommands, numElements;
|
|
// Stuff needed to do local reachability search
|
|
int src, dest;
|
|
StateStorage<State> states;
|
|
LinkedList<State> explore;
|
|
State state, stateNew;
|
|
int[] varMap;
|
|
// Misc
|
|
int i, j, k, numVars;
|
|
|
|
// For now, assume all constant values have been replaced
|
|
// Later, can assume that just some required subset have.
|
|
|
|
// Store number of PC variables and get their initial values
|
|
pcNumVars = pcVars.size();
|
|
pcInit = new State(pcNumVars);
|
|
for (i = 0; i < pcNumVars; i++) {
|
|
decl = modulesFile.getVarDeclaration(modulesFile.getVarIndex(pcVars.get(i)));
|
|
pcInit.setValue(i, decl.getStartOrDefault().evaluate(constantValues));
|
|
}
|
|
|
|
// Build variable index mapping
|
|
numVars = modulesFile.getNumVars();
|
|
varMap = new int[numVars];
|
|
for (i = 0; i < numVars; i++) {
|
|
varMap[i] = -1;
|
|
}
|
|
for (i = 0; i < pcNumVars; i++) {
|
|
varMap[modulesFile.getVarIndex(pcVars.get(i))] = i;
|
|
}
|
|
|
|
// Choose name for new program counter
|
|
// (concatenate replaced variables, prefixed with underscores,
|
|
// (and append more underscores until unique)
|
|
pc = "";
|
|
for (i = 0; i < pcNumVars; i++) {
|
|
pc += "_" + pcVars.get(i);
|
|
}
|
|
while (!identIsUnused(pc)) {
|
|
pc += "_";
|
|
}
|
|
|
|
// Create a new module
|
|
moduleNew = new Module(module.getName());
|
|
|
|
// Create invariant - will be constructed below
|
|
invarNew = null;
|
|
|
|
// Explore local state space of module
|
|
|
|
// Initialise states storage
|
|
states = new IndexedSet<State>();
|
|
explore = new LinkedList<State>();
|
|
// Add initial state
|
|
state = pcInit;
|
|
states.add(state);
|
|
explore.add(state);
|
|
src = -1;
|
|
try {
|
|
while (!explore.isEmpty()) {
|
|
// Pick next state to explore
|
|
// (they are stored in order found so know index is src+1)
|
|
state = explore.removeFirst();
|
|
src++;
|
|
// Build expression for this PC state
|
|
exprPc = new ExpressionVar(pc, TypeInt.getInstance());
|
|
exprPc = new ExpressionBinaryOp(ExpressionBinaryOp.EQ, exprPc, Expression.Int(src));
|
|
// For each command in the module
|
|
numCommands = module.getNumCommands();
|
|
for (i = 0; i < numCommands; i++) {
|
|
command = module.getCommand(i);
|
|
// See if guard is potentially true for this PC value
|
|
guard = command.getGuard();
|
|
guard = (Expression) guard.deepCopy().evaluatePartially(state, varMap).simplify();
|
|
if (!Expression.isFalse(guard)) {
|
|
// If so, build a new command
|
|
commandNew = new Command();
|
|
commandNew.setSynch(command.getSynch());
|
|
guardNew = Expression.And(exprPc, guard);
|
|
commandNew.setGuard(guardNew);
|
|
// Go through updates, modifying them
|
|
updates = command.getUpdates();
|
|
updatesNew = new Updates();
|
|
numUpdates = updates.getNumUpdates();
|
|
for (j = 0; j < numUpdates; j++) {
|
|
update = updates.getUpdate(j);
|
|
// Determine successor (PC) state
|
|
stateNew = new State(state);
|
|
update.updatePartially(state, stateNew, varMap);
|
|
// If new, add it to explore list
|
|
if (states.add(stateNew)) {
|
|
explore.add(stateNew);
|
|
}
|
|
dest = states.getIndexOfLastAdd();
|
|
// Build new update
|
|
updateNew = new Update();
|
|
updateNew.addElement(new ExpressionIdent(pc), Expression.Int(dest));
|
|
numElements = update.getNumElements();
|
|
// Copy across rest of old update
|
|
for (k = 0; k < numElements; k++) {
|
|
if (varMap[update.getVarIndex(k)] == -1) {
|
|
updateNew.addElement(update.getVarIdent(k), update.getExpression(k));
|
|
}
|
|
}
|
|
// we translate the probability as well, as it may reference states
|
|
Expression probNew = updates.getProbability(j);
|
|
if (probNew != null) {
|
|
// if probability expression is null, it implicitly encodes probability 1,
|
|
// so we don't have to change anything
|
|
probNew = probNew.deepCopy();
|
|
probNew = (Expression) probNew.evaluatePartially(state, varMap).simplify();
|
|
}
|
|
updatesNew.addUpdate(probNew, updateNew);
|
|
}
|
|
// Add new stuff to new module
|
|
commandNew.setUpdates(updatesNew);
|
|
moduleNew.addCommand(commandNew);
|
|
}
|
|
}
|
|
|
|
// Also generate the (clock) invariant for this state
|
|
invar = module.getInvariant();
|
|
if (invar != null) {
|
|
// Get (copy of) existing invariant for module
|
|
invar = invar.deepCopy();
|
|
// Evaluate (partially) invariant for this PC value
|
|
invar = (Expression) invar.evaluatePartially(state, varMap).simplify();
|
|
// If not "true", add into new invariant
|
|
if (!Expression.isTrue(invar)) {
|
|
invar = Expression.Parenth(Expression.Implies(exprPc, invar));
|
|
invarNew = (invarNew == null) ? invar : Expression.And(invarNew, invar);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Catch a (possibly) common source of mem-out errors during explicit-state reachability
|
|
catch (OutOfMemoryError e) {
|
|
states.clear();
|
|
System.gc();
|
|
throw new PrismLangException("Out of memory after exploring " + (src + 1) + " states of module " + module.getName(), module);
|
|
}
|
|
|
|
// Set the invariant for the new module
|
|
moduleNew.setInvariant(invarNew);
|
|
|
|
// Add variables to module
|
|
// (one for PC, then all original non-PC variables)
|
|
declNew = new Declaration(pc, new DeclarationInt(Expression.Int(0), Expression.Int(states.size() - 1)));
|
|
moduleNew.addDeclaration(declNew);
|
|
for (Declaration d : module.getDeclarations()) {
|
|
if (!pcVars.contains(d.getName())) {
|
|
moduleNew.addDeclaration((Declaration) d.deepCopy());
|
|
}
|
|
}
|
|
|
|
// Store the list of states representing the values of the PC
|
|
if (pcStates != null) {
|
|
states.toArrayList(pcStates);
|
|
//Collections.sort(pcStates);
|
|
//mainLog.println(pcStates);
|
|
}
|
|
|
|
return moduleNew;
|
|
}
|
|
|
|
/**
|
|
* Local utility method to test if a new identifier is safe to use,
|
|
* i.e. does not appear in model file or (if present) property file.
|
|
*/
|
|
private boolean identIsUnused(String id)
|
|
{
|
|
if (modulesFile.isIdentUsed(id))
|
|
return false;
|
|
// TODO: fix when have added prop file
|
|
//if (propertiesFile != null && propertiesFile.getAllIdentsUsed().indexOf(id) != -1)
|
|
//return false;
|
|
return true;
|
|
}
|
|
}
|