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.
 
 
 
 
 
 

391 lines
13 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 pta;
import java.util.*;
import parser.*;
import parser.ast.*;
import parser.type.*;
import parser.visitor.ASTTraverseModify;
import prism.*;
/**
* Model checker for probabilistic timed automata (PTAs).
*/
public class PTAModelChecker extends PrismComponent
{
// Model file
private ModulesFile modulesFile;
// Properties file
private PropertiesFile propertiesFile;
// Constants from model
private Values constantValues;
// Label list (combined: model/properties file)
private LabelList labelList;
// PTA
private PTA pta;
// Mapping from all (original model) variables to non-clocks only
private int[] nonClockVarMap;
/**
* Constructor.
*/
public PTAModelChecker(PrismComponent parent, ModulesFile modulesFile, PropertiesFile propertiesFile) throws PrismException
{
super(parent);
this.modulesFile = modulesFile;
this.propertiesFile = propertiesFile;
// Get combined constant values from model/properties
constantValues = new Values();
constantValues.addValues(modulesFile.getConstantValues());
if (propertiesFile != null)
constantValues.addValues(propertiesFile.getConstantValues());
// Build a combined label list and expand any constants
// (note labels in model are ignored (removed) during PTA translation so need to store here)
labelList = new LabelList();
for (int i = 0; i < modulesFile.getLabelList().size(); i++) {
labelList.addLabel(modulesFile.getLabelList().getLabelNameIdent(i), modulesFile.getLabelList().getLabel(i).deepCopy());
}
for (int i = 0; i < propertiesFile.getLabelList().size(); i++) {
labelList.addLabel(propertiesFile.getLabelList().getLabelNameIdent(i), propertiesFile.getLabelList().getLabel(i).deepCopy());
}
labelList = (LabelList) labelList.replaceConstants(constantValues);
// Build mapping from all (original model) variables to non-clocks only
int numVars = modulesFile.getNumVars();
nonClockVarMap = new int[numVars];
int count = 0;
for (int i = 0; i < numVars; i++) {
if (modulesFile.getVarType(i) instanceof TypeClock) {
nonClockVarMap[i] = -1;
} else {
nonClockVarMap[i] = count++;
}
}
}
/**
* Model check a property.
*/
public Result check(Expression expr) throws PrismException
{
Modules2PTA m2pta;
Result res;
String resultString;
long timer;
// Starting model checking
timer = System.currentTimeMillis();
// Check for system...endsystem - not supported yet
if (modulesFile.getSystemDefn() != null) {
throw new PrismException("The system...endsystem construct is not supported yet (try the digital clocks engine instead)");
}
// Translate ModulesFile object into a PTA object
mainLog.println("\nBuilding PTA...");
m2pta = new Modules2PTA(this, modulesFile);
pta = m2pta.translate();
mainLog.println("\nPTA: " + pta.infoString());
// Check for references to clocks - not allowed (yet)
// (do this before modifications below for better error reporting)
expr.accept(new ASTTraverseModify()
{
public Object visit(ExpressionVar e) throws PrismLangException
{
if (e.getType() instanceof TypeClock) {
throw new PrismLangException("Properties cannot contain references to clocks (try the digital clocks engine instead)", e);
} else {
return e;
}
}
});
// Take a copy of property, since will modify
expr = expr.deepCopy();
// Remove property refs ands labels from property
expr = (Expression) expr.expandPropRefsAndLabels(propertiesFile, labelList);
// Evaluate constants in property (easier to do now)
expr = (Expression) expr.replaceConstants(constantValues);
// Also simplify expression to optimise model checking
expr = (Expression) expr.simplify();
// Do model checking
res = checkExpression(expr);
// Model checking complete
timer = System.currentTimeMillis() - timer;
mainLog.println("\nModel checking completed in " + (timer / 1000.0) + " secs.");
// Print result to log
resultString = "Result";
if (!("Result".equals(expr.getResultName())))
resultString += " (" + expr.getResultName().toLowerCase() + ")";
resultString += ": " + res;
mainLog.print("\n" + resultString + "\n");
// Return result
return res;
}
/**
* Model check an expression (used recursively).
*/
private Result checkExpression(Expression expr) throws PrismException
{
Result res;
// Current range of supported properties is quite limited...
if (expr instanceof ExpressionProb)
res = checkExpressionProb((ExpressionProb) expr);
else if (expr instanceof ExpressionReward)
res = checkExpressionReward((ExpressionReward) expr);
else if (expr instanceof ExpressionFilter)
throw new PrismException("PTA model checker does not handle filters since it only computes values for the initial state");
else
throw new PrismException("PTA model checking not yet supported for this operator (try the digital clocks engine)");
return res;
}
/**
* Model check a P operator.
*/
private Result checkExpressionProb(ExpressionProb expr) throws PrismException
{
boolean min;
ExpressionTemporal exprTemp;
Expression exprTarget;
BitSet targetLocs;
int timeBound;
boolean timeBoundStrict;
double prob;
// Check whether Pmin=? or Pmax=? (only two cases allowed)
RelOp relOp = expr.getRelOp();
if (expr.getProb() != null) {
throw new PrismException("PTA model checking currently only supports Pmin=? and Pmax=? properties (try the digital clocks engine instead)");
}
min = relOp.isLowerBound() || relOp.isMin();
// Check this is a F path property (only case allowed at the moment)
if (!(expr.getExpression() instanceof ExpressionTemporal))
throw new PrismException("PTA model checking currently only supports the F path operator (try the digital clocks engine instead)");
exprTemp = (ExpressionTemporal) expr.getExpression();
if (exprTemp.getOperator() != ExpressionTemporal.P_F || !exprTemp.isSimplePathFormula())
throw new PrismException("PTA model checking currently only supports the F path operator (try the digital clocks engine instead)");
// Determine locations satisfying target
exprTarget = exprTemp.getOperand2();
targetLocs = checkLocationExpression(exprTarget);
mainLog.println("Target (" + exprTarget + ") satisfied by " + targetLocs.cardinality() + " locations.");
//mainLog.println(targetLocs);
// If there is a time bound, add a clock and encode this into target
if (exprTemp.hasBounds()) {
mainLog.println("Modifying PTA to encode time bound from property...");
// Get time bound info (is always of form <=T or <T)
timeBound = exprTemp.getUpperBound().evaluateInt(constantValues);
timeBoundStrict = exprTemp.upperBoundIsStrict();
// Check for non-allowed time bounds (negative)
if (timeBound < (timeBoundStrict ? 1 : 0)) {
throw new PrismLangException("Negative bound in " + exprTemp);
}
// Modify PTA to include time bound; get new target
targetLocs = buildTimeBoundIntoPta(pta, targetLocs, timeBound, timeBoundStrict);
mainLog.println("New PTA: " + pta.infoString());
}
// Compute probability of reaching the set of target locations
prob = computeProbabilisticReachability(targetLocs, min);
// Return result
return new Result(new Double(prob));
}
/**
* Build a time bounded reachability query into a PTA; return the new target location set.
*/
private BitSet buildTimeBoundIntoPta(PTA pta, BitSet targetLocs, int timeBound, boolean timeBoundStrict)
{
String timerClock = null;
int timerClockIndex, numLocs, newTargetLoc;
String newTargetLocString;
List<Transition> trNewList;
Transition trNew;
BitSet targetLocsNew;
boolean toTarget;
int i;
// Add a timer clock
timerClock = "time";
while (pta.getClockIndex(timerClock) != -1)
timerClock += "_";
timerClockIndex = pta.addClock(timerClock);
// Add a new target location
numLocs = pta.getNumLocations();
newTargetLocString = "target";
while (pta.getLocationIndex(newTargetLocString) != -1)
newTargetLocString += "_";
newTargetLoc = pta.addLocation(newTargetLocString);
// Go through old (on-target) locations
for (i = 0; i < numLocs; i++) {
trNewList = new ArrayList<Transition>();
for (Transition tr : pta.getTransitions(i)) {
// See if the transition can go to a target location
toTarget = false;
for (Edge e : tr.getEdges()) {
if (targetLocs.get(e.getDestination())) {
toTarget = true;
break;
}
}
// Copy transition, modify edges going to target and add guard
if (toTarget) {
trNew = new Transition(tr);
for (Edge e : trNew.getEdges()) {
if (targetLocs.get(e.getDestination())) {
e.setDestination(newTargetLoc);
}
}
if (timeBoundStrict)
trNew.addGuardConstraint(Constraint.buildLt(timerClockIndex, timeBound));
else
trNew.addGuardConstraint(Constraint.buildLeq(timerClockIndex, timeBound));
trNewList.add(trNew);
// Modify guard of copied transition
if (timeBoundStrict)
tr.addGuardConstraint(Constraint.buildGeq(timerClockIndex, timeBound));
else
tr.addGuardConstraint(Constraint.buildGt(timerClockIndex, timeBound));
}
}
// Add new transitions to PTA
for (Transition tr : trNewList) {
pta.addTransition(tr);
}
}
// Re-generate set of target locations
// (newly added target location, plus initial location (always 0) is this is a target)
targetLocsNew = new BitSet(pta.getNumLocations());
targetLocsNew.set(newTargetLoc);
if (targetLocs.get(0)) {
targetLocsNew.set(0);
}
return targetLocsNew;
}
/**
* Compute the min/max probability, from the initial state, of reaching a set of target locations.
*/
private double computeProbabilisticReachability(BitSet targetLocs, boolean min) throws PrismException
{
// Catch the special case where the initial state is already a target - just return 1.0 directly
if (targetLocs.get(0)) {
mainLog.println("Skipping numerical computation since initial state is a target...");
return 1.0;
}
// Determine which method to use for computation
String ptaMethod = settings.getString(PrismSettings.PRISM_PTA_METHOD);
// Do probability computation through abstraction/refinement/stochastic games
if (ptaMethod.equals("Stochastic games")) {
PTAAbstractRefine ptaAR;
ptaAR = new PTAAbstractRefine(this);
String arOptions = settings.getString(PrismSettings.PRISM_AR_OPTIONS);
ptaAR.parseOptions(arOptions.split(","));
return ptaAR.forwardsReachAbstractRefine(pta, targetLocs, null, min);
}
// Do probability computation through backwards reachability
else if (ptaMethod.equals("Backwards reachability")) {
BackwardsReach ptaBw = new BackwardsReach(this);
return ptaBw.computeProbabilisticReachability(pta, targetLocs, min);
}
else
throw new PrismException("Unknown PTA solution method");
}
/**
* Model check an R operator.
*/
private Result checkExpressionReward(ExpressionReward expr) throws PrismException
{
throw new PrismException("Reward properties not yet supported for PTA model checking (try the digital clocks engine instead)");
}
/**
* Determine which locations in the PTA satisfy a (Boolean) expression.
* Note: This is rather inefficiently at the moment.
* TODO: potentially use explicit.StateMC on dummy model eventually
*/
private BitSet checkLocationExpression(Expression expr) throws PrismException
{
int i, n;
BitSet res;
// Labels - expand and recurse
// (note: currently not used - these are expanded earlier)
if (expr instanceof ExpressionLabel) {
ExpressionLabel exprLabel = (ExpressionLabel) expr;
if (exprLabel.isDeadlockLabel())
throw new PrismException("The \"deadlock\" label is not yet supported for PTAs");
if (exprLabel.isInitLabel())
throw new PrismException("The \"init\" label is not yet supported for PTAs");
i = labelList.getLabelIndex(exprLabel.getName());
if (i == -1)
throw new PrismException("Unknown label \"" + exprLabel.getName() + "\" in property");
// Check recursively
return checkLocationExpression(labelList.getLabel(i));
}
// Other expressions...
else {
List<Object> states;
//Object[] state;
states = pta.getLocationNameList();
n = states.size();
res = new BitSet(n);
for (i = 0; i < n; i++) {
//state = (Object[])states.get(i);
State state = (State) states.get(i);
if (expr.evaluateBoolean(state, nonClockVarMap)) {
res.set(i);
}
}
}
return res;
}
}