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.
1234 lines
46 KiB
1234 lines
46 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2013-
|
|
// Authors:
|
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford)
|
|
// * Ernst Moritz Hahn <emhahn@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
|
|
//
|
|
//==============================================================================
|
|
|
|
/*
|
|
* TODO
|
|
* - lumpers should start convert directly from ParamModel plus scheduler and rewards
|
|
* rather than from AlterablePMC as is done currently
|
|
* - could print to log for which parameter values results will be valid
|
|
* - could implement steady-state properties for models w.o.
|
|
* nondeterminism but not needed at least not for next paper
|
|
* - could implement DAG-like functions + probabilistic equality
|
|
* - for each function num and den would each be pointers into DAG
|
|
* - then either exact equality (expensive)
|
|
* - or probabilistic equality (Schwartz-Zippel)
|
|
* - also, DAG-like regexp representation possible
|
|
* - for comparism with previous work, use
|
|
* - could implement other types of regions apart from boxes
|
|
* - could later improve support for optimisation over parameters
|
|
* - using apache math commons
|
|
* - or ipopt (zip file with java support for linux, windows, mac os x exists)
|
|
* - libraries used should be loaded by classloader to make easier to use in
|
|
* projects where we cannot use GPLed code (just delete library and that's it)
|
|
* - could later add support for abstraction of functions
|
|
* - could integrate in GUI (student project?)
|
|
* - if time left, add JUnit tests at least for BigRational and maybe functions and regions
|
|
* basically for all classes where interface is more or less fixed
|
|
* - could try to bind to Ginac for comparability, but probably not much difference
|
|
* - should integrate binding to solvers (RAHD and the like) at some point
|
|
*/
|
|
|
|
package param;
|
|
|
|
import java.util.BitSet;
|
|
import java.util.List;
|
|
|
|
import param.Lumper.BisimType;
|
|
import param.StateEliminator.EliminationOrder;
|
|
import parser.State;
|
|
import parser.Values;
|
|
import parser.ast.Expression;
|
|
import parser.ast.ExpressionBinaryOp;
|
|
import parser.ast.ExpressionConstant;
|
|
import parser.ast.ExpressionExists;
|
|
import parser.ast.ExpressionFilter;
|
|
import parser.ast.ExpressionFilter.FilterOperator;
|
|
import parser.ast.ExpressionForAll;
|
|
import parser.ast.ExpressionFormula;
|
|
import parser.ast.ExpressionFunc;
|
|
import parser.ast.ExpressionLabel;
|
|
import parser.ast.ExpressionLiteral;
|
|
import parser.ast.ExpressionProb;
|
|
import parser.ast.ExpressionProp;
|
|
import parser.ast.ExpressionReward;
|
|
import parser.ast.ExpressionSS;
|
|
import parser.ast.ExpressionTemporal;
|
|
import parser.ast.ExpressionUnaryOp;
|
|
import parser.ast.LabelList;
|
|
import parser.ast.ModulesFile;
|
|
import parser.ast.PropertiesFile;
|
|
import parser.ast.Property;
|
|
import parser.ast.RelOp;
|
|
import parser.ast.RewardStruct;
|
|
import parser.type.TypeBool;
|
|
import parser.type.TypeDouble;
|
|
import parser.type.TypeInt;
|
|
import parser.type.TypePathBool;
|
|
import parser.type.TypePathDouble;
|
|
import prism.ModelType;
|
|
import prism.PrismComponent;
|
|
import prism.PrismException;
|
|
import prism.PrismLog;
|
|
import prism.PrismPrintStreamLog;
|
|
import prism.PrismSettings;
|
|
import prism.PrismNotSupportedException;
|
|
import prism.Result;
|
|
import edu.jas.kern.ComputerThreads;
|
|
import explicit.Model;
|
|
|
|
/**
|
|
* Model checker for parametric Markov models.
|
|
*
|
|
* @author Ernst Moritz Hahn <emhahn@cs.ox.ac.uk> (University of Oxford)
|
|
*/
|
|
final public class ParamModelChecker extends PrismComponent
|
|
{
|
|
// Model file (for reward structures, etc.)
|
|
private ModulesFile modulesFile = null;
|
|
|
|
// Properties file (for labels, constants, etc.)
|
|
private PropertiesFile propertiesFile = null;
|
|
|
|
// Constants (extracted from model/properties)
|
|
private Values constantValues;
|
|
|
|
// The result of model checking will be stored here
|
|
private Result result;
|
|
|
|
// Flags/settings
|
|
|
|
/** The mode (parametric or exact)? */
|
|
private ParamMode mode;
|
|
|
|
// Verbosity level
|
|
private int verbosity = 0;
|
|
|
|
private BigRational[] paramLower;
|
|
private BigRational[] paramUpper;
|
|
|
|
private FunctionFactory functionFactory;
|
|
private RegionFactory regionFactory;
|
|
private ConstraintChecker constraintChecker;
|
|
private ValueComputer valueComputer;
|
|
|
|
private BigRational precision;
|
|
private int splitMethod;
|
|
private EliminationOrder eliminationOrder;
|
|
private int numRandomPoints;
|
|
private Lumper.BisimType bisimType;
|
|
private boolean simplifyRegions;
|
|
|
|
private ModelBuilder modelBuilder;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public ParamModelChecker(PrismComponent parent, ParamMode mode) throws PrismException
|
|
{
|
|
super(parent);
|
|
this.mode = mode;
|
|
|
|
// If present, initialise settings from PrismSettings
|
|
if (settings != null) {
|
|
verbosity = settings.getBoolean(PrismSettings.PRISM_VERBOSE) ? 10 : 1;
|
|
precision = new BigRational(settings.getString(PrismSettings.PRISM_PARAM_PRECISION));
|
|
String splitMethodString = settings.getString(PrismSettings.PRISM_PARAM_SPLIT);
|
|
if (splitMethodString.equals("Longest")) {
|
|
splitMethod = BoxRegion.SPLIT_LONGEST;
|
|
} else if (splitMethodString.equals("All")) {
|
|
splitMethod = BoxRegion.SPLIT_ALL;
|
|
} else {
|
|
throw new PrismException("unknown region splitting method " + splitMethodString);
|
|
}
|
|
String eliminationOrderString = settings.getString(PrismSettings.PRISM_PARAM_ELIM_ORDER);
|
|
if (eliminationOrderString.equals("Arbitrary")) {
|
|
eliminationOrder = EliminationOrder.ARBITRARY;
|
|
} else if (eliminationOrderString.equals("Forward")) {
|
|
eliminationOrder = EliminationOrder.FORWARD;
|
|
} else if (eliminationOrderString.equals("Forward-reversed")) {
|
|
eliminationOrder = EliminationOrder.FORWARD_REVERSED;
|
|
} else if (eliminationOrderString.equals("Backward")) {
|
|
eliminationOrder = EliminationOrder.BACKWARD;
|
|
} else if (eliminationOrderString.equals("Backward-reversed")) {
|
|
eliminationOrder = EliminationOrder.BACKWARD_REVERSED;
|
|
} else if (eliminationOrderString.equals("Random")) {
|
|
eliminationOrder = EliminationOrder.RANDOM;
|
|
} else {
|
|
throw new PrismException("unknown state elimination order " + eliminationOrderString);
|
|
}
|
|
numRandomPoints = settings.getInteger(PrismSettings.PRISM_PARAM_RANDOM_POINTS);
|
|
String bisimTypeString = settings.getString(PrismSettings.PRISM_PARAM_BISIM);
|
|
if (bisimTypeString.equals("Weak")) {
|
|
bisimType = BisimType.WEAK;
|
|
} else if (bisimTypeString.equals("Strong")) {
|
|
bisimType = BisimType.STRONG;
|
|
} else if (bisimTypeString.equals("None")) {
|
|
bisimType = BisimType.NULL;
|
|
} else {
|
|
throw new PrismException("unknown bisimulation type " + bisimTypeString);
|
|
}
|
|
simplifyRegions = settings.getBoolean(PrismSettings.PRISM_PARAM_SUBSUME_REGIONS);
|
|
}
|
|
}
|
|
|
|
// Setters/getters
|
|
|
|
/**
|
|
* Set the attached model file (for e.g. reward structures when model checking)
|
|
* and the attached properties file (for e.g. constants/labels when model checking)
|
|
*/
|
|
public void setModulesFileAndPropertiesFile(ModulesFile modulesFile, PropertiesFile propertiesFile)
|
|
{
|
|
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());
|
|
}
|
|
|
|
public ParamMode getMode()
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
// Model checking functions
|
|
|
|
/**
|
|
* Model check an expression, process and return the result.
|
|
* Information about states and model constants should be attached to the model.
|
|
* For other required info (labels, reward structures, etc.), use the methods
|
|
* {@link #setModulesFile} and {@link #setPropertiesFile}
|
|
* to attach the original model/properties files.
|
|
*/
|
|
public Result check(Model model, Expression expr) throws PrismException
|
|
{
|
|
ParamModel paramModel = (ParamModel) model;
|
|
functionFactory = paramModel.getFunctionFactory();
|
|
constraintChecker = new ConstraintChecker(numRandomPoints);
|
|
regionFactory = new BoxRegionFactory(functionFactory, constraintChecker, precision,
|
|
model.getNumStates(), model.getFirstInitialState(), simplifyRegions, splitMethod);
|
|
valueComputer = new ValueComputer(this, mode, paramModel, regionFactory, precision, eliminationOrder, bisimType);
|
|
|
|
long timer = 0;
|
|
|
|
// Remove labels from property, using combined label list (on a copy of the expression)
|
|
// This is done now so that we can handle labels nested below operators that are not
|
|
// handled natively by the model checker yet (just evaluate()ed in a loop).
|
|
expr = (Expression) expr.deepCopy().expandLabels(propertiesFile.getCombinedLabelList());
|
|
|
|
// Also evaluate/replace any constants
|
|
//expr = (Expression) expr.replaceConstants(constantValues);
|
|
|
|
// Wrap a filter round the property, if needed
|
|
// (in order to extract the final result of model checking)
|
|
expr = ExpressionFilter.addDefaultFilterIfNeeded(expr, model.getNumInitialStates() == 1);
|
|
|
|
// Do model checking and store result vector
|
|
timer = System.currentTimeMillis();
|
|
BitSet needStates = new BitSet(model.getNumStates());
|
|
needStates.set(0, model.getNumStates());
|
|
RegionValues vals = checkExpression(paramModel, expr, needStates);
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.println("\nTime for model checking: " + timer / 1000.0 + " seconds.");
|
|
|
|
if (constraintChecker.unsoundCheckWasUsed()) {
|
|
mainLog.printWarning("Computation of Boolean values / parameter regions used heuristic sampling, results are potentially inaccurate.");
|
|
}
|
|
|
|
// Store result
|
|
result = new Result();
|
|
vals.clearExceptInit();
|
|
result.setResult(new ParamResult(mode, vals, modelBuilder, functionFactory));
|
|
|
|
/* // Output plot to tex file
|
|
if (paramLower.length == 2) {
|
|
try {
|
|
FileOutputStream file = new FileOutputStream("out.tex");
|
|
ResultExporter printer = new ResultExporter();
|
|
printer.setOutputStream(file);
|
|
printer.setRegionValues(vals);
|
|
printer.setPointsPerDimension(19);
|
|
printer.print();
|
|
file.close();
|
|
} catch (Exception e) {
|
|
throw new PrismException("file could not be written");
|
|
}
|
|
}*/
|
|
|
|
return result;
|
|
}
|
|
|
|
private int parserBinaryOpToRegionOp(int parserOp) throws PrismException
|
|
{
|
|
int regionOp;
|
|
switch (parserOp) {
|
|
case ExpressionBinaryOp.IMPLIES:
|
|
regionOp = Region.IMPLIES;
|
|
break;
|
|
case ExpressionBinaryOp.IFF:
|
|
regionOp = Region.IMPLIES;
|
|
break;
|
|
case ExpressionBinaryOp.OR:
|
|
regionOp = Region.OR;
|
|
break;
|
|
case ExpressionBinaryOp.AND:
|
|
regionOp = Region.AND;
|
|
break;
|
|
case ExpressionBinaryOp.EQ:
|
|
regionOp = Region.EQ;
|
|
break;
|
|
case ExpressionBinaryOp.NE:
|
|
regionOp = Region.NE;
|
|
break;
|
|
case ExpressionBinaryOp.GT:
|
|
regionOp = Region.GT;
|
|
break;
|
|
case ExpressionBinaryOp.GE:
|
|
regionOp = Region.GE;
|
|
break;
|
|
case ExpressionBinaryOp.LT:
|
|
regionOp = Region.LT;
|
|
break;
|
|
case ExpressionBinaryOp.LE:
|
|
regionOp = Region.LE;
|
|
break;
|
|
case ExpressionBinaryOp.PLUS:
|
|
regionOp = Region.PLUS;
|
|
break;
|
|
case ExpressionBinaryOp.MINUS:
|
|
regionOp = Region.MINUS;
|
|
break;
|
|
case ExpressionBinaryOp.TIMES:
|
|
regionOp = Region.TIMES;
|
|
break;
|
|
case ExpressionBinaryOp.DIVIDE:
|
|
regionOp = Region.DIVIDE;
|
|
break;
|
|
default:
|
|
throw new PrismNotSupportedException("operator \"" + ExpressionBinaryOp.opSymbols[parserOp]
|
|
+ "\" not currently supported for " + mode + " analyses");
|
|
}
|
|
return regionOp;
|
|
}
|
|
|
|
private int parserUnaryOpToRegionOp(int parserOp) throws PrismException
|
|
{
|
|
int regionOp;
|
|
switch (parserOp) {
|
|
case ExpressionUnaryOp.MINUS:
|
|
regionOp = Region.UMINUS;
|
|
break;
|
|
case ExpressionUnaryOp.NOT:
|
|
regionOp = Region.NOT;
|
|
break;
|
|
case ExpressionUnaryOp.PARENTH:
|
|
regionOp = Region.PARENTH;
|
|
break;
|
|
default:
|
|
throw new PrismNotSupportedException("operator \"" + ExpressionBinaryOp.opSymbols[parserOp]
|
|
+ "\" not currently supported for " + mode + " analyses");
|
|
}
|
|
return regionOp;
|
|
}
|
|
|
|
/**
|
|
* Model check an expression and return a vector result values over all states.
|
|
* Information about states and model constants should be attached to the model.
|
|
* For other required info (labels, reward structures, etc.), use the methods
|
|
* {@link #setModulesFile} and {@link #setPropertiesFile}
|
|
* to attach the original model/properties files.
|
|
*/
|
|
RegionValues checkExpression(ParamModel model, Expression expr, BitSet needStates) throws PrismException
|
|
{
|
|
RegionValues res;
|
|
if (expr instanceof ExpressionUnaryOp) {
|
|
res = checkExpressionUnaryOp(model, (ExpressionUnaryOp) expr, needStates);
|
|
} else if (expr instanceof ExpressionBinaryOp) {
|
|
res = checkExpressionBinaryOp(model, (ExpressionBinaryOp) expr, needStates);
|
|
} else if (expr instanceof ExpressionLabel) {
|
|
res = checkExpressionLabel(model, (ExpressionLabel) expr, needStates);
|
|
} else if (expr instanceof ExpressionFormula) {
|
|
// This should have been defined or expanded by now.
|
|
if (((ExpressionFormula) expr).getDefinition() != null)
|
|
res = checkExpression(model, ((ExpressionFormula) expr).getDefinition(), needStates);
|
|
else
|
|
throw new PrismException("Unexpanded formula \"" + ((ExpressionFormula) expr).getName() + "\"");
|
|
} else if (expr instanceof ExpressionProp) {
|
|
res = checkExpressionProp(model, (ExpressionProp) expr, needStates);
|
|
} else if (expr instanceof ExpressionFilter) {
|
|
if (((ExpressionFilter) expr).isParam()) {
|
|
res = checkExpressionFilterParam(model, (ExpressionFilter) expr, needStates);
|
|
} else {
|
|
res = checkExpressionFilter(model, (ExpressionFilter) expr, needStates);
|
|
}
|
|
} else if (expr instanceof ExpressionProb) {
|
|
res = checkExpressionProb(model, (ExpressionProb) expr, needStates);
|
|
} else if (expr instanceof ExpressionReward) {
|
|
res = checkExpressionReward(model, (ExpressionReward) expr, needStates);
|
|
} else if (expr instanceof ExpressionSS) {
|
|
res = checkExpressionSteadyState(model, (ExpressionSS) expr, needStates);
|
|
} else if (expr instanceof ExpressionForAll || expr instanceof ExpressionExists) {
|
|
throw new PrismNotSupportedException("Non-probabilistic CTL model checking is currently not supported in the " + mode.engine());
|
|
} else if (expr instanceof ExpressionFunc && ((ExpressionFunc)expr).getNameCode() == ExpressionFunc.MULTI) {
|
|
throw new PrismNotSupportedException("Multi-objective model checking is not supported in the " + mode.engine());
|
|
} else {
|
|
res = checkExpressionAtomic(model, expr, needStates);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
private RegionValues checkExpressionAtomic(ParamModel model, Expression expr, BitSet needStates) throws PrismException
|
|
{
|
|
expr = (Expression) expr.deepCopy().replaceConstants(constantValues);
|
|
|
|
int numStates = model.getNumStates();
|
|
List<State> statesList = model.getStatesList();
|
|
StateValues stateValues = new StateValues(numStates, model.getFirstInitialState());
|
|
int[] varMap = new int[statesList.get(0).varValues.length];
|
|
for (int var = 0; var < varMap.length; var++) {
|
|
varMap[var] = var;
|
|
}
|
|
for (int state = 0; state < numStates; state++) {
|
|
Expression exprVar = (Expression) expr.deepCopy().evaluatePartially(statesList.get(state), varMap);
|
|
if (needStates.get(state)) {
|
|
if (exprVar instanceof ExpressionLiteral) {
|
|
ExpressionLiteral exprLit = (ExpressionLiteral) exprVar;
|
|
if (exprLit.getType() instanceof TypeBool) {
|
|
stateValues.setStateValue(state, exprLit.evaluateBoolean());
|
|
} else if (exprLit.getType() instanceof TypeInt || exprLit.getType() instanceof TypeDouble) {
|
|
String exprStr = exprLit.getString();
|
|
BigRational exprRat = new BigRational(exprStr);
|
|
stateValues.setStateValue(state, functionFactory.fromBigRational(exprRat));
|
|
} else {
|
|
throw new PrismNotSupportedException("model checking expresssion " + expr + " not supported for " + mode + " models");
|
|
}
|
|
} else if (exprVar instanceof ExpressionConstant) {
|
|
ExpressionConstant exprConst = (ExpressionConstant) exprVar;
|
|
stateValues.setStateValue(state, functionFactory.getVar(exprConst.getName()));
|
|
} else {
|
|
throw new PrismNotSupportedException("cannot handle expression " + expr + " in " + mode + " analysis");
|
|
}
|
|
} else {
|
|
if (exprVar.getType() instanceof TypeBool) {
|
|
stateValues.setStateValue(state, false);
|
|
} else {
|
|
stateValues.setStateValue(state, functionFactory.getZero());
|
|
}
|
|
}
|
|
}
|
|
return regionFactory.completeCover(stateValues);
|
|
}
|
|
|
|
protected RegionValues checkExpressionUnaryOp(ParamModel model, ExpressionUnaryOp expr, BitSet needStates) throws PrismException
|
|
{
|
|
RegionValues resInner = checkExpression(model, expr.getOperand(), needStates);
|
|
resInner.clearNotNeeded(needStates);
|
|
|
|
return resInner.unaryOp(parserUnaryOpToRegionOp(expr.getOperator()));
|
|
}
|
|
|
|
/**
|
|
* Model check a binary operator.
|
|
*/
|
|
protected RegionValues checkExpressionBinaryOp(ParamModel model, ExpressionBinaryOp expr, BitSet needStates) throws PrismException
|
|
{
|
|
RegionValues res1 = checkExpression(model, expr.getOperand1(), needStates);
|
|
RegionValues res2 = checkExpression(model, expr.getOperand2(), needStates);
|
|
res1.clearNotNeeded(needStates);
|
|
res2.clearNotNeeded(needStates);
|
|
|
|
return res1.binaryOp(parserBinaryOpToRegionOp(expr.getOperator()), res2);
|
|
}
|
|
|
|
/**
|
|
* Model check a label.
|
|
*/
|
|
protected RegionValues checkExpressionLabel(ParamModel model, ExpressionLabel expr, BitSet needStates) throws PrismException
|
|
{
|
|
LabelList ll;
|
|
int i;
|
|
|
|
// treat special cases
|
|
if (expr.isDeadlockLabel()) {
|
|
int numStates = model.getNumStates();
|
|
StateValues stateValues = new StateValues(numStates, model.getFirstInitialState());
|
|
for (i = 0; i < numStates; i++) {
|
|
stateValues.setStateValue(i, model.isDeadlockState(i));
|
|
}
|
|
return regionFactory.completeCover(stateValues);
|
|
} else if (expr.isInitLabel()) {
|
|
int numStates = model.getNumStates();
|
|
StateValues stateValues = new StateValues(numStates, model.getFirstInitialState());
|
|
for (i = 0; i < numStates; i++) {
|
|
stateValues.setStateValue(i, model.isInitialState(i));
|
|
}
|
|
return regionFactory.completeCover(stateValues);
|
|
} else {
|
|
ll = propertiesFile.getCombinedLabelList();
|
|
i = ll.getLabelIndex(expr.getName());
|
|
if (i == -1)
|
|
throw new PrismException("Unknown label \"" + expr.getName() + "\" in property");
|
|
// check recursively
|
|
return checkExpression(model, ll.getLabel(i), needStates);
|
|
}
|
|
}
|
|
|
|
// Check property ref
|
|
|
|
protected RegionValues checkExpressionProp(ParamModel model, ExpressionProp expr, BitSet needStates) throws PrismException
|
|
{
|
|
// Look up property and check recursively
|
|
Property prop = propertiesFile.lookUpPropertyObjectByName(expr.getName());
|
|
if (prop != null) {
|
|
mainLog.println("\nModel checking : " + prop);
|
|
return checkExpression(model, prop.getExpression(), needStates);
|
|
} else {
|
|
throw new PrismException("Unknown property reference " + expr);
|
|
}
|
|
}
|
|
|
|
// Check filter
|
|
|
|
protected RegionValues checkExpressionFilter(ParamModel model, ExpressionFilter expr, BitSet needStates) throws PrismException
|
|
{
|
|
Expression filter = expr.getFilter();
|
|
if (filter == null) {
|
|
filter = Expression.True();
|
|
}
|
|
boolean filterTrue = Expression.isTrue(filter);
|
|
|
|
BitSet needStatesInner = new BitSet(needStates.size());
|
|
needStatesInner.set(0, needStates.size());
|
|
RegionValues rvFilter = checkExpression(model, filter, needStatesInner);
|
|
if (!rvFilter.parameterIndependent()) {
|
|
throw new PrismException("currently, parameter-dependent filters are not supported");
|
|
}
|
|
BitSet bsFilter = rvFilter.getStateValues().toBitSet();
|
|
// Check filter satisfied by exactly one state
|
|
FilterOperator op = expr.getOperatorType();
|
|
if (op == FilterOperator.STATE && bsFilter.cardinality() != 1) {
|
|
String s = "Filter should be satisfied in exactly 1 state";
|
|
s += " (but \"" + filter + "\" is true in " + bsFilter.cardinality() + " states)";
|
|
throw new PrismException(s);
|
|
}
|
|
if (op == FilterOperator.FIRST) {
|
|
// only first state is of interest
|
|
bsFilter.clear(bsFilter.nextSetBit(0) + 1, bsFilter.length());
|
|
}
|
|
RegionValues vals = checkExpression(model, expr.getOperand(), bsFilter);
|
|
|
|
// Check if filter state set is empty; we treat this as an error
|
|
if (bsFilter.isEmpty()) {
|
|
throw new PrismException("Filter satisfies no states");
|
|
}
|
|
|
|
// Remember whether filter is for the initial state and, if so, whether there's just one
|
|
boolean filterInit = (filter instanceof ExpressionLabel && ((ExpressionLabel) filter).isInitLabel());
|
|
// Print out number of states satisfying filter
|
|
if (!filterInit) {
|
|
mainLog.println("\nStates satisfying filter " + filter + ": " + bsFilter.cardinality());
|
|
}
|
|
|
|
// Compute result according to filter type
|
|
RegionValues resVals = null;
|
|
switch (op) {
|
|
case PRINT:
|
|
case PRINTALL:
|
|
// Format of print-out depends on type
|
|
if (expr.getType() instanceof TypeBool) {
|
|
// NB: 'usual' case for filter(print,...) on Booleans is to use no filter
|
|
mainLog.print("\nSatisfying states");
|
|
mainLog.println(filterTrue ? ":" : " that are also in filter " + filter + ":");
|
|
} else {
|
|
mainLog.println("\nResults (non-zero only) for filter " + filter + ":");
|
|
}
|
|
|
|
vals.printFiltered(mainLog, mode, expr.getType(), bsFilter,
|
|
model.getStatesList(),
|
|
op == FilterOperator.PRINT, // printSparse if PRINT
|
|
true, // print state values
|
|
true); // print state index
|
|
|
|
resVals = vals;
|
|
break;
|
|
case MIN:
|
|
case MAX:
|
|
case ARGMIN:
|
|
case ARGMAX:
|
|
throw new PrismNotSupportedException("operation not implemented for " + mode + " models");
|
|
case COUNT:
|
|
resVals = vals.op(Region.COUNT, bsFilter);
|
|
break;
|
|
case SUM:
|
|
resVals = vals.op(Region.PLUS, bsFilter);
|
|
break;
|
|
case AVG:
|
|
resVals = vals.op(Region.AVG, bsFilter);
|
|
break;
|
|
case FIRST:
|
|
if (bsFilter.cardinality() < 1) {
|
|
throw new PrismException("Filter should be satisfied in at least 1 state.");
|
|
}
|
|
resVals = vals.op(Region.FIRST, bsFilter);
|
|
break;
|
|
case RANGE:
|
|
throw new PrismNotSupportedException("operation not implemented for " + mode + " models");
|
|
case FORALL:
|
|
resVals = vals.op(Region.FORALL, bsFilter);
|
|
break;
|
|
case EXISTS:
|
|
resVals = vals.op(Region.EXISTS, bsFilter);
|
|
break;
|
|
case STATE:
|
|
resVals = vals.op(Region.FIRST, bsFilter);
|
|
break;
|
|
default:
|
|
throw new PrismException("Unrecognised filter type \"" + expr.getOperatorName() + "\"");
|
|
}
|
|
|
|
return resVals;
|
|
}
|
|
|
|
// check filter over parameters
|
|
|
|
protected RegionValues checkExpressionFilterParam(ParamModel model, ExpressionFilter expr, BitSet needStates) throws PrismException
|
|
{
|
|
// Filter info
|
|
Expression filter = expr.getFilter();
|
|
// Create default filter (true) if none given
|
|
if (filter == null) {
|
|
filter = Expression.True();
|
|
}
|
|
RegionValues rvFilter = checkExpression(model, filter, needStates);
|
|
RegionValues vals = checkExpression(model, expr.getOperand(), needStates);
|
|
|
|
Optimiser opt = new Optimiser(vals, rvFilter, expr.getOperatorType() == FilterOperator.MIN);
|
|
System.out.println("\n" + opt.optimise());
|
|
|
|
return null;
|
|
|
|
/*
|
|
// Remember whether filter is for the initial state and, if so, whether there's just one
|
|
filterInit = (filter instanceof ExpressionLabel && ((ExpressionLabel) filter).isInitLabel());
|
|
filterInitSingle = filterInit & model.getNumInitialStates() == 1;
|
|
// Print out number of states satisfying filter
|
|
if (!filterInit)
|
|
mainLog.println("\nStates satisfying filter " + filter + ": " + bsFilter.cardinality());
|
|
|
|
// Compute result according to filter type
|
|
op = expr.getOperatorType();
|
|
switch (op) {
|
|
case PRINT:
|
|
// Format of print-out depends on type
|
|
if (expr.getType() instanceof TypeBool) {
|
|
// NB: 'usual' case for filter(print,...) on Booleans is to use no filter
|
|
mainLog.print("\nSatisfying states");
|
|
mainLog.println(filterTrue ? ":" : " that are also in filter " + filter + ":");
|
|
vals.printFiltered(mainLog, bsFilter);
|
|
} else {
|
|
mainLog.println("\nResults (non-zero only) for filter " + filter + ":");
|
|
vals.printFiltered(mainLog, bsFilter);
|
|
}
|
|
// Result vector is unchanged; for ARGMIN, don't store a single value (in resObj)
|
|
// Also, don't bother with explanation string
|
|
resVals = vals;
|
|
// Set vals to null to stop it being cleared below
|
|
vals = null;
|
|
break;
|
|
case MIN:
|
|
// Compute min
|
|
// Store as object/vector
|
|
resObj = vals.minOverBitSet(bsFilter);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Minimum value over " + filterStatesString;
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
// Also find states that (are close to) selected value for display to log
|
|
// TODO: un-hard-code precision once RegionValues knows hoe precise it is
|
|
bsMatch = vals.getBitSetFromCloseValue(resObj, 1e-5, false);
|
|
bsMatch.and(bsFilter);
|
|
break;
|
|
case MAX:
|
|
// Compute max
|
|
// Store as object/vector
|
|
resObj = vals.maxOverBitSet(bsFilter);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Maximum value over " + filterStatesString;
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
// Also find states that (are close to) selected value for display to log
|
|
// TODO: un-hard-code precision once RegionValues knows hoe precise it is
|
|
bsMatch = vals.getBitSetFromCloseValue(resObj, 1e-5, false);
|
|
bsMatch.and(bsFilter);
|
|
break;
|
|
case ARGMIN:
|
|
// Compute/display min
|
|
resObj = vals.minOverBitSet(bsFilter);
|
|
mainLog.print("\nMinimum value over " + filterStatesString + ": " + resObj);
|
|
// Find states that (are close to) selected value
|
|
// TODO: un-hard-code precision once RegionValues knows hoe precise it is
|
|
bsMatch = vals.getBitSetFromCloseValue(resObj, 1e-5, false);
|
|
bsMatch.and(bsFilter);
|
|
// Store states in vector; for ARGMIN, don't store a single value (in resObj)
|
|
// Also, don't bother with explanation string
|
|
resVals = RegionValues.createFromBitSet(bsMatch, model);
|
|
// Print out number of matching states, but not the actual states
|
|
mainLog.println("\nNumber of states with minimum value: " + bsMatch.cardinality());
|
|
bsMatch = null;
|
|
break;
|
|
case ARGMAX:
|
|
// Compute/display max
|
|
resObj = vals.maxOverBitSet(bsFilter);
|
|
mainLog.print("\nMaximum value over " + filterStatesString + ": " + resObj);
|
|
// Find states that (are close to) selected value
|
|
bsMatch = vals.getBitSetFromCloseValue(resObj, precision, false);
|
|
bsMatch.and(bsFilter);
|
|
// Store states in vector; for ARGMAX, don't store a single value (in resObj)
|
|
// Also, don't bother with explanation string
|
|
resVals = RegionValues.createFromBitSet(bsMatch, model);
|
|
// Print out number of matching states, but not the actual states
|
|
mainLog.println("\nNumber of states with maximum value: " + bsMatch.cardinality());
|
|
bsMatch = null;
|
|
break;
|
|
case COUNT:
|
|
// Compute count
|
|
int count = vals.countOverBitSet(bsFilter);
|
|
// Store as object/vector
|
|
resObj = new Integer(count);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = filterTrue ? "Count of satisfying states" : "Count of satisfying states also in filter";
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
break;
|
|
case SUM:
|
|
// Compute sum
|
|
// Store as object/vector
|
|
resObj = vals.sumOverBitSet(bsFilter);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Sum over " + filterStatesString;
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
break;
|
|
case AVG:
|
|
// Compute average
|
|
// Store as object/vector
|
|
resObj = vals.averageOverBitSet(bsFilter);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Average over " + filterStatesString;
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
break;
|
|
case FIRST:
|
|
// Find first value
|
|
resObj = vals.firstFromBitSet(bsFilter);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Value in ";
|
|
if (filterInit) {
|
|
resultExpl += filterInitSingle ? "the initial state" : "first initial state";
|
|
} else {
|
|
resultExpl += filterTrue ? "the first state" : "first state satisfying filter";
|
|
}
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
break;
|
|
case RANGE:
|
|
// Find range of values
|
|
resObj = new prism.Interval(vals.minOverBitSet(bsFilter), vals.maxOverBitSet(bsFilter));
|
|
// Leave result vector unchanged: for a range, result is only available from Result object
|
|
resVals = vals;
|
|
// Set vals to null to stop it being cleared below
|
|
vals = null;
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Range of values over ";
|
|
resultExpl += filterInit ? "initial states" : filterStatesString;
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
break;
|
|
case FORALL:
|
|
// Get access to BitSet for this
|
|
if(paras == null) {
|
|
bs = vals.getBitSet();
|
|
// Print some info to log
|
|
mainLog.print("\nNumber of states satisfying " + expr.getOperand() + ": ");
|
|
mainLog.print(bs.cardinality());
|
|
mainLog.println(bs.cardinality() == model.getNumStates() ? " (all in model)" : "");
|
|
// Check "for all" over filter
|
|
b = vals.forallOverBitSet(bsFilter);
|
|
// Store as object/vector
|
|
resObj = new Boolean(b);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Property " + (b ? "" : "not ") + "satisfied in ";
|
|
mainLog.print("\nProperty satisfied in " + vals.countOverBitSet(bsFilter));
|
|
if (filterInit) {
|
|
if (filterInitSingle) {
|
|
resultExpl += "the initial state";
|
|
} else {
|
|
resultExpl += "all initial states";
|
|
}
|
|
mainLog.println(" of " + model.getNumInitialStates() + " initial states.");
|
|
} else {
|
|
if (filterTrue) {
|
|
resultExpl += "all states";
|
|
mainLog.println(" of all " + model.getNumStates() + " states.");
|
|
} else {
|
|
resultExpl += "all filter states";
|
|
mainLog.println(" of " + bsFilter.cardinality() + " filter states.");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EXISTS:
|
|
// Get access to BitSet for this
|
|
bs = vals.getBitSet();
|
|
// Check "there exists" over filter
|
|
b = vals.existsOverBitSet(bsFilter);
|
|
// Store as object/vector
|
|
resObj = new Boolean(b);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Property satisfied in ";
|
|
if (filterTrue) {
|
|
resultExpl += b ? "at least one state" : "no states";
|
|
} else {
|
|
resultExpl += b ? "at least one filter state" : "no filter states";
|
|
}
|
|
mainLog.println("\n" + resultExpl);
|
|
break;
|
|
case STATE:
|
|
if(paras == null) {
|
|
// Check filter satisfied by exactly one state
|
|
if (bsFilter.cardinality() != 1) {
|
|
String s = "Filter should be satisfied in exactly 1 state";
|
|
s += " (but \"" + filter + "\" is true in " + bsFilter.cardinality() + " states)";
|
|
throw new PrismException(s);
|
|
}
|
|
// Find first (only) value
|
|
// Store as object/vector
|
|
resObj = vals.firstFromBitSet(bsFilter);
|
|
resVals = new RegionValues(expr.getType(), resObj, model);
|
|
// Create explanation of result and print some details to log
|
|
resultExpl = "Value in ";
|
|
if (filterInit) {
|
|
resultExpl += "the initial state";
|
|
} else {
|
|
resultExpl += "the filter state";
|
|
}
|
|
mainLog.println("\n" + resultExpl + ": " + resObj);
|
|
}
|
|
break;
|
|
default:
|
|
throw new PrismException("Unrecognised filter type \"" + expr.getOperatorName() + "\"");
|
|
}
|
|
|
|
// For some operators, print out some matching states
|
|
if (bsMatch != null) {
|
|
states = RegionValues.createFromBitSet(bsMatch, model);
|
|
mainLog.print("\nThere are " + bsMatch.cardinality() + " states with ");
|
|
mainLog.print(expr.getType() instanceof TypeDouble ? "(approximately) " : "" + "this value");
|
|
boolean verbose = verbosity > 0; // TODO
|
|
if (!verbose && bsMatch.cardinality() > 10) {
|
|
mainLog.print(".\nThe first 10 states are displayed below. To view them all, enable verbose mode or use a print filter.\n");
|
|
states.print(mainLog, 10);
|
|
} else {
|
|
mainLog.print(":\n");
|
|
states.print(mainLog);
|
|
}
|
|
|
|
}
|
|
|
|
// Store result
|
|
result.setResult(resObj);
|
|
// Set result explanation (if none or disabled, clear)
|
|
if (expr.getExplanationEnabled() && resultExpl != null) {
|
|
result.setExplanation(resultExpl.toLowerCase());
|
|
} else {
|
|
result.setExplanation(null);
|
|
}
|
|
|
|
// Clear up
|
|
if (vals != null)
|
|
vals.clear();
|
|
|
|
return resVals;
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* Model check a P operator expression and return the values for all states.
|
|
*/
|
|
protected RegionValues checkExpressionProb(ParamModel model, ExpressionProb expr, BitSet needStates) throws PrismException
|
|
{
|
|
Expression pb; // Probability bound (expression)
|
|
BigRational p = null; // Probability bound (actual value)
|
|
//String relOp; // Relational operator
|
|
//boolean min = false; // For nondeterministic models, are we finding min (true) or max (false) probs
|
|
ModelType modelType = model.getModelType();
|
|
RelOp relOp;
|
|
boolean min = false;
|
|
|
|
RegionValues probs = null;
|
|
|
|
// Get info from prob operator
|
|
relOp = expr.getRelOp();
|
|
pb = expr.getProb();
|
|
if (pb != null) {
|
|
p = pb.evaluateExact(constantValues);
|
|
if (p.compareTo(0) == -1 || p.compareTo(1) == 1)
|
|
throw new PrismException("Invalid probability bound " + p + " in P operator");
|
|
}
|
|
min = relOp.isLowerBound() || relOp.isMin();
|
|
|
|
// Compute probabilities
|
|
if (!expr.getExpression().isSimplePathFormula()) {
|
|
throw new PrismNotSupportedException(mode.Engine() + " does not yet handle LTL-style path formulas");
|
|
}
|
|
probs = checkProbPathFormulaSimple(model, expr.getExpression(), min, needStates);
|
|
probs.clearNotNeeded(needStates);
|
|
|
|
if (verbosity > 5) {
|
|
mainLog.print("\nProbabilities (non-zero only) for all states:\n");
|
|
mainLog.print(probs);
|
|
}
|
|
// For =? properties, just return values
|
|
if (pb == null) {
|
|
return probs;
|
|
}
|
|
// Otherwise, compare against bound to get set of satisfying states
|
|
else {
|
|
return probs.binaryOp(Region.getOp(relOp.toString()), p);
|
|
}
|
|
}
|
|
|
|
private RegionValues checkProbPathFormulaSimple(ParamModel model, Expression expr, boolean min, BitSet needStates) throws PrismException
|
|
{
|
|
boolean negated = false;
|
|
RegionValues probs = null;
|
|
|
|
expr = Expression.convertSimplePathFormulaToCanonicalForm(expr);
|
|
|
|
// Negation
|
|
if (expr instanceof ExpressionUnaryOp &&
|
|
((ExpressionUnaryOp)expr).getOperator() == ExpressionUnaryOp.NOT) {
|
|
negated = true;
|
|
min = !min;
|
|
expr = ((ExpressionUnaryOp)expr).getOperand();
|
|
}
|
|
|
|
if (expr instanceof ExpressionTemporal) {
|
|
ExpressionTemporal exprTemp = (ExpressionTemporal) expr;
|
|
|
|
// Next
|
|
if (exprTemp.getOperator() == ExpressionTemporal.P_X) {
|
|
throw new PrismNotSupportedException("Next operator not supported by " + mode + " engine");
|
|
}
|
|
// Until
|
|
else if (exprTemp.getOperator() == ExpressionTemporal.P_U) {
|
|
BitSet needStatesInner = new BitSet(model.getNumStates());
|
|
needStatesInner.set(0, model.getNumStates());
|
|
RegionValues b1 = checkExpression(model, exprTemp.getOperand1(), needStatesInner);
|
|
RegionValues b2 = checkExpression(model, exprTemp.getOperand2(), needStatesInner);
|
|
if (exprTemp.hasBounds()) {
|
|
probs = checkProbBoundedUntil(model, b1, b2, min);
|
|
} else {
|
|
probs = checkProbUntil(model, b1, b2, min, needStates);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (probs == null)
|
|
throw new PrismException("Unrecognised path operator in P operator");
|
|
|
|
if (negated) {
|
|
// Subtract from 1 for negation
|
|
probs = probs.binaryOp(new BigRational(1, 1), parserBinaryOpToRegionOp(ExpressionBinaryOp.MINUS));
|
|
}
|
|
|
|
return probs;
|
|
}
|
|
|
|
private RegionValues checkProbUntil(ParamModel model, RegionValues b1, RegionValues b2, boolean min, BitSet needStates) throws PrismException {
|
|
return valueComputer.computeUnbounded(b1, b2, min, null);
|
|
}
|
|
|
|
private RegionValues checkProbBoundedUntil(ParamModel model, RegionValues b1, RegionValues b2, boolean min) throws PrismException {
|
|
ModelType modelType = model.getModelType();
|
|
//RegionValues probs;
|
|
switch (modelType) {
|
|
case CTMC:
|
|
throw new PrismNotSupportedException("Bounded until operator not supported by " + mode + " engine");
|
|
case DTMC:
|
|
throw new PrismNotSupportedException("Bounded until operator not supported by " + mode + " engine");
|
|
case MDP:
|
|
throw new PrismNotSupportedException("Bounded until operator not supported by " + mode + " engine");
|
|
default:
|
|
throw new PrismNotSupportedException("Cannot model check for a " + modelType);
|
|
}
|
|
|
|
//return probs;
|
|
}
|
|
|
|
/**
|
|
* Model check an R operator expression and return the values for all states.
|
|
*/
|
|
protected RegionValues checkExpressionReward(ParamModel model, ExpressionReward expr, BitSet needStates) throws PrismException
|
|
{
|
|
Expression rb; // Reward bound (expression)
|
|
BigRational r = null; // Reward bound (actual value)
|
|
RegionValues rews = null;
|
|
boolean min = false;
|
|
|
|
// Get info from reward operator
|
|
RewardStruct rewStruct = expr.getRewardStructByIndexObject(modulesFile, constantValues);
|
|
RelOp relOp = expr.getRelOp();
|
|
rb = expr.getReward();
|
|
if (rb != null) {
|
|
r = rb.evaluateExact(constantValues);
|
|
if (r.compareTo(0) == -1)
|
|
throw new PrismException("Invalid reward bound " + r + " in R[] formula");
|
|
}
|
|
min = relOp.isLowerBound() || relOp.isMin();
|
|
|
|
ParamRewardStruct rew = constructRewards(model, rewStruct, constantValues);
|
|
mainLog.println("Building reward structure...");
|
|
rews = checkRewardFormula(model, rew, expr.getExpression(), min, needStates);
|
|
rews.clearNotNeeded(needStates);
|
|
|
|
// Print out probabilities
|
|
if (verbosity > 5) {
|
|
mainLog.print("\nProbabilities (non-zero only) for all states:\n");
|
|
mainLog.print(rews);
|
|
}
|
|
|
|
// For =? properties, just return values
|
|
if (rb == null) {
|
|
return rews;
|
|
}
|
|
// Otherwise, compare against bound to get set of satisfying states
|
|
else {
|
|
return rews.binaryOp(Region.getOp(relOp.toString()), r);
|
|
}
|
|
}
|
|
|
|
private RegionValues checkRewardFormula(ParamModel model,
|
|
ParamRewardStruct rew, Expression expr, boolean min, BitSet needStates) throws PrismException {
|
|
RegionValues rewards = null;
|
|
|
|
if (expr.getType() instanceof TypePathDouble) {
|
|
ExpressionTemporal exprTemp = (ExpressionTemporal) expr;
|
|
switch (exprTemp.getOperator()) {
|
|
case ExpressionTemporal.R_S:
|
|
rewards = checkRewardSteady(model, rew, exprTemp, min, needStates);
|
|
break;
|
|
default:
|
|
throw new PrismNotSupportedException(mode.Engine() + " does not yet handle the " + exprTemp.getOperatorSymbol() + " operator in the R operator");
|
|
}
|
|
} else if (expr.getType() instanceof TypePathBool || expr.getType() instanceof TypeBool) {
|
|
rewards = checkRewardPathFormula(model, rew, expr, min, needStates);
|
|
}
|
|
|
|
if (rewards == null) {
|
|
throw new PrismException("Unrecognised operator in R operator");
|
|
}
|
|
|
|
return rewards;
|
|
}
|
|
|
|
/**
|
|
* Compute rewards for a path formula in a reward operator.
|
|
*/
|
|
private RegionValues checkRewardPathFormula(ParamModel model, ParamRewardStruct rew, Expression expr, boolean min, BitSet needStates) throws PrismException
|
|
{
|
|
if (Expression.isReach(expr)) {
|
|
return checkRewardReach(model, rew, (ExpressionTemporal) expr, min, needStates);
|
|
} else if (Expression.isCoSafeLTLSyntactic(expr, true)) {
|
|
throw new PrismNotSupportedException(mode.Engine() + " does not yet support co-safe reward computation");
|
|
} else {
|
|
throw new PrismException("R operator contains a path formula that is not syntactically co-safe: " + expr);
|
|
}
|
|
}
|
|
|
|
private RegionValues checkRewardReach(ParamModel model,
|
|
ParamRewardStruct rew, ExpressionTemporal expr, boolean min, BitSet needStates) throws PrismException {
|
|
RegionValues allTrue = regionFactory.completeCover(true);
|
|
BitSet needStatesInner = new BitSet(needStates.size());
|
|
needStatesInner.set(0, needStates.size());
|
|
RegionValues reachSet = checkExpression(model, expr.getOperand2(), needStatesInner);
|
|
return valueComputer.computeUnbounded(allTrue, reachSet, min, rew);
|
|
}
|
|
|
|
private RegionValues checkRewardSteady(ParamModel model,
|
|
ParamRewardStruct rew, ExpressionTemporal expr, boolean min, BitSet needStates) throws PrismException {
|
|
if (model.getModelType() != ModelType.DTMC && model.getModelType() != ModelType.CTMC) {
|
|
throw new PrismNotSupportedException(mode.Engine() + " long-run average rewards are only supported for DTMCs and CTMCs");
|
|
}
|
|
RegionValues allTrue = regionFactory.completeCover(true);
|
|
BitSet needStatesInner = new BitSet(needStates.size());
|
|
needStatesInner.set(0, needStates.size());
|
|
return valueComputer.computeSteadyState(allTrue, min, rew);
|
|
}
|
|
|
|
private ParamRewardStruct constructRewards(ParamModel model, RewardStruct rewStruct, Values constantValues2)
|
|
throws PrismException {
|
|
int numStates = model.getNumStates();
|
|
List<State> statesList = model.getStatesList();
|
|
ParamRewardStruct rewSimple = new ParamRewardStruct(functionFactory, model.getNumChoices());
|
|
int numRewItems = rewStruct.getNumItems();
|
|
for (int rewItem = 0; rewItem < numRewItems; rewItem++) {
|
|
Expression expr = rewStruct.getReward(rewItem);
|
|
expr = (Expression) expr.deepCopy().replaceConstants(constantValues);
|
|
Expression guard = rewStruct.getStates(rewItem);
|
|
String action = rewStruct.getSynch(rewItem);
|
|
boolean isTransitionReward = rewStruct.getRewardStructItem(rewItem).isTransitionReward();
|
|
for (int state = 0; state < numStates; state++) {
|
|
if (guard.evaluateBoolean(constantValues, statesList.get(state))) {
|
|
int[] varMap = new int[statesList.get(0).varValues.length];
|
|
for (int i = 0; i < varMap.length; i++) {
|
|
varMap[i] = i;
|
|
}
|
|
Expression exprState = (Expression) expr.deepCopy().evaluatePartially(statesList.get(state), varMap);
|
|
Function newReward = modelBuilder.expr2function(functionFactory, exprState);
|
|
for (int choice = model.stateBegin(state); choice < model.stateEnd(state); choice++) {
|
|
Function sumOut = model.sumLeaving(choice);
|
|
Function choiceReward;
|
|
if (!isTransitionReward) {
|
|
// for state reward, scale by sumOut
|
|
// For DTMC/MDP, this changes nothing;
|
|
// for CTMC this takes the expected duration
|
|
// in this state into account
|
|
choiceReward = newReward.divide(sumOut);
|
|
} else {
|
|
choiceReward = functionFactory.getZero();
|
|
for (int succ = model.choiceBegin(choice); succ < model.choiceEnd(choice); succ++) {
|
|
String mdpAction = model.getLabel(succ);
|
|
if ((isTransitionReward && (mdpAction == null ? (action.isEmpty()) : mdpAction.equals(action)))) {
|
|
choiceReward = choiceReward.add(newReward.multiply(model.succProb(succ)));
|
|
}
|
|
}
|
|
// does not get scaled by sumOut
|
|
}
|
|
rewSimple.addReward(choice, choiceReward);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rewSimple;
|
|
}
|
|
|
|
/**
|
|
* Model check an S operator expression and return the values for all states.
|
|
*/
|
|
protected RegionValues checkExpressionSteadyState(ParamModel model, ExpressionSS expr, BitSet needStates) throws PrismException
|
|
{
|
|
Expression pb; // Probability bound (expression)
|
|
BigRational p = null; // Probability bound (actual value)
|
|
//String relOp; // Relational operator
|
|
//boolean min = false; // For nondeterministic models, are we finding min (true) or max (false) probs
|
|
ModelType modelType = model.getModelType();
|
|
RelOp relOp;
|
|
boolean min = false;
|
|
|
|
RegionValues probs = null;
|
|
|
|
// Get info from prob operator
|
|
relOp = expr.getRelOp();
|
|
pb = expr.getProb();
|
|
if (pb != null) {
|
|
p = pb.evaluateExact(constantValues);
|
|
if (p.compareTo(0) == -1 || p.compareTo(1) == 1)
|
|
throw new PrismException("Invalid probability bound " + p + " in P operator");
|
|
}
|
|
min = relOp.isLowerBound() || relOp.isMin();
|
|
|
|
// Compute probabilities
|
|
probs = checkProbSteadyState(model, expr.getExpression(), min, needStates);
|
|
probs.clearNotNeeded(needStates);
|
|
|
|
if (verbosity > 5) {
|
|
mainLog.print("\nProbabilities (non-zero only) for all states:\n");
|
|
mainLog.print(probs);
|
|
}
|
|
// For =? properties, just return values
|
|
if (pb == null) {
|
|
return probs;
|
|
}
|
|
// Otherwise, compare against bound to get set of satisfying states
|
|
else {
|
|
return probs.binaryOp(Region.getOp(relOp.toString()), p);
|
|
}
|
|
}
|
|
|
|
private RegionValues checkProbSteadyState(ParamModel model, Expression expr, boolean min, BitSet needStates)
|
|
throws PrismException
|
|
{
|
|
BitSet needStatesInner = new BitSet(model.getNumStates());
|
|
needStatesInner.set(0, model.getNumStates());
|
|
RegionValues b = checkExpression(model,expr, needStatesInner);
|
|
if (model.getModelType() != ModelType.DTMC
|
|
&& model.getModelType() != ModelType.CTMC) {
|
|
throw new PrismNotSupportedException(mode.Engine() + " currently only implements steady state for DTMCs and CTMCs.");
|
|
}
|
|
return valueComputer.computeSteadyState(b, min, null);
|
|
}
|
|
|
|
/**
|
|
* Set parameters for parametric analysis.
|
|
*
|
|
* @param paramNames names of parameters
|
|
* @param lower lower bounds of parameters
|
|
* @param upper upper bounds of parameters
|
|
*/
|
|
public void setParameters(String[] paramNames, String[] lower, String[] upper) {
|
|
if (paramNames == null || lower == null || upper == null) {
|
|
throw new IllegalArgumentException("all arguments of this functions must be non-null");
|
|
}
|
|
if (paramNames.length != lower.length || lower.length != upper.length) {
|
|
throw new IllegalArgumentException("all arguments of this function must have the same length");
|
|
}
|
|
|
|
paramLower = new BigRational[lower.length];
|
|
paramUpper = new BigRational[upper.length];
|
|
|
|
for (int i = 0; i < paramNames.length; i++) {
|
|
if (paramNames[i] == null || lower[i] == null || upper[i] == null) {
|
|
throw new IllegalArgumentException("all entries in arguments of this function must be non-null");
|
|
}
|
|
paramLower[i] = new BigRational(lower[i]);
|
|
paramUpper[i] = new BigRational(upper[i]);
|
|
}
|
|
}
|
|
|
|
public static void closeDown() {
|
|
ComputerThreads.terminate();
|
|
}
|
|
|
|
public void setModelBuilder(ModelBuilder builder)
|
|
{
|
|
this.modelBuilder = builder;
|
|
}
|
|
}
|